Java는 OpenCV로 얼굴 인식 로그인의 완전한 예를 구현합니다.

골든 스톤 프로젝트의 첫 번째 챌린지에 참여하기 위해 등록했습니다. 100,000 상금 풀을 공유합니다. 첫 번째 기사 입니다. 이벤트 세부 정보를 보려면 클릭하세요.

OpenCV

효과 미리보기

여기에 이미지 설명 삽입

개요

OpenCV(Open Source Computer Vision Library)는 BSD(Open Source License) 라이선스에 따라 출시됩니다. 실시간 애플리케이션에 중점을 둔 고도로 최적화된 라이브러리입니다. C++, Python 및 Java 인터페이스가 있으며 Windows, Linux, Mac OS, iOS 및 Android를 지원합니다.

다운로드 및 설치

다운로드 링크:https://opencv.org/releases/

여기에 이미지 설명 삽입

로컬로 다운로드 후 더블클릭하여 설치

여기에 이미지 설명 삽입

디렉토리 설명

설치 디렉토리는 다음과 같습니다

여기에 이미지 설명 삽입

build :基于window构建

sources:开源,提供源码
复制代码

빌드 디렉토리 설명

여기에 이미지 설명 삽입

다음은 Java 디렉토리에 대한 Java 개발 초점입니다.

여기에 이미지 설명 삽입

x64与x86代表给不同的系统使用

opencv-460.jar给java操作openvc的程序包
复制代码

64비트 시스템이므로 x64 디렉토리에 주의

여기에 이미지 설명 삽입

DLL(Dynamic Link Library) 파일은 소프트웨어 파일 유형인 "응용 프로그램 확장자"라고도 하는 동적 링크 라이브러리 파일입니다. 시스템에 배치된 DLL 파일. 프로그램이 실행되면 해당 DLL 파일이 호출됩니다.

OpenCV의 기본 사용

공식 웹사이트 문서 주소:https://docs.opencv.org/4.6.0/df/d65/tutorial_table_of_content_introduction.html

중국어 문서:http://wiki.opencv.org.cn/index.php

튜토리얼 참조:https://www.w3cschool.cn/opencv/

튜토리얼 참조:https://www.yiibai.com/opencv/opencv_adding_text.html

프로젝트 통합

여기에서 개발에 IDEA를 사용하고 opencv-460.jar 라이브러리를 가져옵니다.

단축키 Ctrl+Shift+Alt+S를 사용하여 라이브러리 항목을 여기에 이미지 설명 삽입선택 하고 Java 라이브러리를 가져옵니다.여기에 이미지 설명 삽입 여기에 이미지 설명 삽입

위의 방법 외에도 opencv-460.jar로컬 저장소 또는 개인 저장소에 설치한 다음 pom.xml에 종속성을 도입할 수도 있습니다.

이미지 얼굴 감지

    public static void main(String[] args) {
        imageFaceDetection();
    }


    /**
     * 图片人脸检测
     */
    public static void imageFaceDetection() {
        // 加载OpenCV本地库
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        // 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
        CascadeClassifier faceDetector = new CascadeClassifier("D:\\Development\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
        // 读取测试图片
        String imgPath = "D:\\user\\test.png";
        Mat image = Imgcodecs.imread(imgPath);
        if (image.empty()) {
            throw new RuntimeException("图片内存为空");
        }

        // 检测脸部
        MatOfRect face = new MatOfRect();
        // 检测图像中的人脸
        faceDetector.detectMultiScale(image, face);
        // 匹配Rect矩阵
        Rect[] rects = face.toArray();
        System.out.println("识别人脸个数: " + rects.length);
        
        // 识别图片中的所以人脸并分别保存
        int i = 1;
        for (Rect rect : face.toArray()) {
            Imgproc.rectangle(image, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0), 3);
            // 进行图片裁剪
            imageCut(imgPath, "D:\\user\\" + i + ".jpg", rect.x, rect.y, rect.width, rect.height);
            i++;
        }
        // 图片中人脸画框保存到本地
        Imgcodecs.imwrite("D:\\user\\test1.png", image);

        // 展示图片
        HighGui.imshow("人脸识别", image);
        HighGui.waitKey(0);
    }

    /**
     * 裁剪人脸
     *
     * @param readPath 读取文件路径
     * @param outPath  写出文件路径
     * @param x        坐标X
     * @param y        坐标Y
     * @param width    截图宽度
     * @param height   截图长度
     */
    public static void imageCut(String readPath, String outPath, int x, int y, int width, int height) {
        // 原始图像
        Mat image = Imgcodecs.imread(readPath);
        // 截取的区域
        Rect rect = new Rect(x, y, width, height);
        // Mat sub = new Mat(image,rect);
        Mat sub = image.submat(rect);
        Mat mat = new Mat();
        Size size = new Size(width, height);
        // 人脸进行截图并保存
        Imgproc.resize(sub, mat, size);
        Imgcodecs.imwrite(outPath, mat);
    }
