在计算机视觉应用开发中,实时捕获和处理摄像头视频流是一项基础且核心的任务。借助 Python 强大的生态,我们可以轻松实现摄像头的开启、画面预览、视频录制以及回放功能。
核心工具:OpenCV库
opencv-python 库是处理此类任务的主流选择。安装过程非常简单:
pip install opencv-python
安装后,即可通过 import cv2 导入使用。
第一步:捕获与预览摄像头画面
首先,通过一个简单的Demo确认摄像头能够被正常访问和打开。以下代码展示了如何打开默认摄像头并实时显示画面。
import cv2
def preview_camera(camera_index=0):
# 1. 创建VideoCapture对象,0代表系统默认摄像头
cap = cv2.VideoCapture(camera_index)
if not cap.isOpened():
print("摄像头打开失败,请检查设备是否被占用或索引号是否正确。")
return
while True:
# 2. 逐帧读取
ret, frame = cap.read()
if not ret:
print("读取画面失败,可能摄像头已断开。")
break
# 3. 在窗口中显示当前帧
cv2.imshow("Camera Preview - 按 `q` 键退出", frame)
# 4. 等待按键,延迟1毫秒以维持实时显示
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 5. 释放资源并关闭所有窗口
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
preview_camera()
代码逻辑解析:
VideoCapture:如同连接摄像头的“管道”,负责获取视频流。
cap.read():每次从“管道”中读取一帧图像(frame)和状态(ret)。
cv2.imshow():将图像帧显示在指定窗口中。
cv2.waitKey(1):等待一个极短的延时,并监听键盘按键事件。
- 循环结束后,必须调用
release() 和 destroyAllWindows() 来释放资源。
如果连接了多个摄像头,可以通过尝试 camera_index 为 0, 1, 2... 来切换。
第二步:实时录制并保存视频
在预览的基础上,增加录制功能意味着需要将每一帧图像写入视频文件。OpenCV 通过 VideoWriter 类来实现此功能。
import cv2
from datetime import datetime
def record_camera(
camera_index=0,
output_file=None,
fps=20.0,
frame_size=None
):
cap = cv2.VideoCapture(camera_index)
if not cap.isOpened():
print("摄像头打开失败")
return
# 若未指定文件名,则使用时间戳生成
if output_file is None:
output_file = datetime.now().strftime("camera_%Y%m%d_%H%M%S.mp4")
# 获取摄像头原始分辨率
if frame_size is None:
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
frame_size = (width, height)
# 指定视频编码器,不同系统支持的编码器可能不同
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(output_file, fourcc, fps, frame_size)
if not out.isOpened():
print("视频文件创建失败,请检查路径和编码格式。")
cap.release()
return
print(f"开始录制,保存至:{output_file},按 `q` 键停止")
while True:
ret, frame = cap.read()
if not ret:
print("读取摄像头失败,录制中断。")
break
# 确保写入的帧尺寸与VideoWriter设置一致
if (frame.shape[1], frame.shape[0]) != frame_size:
frame = cv2.resize(frame, frame_size)
# 写入当前帧
out.write(frame)
# 同时预览录制画面
cv2.imshow("Recording - 按 `q` 停止", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
out.release()
cv2.destroyAllWindows()
print("录制结束。")
if __name__ == "__main__":
record_camera()
关键参数说明:
- 编码器 (FourCC):不同系统或OpenCV版本支持的编码器可能不同。常见组合有:
'XVID'(对应.avi后缀),'MJPG','mp4v'(对应.mp4后缀)。录制失败时,可以尝试更换编码器和文件后缀。
- 帧率 (FPS):通常设置为 20-30 帧/秒即可满足需求,过高的帧率会增加文件大小和系统负载。
- 分辨率 (frame_size):
VideoWriter 创建时必须指定分辨率,且后续写入的每一帧图像尺寸必须与其严格一致,否则可能导致生成的视频文件无法播放。
第三步:播放已录制的视频文件
播放本地视频文件与预览摄像头的流程高度相似,只需将 VideoCapture 的参数从摄像头索引改为视频文件路径。
import cv2
def play_video(file_path):
cap = cv2.VideoCapture(file_path)
if not cap.isOpened():
print(f"无法打开视频文件:{file_path}")
return
while True:
ret, frame = cap.read()
if not ret: # ret为False表示已读取到文件末尾
print("视频播放结束。")
break
cv2.imshow("Video Playback - 按 `q` 退出", frame)
# 通过控制延迟时间(如30ms)来接近原始视频的播放速度
if cv2.waitKey(30) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
play_video("camera_20240101_120000.mp4") # 替换为实际文件名
通过监听 waitKey 的返回值,可以轻松扩展播放控制功能,如空格键暂停/继续等。
第四步:在录制过程中实时处理画面
OpenCV 的强大之处在于可以方便地对每一帧图像进行处理,例如添加时间戳、转换为灰度图、添加水印或进行简单滤镜操作。以下示例演示如何在录制时实时添加时间戳。
import cv2
from datetime import datetime
def record_with_timestamp(camera_index=0, output_file="record_with_ts.mp4"):
cap = cv2.VideoCapture(camera_index)
if not cap.isOpened():
print("摄像头打开失败")
return
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
frame_size = (width, height)
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(output_file, fourcc, 20.0, frame_size)
print("开始录制(带时间戳),按 `q` 键停止")
while True:
ret, frame = cap.read()
if not ret:
print("读取失败")
break
# 在画面左上角添加当前时间戳
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
cv2.putText(
frame, # 目标图像
now, # 文本内容
(10, 30), # 坐标 (x, y)
cv2.FONT_HERSHEY_SIMPLEX, # 字体类型
0.8, # 字体缩放因子
(0, 255, 0), # 颜色 (B, G, R)
2 # 线条粗细
)
out.write(frame)
cv2.imshow("Recording with timestamp - 按 `q` 停止", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
out.release()
cv2.destroyAllWindows()
print("录制结束。")
if __name__ == "__main__":
record_with_timestamp()
通过这种方式,可以在帧写入视频文件前,应用各种图像处理算法,这是许多 人工智能 和实时分析应用的基础。
第五步:封装为可复用的工具类
为了提升代码的复用性和可维护性,可以将上述功能封装成一个类。
import cv2
from datetime import datetime
from typing import Tuple, Optional
class CameraRecorder:
def __init__(
self,
camera_index: int = 0,
output_file: Optional[str] = None,
fps: float = 20.0,
codec: str = "mp4v",
frame_size: Optional[Tuple[int, int]] = None
):
self.camera_index = camera_index
self.output_file = output_file or datetime.now().strftime("camera_%Y%m%d_%H%M%S.mp4")
self.fps = fps
self.codec = codec
self.frame_size = frame_size
self.cap = None
self.out = None
def open(self):
self.cap = cv2.VideoCapture(self.camera_index)
if not self.cap.isOpened():
raise RuntimeError("摄像头打开失败")
if self.frame_size is None:
width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
self.frame_size = (width, height)
fourcc = cv2.VideoWriter_fourcc(*self.codec)
self.out = cv2.VideoWriter(self.output_file, fourcc, self.fps, self.frame_size)
if not self.out.isOpened():
self.cap.release()
raise RuntimeError("视频文件创建失败")
def run(self):
print(f"开始录制,输出文件:{self.output_file},按 `q` 键停止")
while True:
ret, frame = self.cap.read()
if not ret:
print("读取摄像头失败")
break
# 可在此处添加统一帧处理逻辑,例如镜像翻转
# frame = cv2.flip(frame, 1) # 1表示水平翻转
if (frame.shape[1], frame.shape[0]) != self.frame_size:
frame = cv2.resize(frame, self.frame_size)
self.out.write(frame)
cv2.imshow("CameraRecorder - 按 `q` 停止", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
def close(self):
if self.cap is not None:
self.cap.release()
if self.out is not None:
self.out.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
recorder = CameraRecorder()
try:
recorder.open()
recorder.run()
finally:
recorder.close()
此工具类便于参数统一管理,并为进一步扩展(如定时录制、多线程处理)提供了清晰的结构。
常见问题与排查
- 预览窗口黑屏:通常由摄像头被其他程序(如系统相机应用、浏览器)占用导致,也可能是
camera_index 设置错误,请尝试更换索引号。
- 生成视频文件为空或无法播放:最常见的原因是视频编码器 (
fourcc) 与文件后缀不匹配,或 frame_size 与实际写入的帧尺寸不一致。请确保资源通过 release() 方法正常释放。
- 窗口卡住无响应:检查
cv2.waitKey() 的参数,在实时视频循环中不应设为 0(0表示无限等待按键),应使用一个较小的正整数(如 1 或 10)。
总结
实现摄像头视频的捕获、预览与保存,核心在于掌握三个OpenCV对象:
VideoCapture:负责连接视频源(摄像头或文件),是视频流的“入口”。
imshow 与 waitKey:负责实时显示画面并实现交互控制。
VideoWriter:负责将图像帧序列编码并写入文件,是视频流的“出口”。
理解并熟练运用这三者,就掌握了处理摄像头视频的基础。之后可以根据具体需求,在获取到的每一帧图像上叠加丰富的处理逻辑。