草庐IT

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

CodeDevMaster 2023-07-25 原文

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

OpenCV

效果预览

概述

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

下载与安装

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

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

目录说明

安装目录如下

build :基于window构建

sources:开源,提供源码

build目录说明

这里是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/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>

success.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个目录,任选其一即可。

异常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依赖影响,最初排除此依赖,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)

修改启动类,设置.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

有关Java借助OpenCV实现人脸识别登录完整示例的更多相关文章

  1. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  4. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  5. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  6. 报告回顾丨模型进化狂飙,DetectGPT能否识别最新模型生成结果? - 2

    导读语言模型给我们的生产生活带来了极大便利,但同时不少人也利用他们从事作弊工作。如何规避这些难辨真伪的文字所产生的负面影响也成为一大难题。在3月9日智源Live第33期活动「DetectGPT:判断文本是否为机器生成的工具」中,主讲人Eric为我们讲解了DetectGPT工作背后的思路——一种基于概率曲率检测的用于检测模型生成文本的工具,它可以帮助我们更好地分辨文章的来源和可信度,对保护信息真实、防止欺诈等方面具有重要意义。本次报告主要围绕其功能,实现和效果等展开。(文末点击“阅读原文”,查看活动回放。)Ericmitchell斯坦福大学计算机系四年级博士生,由ChelseaFinn和Chri

  7. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  8. Vscode+Cmake配置并运行opencv环境(Windows和Ubuntu大同小异) - 2

    之前在培训新生的时候,windows环境下配置opencv环境一直教的都是网上主流的vsstudio配置属性表,但是这个似乎对新生来说难度略高(虽然个人觉得完全是他们自己的问题),加之暑假之后对cmake实在是爱不释手,且这样配置确实十分简单(其实都不需要配置),故斗胆妄言vscode下配置CV之法。其实极为简单,图比较多所以很长。如果你看此文还配不好,你应该思考一下是不是自己的问题。闲话少说,直接开始。0.CMkae简介有的人到大二了都不知道cmake是什么,我不说是谁。CMake是一个开源免费并且跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。它能够根据当前所在平台输出对应的m

  9. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  10. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

随机推荐