• Java借助OpenCV实现人脸识别登录完整示例


    OpenCV

    效果预览

    在这里插入图片描述

    概述

    OpenCV(开源计算机视觉库)是在BSD(开源协议)许可下发布的。它是一个高度优化的库,专注于实时应用程序。它具有C ++,Python和Java接口,支持Windows,Linux,Mac OS,iOS和Android。

    下载与安装

    下载地址:https://opencv.org/releases/

    在这里插入图片描述

    下载到本地后,双击进行安装即可

    在这里插入图片描述

    目录说明

    安装目录如下

    在这里插入图片描述

    build :基于window构建
    
    sources:开源,提供源码
    
    • 1
    • 2
    • 3

    build目录说明

    在这里插入图片描述

    这里是Java开发关注java目录即可

    在这里插入图片描述

    x64与x86代表给不同的系统使用
    
    opencv-460.jar给java操作openvc的程序包
    
    • 1
    • 2
    • 3

    由于是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

    项目集成

    方式1:

    这里使用IDEA进行开发,导入opencv-460.jar库

    使用快捷键 Ctrl+Shift+Alt+S打开
    在这里插入图片描述
    选择库项,导入Java库。
    在这里插入图片描述
    在这里插入图片描述

    方式2:

    除了上述方式,还可以将opencv-460.jar安装到本地仓库或私有仓库,然后在pom.xml中引入依赖。

    在项目根目录创建lib目录,将上述提及的opencv-460.jaropencv_java460.dll放到该目录

    在这里插入图片描述
    pom.xml文件中,通过坐标的形式引入

            <!-- 加载lib目录下的opencv包 -->
            <dependency>
                <groupId>org.opencv</groupId>
                <artifactId>opencv</artifactId>
                <version>4.6.0</version>
                <scope>system</scope>
                <systemPath>${basedir}/lib/opencv-460.jar</systemPath>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    图片人脸检测

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

    在这里插入图片描述

    人脸对比相似度

    对比1.jpg与1-1.jpg
    
    对比1.jpg与3.jpg
    
    • 1
    • 2
    • 3

    在这里插入图片描述

        // 初始化人脸探测器
        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
    • 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

    对比结果如下

    对比结果:1.0
    人脸匹配成功
    对比结果:0.2501351968792374
    人脸不匹配识别
    
    • 1
    • 2
    • 3
    • 4

    识别视频中的人脸

        // 初始化人脸探测器
        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;
        }
    
    
    • 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

    在这里插入图片描述

    摄像头识别人脸

        // 初始化人脸探测器
        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;
        }
    
    • 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

    自定义窗口

    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;
        }
    }
    
    • 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
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134

    摄像头拍摄视频写入本地

        public static void main(String[] args) throws Exception {
            MyJPanel.cameraFaceRecognition();
            // 终止当前运行的 Java 虚拟机。
            System.exit(0);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
        // 初始化人脸探测器
        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;
                }
            }
        }
    
    • 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

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

    项目集成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";
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    配置application.yml

    开发环境与生产环境需区分

    opencv:
      lib:
        linuxxmlpath: /usr/local//opencv/haarcascades/haarcascade_frontalface_alt.xml
        windowxmlpath: D:\Development\opencv\sources\data\haarcascades\haarcascade_frontalface_alt.xml
    
    • 1
    • 2
    • 3
    • 4

    指定虚拟机参数

    -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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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;
        }
    }
    
    • 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

    自定义窗口

    自定义窗口用于实时获取摄像头拍摄画面

    @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;
        }
    }
    
    • 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
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135

    创建页面

    创建模拟人脸登录的页面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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    success.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div>
      <h3>人脸识别登录成功</h3>
    </div>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    error.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div>
      <h3>人脸识别登录失败</h3>
    </div>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    启动类配置

    在开发过程中遇到一个异常,即使用自定义窗口时,需要修改启动类,设置.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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    常见异常记录

    异常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)
    
    • 1
    • 2
    • 3
    • 4

    D:\Development\opencv\build\java\x64\opencv_java460.dll文件拷贝至下面2个目录,任选其一即可。

    在这里插入图片描述

    在这里插入图片描述

    也可以通过指定虚拟机参数解决:

    -Djava.library.path=D:\WorkSpace\projectName\demo\lib\opencv_java460.dll
    
    • 1

    异常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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    配置类库路径

    进入D:\Development\opencv\build\x64\vc15\bin,获取该路径
    在这里插入图片描述
    添加JVM运行参数配置

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

    或者

    -Djava.library.path=D:\Development\opencv\build\java\x64;D:\Development\opencv\build\x64\vc15\bin
    
    • 1

    异常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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    异常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)
    
    • 1
    • 2
    • 3

    别忘了加载OpenCV本地库

        static {
            // 加载OpenCV本地库
            System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        }
    
    • 1
    • 2
    • 3
    • 4

    异常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]
    
    • 1
    • 2
    • 3

    spring-boot-devtools依赖影响,最初排除此依赖,clean项目后正常。后来又加上此依赖,结果又不影响,注意当修改配置后没反应等异常情况还是多clean项目。

    异常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)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    修改启动类,设置.setHeadless(false);

    @SpringBootApplication
    public class FaceOpenCvApplication {
        public static void main(String[] args) {
            SpringApplicationBuilder builder = new SpringApplicationBuilder(FaceOpenCvApplication.class);
            builder.headless(false).run(args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    或者设置JVM虚拟机参数

    -Djava.awt.headless=false
    
    • 1
  • 相关阅读:
    readline
    【滴滴出行安全应急响应平台DSRC2倍积分卡】
    3.4 设置环境变量MAKEFILES
    可落地的DDD(6)-工程结构
    美食杰项目重难点记录
    LeetCode-297-二叉树的序列化与反序列化
    分布式与微服务概念
    Nginx-动静分离与 URLRwrite
    ai电销机器人的语音识别是如何实现的呢?
    一看就懂:正则表达式
  • 原文地址:https://blog.csdn.net/qq_38628046/article/details/126455814