复制代码

여기에 이미지 설명 삽입

얼굴 대비 유사도

对比1.jpg与1-1.jpg

对比1.jpg与3.jpg
复制代码

여기에 이미지 설명 삽입

    // 初始化人脸探测器
    static CascadeClassifier faceDetector;

    static {
        // 加载OpenCV本地库
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        // 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
        faceDetector = new CascadeClassifier("D:\\Development\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
    }
    
    public static void main(String[] args) {
        double comparison = faceRecognitionComparison("D:\\user\\1.jpg", "D:\\user\\1-1.jpg");
        System.out.println("对比结果:" + comparison);
        if (comparison > 0.85) {
            System.out.println("人脸匹配成功");
        } else {
            System.out.println("人脸不匹配识别");
        }

        double comparison2 = faceRecognitionComparison("D:\\user\\1.jpg", "D:\\user\\3.jpg");
        System.out.println("对比结果:" + comparison2);
        if (comparison2 > 0.85) {
            System.out.println("人脸匹配成功");
        } else {
            System.out.println("人脸不匹配识别");
        }
        // 终止当前运行的 Java 虚拟机。
        System.exit(0);
    }


    /**
     * 人脸识别比对
     */
    public static double faceRecognitionComparison(String image1, String image2) {
        Mat mat1 = conv_Mat(image1);
        Mat mat2 = conv_Mat(image2);
        Mat mat3 = new Mat();
        Mat mat4 = new Mat();
        // 颜色范围
        MatOfFloat ranges = new MatOfFloat(0f, 256f);
        // 直方图大小, 越大匹配越精确 (越慢)
        MatOfInt histSize = new MatOfInt(1000);

        Imgproc.calcHist(Arrays.asList(mat1), new MatOfInt(0), new Mat(), mat3, histSize, ranges);
        Imgproc.calcHist(Arrays.asList(mat2), new MatOfInt(0), new Mat(), mat4, histSize, ranges);

        // 比较两个密集或两个稀疏直方图
        return Imgproc.compareHist(mat3, mat4, Imgproc.CV_COMP_CORREL);
    }

    /**
     * 灰度化人脸
     */
    public static Mat conv_Mat(String img) {
        // 读取图像
        Mat mat1 = Imgcodecs.imread(img);
        Mat mat2 = new Mat();
        // 灰度化:将图像从一种颜色空间转换为另一种颜色空间
        Imgproc.cvtColor(mat1, mat2, Imgproc.COLOR_BGR2GRAY);
        // 探测人脸:检测到的对象作为矩形列表返回
        MatOfRect faceDetections = new MatOfRect();
        faceDetector.detectMultiScale(mat1, faceDetections);
        // rect中人脸图片的范围
        for (Rect rect : faceDetections.toArray()) {
            Mat face = new Mat(mat1, rect);
            return face;
        }
        return null;
    }
复制代码

비교 결과는 다음과 같다

对比结果:1.0
人脸匹配成功
对比结果:0.2501351968792374
人脸不匹配识别
复制代码

비디오에서 얼굴 식별

    // 初始化人脸探测器
    static CascadeClassifier faceDetector;

    static {
        // 加载OpenCV本地库
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        // 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
        faceDetector = new CascadeClassifier("D:\\Development\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
    }

    public static void main(String[] args) {
        videoFaceRecognition();
        // 终止当前运行的 Java 虚拟机。
        System.exit(0);
    }

    /**
     * 从视频中识别人脸
     */
    public static void videoFaceRecognition() {
        // 读取视频文件
        VideoCapture capture = new VideoCapture();
        capture.open("D:\\user\\test.mp4");
        if (!capture.isOpened()) {
            throw new RuntimeException("读取视频文件失败");
        }

        Mat video = new Mat();
        int index = 0;
        while (capture.isOpened()) {
            // 抓取、解码并返回下一个视频帧写入Mat对象中
            capture.read(video);
            // 显示从视频中识别的人脸图像
            HighGui.imshow("视频识别人脸", getFace(video));
            // 获取键盘输入
            index = HighGui.waitKey(100);
            // 如果是 Esc 则退出
            if (index == 27) {
                capture.release();
                return;
            }
        }
    }

    /**
     * 从视频帧中识别人脸
     *
     * @param image 待处理Mat图片,即视频中的某一帧
     * @return 处理后的图片
     */
    public static Mat getFace(Mat image) {
        MatOfRect face = new MatOfRect();
        // 检测输入图像中不同大小的对象。检测到的对象作为矩形列表返回。
        faceDetector.detectMultiScale(image, face);
        Rect[] rects = face.toArray();
        System.out.println("识别人脸个数: " + rects.length);
        if (rects.length > 0 && Math.random() * 10 > 8) {
            Imgcodecs.imwrite("D:\\user\\" + UUID.randomUUID() + ".png", image);
        }

        if (rects != null && rects.length >= 1) {
            // 为每张识别到的人脸画一个圈
            for (int i = 0; i < rects.length; i++) {
                /**
                 * 绘制一个简单的、粗的或填充的直角矩形
                 *
                 * img 图像
                 * pt1 - 矩形的顶点
                 * pt2 - 与 pt1 相对的矩形的顶点
                 * color – 矩形颜色或亮度(灰度图像)意味着该函数必须绘制一个填充的矩形。
                 */
                Imgproc.rectangle(image, new Point(rects[i].x, rects[i].y), new Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), new Scalar(0, 255, 0));
                /**
                 * 绘制一个文本字符串,放在识别人脸框上
                 *
                 * img -- 图像
                 * text -- 要绘制的文本字符串
                 * org – 图像中文本字符串的左下角
                 * fontFace – 字体类型,请参阅#HersheyFonts
                 * fontScale – 字体比例因子乘以特定字体的基本大小
                 * color - 文本颜色
                 * thickness ——用于绘制文本的线条粗细
                 * lineType – 线型
                 * bottomLeftOrigin – 当为 true 时,图像数据原点位于左下角。否则,它位于左上角
                 */
                Imgproc.putText(image, "test", new Point(rects[i].x, rects[i].y), Imgproc.FONT_HERSHEY_SCRIPT_SIMPLEX, 1.0, new Scalar(0, 255, 0), 1, Imgproc.LINE_AA, false);
            }
        }
        return image;
    }

复制代码

여기에 이미지 설명 삽입

카메라가 얼굴을 인식

    // 初始化人脸探测器
    static CascadeClassifier faceDetector;

    static {
        // 加载OpenCV本地库
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        // 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
        faceDetector = new CascadeClassifier("D:\\Development\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
    }

    public static void main(String[] args) throws Exception {
        cameraFaceRecognition();
        // 终止当前运行的 Java 虚拟机。
        System.exit(0);
    }

    /**
     * 摄像头实时人脸识别
     *
     * @throws Exception
     */
    public static void cameraFaceRecognition() throws Exception {
        // 打开摄像头获取视频流,0 打开默认摄像头
        VideoCapture videoCapture = new VideoCapture(0);
        // 检查是否支持摄像头  true:代表摄像头可以打开  false:不可以打开
        System.out.println(videoCapture.isOpened());
        // 获取摄像头高度
        int height = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT);
        // 获取摄像头宽度
        int width = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH);
        if (height == 0 || width == 0) {
            throw new Exception("摄像头不存在");
        }

        Mat video = new Mat();
        int index = 0;
        if (videoCapture.isOpened()) {
            while (true) {
                videoCapture.read(video);
                HighGui.imshow("实时人脸识别", getFace(video));
                // 键盘输入
                index = HighGui.waitKey(50);
                // 是Esc则退出,比强制退出好
                if (index == 27) {
                    // 写入人脸
                    Imgcodecs.imwrite("D:\\user\\" + "face.png", video);
                    videoCapture.release();
                    return;
                }
            }
        }
    }

    /**
     * 从视频帧中识别人脸
     *
     * @param image 待处理Mat图片,即视频中的某一帧
     * @return 处理后的图片
     */
    public static Mat getFace(Mat image) {
        MatOfRect face = new MatOfRect();
        // 检测输入图像中不同大小的对象。检测到的对象作为矩形列表返回。
        faceDetector.detectMultiScale(image, face);
        Rect[] rects = face.toArray();
        System.out.println("识别人脸个数: " + rects.length);

        if (rects != null && rects.length >= 1) {
            // 为每张识别到的人脸画一个圈
            for (int i = 0; i < rects.length; i++) {
                // 绘制一个简单的、粗的或填充的直角矩形
                Imgproc.rectangle(image, new Point(rects[i].x, rects[i].y), new Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), new Scalar(0, 255, 0));
                // 绘制一个文本字符串,放在识别人脸框上
                Imgproc.putText(image, "test", new Point(rects[i].x, rects[i].y), Imgproc.FONT_HERSHEY_SCRIPT_SIMPLEX, 1.0, new Scalar(0, 255, 0), 1, Imgproc.LINE_AA, false);
            }
        }
        return image;
    }
