pythonopencvffmpegjpegwebcam

Frames took with ELP camera has unknown pixel format at FHD?


I'm trying to take a one frame ever x seconds from my usb camera. Name of the camera is: ELP-USBFHD06H-SFV(5-50). Code is not 100% done yet, but I'm using it this way right now ↓ (shot fn is called from main.py in a loop)


import cv2
import subprocess

from time import sleep
from collections import namedtuple

from errors import *

class Camera:
    def __init__(self, cam_index, res_width, res_height, pic_format, day_time_exposure_ms, night_time_exposure_ms):
        Resolution = namedtuple("resolution", ["width", "height"])
        self.manual_mode(True)

        self.cam_index = cam_index
        self.camera_resolution = Resolution(res_width, res_height)
        self.picture_format = pic_format
        self.day_time_exposure_ms = day_time_exposure_ms
        self.night_time_exposure_ms = night_time_exposure_ms

        self.started: bool = False
        self.night_mode = False

        self.cap = cv2.VideoCapture(self.cam_index, cv2.CAP_V4L2)
        self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.camera_resolution.width)
        self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.camera_resolution.height)
        self.cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*self.picture_format))

    

    def start(self):
        sleep(1)
        if not self.cap.isOpened():
            return CameraCupError()

        self.set_exposure_time(self.day_time_exposure_ms)
        self.set_brightness(0)
        sleep(0.1)
        
        self.started = True



    def shot(self, picture_name, is_night):
        if not self.started:
            return InitializationError()

        self.configure_mode(is_night)

        # Clear buffer
        for _ in range(5):
            ret, _ = self.cap.read()

        ret, frame = self.cap.read()

        sleep(0.1)

        if ret:
            print(picture_name)
            cv2.imwrite(picture_name, frame)
            return True

        else:
            print("No photo")
            return False


    
    def release(self):
        self.set_exposure_time(156)
        self.set_brightness(0)
        self.manual_mode(False)
        self.cap.release()



    def manual_mode(self, switch: bool):
        if switch:
            subprocess.run(["v4l2-ctl", "--set-ctrl=auto_exposure=1"])
        else:
            subprocess.run(["v4l2-ctl", "--set-ctrl=auto_exposure=3"])
        sleep(1)

    
    
    def configure_mode(self, is_night):
        if is_night == self.night_mode:
            return

        if is_night:
            self.night_mode = is_night
            self.set_exposure_time(self.night_time_exposure_ms)
            self.set_brightness(64)
        else:
            self.night_mode = is_night
            self.set_exposure_time(self.day_time_exposure_ms)
            self.set_brightness(0)
        sleep(0.1)



    def set_exposure_time(self, ms: int):
        ms = int(ms)
        default_val = 156

        if ms < 1 or ms > 5000:
            ms = default_val

        self.cap.set(cv2.CAP_PROP_EXPOSURE, ms)



    def set_brightness(self, value: int):
        value = int(value)
        default_val = 0

        if value < -64 or value > 64:
            value = default_val

        self.cap.set(cv2.CAP_PROP_BRIGHTNESS, value)

Here are settings for the camera (yaml file)

camera:
  camera_index: 0
  res_width: 1920
  res_height: 1080
  picture_format: "MJPG"
  day_time_exposure_ms: 5
  night_time_exposure_ms: 5000
  photos_format: "jpg"

I do some configs like set manual mode for the camera, change exposure/brightness and saving frame. Also the camera is probably catching the frames to the buffer (it is not saving latest frame in real time: it's more laggish), so I have to clear buffer every time. like this

        # Clear buffer from old frames
        for _ in range(5):
            ret, _ = self.cap.read()
        
        # Get a new frame
        ret, frame = self.cap.read()

What I really don't like, but I could find a better way (tldr: setting buffer for 1 frame doesn't work on my camera).

Frames saved this method looks good with 1920x1080 resolution. BUT when I try to run ffmpeg command to make a timelapse from saved jpg file like this

ffmpeg -framerate 20 -pattern_type glob -i "*.jpg" -c:v libx264 output.mp4

I got an error like this one

[image2 @ 0x555609c45240] Could not open file : 08:59:20.jpg
[image2 @ 0x555609c45240] Could not find codec parameters for stream 0 (Video: mjpeg, none(bt470bg/unknown/unknown)): unspecified size
Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options
Input #0, image2, from '*.jpg':
  Duration: 00:00:00.05, start: 0.000000, bitrate: N/A
  Stream #0:0: Video: mjpeg, none(bt470bg/unknown/unknown), 20 fps, 20 tbr, 20 tbn
Output #0, mp4, to 'output.mp4':
Output file #0 does not contain any stream

Also when I try to copy the files from Linux to Windows I get some weird copy failing error and option to skip the picture. But even when I press the skip button, the picture is copied and can be opened. I'm not sure what is wrong with the format, but the camera is supporting MPEG at 1920x1080.

>>> v4l2-ctl --all

