From e75425ac148b56aa2ac12def27e18c546fe430c5 Mon Sep 17 00:00:00 2001 From: hubian <908234780@qq.com> Date: Fri, 17 Apr 2026 20:45:41 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=91=84=E5=83=8F=E5=A4=B4=E6=AF=8F?= =?UTF-8?q?=E6=AC=A1=E6=8B=8D=E7=85=A7=E5=90=8E=E7=AB=8B=E5=8D=B3=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E9=87=8A=E6=94=BE=E8=B5=84=E6=BA=90=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=85=A8=E9=BB=91=E6=A3=80=E6=B5=8B=E5=92=8C=E9=87=8D?= =?UTF-8?q?=E8=AF=95=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- camera.py | 163 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 116 insertions(+), 47 deletions(-) diff --git a/camera.py b/camera.py index 885fce4..264ff22 100644 --- a/camera.py +++ b/camera.py @@ -3,6 +3,7 @@ """ import cv2 import datetime +import time from pathlib import Path from config import config_mgr @@ -13,17 +14,30 @@ class CameraCapture: def __init__(self): self.camera_index = config_mgr.get('camera_index', 0) self.cap = None + self._closed = True + self._release_time = 0 def open(self): """打开摄像头""" - if self.cap is None or not self.cap.isOpened(): - self.camera_index = config_mgr.get('camera_index', 0) - self.cap = cv2.VideoCapture(self.camera_index, cv2.CAP_DSHOW) # Windows DirectShow - if not self.cap.isOpened(): - raise Exception(f"无法打开摄像头 {self.camera_index}") - # 设置分辨率 - self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) - self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) + if self.cap is not None and self.cap.isOpened(): + return True + + # 确保之前的摄像头已完全关闭 + if not self._closed: + self.close() + import time + time.sleep(0.5) # 等待摄像头完全释放 + + self.camera_index = config_mgr.get('camera_index', 0) + self.cap = cv2.VideoCapture(self.camera_index, cv2.CAP_DSHOW) # Windows DirectShow + if not self.cap.isOpened(): + raise Exception(f"无法打开摄像头 {self.camera_index}") + + # 设置分辨率 + self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) + self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) + + self._closed = False return self.cap.isOpened() def close(self): @@ -31,49 +45,102 @@ class CameraCapture: if self.cap is not None: self.cap.release() self.cap = None + self._closed = True + self._release_time = time.time() - def capture(self, save_path=None): - """拍照并保存(按日期文件夹组织) + def capture(self, save_path=None, retry_count=3): + """拍照并保存(每次拍照后自动关闭摄像头) + Args: + save_path: 保存路径 + retry_count: 重试次数 + Returns: dict: {'success': bool, 'path': str, 'timestamp': str, 'date_folder': str, 'error': str} """ - try: - self.open() - - # 等待摄像头稳定(Windows 上需要预热) - for _ in range(5): - self.cap.read() - - ret, frame = self.cap.read() - if not ret: - return {'success': False, 'error': '无法捕获图像'} - - # 获取当前日期 - now = datetime.datetime.now() - date_str = now.strftime('%Y-%m-%d') - timestamp = now.strftime('%Y%m%d_%H%M%S') - - # 获取按日期的保存目录 - date_folder = config_mgr.get_images_dir(date_str) - - if save_path is None: - filename = f"capture_{timestamp}.jpg" - save_path = date_folder / filename - - # 保存图片 - cv2.imwrite(str(save_path), frame, [cv2.IMWRITE_JPEG_QUALITY, 90]) - - return { - 'success': True, - 'path': str(save_path), - 'timestamp': timestamp, - 'date_folder': date_str, - 'filename': save_path.name - } - - except Exception as e: - return {'success': False, 'error': str(e)} + for attempt in range(retry_count): + try: + # 每次拍照都重新打开摄像头 + self.open() + + # 等待摄像头稳定(Windows 上需要预热) + time.sleep(0.5) + + # 读取帧(丢弃前几帧确保稳定) + for _ in range(5): + ret, _ = self.cap.read() + if not ret: + self.close() + if attempt < retry_count - 1: + print(f"[Camera] 预热失败,重试 {attempt + 2}/{retry_count}") + time.sleep(1) + continue + return {'success': False, 'error': '摄像头预热失败'} + + # 拍照 + ret, frame = self.cap.read() + + # 立即关闭摄像头释放资源 + self.close() + + if not ret or frame is None: + if attempt < retry_count - 1: + print(f"[Camera] 捕获失败,重试 {attempt + 2}/{retry_count}") + time.sleep(1) + continue + return {'success': False, 'error': '无法捕获图像'} + + # 检查图像是否全黑 + if self._is_black_frame(frame): + if attempt < retry_count - 1: + print(f"[Camera] 全黑图像,重试 {attempt + 2}/{retry_count}") + time.sleep(2) + continue + return {'success': False, 'error': '捕获到全黑图像'} + + # 成功,保存图片 + now = datetime.datetime.now() + timestamp = now.strftime('%Y%m%d_%H%M%S') + date_str = now.strftime('%Y-%m-%d') + + if save_path is None: + date_folder = config_mgr.get_images_dir(date_str) + filename = f"capture_{timestamp}.jpg" + save_path = date_folder / filename + + cv2.imwrite(str(save_path), frame, [cv2.IMWRITE_JPEG_QUALITY, 90]) + + print(f"[Camera] 拍照成功: {save_path.name}") + + return { + 'success': True, + 'path': str(save_path), + 'timestamp': timestamp, + 'date_folder': save_path.parent.name, + 'filename': save_path.name + } + + except Exception as e: + self.close() + if attempt < retry_count - 1: + print(f"[Camera] 异常: {e}, 重试 {attempt + 2}/{retry_count}") + time.sleep(2) + continue + return {'success': False, 'error': str(e)} + + return {'success': False, 'error': '重试后仍失败'} + + def _is_black_frame(self, frame): + """检测图像是否全黑""" + if frame is None: + return True + + # 计算平均亮度 + gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + mean_brightness = cv2.mean(gray)[0] + + # 如果平均亮度小于 5,视为全黑 + return mean_brightness < 5 def get_camera_info(self): """获取摄像头信息""" @@ -82,14 +149,16 @@ class CameraCapture: width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) fps = int(self.cap.get(cv2.CAP_PROP_FPS)) + self.close() return { 'index': self.camera_index, 'width': width, 'height': height, 'fps': fps, - 'status': 'opened' + 'status': 'ok' } except Exception as e: + self.close() return { 'index': self.camera_index, 'status': 'error',