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
commit 057b58a7350b2e1f5ece9d6ffb5781ec70bd6fed (HEAD -> dev, origin/master, origin/dev, origin/HEAD, master)
Author: Flying9001 <[email protected]>
Date: Wed May 8 14:32:42 2024 +0800
代码-新增 SpringBoot 集成 OpenCV 实现图片人脸检测功能
版本回退命令
git reset --soft 057b58a7350b2e1f5ece9d6ffb5781ec70bd6fed