# usb_camera_app.py # 菜品识别:USB 相机实时视频流(UI 复用 ui.classifier_ui) import os import sys import threading import time import cv2 if hasattr(sys, 'frozen'): os.environ['PYTHONIOENCODING'] = 'utf-8' from ui.classifier_ui import BaseDishApp, run_app def _open_usb_capture(index=0): """Windows 优先 DirectShow,避免默认 MSMF 打开要几十秒。""" candidates = [] if sys.platform.startswith('win'): candidates.append(('DSHOW', cv2.CAP_DSHOW)) candidates.append(('MSMF', cv2.CAP_MSMF)) candidates.append(('ANY', cv2.CAP_ANY)) last_err = None for name, backend in candidates: cap = None try: t0 = time.perf_counter() cap = cv2.VideoCapture(index, backend) if not cap.isOpened(): cap.release() continue cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) ok, frame = cap.read() elapsed = time.perf_counter() - t0 if ok and frame is not None and frame.size > 0: print(f'[OK] USB camera opened via {name} in {elapsed:.2f}s') return cap cap.release() except Exception as exc: last_err = exc try: if cap is not None: cap.release() except Exception: pass if last_err: print(f'[WARN] USB open attempts failed: {last_err}') return None class ImageClassifierDemo(BaseDishApp): window_title = "菜品识别系统" app_title = "菜品识别系统" edition_label = "USB 相机版" camera_display_name = "USB相机" def __init__(self, root): self.cap = None self._frame_lock = threading.Lock() self._cam_ready = threading.Event() self._cam_ok = False # 与 YOLO/UI 初始化并行打开相机,缩短出画面时间 threading.Thread(target=self._connect_camera_worker, daemon=True).start() super().__init__(root) def _connect_camera_worker(self): try: cap = _open_usb_capture(0) if cap is not None: self.cap = cap self._cam_ok = True else: self.cap = None self._cam_ok = False except Exception as e: self.cap = None self._cam_ok = False print(f"USB相机连接失败:{e}") finally: self._cam_ready.set() def auto_connect_camera(self): def finish(): self._cam_ready.wait(timeout=90) if self._cam_ok and self.cap is not None and self.cap.isOpened(): self.camera_connected = True self.root.after(0, self._update_camera_ui_success) else: self.camera_connected = False self.root.after(0, self._update_camera_ui_fail) threading.Thread(target=finish, daemon=True).start() def grab_preview_frame(self): if not self.camera_connected or self.cap is None: return None with self._frame_lock: try: self.cap.grab() ret, frame = self.cap.retrieve() if not ret or frame is None or frame.size == 0: ret, frame = self.cap.read() if ret and frame is not None and frame.size > 0: return frame except Exception as e: print(f"USB取帧失败: {e}") return None def take_photo(self): if not self.camera_connected or self.cap is None: return -1, None os.makedirs("images", exist_ok=True) capture_time = time.strftime("%Y%m%d_%H%M%S") save_path = os.path.abspath(f'images/capture_{capture_time}.jpg') try: frame = self.grab_preview_frame() if frame is None: return -1, None if cv2.imwrite(save_path, frame): return 0, save_path except Exception as e: print(f"USB拍照失败: {e}") return -1, None def close_camera(self): if self.cap is not None: self.cap.release() self.cap = None self.camera_connected = False if __name__ == "__main__": try: from service.media_server import ensure_media_server ensure_media_server() except Exception as exc: print(f'[WARN] media server start failed: {exc}') run_app(ImageClassifierDemo)