Visual Servoing Platform  version 3.6.1 under development (2024-07-15)
tutorial-face-detector-live-threaded.cpp
1 #include <iostream>
3 
4 #include <visp3/core/vpConfig.h>
5 #include <visp3/core/vpImageConvert.h>
6 #include <visp3/core/vpTime.h>
7 #include <visp3/detection/vpDetectorFace.h>
8 #include <visp3/gui/vpDisplayGDI.h>
9 #include <visp3/gui/vpDisplayX.h>
10 #include <visp3/sensor/vpV4l2Grabber.h>
11 
12 #if defined(HAVE_OPENCV_OBJDETECT) && defined(HAVE_OPENCV_HIGHGUI) && defined(HAVE_OPENCV_IMGPROC) \
13  && defined(HAVE_OPENCV_VIDEOIO) && defined(VISP_HAVE_THREADS)
14 
15 #include <thread>
16 #include <mutex>
17 
18 #include <opencv2/videoio.hpp>
19 
20 #ifdef ENABLE_VISP_NAMESPACE
21 using namespace VISP_NAMESPACE_NAME;
22 #endif
23 
24 // Shared vars
25 typedef enum { capture_waiting, capture_started, capture_stopped } t_CaptureState;
26 
27 #if defined(VISP_HAVE_V4L2)
28 void captureFunction(vpV4l2Grabber &cap, std::mutex &mutex_capture, vpImage<unsigned char> &frame, t_CaptureState &capture_state)
29 #elif defined(HAVE_OPENCV_VIDEOIO)
30 void captureFunction(cv::VideoCapture &cap, std::mutex &mutex_capture, cv::Mat &frame, t_CaptureState &capture_state)
31 #endif
32 {
33  // If the image is larger than 640 by 480, we subsample
34 #if defined(VISP_HAVE_V4L2)
36 #elif defined(HAVE_OPENCV_VIDEOIO)
37  cv::Mat frame_;
38 #endif
39  bool stop_capture_ = false;
40 
41  double start_time = vpTime::measureTimeSecond();
42  while ((vpTime::measureTimeSecond() - start_time) < 30 && !stop_capture_) {
43  // Capture in progress
44  cap >> frame_; // get a new frame from camera
45 
46  // Update shared data
47  {
48  std::lock_guard<std::mutex> lock(mutex_capture);
49  if (capture_state == capture_stopped)
50  stop_capture_ = true;
51  else
52  capture_state = capture_started;
53  frame = frame_;
54  }
55  }
56  {
57  std::lock_guard<std::mutex> lock(mutex_capture);
58  capture_state = capture_stopped;
59  }
60 
61  std::cout << "End of capture thread" << std::endl;
62 }
63 
64 #if defined(VISP_HAVE_V4L2)
65 void displayFunction(std::mutex &mutex_capture, std::mutex &mutex_face, vpImage<unsigned char> &frame, t_CaptureState &capture_state, vpRect &face_bbox, bool &face_available)
66 #elif defined(HAVE_OPENCV_VIDEOIO)
67 void displayFunction(std::mutex &mutex_capture, std::mutex &mutex_face, cv::Mat &frame, t_CaptureState &capture_state, vpRect &face_bbox, bool &face_available)
68 #endif
69 {
71 
72  t_CaptureState capture_state_;
73  bool display_initialized_ = false;
74  bool face_available_ = false;
75  vpRect face_bbox_;
76 #if defined(VISP_HAVE_X11)
77  vpDisplayX *d_ = nullptr;
78 #elif defined(VISP_HAVE_GDI)
79  vpDisplayGDI *d_ = nullptr;
80 #endif
81 
82  do {
83  mutex_capture.lock();
84  capture_state_ = capture_state;
85  mutex_capture.unlock();
86 
87  // Check if a frame is available
88  if (capture_state_ == capture_started) {
89  // Get the frame and convert it to a ViSP image used by the display
90  // class
91  {
92  std::lock_guard<std::mutex> lock(mutex_capture);
93 #if defined(VISP_HAVE_V4L2)
94  I_ = frame;
95 #elif defined(VISP_HAVE_OPENCV)
96  vpImageConvert::convert(frame, I_);
97 #endif
98  }
99 
100  // Check if we need to initialize the display with the first frame
101  if (!display_initialized_) {
102  // Initialize the display
103 #if defined(VISP_HAVE_X11)
104  d_ = new vpDisplayX(I_);
105  display_initialized_ = true;
106 #elif defined(VISP_HAVE_GDI)
107  d_ = new vpDisplayGDI(I_);
108  display_initialized_ = true;
109 #endif
110  }
111 
112  // Display the image
113  vpDisplay::display(I_);
114 
115  // Check if a face was detected
116  {
117 
118  std::lock_guard<std::mutex> lock(mutex_face);
119  face_available_ = face_available;
120  face_bbox_ = face_bbox;
121  }
122  if (face_available_) {
123  // Access to the face bounding box to display it
124  vpDisplay::displayRectangle(I_, face_bbox_, vpColor::green, false, 4);
125  face_available_ = false;
126  }
127 
128  // Trigger end of acquisition with a mouse click
129  vpDisplay::displayText(I_, 10, 10, "Click to exit...", vpColor::red);
130  if (vpDisplay::getClick(I_, false)) {
131  std::lock_guard<std::mutex> lock(mutex_capture);
132  capture_state = capture_stopped;
133  }
134 
135  // Update the display
136  vpDisplay::flush(I_);
137  }
138  else {
139  vpTime::wait(2); // Sleep 2ms
140  }
141  } while (capture_state_ != capture_stopped);
142 
143 #if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI)
144  delete d_;
145 #endif
146 
147  std::cout << "End of display thread" << std::endl;
148 }
149 
151 #if defined(VISP_HAVE_V4L2)
152 void detectionFunction(std::mutex &mutex_capture, std::mutex &mutex_face, vpImage<unsigned char> &frame, t_CaptureState &capture_state, vpRect &face_bbox, std::string &face_cascade_name, bool &face_available)
153 #elif defined(HAVE_OPENCV_VIDEOIO)
154 void detectionFunction(std::mutex &mutex_capture, std::mutex &mutex_face, cv::Mat &frame, t_CaptureState &capture_state, vpRect &face_bbox, std::string &face_cascade_name, bool &face_available)
155 #endif
156 {
157  vpDetectorFace face_detector_;
158  face_detector_.setCascadeClassifierFile(face_cascade_name);
159 
160  t_CaptureState capture_state_;
161 #if defined(VISP_HAVE_V4L2)
162  vpImage<unsigned char> frame_;
163 #elif defined(VISP_HAVE_OPENCV)
164  cv::Mat frame_;
165 #endif
166  do {
167  mutex_capture.lock();
168  capture_state_ = capture_state;
169  mutex_capture.unlock();
170 
171  // Check if a frame is available
172  if (capture_state_ == capture_started) {
173  // Backup the frame
174  {
175  std::lock_guard<std::mutex> lock(mutex_capture);
176  frame_ = frame;
177  }
178 
179  // Detect faces
180  bool face_found_ = face_detector_.detect(frame_);
181  if (face_found_) {
182  std::lock_guard<std::mutex> lock(mutex_face);
183  face_available = true;
184  face_bbox = face_detector_.getBBox(0); // Get largest face bounding box
185  }
186  }
187  else {
188  vpTime::wait(2); // Sleep 2ms
189  }
190  } while (capture_state_ != capture_stopped);
191  std::cout << "End of face detection thread" << std::endl;
192 }
194 
196 int main(int argc, const char *argv[])
197 {
198  std::string opt_face_cascade_name = "./haarcascade_frontalface_alt.xml";
199  unsigned int opt_device = 0;
200  unsigned int opt_scale = 2; // Default value is 2 in the constructor. Turn
201  // it to 1 to avoid subsampling
202 
203  for (int i = 0; i < argc; i++) {
204  if (std::string(argv[i]) == "--haar")
205  opt_face_cascade_name = std::string(argv[i + 1]);
206  else if (std::string(argv[i]) == "--device")
207  opt_device = (unsigned int)atoi(argv[i + 1]);
208  else if (std::string(argv[i]) == "--scale")
209  opt_scale = (unsigned int)atoi(argv[i + 1]);
210  else if (std::string(argv[i]) == "--help") {
211  std::cout << "Usage: " << argv[0]
212  << " [--haar <haarcascade xml filename>] [--device <camera "
213  "device>] [--scale <subsampling factor>] [--help]"
214  << std::endl;
215  return EXIT_SUCCESS;
216  }
217  }
218 
219  // Instantiate the capture
220 #if defined(VISP_HAVE_V4L2)
222  vpV4l2Grabber cap;
223  std::ostringstream device;
224  device << "/dev/video" << opt_device;
225  cap.setDevice(device.str());
226  cap.setScale(opt_scale);
227 #elif defined(HAVE_OPENCV_VIDEOIO)
228  cv::Mat frame;
229  cv::VideoCapture cap;
230  cap.open(opt_device);
231 #if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
232  int width = (int)cap.get(cv::CAP_PROP_FRAME_WIDTH);
233  int height = (int)cap.get(cv::CAP_PROP_FRAME_HEIGHT);
234  cap.set(cv::CAP_PROP_FRAME_WIDTH, width / opt_scale);
235  cap.set(cv::CAP_PROP_FRAME_HEIGHT, height / opt_scale);
236 #else
237  int width = cap.get(CV_CAP_PROP_FRAME_WIDTH);
238  int height = cap.get(CV_CAP_PROP_FRAME_HEIGHT);
239  cap.set(CV_CAP_PROP_FRAME_WIDTH, width / opt_scale);
240  cap.set(CV_CAP_PROP_FRAME_HEIGHT, height / opt_scale);
241 #endif
242 #endif
243 
244  std::mutex mutex_capture;
245  std::mutex mutex_face;
246  vpRect face_bbox;
247  t_CaptureState capture_state = capture_waiting;
248  bool face_available = false;
249 
250  // Start the threads
251  std::thread thread_capture(&captureFunction, std::ref(cap), std::ref(mutex_capture), std::ref(frame), std::ref(capture_state));
252  std::thread thread_display(&displayFunction, std::ref(mutex_capture), std::ref(mutex_face), std::ref(frame),
253  std::ref(capture_state), std::ref(face_bbox), std::ref(face_available));
254  std::thread thread_detection(&detectionFunction, std::ref(mutex_capture), std::ref(mutex_face), std::ref(frame),
255  std::ref(capture_state), std::ref(face_bbox), std::ref(opt_face_cascade_name), std::ref(face_available));
256 
257  // Wait until thread ends up
258  thread_capture.join();
259  thread_display.join();
260  thread_detection.join();
261 
262  return EXIT_SUCCESS;
263 }
265 
266 #else
267 int main()
268 {
269 #ifndef VISP_HAVE_OPENCV
270  std::cout << "You should install OpenCV to make this example working..." << std::endl;
271 #elif !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
272  std::cout << "You should enable pthread usage and rebuild ViSP..." << std::endl;
273 #else
274  std::cout << "Multi-threading seems not supported on this platform" << std::endl;
275 #endif
276  return EXIT_SUCCESS;
277 }
278 
279 #endif
static const vpColor red
Definition: vpColor.h:217
static const vpColor green
Definition: vpColor.h:220
vpRect getBBox(size_t i) const
void setCascadeClassifierFile(const std::string &filename)
bool detect(const vpImage< unsigned char > &I) VP_OVERRIDE
Display for windows using GDI (available on any windows 32 platform).
Definition: vpDisplayGDI.h:130
Use the X11 console to display images on unix-like OS. Thus to enable this class X11 should be instal...
Definition: vpDisplayX.h:135
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
static void flush(const vpImage< unsigned char > &I)
static void displayRectangle(const vpImage< unsigned char > &I, const vpImagePoint &topLeft, unsigned int width, unsigned int height, const vpColor &color, bool fill=false, unsigned int thickness=1)
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
Defines a rectangle in the plane.
Definition: vpRect.h:79
Class that is a wrapper over the Video4Linux2 (V4L2) driver.
void open(vpImage< unsigned char > &I)
void setScale(unsigned scale=vpV4l2Grabber::DEFAULT_SCALE)
void setDevice(const std::string &devname)
VISP_EXPORT int wait(double t0, double t)
VISP_EXPORT double measureTimeSecond()