复制代码

사용자 정의 창

OpenCV가 있는 HighGUI 그래픽 사용자 인터페이스는 구성 가능한 매개변수가 너무 적기 때문에 사용자 정의 가능한 창이 대신 사용됩니다.

import org.opencv.core.Point;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.Videoio;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;

public class MyJPanel extends JPanel {
    private BufferedImage mImg;
    // 初始化人脸探测器
    static CascadeClassifier faceDetector;

    static VideoCapture videoCapture;
    static JFrame frame;

    static {
        // 加载OpenCV本地库
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        // 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
        faceDetector = new CascadeClassifier("D:\\Development\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
    }


    public void paintComponent(Graphics g) {
        if (mImg != null) {
            g.drawImage(mImg, 0, 0, mImg.getWidth(), mImg.getHeight(), this);
        }
    }


    /**
     * 摄像头识别人脸
     */
    public static void cameraFaceRecognition() throws Exception {
        try {
            // 打开摄像头获取视频流,0 打开默认摄像头
            videoCapture = new VideoCapture(0);
            // 检查是否支持摄像头  true:代表摄像头可以打开  false:不可以打开
            System.out.println(videoCapture.isOpened());
            // 获取摄像头高度
            int height = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT);
            // 获取摄像头宽度
            int width = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH);
            if (height == 0 || width == 0) {
                throw new Exception("摄像头不存在");
            }

            //使用Swing生成GUI
            frame = new JFrame("人脸识别");
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            MyJPanel panel = new MyJPanel();
            //设置中心显示
            frame.setContentPane(panel);
            frame.setVisible(true);
            frame.setSize(width + frame.getInsets().left + frame.getInsets().right, height + frame.getInsets().top + frame.getInsets().bottom);
            frame.setLocationRelativeTo(null);

            // 创建矩阵
            Mat capImg = new Mat();
            // 创建一个临时矩阵
            Mat temp = new Mat();
            while (frame.isShowing()) {
                //从摄像头读取一帧数据,保存到capImg矩阵中。
                videoCapture.read(capImg);
                //转换为彩色图
                Imgproc.cvtColor(capImg, temp, Imgproc.COLOR_RGBA2BGRA);
                // 人脸识别
                capImg = getFace(capImg);
                // 本地图片保存
                Imgcodecs.imwrite("D:\\user\\1.jpg", capImg);
                //转为图像显示
                panel.mImg = panel.matToImage(capImg);
                // 重绘此组件
                panel.repaint();
            }
        } finally {
            // 关闭摄像头
            videoCapture.release();
            frame.dispose();
        }

    }


    /**
     * 从视频帧中识别人脸
     *
     * @param image 待处理Mat图片,即视频中的某一帧
     * @return 处理后的图片
     */
    public static Mat getFace(Mat image) {
        MatOfRect face = new MatOfRect();
        // 检测输入图像中不同大小的对象。检测到的对象作为矩形列表返回。
        faceDetector.detectMultiScale(image, face);
        Rect[] rects = face.toArray();
        System.out.println("识别人脸个数: " + rects.length);

        if (rects != null && rects.length >= 1) {
            // 为每张识别到的人脸画一个圈
            for (int i = 0; i < rects.length; i++) {
                // 绘制一个简单的、粗的或填充的直角矩形
                Imgproc.rectangle(image, new org.opencv.core.Point(rects[i].x, rects[i].y), new org.opencv.core.Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), new Scalar(0, 255, 0));
                // 绘制一个文本字符串,放在识别人脸框上
                Imgproc.putText(image, "test", new Point(rects[i].x, rects[i].y), Imgproc.FONT_HERSHEY_SCRIPT_SIMPLEX, 1.0, new Scalar(0, 255, 0), 1, Imgproc.LINE_AA, false);
            }
        }
        return image;
    }

    /**
     * 转换图像
     */
    private BufferedImage matToImage(Mat mat) {
        int dataSize = mat.cols() * mat.rows() * (int) mat.elemSize();
        byte[] data = new byte[dataSize];
        mat.get(0, 0, data);
        int type = mat.channels() == 1 ? BufferedImage.TYPE_BYTE_GRAY : BufferedImage.TYPE_3BYTE_BGR;
        if (type == BufferedImage.TYPE_3BYTE_BGR) {
            for (int i = 0; i < dataSize; i += 3) {
                byte blue = data[i + 0];
                data[i + 0] = data[i + 2];
                data[i + 2] = blue;
            }
        }
        BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
        image.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), data);
        return image;
    }
}
复制代码

