#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 视频捕获管理 支持摄像头和视频文件的切换和管理 """ import cv2 import os import time import threading from typing import Optional, Tuple class VideoCapture: """视频捕获管理类""" def __init__(self): """ 初始化视频捕获管理器 """ self.cap = None self.is_camera = True self.video_path = None self.fps_counter = FPSCounter() self.frame_lock = threading.Lock() self.current_frame = None self.is_running = False self.video_fps = 30.0 # 视频原始帧率 # 设置视频文件路径 self.video_file_path = os.path.join(os.path.dirname(__file__), "..", "video.mp4") def start_capture(self, use_camera: int = 1) -> bool: """ 开始视频捕获 Args: use_camera: 1使用摄像头,0使用视频文件 Returns: 是否成功启动 """ self.stop_capture() self.is_camera = bool(use_camera) try: if self.is_camera: # 使用摄像头 self.cap = cv2.VideoCapture(0) if not self.cap.isOpened(): # 尝试其他摄像头索引 for i in range(1, 5): self.cap = cv2.VideoCapture(i) if self.cap.isOpened(): break else: print("无法打开摄像头") return False # 设置摄像头参数 self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) self.cap.set(cv2.CAP_PROP_FPS, 30) print("摄像头启动成功") else: # 使用视频文件 if not os.path.exists(self.video_file_path): print(f"视频文件不存在: {self.video_file_path}") return False self.cap = cv2.VideoCapture(self.video_file_path) if not self.cap.isOpened(): print(f"无法打开视频文件: {self.video_file_path}") return False # 获取视频原始帧率 self.video_fps = self.cap.get(cv2.CAP_PROP_FPS) if self.video_fps <= 0: self.video_fps = 25.0 # 默认帧率 print(f"视频文件加载成功: {self.video_file_path}, FPS: {self.video_fps}") self.is_running = True self.fps_counter.reset() return True except Exception as e: print(f"启动视频捕获失败: {e}") return False def stop_capture(self): """停止视频捕获""" self.is_running = False if self.cap is not None: self.cap.release() self.cap = None with self.frame_lock: self.current_frame = None print("视频捕获已停止") def get_frame(self) -> Tuple[Optional[cv2.Mat], float]: """ 获取当前帧 Returns: (frame, fps): 当前帧和FPS """ if not self.is_running or self.cap is None: return None, 0.0 try: ret, frame = self.cap.read() if not ret: if not self.is_camera: # 视频文件播放完毕,重新开始(循环播放) self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0) ret, frame = self.cap.read() if not ret: return None, 0.0 # 更新FPS计数器 fps = self.fps_counter.update() # 在帧上绘制FPS信息 frame_with_fps = self._draw_fps(frame, fps) with self.frame_lock: self.current_frame = frame_with_fps.copy() return frame_with_fps, fps except Exception as e: print(f"获取帧失败: {e}") return None, 0.0 def _draw_fps(self, frame: cv2.Mat, fps: float) -> cv2.Mat: """ 在帧上绘制FPS信息 Args: frame: 输入帧 fps: 当前FPS Returns: 绘制了FPS的帧 """ result_frame = frame.copy() # FPS文本 fps_text = f"FPS: {fps:.1f}" # 文本参数 font = cv2.FONT_HERSHEY_SIMPLEX font_scale = 0.7 color = (0, 255, 0) # 绿色 thickness = 2 # 获取文本尺寸 text_size = cv2.getTextSize(fps_text, font, font_scale, thickness)[0] # 绘制背景矩形 cv2.rectangle(result_frame, (10, 10), (20 + text_size[0], 20 + text_size[1]), (0, 0, 0), -1) # 绘制FPS文本 cv2.putText(result_frame, fps_text, (15, 15 + text_size[1]), font, font_scale, color, thickness) return result_frame def get_capture_info(self) -> dict: """ 获取捕获信息 Returns: 捕获信息字典 """ info = { 'is_running': self.is_running, 'is_camera': self.is_camera, 'video_path': self.video_file_path if not self.is_camera else None, 'fps': self.fps_counter.get_fps(), 'video_fps': self.video_fps } if self.cap is not None: try: info['width'] = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)) info['height'] = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) if not self.is_camera: info['total_frames'] = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT)) info['current_frame'] = int(self.cap.get(cv2.CAP_PROP_POS_FRAMES)) except: pass return info def get_video_fps(self) -> float: """ 获取视频帧率 Returns: 视频帧率,摄像头返回30.0,视频文件返回原始帧率 """ if self.is_camera: return 30.0 # 摄像头固定30FPS else: return self.video_fps # 视频文件原始帧率 def __del__(self): """析构函数""" self.stop_capture() class FPSCounter: """FPS计数器""" def __init__(self, window_size: int = 30): """ 初始化FPS计数器 Args: window_size: 滑动窗口大小 """ self.window_size = window_size self.frame_times = [] self.last_time = time.time() def update(self) -> float: """ 更新FPS计数 Returns: 当前FPS """ current_time = time.time() # 添加当前帧时间 self.frame_times.append(current_time) # 保持窗口大小 if len(self.frame_times) > self.window_size: self.frame_times.pop(0) # 计算FPS if len(self.frame_times) >= 2: time_diff = self.frame_times[-1] - self.frame_times[0] if time_diff > 0: fps = (len(self.frame_times) - 1) / time_diff return fps return 0.0 def get_fps(self) -> float: """ 获取当前FPS Returns: 当前FPS """ if len(self.frame_times) >= 2: time_diff = self.frame_times[-1] - self.frame_times[0] if time_diff > 0: return (len(self.frame_times) - 1) / time_diff return 0.0 def reset(self): """重置计数器""" self.frame_times.clear() self.last_time = time.time()