Java藉助OpenCV實現人臉識別登入完整示例

語言: CN / TW / HK
ead>

highlight: an-old-hope theme: cyanosis


我報名參加金石計劃1期挑戰——瓜分10萬獎池,這是我的第1篇文章,點選檢視活動詳情

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中引入依賴。

圖片人臉檢測

```clike 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

在這裡插入圖片描述

```java // 初始化人臉探測器 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;
}

對比結果如下java 對比結果:1.0 人臉匹配成功 對比結果:0.2501351968792374 人臉不匹配識別 ```

識別視訊中的人臉

```java // 初始化人臉探測器 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;
}

``` 在這裡插入圖片描述

攝像頭識別人臉

```java // 初始化人臉探測器 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圖形使用者介面感覺可配置引數太少,因此可自定義視窗用於代替。 ```java 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;
}

} ```

攝像頭拍攝視訊寫入本地

java public static void main(String[] args) throws Exception { MyJPanel.cameraFaceRecognition(); // 終止當前執行的 Java 虛擬機器。 System.exit(0); }

```java // 初始化人臉探測器 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

新增依賴

```java org.springframework.boot spring-boot-starter-web

    <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的基本使用中的專案整合

請求介面

```java @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

開發環境與生產環境需區分 java opencv: lib: linuxxmlpath: /usr/local/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml windowxmlpath: D:\Development\opencv\sources\data\haarcascades\haarcascade_frontalface_alt.xml 指定虛擬機器引數 ```java -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

完成初始化工作以及新增人臉匹配功能,更多功能擴充套件此工具類即可。 ```java @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;
}

} ```

自定義視窗

自定義視窗用於實時獲取攝像頭拍攝畫面

```java @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 ```java

Title

**success.html**java

Title

人臉識別登入成功

**error.html**java

Title

人臉識別登入失敗

```

啟動類配置

在開發過程中遇到一個異常,即使用自定義視窗時,需要修改啟動類,設定.setHeadless(false),或新增JVM引數-Djava.awt.headless=false來解決。 ```java @SpringBootApplication public class FaceOpenCvApplication {

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

} ```

常見異常記錄

異常1

clike 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 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執行引數配置 java -Djava.library.path=D:\Development\opencv\build\java\x64 或者 java -Djava.library.path=D:\Development\opencv\build\java\x64;D:\Development\opencv\build\x64\vc15\bin

異常3

沒重啟Tomcat,而是讓Tomcat自動重啟war包導致 java 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

java 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本地庫 java static { // 載入OpenCV本地庫 System.loadLibrary(Core.NATIVE_LIBRARY_NAME); }

異常5

java 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 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); java @SpringBootApplication public class FaceOpenCvApplication { public static void main(String[] args) { SpringApplicationBuilder builder = new SpringApplicationBuilder(FaceOpenCvApplication.class); builder.headless(false).run(args); } } 或者設定JVM虛擬機器引數 java -Djava.awt.headless=false