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.