本篇概览

如果您看过《Java版人脸检测上篇》一文,甚至动手实际操作过,那么你应该会对背后的技术细节感兴趣,开发这样一个应用,咱们总共要做以下三件事:
1.准备好docker基础镜像
2.开发java应用
3.将java应用打包成package文件,集成到基础镜像中,得到最终的java应用镜像

对于准备好docker基础镜像这项工作,咱们在前文《Java版人脸检测详解上篇:运行环境的Docker镜像(CentOS+JDK+OpenCV)》已经完成了,接下来要做的就是开发java应用并将其做成docker镜像

版本信息
这个java应用的涉及的版本信息如下:

  • springboot:2.4.8
  • javacpp:1.4.3
  • javacv:1.4.3
    源码下载
    本篇实战中的完整源码可在GitHub下载到,地址和链接信息如下表所示:

编码

  • 为了统一管理源码和jar依赖,项目采用了maven父子结构,父工程名为javacv-tutorials,其pom.xml如下,可见主要是定义了一些jar的版本:
<?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><artifactId>javacv-tutorials</artifactId><packaging>pom</packaging><version>1.0-SNAPSHOT</version><modules><module>face-detect-demo</module></modules><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><!-- javacpp当前版本 --><javacpp.version>1.4.3</javacpp.version><!-- opencv版本 --><opencv.version>3.4.3</opencv.version><!-- ffmpeg版本 --><ffmpeg.version>4.0.2</ffmpeg.version></properties><dependencyManagement><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.18</version></dependency><dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>${javacpp.version}</version></dependency><dependency><groupId>org.bytedeco</groupId><artifactId>javacv</artifactId><version>${javacpp.version}</version></dependency><!-- javacpp --><dependency><groupId>org.bytedeco</groupId><artifactId>javacpp</artifactId><version>${javacpp.version}</version></dependency><!-- ffmpeg --><dependency><groupId>org.bytedeco.javacpp-presets</groupId><artifactId>ffmpeg-platform</artifactId><version>${ffmpeg.version}-${javacpp.version}</version></dependency><dependency><groupId>org.bytedeco.javacpp-presets</groupId><artifactId>ffmpeg</artifactId><version>${ffmpeg.version}-${javacpp.version}</version></dependency></dependencies></dependencyManagement>
</project>

在javacv-tutorials下面新建名为face-detect-demo的子工程,这里面是咱们今天要开发的应用,其pom.xml如下:

<?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"><parent><artifactId>javacv-tutorials</artifactId><groupId>com.bolingcavalry</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>face-detect-demo</artifactId><packaging>jar</packaging><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></dependency><dependency><groupId>org.bytedeco</groupId><artifactId>javacv</artifactId></dependency><!-- javacpp --><dependency><groupId>org.bytedeco</groupId><artifactId>javacpp</artifactId></dependency><!-- ffmpeg --><dependency><groupId>org.bytedeco.javacpp-presets</groupId><artifactId>ffmpeg-platform</artifactId></dependency><dependency><groupId>org.bytedeco.javacpp-presets</groupId><artifactId>ffmpeg</artifactId></dependency></dependencies><build><plugins><!-- 如果父工程不是springboot,就要用以下方式使用插件,才能生成正常的jar --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><mainClass>com.bolingcavalry.facedetect.FaceDetectApplication</mainClass></configuration><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build>
</project>

配置文件如下,要重点关注前段模板、文件上传大小、模型文件目录等配置:

###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
# 模型路径
opencv.model-path=/app/model/haarcascade_frontalface_default.xml

前端页面文件只有一个index.ftl,请原谅欣宸不入流的前端水平,前端只有一个页面,可以提交页面,同时也是展示处理结果的页面:

<!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="number" value="32" name="minneighbors"/></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>

再来看后台代码,先是最常见的应用启动类:

package com.bolingcavalry.facedetect;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class FaceDetectApplication {public static void main(String[] args) {SpringApplication.run(FaceDetectApplication.class, args);}
}

前端上传图片后,后端要做哪些处理呢?先不贴代码,咱们把后端要做的事情捋一遍,如下图:

接下来是最核心的业务类UploadController.java,web接口和业务逻辑处理都在这里面,是按照上图的流程顺序执行的,有几处要注意的地方稍后会提到:

