1.4 视频与摄像头
总目录:1.1 OpenCV介绍与学习路线 | 第一章:认识图像与跑通流程 | 编号:1.4
← 上一节:1.3 读取显示与保存图像 | 下一节:1.5 NumPy与图像数组 →
1.4 视频与摄像头
视频没有那么神秘。站在 OpenCV 的角度,它就是一张、两张、三张……不断到来的图像。我们对每一帧重复同一套处理,就得到实时视觉。
一、视频就是连续到来的图像帧
视频文件 / 摄像头
↓
读取第 1 帧 → 处理 → 显示
读取第 2 帧 → 处理 → 显示
读取第 3 帧 → 处理 → 显示
↓
一直循环
这也解释了为什么视频处理离不开循环。
二、VideoCapture:打开视频源
打开视频文件
cap = cv2.VideoCapture("resources/test_video.mp4")
打开摄像头
cap = cv2.VideoCapture(0)
0 通常是默认摄像头;有多个设备时可能是 1、2。打开后先检查:
if not cap.isOpened():
raise RuntimeError("视频源打开失败")
三、read:读出一帧
ok, frame = cap.read()
ok:这一帧有没有成功读到;frame:读到的图像数组;失败时可能是None。
视频播完、文件损坏、摄像头掉线,都可能让 ok 变成 False。不要拿失败的 frame 继续做 cvtColor,否则真正原因会被后续报错盖住。
四、标准的实时循环
import cv2
cap = cv2.VideoCapture(0)
if not cap.isOpened():
raise RuntimeError("无法打开摄像头")
try:
while True:
ok, frame = cap.read()
if not ok:
print("读取视频帧失败")
break
cv2.imshow("Camera", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
finally:
cap.release()
cv2.destroyAllWindows()
这里用 try/finally,是为了即使中途报错,也尽量释放摄像头并关闭窗口。
五、摄像头属性与曝光
属性不要写成没有含义的数字 3、4、10,可读性更好的写法是:
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
cap.set(cv2.CAP_PROP_FPS, 30)
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
fps = cap.get(cv2.CAP_PROP_FPS)
print(width, height, fps)
set 返回成功也不保证设备一定采用了你请求的值,所以要再用 get 验证。
曝光更加依赖设备、驱动和后端:
cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0.25)
cap.set(cv2.CAP_PROP_EXPOSURE, -6)
print(cap.get(cv2.CAP_PROP_EXPOSURE))
上面的 0.25、-6 只是在部分 Windows DirectShow 摄像头上的常见写法,不是跨平台标准。Linux V4L2、USB 工业相机和板卡摄像头可能完全不同。
六、grab 与 retrieve
read() 大致可以理解为:先抓取,再解码。
read() ≈ grab() + retrieve()
多摄像头需要尽量同步时,可以先对两路都 grab(),再分别 retrieve():
cap_a.grab()
cap_b.grab()
ok_a, frame_a = cap_a.retrieve()
ok_b, frame_b = cap_b.retrieve()
这样能减少“先把 A 完整解码完,才轮到 B”的时间差,但它不等于硬件同步。真正严格同步仍需要摄像头触发、时间戳或驱动支持。
视频循环速查
打不开 → isOpened()、设备索引、权限、是否被占用
读不到帧 → read() 返回值、文件是否播完、摄像头是否掉线
延迟越来越大 → 处理速度是否低于采集速度、缓冲区是否积帧
画面太暗 → 曝光、增益、光源,不要先怪识别算法
窗口卡死 → 循环内是否调用 waitKey
← 上一节:1.3 读取显示与保存图像 | 下一节:1.5 NumPy与图像数组 →
留言区
不收集任何个人信息;昵称只绑定浏览器里的随机 Cookie。