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