카메라가 비디오를 캡처하고 로컬에 씁니다.

    public static void main(String[] args) throws Exception {
        MyJPanel.cameraFaceRecognition();
        // 终止当前运行的 Java 虚拟机。
        System.exit(0);
    }
复制代码
    // 初始化人脸探测器
    static CascadeClassifier faceDetector;

    static BufferedImage mImg;

    static {
        // 加载OpenCV本地库
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        // 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
        faceDetector = new CascadeClassifier("D:\\Development\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
    }

    public static void main(String[] args) throws Exception {
        writeVideo();
        // 终止当前运行的 Java 虚拟机。
        System.exit(0);
    }

    /**
     * 摄像头拍摄视频写入本地
     */
    public static void writeVideo() throws Exception {
        // 打开摄像头获取视频流,0 打开默认摄像头
        VideoCapture videoCapture = new VideoCapture(0);
        // 检查是否支持摄像头  true:代表摄像头可以打开  false:不可以打开
        System.out.println(videoCapture.isOpened());
        // 获取摄像头高度
        int height = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT);
        // 获取摄像头宽度
        int width = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH);
        if (height == 0 || width == 0) {
            throw new Exception("摄像头不存在");
        }

        Mat video = new Mat();
        int index = 0;
        Size size = new Size(videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH), videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT));
        VideoWriter writer = new VideoWriter("D:\\user\\1.mp4", VideoWriter.fourcc('D', 'I', 'V', 'X'), 30.0, size, true);
        while (videoCapture.isOpened()) {
            //从摄像头读取一帧数据,保存到capImg矩阵中。
            videoCapture.read(video);
            writer.write(video);
            HighGui.imshow("视频人脸识别", video);
            // 获取键盘输入
            index = HighGui.waitKey(100);
            // 是Esc则退出,若强制退出将导致录制视频无法播放
            if (index == 27) {
                videoCapture.release();
                writer.release();
                return;
            }
        }
    }
