赞
踩
OpenCV 作为一个世界上最大的计算机视觉库,里边包含了非常丰富的函数,开箱即用。不过 OpenCV 都是以应用程序直接运行的方式在计算机上运行,要想通过各种编程语言来操作,就需要开发者自行适配。本文将介绍基于 Java 语言 SpringBoot 框架集成 OpenCV 实现图片人脸检测功能。
OpenCV 官网: https://opencv.org
市面上集成 OpenCV 的 java 库有不少,这里推荐两个用户比较多的:
JavaCV: https://github.com/bytedeco/javacv
JavaCV 是一个综合的 Java 库,提供统一的操作方法,除了集成了 OpenCV 之外还包括 FFmpeg 等众多出名的算法库。
OpenPnP: https://github.com/openpnp/opencv
OpenPnP-OpenCV 是由 OpenPnP 组织提供的 OpenCV 的 Java 适配版本,这个是只有 OpenCV 的接口,依赖体积小。
本文使用 OpenPnP 提供的 Java 库。
demo-opencv/pom.xml
<!-- openCV 函数库 -->
<dependency>
<groupId>org.openpnp</groupId>
<artifactId>opencv</artifactId>
<version>${openpnp-opencv.version}</version>
</dependency>
其中 openpnp-opencv
的版本为:
<openpnp-opencv.version>4.9.0-0</openpnp-opencv.version>
这里 OpenPnP 的版本与 openCV 官方的版本保持了一致。
人脸特征值匹配文件:
demo-opencv/src/main/resources/opencv/haarcascade_frontalface_alt.xml
这个文件比较大,这里就不展示代码了,需要的直接到 OpenCV 官方下载。
OpenCV 官方人脸特征值文件: https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_frontalface_alt.xml
demo-opencv/src/main/java/com/ljq/demo/springboot/opencv/common/util/FaceDetectUtil.java
package com.ljq.demo.springboot.opencv.common.util; import lombok.extern.slf4j.Slf4j; import nu.pattern.OpenCV; import org.opencv.core.Mat; import org.opencv.core.MatOfByte; import org.opencv.core.MatOfRect; import org.opencv.core.Rect; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.objdetect.CascadeClassifier; import org.springframework.util.ResourceUtils; import java.io.File; import java.util.Objects; /** * @Description: 人脸检测工具 * @Author: junqiang.lu * @Date: 2024/3/15 */ @Slf4j public class FaceDetectUtil { private static CascadeClassifier faceDetector; /** * 初始化 opencv * * @param isDebug * @return */ public static CascadeClassifier init(boolean isDebug) { if (Objects.isNull(faceDetector) || faceDetector.empty()) { synchronized (FaceDetectUtil.class) { if (Objects.isNull(faceDetector) || faceDetector.empty()) { try { // 加载 openCV 函数库 OpenCV.loadShared(); log.info("opencv 函数库加载成功"); // 初始化人脸探测器 faceDetector = new CascadeClassifier(); String xmlFilePath = "opencv" + File.separator + "haarcascade_frontalface_alt.xml"; boolean detectorInitResult = false; if (isDebug) { File xmlFile = ResourceUtils.getFile("classpath:" + xmlFilePath); detectorInitResult = faceDetector.load(xmlFile.getAbsolutePath()); } else { detectorInitResult = faceDetector.load(System.getProperty("user.dir") + File.separator + xmlFilePath); } log.info("opencv 人脸检测工具加载结果: {}", detectorInitResult); } catch (Exception e) { log.error("opencv face check init error", e); } } } } return faceDetector; } /** * 检测本地照片中是否包含人脸 * * @param imgPath * @param isDebug * @return */ public static boolean detectFace(String imgPath, boolean isDebug) { init(isDebug); // 读取图片 Mat image = Imgcodecs.imread(imgPath); return detectFace(image, isDebug); } /** * 检测照片中是否包含人脸 * * @param byteArray * @param isDebug * @return */ public static boolean detectFace(byte[] byteArray, boolean isDebug) { init(isDebug); // 读取图片 Mat image = Imgcodecs.imdecode(new MatOfByte(byteArray), Imgcodecs.IMREAD_UNCHANGED); return detectFace(image, isDebug); } /** * 检测照片中是否包含人脸 * * @param image * @param isDebug * @return */ public static boolean detectFace(Mat image, boolean isDebug) { // 读取图片 if (image.empty()) { return false; } // 人脸特征值匹配 MatOfRect face = new MatOfRect(); init(isDebug).detectMultiScale(image, face); // 特征匹配 Rect[] rects = face.toArray(); if (rects.length > 0) { return true; } return false; } }
这里提供了三种图片人脸检测的方式,分别是本地文件,图片字节数组,以及 opencv 的 org.opencv.core.Mat
对象。
根据真实业务场景,用户上传图片,然后进行人脸检测。
demo-opencv/src/main/java/com/ljq/demo/springboot/opencv/controller/FaceDetectController.java
package com.ljq.demo.springboot.opencv.controller; import com.ljq.demo.springboot.opencv.common.config.OpencvConfig; import com.ljq.demo.springboot.opencv.common.util.FaceDetectUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.io.IOException; /** * @Description: 人脸检测控制层 * @Author: junqiang.lu * @Date: 2024/3/18 */ @Slf4j @RequestMapping(value = "/api/opencv") @RestController public class FaceDetectController { @Resource private OpencvConfig opencvConfig; /** * 人脸检测 * * @param file * @return */ @PostMapping(value = "/face/detect", produces = {MediaType.APPLICATION_JSON_VALUE}) public ResponseEntity<Object> detectFace(MultipartFile file){ boolean flag = false; try { flag = FaceDetectUtil.detectFace(file.getBytes(), opencvConfig.getOpencvDebug()); log.info("图片人脸检测结果: {}", flag); } catch (IOException e) { log.error("图片读取错误", e); } return ResponseEntity.ok(flag); } }
demo-opencv/src/main/java/com/ljq/demo/springboot/opencv/common/config/OpencvConfig.java
package com.ljq.demo.springboot.opencv.common.config; import lombok.Getter; import lombok.ToString; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; /** * @Description: opencv 配置 * @Author: junqiang.lu * @Date: 2024/3/18 */ @ToString @Getter @Configuration public class OpencvConfig { /** * 是否开启 opencv 调试模式 * true: 从项目resource文件中加载人脸特征xml,实际部署,以 jar 启动无法获取到 * false: 从本地文件加载人脸特征xml */ @Value("${opencv.debug:false}") private Boolean opencvDebug; }
demo-opencv/src/main/resources/application.yml
# config server: port: 9211 # spring spring: application: name: demo-opencv servlet: multipart: max-file-size: 1000MB max-request-size: 1000MB # opencv properties opencv: debug: true
OpenCV 函数库读取配置文件是必须按照文件的绝对路径来读取的,对于在 jar 包内的配置文件,是无法读取到的。因此在实际项目的部署中必须将人脸特征匹配文件外置。这也是上边专门设置一个 opencv 配置类的原因。
OpenCV 4.9 依赖的 GLIBC 版本为 2.27,CentOS 7 系统自带的 GLIBC 版本为 2.17,如果不手动安装高版本 GLIBC,程序将无法在 CentOS 7 上边运行。Windows 7 系统也无法运行,Windows 10 系统可以。
如果要在 CentOS 7 系统上直接运行,可以选择依赖的版本为 3.4.2-2。
作为 CentOS 系统的平行替代, Alma Linux 9.3 系统自带的 GLIBC 版本为 2.34,可以直接支持 OpenCV 4.9 运行。
Linux 系统查看 GLIBC 版本命令:
ldd --version
Linux 系统查看系统支持的 GLIBC 版本列表:
strings /lib64/libc.so.6 | grep GLIBC_
低版本 GLIBC 运行程序时抛出的异常如下:
2024-05-08 11:05:09 | ERROR | http-nio-9211-exec-1 | org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet] 175 | Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.UnsatisfiedLinkError: /tmp/opencv_openpnp7898379110605450114/nu/pattern/opencv/linux/x86_64/libopencv_java490.so: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by /tmp/opencv_openpnp7898379110605450114/nu/pattern/opencv/linux/x86_64/libopencv_java490.so)] with root cause
java.lang.UnsatisfiedLinkError: /tmp/opencv_openpnp7898379110605450114/nu/pattern/opencv/linux/x86_64/libopencv_java490.so: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by /tmp/opencv_openpnp7898379110605450114/nu/pattern/opencv/linux/x86_64/libopencv_java490.so)
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1941)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1857)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at nu.pattern.OpenCV$SharedLoader.<init>(OpenCV.java:225)
at nu.pattern.OpenCV$SharedLoader.<init>(OpenCV.java:189)
at nu.pattern.OpenCV$SharedLoader$Holder.<clinit>(OpenCV.java:261)
at nu.pattern.OpenCV$SharedLoader.getInstance(OpenCV.java:265)
at nu.pattern.OpenCV.loadShared(OpenCV.java:183)
at com.ljq.demo.springboot.opencv.common.util.FaceCheckUtil.init(FaceCheckUtil.java:40)
at com.ljq.demo.springboot.opencv.common.util.FaceCheckUtil.checkFace(FaceCheckUtil.java:85)
at com.ljq.demo.springboot.opencv.controller.FaceCheckController.checkFaceOpenpnp(FaceCheckController.java:39)
Intro to OpenCV with Java–Baeldung
Capturing Image From Webcam in Java – Baeldung
Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo/tree/master/demo-opencv
个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。