java+opencv实现人脸识别

背景:最近需要用到人脸识别,但又不花钱使用现有的第三方人脸识别接口,为此使用opencv结合java进行人脸识别(ps:opencv是开源的,使用它来做人脸识别存在一定的误差,效果一般)。

  1. 安装opencv
    官网地址:https://opencv.org/, 由于官网下载速度是真的慢
    为此这边是我下的百度云盘,opencv4.1.0,提取码1o36。
    如果是官网下载,就无脑安装就行了,安装完毕后。

将图一的两个文件复制到图二中。
java+opencv实现人脸识别_第1张图片
java+opencv实现人脸识别_第2张图片
从我网盘下载的,忽略这些。

  1. 在项目中引入pom依赖
<!-- opencv + javacv + ffmpeg-->
        <dependency>
            <groupId>org.bytedeco.javacpp-presets</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>4.1-1.4.4</version>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>1.4.4</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.bytedeco.javacpp-presets/ffmpeg-platform -->
        <dependency>
            <groupId>org.bytedeco.javacpp-presets</groupId>
            <artifactId>ffmpeg-platform</artifactId>
            <version>4.1-1.4.4</version>
        </dependency>

        <!-- 视频摄像头 -->
        <!-- https://mvnrepository.com/artifact/org.bytedeco/javacv-platform -->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv-platform</artifactId>
            <version>1.4.4</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.bytedeco.javacpp-presets/opencv-platform -->
        <dependency>
            <groupId>org.bytedeco.javacpp-presets</groupId>
            <artifactId>opencv-platform</artifactId>
            <version>4.0.1-1.4.4</version>
        </dependency>
  1. 导入库依赖
    File --> Project Structure,点击Modules,选择需要使用opencv.jar的项目。
    java+opencv实现人脸识别_第3张图片
    java+opencv实现人脸识别_第4张图片
    选择直接opencv安装路径
    java+opencv实现人脸识别_第5张图片
    java+opencv实现人脸识别_第6张图片

  2. java代码demo

package org.Litluecat.utils;

import org.apache.commons.lang.StringUtils;
import org.opencv.core.*;
import org.opencv.highgui.HighGui;
import org.opencv.highgui.ImageWindow;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.VideoWriter;
import org.opencv.videoio.Videoio;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;

/**
 * 人脸比对工具类
 * @author Litluecat
 * @Title: Opencv 图片人脸识别、实时摄像头人脸识别
**/
public class FaceVideo {

    private static final Logger log = LoggerFactory.getLogger(FaceVideo.class);

    private static final String endImgUrl = "C:\\Users\\lenovo\\Desktop\\";
    /**
     * opencv的人脸识别xml文件路径
     */
    private static final String faceDetectorXML2URL = "D:\\Sofeware\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml";
    /**
     * opencv的人眼识别xml文件路径
     */
    private static final String eyeDetectorXML2URL = "D:\\Sofeware\\opencv\\sources\\data\\haarcascades\\haarcascade_eye.xml";
    /**
     * 直方图大小,越大精度越高,运行越慢
     */
    private static int Matching_Accuracy = 100000;
    /**
     * 初始化人脸探测器
     */
    private static CascadeClassifier faceDetector;
    /**
     * 初始化人眼探测器
     */
    private static CascadeClassifier eyeDetector;

    private static int i=0;

    static {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        faceDetector = new CascadeClassifier(faceDetectorXML2URL);
        eyeDetector = new CascadeClassifier(eyeDetectorXML2URL);
    }

    public static void main(String[] args) {
        log.info("开始人脸匹配");
        long begin = System.currentTimeMillis();
        // 1- 从摄像头实时人脸识别,识别成功保存图片到本地
        try{
            getVideoFromCamera(endImgUrl + "2.png");
            //仅用于强制抛异常,从而关闭GUI界面
            Thread.sleep(1000);
            int err = 1/0;
     
            // 2- 比对本地2张图的人脸相似度 (越接近1越相似)
//            double compareHist = FaceVideo.compare_image(endImgUrl + "test1.png" , endImgUrl + "face.png");
//            log.info("匹配度:{}",compareHist);
//            if (compareHist > 0.72) {
//                log.info("人脸匹配");
//            } else {
//                log.info("人脸不匹配");
//            }

        }catch (Exception e){
            log.info("开始强制关闭");
            log.info("人脸匹配结束,总耗时:{}ms",(System.currentTimeMillis()-begin));
            System.exit(0);
        }
    }


    /**
     * OpenCV-4.1.1 从摄像头实时读取
     * @param targetImgUrl 比对身份证图片
     * @return: void
     * @date: 2019年8月19日 17:20:13
     */
    public static void getVideoFromCamera(String targetImgUrl) {
        //1 如果要从摄像头获取视频 则要在 VideoCapture 的构造方法写 0
        VideoCapture capture = new VideoCapture(0);
        Mat video = new Mat();
        int index = 0;
        if (capture.isOpened()) {
            while(i<3) {
                // 匹配成功3次退出
                capture.read(video);
                HighGui.imshow("实时人脸识别", getFace(video, targetImgUrl));
                //窗口延迟等待100ms,返回退出按键
                index = HighGui.waitKey(100);
                //当退出按键为Esc时,退出窗口
                if (index == 27) {
                    break;
                }
            }
        }else{
            log.info("摄像头未开启");
        }
        //该窗口销毁不生效,该方法存在问题
        HighGui.destroyAllWindows();
        capture.release();
        return;
    }

