Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

How to make OpenCV to ask libv4l YUYV pixelformat instead of BGR24?

I use at the moment OpenCV 2.4.4 on Linux Mint 14 (aka Ubuntu 12.10). I got 2 identical webcams (Logitech C270) and I need to run them simultaneously. From the start I want to say - using 2 USB hosts is not a solution for me. It is possible to run them simultaneously at 640x480@30 fps. But with one condition - use YUYV pixelformat. As all of us knows OpenCV uses BGR24 pixelformat. And here starts strange things... I am running v4l2-ctl -d /dev/video0 --list-formats and I get this:

    Index       : 0
    Type        : Video Capture
    Pixel Format: 'YUYV'
    Name        : YUV 4:2:2 (YUYV)

    Index       : 1
    Type        : Video Capture
    Pixel Format: 'MJPG' (compressed)
    Name        : MJPEG

As you can see we don't have here BGR24 pixelformat. For testing purposes I use qv4l2 application. It can list supported pixelformats and one of them is BGR24! How is that possible? Strange things continues... If I try BGR24 with qv4l2 (OpenCV force this one) I can't get higher than 15 fps for simultaneous running. How is that possible - call for unlisted pixelformat and get it?! The most strange thing - it gets it and USB host can't let through such a large datastream :\ This is weird because it means that v4l doesn't convert YUYV to BGR24, it just gets BGR24 from camera (???)! One the solution would be lowering fps, but I don't want to do that. As far as I know to achieve what I want I have to modify ./modules/highgui/src/cap_libv4l.cpp (cap_v4l.cpp doesn't play role, because it isn't even compiled). From the start I want to say - I am lame at programming. I made this changes:

1) I took from cap_v4l.cpp code which converts YUYV to RGB24:

/* convert from 4:2:2 YUYV interlaced to RGB24 */
/* based on ccvt_yuyv_bgr32() from camstream */
#define SAT(c) \
   if (c & (~255)) { if (c < 0) c = 0; else c = 255; }

static void
yuyv_to_rgb24 (int width, int height, unsigned char *src, unsigned char *dst)
{
   unsigned char *s;
   unsigned char *d;
   int l, c;
   int r, g, b, cr, cg, cb, y1, y2;

   l = height;
   s = src;
   d = dst;
   while (l--) {
      c = width >> 1;
      while (c--) {
         y1 = *s++;
         cb = ((*s - 128) * 454) >> 8;
         cg = (*s++ - 128) * 88;
         y2 = *s++;
         cr = ((*s - 128) * 359) >> 8;
         cg = (cg + (*s++ - 128) * 183) >> 8;

         r = y1 + cr;
         b = y1 + cb;
         g = y1 - cg;
         SAT(r);
         SAT(g);
         SAT(b);

     *d++ = b;
     *d++ = g;
     *d++ = r;

         r = y2 + cr;
         b = y2 + cb;
         g = y2 - cg;
         SAT(r);
         SAT(g);
         SAT(b);

     *d++ = b;
     *d++ = g;
     *d++ = r;
      }
   }
} 

2) Made OpenCV ask for YUYV instead BGR24 (here I show already modified code):

  /* libv4l will convert from any format to V4L2_PIX_FMT_YUYV */
  CLEAR (capture->form);
  capture->form.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  capture->form.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
  capture->form.fmt.pix.field       = V4L2_FIELD_ANY;
  capture->form.fmt.pix.width = capture->width;
  capture->form.fmt.pix.height = capture->height;
...
  if (V4L2_PIX_FMT_YUYV != capture->form.fmt.pix.pixelformat) {
      fprintf( stderr, "HIGHGUI ERROR: libv4l unable convert to requested pixfmt\n");
      return -1;

3) Made OpenCV to call for conversion to obtain image data in right pixelformat:

  if (capture->is_v4l2_device == 1)
  {

    if(capture->buffers[capture->bufferIndex].start){
//      memcpy((char *)capture->frame.imageData,
//           (char *)capture->buffers[capture->bufferIndex].start,
//           capture->frame.imageSize);
//    }
        yuyv_to_rgb24(capture->form.fmt.pix.width,
                  capture->form.fmt.pix.height,
                  (unsigned char*)(capture->buffers[capture->bufferIndex].start),
                  (unsigned char*)capture->frame.imageData);}
  }

I successfully compiled OpenCV using modified cap_libv4l.cpp but I got no changes - I still can't get both cameras working. It means I did not forced in the right way OpenCV to ask for YUYV. Can you please help me and get the right modifications in code to make it working in the way I want it? I am pretty sure many of OpenCV users will be thankful if we will be able to write code to run 2 webcams on VGA resolution with 30 fps using only one USB host (this is especially critical for small ARM machines)! If any additional info is required just ask!

