137 lines
4.5 KiB
Python
137 lines
4.5 KiB
Python
# hik_camera_app.py
|
||
# 菜品识别:仅海康相机(UI 复用 ui.classifier_ui)
|
||
|
||
import os
|
||
import sys
|
||
import threading
|
||
import time
|
||
from ctypes import WinDLL
|
||
|
||
import cv2
|
||
|
||
dll_path = r'C:\Program Files (x86)\Common Files\MVS\Runtime\Win64_x64\MvCameraControl.dll'
|
||
if not os.path.exists(dll_path):
|
||
raise FileNotFoundError(f"找不到海康 DLL:{dll_path}")
|
||
try:
|
||
MvCamCtrldll = WinDLL(dll_path)
|
||
print(f"[OK] loaded DLL: {dll_path}")
|
||
except Exception as e:
|
||
raise RuntimeError(f"加载海康 DLL 失败:{e}")
|
||
|
||
if hasattr(sys, 'frozen'):
|
||
os.environ['PYTHONIOENCODING'] = 'utf-8'
|
||
|
||
from ui.classifier_ui import BaseDishApp, run_app
|
||
|
||
try:
|
||
from camera.hik_camera_tool import HikCameraCapture
|
||
CAMERA_AVAILABLE = True
|
||
except ImportError:
|
||
CAMERA_AVAILABLE = False
|
||
print("[WARN] hik camera tool not found")
|
||
|
||
|
||
class ImageClassifierDemo(BaseDishApp):
|
||
window_title = "菜品识别系统"
|
||
app_title = "菜品识别系统"
|
||
edition_label = "海康相机版"
|
||
camera_display_name = "海康相机"
|
||
|
||
def __init__(self, root):
|
||
self.camera = None
|
||
self._frame_lock = threading.Lock()
|
||
super().__init__(root)
|
||
|
||
def auto_connect_camera(self):
|
||
if not CAMERA_AVAILABLE:
|
||
self.root.after(0, self._update_camera_ui_fail)
|
||
return
|
||
|
||
def connect_thread():
|
||
try:
|
||
self.camera = HikCameraCapture()
|
||
devices = self.camera.enum_devices()
|
||
if devices:
|
||
self.camera.connect_camera(0)
|
||
self.camera.start_grabbing()
|
||
self.camera_connected = True
|
||
self.root.after(0, self._update_camera_ui_success)
|
||
return
|
||
self.camera = None
|
||
except Exception as e:
|
||
self.camera = None
|
||
print(f"海康相机连接失败:{e}")
|
||
self.root.after(0, self._update_camera_ui_fail)
|
||
|
||
threading.Thread(target=connect_thread, daemon=True).start()
|
||
|
||
def grab_preview_frame(self):
|
||
"""内存取 BGR,供流畅预览(不落盘)。"""
|
||
if not self.camera_connected or self.camera is None:
|
||
return None
|
||
with self._frame_lock:
|
||
try:
|
||
return self.camera.grab_bgr_frame(timeout_ms=200)
|
||
except Exception as e:
|
||
print(f"海康取帧失败: {e}")
|
||
return None
|
||
|
||
def take_live_frame(self):
|
||
"""识别用:优先复用预览缓存帧;否则再取一帧并落盘。"""
|
||
frame = None
|
||
with getattr(self, "_live_lock", self._frame_lock):
|
||
latest = getattr(self, "_latest_frame", None)
|
||
if latest is not None:
|
||
try:
|
||
frame = latest.copy()
|
||
except Exception:
|
||
frame = None
|
||
if frame is None:
|
||
frame = self.grab_preview_frame()
|
||
if frame is None:
|
||
return -1, None, None
|
||
os.makedirs("images", exist_ok=True)
|
||
stamp = int(time.time() * 1000)
|
||
save_path = os.path.abspath(os.path.join("images", f"_live_frame_{stamp}.jpg"))
|
||
try:
|
||
if cv2.imwrite(save_path, frame):
|
||
self._cleanup_old_live_frames(keep=30)
|
||
return 0, save_path, frame
|
||
except Exception as e:
|
||
print(f"海康实时取帧失败: {e}")
|
||
return -1, None, None
|
||
|
||
def take_photo(self):
|
||
os.makedirs("images", exist_ok=True)
|
||
capture_time = time.strftime("%Y%m%d_%H%M%S")
|
||
save_path = f'images/capture_{capture_time}.jpg'
|
||
if not self.camera_connected or self.camera is None:
|
||
return -1, None
|
||
try:
|
||
with self._frame_lock:
|
||
ret, path = self.camera.capture_and_save_jpg(save_path, 90)
|
||
if ret == 0:
|
||
return ret, path
|
||
except Exception as e:
|
||
print(f"海康拍照失败: {e}")
|
||
return -1, None
|
||
|
||
def close_camera(self):
|
||
if self.camera is not None:
|
||
try:
|
||
self.camera.finalize()
|
||
except Exception as e:
|
||
print(f"释放海康相机失败: {e}")
|
||
finally:
|
||
self.camera = 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)
|