4、视频处理入门:VideoCapture读取摄像头/视频文件、读取帧循环、保存视频(VideoWriter)、实时显示FPS

视频处理,说白了就是让计算机看懂「会动的图片」。你想想看,视频本质上就是一帧一帧的静态图像,每秒播放24帧或30帧,人眼就觉得画面动起来了。OpenCV处理视频的核心,就是跟这些帧打交道。

我个人习惯把视频处理分成三块:读进来、处理每一帧、写出去。今天咱们就把这三块彻底搞明白。

4.1 用VideoCapture读取视频源

VideoCapture是OpenCV里读取视频的入口。它既能读摄像头,也能读视频文件。怎么区分?看参数:

  • 传一个整数(比如0、1、2)—— 代表摄像头设备ID
  • 传一个字符串(比如"test.mp4")—— 代表视频文件路径

核心代码示例:

import cv2

# 读取摄像头(设备ID为0)
cap = cv2.VideoCapture(0)

# 或者读取视频文件
# cap = cv2.VideoCapture('test.mp4')

# 检查是否成功打开
if not cap.isOpened():
    print("无法打开视频源")
    exit()

嗯,这里要注意:摄像头打开失败很常见。我在项目中遇到过好几次,明明代码没问题,但摄像头就是打不开。后来发现是权限问题——有些系统需要给终端授权才能访问摄像头。

小技巧:打开摄像头后,可以用 cap.get(cv2.CAP_PROP_FRAME_WIDTH)cap.get(cv2.CAP_PROP_FRAME_HEIGHT) 查看当前分辨率。我一般会先打印出来确认一下。

4.2 读取帧循环:让视频动起来

视频处理的核心就是一个死循环——不停地读帧、处理帧、显示帧。直到用户按某个键退出,或者视频播放完毕。

while True:
    ret, frame = cap.read()
    
    # ret是布尔值,True表示成功读到一帧
    if not ret:
        print("视频读取完毕或出错")
        break
    
    # 在这里处理每一帧
    # 比如转成灰度图
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    # 显示原图和灰度图
    cv2.imshow('Original', frame)
    cv2.imshow('Gray', gray)
    
    # 按'q'键退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 释放资源
cap.release()
cv2.destroyAllWindows()

为什么用 waitKey(1)?这个函数有两个作用:一是刷新画面,二是检测按键。参数1表示等待1毫秒。你想想看,如果设成0,画面就卡住了,因为它在等按键。我刚开始学的时候踩过这个坑,画面死活不更新,急得不行。

注意:cap.read() 返回的 ret 一定要检查。我曾经在项目里偷懒没写这个判断,结果视频文件读到末尾时程序直接崩溃了。血的教训。

4.3 保存视频:用VideoWriter写出去

处理完的帧,怎么保存成视频文件?用VideoWriter。它需要四个参数:

参数 说明 示例
文件名 输出视频的路径 'output.avi'
编码格式 FourCC编码,决定视频压缩方式 cv2.VideoWriter_fourcc(*'XVID')
帧率 每秒多少帧,通常跟输入一致 20.0
分辨率 宽和高,必须跟输入帧一致 (640, 480)
# 定义编码器
fourcc = cv2.VideoWriter_fourcc(*'XVID')
# 创建VideoWriter对象
out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480))

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 处理帧(比如画个框)
    processed = frame.copy()
    cv2.rectangle(processed, (100, 100), (200, 200), (0, 255, 0), 2)
    
    # 写入视频文件
    out.write(processed)
    
    cv2.imshow('Processing', processed)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

out.release()

编码格式这块容易出问题。FourCC编码有很多种:XVID、MJPG、H264等。我建议用XVID,兼容性最好。如果你用H264,有些系统没装解码器,写出来的视频打不开。

避坑指南:写入视频的分辨率必须跟输入帧完全一致。我曾经把输入帧缩放了,但忘了改VideoWriter的分辨率,结果写出来的视频全是绿的。嗯,排查了半天才发现。

4.4 实时显示FPS:看看你的程序跑多快

FPS(Frames Per Second)是衡量视频处理性能的关键指标。说白了就是「每秒能处理多少帧」。实时显示FPS,能帮你判断代码有没有优化空间。

计算FPS的原理很简单:记录每一帧的时间,然后算差值。

import time

cap = cv2.VideoCapture(0)
prev_time = 0

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 计算FPS
    current_time = time.time()
    fps = 1 / (current_time - prev_time)
    prev_time = current_time
    
    # 把FPS显示在画面上
    fps_text = f"FPS: {fps:.2f}"
    cv2.putText(frame, fps_text, (10, 30), 
                cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
    
    cv2.imshow('FPS Demo', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

为什么用 time.time() 而不是 cv2.getTickCount()?两种都可以。我个人习惯用 time.time(),因为更直观,而且跨平台表现一致。

你想想看,如果FPS突然掉到个位数,说明你的处理逻辑太慢了。我在做实时人脸检测时就遇到过,每帧都要跑一遍深度学习模型,FPS直接掉到5以下。后来改成跳帧处理,每3帧才检测一次,FPS就上来了。

注意:第一次计算FPS时,prev_time 是0,算出来的FPS会非常大。我一般会加个判断,跳过第一帧的FPS显示。或者初始化时把 prev_time 设成当前时间。

4.5 完整示例:读取摄像头 → 处理 → 保存 → 显示FPS

把上面所有知识点串起来,就是一个完整的视频处理流水线:

import cv2
import time

# 打开摄像头
cap = cv2.VideoCapture(0)
if not cap.isOpened():
    print("摄像头打开失败")
    exit()

# 获取原始分辨率
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps_input = cap.get(cv2.CAP_PROP_FPS)
print(f"输入分辨率: {width}x{height}, 帧率: {fps_input}")

# 准备写入视频
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('live_output.avi', fourcc, 20.0, (width, height))

prev_time = time.time()
frame_count = 0

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 处理:画个矩形框
    processed = frame.copy()
    cv2.rectangle(processed, (50, 50), (200, 200), (0, 0, 255), 3)
    
    # 计算并显示FPS
    current_time = time.time()
    fps = 1 / (current_time - prev_time)
    prev_time = current_time
    frame_count += 1
    
    cv2.putText(processed, f"FPS: {fps:.1f}", (10, 30),
                cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
    cv2.putText(processed, f"Frame: {frame_count}", (10, 70),
                cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 0, 0), 2)
    
    # 显示和保存
    cv2.imshow('Live Processing', processed)
    out.write(processed)
    
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 清理
cap.release()
out.release()
cv2.destroyAllWindows()
print(f"共处理 {frame_count} 帧,视频已保存")

这个例子我经常在项目里当模板用。你只需要替换中间的处理逻辑,就能快速搭建一个视频处理工具。比如换成边缘检测、人脸识别、运动检测——框架都是一样的。

个人经验:实际项目中,我一般会把FPS显示做成可开关的。调试时打开看性能,上线时关掉省资源。用个全局变量控制就行,简单又实用。

好了,视频处理入门就讲到这里。记住三个核心:VideoCapture读、循环处理、VideoWriter写。再加上FPS监控,你就能掌控整个视频处理流程了。下一章咱们会深入视频分析的实战应用,到时候这些基础都会用上。