Driver Info:
        Driver name      : uvcvideo
        Card type        : H264 USB Camera: USB Camera
        Bus info         : usb-xhci-hcd.1-1
        Driver version   : 6.6.51
        Capabilities     : 0x84a00001
                Video Capture
                Metadata Capture
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps      : 0x04200001
                Video Capture
                Streaming
                Extended Pix Format
Media Driver Info:
        Driver name      : uvcvideo
        Model            : H264 USB Camera: USB Camera
        Serial           : 2020032801
        Bus info         : usb-xhci-hcd.1-1
        Media version    : 6.6.51
        Hardware revision: 0x00000100 (256)
        Driver version   : 6.6.51
Interface Info:
        ID               : 0x03000002
        Type             : V4L Video
Entity Info:
        ID               : 0x00000001 (1)
        Name             : H264 USB Camera: USB Camera
        Function         : V4L2 I/O
        Flags            : default
        Pad 0x0100000d   : 0: Sink
          Link 0x0200001a: from remote pad 0x1000010 of entity 'Extension 4' (Video Pixel Formatter): Data, Enabled, Immutable
Priority: 2
Video input : 0 (Camera 1: ok)
Format Video Capture:
        Width/Height      : 1920/1080
        Pixel Format      : 'MJPG' (Motion-JPEG)
        Field             : None
        Bytes per Line    : 0
        Size Image        : 4147789
        Colorspace        : sRGB
        Transfer Function : Default (maps to sRGB)
        YCbCr/HSV Encoding: Default (maps to ITU-R 601)
        Quantization      : Default (maps to Full Range)
        Flags             :
Crop Capability Video Capture:
        Bounds      : Left 0, Top 0, Width 1920, Height 1080
        Default     : Left 0, Top 0, Width 1920, Height 1080
        Pixel Aspect: 1/1
Selection Video Capture: crop_default, Left 0, Top 0, Width 1920, Height 1080, Flags:
Selection Video Capture: crop_bounds, Left 0, Top 0, Width 1920, Height 1080, Flags:
Streaming Parameters Video Capture:
        Capabilities     : timeperframe
        Frames per second: 15.000 (15/1)
        Read buffers     : 0

User Controls

                     brightness 0x00980900 (int)    : min=-64 max=64 step=1 default=0 value=64
                       contrast 0x00980901 (int)    : min=0 max=64 step=1 default=32 value=32
                     saturation 0x00980902 (int)    : min=0 max=128 step=1 default=56 value=56
                            hue 0x00980903 (int)    : min=-40 max=40 step=1 default=0 value=0
        white_balance_automatic 0x0098090c (bool)   : default=1 value=1
                          gamma 0x00980910 (int)    : min=72 max=500 step=1 default=100 value=100
                           gain 0x00980913 (int)    : min=0 max=100 step=1 default=0 value=0
           power_line_frequency 0x00980918 (menu)   : min=0 max=2 default=1 value=1 (50 Hz)
                                0: Disabled
                                1: 50 Hz
                                2: 60 Hz
      white_balance_temperature 0x0098091a (int)    : min=2800 max=6500 step=1 default=4600 value=4600 flags=inactive
                      sharpness 0x0098091b (int)    : min=0 max=6 step=1 default=3 value=3
         backlight_compensation 0x0098091c (int)    : min=0 max=2 step=1 default=1 value=1

Camera Controls

                  auto_exposure 0x009a0901 (menu)   : min=0 max=3 default=3 value=1 (Manual Mode)
                                1: Manual Mode
                                3: Aperture Priority Mode
         exposure_time_absolute 0x009a0902 (int)    : min=1 max=5000 step=1 default=156 value=5000
     exposure_dynamic_framerate 0x009a0903 (bool)   : default=0 value=0

I also tried to save the picture using ffmpeg in a case something is not right with opencv like this:

ffmpeg -f v4l2 -framerate 30 -video_size 1920x1080 -i /dev/video0 -c:v libx264 -preset fast -crf 23 -t 00:01:00 output.mp4

It is saving the picture but also changing its format

[video4linux2,v4l2 @ 0x555659ed92b0] The V4L2 driver changed the video from 1920x1080 to 800x600
[video4linux2,v4l2 @ 0x555659ed92b0] The driver changed the time per frame from 1/30 to 1/15

But the format looks right when set it back to FHD using v4l2


>>> v4l2-ctl --device=/dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=MJPG
>>> v4l2-ctl --get-fmt-video

Format Video Capture:
        Width/Height      : 1920/1080
        Pixel Format      : 'MJPG' (Motion-JPEG)
        Field             : None
        Bytes per Line    : 0
        Size Image        : 4147789
        Colorspace        : sRGB
        Transfer Function : Default (maps to sRGB)
        YCbCr/HSV Encoding: Default (maps to ITU-R 601)
        Quantization      : Default (maps to Full Range)
        Flags             :

I'm not sure what could be wrong with the format/camera and I don't think I have enough information to figure it out.

I tried to use ffmpeg instead of opencv and also change a few settings in opencv's cup config.


Solution

  • Got it. Problem was with the name of saved .jpg files. From 11:50:45.jpg to 11-50-45.jpg did the trick.