找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1107

积分

0

好友

159

主题
发表于 前天 04:16 | 查看: 4| 回复: 0

在计算机视觉应用开发中,实时捕获和处理摄像头视频流是一项基础且核心的任务。借助 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() 的参数,在实时视频循环中不应设为 00表示无限等待按键),应使用一个较小的正整数(如 110)。

总结

实现摄像头视频的捕获、预览与保存,核心在于掌握三个OpenCV对象:

  1. VideoCapture:负责连接视频源(摄像头或文件),是视频流的“入口”。
  2. imshowwaitKey:负责实时显示画面并实现交互控制。
  3. VideoWriter:负责将图像帧序列编码并写入文件,是视频流的“出口”。

理解并熟练运用这三者,就掌握了处理摄像头视频的基础。之后可以根据具体需求,在获取到的每一帧图像上叠加丰富的处理逻辑。




上一篇:最小因式分解算法详解:贪心策略解决Python面试编码题
下一篇:00后大厂就业现状:运营岗困境与AI岗位高门槛下的职业选择
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-17 13:08 , Processed in 0.135363 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表