-
This commit is contained in:
+1
-1
@@ -35,7 +35,7 @@ MAP_CONFIG = {
|
||||
|
||||
# ========== 摄像头 ==========
|
||||
CAMERA_CONFIG = {
|
||||
"device_index": 3, # AGV 摄像头 video3(Orbbec Gemini 彩色流,V4L2后端)
|
||||
"device_index": 4, # AGV 摄像头 video4(Orbbec Gemini 彩色流,YUYV 640x480)
|
||||
"backend": "v4l2", # 使用 V4L2 后端获取标准彩色格式(640x480)
|
||||
"qr_detect_interval": 0.5,
|
||||
"capture_delay": 0.5,
|
||||
|
||||
@@ -74,7 +74,7 @@ class QRScanner:
|
||||
# 情况 2: 3 通道但实际帧数据显示为 YUYV(绿屏特征:G 通道全满,B/R 近空)
|
||||
if ndim == 3:
|
||||
g_mean = frame[:, :, 1].mean()
|
||||
if g_mean > 220 and frame[:, :, 0].mean() < 30 and frame[:, :, 2].mean() < 30:
|
||||
if g_mean > 80 and frame[:, :, 0].mean() < 30 and frame[:, :, 2].mean() < 30:
|
||||
# 典型的"Lime"绿屏 — 当做 YUYV 原始数据解码
|
||||
logger.debug(f"检测到绿屏 (G={g_mean:.0f}, B={frame[:,:,0].mean():.0f}, R={frame[:,:,2].mean():.0f}),尝试修复")
|
||||
try:
|
||||
@@ -85,7 +85,7 @@ class QRScanner:
|
||||
# 我们只需要取前 w*h*2 字节作为 YUYV 数据
|
||||
yuyv_len = w * h * 2
|
||||
if len(raw_bytes) >= yuyv_len:
|
||||
yuyv_img = np.frombuffer(raw_bytes[:yuyv_len], dtype=np.uint8).reshape(h, w * 2, 1)
|
||||
yuyv_img = np.frombuffer(raw_bytes[:yuyv_len], dtype=np.uint8).reshape(h, w, 2)
|
||||
frame = cv2.cvtColor(yuyv_img, cv2.COLOR_YUV2BGR_YUYV)
|
||||
logger.debug("绿屏修复完成")
|
||||
return frame
|
||||
@@ -101,14 +101,35 @@ class QRScanner:
|
||||
# 正常 BGR 帧
|
||||
return frame
|
||||
|
||||
def read_frame(self) -> Optional[np.ndarray]:
|
||||
"""读取一帧"""
|
||||
def read_frame(self, timeout: float = 2.0) -> Optional[np.ndarray]:
|
||||
"""读取一帧(带超时保护,避免 V4L2 select() 永久阻塞)"""
|
||||
if not self._cap or not self._cap.isOpened():
|
||||
return None
|
||||
ret, frame = self._cap.read()
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeout
|
||||
|
||||
pool = ThreadPoolExecutor(max_workers=1)
|
||||
try:
|
||||
fut = pool.submit(self._cap.read)
|
||||
ret, frame = fut.result(timeout=timeout)
|
||||
if not ret or frame is None:
|
||||
return None
|
||||
return self._fix_frame(frame)
|
||||
except FuturesTimeout:
|
||||
logger.warning(f"摄像头 read_frame 超时 ({timeout}s),尝试重建 _cap")
|
||||
self.close()
|
||||
self.open()
|
||||
# 重建后重试一次
|
||||
if self._cap and self._cap.isOpened():
|
||||
ret, frame = self._cap.read()
|
||||
if ret and frame is not None:
|
||||
return self._fix_frame(frame)
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"read_frame 异常: {e}")
|
||||
return None
|
||||
finally:
|
||||
pool.shutdown(wait=False)
|
||||
|
||||
def detect_qr(self, frame: np.ndarray) -> Optional[str]:
|
||||
"""从图像帧中检测二维码"""
|
||||
|
||||
Reference in New Issue
Block a user