3、视频帧读取基础:cv2.VideoCapture的使用,帧率、分辨率参数获取与设置

做视频处理,第一步就是把视频文件或者摄像头的数据「拽」进来。OpenCV 里干这活的就是 cv2.VideoCapture。说白了,它就是个视频流读取器,不管是本地的 MP4 文件,还是你笔记本上的摄像头,它都能搞定。

我个人习惯,拿到一个新视频流,第一件事不是急着处理,而是先看看它的「底细」——帧率多少?分辨率多大?这些参数决定了你后面怎么设计处理流程。

3.1 打开视频源:从文件到摄像头

cv2.VideoCapture 的构造方法很简单,传一个参数就行。传文件路径,它就打开本地视频;传一个数字(比如 0),它就打开摄像头。

import cv2

# 打开本地视频文件
cap = cv2.VideoCapture('test_video.mp4')

# 打开默认摄像头(通常是笔记本前置摄像头)
cap_cam = cv2.VideoCapture(0)

# 检查是否成功打开
if not cap.isOpened():
    print("视频文件打开失败,请检查路径")
    exit()
注意: 摄像头索引 0 是系统默认摄像头。如果你插了外接摄像头,索引可能是 1 或 2。我在项目里遇到过,笔记本自带摄像头坏了,插了个 USB 摄像头,结果索引 0 打不开,换成 1 就好了。建议打开前先打印一下可用设备列表。

3.2 读取帧:核心循环

视频其实就是一帧一帧的图片快速播放。读取帧的代码就两行,但这里有个坑——ret 这个返回值,很多人会忽略它。

while True:
    ret, frame = cap.read()
    if not ret:
        print("读取完毕或读取失败")
        break
    
    # 在这里处理 frame
    cv2.imshow('Frame', frame)
    
    # 按 'q' 键退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

ret 是个布尔值,告诉你这一帧有没有读成功。为什么会失败?视频读到结尾了,或者文件损坏了,或者摄像头突然断开了。我曾经在部署到嵌入式设备时,摄像头线松了,程序直接卡死在 cap.read() 上,就是因为没检查 ret。

3.3 获取视频参数:帧率、分辨率、总帧数

拿到视频流之后,你得知道它的「体质」。OpenCV 用 cv2.CAP_PROP_* 这些常量来获取参数。

参数常量 含义 返回值类型
cv2.CAP_PROP_FPS 帧率(每秒多少帧) float
cv2.CAP_PROP_FRAME_WIDTH 帧宽度(像素) float
cv2.CAP_PROP_FRAME_HEIGHT 帧高度(像素) float
cv2.CAP_PROP_FRAME_COUNT 视频总帧数 float
cv2.CAP_PROP_POS_FRAME 当前帧索引 float
# 获取视频参数
fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

print(f"帧率: {fps} FPS")
print(f"分辨率: {width} x {height}")
print(f"总帧数: {total_frames}")
print(f"视频时长: {total_frames / fps:.2f} 秒")
小技巧: 有些视频文件(尤其是网络流或某些编码格式)的 CAP_PROP_FRAME_COUNT 返回 -1,这是正常的。别慌,你只需要按帧读取直到 ret 为 False 就行。

3.4 设置视频参数:分辨率、帧率、编码

有时候你需要改变视频流的参数,比如把摄像头采集的分辨率从 640x480 改成 1280x720。用 cap.set() 就行。

# 设置摄像头分辨率
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
cap.set(cv2.CAP_PROP_FPS, 30)  # 设置帧率

# 验证设置是否生效
actual_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
actual_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(f"实际分辨率: {actual_width} x {actual_height}")
注意: 不是所有摄像头都支持你设置的分辨率。比如你设 1920x1080,但摄像头最高只支持 1280x720,那它可能会自动降级到最接近的支持分辨率。我建议设置完后,用 get() 读一下实际值,确认一下。

3.5 知识结构图:视频帧读取核心流程

下面这张图,把整个视频帧读取的流程串起来了。从打开源到释放资源,每一步都离不开。

视频帧读取核心流程 打开视频源 获取参数(FPS/分辨率) 循环读取帧 检查 ret 返回值 处理帧(显示/推理) 释放资源 关键点: • 打开后检查 isOpened() • 读取后检查 ret • 设置参数后 get() 验证 常见坑: • 摄像头索引不对 • 分辨率设置不生效

3.6 实战:读取视频并显示参数信息

把上面学的东西串起来,写一个完整的例子。这个程序会打开一个视频文件,在每一帧的左上角显示当前帧索引和帧率。

import cv2

cap = cv2.VideoCapture('test_video.mp4')

# 获取参数
fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

print(f"视频信息: {width}x{height} @ {fps:.2f} FPS, 共 {total} 帧")

frame_idx = 0
while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 在帧上显示信息
    text = f"Frame: {frame_idx} / {total}"
    cv2.putText(frame, text, (10, 30), 
                cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
    
    cv2.imshow('Video', frame)
    
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
    
    frame_idx += 1

cap.release()
cv2.destroyAllWindows()
核心要点:
  • cv2.VideoCapture 是视频读取的入口,支持文件和摄像头
  • 每次读取都要检查 ret,防止空帧导致程序崩溃
  • 用 get() 获取参数,用 set() 设置参数
  • 设置参数后务必用 get() 验证是否生效
  • 记得释放资源:cap.release()