package com.bolingcavalry.facedetect.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;import java.io.File;
import java.io.IOException;
import java.util.Map;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;import java.util.UUID;import static org.bytedeco.javacpp.opencv_objdetect.CV_HAAR_DO_CANNY_PRUNING;@Controller
@Slf4j
public class UploadController {static {// 加载 动态链接库System.loadLibrary(Core.NATIVE_LIBRARY_NAME);}private final ResourceLoader resourceLoader;@Autowiredpublic UploadController(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}@Value("${web.upload-path}")private String uploadPath;@Value("${opencv.model-path}")private String modelPath;/*** 跳转到文件上传页面* @return*/@RequestMapping("index")public String toUpload(){return "index";}/*** 上次文件到指定目录* @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 blocke.printStackTrace();return false;} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();return false;}}/**** @param file 要上传的文件* @return*/@RequestMapping("fileUpload")public String upload(@RequestParam("fileName") MultipartFile file, @RequestParam("minneighbors") int minneighbors, Map<String, Object> map){log.info("file [{}], size [{}], minneighbors [{}]", file.getOriginalFilename(), file.getSize(), minneighbors);String originalFileName = file.getOriginalFilename();if (!upload(file, uploadPath, originalFileName)){map.put("msg", "上传失败!");return "forward:/index";}String realPath = uploadPath + "/" + originalFileName;Mat srcImg = Imgcodecs.imread(realPath);// 目标灰色图像Mat dstGrayImg = new Mat();// 转换灰色Imgproc.cvtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);// OpenCv人脸识别分类器CascadeClassifier classifier = new CascadeClassifier(modelPath);// 用来存放人脸矩形MatOfRect faceRect = new MatOfRect();// 特征检测点的最小尺寸Size minSize = new Size(32, 32);// 图像缩放比例,可以理解为相机的X倍镜double scaleFactor = 1.2;// 执行人脸检测classifier.detectMultiScale(dstGrayImg, faceRect, scaleFactor, minneighbors, CV_HAAR_DO_CANNY_PRUNING, minSize);//遍历矩形,画到原图上面// 定义绘制颜色Scalar color = new Scalar(0, 0, 255);Rect[] rects = faceRect.toArray();// 没检测到if (null==rects || rects.length<1) {// 显示图片map.put("msg", "未检测到人脸");// 文件名map.put("fileName", originalFileName);return "forward:/index";}// 逐个处理for(Rect rect: rects) {int x = rect.x;int y = rect.y;int w = rect.width;int h = rect.height;// 单独框出每一张人脸Imgproc.rectangle(srcImg, new Point(x, y), new Point(x + w, y + w), color, 2);}// 添加人脸框之后的图片的名字String newFileName = UUID.randomUUID().toString() + ".png";// 保存Imgcodecs.imwrite(uploadPath + "/" + newFileName, srcImg);// 显示图片map.put("msg", "一共检测到" + rects.length + "个人脸");// 文件名map.put("fileName", newFileName);return "forward:/index";}/*** 显示单张图片* @return*/@RequestMapping("show")public ResponseEntity showPhotos(String fileName){if (null==fileName) {return ResponseEntity.notFound().build();}try {// 由于是读取本机的文件,file是一定要加上的, path是在application配置文件中的路径return ResponseEntity.ok(resourceLoader.getResource("file:" + uploadPath + "/" + fileName));} catch (Exception e) {return ResponseEntity.notFound().build();}}
}
  • UploadController.java的代码,有以下几处要关注:

1.在静态方法中通过System.loadLibrary加载本地库函,实际开发过程中,这里是最容易报错的地方,一定要确保-Djava.library.path参数配置的路径中的本地库是正常可用的,前文制作的基础镜像中已经准比好了这些本地库,因此只要确保-Djava.library.path参数配置正确即可,这个配置在稍后的Dockerfile中会提到

2.public String upload方法是处理人脸检测的代码入口,内部按照前面分析的流程顺序执行

3.new CascadeClassifier(modelPath)是根据指定的模型来实例化分类器,模型文件是从GitHub下载的,opencv官方提前训练好的模型,地址是:好像不能放网址哈哈哈哈哈哈哈

4.看似神奇的人脸检测功能,实际上只需一行代码classifier.detectMultiScale,就能得到每个人脸在原图中的矩形位置,接下来,咱们只要按照位置在原图上添加矩形框即可

  • 现在代码已经写完了,接下来将其做成docker镜像

docker镜像制作

