#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 离线测试脚本:直接复用 detect_circle 逻辑进行测试 运行环境:MaixPy (Sipeed MAIX) """ import sys import os # import time from maix import image,time import cv2 import numpy as np # ==================== 全局配置 (与 test_main.py 保持一致) ==================== REAL_RADIUS_CM = 20 # 靶心实际半径(厘米) # ==================== 复制的核心算法 ==================== # 注意:这里直接复制了 detect_circle 的逻辑,避免 import main 导致的冲突 def detect_circle_v3(frame, laser_point=None): """检测图像中的靶心(优先清晰轮廓,其次黄色区域)- 返回椭圆参数版本 增加红色圆圈检测,验证黄色圆圈是否为真正的靶心 如果提供 laser_point,会选择最接近激光点的目标 Args: frame: 图像帧 laser_point: 激光点坐标 (x, y),用于多目标场景下的目标选择 Returns: (result_img, best_center, best_radius, method, best_radius1, ellipse_params) """ img_cv = image.image2cv(frame, False, False) best_center = best_radius = best_radius1 = method = None ellipse_params = None # HSV 黄色掩码检测(模糊靶心) hsv = cv2.cvtColor(img_cv, cv2.COLOR_RGB2HSV) h, s, v = cv2.split(hsv) # 调整饱和度策略:稍微增强,不要过度 s = np.clip(s * 1.1, 0, 255).astype(np.uint8) hsv = cv2.merge((h, s, v)) # 放宽 HSV 阈值范围(针对模糊图像的关键调整) lower_yellow = np.array([7, 80, 0]) # 饱和度下限降低,捕捉淡黄色 upper_yellow = np.array([32, 255, 255]) # 亮度上限拉满 mask_yellow = cv2.inRange(hsv, lower_yellow, upper_yellow) # 调整形态学操作 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) mask_yellow = cv2.morphologyEx(mask_yellow, cv2.MORPH_CLOSE, kernel) contours_yellow, _ = cv2.findContours(mask_yellow, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 存储所有有效的黄色-红色组合 valid_targets = [] if contours_yellow: for cnt_yellow in contours_yellow: area = cv2.contourArea(cnt_yellow) perimeter = cv2.arcLength(cnt_yellow, True) # 计算圆度 if perimeter > 0: circularity = (4 * np.pi * area) / (perimeter * perimeter) else: circularity = 0 logger = get_logger() if area > 50 and circularity > 0.7: if logger: logger.info(f"[target] -> 面积:{area}, 圆度:{circularity:.2f}") # 尝试拟合椭圆 yellow_center = None yellow_radius = None yellow_ellipse = None if len(cnt_yellow) >= 5: (x, y), (width, height), angle = cv2.fitEllipse(cnt_yellow) yellow_ellipse = ((x, y), (width, height), angle) axes_minor = min(width, height) radius = axes_minor / 2 yellow_center = (int(x), int(y)) yellow_radius = int(radius) else: (x, y), radius = cv2.minEnclosingCircle(cnt_yellow) yellow_center = (int(x), int(y)) yellow_radius = int(radius) yellow_ellipse = None # 如果检测到黄色圆圈,再检测红色圆圈进行验证 if yellow_center and yellow_radius: # HSV 红色掩码检测(红色在HSV中跨越0度,需要两个范围) # 红色范围1: 0-10度(接近0度的红色) lower_red1 = np.array([0, 80, 0]) upper_red1 = np.array([10, 255, 255]) mask_red1 = cv2.inRange(hsv, lower_red1, upper_red1) # 红色范围2: 170-180度(接近180度的红色) lower_red2 = np.array([170, 80, 0]) upper_red2 = np.array([180, 255, 255]) mask_red2 = cv2.inRange(hsv, lower_red2, upper_red2) # 合并两个红色掩码 mask_red = cv2.bitwise_or(mask_red1, mask_red2) # 形态学操作 kernel_red = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) mask_red = cv2.morphologyEx(mask_red, cv2.MORPH_CLOSE, kernel_red) contours_red, _ = cv2.findContours(mask_red, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) found_valid_red = False if contours_red: # 找到所有符合条件的红色圆圈 for cnt_red in contours_red: area_red = cv2.contourArea(cnt_red) perimeter_red = cv2.arcLength(cnt_red, True) if perimeter_red > 0: circularity_red = (4 * np.pi * area_red) / (perimeter_red * perimeter_red) else: circularity_red = 0 # 红色圆圈也应该有一定的圆度 if area_red > 50 and circularity_red > 0.6: # 计算红色圆圈的中心和半径 if len(cnt_red) >= 5: (x_red, y_red), (w_red, h_red), angle_red = cv2.fitEllipse(cnt_red) radius_red = min(w_red, h_red) / 2 red_center = (int(x_red), int(y_red)) red_radius = int(radius_red) else: (x_red, y_red), radius_red = cv2.minEnclosingCircle(cnt_red) red_center = (int(x_red), int(y_red)) red_radius = int(radius_red) # 计算黄色和红色圆心的距离 if red_center: dx = yellow_center[0] - red_center[0] dy = yellow_center[1] - red_center[1] distance = np.sqrt(dx*dx + dy*dy) # 圆心距离阈值:应该小于黄色半径的某个倍数(比如1.5倍) max_distance = yellow_radius * 1.5 # 红色圆圈应该比黄色圆圈大(外圈) if distance < max_distance and red_radius > yellow_radius * 0.8: found_valid_red = True logger = get_logger() if logger: logger.info(f"[target] -> 找到匹配的红圈: 黄心({yellow_center}), 红心({red_center}), 距离:{distance:.1f}, 黄半径:{yellow_radius}, 红半径:{red_radius}") # 记录这个有效目标 valid_targets.append({ 'center': yellow_center, 'radius': yellow_radius, 'ellipse': yellow_ellipse, 'area': area }) break if not found_valid_red: logger = get_logger() if logger: logger.debug("Debug -> 未找到匹配的红色圆圈,可能是误识别") # 从所有有效目标中选择最佳目标 if valid_targets: if laser_point: # 如果有激光点,选择最接近激光点的目标 best_target = None min_distance = float('inf') for target in valid_targets: dx = target['center'][0] - laser_point[0] dy = target['center'][1] - laser_point[1] distance = np.sqrt(dx*dx + dy*dy) if distance < min_distance: min_distance = distance best_target = target if best_target: best_center = best_target['center'] best_radius = best_target['radius'] ellipse_params = best_target['ellipse'] method = "v3_ellipse_red_validated_laser_selected" best_radius1 = best_radius * 5 else: # 如果没有激光点,选择面积最大的目标 best_target = max(valid_targets, key=lambda t: t['area']) best_center = best_target['center'] best_radius = best_target['radius'] ellipse_params = best_target['ellipse'] method = "v3_ellipse_red_validated" best_radius1 = best_radius * 5 result_img = image.cv2image(img_cv, False, False) return result_img, best_center, best_radius, method, best_radius1, ellipse_params def detect_circle(frame): """检测图像中的靶心(优先清晰轮廓,其次黄色区域)""" img_cv = image.image2cv(frame, False, False) # gray = cv2.cvtColor(img_cv, cv2.COLOR_RGB2GRAY) # blurred = cv2.GaussianBlur(gray, (5, 5), 0) # edged = cv2.Canny(blurred, 50, 150) # kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # ceroded = cv2.erode(cv2.dilate(edged, kernel), kernel) # contours, _ = cv2.findContours(ceroded, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # best_center = best_radius = best_radius1 = method = None # hsv = cv2.cvtColor(img_cv, cv2.COLOR_RGB2HSV) # h, s, v = cv2.split(hsv) # s = np.clip(s * 2, 0, 255).astype(np.uint8) # hsv = cv2.merge((h, s, v)) # lower_yellow = np.array([7, 80, 0]) # upper_yellow = np.array([32, 255, 182]) # mask = cv2.inRange(hsv, lower_yellow, upper_yellow) # kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # mask = cv2.morphologyEx(mask, cv2.MORPH_DILATE, kernel) # contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # if contours: # largest = max(contours, key=cv2.contourArea) # if cv2.contourArea(largest) > 50: # (x, y), radius = cv2.minEnclosingCircle(largest) # best_center = (int(x), int(y)) # best_radius = int(radius) # best_radius1 = radius * 5 # method = "v2" # auto # R:31 M:v2 D:2.410110127692767 # hsv = cv2.cvtColor(img_cv, cv2.COLOR_RGB2HSV) # h, s, v = cv2.split(hsv) # # 1. 增强饱和度(模糊照片需要更强的增强) # s = np.clip(s * 2.5, 0, 255).astype(np.uint8) # 从2.0改为2.5 # # 2. 增强亮度(模糊照片可能偏暗) # v = np.clip(v * 1.2, 0, 255).astype(np.uint8) # 新增:提升亮度 # hsv = cv2.merge((h, s, v)) # # 3. 放宽HSV颜色范围(特别是模糊照片) # # 降低饱和度下限,提高亮度上限 # lower_yellow = np.array([5, 50, 30]) # H:5-35, S:50-255, V:30-255 # upper_yellow = np.array([35, 255, 255]) # mask = cv2.inRange(hsv, lower_yellow, upper_yellow) # # 4. 增强形态学操作(连接被分割的区域) # kernel_small = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # kernel_large = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9, 9)) # 更大的核 # # 先开运算去除噪声 # mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel_small) # # 多次膨胀连接区域(模糊照片需要更多膨胀) # mask = cv2.dilate(mask, kernel_large, iterations=2) # 增加迭代次数 # mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel_large) # 闭运算填充空洞 # contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # if contours: # largest = max(contours, key=cv2.contourArea) # area = cv2.contourArea(largest) # if area > 50: # # 5. 使用面积计算等效半径(更准确) # equivalent_radius = np.sqrt(area / np.pi) # # 6. 同时使用minEnclosingCircle作为备选(取较大值) # (x, y), enclosing_radius = cv2.minEnclosingCircle(largest) # # 取两者中的较大值,确保不遗漏 # radius = max(equivalent_radius, enclosing_radius) # best_center = (int(x), int(y)) # best_radius = int(radius) # best_radius1 = radius * 5 # method = "v2" # codegee # R:24 M:v2 D:3.061493895819174 # R:22 M:v2 D:3.3644971681267077 np.clip(s * 1.1, 0, 255) hsv = cv2.cvtColor(img_cv, cv2.COLOR_RGB2HSV) h, s, v = cv2.split(hsv) # 2. 调整饱和度策略: # 不要暴力翻倍,可以尝试稍微增强,或者使用 CLAHE 增强亮度/对比度 # 这里我们稍微增加一点饱和度,并确保不溢出 s = np.clip(s * 1.1, 0, 255).astype(np.uint8) # 对亮度通道 v 也可以做一点 CLAHE 处理来增强对比度(可选) # clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) # v = clahe.apply(v) hsv = cv2.merge((h, s, v)) # 3. 放宽 HSV 阈值范围(针对模糊图像的关键调整) # 降低 S 的下限 (80 -> 35),提高 V 的上限 (182 -> 255) lower_yellow = np.array([7, 80, 0]) # 饱和度下限降低,捕捉淡黄色 upper_yellow = np.array([32, 255, 255]) # 亮度上限拉满 mask = cv2.inRange(hsv, lower_yellow, upper_yellow) # 4. 调整形态学操作 # 去掉 MORPH_OPEN,因为它会减小面积。 # 使用 MORPH_CLOSE (先膨胀后腐蚀) 来填充内部小黑洞,连接近邻区域 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 再进行一次膨胀,确保边缘被包含进来 # mask = cv2.dilate(mask, kernel, iterations=1) contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if contours: largest = max(contours, key=cv2.contourArea) # 这里可以适当降低面积阈值,或者保持不变 if cv2.contourArea(largest) > 50: # (x, y), radius = cv2.minEnclosingCircle(largest) # best_center = (int(x), int(y)) # best_radius = int(radius) # --- 核心修改开始 --- # 1. 尝试拟合椭圆 (需要轮廓点至少为5个) if len(largest) >= 5: # 返回值: ((中心x, 中心y), (长轴, 短轴), 旋转角度) (x, y), (axes_major, axes_minor), angle = cv2.fitEllipse(largest) # 2. 计算半径 # 选项A:取长短轴的平均值 (比较稳健) # radius = (axes_major + axes_minor) / 4 # 选项B:直接取短轴的一半 (抗模糊最强,推荐) radius = axes_minor / 2 best_center = (int(x), int(y)) best_radius = int(radius) method = "v2_ellipse" else: # 如果点太少无法拟合椭圆,降级回 minEnclosingCircle (x, y), radius = cv2.minEnclosingCircle(largest) best_center = (int(x), int(y)) best_radius = int(radius) method = "v2" # --- 核心修改结束 --- # 你的后续逻辑 best_radius1 = radius * 5 # operas 4.5 # R:25 M:v2 D:2.9554872521538527 # hsv = cv2.cvtColor(img_cv, cv2.COLOR_RGB2HSV) # h, s, v = cv2.split(hsv) # # 1. 适度增强饱和度(不要过度,否则噪声也会增强) # s = np.clip(s * 1.5, 0, 255).astype(np.uint8) # hsv = cv2.merge((h, s, v)) # # 2. 放宽 HSV 阈值范围(关键改动) # # - 饱和度下限从 80 降到 40(捕捉淡黄色) # # - 亮度上限从 182 提高到 255(允许更亮的黄色) # lower_yellow = np.array([7, 40, 30]) # upper_yellow = np.array([35, 255, 255]) # mask = cv2.inRange(hsv, lower_yellow, upper_yellow) # # 3. 调整形态学操作:用 CLOSE 替代 OPEN # # CLOSE(先膨胀后腐蚀):填充内部空洞,连接相邻区域 # # OPEN(先腐蚀后膨胀):会缩小区域,不适合模糊图像 # kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7)) # 稍大的核 # mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # mask = cv2.dilate(mask, kernel, iterations=1) # 额外膨胀,确保边缘被包含 # contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # if contours: # largest = max(contours, key=cv2.contourArea) # if cv2.contourArea(largest) > 50: # (x, y), radius = cv2.minEnclosingCircle(largest) # best_center = (int(x), int(y)) # best_radius = int(radius) # best_radius1 = radius * 5 # method = "v2" # # --- 新增:将 Mask 叠加到原图上用于调试 --- # # 创建一个彩色掩码(红色通道为255,其他为0) # mask_overlay = np.zeros_like(img_cv) # mask_overlay[:, :, 2] = mask # 将掩码放在红色通道 (BGR中的R) # # cv2.addWeighted(img_cv, 0.6, mask_overlay, 0.4, 0, img_cv) result_img = image.cv2image(img_cv, False, False) return result_img, best_center, best_radius, method, best_radius1 def detect_circle_v2(frame): """检测图像中的靶心(优先清晰轮廓,其次黄色区域)- 返回椭圆参数版本""" global REAL_RADIUS_CM img_cv = image.image2cv(frame, False, False) best_center = best_radius = best_radius1 = method = None ellipse_params = None # 存储椭圆参数 ((x, y), (axes_major, axes_minor), angle) # HSV 黄色掩码检测(模糊靶心) hsv = cv2.cvtColor(img_cv, cv2.COLOR_RGB2HSV) h, s, v = cv2.split(hsv) # 调整饱和度策略:稍微增强,不要过度 s = np.clip(s * 1.1, 0, 255).astype(np.uint8) hsv = cv2.merge((h, s, v)) # 放宽 HSV 阈值范围(针对模糊图像的关键调整) lower_yellow = np.array([7, 80, 0]) # 饱和度下限降低,捕捉淡黄色 upper_yellow = np.array([32, 255, 255]) # 亮度上限拉满 mask = cv2.inRange(hsv, lower_yellow, upper_yellow) # 调整形态学操作 # 使用 MORPH_CLOSE (先膨胀后腐蚀) 来填充内部小黑洞,连接近邻区域 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if contours: largest = max(contours, key=cv2.contourArea) if cv2.contourArea(largest) > 50: # 尝试拟合椭圆 (需要轮廓点至少为5个) if len(largest) >= 5: # 返回值: ((中心x, 中心y), (width, height), 旋转角度) # 注意:width 和 height 是外接矩形的尺寸,不是长轴和短轴 (x, y), (width, height), angle = cv2.fitEllipse(largest) # 保存椭圆参数(保持原始顺序,用于绘制) ellipse_params = ((x, y), (width, height), angle) # 计算半径:使用较小的尺寸作为短轴 axes_minor = min(width, height) radius = axes_minor / 2 best_center = (int(x), int(y)) best_radius = int(radius) method = "v2_ellipse" else: # 如果点太少无法拟合椭圆,降级回 minEnclosingCircle (x, y), radius = cv2.minEnclosingCircle(largest) best_center = (int(x), int(y)) best_radius = int(radius) method = "v2" ellipse_params = None # 圆形,没有椭圆参数 best_radius1 = radius * 5 result_img = image.cv2image(img_cv, False, False) return result_img, best_center, best_radius, method, best_radius1, ellipse_params # ==================== 测试逻辑 ==================== def run_offline_test(image_path): """读取图片,检测圆,绘制结果,保存图片""" # 1. 检查文件是否存在 if not os.path.exists(image_path): print(f"[ERROR] 找不到图片文件: {image_path}") return # 2. 使用 maix.image 读取图片 (适配 MaixPy v4) try: # 使用 image.load 读取文件,返回 Image 对象 img = image.load(image_path) print(f"[INFO] 成功读取图片: {image_path} (尺寸: {img.width()}x{img.height()})") except Exception as e: print(f"[ERROR] 读取图片失败: {e}") print("提示:请确认 MaixPy 版本是否为 v4,且图片路径正确。") return # 3. 调用 detect_circle_v2 函数 print("[INFO] 正在调用 detect_circle_v2 进行检测...") start_time = time.ticks_ms() result_img, center, radius, method, radius1, ellipse_params = detect_circle_v3(img) cost_time = time.ticks_ms() - start_time print(f"[INFO] 检测完成,耗时: {cost_time}ms") print(f" 结果 -> 圆心: {center}, 半径: {radius}, 方法: {method}") if ellipse_params: (ell_center, (width, height), angle) = ellipse_params print(f" 椭圆 -> 中心: ({ell_center[0]:.1f}, {ell_center[1]:.1f}), 长轴: {max(width, height):.1f}, 短轴: {min(width, height):.1f}, 角度: {angle:.1f}°") # 4. 绘制辅助线(可选,用于调试) if center and radius: # 为了绘制椭圆,需要转换回 cv2 图像 img_cv = image.image2cv(result_img, False, False) cx, cy = center # 如果有椭圆参数,绘制椭圆 if ellipse_params: (ell_center, (width, height), angle) = ellipse_params cx_ell, cy_ell = int(ell_center[0]), int(ell_center[1]) # 确定长轴和短轴 if width >= height: # width 是长轴,height 是短轴 axes_major = width axes_minor = height major_angle = angle # 长轴角度就是 angle minor_angle = angle + 90 # 短轴角度 = 长轴角度 + 90度 else: # height 是长轴,width 是短轴 axes_major = height axes_minor = width major_angle = angle + 90 # 长轴角度 = width角度 + 90度 minor_angle = angle # 短轴角度就是 angle # 使用 OpenCV 绘制椭圆(绿色,线宽2) cv2.ellipse(img_cv, (cx_ell, cy_ell), # 中心点 (int(width/2), int(height/2)), # 半宽、半高 angle, # 旋转角度(OpenCV需要原始angle) 0, 360, # 起始和结束角度 (0, 255, 0), # 绿色 (RGB格式) 2) # 线宽 # 绘制椭圆中心点(红色) cv2.circle(img_cv, (cx_ell, cy_ell), 3, (255, 0, 0), -1) import math # 绘制短轴(蓝色线条) minor_length = axes_minor / 2 minor_angle_rad = math.radians(minor_angle) dx_minor = minor_length * math.cos(minor_angle_rad) dy_minor = minor_length * math.sin(minor_angle_rad) pt1_minor = (int(cx_ell - dx_minor), int(cy_ell - dy_minor)) pt2_minor = (int(cx_ell + dx_minor), int(cy_ell + dy_minor)) cv2.line(img_cv, pt1_minor, pt2_minor, (0, 0, 255), 2) # 蓝色 (RGB格式) else: # 如果没有椭圆参数,绘制圆形(红色) cv2.circle(img_cv, (cx, cy), radius, (0, 0, 255), 2) cv2.circle(img_cv, (cx, cy), 2, (0, 0, 255), -1) # 转换回 maix image result_img = image.cv2image(img_cv, False, False) # 定义颜色对象用于文字 try: color_black = image.Color.from_rgb(0,0,0) except AttributeError: color_black = image.Color(0,0,0) # D. 添加文字信息 FOCAL_LENGTH_PIX = 1900 d = (REAL_RADIUS_CM * FOCAL_LENGTH_PIX) / radius1 / 100.0 info_str = f"R:{radius} M:{method} D:{d:.2f}" print(info_str) # 计算文字位置,防止超出图片边界 r_outer = int(radius * 11.0) if radius else 100 text_y = cy - r_outer - 20 if cy > r_outer + 20 else cy + r_outer + 20 # 调用 draw_string result_img.draw_string(0, 0, info_str, color=color_black, scale=1.0) # 5. 保存结果图片 output_path = image_path.replace(".bmp", "_result.bmp") output_path = image_path.replace(".jpg", "_result.jpg") try: result_img.save(output_path, quality=100) print(f"[SUCCESS] 结果已保存至: {output_path}") except Exception as e: print(f"[ERROR] 保存图片失败: {e}") if __name__ == "__main__": # ================= 配置区域 ================= # 1. 设置要测试的图片路径 # 建议将图片放在与脚本同级目录,或者使用绝对路径 TARGET_IMAGE = "/root/phot/None_314_258_0_0041.bmp" # TARGET_DIR = "/root/phot_test2" # 修改为你想要读取的目录路径 # 支持的图片格式 IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp'] # ================= 执行区域 ================= if 'TARGET_DIR' in locals(): # 读取目录下所有图片文件,过滤掉 _result.jpg 后缀的文件 image_files = [] if os.path.exists(TARGET_DIR) and os.path.isdir(TARGET_DIR): for filename in os.listdir(TARGET_DIR): # 检查文件扩展名 if any(filename.lower().endswith(ext) for ext in IMAGE_EXTENSIONS): # 过滤掉 _result.jpg 后缀的文件 if not filename.endswith('_result.jpg'): filepath = os.path.join(TARGET_DIR, filename) if os.path.isfile(filepath): image_files.append(filepath) # 按文件名排序(可选) image_files.sort() print(f"[INFO] 在目录 {TARGET_DIR} 中找到 {len(image_files)} 张图片") # 处理每张图片 for img_path in image_files: print(f"\n{'='*10} 开始处理: {img_path} {'='*10}") run_offline_test(img_path) else: print(f"[ERROR] 目录不存在或不是有效目录: {TARGET_DIR}") else: run_offline_test(TARGET_IMAGE)