复制代码

Spring Boot는 OpenCV를 통합합니다.

종속성 추가

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>
复制代码

프로젝트 통합 OpenCV

프로젝트 통합 OpenCV는 OpenCV的基本使用위 참조项目集成

요청 인터페이스

@Controller
@RequestMapping("/user")
public class UserFaceLogin  {

    @Autowired
    private MyJPanel myJPanel;

    @RequestMapping("/login")
    public String login() throws Exception {
        // 调用摄像头显示
        boolean  result = myJPanel.cameraFaceRecognition();
        if (result) {
            return "/success.html";
        } else {
            return "/error.html";
        }
    }
}
复制代码

application.yml 구성

개발 환경과 생산 환경은 구별되어야 합니다.

opencv:
  lib:
    linuxxmlpath: /usr/local/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml
    windowxmlpath: D:\Development\opencv\sources\data\haarcascades\haarcascade_frontalface_alt.xml
复制代码

가상 머신 매개변수 지정

-Djava.library.path=D:\Development\opencv\build\java\x64


-Djava.library.path=D:\Development\opencv\build\java\x64;D:\Development\opencv\build\x64\vc15\bin
复制代码

OpenCvUtil

초기화 작업을 완료하고 얼굴 일치 기능을 추가하면 이 도구 클래스에 더 많은 기능을 확장할 수 있습니다.

