欢送拜访我的 GitHub
https://github.com/zq2599/blog_demos
内容:所有原创文章分类汇总及配套源码,波及 Java、Docker、Kubernetes、DevOPS 等;
本篇概览
- 在《三分钟:极速体验 JAVA 版指标检测(YOLO4)》一文中,咱们体验了 YOLO4 弱小的物体辨认能力,如下图,原图中的狗子、人、马都被辨认并标注进去了:
- 如果您之前对深度学习和 YOLO、darknet 等有过理解,置信您会产生疑难:Java 能实现这些?
- 没错,明天咱们就从零开始,开发一个 SpringBoot 利用实现上述性能,该利用名为 <font color=”blue”>yolo-demo</font>
- 让 SpringBoot 利用辨认图片中的物体,其要害在如何应用曾经训练好的神经网络模型,好在 OpenCV 集成的 DNN 模块能够加载和应用 YOLO4 模型,咱们只有找到应用 OpenCV 的方法即可
- 我这里的办法是应用 JavaCV 库,因为 JavaCV 自身封装了 OpenCV,最终能够应用 YOLO4 模型进行推理,依赖状况如下图所示:
关键技术
- 本篇波及到 JavaCV、OpenCV、YOLO4 等,从上图能够看出 JavaCV 已将这些做了封装,包含最终推理时所用的模型也是 YOLO4 官网提前训练好的,咱们只有晓得如何应用 JavaCV 的 API 即可
- YOVO4 的 paper 在此:https://arxiv.org/pdf/2004.10…
版本信息
- 这里给出我的开发环境供您参考:
- 操作系统:Ubuntu 16(MacBook Pro 也能够,版本是 11.2.3,macOS Big Sur)
- docker:20.10.2 Community
- java:1.8.0_211
- springboot:2.4.8
- javacv:1.5.6
- opencv:4.5.3
实战步骤
- 在正式入手前,先把本次实战的步骤梳理分明,前面循序渐进执行即可;
- 为了缩小环境和软件差别的影响,让程序的运行调试更简略,这里会把 SpringBoot 利用制作成 docker 镜像,而后在 docker 环境运行,所以,整个实战简略来说分为三步:制做根底镜像、开发 SpringBoot 利用、把利用做成镜像,如下图:
- 上述流程中的第一步 <font color=”blue”> 制做根底镜像 </font>,曾经在《制作 JavaCV 利用依赖的根底 Docker 镜像(CentOS7+JDK8+OpenCV4)》一文中具体介绍,咱们间接应用镜像 <font color=”red”>bolingcavalry/opencv4.5.3:0.0.1</font> 即可,接下来的内容将会聚焦 SpringBoot 利用的开发;
- 这个 SpringBoot 利用的性能很繁多,如下图所示:
- 整个开发过程波及到这些步骤:提交照片的网页、神经网络初始化、文件解决、图片检测、解决检测后果、在图片上规范辨认后果、前端展现图片等,残缺步骤曾经整顿如下图:
- 内容很丰盛,播种也不会少,更何况前文已确保能够胜利运行,那么,别犹豫啦,咱们开始吧!
源码下载
- 本篇实战中的残缺源码可在 GitHub 下载到,地址和链接信息如下表所示(https://github.com/zq2599/blo…):
名称 | 链接 | 备注 |
---|---|---|
我的项目主页 | https://github.com/zq2599/blo… | 该我的项目在 GitHub 上的主页 |
git 仓库地址(https) | https://github.com/zq2599/blo… | 该我的项目源码的仓库地址,https 协定 |
git 仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该我的项目源码的仓库地址,ssh 协定 |
- 这个 git 我的项目中有多个文件夹,本篇的源码在 <font color=”blue”>javacv-tutorials</font> 文件夹下,如下图红框所示:
- <font color=”blue”>javacv-tutorials</font> 外面有多个子工程,明天的代码在 <font color=”red”>yolo-demo</font> 工程下:
新建 SpringBoot 利用
- 新建名为 <font color=”blue”>yolo-demo</font> 的 maven 工程,首先这是个规范的 SpringBoot 工程,其次增加了 javacv 的依赖库,pom.xml 内容如下,重点是 javacv、opencv 等库的依赖和精确的版本匹配:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bolingcavalry</groupId>
<version>1.0-SNAPSHOT</version>
<artifactId>yolo-demo</artifactId>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<maven-compiler-plugin.version>3.6.1</maven-compiler-plugin.version>
<springboot.version>2.4.8</springboot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--FreeMarker 模板视图依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.6</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv-platform-gpu</artifactId>
<version>4.5.3-1.5.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 如果父工程不是 springboot,就要用以下形式应用插件,能力生成失常的 jar -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.bolingcavalry.yolodemo.YoloDemoApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
- 接下来的重点是配置文件 <font color=”blue”>application.properties</font>,如下可见,除了常见的 spring 配置,还有几个文件门路配置,理论运行时,这些门路都要寄存对应的文件给程序应用,这些文件如何获取稍后会讲到:
### FreeMarker 配置
spring.freemarker.allow-request-override=false
#Enable template caching. 启用模板缓存。spring.freemarker.cache=false
spring.freemarker.check-template-location=true
spring.freemarker.charset=UTF-8
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=false
spring.freemarker.expose-session-attributes=false
spring.freemarker.expose-spring-macro-helpers=false
#设置面板后缀
spring.freemarker.suffix=.ftl
# 设置单个文件最大内存
spring.servlet.multipart.max-file-size=100MB
# 设置所有文件最大内存
spring.servlet.multipart.max-request-size=1000MB
# 自定义文件上传门路
web.upload-path=/app/images
# 模型门路
# yolo 的配置文件所在位置
opencv.yolo-cfg-path=/app/model/yolov4.cfg
# yolo 的模型文件所在位置
opencv.yolo-weights-path=/app/model/yolov4.weights
# yolo 的分类文件所在位置
opencv.yolo-coconames-path=/app/model/coco.names
# yolo 模型推理时的图片宽度
opencv.yolo-width=608
# yolo 模型推理时的图片高度
opencv.yolo-height=608
- 启动类 <font color=”blue”>YoloDemoApplication.java</font>:
package com.bolingcavalry.yolodemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class YoloDemoApplication {public static void main(String[] args) {SpringApplication.run(YoloDemoApplication.class, args);
}
}
- 工程已建好,接下来开始编码,先从前端页面开始
前端页面
- 只有波及到前端,欣宸个别都会发个自保申明:请大家原谅欣宸不入流的前端程度,页面做得我本人都不忍直视,但为了性能的残缺,请您忍忍,也不是不能用,咱们总要有个中央提交照片并且展现辨认后果不是?
- 新增名为 <font color=”blue”>index.ftl</font> 的前端模板文件,地位如下图红框:
- <font color=”blue”>index.ftl</font> 的内容如下,可见很简略,有抉择和提交文件的表单,也有展现后果的脚本,还能展现后盾返回的提示信息,嗯嗯,这就够用了:
<!DOCTYPE html>
<head>
<meta charset="UTF-8" />
<title> 图片上传 Demo</title>
</head>
<body>
<h1 > 图片上传 Demo</h1>
<form action="fileUpload" method="post" enctype="multipart/form-data">
<p> 抉择检测文件: <input type="file" name="fileName"/></p>
<p><input type="submit" value="提交"/></p>
</form>
<#-- 判断是否上传文件 -->
<#if msg??>
<span>${msg}</span><br><br>
<#else >
<span>${msg!("文件未上传")}</span><br>
</#if>
<#-- 显示图片,肯定要在 img 中的 src 发申请给 controller,否则间接跳转是乱码 -->
<#if fileName??>
<#--<img src="/show?fileName=${fileName}" style="width: 100px"/>-->
<img src="/show?fileName=${fileName}"/>
<#else>
<#--<img src="/show" style="width: 200px"/>-->
</#if>
</body>
</html>
- 页面的成果,就像上面这样:
后端逻辑:初始化
- 为了放弃简略,所有后端逻辑放在一个 java 文件中:YoloServiceController.java,依照后面梳理的流程,咱们先看初始化局部
- 首先是成员变量和依赖
private final ResourceLoader resourceLoader;
@Autowired
public YoloServiceController(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}
@Value("${web.upload-path}")
private String uploadPath;
@Value("${opencv.yolo-cfg-path}")
private String cfgPath;
@Value("${opencv.yolo-weights-path}")
private String weightsPath;
@Value("${opencv.yolo-coconames-path}")
private String namesPath;
@Value("${opencv.yolo-width}")
private int width;
@Value("${opencv.yolo-height}")
private int height;
/**
* 置信度门限(超过这个值才认为是可信的推理后果)*/
private float confidenceThreshold = 0.5f;
private float nmsThreshold = 0.4f;
// 神经网络
private Net net;
// 输入层
private StringVector outNames;
// 分类名称
private List<String> names;
- 接下来是初始化办法 init,可见会从之前配置的几个文件门路中加载神经网络所需的配置、训练模型等文件,要害办法是 readNetFromDarknet 的调用,还有就是查看是否有反对 CUDA 的设施,如果有就在神经网络中做好设置:
@PostConstruct
private void init() throws Exception {
// 初始化打印一下,确保编码失常,否则日志输入会是乱码
log.error("file.encoding is" + System.getProperty("file.encoding"));
// 神经网络初始化
net = readNetFromDarknet(cfgPath, weightsPath);
// 查看网络是否为空
if (net.empty()) {log.error("神经网络初始化失败");
throw new Exception("神经网络初始化失败");
}
// 输入层
outNames = net.getUnconnectedOutLayersNames();
// 查看 GPU
if (getCudaEnabledDeviceCount() > 0) {net.setPreferableBackend(opencv_dnn.DNN_BACKEND_CUDA);
net.setPreferableTarget(opencv_dnn.DNN_TARGET_CUDA);
}
// 分类名称
try {names = Files.readAllLines(Paths.get(namesPath));
} catch (IOException e) {log.error("获取分类名称失败,文件门路[{}]", namesPath, e);
}
}
解决上传文件
- 前端将二进制格局的图片文件提交上来后如何解决?这里整顿了一个简略的文件解决办法 upload,会将文件保留在服务器的指定地位,前面会调用:
/**
* 上传文件到指定目录
* @param file 文件
* @param path 文件寄存门路
* @param fileName 源文件名
* @return
*/
private static boolean upload(MultipartFile file, String path, String fileName){
// 应用原文件名
String realPath = path + "/" + fileName;
File dest = new File(realPath);
// 判断文件父目录是否存在
if(!dest.getParentFile().exists()){dest.getParentFile().mkdir();}
try {
// 保留文件
file.transferTo(dest);
return true;
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
物体检测
- 筹备工作都实现了,来写最外围的物体检测代码,这些代码放在 yolo-demo 利用解决 web 申请的办法中,如下所示,可见这里只是个纲要,将推理、后果解决、图片标注等性能串起来造成残缺流程,然而不波及每个具体性能的细节:
@RequestMapping("fileUpload")
public String upload(@RequestParam("fileName") MultipartFile file, Map<String, Object> map){log.info("文件 [{}], 大小 [{}]", file.getOriginalFilename(), file.getSize());
// 文件名称
String originalFileName = file.getOriginalFilename();
if (!upload(file, uploadPath, originalFileName)){map.put("msg", "上传失败!");
return "forward:/index";
}
// 读取文件到 Mat
Mat src = imread(uploadPath + "/" + originalFileName);
// 执行推理
MatVector outs = doPredict(src);
// 解决原始的推理后果,// 对检测到的每个指标,找出置信度最高的类别作为改指标的类别,// 还要找出每个指标的地位,这些信息都保留在 ObjectDetectionResult 对象中
List<ObjectDetectionResult> results = postprocess(src, outs);
// 开释资源
outs.releaseReference();
// 检测到的指标总数
int detectNum = results.size();
log.info("一共检测到 {} 个指标", detectNum);
// 没检测到
if (detectNum<1) {
// 显示图片
map.put("msg", "未检测到指标");
// 文件名
map.put("fileName", originalFileName);
return "forward:/index";
} else {
// 检测后果页面的提示信息
map.put("msg", "检测到" + results.size() + "个指标");
}
// 计算出总耗时,并输入在图片的左上角
printTimeUsed(src);
// 将每一个被辨认的对象在图片框出来,并在框的左上角标注该对象的类别
markEveryDetectObject(src, results);
// 将增加了标注的图片放弃在磁盘上,并将图片信息写入 map(给跳转页面应用)saveMarkedImage(map, src);
return "forward:/index";
}
- 这里曾经能够把整个流程弄明确了,接下来开展每个细节
用神经网络检测物体
- 由下面的代码可见,图片被转为 Mat 对象后(OpenCV 中的重要数据结构,能够了解为矩阵,外面寄存着图片每个像素的信息),被送入 <font color=”blue”>doPredict</font> 办法,该办法执行结束后就失去了物体辨认的后果
- 细看 doPredict 办法,可见外围是用 blobFromImage 办法失去四维 blob 对象,再将这个对象送给神经网络去检测(net.setInput、net.forward)
/**
* 用神经网络执行推理
* @param src
* @return
*/
private MatVector doPredict(Mat src) {
// 将图片转为四维 blog,并且对尺寸做调整
Mat inputBlob = blobFromImage(src,
1 / 255.0,
new Size(width, height),
new Scalar(0.0),
true,
false,
CV_32F);
// 神经网络输出
net.setInput(inputBlob);
// 设置输入后果保留的容器
MatVector outs = new MatVector(outNames.size());
// 推理,后果保留在 outs 中
net.forward(outs, outNames);
// 开释资源
inputBlob.release();
return outs;
}
- 要留神的是,blobFromImage、net.setInput、net.forward 这些都是 native 办法,是 OpenCV 的 dnn 模块提供的
- doPredict 办法返回的是 MatVector 对象,这外面就是检测后果
解决原始检测后果
- 检测后果 MatVector 对象是个汇合,外面有多个 Mat 对象,每个 Mat 对象是一个表格,外面有丰盛的数据,具体的内容如下图:
- 看过上图后,置信您对如何解决原始的检测后果曾经胸有成竹了,只有从 MatVector 中一一取出 Mat,把每个 Mat 当做表格,将表格每一行中概率最大的列找到,此列就是该物体的类别了(至于每一列到底是啥货色,为啥下面表格中第五列是人,第六列是自行车,最初一列是牙刷?这个稍后会讲到):
/**
* 推理实现后的操作
* @param frame
* @param outs
* @return
*/
private List<ObjectDetectionResult> postprocess(Mat frame, MatVector outs) {final IntVector classIds = new IntVector();
final FloatVector confidences = new FloatVector();
final RectVector boxes = new RectVector();
// 解决神经网络的输入后果
for (int i = 0; i < outs.size(); ++i) {
// extract the bounding boxes that have a high enough score
// and assign their highest confidence class prediction.
// 每个检测到的物体,都有对应的每种类型的置信度,取最高的那种
// 例如检车到猫的置信度百分之九十,狗的置信度百分之八十,那就认为是猫
Mat result = outs.get(i);
FloatIndexer data = result.createIndexer();
// 将检测后果看做一个表格,// 每一行示意一个物体,// 后面四列示意这个物体的坐标,前面的每一列,示意这个物体在某个类别上的置信度,// 每行都是从第五列开始遍历,找到最大值以及对应的列号,for (int j = 0; j < result.rows(); j++) {
// minMaxLoc implemented in java because it is 1D
int maxIndex = -1;
float maxScore = Float.MIN_VALUE;
for (int k = 5; k < result.cols(); k++) {float score = data.get(j, k);
if (score > maxScore) {
maxScore = score;
maxIndex = k - 5;
}
}
// 如果最大值大于之前设定的置信度门限,就示意能够确定是这类物体了,// 而后就把这个物体相干的辨认信息保留下来,要保留的信息有:类别、置信度、坐标
if (maxScore > confidenceThreshold) {int centerX = (int) (data.get(j, 0) * frame.cols());
int centerY = (int) (data.get(j, 1) * frame.rows());
int width = (int) (data.get(j, 2) * frame.cols());
int height = (int) (data.get(j, 3) * frame.rows());
int left = centerX - width / 2;
int top = centerY - height / 2;
// 保留类别
classIds.push_back(maxIndex);
// 保留置信度
confidences.push_back(maxScore);
// 保留坐标
boxes.push_back(new Rect(left, top, width, height));
}
}
// 资源开释
data.release();
result.release();}
// remove overlapping bounding boxes with NMS
IntPointer indices = new IntPointer(confidences.size());
FloatPointer confidencesPointer = new FloatPointer(confidences.size());
confidencesPointer.put(confidences.get());
// 非极大值克制
NMSBoxes(boxes, confidencesPointer, confidenceThreshold, nmsThreshold, indices, 1.f, 0);
// 将检测后果放入 BO 对象中,便于业务解决
List<ObjectDetectionResult> detections = new ArrayList<>();
for (int i = 0; i < indices.limit(); ++i) {final int idx = indices.get(i);
final Rect box = boxes.get(idx);
final int clsId = classIds.get(idx);
detections.add(new ObjectDetectionResult(
clsId,
names.get(clsId),
confidences.get(idx),
box.x(),
box.y(),
box.width(),
box.height()));
// 开释资源
box.releaseReference();}
// 开释资源
indices.releaseReference();
confidencesPointer.releaseReference();
classIds.releaseReference();
confidences.releaseReference();
boxes.releaseReference();
return detections;
}
- 可见代码很简略,就是把每个 Mat 当做表格来解决,有两处特地的中央要解决:
- confidenceThreshold 变量,置信度门限,这里是 0.5,如果某一行的最大概率连 0.5 都达不到,那就相当于已知所有类别的可能性都不大,那就不算辨认进去了,所以不会存入 detections 汇合中(不会在后果图片中标注)
- NMSBoxes:分类器进化为检测器时,在原始图像上从多个尺度产生窗口,这就导致下图左侧的成果,同一个人检测了多张人脸,此时用 NMSBoxes 来保留最优的一个后果
- 当初解释一下 Mat 对象对应的表格中,每一列到底是什么类别:这个表格是 YOLO4 的检测后果,所以每一列是什么类别应该由 YOLO4 来解释,官网提供了名为 <font color=”blue”>coco.names</font> 的文件,该文件的内容如下图,一共 80 行,每一行是示意一个类别:
- 此刻聪慧的您必定曾经明确 Mat 表格中的每一列代表什么类别了:Mat 表格中的每一列对应 <font color=”blue”>coco.names</font> 的每一行,如下图:
- postprocess 办法执行结束后,一张照片的辨认后果就被放入名为 detections 的汇合中,该汇合内的每个元素代表一个辨认出的物体,来看看这个元素的数据结构,如下所示,这些数据够咱们在照片上标注辨认后果了:
@Data
@AllArgsConstructor
public class ObjectDetectionResult {
// 类别索引
int classId;
// 类别名称
String className;
// 置信度
float confidence;
// 物体在照片中的横坐标
int x;
// 物体在照片中的纵坐标
int y;
// 物体宽度
int width;
// 物体高度
int height;
}
把检测后果画在图片上
- 手里有了检测后果,接下来要做的就是将这些后果画在原图上,这样就有了物体辨认的成果,画图分两局部,首先是左上角的总耗时,其次是每个物体辨认后果
- 先在图片的上角画出本次检测的总耗时,成果如下图所示:
- 负责画出总耗时的是 printTimeUsed 办法,如下,可见总耗时是用多层网络的总次数除以频率失去的,留神,这不是网页上的接口总耗时,而是神经网络辨认物体的总耗时,例外画图的 putText 是个本地办法,这也是 OpenCV 的罕用办法之一:
/**
* 计算出总耗时,并输入在图片的左上角
* @param src
*/
private void printTimeUsed(Mat src) {
// 总次数
long totalNums = net.getPerfProfile(new DoublePointer());
// 频率
double freq = getTickFrequency()/1000;
// 总次数除以频率就是总耗时
double t = totalNums / freq;
// 将本次检测的总耗时打印在展现图像的左上角
putText(src,
String.format("Inference time : %.2f ms", t),
new Point(10, 20),
FONT_HERSHEY_SIMPLEX,
0.6,
new Scalar(255, 0, 0, 0),
1,
LINE_AA,
false);
}
- 接下来是画出每个物体辨认的后果,有了 ObjectDetectionResult 对象汇合,画图就非常简单了:调用画矩形和文本的本地办法即可:
/**
* 将每一个被辨认的对象在图片框出来,并在框的左上角标注该对象的类别
* @param src
* @param results
*/
private void markEveryDetectObject(Mat src, List<ObjectDetectionResult> results) {
// 在图片上标出每个指标以及类别和置信度
for(ObjectDetectionResult result : results) {log.info("类别[{}],置信度[{}%]", result.getClassName(), result.getConfidence() * 100f);
// annotate on image
rectangle(src,
new Point(result.getX(), result.getY()),
new Point(result.getX() + result.getWidth(), result.getY() + result.getHeight()),
Scalar.MAGENTA,
1,
LINE_8,
0);
// 写在指标左上角的内容:类别 + 置信度
String label = result.getClassName() + ":" + String.format("%.2f%%", result.getConfidence() * 100f);
// 计算显示这些内容所需的高度
IntPointer baseLine = new IntPointer();
Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, baseLine);
int top = Math.max(result.getY(), labelSize.height());
// 增加内容到图片上
putText(src, label, new Point(result.getX(), top-4), FONT_HERSHEY_SIMPLEX, 0.5, new Scalar(0, 255, 0, 0), 1, LINE_4, false);
}
}
展现后果
- 外围工作曾经实现,接下来就是保留图片再跳转到展现网页:
- 至此 SpringBoot 工程编码实现,接下来要做的就是将整个工程做成 docker 镜像
将 SpringBoot 工程做成 docker 镜像
- 后面《制作 JavaCV 利用依赖的根底 Docker 镜像(CentOS7+JDK8+OpenCV4)》做好了根底镜像,帮咱们筹备好了 JDK 和 OpenCV 库,使得接下来的操作分外简略,咱们一步一步来
- 先编写 Dockerfile 文件,Dockerfile 文件请放在 <font color=”blue”> 和 pom.xml 同一目录 </font>,内容如下:
# 根底镜像集成了 openjdk8 和 opencv4.5.3
FROM bolingcavalry/opencv4.5.3:0.0.1
# 创立目录
RUN mkdir -p /app/images && mkdir -p /app/model
# 指定镜像的内容的起源地位
ARG DEPENDENCY=target/dependency
# 复制内容到镜像
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENV LANG C.UTF-8
ENV LANGUAGE zh_CN.UTF-8
ENV LC_ALL C.UTF-8
ENV TZ Asia/Shanghai
# 指定启动命令(留神要执行编码,否则日志是乱码)
ENTRYPOINT ["java","-Dfile.encoding=utf-8","-cp","app:app/lib/*","com.bolingcavalry.yolodemo.YoloDemoApplication"]
- 控制台进入 pom.xml 所在目录,执行命令 <font color=”blue”>mvn clean package -U</font>,这是个一般的 maven 命令,会编译源码,在 target 目录下生成文件 <font color=”red”>yolo-demo-1.0-SNAPSHOT.jar</font>
- 执行以下命令,能够从 jar 文件中提取出制作 docker 镜像所需的内容:
mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
- 执行以下命令即可构建镜像:
docker build -t bolingcavalry/yolodemo:0.0.1 .
- 构建胜利:
will@willMini yolo-demo % docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
bolingcavalry/yolodemo 0.0.1 d0ef6e734b53 About a minute ago 2.99GB
bolingcavalry/opencv4.5.3 0.0.1 d1518ffa4699 6 days ago 2.01GB
- 此刻,具备残缺物体辨认能力的 SpringBoot 利用曾经开发实现了,还记得 application.properties 中的那几个文件门路配置么?咱们要去下载这几个文件,有两种下载方式,您二选一即可
- 第一种是从官网下载,从上面这三个地址别离下下载:
- YOLOv4 配置文件: https://raw.githubusercontent…
- YOLOv4 权重: https://github.com/AlexeyAB/d…
- 分类名称: https://raw.githubusercontent…
- 第二种是从 csdn 下载(无需积分),上述三个文件我已打包放在此:https://download.csdn.net/dow…
- 上述两种形式无论哪种,最终都会失去三个文件:yolov4.cfg、yolov4.weights、coco.names,请将它们放在同一目录下,我是放在这里:/home/will/temp/202110/19/model
- 新建一个目录用来寄存照片,我这里新建的目录是:<font color=”blue”>/home/will/temp/202110/19/images</font>,留神要确保该目录能够读写
最终目录构造如下所示:
/home/will/temp/202110/19/
├── images
└── model
├── coco.names
├── yolov4.cfg
└── yolov4.weights
- 万事俱备,执行以下命令即可运行服务:
sudo docker run \
--rm \
--name yolodemo \
-p 8080:8080 \
-v /home/will/temp/202110/19/images:/app/images \
-v /home/will/temp/202110/19/model:/app/model \
bolingcavalry/yolodemo:0.0.1
- 服务运行起来后,操作过程和成果与《三分钟:极速体验 JAVA 版指标检测(YOLO4)》一文完全相同,就不多赘述了
- 至此,整个物体辨认的开发实战就实现了,Java 在工程化方面的便利性,再联合深度学习畛域的优良模型,为咱们解决视觉图像问题减少了一个备选计划,如果您是一位对视觉和图像感兴趣的 Java 程序员,心愿本文能给您一些参考
你不孤独,欣宸原创一路相伴
- Java 系列
- Spring 系列
- Docker 系列
- kubernetes 系列
- 数据库 + 中间件系列
- DevOps 系列
欢送关注公众号:程序员欣宸
微信搜寻「程序员欣宸」,我是欣宸,期待与您一起畅游 Java 世界 …
https://github.com/zq2599/blog_demos