How to write video files with OpenCV

Video writing in OpenCV turns processed frames, generated graphics, or annotated camera output into a file that other tools can replay. The VideoWriter object owns the output path, codec code, frame rate, and frame size, so those values need to match every frame sent to it.

The Python script writes generated NumPy frames to an .mp4 file with the mp4v FourCC. Each frame is a color image with the same width and height passed to cv.VideoWriter, and the script checks isOpened() before writing.

Codec support comes from the video backend compiled into the local OpenCV build. Reopening the output with cv.VideoCapture confirms that the file was finalized, the frame count is readable, and the saved dimensions match the writer settings.

Steps to write a video file with OpenCV:

  1. Save the video writer script.
    write_video.py
    #!/usr/bin/env python3
    from pathlib import Path
     
    import cv2 as cv
    import numpy as np
     
     
    output_path = Path("output/generated-motion.mp4")
    width, height = 640, 360
    fps = 24.0
    frame_count = 72
    fourcc_label = "mp4v"
    fourcc = cv.VideoWriter_fourcc(*fourcc_label)
     
    output_path.parent.mkdir(parents=True, exist_ok=True)
    writer = cv.VideoWriter(str(output_path), fourcc, fps, (width, height))
     
    if not writer.isOpened():
        raise SystemExit(f"Could not open VideoWriter for: {output_path}")
     
    for index in range(frame_count):
        frame = np.zeros((height, width, 3), dtype=np.uint8)
        frame[:] = (34, 42, 58)
     
        progress = index / max(frame_count - 1, 1)
        center_x = int(70 + progress * (width - 140))
        center_y = height // 2
        color = (40, 180 + index % 60, 245)
     
        cv.circle(frame, (center_x, center_y), 42, color, -1)
        cv.putText(
            frame,
            f"Frame {index + 1:02d}/{frame_count}",
            (36, 62),
            cv.FONT_HERSHEY_SIMPLEX,
            1.1,
            (245, 245, 245),
            2,
            cv.LINE_AA,
        )
        cv.putText(
            frame,
            "OpenCV VideoWriter",
            (36, height - 34),
            cv.FONT_HERSHEY_SIMPLEX,
            0.85,
            (215, 230, 255),
            2,
            cv.LINE_AA,
        )
     
        writer.write(frame)
     
    writer.release()
     
    capture = cv.VideoCapture(str(output_path))
    if not capture.isOpened():
        raise SystemExit(f"Could not reopen video: {output_path}")
     
    saved_frames = int(capture.get(cv.CAP_PROP_FRAME_COUNT))
    saved_fps = capture.get(cv.CAP_PROP_FPS)
    saved_width = int(capture.get(cv.CAP_PROP_FRAME_WIDTH))
    saved_height = int(capture.get(cv.CAP_PROP_FRAME_HEIGHT))
    ok, first_frame = capture.read()
    capture.release()
     
    if not ok or first_frame is None:
        raise SystemExit(f"Could not read first frame from: {output_path}")
     
    size_kib = output_path.stat().st_size / 1024
     
    print(f"wrote: {output_path}")
    print(f"fourcc: {fourcc_label}")
    print(f"frames written: {frame_count}")
    print(f"frames read back: {saved_frames}")
    print(f"fps: {saved_fps:.1f}")
    print(f"size: {saved_width}x{saved_height}")
    print(f"first frame: {first_frame.shape[1]}x{first_frame.shape[0]}x{first_frame.shape[2]}")
    print(f"file size: {size_kib:.1f} KiB")

    For project frames, keep width and height equal to the actual frame shape. Common FFMPEG paths handle even dimensions most predictably for MP4 output.

  2. Run the script to write and verify the video.
    $ python3 write_video.py
    wrote: output/generated-motion.mp4
    fourcc: mp4v
    frames written: 72
    frames read back: 72
    fps: 24.0
    size: 640x360
    first frame: 640x360x3
    file size: 142.4 KiB

    The exact file size can change with the backend, encoder settings, and frame content. Success means the writer opened, the read-back frame count matches frame_count, and the reported size matches width by height.