From 0708a5b791cc85b35248e554dc9e95362d0acd89 Mon Sep 17 00:00:00 2001 From: bruno Date: Wed, 12 Nov 2025 18:38:19 +0100 Subject: [PATCH] Added Yunet face detection example --- .../yunetFaceDetection/Readme.md | 19 +++ .../yunetFaceDetection/src/main.cpp | 17 +++ .../yunetFaceDetection/src/ofApp.cpp | 112 ++++++++++++++++++ .../yunetFaceDetection/src/ofApp.h | 35 ++++++ 4 files changed, 183 insertions(+) create mode 100644 examples/computer_vision/yunetFaceDetection/Readme.md create mode 100644 examples/computer_vision/yunetFaceDetection/src/main.cpp create mode 100644 examples/computer_vision/yunetFaceDetection/src/ofApp.cpp create mode 100644 examples/computer_vision/yunetFaceDetection/src/ofApp.h diff --git a/examples/computer_vision/yunetFaceDetection/Readme.md b/examples/computer_vision/yunetFaceDetection/Readme.md new file mode 100644 index 00000000000..fc5a40c2d2f --- /dev/null +++ b/examples/computer_vision/yunetFaceDetection/Readme.md @@ -0,0 +1,19 @@ +# Face detection with yunet + +### Face detection in OpenFrameworks using dnn + +It turns out that doing face detection in 2025 is much more efficient with neural networks than cascades. +So here is an OF example that uses opencv's facedetectoryn (for yunet). + +You will need the file with the pre-trained model. The model version must be compatible with your opencv version. + +How to find out your opencv version? + * It will be printed out during the ofApp::setup() function. + * If you have a version greater than 4.9, you can download the current [face_detection_yunet_2023mar.onnx](https://github.com/opencv/opencv_zoo/tree/main/models/face_detection_yunet) model + * If your version is 4.6 or earlier, download the [2022](https://github.com/opencv/opencv_zoo/blob/088c3571ec70df15100a5e4c26894d95951e92e9/models/face_detection_yunet/face_detection_yunet_2022mar.onnx) version of the model. + +The versions are coded in the ofApp::setup() function. Different versions might require editing the code. + +### Expected behavior + +The camera starts, and a red square will be drawn around the faces found. diff --git a/examples/computer_vision/yunetFaceDetection/src/main.cpp b/examples/computer_vision/yunetFaceDetection/src/main.cpp new file mode 100644 index 00000000000..7a2c4705b79 --- /dev/null +++ b/examples/computer_vision/yunetFaceDetection/src/main.cpp @@ -0,0 +1,17 @@ +#include "ofMain.h" +#include "ofApp.h" + +//======================================================================== +int main( ){ + + //Use ofGLFWWindowSettings for more options like multi-monitor fullscreen + ofGLWindowSettings settings; + settings.setSize(640, 480); + settings.windowMode = OF_WINDOW; //can also be OF_FULLSCREEN + + auto window = ofCreateWindow(settings); + + ofRunApp(window, std::make_shared()); + ofRunMainLoop(); + +} diff --git a/examples/computer_vision/yunetFaceDetection/src/ofApp.cpp b/examples/computer_vision/yunetFaceDetection/src/ofApp.cpp new file mode 100644 index 00000000000..4e2e68a22ba --- /dev/null +++ b/examples/computer_vision/yunetFaceDetection/src/ofApp.cpp @@ -0,0 +1,112 @@ +// in ofApp.cpp +#include "ofApp.h" + +//-------------------------------------------------------------- +void ofApp::setup(){ + ofSetLogLevel(OF_LOG_VERBOSE); + + cout << "OpenCV version: " << cv::getVersionMajor() << "." << cv::getVersionMinor() << "."<< cv::getVersionRevision() << "\n"; + + // 1. Setup Live Video + camWidth = 640; + camHeight = 480; + grabber.setup(camWidth, camHeight); + + // 2. Load the YuNet Model via the FaceDetectorYN wrapper + + string modelPath; + + if (cv::getVersionMajor()>=4) { + if (cv::getVersionMinor()<=6) { + // opencv 4.6.0 + modelPath = ofToDataPath("face_detection_yunet_2022mar.onnx"); + } else { + // opencv 4.9.0 and greater + modelPath = ofToDataPath("face_detection_yunet_2023mar.onnx"); + + } + } + + + + //string modelPath = ofToDataPath("yunet.onnx"); + cv::Size inputSize = cv::Size(320, 320); // The size the network prefers + float scoreThreshold = 0.6; // Confidence threshold + float nmsThreshold = 0.3; // Non-Maximum Suppression threshold + int topK = 5000; // Keep top 5000 detections before NMS + + // Call the static 'create' method to initialize the detector + detector = cv::FaceDetectorYN::create( + modelPath, + "", // config file (not needed for this model) + inputSize, + scoreThreshold, + nmsThreshold, + topK + ); + + if (detector.empty()) { + ofLogError() << "Failed to load YuNet model. Make sure 'face_detection_yunet_2023mar.onnx' is in bin/data/"; + } else { + ofLogNotice() << "cv::FaceDetectorYN loaded successfully."; + } +} + +//-------------------------------------------------------------- +void ofApp::update(){ + grabber.update(); + + if (grabber.isFrameNew()) { + // Clear previous detections + detectedFaces.clear(); + + // + // 1. Load pixels from the grabber into our ofxCvColorImage + colorImg.setFromPixels(grabber.getPixels()); + + // 2. Get the cv::Mat from the wrapper image + cv::Mat frame = colorImg.getCvMat(); + + // 3. Resize the detector's input. + detector->setInputSize(cv::Size(frame.cols, frame.rows)); + + // 4. Detect! + cv::Mat faces; + detector->detect(frame, faces); + if (faces.rows > 0) { + for (int i = 0; i < faces.rows; i++) { + // The format is: + // [0-3] = bbox (x, y, w, h) + // [4-13] = 10 facial landmarks + // [14] = score + + int x = static_cast(faces.at(i, 0)); + int y = static_cast(faces.at(i, 1)); + int w = static_cast(faces.at(i, 2)); + int h = static_cast(faces.at(i, 3)); + + detectedFaces.push_back(ofRectangle(x, y, w, h)); + } + } + } +} + +//-------------------------------------------------------------- +void ofApp::draw(){ + ofSetColor(255); + grabber.draw(0, 0); // Draw the live video + + // Draw the detected faces + ofNoFill(); + ofSetLineWidth(2); + ofSetColor(ofColor::red); + + for (auto& rect : detectedFaces) { + ofDrawRectangle(rect); + } + + // Draw info + ofSetColor(ofColor::white); + ofDrawBitmapString("FPS: " + ofToString(ofGetFrameRate(), 0), 20, 30); + ofDrawBitmapString("Detections: " + ofToString(detectedFaces.size()), 20, 50); +} \ No newline at end of file diff --git a/examples/computer_vision/yunetFaceDetection/src/ofApp.h b/examples/computer_vision/yunetFaceDetection/src/ofApp.h new file mode 100644 index 00000000000..450d131ab0f --- /dev/null +++ b/examples/computer_vision/yunetFaceDetection/src/ofApp.h @@ -0,0 +1,35 @@ +// in ofApp.h +#pragma once + +#include "ofMain.h" +#include "ofxOpenCv.h" +#include "ofVideoGrabber.h" + +// include the dnn and objdetect headers +#include "opencv2/objdetect.hpp" +#include "opencv2/imgproc.hpp" + +class ofApp : public ofBaseApp{ + +public: + void setup(); + void update(); + void draw(); + + ofVideoGrabber grabber; + int camWidth; + int camHeight; + + + // + // We use a Ptr (a smart pointer) to hold the detector + cv::Ptr detector; + // + // ----------------------------- + // + + // Vector to store the final, processed face rectangles + std::vector detectedFaces; + + ofxCvColorImage colorImg; +}; \ No newline at end of file