  • 首先是编写Dockerfile:
# 基础镜像集成了openjdk8和opencv3.4.3
FROM bolingcavalry/opencv3.4.3:0.0.3# 创建目录
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# 指定启动命令
ENTRYPOINT ["java","-Djava.library.path=/opencv-3.4.3/build/lib","-cp","app:app/lib/*","com.bolingcavalry.facedetect.FaceDetectApplication"]
  • 上述Dockerfile内容很简单,就是一些复制文件的处理,只有一处要格外注意:启动命令中有个参数-Djava.library.path=/opencv-3.4.3/build/lib,指定了本地so库的位置,前面的java代码中,System.loadLibrary加载的本地库就是从这个位置加载的,咱们用的基础镜像是bolingcavalry/opencv3.4.3:0.0.3,已经在该位置准备好了opencv的所有本地库
  • 在父工程目录下执行mvn clean package -U,这是个纯粹的maven操作,和docker没有任何关系
  • 进入face-detect-demo目录,执行以下命令,作用是从jar文件中提取class、配置文件、依赖库等内容到target/dependency目录:
mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
  • 最后,在Dockerfile文件所在目录执行命令docker build -t bolingcavalry/facedetect:0.0.1 .(命令的最后有个点,不要漏了),即可完成镜像制作
  • 如果您有hub.docker.com的账号,还可以通过docker push命令把镜像推送到中央仓库,让更多的人用到:
  • 最后,再来回顾一下《三分钟极速体验:Java版人脸检测》一文中启动docker容器的命令,如下可见,通过两个-v参数,将宿主机的目录映射到容器中,因此,容器中的/app/images和/app/model可以保持不变,只要能保证宿主机的目录映射正确即可:
docker run \
--rm \
-p 18080:8080 \
-v /root/temp/202107/17/images:/app/images \
-v /root/temp/202107/17/model:/app/model \
bolingcavalry/facedetect:0.0.1
  • 有关SpringBoot官方推荐的docker镜像制作的更多信息,请参考《SpringBoot(2.4)应用制作Docker镜像(Gradle版官方方案)》

需要重点注意的地方

  • 请大家关注pom.xml中和javacv相关的几个库的版本,这些版本是不能随便搭配的,建议按照文中的来,就算要改,也请在maven中央仓库检查您所需的版本是否存在;

  • 至此,《Java版人脸检测》详解都完成了,小小的功能涉及到不少知识点,也让我们体验到了javacv的便捷和强大,借助docker将环境配置和应用开发分离开来,降低了应用开发和部署的难度(不再花时间到jdk和opencv的部署上),如果您正在寻找简单易用的javacv开发和部署方案,希望本文能给您提供参考;


http://www.taodudu.cc/news/show-3620877.html

相关文章:

  • Spring 家族
  • jy-16-T-STORE——陈子枢总结
  • MAVEN整合Spring+SpringMVC+Mybatis
  • linux开启多个matlab_Matlab并行编程方法
  • 点击图片验证码更换验证码图片
  • python用selenium 验证码图片_selenium实现定位截图----获取验证码图片
  • java随机生成验证码图片
  • 在线生成验证码图片
  • 解决微信开发者工具报错:当前开发者未绑定此AppID,请到小程序后台操作重试
  • eShopOnContainers 知多少[3]:Identity microservice
  • ASP.NET Core 奇淫技巧之动态WebApi
  • html网页设计期末大作业——清新的手工肥皂网页(4页) web网页设计实例作业
  • 【总】IdentityServer4 32篇汇总
  • python开发微信公众号开发教程百度云_Python开发微信公众号后台(系列一)
  • C++菜鸟学习笔记系列(3)——基本内置类型的使用
  • C#使用微软TTS语音引擎实现文字转语音示例
  • 点云地面点滤波-progressive TIN densification(PTD)算法介绍
  • 《Head First HTML与CSS》笔记 ——1 Web语言
  • 晓晨高效IP提取工具 附源码
  • ROS学习-理解ROS Services 和 Parameters
  • 堡垒机应用服务器发布原理,堡垒机原理
  • 什么是堡垒机?
  • 堡垒机、堡垒机的原理
  • 等保测评的堡垒机是什么?有什么功能?
  • 云堡垒机的作用_三分钟了解什么是云堡垒机
  • 开源堡垒机是什么?开源堡垒机的优缺点是什么?
  • 堡垒机浅析
  • 堡垒机怎么安装mysql_堡垒机安装
  • 云堡垒机的作用_堡垒机作用
  • 跳板机和堡垒机的区别_什么是堡垒机?为什么需要堡垒机?

Java版人脸检测详解下篇:开发java应用并做成docker镜像相关推荐

