學習《OpenCV應用開發:入門、進階與工程化實踐》一書,學會本文所有技能就這么簡單!
做真正的OpenCV開發者,從入門到入職,一步到位!
前言
我寫這篇文章之前,我搜索整個網絡文章跟問各種語言大模型,太可怕了,它們沒有一個正確的,但是都在給我一本正經的胡說八道。所以沒辦法,我只好自己研究一番,經過兩天的折騰終于搞定了OpenCV DNN部署YOLOv5、YOLOv8等各種模型。然后我特別想把這塊最關鍵的知識點給大家分享一下,所以寫了這篇文章,以Java語言完成OpenCV DNN的實時人臉檢測,同時解釋其中的關鍵知識點。
OpenCV DNN人臉檢測
各種博客上的很多Java人臉檢測的文章都還是基于級聯檢測器的,有的好像是我2017年前文章的代碼。后來我再也沒寫過Java,所以網上居然再也找不到Java版本的OpenCV DNN人臉檢測的文章跟代碼,各種博客上的代碼一看就早已落伍多時。這里使用最新版本的Java SDK和OpenCV4.8深度神經網絡模塊進行深度學習和人臉檢測的方法。關于JDK環境搭建與IDE安裝可以看這篇文章:
OpenCV4.8 Java SDK實現YOLOv5模型部署
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支持,然后就可以正常調用了。客戶端代碼如下:
public static void main(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 class JavaFaceDetection {public Net getNet() {return net;}public void setNet(Net net) {this.net = net;}private Net net;private float score_t = 0.5f;public JavaFaceDetection(String model_path, String pb_txt_file, float conf) {this.score_t = conf;this.net = Dnn.readNetFromTensorflow(model_path, pb_txt_file);}public void infer_image(Mat frame) {long stime = System.currentTimeMillis();// 推理Mat blob = Dnn.blobFromImage(frame, 1.0, new Size(300, 300), new Scalar(104.0, 177.0, 123.0), false, false);this.net.setInput(blob);Mat probs = this.net.forward();// 1x1xNx7int rows = probs.size(2);int cols = probs.size(3);float[] result = new Pointer(probs.dataAddr()).getFloatArray(0, rows*cols);probs.get(0, 0, result);Mat detectOut = new Mat(rows, cols, CvType.CV_32F);detectOut.put(0, 0, 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,0, 255), 2, 8);Imgproc.putText(frame, String.format("%.2f", conf), new Point(rect.x, rect.y-5), Imgproc.FONT_HERSHEY_COMPLEX, 0.5, new Scalar(255, 0, 255), 1, 8);}}long end_time = System.currentTimeMillis();float fps = 1000.0f / (end_time - stime);Imgproc.putText(frame, String.format("FPS: %.2f", fps), new Point(30, 30), Imgproc.FONT_HERSHEY_COMPLEX, 1.0, new Scalar(0, 0, 255), 2, 8);}
}
其中最關鍵的是如何把推理輸出得到四維Tensor張量 1x1xNx7 轉換為 一個2D的Mat對象,這個就是各種大語言模型胡編亂造的地方,其實只有用JNA通過JNI接口訪問本地C++地址獲取推理以后的浮點數數組,然后重新構建一個2D Mat對象即可。解決這個問題其它代碼基本是C++版本的Java語言翻譯,容易了。
檢測單張圖像
視頻實時檢測-本人親測有效
學習《OpenCV應用開發:入門、進階與工程化實踐》一書,學會本文所有技能就這么簡單!
做真正的OpenCV開發者,從入門到入職,一步到位!