Python技术迷

如何用 Python 捕获、播放和保存摄像头视频

那我就按平时跟同事解释的方式来聊一遍,别紧张,这个主题其实没你想得那么复杂:就是拿 Python 把摄像头画面抓出来,在屏幕上放一下,再顺手存成一个视频文件。

先说个前提:我们主要用 OpenCV

绝大多数人用 Python 玩摄像头,都是用 opencv-python 这个库,安装就一行:

pip install opencv-python

装完就可以 import cv2 了。如果你是虚拟环境,记得先激活环境再装,不然容易搞混。

第一步:打开摄像头,把画面“抓出来”

你可以先写一个最小可运行的 demo,确认你的摄像头能正常被 Python 访问。比如下面这段代码,我一般也是先跑它:

import cv2

defpreview_camera(camera_index=0):
# 1. 打开摄像头,0 通常是默认摄像头,笔记本就是内置摄像头
    cap = cv2.VideoCapture(camera_index)

ifnot cap.isOpened():
        print("摄像头打开失败,检查一下摄像头是否被占用,或者 camera_index 写错了")
return

whileTrue:
# 2. 读取一帧图像
        ret, frame = cap.read()

ifnot ret:
            print("读取摄像头画面失败,可能是摄像头断开了")
break

# 3. 在窗口中显示这一帧
        cv2.imshow("Camera Preview - 按 q 退出", frame)

# 4. 等待键盘事件,参数是等待毫秒数
#   如果 1ms 内按下 q,就退出循环
if cv2.waitKey(1) & 0xFF == ord('q'):
break

# 5. 释放摄像头资源,关闭窗口
    cap.release()
    cv2.destroyAllWindows()

if __name__ == "__main__":
    preview_camera()

大概的逻辑你可以理解成:

  • VideoCapture 负责“管子”:从摄像头源头往外输视频帧;
  • read() 每次从这个“管子”里捞一帧出来;
  • imshow() 就是把这一帧画在一个窗体上;
  • waitKey() 不光负责等个几毫秒,还顺便监听键盘按键;
  • 最后记得 release() 和 destroyAllWindows(),不然总感觉资源没收拾干净。

如果你电脑插了好几个摄像头,camera_index 可以试 0、1、2 去找。

第二步:一边预览,一边录制保存成视频

能预览之后,离“录屏”就差一步:把每一帧写进一个视频文件。OpenCV 里用 VideoWriter 干这个事。

可以这样写个小工具函数,把摄像头画面录下来:

import cv2
from datetime import datetime

defrecord_camera(
    camera_index=0,
    output_file=None,
    fps=20.0,
    frame_size=None
)
:

    cap = cv2.VideoCapture(camera_index)

ifnot cap.isOpened():
        print("摄像头打开失败")
return

# 如果没指定输出文件名,就用时间戳来生成一个
if output_file isNone:
        output_file = datetime.now().strftime("camera_%Y%m%d_%H%M%S.mp4")

# 获取摄像头的宽高
if frame_size isNone:
        width  = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        frame_size = (width, height)

# fourcc 指定视频编码格式,不同系统可能要试几个
# 'mp4v' 生成 mp4,一般还算通用
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    out = cv2.VideoWriter(output_file, fourcc, fps, frame_size)

ifnot out.isOpened():
        print("视频文件创建失败,检查路径和编码格式")
        cap.release()
return

    print(f"开始录制,保存到:{output_file},按 q 停止")

whileTrue:
        ret, frame = cap.read()
ifnot ret:
            print("读取摄像头失败,中断录制")
break

# 如果摄像头分辨率和 frame_size 不一致,调整一下大小
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 版本,支持的编码器不太一样。

    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    output_file = "output.avi"
    • Windows 上常见 'XVID'、'MJPG'、'mp4v';
    • Linux 上配合 ffmpeg 也能用 X264 等等; 录不出来多半就是编码器或后缀名不对,可以换几个组合试试,例如:
  • fps(帧率):设置得高,文件更大,对性能也更挑;一般 20~30fps 就足够了。

  • frame_size 一定要和写入帧的尺寸一致,不然 VideoWriter 会悄悄失败,最后生成个打不开的空文件。

第三步:再来一个“回放”功能,看看录好的视频

录完了你肯定想看一眼是不是正常,这个其实又回到了第一步:VideoCapture 不一定要接摄像头,也可以接一个视频文件路径。