    /**
     * OpenCV-4.1.0 人脸识别
     * @param image 待处理Mat图片(视频中的某一帧)
     * @param targetImgUrl 匹配身份证照片地址
     * @return 处理后的图片
     */
    public static Mat getFace(Mat image, String targetImgUrl) {
        MatOfRect face = new MatOfRect();
        faceDetector.detectMultiScale(image, face);
        Rect[] rects=face.toArray();
        log.info("匹配到 "+rects.length+" 个人脸");
        if(rects != null && rects.length >= 1) {
            i++;
            if(i==3) {
                // 获取匹配成功第3次的照片
                Imgcodecs.imwrite(endImgUrl + "face.png", image);
                FaceVideoThread faceVideoThread = new FaceVideoThread(targetImgUrl , endImgUrl + "face.png");
                new Thread(faceVideoThread,"人脸比对线程").start();
            }
        }
        return image;
    }

    /**
     * 人脸截图
     * @param img
     * @return
     */
    public static String face2Img(String img) {
        String faceImg = null;
        Mat image0 = Imgcodecs.imread(img);
        Mat image1 = new Mat();
        // 灰度化
        Imgproc.cvtColor(image0, image1, Imgproc.COLOR_BGR2GRAY);
        // 探测人脸
        MatOfRect faceDetections = new MatOfRect();
        faceDetector.detectMultiScale(image1, faceDetections);
        // rect中人脸图片的范围
        for (Rect rect : faceDetections.toArray()) {
            faceImg = img+"_.jpg";
            // 进行图片裁剪
            imageCut(img, faceImg, rect.x, rect.y, rect.width, rect.height);
        }
        if(null == faceImg){
            log.info("face2Img未识别出该图像中的人脸,img={}",img);
        }
        return faceImg;
    }

    /**
     * 人脸比对
     * @param img_1
     * @param img_2
     * @return
     */
    public static double compare_image(String img_1, String img_2) {
        Mat mat_1 = conv_Mat(img_1);
        Mat mat_2 = conv_Mat(img_2);
        Mat hist_1 = new Mat();
        Mat hist_2 = new Mat();

        //颜色范围
        MatOfFloat ranges = new MatOfFloat(0f, 256f);
        //直方图大小, 越大匹配越精确 (越慢)
        MatOfInt histSize = new MatOfInt(Matching_Accuracy);

        Imgproc.calcHist(Arrays.asList(mat_1), new MatOfInt(0), new Mat(), hist_1, histSize, ranges);
        Imgproc.calcHist(Arrays.asList(mat_2), new MatOfInt(0), new Mat(), hist_2, histSize, ranges);

        // CORREL 相关系数
        double res = Imgproc.compareHist(hist_1, hist_2, Imgproc.CV_COMP_CORREL);
        return res;
    }

    /**
     * 灰度化人脸
     * @param img
     * @return
     */
    public static Mat conv_Mat(String img) {
        if(StringUtils.isBlank(img)){
            return null;
        }
        Mat image0 = Imgcodecs.imread(img);
        Mat image1 = new Mat();
        //Mat image2 = new Mat();
        // 灰度化
        Imgproc.cvtColor(image0, image1, Imgproc.COLOR_BGR2GRAY);
        //直方均匀
        //Imgproc.equalizeHist(image1, image2);

        // 探测人脸
        MatOfRect faceDetections = new MatOfRect();
        faceDetector.detectMultiScale(image1, faceDetections);

        //探测人眼
//        MatOfRect eyeDetections = new MatOfRect();
//        eyeDetector.detectMultiScale(image1, eyeDetections);

        // rect中人脸图片的范围
        Mat face = null;
        for (Rect rect : faceDetections.toArray()) {

            //给图片上画框框 参数1是图片 参数2是矩形 参数3是颜色 参数四是画出来的线条大小
            //Imgproc.rectangle(image0,rect,new Scalar(0,0,255),2);
            //输出图片
            //Imgcodecs.imwrite(img+"_.jpg",image0);

            face = new Mat(image1, rect);
        }
        if(null == face){
            log.info("conv_Mat未识别出该图像中的人脸,img={}",img);
        }
        return face;
    }

}

这边的人脸识别是另外其线程进行比对,代码如下。

package org.Litluecat.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class FaceVideoThread implements Runnable{
    private static final Logger log = LoggerFactory.getLogger(FaceVideoThread.class);

    private String oneImgUrl = null;
    private String otherImgUrl = null;
    public FaceVideoThread(String oneImgUrl, String otherImgUrl){
        this.oneImgUrl = oneImgUrl;
        this.otherImgUrl = otherImgUrl;
    }
    @Override
    public void run() {
        try {
            double compareHist = FaceVideo.compare_image(oneImgUrl , otherImgUrl);
            log.info("匹配度:{}",compareHist);
            if (compareHist > 0.72) {
                log.info("人脸匹配");
            } else {
                log.info("人脸不匹配");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

提醒:如果运行异常,请添加你opencv的安装地址-Djava.library.path=D:\Sofeware\opencv\build\java\x64;
java+opencv实现人脸识别_第7张图片

总结:java+opencv做人脸识别的精度不够,我也是有待学习,如果大家有更好的方式,能将opencv更好的展现出来,并达到更精准的人脸识别,请分享给我,谢谢。

你可能感兴趣的:(java,人脸识别,opencv,java)