当前位置:   article > 正文

SpringBoot 集成 OpenCV 实现人脸检测功能_java springboot整合opencv

java springboot整合opencv

1 摘要

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 库。

2 核心 Maven 依赖

demo-opencv/pom.xml
  • 1
        <!--  openCV 函数库 -->
        <dependency>
            <groupId>org.openpnp</groupId>
            <artifactId>opencv</artifactId>
            <version>${openpnp-opencv.version}</version>
        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其中 openpnp-opencv 的版本为:

<openpnp-opencv.version>4.9.0-0</openpnp-opencv.version>
  • 1

这里 OpenPnP 的版本与 openCV 官方的版本保持了一致。

3 核心代码

3.1 人脸特征值匹配文件

人脸特征值匹配文件:

demo-opencv/src/main/resources/opencv/haarcascade_frontalface_alt.xml
  • 1

这个文件比较大,这里就不展示代码了,需要的直接到 OpenCV 官方下载。

OpenCV 官方人脸特征值文件: https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_frontalface_alt.xml

3.2 人脸检测工具类

demo-opencv/src/main/java/com/ljq/demo/springboot/opencv/common/util/FaceDetectUtil.java
  • 1
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;
    }




}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117

这里提供了三种图片人脸检测的方式,分别是本地文件,图片字节数组,以及 opencv 的 org.opencv.core.Mat 对象。

3.3 人脸检测 Controller

根据真实业务场景,用户上传图片,然后进行人脸检测。

demo-opencv/src/main/java/com/ljq/demo/springboot/opencv/controller/FaceDetectController.java
  • 1
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);
    }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

3.4 其他相关代码

3.4.1 OpenCV 的配置类
demo-opencv/src/main/java/com/ljq/demo/springboot/opencv/common/config/OpencvConfig.java
  • 1
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;


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
3.4.2 application 配置文件
demo-opencv/src/main/resources/application.yml
  • 1
# 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

4 注意事项

4.1 人脸特征值匹配文件

OpenCV 函数库读取配置文件是必须按照文件的绝对路径来读取的,对于在 jar 包内的配置文件,是无法读取到的。因此在实际项目的部署中必须将人脸特征匹配文件外置。这也是上边专门设置一个 opencv 配置类的原因。

4.2 OpenCV 依赖的 GLIBC 版本问题

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
  • 1

Linux 系统查看系统支持的 GLIBC 版本列表:

strings  /lib64/libc.so.6 | grep GLIBC_
  • 1

低版本 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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

5 推荐参考资料

OpenPnP OpenCV Github

Intro to OpenCV with Java–Baeldung

Capturing Image From Webcam in Java – Baeldung

6 Github 源码

Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo/tree/master/demo-opencv

个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.
404Code

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/623232
推荐阅读
相关标签
  

闽ICP备14008679号