比如:

import cv2

defplay_video(file_path):
    cap = cv2.VideoCapture(file_path)

ifnot cap.isOpened():
        print("无法打开视频文件:", file_path)
return

whileTrue:
        ret, frame = cap.read()
ifnot ret:
            print("播放结束")
break

        cv2.imshow("Video Playback - 按 q 退出", frame)

# 这里可以稍微大一点的延时,模拟接近原始 fps
if cv2.waitKey(30) & 0xFF == ord('q'):
break

    cap.release()
    cv2.destroyAllWindows()

if __name__ == "__main__":
    play_video("camera_20240101_120000.mp4")  # 换成你实际录出来的文件名

这个函数其实跟摄像头预览几乎一模一样,区别就是:

  • VideoCapture 传的是文件路径;
  • ret == False 代表“读到头了”,不是摄像头坏了。

如果你想用键盘控制“暂停/继续”,也可以在循环里多判断几个按键,比如空格暂停,p 继续,这些都是在 waitKey 上做文章。

第四步:顺手再做点小处理,比如灰度、翻转、加时间戳

既然每一帧都拿到了,不处理一下就关掉有点可惜。OpenCV 的好玩之处也在这儿:你在保存前可以对画面“顺便加工”。

比如在录制时,把画面变成灰度再显示,但保存成彩色,或者干脆保存灰度版。随便举一个加时间戳再录制的版本:

import cv2
from datetime import datetime

defrecord_with_timestamp(camera_index=0, output_file="record_with_ts.mp4"):
    cap = cv2.VideoCapture(camera_index)
ifnot 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 停止")

whileTrue:
        ret, frame = cap.read()
ifnot 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()

你想做实时美颜、加水印、打马赛克,都是在 frame 上先处理,然后再 out.write(frame) 就行了。

第五步:稍微封装一下,做成一个小工具类

如果你不想每次都复制一坨 while 循环,可以简单封装个类,用起来会舒服一点,比如:

import cv2
from datetime import datetime
from typing import Tuple, Optional

classCameraRecorder:
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

defopen(self):
        self.cap = cv2.VideoCapture(self.camera_index)
ifnot self.cap.isOpened():
raise RuntimeError("摄像头打开失败")

if self.frame_size isNone:
            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)

ifnot self.out.isOpened():
            self.cap.release()
raise RuntimeError("视频文件创建失败")

defrun(self):
        print(f"开始录制,输出文件:{self.output_file},按 q 停止")
whileTrue:
            ret, frame = self.cap.read()
ifnot ret:
                print("读取摄像头失败")
break

# 可以在这里对 frame 做一些统一处理,比如翻转
# 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

defclose(self):
if self.cap isnotNone:
            self.cap.release()
if self.out isnotNone:
            self.out.release()
        cv2.destroyAllWindows()

if __name__ == "__main__":
    recorder = CameraRecorder()
try:
        recorder.open()
        recorder.run()
finally:
        recorder.close()

以后你要改录制参数、改分辨率、改编码器,就改构造函数参数;要拓展成“录一段 N 秒然后自动停”,也只要在 run 里加个时间判断。

最后说几个常见小坑,免得你踩

我随口列几个,基本都是大家第一次玩摄像头必踩的:

  • 代码没报错,但窗口一片黑:

    • 很可能是摄像头被别的程序占用了(比如系统自带相机、浏览器);
    • 或者 camera_index 写错,试试 1、2。
  • 生成的视频文件为 0KB 或打不开:

    • 要么编码器不兼容,要么 frame_size 对不上,
    • 再要么你没正常走到 release(),进程被你强行停掉了。
  • waitKey 写成了 0 导致窗口“卡住”:

    • waitKey(0) 是“无限等键盘”,通常是在显示图片时用,
    • 实时视频预览就别这么写了,用个小延时,比如 1 或 10。

整体思路就是三句话:

  1. 用 VideoCapture 从摄像头“接水”;
  2. 用 imshow 把水龙头打开看一眼;
  3. 想要存起来,就再接个 VideoWriter 把每一帧写进文件。

搞清这三块,捕获、播放、保存摄像头视频就算拿下了。剩下的就是看你想在每一帧上做多少骚操作了 😄

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

🔥虎哥私藏精品🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB