Search code examples
pythonpython-3.xopencvstderr

How to capture messages written to stderr by OpenCV?


In case of invalid parameters, cv2.VideoWriter writes stuff to stderr. here is a minimal example:

import cv2

cv2.VideoWriter("foo.mp4", cv2.VideoWriter_fourcc(*"mp4v"), 90000, (240, 240))

Running like so

python3 video_verification/verification.py 2> err.txt

one can see the expected errors in err.txt:

[mpeg4 @ 0x13e4640] timebase 1/90000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535
[ERROR:0] global /tmp/pip-req-build-13uokl4r/opencv/modules/videoio/src/cap_ffmpeg_impl.hpp (2705) open Could not open codec mpeg4, error: Unspecified error
[ERROR:0] global /tmp/pip-req-build-13uokl4r/opencv/modules/videoio/src/cap_ffmpeg_impl.hpp (2722) open VIDEOIO/FFMPEG: Failed to initialize VideoWriter

However, I'm unable to capture this directly in Python. Here are my two (failing) attemps so far:

import io
from contextlib import redirect_stdout, redirect_stderr

import cv2

f_out = io.StringIO()
f_err = io.StringIO()
with redirect_stdout(f_out):
    with redirect_stderr(f_err):
        fps = 90000
        writer = cv2.VideoWriter(
            "foo.mp4", cv2.VideoWriter_fourcc(*"mp4v"),
            fps, (240, 240)
        )

print(f"stdout: {f_out.getvalue()}")
print(f"stderr: {f_err.getvalue()}")
import io
import sys

import cv2


def flush():
    sys.stdout.flush()
    sys.stderr.flush()


flush()

original_stdout = sys.stdout
original_stderr = sys.stderr

captured_stdout = io.StringIO()
captured_stderr = io.StringIO()

sys.stdout = captured_stdout
sys.stderr = captured_stderr

flush()

cv2.VideoWriter("foo.mp4", cv2.VideoWriter_fourcc(*"mp4v"), 90000, (240, 240))

flush()

sys.stdout = original_stdout
sys.stderr = original_stderr

print(f"stdout: {captured_stdout.getvalue()}")
print(f"stderr: {captured_stderr.getvalue()}")

I would love to learn what I'm doing wrong. :-)


Solution

  • I've found the wurlitzer library, which can do exactly that, i.e., capture the streams written to by a C library:

    import cv2
    from wurlitzer import pipes
    
    with pipes() as (out, err):
        cv2.VideoWriter("foo.mp4", cv2.VideoWriter_fourcc(*"mp4v"), 90000, (240, 240))
    
    print(f"stderr: {err.read()}")