44#include <visp3/core/vpConfig.h>
46#if defined(VISP_HAVE_V4L2) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GTK)) && defined(VISP_HAVE_THREADS)
48#include <condition_variable>
55#include <visp3/core/vpDisplay.h>
56#include <visp3/core/vpImageFilter.h>
57#include <visp3/core/vpIoTools.h>
58#include <visp3/core/vpTime.h>
59#include <visp3/gui/vpDisplayGTK.h>
60#include <visp3/gui/vpDisplayX.h>
61#include <visp3/io/vpParseArgv.h>
62#include <visp3/io/vpVideoWriter.h>
63#include <visp3/sensor/vpV4l2Grabber.h>
65#define GETOPTARGS "d:oh"
67#ifdef ENABLE_VISP_NAMESPACE
74void usage(
const char *name,
const char *badparam)
78 %s [-d <device count>] [-o] [-h]\n\
81 Capture multiple camera streams and save the stream without slowing down the acquisition.\n\
85 Open the specified number of camera streams.\n\
88 Save each stream in a dedicated folder.\n\
95 fprintf(stdout,
"\nERROR: Bad parameter [%s]\n", badparam);
98bool getOptions(
int argc,
char **argv,
unsigned int &deviceCount,
bool &saveVideo)
101 const char **argv1 = (
const char **)argv;
107 deviceCount =
static_cast<unsigned int>(atoi(optarg));
113 usage(argv[0],
nullptr);
117 usage(argv[0], optarg);
122 if ((c == 1) || (c == -1)) {
124 usage(argv[0],
nullptr);
125 std::cerr <<
"ERROR: " << std::endl;
126 std::cerr <<
" Bad argument " << optarg << std::endl << std::endl;
133#ifndef DOXYGEN_SHOULD_SKIP_THIS
144 : m_cancelled(false), m_cond(), m_queueColor(), m_maxQueueSize(std::numeric_limits<size_t>::max()), m_mutex()
149 std::lock_guard<std::mutex> lock(m_mutex);
155 void push(
const vpImage<vpRGBa> &image)
157 std::lock_guard<std::mutex> lock(m_mutex);
159 m_queueColor.push(image);
162 while (m_queueColor.size() > m_maxQueueSize) {
170 vpImage<vpRGBa> pop()
172 std::unique_lock<std::mutex> lock(m_mutex);
174 while (m_queueColor.empty()) {
176 throw vpCancelled_t();
182 throw vpCancelled_t();
186 vpImage<vpRGBa> image(m_queueColor.front());
192 void setMaxQueueSize(
const size_t max_queue_size) { m_maxQueueSize = max_queue_size; }
196 std::condition_variable m_cond;
197 std::queue<vpImage<vpRGBa> > m_queueColor;
198 size_t m_maxQueueSize;
205 vpStorageWorker(vpFrameQueue &queue,
const std::string &filename,
unsigned int width,
unsigned int height)
212 vpImage<vpRGBa> O_color(m_height, m_width);
214 vpVideoWriter writer;
215 if (!m_filename.empty()) {
217 writer.
open(O_color);
222 vpImage<vpRGBa> image(m_queue.pop());
224 if (!m_filename.empty()) {
229 catch (vpFrameQueue::vpCancelled_t &) {
234 vpFrameQueue &m_queue;
235 std::string m_filename;
236 unsigned int m_width;
237 unsigned int m_height;
245 std::condition_variable m_cond;
247 unsigned char *m_pImgData;
248 unsigned int m_totalSize;
254 vpShareImage() : m_cancelled(false), m_cond(), m_mutex(), m_pImgData(nullptr), m_totalSize(0) { }
256 virtual ~vpShareImage()
258 if (m_pImgData !=
nullptr) {
265 std::lock_guard<std::mutex> lock(m_mutex);
271 void getImage(
unsigned char *
const imageData,
const unsigned int totalSize)
273 std::unique_lock<std::mutex> lock(m_mutex);
276 throw vpCancelled_t();
282 throw vpCancelled_t();
286 if (totalSize <= m_totalSize) {
287 memcpy(imageData, m_pImgData, totalSize *
sizeof(
unsigned char));
290 std::cerr <<
"totalSize <= m_totalSize !" << std::endl;
296 std::lock_guard<std::mutex> lock(m_mutex);
301 void setImage(
const unsigned char *
const imageData,
const unsigned int totalSize)
303 std::lock_guard<std::mutex> lock(m_mutex);
305 if (m_pImgData ==
nullptr || m_totalSize != totalSize) {
306 m_totalSize = totalSize;
308 if (m_pImgData !=
nullptr) {
312 m_pImgData =
new unsigned char[m_totalSize];
316 memcpy(m_pImgData, imageData, m_totalSize *
sizeof(
unsigned char));
322void capture(
vpV4l2Grabber *
const pGrabber, vpShareImage &share_image)
327 pGrabber->
open(local_img);
330 if (share_image.isCancelled()) {
337 share_image.setImage((
unsigned char *)local_img.
bitmap, local_img.
getSize() * 4);
341void display(
unsigned int width,
unsigned int height,
int win_x,
int win_y,
unsigned int deviceId,
342 vpShareImage &share_image, vpFrameQueue &queue,
bool save)
346#if defined(VISP_HAVE_X11)
348#elif defined(VISP_HAVE_GTK)
354 std::stringstream ss;
355 ss <<
"Camera stream " << deviceId;
356 display.init(local_img, win_x, win_y, ss.str());
362 vpImage<unsigned char> I_red(height, width), I_green(height, width), I_blue(height, width), I_alpha(height, width);
364 I_blue_gaussian(height, width);
365 vpImage<double> I_red_gaussian_double, I_green_gaussian_double, I_blue_gaussian_double;
367 bool exit =
false, gaussian_blur =
false;
372 share_image.getImage((
unsigned char *)local_img.
bitmap, local_img.
getSize() * 4);
394 std::stringstream ss;
395 ss <<
"Time: " <<
t <<
" ms";
405 queue.push(local_img);
411 gaussian_blur = !gaussian_blur;
421 catch (vpShareImage::vpCancelled_t &) {
422 std::cout <<
"Cancelled!" << std::endl;
425 share_image.cancel();
431int main(
int argc,
char *argv[])
433 unsigned int deviceCount = 1;
434 unsigned int cameraScale = 1;
435 bool saveVideo =
false;
438 if (!getOptions(argc, argv, deviceCount, saveVideo)) {
442 std::vector<vpV4l2Grabber *> grabbers;
444 const unsigned int offsetX = 100, offsetY = 100;
445 for (
unsigned int devicedId = 0; devicedId < deviceCount; devicedId++) {
448 std::stringstream ss;
449 ss <<
"/dev/video" << devicedId;
453 grabbers.push_back(pGrabber);
456 std::cerr <<
"Exception: " <<
e.what() << std::endl;
460 std::cout <<
"Grabbers: " << grabbers.size() << std::endl;
462 std::vector<vpShareImage> share_images(grabbers.size());
463 std::vector<std::thread> capture_threads;
464 std::vector<std::thread> display_threads;
467 std::vector<vpFrameQueue> save_queues(grabbers.size());
468 std::vector<vpStorageWorker> storages;
469 std::vector<std::thread> storage_threads;
472 for (
size_t deviceId = 0; deviceId < grabbers.size(); deviceId++) {
474 capture_threads.emplace_back(capture, grabbers[deviceId], std::ref(share_images[deviceId]));
475 int win_x = deviceId * offsetX, win_y = deviceId * offsetY;
478 display_threads.emplace_back(display, grabbers[deviceId]->getWidth(), grabbers[deviceId]->getHeight(), win_x, win_y,
479 deviceId, std::ref(share_images[deviceId]), std::ref(save_queues[deviceId]),
483 std::stringstream ss;
484 ss << parent_directory <<
"/Camera_Stream" << deviceId;
485 std::cout <<
"Create directory: " << ss.str() << std::endl;
490 storages.emplace_back(std::ref(save_queues[deviceId]), std::cref(filename), grabbers[deviceId]->getWidth(),
491 grabbers[deviceId]->getHeight());
496 for (
auto &s : storages) {
498 storage_threads.emplace_back(&vpStorageWorker::run, &s);
503 for (
auto &ct : capture_threads) {
507 for (
auto &dt : display_threads) {
513 for (
auto &g : grabbers) {
518 std::cout <<
"\nWaiting for finishing thread to write images..." << std::endl;
522 for (
auto &qu : save_queues) {
527 for (
auto &st : storage_threads) {
534#if !(defined(VISP_HAVE_X11) || defined(VISP_HAVE_GTK))
537 std::cout <<
"You do not have X11, or GTK functionalities to display images..." << std::endl;
538 std::cout <<
"Tip if you are on a unix-like system:" << std::endl;
539 std::cout <<
"- Install X11, configure again ViSP using cmake and build again this example" << std::endl;
540 std::cout <<
"Tip if you are on a windows-like system:" << std::endl;
541 std::cout <<
"- Install GTK, configure again ViSP using cmake and build again this example" << std::endl;
544#elif !defined(VISP_HAVE_V4L2)
547 std::cout <<
"You do not have Video 4 Linux 2 functionality enabled" << std::endl;
548 std::cout <<
"Tip if you are on a unix-like system:" << std::endl;
549 std::cout <<
"- Install libv4l2, configure again ViSP using cmake and build again this example" << std::endl;
555 std::cout <<
"You do not build ViSP with c++11 or higher compiler flag" << std::endl;
556 std::cout <<
"Tip:" << std::endl;
557 std::cout <<
"- Configure ViSP again using cmake -DUSE_CXX_STANDARD=11, and build again this example" << std::endl;
The vpDisplayGTK allows to display image using the GTK 3rd party library. Thus to enable this class G...
Use the X11 console to display images on unix-like OS. Thus to enable this class X11 should be instal...
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)
error that can be emitted by ViSP classes.
static void merge(const vpImage< unsigned char > *R, const vpImage< unsigned char > *G, const vpImage< unsigned char > *B, const vpImage< unsigned char > *a, vpImage< vpRGBa > &RGBa)
static void split(const vpImage< vpRGBa > &src, vpImage< unsigned char > *pR, vpImage< unsigned char > *pG, vpImage< unsigned char > *pB, vpImage< unsigned char > *pa=nullptr)
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
static void gaussianBlur(const vpImage< ImageType > &I, vpImage< OutputType > &GI, unsigned int size=7, FilterType sigma=0., bool normalize=true, const vpImage< bool > *p_mask=nullptr)
Definition of the vpImage class member functions.
unsigned int getSize() const
Type * bitmap
points toward the bitmap
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
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)
void saveFrame(vpImage< vpRGBa > &I)
void setFileName(const std::string &filename)
void open(vpImage< vpRGBa > &I)
VISP_EXPORT double measureTimeMs()
VISP_EXPORT std::string getDateTime(const std::string &format="%Y/%m/%d %H:%M:%S")