cv.VideoCapture() API backend issue

ISSUE:
Unable to open a stream (cv.VideoCapture()) from the USB capture device using a non-V4L2 API backend
I want 640x480 resolution in any workable format
I can only get 480x320

SETUP:
Ras-pi4, Raspian Billseye 5.15.32-v7l+, openCV 4.6.0, Python 3.9.2.
easierCAP video capture USB device, driver: uvcvideo 5.15.32

FACTS:

  1. device in question is /dev/video0
    Bus 001 Device 003: ID 534d:0021 MacroSilicon MS210x Video Grabber [EasierCAP]
    It will open and work fine as backend V4L2 at 480x320 resolution

  2. USB device “easierCAP” has the following formats available:
    $ v4l2-ctl --list-formats-ext
    .ioctl: VIDIOC_ENUM_FMT
    Type: Video Capture

     Size: Discrete 480x320
     	Interval: Discrete 0.040s (25.000 fps)
     Size: Discrete 640x480
     	Interval: Discrete 0.040s (25.000 fps)
     	Interval: Discrete 0.050s (20.000 fps)
     	Interval: Discrete 0.067s (15.000 fps)
     	Interval: Discrete 0.100s (10.000 fps)
     Size: Discrete 720x480
     	Interval: Discrete 0.040s (25.000 fps)
     	Interval: Discrete 0.050s (20.000 fps)
     	Interval: Discrete 0.067s (15.000 fps)
     	Interval: Discrete 0.100s (10.000 fps)
    
     Size: Discrete 480x320
     	Interval: Discrete 0.040s (25.000 fps)
    
  3. v4l2-ctl can set the device to 640x480, MJPG capture, which is what I want:
    $ v4l2-ctl -V
    Format Video Capture:
    Width/Height : 640/480
    Pixel Format : ‘MJPG’ (Motion-JPEG)
    Field : None
    Bytes per Line : 0
    Size Image : 614400
    Colorspace : sRGB
    Transfer Function : Rec. 709
    YCbCr/HSV Encoding: ITU-R 601
    Quantization : Default (maps to Full Range)
    Flags :

  4. qv4l2 shows the same as #3 above and can capture the 640 MJPG stream.
    qv4l2 can also set the device to any of the formats listed in #2, and can capture fine.

THE PROBLEM:
I run the following code:

import cv2 as cv

device= '/dev/video0'
api= cv.CAP_OPENCV_MJPEG
cap = cv.VideoCapture(device, api)

print("cap.getBackendName = {}".format(cap.getBackendName()))
""" prints 'V4L2' """

ret, frame=cap.read()
print("Frame actual is {}x{}".format(str(frame.shape[1]), str(frame.shape[0])))
""" prints 'Frame actual is 480x320' """

!! Not what I want.

Back in the shell I look at the device setup again and it has changed to 480x320 in YUYV format:
$ v4l2-ctl -V
Format Video Capture:
Width/Height : 480/320
Pixel Format : ‘YUYV’ (YUYV 4:2:2)
Field : None
Bytes per Line : 960
Size Image : 307200
Colorspace : sRGB
Transfer Function : Rec. 709
YCbCr/HSV Encoding: ITU-R 601
Quantization : Default (maps to Limited Range)
Flags :

From this I conclude cv.VideoCapture is simply opening the device with the V4L2 backend, ignoring my MJPEG request.

I HAVE TRIED:

  • Every API listed in the cv::VideoCaptureAPIs enumerations.
  • Verifying the API is available:
    print("stream backendS = " +str(cv.videoio_registry.getStreamBackends()))
    prints ‘stream backendS = (1900, 1800, 2300, 200, 2000, 2200)’
    NOTE: cv.CAP_OPENCV_MJPEG = 2200
    NOTE: cv.CAP_V4L2 = 200
  • after creating the capture object cap, I release it and then open again using cap.open(device, api) just to see if that might trick it into working. No good…

MY THINKING:
I can’t blame the device and driver given that v4l2-ctl and qv4l2 seems to handle the device correctly.
I blame something funny in openCV VideoCapture(). It does not seem to want to open any of the MJPG formats.

Any input is welcome. Many thanks,
Jeff C.

yeah you’re looking in the wrong places.

you can’t just “try” random backends without knowing what they are and whether they’re available on your OS and for that device. I mean, yes you can, but that was unlikely to lead far. you’ve got a regular USB UVC device there. those other backends are for niche applications, not for the majority of uses.

V4L is the only one you can use, for an USB webcam, on linux. you don’t have to specify that explicitly. just open device ID 0 and OpenCV does the rest.

you need to call .set() using CAP_PROP_* values. you did not say that you know about that method. your entire post contains no references to cap.set() or CAP_PROP_*, but you reference everything else, so I’m assuming it’s not an oversight in the writing of your post.

you can’t just poke the device with v4l2-ctl and hope that OpenCV will be okay with that. OpenCV does its own initialization, to a default of 640x480, or whatever’s closest that the device will accept. OpenCV will (currently) not touch any MJPEG modes automatically. it’s always defaulting to something uncompressed, in the interest of better data.

if you want a specific width and height, you need to set the width and height properties. you use the cap.set() method for that.

if that doesn’t happen to work on its own, you need to try first setting CAP_PROP_FOURCC to VideoWriter_fourcc(*"MJPG").

that should work.

Hello crackwitz,

Your suggestion that I first try CAP_PROP_FOURCCdid the trick. I would not have thought what was writable after the cap object was created.

Many thanks for the help.

yeah everything’s writable. if needed, it’ll reinitialize on its own.

if a parameter isn’t “acceptable”, like when you set width and height and between calls there’s a “strange” frame size (640x480 → 1280x480 → 1280x720), which doesn’t match a valid mode of the device, it won’t re-initialize the device yet, but will keep the parameter value, and will reinitialize when all parameters are set to match a requestable camera mode.

the FOURCC MJPG thing seems to be less well-known/documented. if you ever want to get your feet wet contributing to OpenCV, documentation sources can be found in a few different places, depending on whether it’s API docs (header file in modules/videoio/include), or articles and tutorials (markdowns in modules/videoio/doc).