[Solved] How to make OpenCV to ask libv4l YUYV pixelformat instead of BGR24?

I use at the moment OpenCV 2.4.4 on Linux Mint 14 (aka Ubuntu 12.10). I got 2 identical webcams (Logitech C270) and I need to run them simultaneously. From the start I want to say - using 2 USB hosts is not a solution for me. It is possible to run them simultaneously at 640x480@30 fps. But with one condition - use YUYV pixelformat. As all of us knows OpenCV uses BGR24 pixelformat. And here starts strange things... I am running v4l2-ctl -d /dev/video0 --list-formats and I get this:

    Index       : 0
    Type        : Video Capture
    Pixel Format: 'YUYV'
    Name        : YUV 4:2:2 (YUYV)

    Index       : 1
    Type        : Video Capture
    Pixel Format: 'MJPG' (compressed)
    Name        : MJPEG

As you can see we don't have here BGR24 pixelformat. For testing purposes I use qv4l2 application. It can list supported pixelformats and one of them is BGR24! How is that possible? Strange things continues... If I try BGR24 with qv4l2 (OpenCV force this one) I can't get higher than 15 fps for simultaneous running. How is that possible - call for unlisted pixelformat and get it?! The most strange thing - it gets it and USB host can't let through such a large datastream :\ This is weird because it means that v4l doesn't convert YUYV to BGR24, it just gets BGR24 from camera (???)! One the solution would be lowering fps, but I don't want to do that. As far as I know to achieve what I want I have to modify ./modules/highgui/src/cap_libv4l.cpp (cap_v4l.cpp doesn't play role, because it isn't even compiled). From the start I want to say - I am lame at programming. I made this changes:

1) I took from cap_v4l.cpp code which converts YUYV to RGB24:

/* convert from 4:2:2 YUYV interlaced to RGB24 */
/* based on ccvt_yuyv_bgr32() from camstream */
#define SAT(c) \
   if (c & (~255)) { if (c < 0) c = 0; else c = 255; }

static void
yuyv_to_rgb24 (int width, int height, unsigned char *src, unsigned char *dst)
{
   unsigned char *s;
   unsigned char *d;
   int l, c;
   int r, g, b, cr, cg, cb, y1, y2;

   l = height;
   s = src;
   d = dst;
   while (l--) {
      c = width >> 1;
      while (c--) {
         y1 = *s++;
         cb = ((*s - 128) * 454) >> 8;
         cg = (*s++ - 128) * 88;
         y2 = *s++;
         cr = ((*s - 128) * 359) >> 8;
         cg = (cg + (*s++ - 128) * 183) >> 8;

         r = y1 + cr;
         b = y1 + cb;
         g = y1 - cg;
         SAT(r);
         SAT(g);
         SAT(b);

     *d++ = b;
     *d++ = g;
     *d++ = r;

         r = y2 + cr;
         b = y2 + cb;
         g = y2 - cg;
         SAT(r);
         SAT(g);
         SAT(b);

     *d++ = b;
     *d++ = g;
     *d++ = r;
      }
   }
} 

2) Made OpenCV ask for YUYV instead BGR24 (here I show already modified code):

  /* libv4l will convert from any format to V4L2_PIX_FMT_YUYV */
  CLEAR (capture->form);
  capture->form.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  capture->form.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
  capture->form.fmt.pix.field       = V4L2_FIELD_ANY;
  capture->form.fmt.pix.width = capture->width;
  capture->form.fmt.pix.height = capture->height;
...
  if (V4L2_PIX_FMT_YUYV != capture->form.fmt.pix.pixelformat) {
      fprintf( stderr, "HIGHGUI ERROR: libv4l unable convert to requested pixfmt\n");
      return -1;

3) Made OpenCV to call for conversion to obtain image data in right pixelformat:

  if (capture->is_v4l2_device == 1)
  {

    if(capture->buffers[capture->bufferIndex].start){
//      memcpy((char *)capture->frame.imageData,
//           (char *)capture->buffers[capture->bufferIndex].start,
//           capture->frame.imageSize);
//    }
        yuyv_to_rgb24(capture->form.fmt.pix.width,
                  capture->form.fmt.pix.height,
                  (unsigned char*)(capture->buffers[capture->bufferIndex].start),
                  (unsigned char*)capture->frame.imageData);}
  }

I successfully compiled OpenCV using modified cap_libv4l.cpp but I got no changes - I still can't get both cameras working. It means I did not forced in the right way OpenCV to ask for YUYV. Can you please help me and get the right modifications in code to make it working in the way I want it? I am pretty sure many of OpenCV users will be thankful if we will be able to write code to run 2 webcams on VGA resolution with 30 fps using only one USB host (this is especially critical for small ARM machines)! If any additional info is required just ask!