update power estimation and upload 2 models of yolo
This commit is contained in:
155
vision.py
155
vision.py
@@ -678,6 +678,91 @@ def estimate_distance(pixel_radius):
|
||||
return 0.0
|
||||
return (config.REAL_RADIUS_CM * config.FOCAL_LENGTH_PIX) / pixel_radius / 100.0
|
||||
|
||||
def _draw_yolo_roi_on_rgb_numpy(img_cv, yolo_roi_xyxy):
|
||||
"""
|
||||
在 RGB numpy 图像上绘制靶环 YOLO ROI(与原先 shoot_manager 主线程绘制语义一致)。
|
||||
供存图 worker 异步调用,不阻塞射箭主流程。
|
||||
"""
|
||||
if yolo_roi_xyxy is None:
|
||||
return
|
||||
if not getattr(config, "TRIANGLE_YOLO_DRAW_ROI_ON_SHOT", True):
|
||||
return
|
||||
try:
|
||||
rx0, ry0, rx1, ry1 = (int(round(float(v))) for v in yolo_roi_xyxy)
|
||||
ih, iw = img_cv.shape[:2]
|
||||
rx0 = max(0, min(rx0, iw - 1))
|
||||
ry0 = max(0, min(ry0, ih - 1))
|
||||
rx1 = max(rx0 + 1, min(rx1, iw))
|
||||
ry1 = max(ry0 + 1, min(ry1, ih))
|
||||
cv2.rectangle(
|
||||
img_cv,
|
||||
(rx0, ry0),
|
||||
(rx1 - 1, ry1 - 1),
|
||||
(0, 255, 255),
|
||||
2,
|
||||
)
|
||||
cv2.putText(
|
||||
img_cv,
|
||||
"YOLO ROI",
|
||||
(max(0, rx0), max(16, ry0 - 4)),
|
||||
cv2.FONT_HERSHEY_SIMPLEX,
|
||||
0.55,
|
||||
(0, 255, 255),
|
||||
1,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def prune_old_images_in_dir(photo_dir, max_images, logger=None, log_prefix="[VISION]"):
|
||||
"""
|
||||
若目录内 bmp/jpg/jpeg 超过 max_images,按 mtime 从最旧开始删,直到数量 ≤ max_images。
|
||||
与射箭主图目录清理规则一致,供 PHOTO_DIR、stage2_roi 等共用。
|
||||
"""
|
||||
try:
|
||||
max_images = int(max_images)
|
||||
except (TypeError, ValueError):
|
||||
return
|
||||
if max_images <= 0 or not photo_dir:
|
||||
return
|
||||
if logger is None:
|
||||
logger = logger_manager.logger
|
||||
try:
|
||||
if not os.path.isdir(photo_dir):
|
||||
return
|
||||
image_files = []
|
||||
for f in os.listdir(photo_dir):
|
||||
if f.endswith((".bmp", ".jpg", ".jpeg")):
|
||||
filepath = os.path.join(photo_dir, f)
|
||||
try:
|
||||
mtime = os.path.getmtime(filepath)
|
||||
image_files.append((mtime, filepath, f))
|
||||
except Exception:
|
||||
pass
|
||||
if len(image_files) <= max_images:
|
||||
return
|
||||
image_files.sort(key=lambda x: x[0])
|
||||
to_delete = len(image_files) - max_images
|
||||
deleted_count = 0
|
||||
for _, filepath, fname in image_files[:to_delete]:
|
||||
try:
|
||||
os.remove(filepath)
|
||||
deleted_count += 1
|
||||
if logger:
|
||||
logger.debug(f"{log_prefix} 删除旧图片: {fname}")
|
||||
except Exception as e:
|
||||
if logger:
|
||||
logger.warning(f"{log_prefix} 删除旧图片失败 {fname}: {e}")
|
||||
if logger and deleted_count > 0:
|
||||
logger.info(
|
||||
f"{log_prefix} 已清理 {deleted_count} 张旧图,"
|
||||
f"目录保留至多 {max_images} 张: {photo_dir}"
|
||||
)
|
||||
except Exception as e:
|
||||
if logger:
|
||||
logger.warning(f"{log_prefix} 清理旧图片时出错(可忽略): {e}")
|
||||
|
||||
|
||||
def estimate_pixel(physical_distance_cm, target_distance_m):
|
||||
"""
|
||||
根据物理距离和目标距离计算对应的像素偏移
|
||||
@@ -696,7 +781,8 @@ def estimate_pixel(physical_distance_cm, target_distance_m):
|
||||
|
||||
|
||||
def _save_shot_image_impl(img_cv, center, radius, method, ellipse_params,
|
||||
laser_point, distance_m, shot_id=None, photo_dir=None):
|
||||
laser_point, distance_m, shot_id=None, photo_dir=None,
|
||||
yolo_roi_xyxy=None):
|
||||
"""
|
||||
内部实现:在 img_cv (numpy HWC RGB) 上绘制标注并保存。
|
||||
由 save_shot_image(同步)和存图 worker(异步)调用。
|
||||
@@ -735,6 +821,8 @@ def _save_shot_image_impl(img_cv, center, radius, method, ellipse_params,
|
||||
distance_str = str(round((distance_m or 0.0) * 100))
|
||||
filename = f"{photo_dir}/{method_str}_{int(x)}_{int(y)}_{distance_str}_{img_count:04d}.jpg"
|
||||
|
||||
_draw_yolo_roi_on_rgb_numpy(img_cv, yolo_roi_xyxy)
|
||||
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
if shot_id:
|
||||
@@ -786,37 +874,7 @@ def _save_shot_image_impl(img_cv, center, radius, method, ellipse_params,
|
||||
else:
|
||||
logger.debug(f"图像已保存(无靶心,含激光十字线): {filename}")
|
||||
|
||||
# 清理旧图片:如果目录下图片超过100张,删除最老的
|
||||
try:
|
||||
image_files = []
|
||||
for f in os.listdir(photo_dir):
|
||||
if f.endswith(('.bmp', '.jpg', '.jpeg')):
|
||||
filepath = os.path.join(photo_dir, f)
|
||||
try:
|
||||
mtime = os.path.getmtime(filepath)
|
||||
image_files.append((mtime, filepath, f))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
from config import MAX_IMAGES
|
||||
if len(image_files) > MAX_IMAGES:
|
||||
image_files.sort(key=lambda x: x[0])
|
||||
to_delete = len(image_files) - MAX_IMAGES
|
||||
deleted_count = 0
|
||||
for _, filepath, fname in image_files[:to_delete]:
|
||||
try:
|
||||
os.remove(filepath)
|
||||
deleted_count += 1
|
||||
if logger:
|
||||
logger.debug(f"[VISION] 删除旧图片: {fname}")
|
||||
except Exception as e:
|
||||
if logger:
|
||||
logger.warning(f"[VISION] 删除旧图片失败 {fname}: {e}")
|
||||
if logger and deleted_count > 0:
|
||||
logger.info(f"[VISION] 已清理 {deleted_count} 张旧图片,当前剩余 {MAX_IMAGES} 张")
|
||||
except Exception as e:
|
||||
if logger:
|
||||
logger.warning(f"[VISION] 清理旧图片时出错(可忽略): {e}")
|
||||
prune_old_images_in_dir(photo_dir, config.MAX_IMAGES, logger, "[VISION]")
|
||||
|
||||
return filename
|
||||
except Exception as e:
|
||||
@@ -864,7 +922,8 @@ def start_save_shot_worker():
|
||||
|
||||
|
||||
def enqueue_save_shot(result_img, center, radius, method, ellipse_params,
|
||||
laser_point, distance_m, shot_id=None, photo_dir=None):
|
||||
laser_point, distance_m, shot_id=None, photo_dir=None,
|
||||
yolo_roi_xyxy=None):
|
||||
"""
|
||||
将存图任务放入队列,由 worker 异步保存。主线程传入 result_img 的复制,不阻塞。
|
||||
"""
|
||||
@@ -880,7 +939,18 @@ def enqueue_save_shot(result_img, center, radius, method, ellipse_params,
|
||||
if logger:
|
||||
logger.error(f"[VISION] enqueue_save_shot 复制图像失败: {e}")
|
||||
return
|
||||
task = (img_copy, center, radius, method, ellipse_params, laser_point, distance_m, shot_id, photo_dir)
|
||||
task = (
|
||||
img_copy,
|
||||
center,
|
||||
radius,
|
||||
method,
|
||||
ellipse_params,
|
||||
laser_point,
|
||||
distance_m,
|
||||
shot_id,
|
||||
photo_dir,
|
||||
yolo_roi_xyxy,
|
||||
)
|
||||
try:
|
||||
_save_queue.put_nowait(task)
|
||||
except queue.Full:
|
||||
@@ -890,7 +960,8 @@ def enqueue_save_shot(result_img, center, radius, method, ellipse_params,
|
||||
|
||||
|
||||
def save_shot_image(result_img, center, radius, method, ellipse_params,
|
||||
laser_point, distance_m, shot_id=None, photo_dir=None):
|
||||
laser_point, distance_m, shot_id=None, photo_dir=None,
|
||||
yolo_roi_xyxy=None):
|
||||
"""
|
||||
保存射击图像(带标注)。同步调用,会阻塞。
|
||||
主流程建议使用 enqueue_save_shot;此处保留供校准、测试等场景使用。
|
||||
@@ -901,8 +972,18 @@ def save_shot_image(result_img, center, radius, method, ellipse_params,
|
||||
photo_dir = config.PHOTO_DIR
|
||||
try:
|
||||
img_cv = image.image2cv(result_img, False, False)
|
||||
return _save_shot_image_impl(img_cv, center, radius, method, ellipse_params,
|
||||
laser_point, distance_m, shot_id, photo_dir)
|
||||
return _save_shot_image_impl(
|
||||
img_cv,
|
||||
center,
|
||||
radius,
|
||||
method,
|
||||
ellipse_params,
|
||||
laser_point,
|
||||
distance_m,
|
||||
shot_id,
|
||||
photo_dir,
|
||||
yolo_roi_xyxy,
|
||||
)
|
||||
except Exception as e:
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
|
||||
Reference in New Issue
Block a user