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