Back to Blog
Python

Python OpenCV: Write and Process Video

python opencv write and process video: Learn how to read, process, and write video files with Python OpenCV using VideoCapture and VideoWriter, including codec selecti...

OpenCVVideo ProcessingVideoWriterComputer VisionFourCC
A diagram showing video frames flowing from a capture source through a processing stage into an output video file, representing the OpenCV read-process-write pipeline.

To write and process video with Python OpenCV, you build a loop that reads frames from a VideoCapture object, applies a transformation, and passes the result to a VideoWriter. This article covers the full python opencv write and process video pipeline: setting up the loop correctly, choosing a codec, and avoiding the timing and format mistakes that produce broken or empty output files.

The Core Loop: Reading and Writing Frames

The foundation of any video processing script is the frame loop. VideoCapture opens a video file and exposes frames one at a time, while VideoWriter receives processed frames and encodes them into an output container.

import cv2 capture = cv2.VideoCapture("input.mp4") fourcc = cv2.VideoWriter_fourcc(*"mp4v") writer = cv2.VideoWriter("output.mp4", fourcc, 30.0, (1920, 1080)) while True: ret, frame = capture.read() if not ret: break processed = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) processed_bgr = cv2.cvtColor(processed, cv2.COLOR_GRAY2BGR) writer.write(processed_bgr) capture.release() writer.release()

The read() method returns a tuple: a boolean that indicates whether a frame was successfully decoded, and the frame itself. When ret is False, the video has ended or a decoding error occurred, and the loop must break. The release() calls are not optional; they flush buffered frames to disk and free the underlying file handles.

Note that VideoWriter requires a three-channel BGR frame. If your processing step produces a grayscale image, you must convert it back to BGR before calling write(), otherwise the output file will be corrupt or the writer will raise an error.

Choosing a Codec with FourCC

The codec is specified by a FourCC code, a four-character identifier that tells the writer which encoder to use. OpenCV does not ship with encoders; it relies on the codecs installed on the system, usually via FFmpeg. This means the set of available FourCC codes depends on your OpenCV build and your operating system.

Common choices:

FourCCContainerTypical use
mp4vMP4Broad compatibility, MPEG-4 Part 2
avc1MP4H.264, smaller files, wider player support
XVIDAVILegacy AVI workflows
MJPGAVI/MP4Motion JPEG, fast but large

The FourCC is created with cv2.VideoWriter_fourcc(*"mp4v"). The unpacking operator splits the string into four characters, which is the expected argument form.

If you request a codec that is not installed, VideoWriter.isOpened() returns False and the writer silently produces no output. Always check this flag after constructing the writer:

writer = cv2.VideoWriter("output.mp4", fourcc, 30.0, (1920, 1080)) if not writer.isOpened(): raise RuntimeError("Could not open the video writer with the requested codec")

Matching Frame Rate and Frame Size

VideoWriter does not resample frames. It writes exactly the frames you pass, at the frame rate you declare in the constructor. If the actual processing rate differs from the declared rate, the output video will play at the wrong speed.

The frame size passed to VideoWriter must match the dimensions of every frame you write. A common mistake is reading the input size from capture.get(cv2.CAP_PROP_FRAME_WIDTH) but then resizing frames before writing them, which produces a runtime error or a corrupt file.

width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)) fps = capture.get(cv2.CAP_PROP_FPS) writer = cv2.VideoWriter("output.mp4", fourcc, fps, (width, height))

Using the input's own fps and dimensions guarantees the output matches the source timing, which is what most processing pipelines want.

A Complete Processing Pipeline Example

The following example reads a video, applies a blur and a color shift, and writes the result. It also handles the case where the input has no frames at all.

import cv2 def process_video(input_path, output_path): capture = cv2.VideoCapture(input_path) if not capture.isOpened(): raise RuntimeError(f"Cannot open {input_path}") width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)) fps = capture.get(cv2.CAP_PROP_FPS) fourcc = cv2.VideoWriter_fourcc(*"mp4v") writer = cv2.VideoWriter(output_path, fourcc, fps, (width, height)) if not writer.isOpened(): capture.release() raise RuntimeError(f"Cannot write {output_path}") frame_count = 0 while True: ret, frame = capture.read() if not ret: break blurred = cv2.GaussianBlur(frame, (15, 15), 0) shifted = cv2.convertScaleAbs(blurred, alpha=1.1, beta=10) writer.write(shifted) frame_count += 1 capture.release() writer.release() return frame_count if __name__ == "__main__": count = process_video("input.mp4", "output.mp4") print(f"Processed {count} frames")

The convertScaleAbs call applies a brightness and contrast adjustment without changing the channel count, so the frame remains valid BGR input for the writer. This pattern — read, transform, write — is the same for any per-frame operation, whether it is object detection, color grading, or resizing.

Common Failure Modes and Their Causes

The most frequent problems in OpenCV video writing are not logic errors but environment and format mismatches.

Empty output file. The writer was created with a codec the system does not support, or isOpened() was never checked. The loop may have run, but every frame was silently discarded.

Video plays too fast or too slow. The declared frame rate in the VideoWriter constructor does not match the actual frame rate of the source. If you process a 30 FPS source but declare 60 FPS, the output plays at double speed.

Runtime error on write(). The frame dimensions or channel count do not match what the writer expects. This happens after resizing or after converting to grayscale without converting back to BGR.

Corrupt output that plays in some players but not others. The container extension and the codec are mismatched, such as writing an H.264 stream into an AVI container. Some players tolerate this, others do not.

The writer opens but produces a file of zero bytes. This occurs when the loop breaks immediately because read() returns False on the first call, usually because the input path is wrong or the file is not a valid video.

Performance: Where Processing Time Goes

Video processing is I/O-bound and CPU-bound at the same time. The frame loop reads a compressed frame, decodes it, runs your transformation, encodes the result, and writes it to disk. The slowest stage is usually the transformation, not the reading or writing.

For per-frame operations, avoid reallocating large arrays inside the loop. Reuse buffers when the operation allows it. For example, cv2.GaussianBlur allocates a new output each call; if you process many frames, the allocation overhead becomes measurable. Some OpenCV functions accept an optional destination parameter:

blurred = cv2.GaussianBlur(frame, (15, 15), 0, dst=blurred)

This reuses the existing buffer and reduces garbage collection pressure. The same applies to cv2.addWeighted and cv2.convertScaleAbs, which both accept a dst argument.

Real-time processing is a different constraint. If your transformation takes longer than one frame interval, the loop falls behind and the output video will have gaps or the process will buffer frames in memory. For offline processing this is irrelevant; for live camera feeds you need to decouple capture and processing, typically with a queue and a separate worker thread.

When to Use VideoWriter Instead of an Image Sequence

For long-running processing jobs, writing individual frames as PNG or JPEG files and assembling them later is sometimes more robust than writing a video directly. A crash mid-run leaves you with all the frames up to that point, whereas a partially written video file is often unusable.

The tradeoff is disk space and assembly time. PNG frames are much larger than a compressed video, and you need a second pass with cv2.VideoWriter or FFmpeg to combine them. For short clips or when you need to inspect intermediate frames, the image sequence is more practical. For anything that will be delivered as a video, write directly with VideoWriter and keep the processing pipeline idempotent so you can rerun it after a failure.

python opencv write and process video: Practical Usage and C | RYUSLOG DEV