@Component
public class OpenCvUtil implements CommandLineRunner {
    // 初始化人脸探测器
    static CascadeClassifier faceDetector;

    @Value("${opencv.lib.linuxxmlpath}")
    private String linuxXmlPath;
    @Value("${opencv.lib.windowxmlpath}")
    private String windowXmlPath;

    /**
     * 判断是否是Windows系统
     */
    private static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().contains("win");


    @Override
    public void run(String... args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        String path = "";
        if (IS_WINDOWS) {
            path = windowXmlPath;
        } else {
            path = linuxXmlPath;
        }
        /**
         * 初始化人脸探测器
         */
        faceDetector = new CascadeClassifier(path);
    }

    public static int match(String loginImagePath, String comparedImagePath) {
        Mat mat1 = conv_Mat(loginImagePath);
        if (mat1 == null) {
            return 0;
        }

        Mat mat2 = conv_Mat(comparedImagePath);
        Mat mat3 = new Mat();
        Mat mat4 = new Mat();
        // 颜色范围
        MatOfFloat ranges = new MatOfFloat(0f, 256f);
        // 直方图大小, 越大匹配越精确 (越慢)
        MatOfInt histSize = new MatOfInt(1000);

        Imgproc.calcHist(Arrays.asList(mat1), new MatOfInt(0), new Mat(), mat3, histSize, ranges);
        Imgproc.calcHist(Arrays.asList(mat2), new MatOfInt(0), new Mat(), mat4, histSize, ranges);

        // 比较两个密集或两个稀疏直方图
        Double score = Imgproc.compareHist(mat3, mat4, Imgproc.CV_COMP_CORREL);
        System.out.println("score " + score);
        if (score >= 0.8) {
            return 1;
        }
        return 0;
    }

    public static Mat conv_Mat(String img) {
        // 读取图像
        Mat mat1 = Imgcodecs.imread(img);
        Mat mat2 = new Mat();
        // 灰度化:将图像从一种颜色空间转换为另一种颜色空间
        Imgproc.cvtColor(mat1, mat2, Imgproc.COLOR_BGR2GRAY);
        // 探测人脸:检测到的对象作为矩形列表返回
        MatOfRect faceDetections = new MatOfRect();
        faceDetector.detectMultiScale(mat1, faceDetections);
        // rect中人脸图片的范围
        for (Rect rect : faceDetections.toArray()) {
            Mat face = new Mat(mat1, rect);
            return face;
        }
        return null;
    }
}
复制代码

사용자 정의 창

사용자 정의 창은 실시간으로 카메라 샷을 얻는 데 사용됩니다.

@Component
public class MyJPanel extends JPanel {

    @Autowired
    private OpenCvUtil openCvUtil;

    private BufferedImage mImg;

    private VideoCapture videoCapture;

    private JFrame frame;

    public void paintComponent(Graphics g) {
        if (mImg != null) {
            g.drawImage(mImg, 0, 0, mImg.getWidth(), mImg.getHeight(), this);
        }
    }


    /**
     * 摄像头识别人脸
     */
    public Boolean cameraFaceRecognition() throws Exception {
        try {
            // 打开摄像头获取视频流,0 打开默认摄像头
            videoCapture = new VideoCapture(0);
            // 检查是否支持摄像头  true:代表摄像头可以打开  false:不可以打开
            System.out.println(videoCapture.isOpened());
            // 获取摄像头高度
            int height = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT);
            // 获取摄像头宽度
            int width = (int) videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH);
            if (height == 0 || width == 0) {
                throw new Exception("摄像头不存在");
            }

            // 使用Swing生成GUI
            frame = new JFrame("人脸识别");
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            MyJPanel panel = new MyJPanel();
            //设置中心显示
            frame.setContentPane(panel);
            frame.setVisible(true);
            frame.setSize(width + frame.getInsets().left + frame.getInsets().right, height + frame.getInsets().top + frame.getInsets().bottom);
            frame.setLocationRelativeTo(null);

