allo- / virtual_webcam_background

Use a virtual webcam background and overlays with body-pix and v4l2loopback
GNU General Public License v3.0
306 stars 47 forks source link

Using video filter results in ZeroDivisionError #80

Open faxm0dem opened 2 years ago

faxm0dem commented 2 years ago

Using the video filter with the following config item:

  - "empty": [["video", "tumbleweed.gif", 5]]

I get the following error trace :

Loading video: tumbleweed.gif
[ERROR:0] global /tmp/scratch/3942/pip-install-5mr76rqs/opencv-python_bb4cd8da7f5644bc83c7d0718e3f53ca/opencv/modules/videoio/src/cap.cpp (140) open VIDEOIO(CV_IMAGES): raised OpenCV exception:

OpenCV(4.4.0) /tmp/scratch/3942/pip-install-5mr76rqs/opencv-python_bb4cd8da7f5644bc83c7d0718e3f53ca/opencv/modules/videoio/src/cap_images.cpp:253: error: (-5:Bad argument) CAP_IMAGES: can't find starting number (in the name of file): tumbleweed.gif in function 'icvExtractPattern'

Traceback (most recent call last):
  File "/home/me/git/virtual_webcam_background/./virtual_webcam.py", line 154, in <module>
    layers = reload_layers(config)
  File "/home/me/git/virtual_webcam_background/./virtual_webcam.py", line 60, in reload_layers
    layers.append((layer_type, filters.get_filters(config, layer_filters)))
  File "/home/me/git/virtual_webcam_background/filters/__init__.py", line 39, in get_filters
    image_filters.append(image_filter_class(config=config,
  File "/home/me/git/virtual_webcam_background/filters/video.py", line 69, in __init__
    self.reload_video()
  File "/home/me/git/virtual_webcam_background/filters/video.py", line 81, in reload_video
    self.image = next(self.generator)
  File "/home/me/git/virtual_webcam_background/filters/video.py", line 28, in lazy_load_video
    every_nth_frame = np.ceil(fps / target_fps)
ZeroDivisionError: division by zero
allo- commented 2 years ago

I am not sure how opencv calculates the FPS of a GIF, but it seems that it return 0. The relevant code is

    if target_fps <= 0 or target_fps > fps:
        target_fps = fps
    every_nth_frame = np.ceil(fps / target_fps) # division by zero happens here

so it must have target_fps > fps (so target_fps=5 is changed to fps) and fps=0 (because of the division by zero error)

Can you try:

    if fps <= 0:
        fps = 1
    if target_fps <= 0 or target_fps > fps:
        target_fps = fps
    every_nth_frame = np.ceil(fps / target_fps) # division by zero happens here

It would also be interesting, if you could add a print(fps) before the block and tell me what fps opencv calculcates. I wonder if it uses 0 for "No FPS given in the file".

faxm0dem commented 2 years ago

I checked and it does calculate 0. I tried using the gif and also a conversion to mp4 (using ffmpeg defaults). Also, I forced the fps to 1 as requested, and it get:

Loading video: tumbleweed.mp4
============================================
Found a frame rate of 0
============================================
Finished loading video: tumbleweed.mp4
Error loading video (format not supported by OpenCV?): tumbleweed.mp4
Loading video: tumbleweed.mp4
============================================
Found a frame rate of 0
============================================
Finished loading video: tumbleweed.mp4
Error loading video (format not supported by OpenCV?): tumbleweed.mp4

I guess I'm only having a format issue here. Maybe some missing explicit requirement ?

allo- commented 2 years ago

The gif should most likely work once the frame rate issue is resolved. For mp4 it depends on which video codec ffmpeg is using and if opencv can read it.

The error is returned when the filter cannot read the first frame of a video. When I see it correctly, you may get problems when your frame rate is larger than the number of images in the video.

For fps=5

        frame_no += 1
        if (frame_no % every_nth_frame) != 0:
            continue

skips the first 4 frames. When the video has less frames, the loop ends and the generator is finished without returning a frame at all. reload_video reads the first frame returned by the generator when opening the video and generates the error when the generator does not return at least one frame.

You could test with fps=1 if the video formats are supported, then every_nth_frame should be 1 and the generator returns at least the first frame of the video, if the video can be read by OpenCV.

faxm0dem commented 2 years ago

Whatever video file I choose, I get the same error. I'm guessing we're chasing a red herring here

allo- commented 2 years ago

I don't have the time to write a minimum example right now, but you could look at lazy_load_video and try to extract the minimum code for testing. You need to create a VideoCapture object and then test .isOpened() and .read().

When every file fails, I could imagine wrong paths (relative to the wrong directory?). I am not sure if you wouldn't get some "file not found" exception, though.