|
|
@@ -1527,23 +1527,30 @@ class AsyncCoordinator(Coordinator):
|
|
|
self.ptz.get_frame()
|
|
|
time.sleep(0.05)
|
|
|
|
|
|
- for i in range(max_attempts):
|
|
|
+ # 【关键修复】只尝试少量次数,尽快拿到可用帧
|
|
|
+ # 避免在获取帧期间球机被移动到其他位置
|
|
|
+ effective_attempts = min(max_attempts, 5)
|
|
|
+
|
|
|
+ for i in range(effective_attempts):
|
|
|
frame = self.ptz.get_frame()
|
|
|
if frame is not None:
|
|
|
+ # 立即复制帧,防止 RTSP 流更新导致帧被覆盖
|
|
|
+ frame_copy = frame.copy()
|
|
|
+
|
|
|
# 使用拉普拉斯算子评估图像清晰度
|
|
|
- gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
|
|
+ gray = cv2.cvtColor(frame_copy, cv2.COLOR_BGR2GRAY)
|
|
|
laplacian_var = cv2.Laplacian(gray, cv2.CV_64F).var()
|
|
|
|
|
|
- logger.debug(f"[帧获取] 尝试 {i+1}/{max_attempts}: 清晰度={laplacian_var:.1f}")
|
|
|
+ logger.debug(f"[帧获取] 尝试 {i+1}/{effective_attempts}: 清晰度={laplacian_var:.1f}")
|
|
|
|
|
|
if laplacian_var > best_score:
|
|
|
best_score = laplacian_var
|
|
|
- best_frame = frame.copy()
|
|
|
+ best_frame = frame_copy
|
|
|
|
|
|
# 如果清晰度足够高,直接返回
|
|
|
- if laplacian_var > 150: # 【提高】清晰度阈值从100提高到150
|
|
|
+ if laplacian_var > 150:
|
|
|
logger.info(f"[帧获取] 获取清晰帧: 尝试 {i+1} 次, 清晰度={laplacian_var:.1f}")
|
|
|
- return frame
|
|
|
+ return frame_copy
|
|
|
|
|
|
time.sleep(wait_interval)
|
|
|
|
|
|
@@ -1837,13 +1844,14 @@ class SequentialCoordinator(AsyncCoordinator):
|
|
|
state = self._get_capture_state()
|
|
|
|
|
|
if state == 'capturing':
|
|
|
- # 执行顺序抓拍
|
|
|
+ # 执行顺序抓拍(阻塞直到当前目标抓拍完成)
|
|
|
self._execute_sequential_capture()
|
|
|
|
|
|
elif state == 'returning':
|
|
|
# 回到默认位置
|
|
|
self._return_to_default_position()
|
|
|
|
|
|
+ # idle 状态不做任何操作,让检测线程控制
|
|
|
time.sleep(0.05)
|
|
|
|
|
|
except Exception as e:
|
|
|
@@ -1931,6 +1939,10 @@ class SequentialCoordinator(AsyncCoordinator):
|
|
|
f"位置=({x_ratio:.3f}, {y_ratio:.3f}) -> "
|
|
|
f"PTZ=({pan:.1f}°, {tilt:.1f}°, zoom={zoom}) [{coord_type}], 批次={batch_id}")
|
|
|
|
|
|
+ # 【关键修复】先递增索引,防止 PTZ 线程重复进入时重复执行
|
|
|
+ with self._batch_targets_lock:
|
|
|
+ self._current_capture_index += 1
|
|
|
+
|
|
|
# 执行PTZ移动
|
|
|
self._set_state(TrackingState.TRACKING)
|
|
|
success = self.ptz.goto_exact_position(pan, tilt, zoom)
|
|
|
@@ -1983,10 +1995,6 @@ class SequentialCoordinator(AsyncCoordinator):
|
|
|
else:
|
|
|
logger.warning(f"[顺序模式] PTZ移动失败")
|
|
|
|
|
|
- # 移动到下一个目标
|
|
|
- with self._batch_targets_lock:
|
|
|
- self._current_capture_index += 1
|
|
|
-
|
|
|
# 抓拍间隔
|
|
|
time.sleep(self._capture_config['capture_wait_time'])
|
|
|
|
|
|
@@ -2001,6 +2009,10 @@ class SequentialCoordinator(AsyncCoordinator):
|
|
|
default_tilt = self._capture_config['default_tilt']
|
|
|
default_zoom = self._capture_config['default_zoom']
|
|
|
|
|
|
+ # 【关键修复】等待一小段时间,确保最后的帧已经被读取和保存
|
|
|
+ # 因为 _get_clear_ptz_frame 是异步获取帧,可能在抓拍后立刻触发返回默认位置
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
logger.info(f"[顺序模式] 球机回到默认位置: ({default_pan}°, {default_tilt}°, zoom={default_zoom})")
|
|
|
|
|
|
success = self.ptz.goto_exact_position(default_pan, default_tilt, default_zoom)
|