第一步:在项目中pom.xml 文件配置库。

<!-- OpenCV Java绑定 -->
<dependency>
<groupId>org.openpnp</groupId>
<artifactId>opencv</artifactId>
<version>4.9.0-0</version>
</dependency>

第二步:下载人脸识别模型

  1. 人脸识别模型:face_recognition_sface_2021dec.onnx

  2. 人脸检测模型:face_detection_yunet_2023mar.onnx

  3. 下载地址:

    通过网盘分享的文件:opencvModels.rar
    链接: https://pan.baidu.com/s/12gaUpC9e6zN7p2C-PpOvKg?pwd=syr6

第三步:java想关代码

package com.jeeplus.modules.sys.service;

import nu.pattern.OpenCV;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.objdetect.FaceDetectorYN;
import org.opencv.objdetect.FaceRecognizerSF;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Base64;

@Service
@Transactional(readOnly = true)
public class FaceRecognitionService {
    // consine距离阈值
    private static double cosine_similar_threshold = 0.7;

    // 人脸检测模型的本地全路径
    private static String faceDetectModelPath = "C:\\zl_models\\face_detection_yunet_2023mar.onnx";
    // 声明人脸检测对象
    private static FaceDetectorYN faceDetector = null;

    // 人脸识别模型的本地全路径
    private static String faceRecognizModelPath =  "C:\\zl_models\\face_recognition_sface_2021dec.onnx";
    // 声明人脸识别对象
    private static FaceRecognizerSF faceRecognizer = null;

    public String faceRecognizer(String imgPathA, String imgPathB) throws IOException {
        // 加载opencv
        OpenCV.loadLocally();
        // 加载人脸检测模型
        loadFaceDetector();
        // 加载人脸识别模型
        loadFaceRecognizer();

        return faceRecognizerUtil(imgPathA, imgPathB);
    }

    /**
     * 加载人脸检测模型
     */
    private static void loadFaceDetector() {
        if (faceDetector != null) {
            return;
        }
        // 你需要传入人脸检测模型的本地全路径
        faceDetector = FaceDetectorYN.create(faceDetectModelPath, "", new Size());
    }

    /**
     * 加载人脸识别模型
     */
    private static void loadFaceRecognizer() {
        if (faceRecognizer != null) {
            return;
        }
        // 你需要传入人脸识别模型的本地全路径
        faceRecognizer = FaceRecognizerSF.create(faceRecognizModelPath, "");
    }

    /**
     * 人脸识别. 比对两张图片的相似程度
     *
     * @param imgPathA imgABase64
     * @param imgPathB imgB路径
     * @return 返回是否一致
     */
    public static String faceRecognizerUtil(String imgPathA, String imgPathB)  {
        String result = "";
        try {
            // 将网络地址转换为Mat对象
            Mat imgA = base64ToMat(imgPathA.split(",")[1]);
            Mat imgB = urlToMat(imgPathB);

            // 2.检测人脸信息
            Mat faceA = new Mat();
            faceDetector.setInputSize(imgA.size());
            faceDetector.detect(imgA, faceA);
            Mat faceB = new Mat();
            faceDetector.setInputSize(imgB.size());
            faceDetector.detect(imgB, faceB);
            if (faceA.rows()>0 && faceB.rows()>0) { // 人脸检测成功
                // 3.对齐图像以将脸放在标准位置
                Mat alignFaceA = new Mat();
                faceRecognizer.alignCrop(imgA, faceA.row(0), alignFaceA);
                Mat alignFaceB = new Mat();
                faceRecognizer.alignCrop(imgB, faceB.row(0), alignFaceB);

                // 4.从对齐图像中提取人脸特征
                Mat featureA = new Mat();
                faceRecognizer.feature(alignFaceA, featureA);
                featureA = featureA.clone();
                Mat featureB = new Mat();
                faceRecognizer.feature(alignFaceB, featureB);
                featureB = featureB.clone();

                // 5.人脸识别。计算两个面要素之间的距离
                //  获取cosine相似度
                double match1 = faceRecognizer.match(featureA, featureB, FaceRecognizerSF.FR_COSINE);
                System.out.println("人脸数据库中数据:" + imgPathB + "与拍照人脸的相似度:" + match1);
                // 获取l2norm相似度
//             double match2 = faceRecognizer.match(featureA, featureB, FaceRecognizerSF.FR_NORM_L2);
                if (match1 >= cosine_similar_threshold) {
                    result = match1+","+true;
                } else {
                    result = match1+","+false;
                }
            }else {
                result = 0+","+false;
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return 0+","+false;
        }
    }
    /**
     * 将网络地址转换为Mat对象
     * @param imgUrl 网络地址
     * @return Mat对象
     * @throws Exception
     */
    private static Mat urlToMat(String imgUrl) throws Exception {
        URL url = new URL(imgUrl);
        InputStream in = url.openStream();
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        int nRead;
        byte[] data = new byte[16384]; // 16KB buffer size

        while ((nRead = in.read(data, 0, data.length)) != -1) {
            buffer.write(data, 0, nRead);
        }

        in.close();
        byte[] bytes = buffer.toByteArray();

        MatOfByte matOfByte = new MatOfByte();
        matOfByte.fromArray(bytes);
        return Imgcodecs.imdecode(matOfByte, Imgcodecs.IMREAD_COLOR);
    }
    /**
     * 将base64字符串转换为Mat对象
     * @param base64Str base64字符串
     * @return Mat对象
     * @throws Exception
     * 注意:这里的base64字符串是不含前缀的,即不含data:image/jpeg;base64,
     */
    private static Mat base64ToMat(String base64Str) throws Exception {
        byte[] decodedBytes = Base64.getDecoder().decode(base64Str);
        MatOfByte matOfByte = new MatOfByte();
        matOfByte.fromArray(decodedBytes);
        return Imgcodecs.imdecode(matOfByte, Imgcodecs.IMREAD_COLOR);
    }
}