當前位置: 妍妍網 > 碼農

OpenCV4.8 For Java即時人臉檢測

2024-05-17碼農

點選上方 藍字 關註我們

微信公眾號: OpenCV學堂

關註獲取更多電腦視覺與深度學習知識

前言

我寫這篇文章之前,我搜尋整個網路文章跟問各種語言大模型,太可怕了,它們沒有一個正確的,但是都在給我一本正經的胡說八道。所以沒辦法,我只好自己研究一番,經過兩天的折騰終於搞定了OpenCV DNN部署YOLOv5、YOLOv8等各種模型。然後我特別想把這塊最關鍵的知識點給大家分享一下,所以寫了這篇文章,以Java語言完成OpenCV DNN的即時人臉檢測,同時解釋其中的關鍵知識點。

OpenCV DNN人臉檢測

各種部落格上的很多Java人臉檢測的文章都還是基於級聯檢測器的,有的好像是我2017年前文章的程式碼。後來我再也沒寫過Java,所以網上居然再也找不到Java版本的OpenCV DNN人臉檢測的文章跟程式碼,各種部落格上的程式碼一看就早已落伍多時。這裏使用最新版本的Java SDK和OpenCV4.8深度神經網路模組進行深度學習和人臉檢測的方法。關於JDK環境搭建與IDE安裝可以看這篇文章:

OpenCV DNN官方提供的人臉檢測模型下載地址如下:

https://gitee.com/opencv_ai/opencv_tutorial_data/tree/master/models

輸入的數據格式如下:

這是一個SSD的物件檢測模型輸出的格式為:

1x1xNx7[batchId, classId, confidence, left, top, right, bottom]

程式碼實作與演示

我給OpenCV DNN 人臉檢測的Java實作封裝成了一個類,客戶端只要兩行程式碼即可呼叫執行,簡單方便,寫個Java的Main方法即可呼叫,實作人臉檢測,唯一需要的就是先載入OpenCV Java的DLL支持,然後就可以正常呼叫了。客戶端程式碼如下:

publicstaticvoidmain(String[] args){
String model_file = "D:/projects/opencv_face_detector_uint8.pb";
String pb_txt_file = "D:/projects/opencv_face_detector.pbtxt";
System.load("D:/opencv-4.8.0/opencv/build/java/x64/opencv_java480.dll");
System.out.println("start to read image...");
Mat inputImage = Imgcodecs.imread("D:/images/mmc.png");
JavaFaceDetection face_detector = new JavaFaceDetection(model_file, pb_txt_file, 0.5f);
face_detector.infer_image(inputImage);
HighGui.imshow("OpenCV Java 深度學習人臉檢測演示", inputImage);
HighGui.waitKey(0);
VideoCapture capture = new VideoCapture();
capture.open(0);
while(true) {
Mat frame = new Mat();
boolean ret = capture.read(frame);
Core.flip(frame, frame, 1);
if(ret) {
face_detector.infer_image(frame);
HighGui.imshow("OpenCV Java 深度學習人臉檢測演示", frame);
int c = HighGui.waitKey(1);
if (c == 27) {
break;
}
}
}
HighGui.destroyAllWindows();
System.exit(0);
}

封裝的 Java版本深度學習人臉檢測類 的程式碼如下:

import com.sun.jna.Pointer;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import org.opencv.dnn.*;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.videoio.VideoCapture;
public classJavaFaceDetection{
public Net getNet(){
return net;
}
publicvoidsetNet(Net net){
this.net = net;
}
private Net net;
privatefloat score_t = 0.5f;
publicJavaFaceDetection(String model_path, String pb_txt_file, float conf){
this.score_t = conf;
this.net = Dnn.readNetFromTensorflow(model_path, pb_txt_file);
}
publicvoidinfer_image(Mat frame){
long stime = System.currentTimeMillis();
// 推理
Mat blob = Dnn.blobFromImage(frame, 1.0new Size(300300), new Scalar(104.0177.0123.0), falsefalse);
this.net.setInput(blob);
Mat probs = this.net.forward();
// 1x1xNx7
int rows = probs.size(2);
int cols = probs.size(3);
float[] result = new Pointer(probs.dataAddr()).getFloatArray(0, rows*cols);
probs.get(00, result);
Mat detectOut = new Mat(rows, cols, CvType.CV_32F);
detectOut.put(00, result);
for (int row = 0; row < detectOut.rows(); row++) {
float conf = (float)detectOut.get(row, 2)[0];
if (conf > this.score_t) {
float x1 = (float)(detectOut.get(row, 3)[0] * frame.cols());
float y1 = (float)(detectOut.get(row, 4)[0] * frame.rows());
float x2 = (float)(detectOut.get(row, 5)[0] * frame.cols());
float y2 = (float)(detectOut.get(row, 6)[0] * frame.rows());
Rect2d box = new Rect2d();
box.x = x1;
box.y = y1;
box.width = x2 - x1;
box.height = y2 - y1;
Rect rect = new Rect((int) box.x, (int) box.y, (int) box.width, (int) box.height);
Imgproc.rectangle(frame, rect, new Scalar(0,0255), 28);
Imgproc.putText(frame, String.format("%.2f", conf), new Point(rect.x, rect.y-5), Imgproc.FONT_HERSHEY_COMPLEX, 0.5new Scalar(2550255), 18);
}
}
long end_time = System.currentTimeMillis();
float fps = 1000.0f / (end_time - stime);
Imgproc.putText(frame, String.format("FPS: %.2f", fps), new Point(3030), Imgproc.FONT_HERSHEY_COMPLEX, 1.0new Scalar(00255), 28);
}
}









其中最關鍵的是 如何把推理輸出得到四維Tensor張量 1x1xNx7 轉換為 一個2D的Mat物件 ,這個就是各種大語言模型胡編亂造的地方,其實只有用JNA透過JNI介面存取本地C++地址獲取推理以後的浮點數陣列,然後重新構建一個2D Mat物件即可。解決這個問題其它程式碼基本是C++版本的Java語言轉譯,容易了。

檢測單張影像

視訊即時檢測-本人親測有效

系統化學習直接掃碼檢視

推薦閱讀