  1. Java版人脸检测详解下篇:编码

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 如果您看过<三分钟极速体验:Java版 ...

  2. Java版人脸检测详解上篇:运行环境的Docker镜像(CentOS+JDK+OpenCV)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 如果您看过<三分钟极速体验:Java版 ...

  3. 三分钟极速体验:Java版人脸检测

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 检测照片中的人脸,用Java可以实现吗? 当 ...

  4. OpenCV-Python实战(14)——人脸检测详解(仅需6行代码学会4种人脸检测方法)

    OpenCV-Python实战(14)--人脸检测详解(仅需6行代码学会4种人脸检测方法) 0. 前言 1. 人脸处理简介 2. 安装人脸处理相关库 2.1 安装 dlib 2.2 安装 face_r ...

  5. Java快速输入输出使用详解(解决Java输入输出超时问题)

    Java快速输入输出使用详解 一.背景:   Scanner类输入时,虽然方式很简单,但是输入实在太慢了,经常遇到大的数据量时候就超时.(你觉得数据有点多时就用快速输入即可)   某些题目用Scann ...

  6. 【OpenCV实战】OpenCV实现人脸检测详解(含代码)

    OpenCV中有许多可以进行人脸.人眼检测的特征文件,今天我们利用OpenCV中自带的特征文件haarcascade_frontalface_default.xml来进行人脸检测. [OpenCV实战 ...

  7. Viola-Jones人脸检测详解

    在人脸检测中,Viola-Jones算法是一种非常经典的算法,该算法在2001年的CVPR上提出,因其高效快速的检测而被广泛使用. 这个算法用来检测正面的人脸图像,对于侧脸图像的检测不是很稳健. 算法 ...

  8. 我的世界java版合成表_《我的世界》基础攻略 JAVA版合成系统详解

    Java版合成系统 2×2合成网格 3×3合成网格(工作台上) 对于一些物品的合成,其原材料的排放位置无关紧要.这些配方通常称为"无序配方".譬如,发酵蛛眼的合成配方就是无序配方, ...

  9. 人脸算法系列:MTCNN人脸检测详解

    点击上方"AI算法与图像处理",选择加"星标"或"置顶" 重磅干货,第一时间送达 大家好,从今天起我要开始更新人脸识别系列的文章了.为了提升 ...

最新文章

  1. c mysql 时间段查询_mySql 时间段查询
  2. function 多个函数用一个_一列转多行多列,用INDIRECT函数,给你一个可套用的公式模板...
  3. linux io读写次数 oid,Oidiomycosis in Porto Rico.
  4. 2017计算机应用类专业综合知识试题,对口高考2017计算机应用类专业综合模拟题.doc...
  5. ASP.NET中TextBox控件设置ReadOnly=true后台取不到值
  6. 用IBM WebSphere DataStage进行数据整合: 第 1 部分
  7. spark mysql 交互_Spark - 直接操作数据源 MySQL
  8. 把word地址做链接在线打开word
  9. python安装教程-PyCharm 安装教程(Windows)
  10. Web页面打印及GridView导出到Excel
  11. BlogEngine.Net架构与源代码分析系列part2:业务对象——共同的父类BusinessBase
  12. InDesign 软件教程,如何新建文档?
  13. 120个常用货源网站,赶紧收藏!
  14. visio反向生成mysql数据库模型图_Visio2010中逆向工程建立数据库模型图
  15. vb.net 教程 6-14 终止线程的例子
  16. 算法精解(一):C语言描述(链表)
  17. 三分钟快速了解怎么查询ip地址,怎么更换ip地址!
  18. 解除百度网盘下载限制的办法
  19. 如何快速调出软键盘_软键盘怎么调出来 打开软键盘的方法【图文】
  20. 苹果拍照怎么显示地点和时间_2020年康复理疗师证报名时间怎么报考考试地点...

热门文章

  1. 激光枪真的能杀人于无形吗?
  2. 美甲店如何通过线上找客户?适合美甲店的引流渠道分享
  3. [GXYCTF2019]BabyUpload 1 文件上传
  4. 最近使用的一些Win10运行快捷命令
  5. 腾讯QQ每分钟在线人数的计算方法
  6. linux 下搜狗输入法安装
  7. H5 app之初体验
  8. 作用力与反作用力摘要(补课= =)
  9. 记fofa爬虫工具开发篇
  10. 距离度量 —— 欧式距离(Euclidean Distance)