            // 创建矩阵
            Mat capImg = new Mat();
            // 创建一个临时矩阵
            Mat temp = new Mat();
            // 对比图片
            String comparedImagePath = "D:\\user\\" + "compared.jpg";
            // 摄像头拍摄图片
            String loginImagePath = "D:\\user\\" + "login.jpg";
            int tag = 0;
            while (frame.isShowing() && tag < 5) {
                tag++;
                //从摄像头读取一帧数据,保存到capImg矩阵中。
                videoCapture.read(capImg);
                //转换为彩色图
                Imgproc.cvtColor(capImg, temp, Imgproc.COLOR_RGBA2BGRA);
                // 人脸识别
                capImg = this.getFace(capImg);
                // 本地图片保存
                Imgcodecs.imwrite(loginImagePath, capImg);
                //转为图像显示
                panel.mImg = panel.matToImage(capImg);
                // 重绘组件
                panel.repaint();
                int result = OpenCvUtil.match(loginImagePath, comparedImagePath);
                if (result == 1) {
                    return true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭窗口
            if (frame != null) {
                frame.dispose();
            }
            //  关闭摄像头
            if (videoCapture != null) {
                videoCapture.release();
            }
        }
        return false;
    }


    /**
     * 从视频帧中识别人脸
     *
     * @param image 待处理Mat图片,即视频中的某一帧
     * @return 处理后的图片
     */
    public Mat getFace(Mat image) {
        MatOfRect face = new MatOfRect();
        // 检测输入图像中不同大小的对象。检测到的对象作为矩形列表返回。
        openCvUtil.faceDetector.detectMultiScale(image, face);
        Rect[] rects = face.toArray();
        System.out.println("识别人脸个数: " + rects.length);

        if (rects != null && rects.length >= 1) {
            // 为每张识别到的人脸画一个圈
            for (int i = 0; i < rects.length; i++) {
                // 绘制一个简单的、粗的或填充的直角矩形
                Imgproc.rectangle(image, new org.opencv.core.Point(rects[i].x, rects[i].y), new org.opencv.core.Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), new Scalar(0, 255, 0));
                // 绘制一个文本字符串,放在识别人脸框上
                Imgproc.putText(image, "test", new Point(rects[i].x, rects[i].y), Imgproc.FONT_HERSHEY_SCRIPT_SIMPLEX, 1.0, new Scalar(0, 255, 0), 1, Imgproc.LINE_AA, false);
            }
        }
        return image;
    }

    /**
     * 转换图像
     */
    private BufferedImage matToImage(Mat mat) {
        int dataSize = mat.cols() * mat.rows() * (int) mat.elemSize();
        byte[] data = new byte[dataSize];
        mat.get(0, 0, data);
        int type = mat.channels() == 1 ? BufferedImage.TYPE_BYTE_GRAY : BufferedImage.TYPE_3BYTE_BGR;
        if (type == BufferedImage.TYPE_3BYTE_BGR) {
            for (int i = 0; i < dataSize; i += 3) {
                byte blue = data[i + 0];
                data[i + 0] = data[i + 2];
                data[i + 2] = blue;
            }
        }
        BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
        image.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), data);
        return image;
    }
}
复制代码

페이지 만들기

얼굴 로그인을 시뮬레이트하는 Index.html 페이지를 만들고 얼굴 로그인에 성공한 점프 페이지 success.html과 얼굴 로그인 실패 점프 페이지 error.html을 만듭니다.

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="index" class="tab-pane">
    <a href="/user/login">人脸登录</a>
</div>
</body>
</html>
复制代码

성공.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
  <h3>人脸识别登录成功</h3>
</div>
</body>
</html>
复制代码

error.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
  <h3>人脸识别登录失败</h3>
</div>
</body>
</html>
复制代码

시작 클래스 구성

개발 과정에서 예외가 발생했습니다. 즉, 사용자 정의 창을 사용할 때 시작 클래스, 설정을 수정 .setHeadless(false)하거나 JVM 매개변수를 추가 -Djava.awt.headless=false하여 해결해야 합니다.

@SpringBootApplication
public class FaceOpenCvApplication {

