4. 视频流采集与处理:OpenCV基础

视频监控系统里,摄像头就是我们的眼睛。

但光有眼睛不够,还得让电脑看懂它看到了什么。这就是OpenCV登场的地方。我个人习惯把OpenCV称为「视觉瑞士军刀」——功能多,上手快,而且免费。

4.1 读取摄像头:从零到一

先来最基础的:让摄像头开始工作。

OpenCV里读取摄像头就一行代码:cv2.VideoCapture(0)。参数0代表第一个摄像头,1就是第二个。我在项目中遇到过一个问题——笔记本自带摄像头和USB外接摄像头同时存在时,索引号经常搞混。后来我养成了一个习惯:先写个小脚本枚举所有设备,再确定用哪个。

import cv2

# 打开摄像头
cap = cv2.VideoCapture(0)

# 检查是否成功
if not cap.isOpened():
    print("摄像头打开失败,检查连接或索引号")
    exit()

# 循环读取帧
while True:
    ret, frame = cap.read()
    if not ret:
        print("读取帧失败,可能摄像头断开")
        break
    
    # 显示画面
    cv2.imshow('Camera', frame)
    
    # 按q退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

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

⚠️ 注意: 我曾经因为忘记调用 cap.release(),导致摄像头一直被占用,其他程序打不开。嗯,这个坑踩过一次就记住了。

4.2 帧处理:不只是看看而已

拿到原始帧之后,我们通常要做一些处理。说白了,摄像头给的数据是「生肉」,得加工一下才能用。

常见的帧处理操作:

  • 灰度转换cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY),减少数据量,提高处理速度
  • 尺寸缩放cv2.resize(frame, (640, 480)),统一分辨率
  • 翻转镜像cv2.flip(frame, 1),解决摄像头左右颠倒的问题
  • 绘制标记cv2.rectangle()cv2.putText(),标注检测结果

你想想看,一个1080p的原始帧,不做任何处理直接传输,带宽压力有多大?所以帧处理不仅是「好看」,更是为了「好用」。

4.3 图像保存:关键时刻要留证据

监控系统里,截图功能是刚需。比如检测到异常行为,立刻保存当前帧。

# 保存当前帧
cv2.imwrite('snapshot.jpg', frame)

# 带时间戳的保存
import time
timestamp = time.strftime("%Y%m%d_%H%M%S")
cv2.imwrite(f'snapshot_{timestamp}.jpg', frame)

💡 小技巧: 我建议保存时用JPEG格式,质量参数设为95左右。太高质量文件太大,太低质量细节丢失。95是个不错的平衡点。

4.4 视频流编码:MJPEG vs H.264

这里要聊一个核心问题:视频流怎么编码?

两种主流方案:

特性 MJPEG H.264
压缩原理 每帧独立压缩 帧间预测+帧内压缩
压缩比 低(约10:1) 高(约100:1)
延迟
解码复杂度
适用场景 局域网、低延迟需求 广域网、存储优化

我个人习惯:局域网内用MJPEG,因为延迟低、实现简单。广域网传输用H.264,带宽利用率高。为什么?你想想看,一个1080p的MJPEG流,码率可能跑到30Mbps以上,而H.264只需要2-3Mbps就能达到差不多的画质。

🔑 关键点: 选择编码方式时,要权衡三个因素:带宽、延迟、计算资源。没有银弹,只有最适合你场景的方案。

4.5 实时视频预览:cv2.imshow + waitKey

实时预览是调试阶段最常用的功能。核心就两个函数:

  • cv2.imshow(window_name, image):显示图像窗口
  • cv2.waitKey(delay):等待键盘输入,同时控制帧率

这里有个容易忽略的细节:waitKey(1) 的参数1表示等待1毫秒。如果改成0,就会一直等待按键,画面就卡住了。我曾经在调试时不小心写成了 waitKey(0),结果画面一动不动,我还以为是摄像头坏了……

# 控制帧率的技巧
fps = 30
delay = int(1000 / fps)  # 约33ms

while True:
    ret, frame = cap.read()
    # 处理帧...
    cv2.imshow('Preview', frame)
    
    # 用delay控制显示速度
    if cv2.waitKey(delay) & 0xFF == ord('q'):
        break

⚠️ 注意: waitKey 返回的是按键的ASCII码。用 & 0xFF 是为了兼容64位系统。这个细节我见过不少新手踩坑。

4.6 本章知识体系

下面这张图,把视频流采集与处理的核心流程串起来了:

视频流采集与处理流程 摄像头 VideoCapture(0) 帧读取 cap.read() 帧处理 灰度/缩放/翻转 绘制/标注 编码 MJPEG/H.264 压缩 实时预览 imshow + waitKey 图像保存 imwrite() 网络传输 RTSP/HTTP 核心流程:采集 → 读取 → 处理 → 编码 → 分发 三种输出方式:预览、保存、传输 图:视频流采集与处理核心流程

这张图把整个流程分成了三个阶段:采集端、处理端、输出端。每个环节都有对应的OpenCV函数。我个人建议初学者先把「采集→预览」这个闭环跑通,再逐步加入处理和编码。

💡 经验之谈: 调试阶段,先用 cv2.imshow 确认画面正常,再去做编码和传输。否则你根本不知道问题是出在采集环节还是编码环节。这个「分步验证」的思路,能帮你省下大量排查时间。


公众号:蓝海资料掘金营,微信deep3321