Visual Servoing Platform  version 3.6.1 under development (2024-07-15)
tutorial-grabber-v4l2-threaded.cpp
1 #include <iostream>
4 #include <visp3/core/vpConfig.h>
5 #include <visp3/core/vpImageConvert.h>
6 #include <visp3/core/vpTime.h>
7 #include <visp3/gui/vpDisplayFactory.h>
8 #include <visp3/sensor/vpV4l2Grabber.h>
9 
10 #if defined(VISP_HAVE_V4L2) && defined(VISP_HAVE_THREADS) && defined(VISP_HAVE_DISPLAY)
11 
12 #include <thread>
13 #include <mutex>
14 
15 #ifdef ENABLE_VISP_NAMESPACE
16 using namespace VISP_NAMESPACE_NAME;
17 #endif
18 
19 // Shared vars
20 typedef enum { capture_waiting, capture_started, capture_stopped } t_CaptureState;
22 
24 void captureFunction(vpV4l2Grabber &cap, std::mutex &mutex_capture, vpImage<unsigned char> &frame, t_CaptureState &capture_state)
25 {
27  bool stop_capture_ = false;
28 
29  cap.open(frame_);
30 
31  double start_time = vpTime::measureTimeSecond();
32  while ((vpTime::measureTimeSecond() - start_time) < 30 && !stop_capture_) {
33  // Capture in progress
34  cap.acquire(frame_); // get a new frame from camera
35 
36  // Update shared data
37  {
38  std::lock_guard<std::mutex> lock(mutex_capture);
39  if (capture_state == capture_stopped)
40  stop_capture_ = true;
41  else
42  capture_state = capture_started;
43  frame = frame_;
44  }
45  }
46 
47  {
48  std::lock_guard<std::mutex> lock(mutex_capture);
49  capture_state = capture_stopped;
50  }
51  std::cout << "End of capture thread" << std::endl;
52 }
54 
56 void displayFunction(std::mutex &mutex_capture, vpImage<unsigned char> &frame, t_CaptureState &capture_state)
57 {
59 
60  t_CaptureState capture_state_;
61  bool display_initialized_ = false;
63 
64  do {
65  mutex_capture.lock();
66  capture_state_ = capture_state;
67  mutex_capture.unlock();
68 
69  // Check if a frame is available
70  if (capture_state_ == capture_started) {
71  // Create a copy of the captured frame
72  {
73  std::lock_guard<std::mutex> lock(mutex_capture);
74  I_ = frame;
75  }
76 
77  // Check if we need to initialize the display with the first frame
78  if (!display_initialized_) {
79  // Initialize the display
80  d_->init(I_);
81  display_initialized_ = true;
82  }
83 
84  // Display the image
86 
87  // Trigger end of acquisition with a mouse click
88  vpDisplay::displayText(I_, 10, 10, "Click to exit...", vpColor::red);
89  if (vpDisplay::getClick(I_, false)) {
90  std::lock_guard<std::mutex> lock(mutex_capture);
91  capture_state = capture_stopped;
92  }
93 
94  // Update the display
95  vpDisplay::flush(I_);
96  }
97  else {
98  vpTime::wait(2); // Sleep 2ms
99  }
100  } while (capture_state_ != capture_stopped);
101 
102  delete d_;
103 
104  std::cout << "End of display thread" << std::endl;
105 }
107 
109 int main(int argc, const char *argv[])
110 {
111  unsigned int opt_device = 0; // Default is opening /dev/video0
112  unsigned int opt_scale = 2; // Default value is 2 in the constructor. Turn
113  // it to 1 to avoid subsampling
114 
115  // Command line options
116  for (int i = 0; i < argc; i++) {
117  if (std::string(argv[i]) == "--camera_device")
118  opt_device = (unsigned int)atoi(argv[i + 1]);
119  else if (std::string(argv[i]) == "--scale")
120  opt_scale = (unsigned int)atoi(argv[i + 1]);
121  else if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "--h") {
122  std::cout << "Usage: " << argv[0]
123  << " [--camera_device <camera device (default: 0)>] [--scale <subsampling factor>]"
124  << " [--help] [-h]" << std::endl;
125  return EXIT_SUCCESS;
126  }
127  }
128 
129  // Instantiate the grabber
130  vpV4l2Grabber g;
131  std::ostringstream device;
132  device << "/dev/video" << opt_device;
133  g.setDevice(device.str());
134  g.setScale(opt_scale);
135 
137  std::mutex mutex_capture;
138  t_CaptureState capture_state = capture_waiting;
139 
140  // Start the threads
141  std::thread thread_capture(&captureFunction, std::ref(g), std::ref(mutex_capture), std::ref(frame), std::ref(capture_state));
142  std::thread thread_display(&displayFunction, std::ref(mutex_capture), std::ref(frame), std::ref(capture_state));
143 
144  // Wait until thread ends up
145  thread_capture.join();
146  thread_display.join();
147 
148  return EXIT_SUCCESS;
149 }
151 
152 #else
153 int main()
154 {
155 #ifndef VISP_HAVE_V4L2
156  std::cout << "You should enable V4L2 to make this example working..." << std::endl;
157 #elif !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
158  std::cout << "You should enable pthread usage and rebuild ViSP..." << std::endl;
159 #elif !defined(VISP_HAVE_DISPLAY)
160  std::cout << "You should have at least one GUI library installed to use this example." << std::endl;
161 #else
162  std::cout << "Multi-threading seems not supported on this platform" << std::endl;
163 #endif
164 }
165 
166 #endif
static const vpColor red
Definition: vpColor.h:217
Class that defines generic functionalities for display.
Definition: vpDisplay.h:178
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 displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
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)
void acquire(vpImage< unsigned char > &I)
vpDisplay * displayFactory()
Return a newly allocated vpDisplay specialization if a GUI library is available or nullptr otherwise.
VISP_EXPORT int wait(double t0, double t)
VISP_EXPORT double measureTimeSecond()