    public static void main(String[] args) {
        SpringApplicationBuilder builder = new SpringApplicationBuilder(FaceOpenCvApplication.class);
        builder.headless(false).run(args);
    }
}
复制代码

일반적인 예외 레코드

예외 1

Exception in thread "main" java.lang.UnsatisfiedLinkError: no opencv_java460 in java.library.path
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
	at java.lang.Runtime.loadLibrary0(Runtime.java:871)
	at java.lang.System.loadLibrary(System.java:1122)
复制代码

D:\Development\opencv\build\java\x64\opencv_java460.dll파일을 다음 두 디렉토리에 복사하고 그 중 하나를 선택하십시오.

여기에 이미지 설명 삽입

여기에 이미지 설명 삽입

예외 2

java.lang.Exception: unknown exception
	org.opencv.videoio.VideoCapture.VideoCapture_3(Native Method)
	org.opencv.videoio.VideoCapture.<init>(VideoCapture.java:62)
	com.boxuegu.servlet.UserFaceLogin.doGet(UserFaceLogin.java:25)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
复制代码

클래스 라이브러리 경로 구성

입력 D:\Development\opencv\build\x64\vc15\bin하고 경로를 얻은 다음 JVM 실행 매개변수 구성을 여기에 이미지 설명 삽입추가

-Djava.library.path=D:\Development\opencv\build\java\x64
复制代码

또는

-Djava.library.path=D:\Development\opencv\build\java\x64;D:\Development\opencv\build\x64\vc15\bin
复制代码

예외 3

Tomcat을 다시 시작하는 대신 Tomcat이 자동으로 war 패키지를 다시 시작하도록 합니다.

java.lang.UnsatisfiedLinkError: Native Library D:\Development\opencv\build\java\x64\opencv_java460.dll already loaded in another classloader
	java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1900)
	java.lang.ClassLoader.loadLibrary(ClassLoader.java:1850)
	java.lang.Runtime.loadLibrary0(Runtime.java:871)
	java.lang.System.loadLibrary(System.java:1122)
	com.boxuegu.servlet.UserFaceLogin.doGet(UserFaceLogin.java:24)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
复制代码

예외 4

Exception in thread "main" java.lang.UnsatisfiedLinkError: org.opencv.videoio.VideoCapture.VideoCapture_5(I)J
	at org.opencv.videoio.VideoCapture.VideoCapture_5(Native Method)
	at org.opencv.videoio.VideoCapture.<init>(VideoCapture.java:181)
复制代码

OpenCV 네이티브 라이브러리를 로드하는 것을 잊지 마십시오.

    static {
        // 加载OpenCV本地库
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }
复制代码

예외 5

java.lang.UnsatisfiedLinkError: org.opencv.objdetect.CascadeClassifier.CascadeClassifier_1(Ljava/lang/String;)J
	at org.opencv.objdetect.CascadeClassifier.CascadeClassifier_1(Native Method) ~[opencv-460.jar:4.6.0]
	at org.opencv.objdetect.CascadeClassifier.<init>(CascadeClassifier.java:48) ~[opencv-460.jar:4.6.0]
复制代码

종속성의 영향을 받아 spring-boot-devtools이 종속성은 처음에 제외되었고 프로젝트를 정리한 후에는 정상이었습니다. 이후에 이 종속성을 추가하여 결과에 영향을 미치지 않았으며, 구성을 수정해도 응답 없음 및 기타 비정상 상태 등의 클린 프로젝트가 여전히 많다는 점에 유의하십시오.

예외 6

java.awt.HeadlessException
	at java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:204)
	at java.awt.Window.<init>(Window.java:536)
	at java.awt.Frame.<init>(Frame.java:420)
	at javax.swing.JFrame.<init>(JFrame.java:233)
复制代码

시작 클래스 수정, 설정.setHeadless(false);

@SpringBootApplication
public class FaceOpenCvApplication {
    public static void main(String[] args) {
        SpringApplicationBuilder builder = new SpringApplicationBuilder(FaceOpenCvApplication.class);
        builder.headless(false).run(args);
    }
}
复制代码

또는 JVM 가상 머신 매개변수 설정

-Djava.awt.headless=false
复制代码

Ich denke du magst

Origin juejin.im/post/7139739779519905806
Empfohlen
Rangfolge