如何用OpenCV录制无加速的1080x720@30fps 10秒触发视频?
问题解决指南:PIR触发摄像头录制时长/分辨率问题
核心问题分析
- 录制时长加速:本质是帧率不匹配或计时逻辑错误——用
time.sleep(10)无法保证录够300帧(30fps×10s),摄像头实际输出帧率与VideoWriter设置值不一致时,播放器会按设置帧率解码,导致时长压缩。 - 分辨率设置后视频损坏:一是使用了摄像头不支持的非标准分辨率(你提到的1080×720不是罗技C920的标准支持规格,它的720p是1280×720);二是
VideoWriter的分辨率与摄像头实际输出分辨率不匹配;三是编码格式兼容性问题。
分步解决方案
一、修复录制时长问题
- 放弃
time.sleep,用帧计数控制时长:计算需要录制的总帧数(目标帧率×时长),循环读取并写入帧直到达到总帧数,确保实际时长准确。 - 强制匹配摄像头帧率:先设置摄像头帧率,再读取实际生效的帧率传给
VideoWriter,避免解码时的速度偏差。
二、解决分辨率与视频播放问题
- 使用摄像头支持的标准分辨率:罗技C920支持的主流规格是:
- 720p: 1280×720@30fps
- 1080p: 1920×1080@30fps
不要使用非标准的1080×720,摄像头会自动降为兼容分辨率,导致VideoWriter参数不匹配。
- 正确设置分辨率的顺序:部分USB摄像头需要先设置帧率,再设置分辨率,否则参数不生效。
- 保证
VideoWriter与摄像头分辨率完全一致:设置完摄像头参数后,必须读取实际生效的分辨率,再传给VideoWriter,不能硬写目标值。 - 选择兼容的编码格式:优先用
MJPG编码(兼容性最强,对应.avi后缀),避免因系统缺少编码库导致视频损坏。
完整可运行代码
import cv2 import time from pyfirmata import Arduino, util # 初始化Arduino与PIR传感器 board = Arduino('COM3') # 替换为你的Arduino串口 pir_pin = board.get_pin('d:2:i') it = util.Iterator(board) it.start() pir_pin.enable_reporting() # 目标参数(使用C920支持的标准规格) TARGET_FPS = 30 TARGET_DURATION = 10 TOTAL_FRAMES = TARGET_FPS * TARGET_DURATION TARGET_WIDTH = 1280 # 标准720p宽度 TARGET_HEIGHT = 720 # 初始化摄像头 cap = cv2.VideoCapture(0) # 先设置帧率,再设置分辨率 cap.set(cv2.CAP_PROP_FPS, TARGET_FPS) cap.set(cv2.CAP_PROP_FRAME_WIDTH, TARGET_WIDTH) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, TARGET_HEIGHT) # 读取实际生效的参数,避免硬写不匹配 actual_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) actual_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) actual_fps = cap.get(cv2.CAP_PROP_FPS) print(f"实际运行参数:分辨率{actual_width}×{actual_height},帧率{actual_fps:.1f}fps") # 初始化视频写入器(MJPG编码兼容性最优) fourcc = cv2.VideoWriter_fourcc(*'MJPG') out = cv2.VideoWriter('motion_record.avi', fourcc, actual_fps, (actual_width, actual_height)) try: while True: pir_state = pir_pin.read() if pir_state: print("检测到运动,启动录制...") frame_count = 0 start_time = time.time() while frame_count < TOTAL_FRAMES: ret, frame = cap.read() if not ret: print("读取帧失败,终止录制") break out.write(frame) frame_count += 1 # 可选:实时预览录制画面 cv2.imshow('Recording', frame) if cv2.waitKey(1) == ord('q'): break print(f"录制完成,共录制{frame_count}帧,实际时长{time.time()-start_time:.1f}秒") # 延迟避免PIR重复触发 time.sleep(2) if cv2.waitKey(1) == ord('q'): break finally: # 资源释放 cap.release() out.release() cv2.destroyAllWindows() board.exit()
额外排查点
- USB供电检查:C920在高分辨率下需要足够供电,建议使用USB3.0端口,避免因供电不足降规格。
- 摄像头占用检查:确保没有其他软件(如视频会议工具)占用摄像头,否则OpenCV无法获取正确参数。
- 编码库验证:如果需要使用H.264编码,Windows需安装OpenCV扩展包,Linux可安装
libx264-dev,macOS通过brew安装ffmpeg。
内容的提问来源于stack exchange,提问作者leuji241
相关产品推荐
相关产品推荐

