contribute上次修改:2026-09-29

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。