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监控,你就能掌控整个视频处理流程了。下一章咱们会深入视频分析的实战应用,到时候这些基础都会用上。