diff --git a/base_move/center_on_dual_tracks.py b/base_move/center_on_dual_tracks.py index f2f15d7..4972f42 100644 --- a/base_move/center_on_dual_tracks.py +++ b/base_move/center_on_dual_tracks.py @@ -5,10 +5,11 @@ import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from utils.log_helper import LogHelper, get_logger, section, info, debug, warning, error, success, timing -from utils.detect_dual_track_lines import detect_dual_track_lines, auto_detect_dual_track_lines +from utils.detect_dual_track_lines import detect_dual_track_lines def center_on_dual_tracks(ctrl, msg, max_time=15, max_deviation=8.0, observe=False, - mode=11, gait_id=26, step_height=[0.06, 0.06]): + mode=11, gait_id=26, step_height=[0.06, 0.06], + estimated_track_distance=1.0): """ 控制机器狗仅使用Y轴移动调整到双轨道线的中间位置 @@ -21,6 +22,7 @@ def center_on_dual_tracks(ctrl, msg, max_time=15, max_deviation=8.0, observe=Fal mode: 控制模式,默认为11 gait_id: 步态ID,默认为26 step_height: 抬腿高度,默认为[0.06, 0.06] + estimated_track_distance: 估计的机器人到轨道的距离(米),用于计算实际偏差,默认为1.0米 返回: bool: 是否成功调整到中心位置 @@ -44,12 +46,14 @@ def center_on_dual_tracks(ctrl, msg, max_time=15, max_deviation=8.0, observe=Fal if hasattr(ctrl, 'place_marker'): ctrl.place_marker(start_position[0], start_position[1], start_position[2] if len(start_position) > 2 else 0.0, 'green', observe=True) - # PID控制参数 - 增加比例系数提高响应速度 - kp = 0.005 # 比例系数增加 + # 根据RGB相机参数,计算像素偏差到实际距离的转换 + # 相机水平视场角FOV = 1.46608弧度(约84度) + # 图像宽度 = 1920像素 + camera_hfov = 1.46608 # 水平视场角(弧度) + image_width = 1920 # 图像宽度(像素) - # 帧间滤波参数 - 减少滤波长度以提高响应性 - filter_size = 3 # 滤波队列大小 - deviation_queue = [] # 偏差值队列 + # 计算像素到角度的转换比例 + pixels_per_radian = image_width / camera_hfov # 统计变量 detection_success_count = 0 @@ -57,7 +61,7 @@ def center_on_dual_tracks(ctrl, msg, max_time=15, max_deviation=8.0, observe=Fal # 稳定计数器 - 连续几次在中心位置才认为稳定 stable_count = 0 - required_stable_count = 2 # 降低稳定条件,更快响应 + required_stable_count = 2 # 变量记录最后有效的偏差,用于处理检测失败的情况 last_valid_deviation = 0 @@ -67,7 +71,7 @@ def center_on_dual_tracks(ctrl, msg, max_time=15, max_deviation=8.0, observe=Fal # 获取当前图像 image = ctrl.image_processor.get_current_image() - # 检测双轨道线,尝试使用自动检测方法,可能更稳定 + # 检测双轨道线 detection_total_count += 1 center_info, left_info, right_info = detect_dual_track_lines(image, observe=observe, save_log=True) @@ -79,68 +83,51 @@ def center_on_dual_tracks(ctrl, msg, max_time=15, max_deviation=8.0, observe=Fal current_deviation = center_info["deviation"] last_valid_deviation = current_deviation # 记录有效偏差 - # 添加到队列 - deviation_queue.append(current_deviation) - if len(deviation_queue) > filter_size: - deviation_queue.pop(0) - - # 计算滤波后的偏差值 (去除最大和最小值后的平均) - if len(deviation_queue) >= 3: - filtered_deviations = sorted(deviation_queue)[1:-1] if len(deviation_queue) > 2 else deviation_queue - filtered_deviation = sum(filtered_deviations) / len(filtered_deviations) - else: - filtered_deviation = current_deviation + # 转换像素偏差到实际距离偏差 + # 1. 像素偏差转换为弧度 + deviation_rad = current_deviation / pixels_per_radian + # 2. 根据估计距离计算实际偏差(单位:米) + actual_deviation = math.tan(deviation_rad) * estimated_track_distance if observe: - debug(f"原始偏差: {current_deviation:.1f}px, 滤波后: {filtered_deviation:.1f}px", "偏差") + debug(f"偏差: {current_deviation:.1f}px", "偏差") + debug(f"实际距离偏差: {actual_deviation:.3f}m (距离估计: {estimated_track_distance:.1f}m)", "实际偏差") # 判断是否已经居中 - if abs(filtered_deviation) <= max_deviation: + if abs(current_deviation) <= max_deviation: stable_count += 1 if observe: info(f"已接近中心,稳定计数: {stable_count}/{required_stable_count}", "居中") + info(f"距离偏差: {actual_deviation:.3f}m", "居中") # 即使在稳定过程中,也要保持小幅调整以保持居中 if stable_count < required_stable_count: - # 使用非常小的调整速度 - lateral_velocity = kp * filtered_deviation * 0.5 - max_lateral_velocity = 0.1 # 减小稳定阶段的最大速度 - lateral_velocity = max(-max_lateral_velocity, min(max_lateral_velocity, lateral_velocity)) - - msg.vel_des = [0, lateral_velocity, 0] + # 直接根据实际偏差移动 + msg.vel_des = [0, actual_deviation, 0] msg.life_count += 1 ctrl.Send_cmd(msg) if stable_count >= required_stable_count: # 已经稳定在中心位置 if observe: - success(f"成功居中,最终偏差: {filtered_deviation:.1f}px", "完成") + success(f"成功居中,最终偏差: {current_deviation:.1f}px ({actual_deviation:.3f}m)", "完成") break else: # 不在中心,重置稳定计数 stable_count = 0 - # 计算横向移动速度 (只使用y轴移动) - # 注意:偏差为正表示需要向左移动(y轴正方向),偏差为负表示需要向右移动(y轴负方向) - - # 使用动态kp:偏差越大,移动越快;偏差越小,移动越慢 - dynamic_kp = kp - if abs(filtered_deviation) > 50: - dynamic_kp = kp * 1.5 # 大偏差时增加响应 - elif abs(filtered_deviation) < 20: - dynamic_kp = kp * 0.8 # 小偏差时减小响应,避免过冲 - - lateral_velocity = dynamic_kp * filtered_deviation - - # 限制横向移动速度 - max_lateral_velocity = 0.35 # 增加最大横向速度 (米/秒) - lateral_velocity = max(-max_lateral_velocity, min(max_lateral_velocity, lateral_velocity)) + # 直接根据计算出的实际偏差距离进行移动 + # 为了避免移动过猛,可以限制最大移动距离 + max_move_distance = 0.35 # 最大移动距离(米) + move_distance = actual_deviation + if abs(move_distance) > max_move_distance: + move_distance = max_move_distance * (1 if move_distance > 0 else -1) if observe: - debug(f"横向移动速度: {lateral_velocity:.3f}m/s", "控制") + debug(f"横向移动距离: {move_distance:.3f}m", "控制") # 设置速度命令 - 只使用y轴移动,不前进和转向 - msg.vel_des = [0, lateral_velocity, 0] # [前进速度, 侧向速度, 角速度] + msg.vel_des = [0, move_distance, 0] # [前进速度, 侧向速度, 角速度] # 发送命令 msg.life_count += 1 @@ -149,27 +136,30 @@ def center_on_dual_tracks(ctrl, msg, max_time=15, max_deviation=8.0, observe=Fal warning("未检测到双轨道线", "警告") # 如果已经有了一些有效的检测,使用最后一次有效的偏差继续移动 - if len(deviation_queue) > 0: - # 使用衰减的最后偏差值继续移动 - lateral_velocity = kp * last_valid_deviation * 0.7 # 衰减系数,降低未检测到时的移动速度 - max_lateral_velocity = 0.2 # 降低未检测情况下的移动速度 - lateral_velocity = max(-max_lateral_velocity, min(max_lateral_velocity, lateral_velocity)) + if detection_success_count > 0: + # 使用最后一次有效偏差值计算移动距离 + deviation_rad = last_valid_deviation / pixels_per_radian + move_distance = math.tan(deviation_rad) * estimated_track_distance * 0.5 # 缩小系数,避免过度移动 - msg.vel_des = [0, lateral_velocity, 0] + # 限制最大移动距离 + max_move_distance = 0.15 + move_distance = max(-max_move_distance, min(max_move_distance, move_distance)) + + msg.vel_des = [0, move_distance, 0] msg.life_count += 1 ctrl.Send_cmd(msg) if observe: - warning(f"使用最后偏差值继续移动: {lateral_velocity:.3f}m/s", "暂停") + warning(f"使用最后偏差值继续移动: {move_distance:.3f}m", "暂停") else: # 如果一开始就没有检测到,可以尝试小范围搜索 if detection_total_count < 10: if detection_total_count % 2 == 0: - # 向右搜索,增加搜索速度 - msg.vel_des = [0, -0.15, 0] + # 向右搜索 + msg.vel_des = [0, -0.08, 0] else: - # 向左搜索,增加搜索速度 - msg.vel_des = [0, 0.15, 0] + # 向左搜索 + msg.vel_des = [0, 0.08, 0] msg.life_count += 1 ctrl.Send_cmd(msg) @@ -220,9 +210,8 @@ def center_on_dual_tracks(ctrl, msg, max_time=15, max_deviation=8.0, observe=Fal success = True return success - def center_and_follow_dual_tracks(ctrl, msg, distance, speed=0.5, max_centering_time=15, observe=False, - mode=11, gait_id=26, step_height=[0.06, 0.06]): + mode=11, gait_id=26, step_height=[0.06, 0.06], estimated_track_distance=1.0): """ 先居中到双轨道线中间,然后沿轨道线行走指定距离 @@ -236,6 +225,7 @@ def center_and_follow_dual_tracks(ctrl, msg, distance, speed=0.5, max_centering_ mode: 控制模式,默认为11 gait_id: 步态ID,默认为26 step_height: 抬腿高度,默认为[0.06, 0.06] + estimated_track_distance: 估计的机器人到轨道的距离(米),用于计算实际偏差,默认为1.0米 返回: bool: 是否成功完成居中和轨道跟随 @@ -253,6 +243,7 @@ def center_and_follow_dual_tracks(ctrl, msg, distance, speed=0.5, max_centering_ mode=mode, gait_id=gait_id, step_height=step_height, + estimated_track_distance=estimated_track_distance ) if not centering_success: diff --git a/utils/detect_dual_track_lines.py b/utils/detect_dual_track_lines.py index 31ee819..669fe92 100644 --- a/utils/detect_dual_track_lines.py +++ b/utils/detect_dual_track_lines.py @@ -685,7 +685,7 @@ def detect_dual_track_lines(image, observe=False, delay=1000, save_log=True, center_point = (int(bottom_x), int(height)) # 计算中心线与图像中心线的偏差 - deviation = bottom_x - center_x + deviation = width / 2 - bottom_x result_img = None if observe or save_log: @@ -847,895 +847,3 @@ def detect_dual_track_lines(image, observe=False, delay=1000, save_log=True, } return center_info, left_track_info, right_track_info - -def detect_center_based_dual_track_lines(image, observe=False, delay=1000, save_log=True, expected_track_width_ratio=0.45): - """ - 通过先检测中心线,然后向两侧扩展来检测双轨迹线 - - 参数: - image: 输入图像,可以是文件路径或者已加载的图像数组 - observe: 是否输出中间状态信息和可视化结果,默认为False - delay: 展示每个步骤的等待时间(毫秒) - save_log: 是否保存日志和图像 - expected_track_width_ratio: 预期轨道宽度占图像宽度的比例,默认为0.45 - - 返回: - tuple: (中心线信息, 左轨迹线信息, 右轨迹线信息) - 如果检测失败返回(None, None, None) - """ - # 如果输入是字符串(文件路径),则加载图像 - if isinstance(image, str): - img = cv2.imread(image) - else: - img = image.copy() - - if img is None: - error("无法加载图像", "失败") - return None, None, None - - # 获取图像尺寸 - height, width = img.shape[:2] - - # 计算图像中间区域的范围 - center_x = width // 2 - - # 转换到HSV颜色空间以便更容易提取黄色 - hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) - - # 标准黄色的HSV范围 - lower_yellow = np.array([15, 80, 80]) - upper_yellow = np.array([35, 255, 255]) - - # 创建黄色的掩码 - mask = cv2.inRange(hsv, lower_yellow, upper_yellow) - - # 对较暗的黄色使用更宽松的阈值 - lower_dark_yellow = np.array([10, 60, 60]) - upper_dark_yellow = np.array([40, 255, 255]) - dark_mask = cv2.inRange(hsv, lower_dark_yellow, upper_dark_yellow) - - # 合并掩码 - combined_mask = cv2.bitwise_or(mask, dark_mask) - - # 形态学操作以改善掩码 - kernel = np.ones((5, 5), np.uint8) - combined_mask = cv2.dilate(combined_mask, kernel, iterations=1) - combined_mask = cv2.erode(combined_mask, np.ones((3, 3), np.uint8), iterations=1) - - if observe: - debug("步骤1: 创建黄色掩码", "处理") - mask_display = cv2.cvtColor(combined_mask, cv2.COLOR_GRAY2BGR) - cv2.putText(mask_display, "Step 1: Yellow Mask (Center Based)", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) - cv2.putText(mask_display, f"Lower: {lower_yellow} + Darker Var.", (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 0), 1) - cv2.putText(mask_display, f"Upper: {upper_yellow} + Darker Var.", (10, 55), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 0), 1) - cv2.imshow("黄色掩码", mask_display) - cv2.waitKey(delay) - - # 裁剪底部区域重点关注近处的黄线 - bottom_roi_height = int(height * 0.6) # 关注图像底部60% - bottom_roi = combined_mask[height-bottom_roi_height:, :] - - if observe: - debug("步骤1.5: 底部区域掩码", "处理") - bottom_roi_display = cv2.cvtColor(bottom_roi, cv2.COLOR_GRAY2BGR) - cv2.putText(bottom_roi_display, "Step 1.5: Bottom ROI (Center Based)", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) - cv2.putText(bottom_roi_display, f"ROI Height: {bottom_roi_height}px ({bottom_roi_height/height*100:.0f}%)", (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 0), 1) - cv2.imshow("底部区域掩码", bottom_roi_display) - cv2.waitKey(delay) - - # 边缘检测 - edges_roi = cv2.Canny(bottom_roi, 50, 150, apertureSize=3) - - if observe: - debug("步骤2: 边缘检测 (底部ROI)", "处理") # Updated text - # Displaying edges from bottom_roi directly - edges_display = cv2.cvtColor(edges_roi, cv2.COLOR_GRAY2BGR) - cv2.putText(edges_display, "Step 2: Edge Detection (Canny on Bottom ROI)", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) - cv2.putText(edges_display, "Thresholds: (50, 150)", (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 0), 1) - cv2.imshow("边缘检测 (底部ROI)", edges_display) # Updated window title - cv2.waitKey(delay) - - # INFO 霍夫变换检测直线 (on edges from bottom_roi) - lines = cv2.HoughLinesP(edges_roi, 1, np.pi/180, threshold=25, - minLineLength=width*min_line_length, maxLineGap=max_line_gap) - - # Adjust y-coordinates of lines detected in bottom_roi to be relative to the full image - if lines is not None: - offset_y = height - bottom_roi_height - for i in range(len(lines)): - # line[0] is [x1, y1, x2, y2] - lines[i][0][1] += offset_y # y1 - lines[i][0][3] += offset_y # y2 - - if lines is None or len(lines) == 0: - error("未检测到直线 (在底部ROI)", "失败") # Updated error message - return None, None, None - - if observe: - debug(f"步骤3: 检测到 {len(lines)} 条直线", "处理") - lines_img = img.copy() - cv2.putText(lines_img, "Step 3: Hough Lines", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) - cv2.putText(lines_img, f"Th:25, MinLen:{width*0.05:.1f}, MaxGap:{max_line_gap}", (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 255), 1) - for line in lines: - x1, y1, x2, y2 = line[0] - cv2.line(lines_img, (x1, y1), (x2, y2), (0, 255, 0), 2) - cv2.imshow("检测到的直线", lines_img) - cv2.waitKey(delay) - - # 筛选近似垂直的线 - vertical_only_lines = [] - for line in lines: - x1, y1, x2, y2 = line[0] - - # 优先选择图像底部的线 - if y1 < height * 0.4 and y2 < height * 0.4: - continue # 忽略上半部分的线 - - # 计算斜率 (避免除零错误) - if abs(x2 - x1) < 5: # 几乎垂直的线 - slope = 100 # 设置一个较大的值表示接近垂直 - else: - slope = (y2 - y1) / (x2 - x1) - - # 筛选接近垂直的线 (斜率较大),但允许更多倾斜度 - if abs(slope) > min_slope_threshold: - vertical_only_lines.append(line[0]) - - if observe: - debug(f"步骤3.2: 筛选出 {len(vertical_only_lines)} 条垂直候选线 (合并前)", "可视化") - pre_merge_lines_img = img.copy() - cv2.putText(pre_merge_lines_img, "Step 3.2: Vertical Candidates (Pre-Merge)", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) - colors = [(0, 0, 255), (0, 255, 0), (0, 255, 255), (255, 0, 0), (255, 0, 255), (255, 255, 0)] - for i, line in enumerate(vertical_only_lines): - x1, y1, x2, y2 = line - cv2.line(pre_merge_lines_img, (x1, y1), (x2, y2), colors[i % len(colors)], 2) # 使用红色显示合并前的线 - cv2.imshow("合并前的垂直候选线", pre_merge_lines_img) - cv2.waitKey(delay) - - if observe: - vertical_only_lines_tmp = vertical_only_lines.copy() - - vertical_only_lines = _merge_collinear_lines_iterative(vertical_only_lines, - min_initial_len=5.0, # 最小初始线段长度 - max_angle_diff_deg=4.0, # 最大允许角度差(度) - max_ep_gap_abs=max_line_gap, # 端点间最大绝对距离 - max_ep_gap_factor=1, # 端点间最大相对距离因子 - max_p_dist_abs=max_line_gap, # 点到线段最大绝对距离 - max_p_dist_factor=1) # 点到线段最大相对距离因子 - if observe: - print(f"合并前: {len(vertical_only_lines_tmp)} 条线, 合并后: {len(vertical_only_lines)} 条线") - debug(f"步骤3.5: 合并筛选出 {len(vertical_only_lines)} 条垂直候选线 (合并后)", "可视化") - - # 创建两个图像用于对比显示 - pre_merge_img = img.copy() - post_merge_img = img.copy() - - # 在两张图上添加标题 - cv2.putText(pre_merge_img, "合并前的垂直候选线", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) - cv2.putText(post_merge_img, "合并后的垂直候选线", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) - - # 为每条线分配不同的颜色 - colors = [ - (255, 0, 0), # 蓝色 - (0, 255, 0), # 绿色 - (0, 0, 255), # 红色 - (255, 255, 0), # 青色 - (255, 0, 255), # 品红 - (0, 255, 255), # 黄色 - ] - - # 绘制合并前的线 - for i, line in enumerate(vertical_only_lines_tmp): - x1, y1, x2, y2 = line - color = colors[i % len(colors)] - cv2.line(pre_merge_img, (x1, y1), (x2, y2), color, 2) - mid_x = (x1 + x2) // 2 - mid_y = (y1 + y2) // 2 - cv2.putText(pre_merge_img, str(i+1), (mid_x, mid_y), - cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) - - # 绘制合并后的线 - for i, line in enumerate(vertical_only_lines): - x1, y1, x2, y2 = line[0] - color = colors[i % len(colors)] - cv2.line(post_merge_img, (x1, y1), (x2, y2), color, 2) - mid_x = (x1 + x2) // 2 - mid_y = (y1 + y2) // 2 - cv2.putText(post_merge_img, str(i+1), (mid_x, mid_y), - cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) - - # 水平拼接两张图片 - combined_img = np.hstack((pre_merge_img, post_merge_img)) - cv2.imshow("合并前后的垂直候选线对比", combined_img) - cv2.waitKey(delay) - - vertical_lines = [] - for line in vertical_only_lines: - x1, y1, x2, y2 = line[0] - - # 优先选择图像底部的线 - if y1 < height * 0.4 and y2 < height * 0.4: - continue # 忽略上半部分的线 - - # 计算斜率 (避免除零错误) - if abs(x2 - x1) < 5: # 几乎垂直的线 - slope = 100 # 设置一个较大的值表示接近垂直 - else: - slope = (y2 - y1) / (x2 - x1) - - # 筛选接近垂直的线 (斜率较大),但允许更多倾斜度 - if abs(slope) > min_slope_threshold: - line_length = np.sqrt((x2-x1)**2 + (y2-y1)**2) - # 计算线的中点x坐标 - mid_x = (x1 + x2) / 2 - # 计算线的中点y坐标 - mid_y = (y1 + y2) / 2 - # 保存线段、其坐标、斜率和长度 - vertical_lines.append((line[0], mid_x, mid_y, slope, line_length)) - - if len(vertical_lines) < 2: - if len(vertical_lines) < 2: - error("未检测到足够的垂直线", "失败") - return None, None, None - - if observe: - debug(f"步骤4: 找到 {len(vertical_lines)} 条垂直线", "处理") - v_lines_img = img.copy() - cv2.putText(v_lines_img, "Step 4: Vertical Lines Filtered", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) - cv2.putText(v_lines_img, f"Min Slope Abs: {min_slope_threshold:.2f}", (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 255), 1) - for line_info in vertical_lines: - line, _, _, slope, _ = line_info - x1, y1, x2, y2 = line - cv2.line(v_lines_img, (x1, y1), (x2, y2), (0, 255, 255), 2) - # 显示斜率 - cv2.putText(v_lines_img, f"{slope:.2f}", ((x1+x2)//2, (y1+y2)//2), - cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1) - cv2.imshow("垂直线", v_lines_img) - cv2.waitKey(delay) - - # 按x坐标将线分为左右两组 - left_lines = [line for line in vertical_lines if line[1] < center_x] - right_lines = [line for line in vertical_lines if line[1] > center_x] - - # 如果任一侧没有检测到线,则放宽左右两侧线的分组条件 - if not left_lines or not right_lines: - # 按x坐标排序所有垂直线 - vertical_lines.sort(key=lambda x: x[1]) - - # 如果有至少两条线,将最左侧的线作为左轨迹线,最右侧的线作为右轨迹线 - if len(vertical_lines) >= 2: - left_lines = [vertical_lines[0]] - right_lines = [vertical_lines[-1]] - else: - error("左侧或右侧未检测到轨迹线", "失败") - return None, None, None - - if observe: - debug(f"左侧候选线数量: {len(left_lines)}, 右侧候选线数量: {len(right_lines)}", "线候选") - - # 创建左右线可视化图像 - left_right_img = img.copy() - cv2.putText(left_right_img, "Left and Right Candidate Lines", (10, 20), - cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) - - # 绘制左侧候选线(蓝色) - for line_info in left_lines: - line = line_info[0] - x1, y1, x2, y2 = line - cv2.line(left_right_img, (x1, y1), (x2, y2), (255, 0, 0), 2) - # 显示斜率 - mid_x = (x1 + x2) // 2 - mid_y = (y1 + y2) // 2 - cv2.putText(left_right_img, f"L:{line_info[3]:.2f}", (mid_x, mid_y), - cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1) - - # 绘制右侧候选线(红色) - for line_info in right_lines: - line = line_info[0] - x1, y1, x2, y2 = line - cv2.line(left_right_img, (x1, y1), (x2, y2), (0, 0, 255), 2) - # 显示斜率 - mid_x = (x1 + x2) // 2 - mid_y = (y1 + y2) // 2 - cv2.putText(left_right_img, f"R:{line_info[3]:.2f}", (mid_x, mid_y), - cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) - - cv2.imshow("左右候选线", left_right_img) - cv2.waitKey(delay) - - # 优化说明:在默认模式下,评分函数和线对选择都优先考虑更靠近图像中心的线段 - # 这有助于减少对图像边缘可能出现的干扰线的选择,提高轨道线检测的准确性 - - # INFO 改进的评分函数 - 同时考虑斜率、位置、长度和在图像中的位置 - def score_line(line_info, is_left): - _, mid_x, mid_y, slope, length = line_info - line_points = line_info[0] # 获取线段的端点坐标 - x1, y1, x2, y2 = line_points - - # 确保线段从上到下排序 - if y1 > y2: - x1, x2 = x2, x1 - y1, y2 = y2, y1 - - # 线段靠近底部评分 - y越大(越靠近底部)分数越高 - bottom_ratio = mid_y / height - y_score = min(1.0, bottom_ratio * 1.2) # 适当提高底部线段的权重 - - # 线段长度评分 - 线越长分数越高 - length_ratio = length / (height * 0.3) - length_score = min(1.0, length_ratio * 1.2) - - # 计算到图像中心的距离得分 - 更重视靠近垂直中线的线 - center_x = width / 2 - distance_to_center = abs(mid_x - center_x) - # 使用更陡峭的衰减函数,使得距离中心越近的线得分越高 - center_proximity_score = max(0, 1.0 - (distance_to_center / (width * 0.25)) ** 2) - - # 计算底部点与中心的偏差 - bottom_x = x1 - if abs(y2 - y1) > 1: # 非水平线 - t = (height - y1) / (y2 - y1) - if t >= 0: # 确保是向下延伸 - bottom_x = x1 + t * (x2 - x1) - - bottom_x_distance = abs(bottom_x - center_x) - bottom_x_score = max(0, 1.0 - (bottom_x_distance / (width * 0.25)) ** 2) - - # 斜率评分 - 轨道线应该有一定的倾斜度 - if abs(slope) > 5: - slope_score = 0.3 - else: - ideal_slope_magnitude = 0.8 - ideal_slope = -ideal_slope_magnitude if is_left else ideal_slope_magnitude - - if (is_left and slope > 0) or (not is_left and slope < 0): - slope_sign_score = 0.3 - else: - slope_sign_score = 1.0 - - slope_diff = abs(slope - ideal_slope) - slope_magnitude_score = max(0, 1.0 - slope_diff / 2.0) - slope_score = slope_sign_score * slope_magnitude_score - - # 检查线段是否从底部区域开始 - bottom_region_threshold = height * 0.7 - reaches_bottom = max(y1, y2) > bottom_region_threshold - bottom_reach_score = 1.0 if reaches_bottom else 0.5 - - # 综合评分 - 大幅提高中心接近性的权重 - final_score = ( - y_score * 0.1 + # 底部接近度 - length_score * 0.05 + # 线段长度 - center_proximity_score * 0.6 + # 与中心的接近度 (权重提高) - bottom_x_score * 0.15 + # 底部点位置 - slope_score * 0.05 + # 斜率合适性 - bottom_reach_score * 0.05 # 是否到达底部 - ) - - return final_score - # 如果有多条线,评估左右线对是否平行 - if len(left_lines) > 0 and len(right_lines) > 0: - # 计算最佳的左右线对 - best_pair_score = -1 - best_left_line = None - best_right_line = None - - # 先对左右线按照评分排序,只考虑评分较高的候选线(减少计算量) - left_lines = sorted(left_lines, key=lambda line: score_line(line, True), reverse=True) - right_lines = sorted(right_lines, key=lambda line: score_line(line, False), reverse=True) - left_line = left_lines[0] if left_lines else None - right_line = right_lines[0] if right_lines else None - if observe: - debug(f"选择最佳线对,评分: {best_pair_score:.2f}", "线对") - - # 创建评分可视化图像 - score_img = img.copy() - cv2.putText(score_img, "Line Scores Visualization", (10, 20), - cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) - - # 显示左侧线的评分 - for i, line_info in enumerate(left_lines): - line = line_info[0] - x1, y1, x2, y2 = line - score = score_line(line_info, True) - cv2.line(score_img, (x1, y1), (x2, y2), (255, 0, 0), 2) - mid_x = (x1 + x2) // 2 - mid_y = (y1 + y2) // 2 - cv2.putText(score_img, f"L{i+1}:{score:.2f}", (mid_x, mid_y), - cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1) - - # 显示右侧线的评分 - for i, line_info in enumerate(right_lines): - line = line_info[0] - x1, y1, x2, y2 = line - score = score_line(line_info, False) - cv2.line(score_img, (x1, y1), (x2, y2), (0, 0, 255), 2) - mid_x = (x1 + x2) // 2 - mid_y = (y1 + y2) // 2 - cv2.putText(score_img, f"R{i+1}:{score:.2f}", (mid_x, mid_y), - cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) - - cv2.imshow("线评分可视化", score_img) - cv2.waitKey(delay) - else: - # 如果只有单侧有线,使用评分最高的线 - left_lines = sorted(left_lines, key=lambda line: score_line(line, True), reverse=True) - right_lines = sorted(right_lines, key=lambda line: score_line(line, False), reverse=True) - left_line = left_lines[0] if left_lines else None - right_line = right_lines[0] if right_lines else None - - # 确保两侧都有线 - if left_line is None or right_line is None: - error("无法确定合适的左右轨迹线对", "失败") - return None, None, None - - # 获取两条线的坐标 - left_x1, left_y1, left_x2, left_y2 = left_line[0] - right_x1, right_y1, right_x2, right_y2 = right_line[0] - - # 确保线段的顺序是从上到下 - if left_y1 > left_y2: - left_x1, left_x2 = left_x2, left_x1 - left_y1, left_y2 = left_y2, left_y1 - - if right_y1 > right_y2: - right_x1, right_x2 = right_x2, right_x1 - right_y1, right_y2 = right_y2, right_y1 - - - # 尝试延长线段到图像底部,处理被石板路部分遮挡的情况 - left_extended_y2 = height - if abs(left_x2 - left_x1) < 5: # 几乎垂直 - left_extended_x2 = left_x2 - else: - left_slope = (left_y2 - left_y1) / (left_x2 - left_x1) - left_extended_x2 = left_x1 + (left_extended_y2 - left_y1) / left_slope - - right_extended_y2 = height - if abs(right_x2 - right_x1) < 5: # 几乎垂直 - right_extended_x2 = right_x2 - else: - right_slope = (right_y2 - right_y1) / (right_x2 - right_x1) - right_extended_x2 = right_x1 + (right_extended_y2 - right_y1) / right_slope - - # 更新线段端点为延长后的坐标 - left_x2, left_y2 = int(left_extended_x2), left_extended_y2 - right_x2, right_y2 = int(right_extended_x2), right_extended_y2 - - # 尝试延长线段到图像底部,处理被石板路部分遮挡的情况 - left_extended_y2 = height - if abs(left_x2 - left_x1) < 5: # 几乎垂直 - left_extended_x2 = left_x2 - else: - left_slope = (left_y2 - left_y1) / (left_x2 - left_x1) - left_extended_x2 = left_x1 + (left_extended_y2 - left_y1) / left_slope - - right_extended_y2 = height - if abs(right_x2 - right_x1) < 5: # 几乎垂直 - right_extended_x2 = right_x2 - else: - right_slope = (right_y2 - right_y1) / (right_x2 - right_x1) - right_extended_x2 = right_x1 + (right_extended_y2 - right_y1) / right_slope - - # 更新线段端点为延长后的坐标 - left_x2, left_y2 = int(left_extended_x2), left_extended_y2 - right_x2, right_y2 = int(right_extended_x2), right_extended_y2 - - # 改进的中心线计算方法 - # 首先确定两条轨迹线的有效部分 - 以两条线段的y坐标重叠部分为准 - min_y = max(left_y1, right_y1) - max_y = min(left_y2, right_y2) - - # 如果两条线没有重叠的y部分,则使用整个图像范围 - if min_y >= max_y: - min_y = 0 - max_y = height - - # 计算中间路径点的方式:在多个高度上计算左右线的中点,然后拟合中心线 - num_points = 20 # 增加采样点数量以提高精度 - center_points = [] - - # 优化采样策略 - 在底部区域采样更密集 - sample_ys = [] - - # 前半部分采样点均匀分布 - first_half = np.linspace(min_y, min_y + (max_y - min_y) * 0.5, num_points // 4) - # 后半部分(更靠近底部)采样点更密集 - second_half = np.linspace(min_y + (max_y - min_y) * 0.5, max_y, num_points - num_points // 4) - - sample_ys = np.concatenate([first_half, second_half]) - - for y in sample_ys: - # 计算左侧线在当前y值处的x坐标 - if abs(left_y2 - left_y1) < 1: # 防止除零 - left_x = left_x1 - else: - t = (y - left_y1) / (left_y2 - left_y1) - left_x = left_x1 + t * (left_x2 - left_x1) - - # 计算右侧线在当前y值处的x坐标 - if abs(right_y2 - right_y1) < 1: # 防止除零 - right_x = right_x1 - else: - t = (y - right_y1) / (right_y2 - right_y1) - right_x = right_x1 + t * (right_x2 - right_x1) - - # 计算中心点 - center_x = (left_x + right_x) / 2 - center_points.append((center_x, y)) - - # 使用采样的中心点拟合中心线 - center_xs = np.array([p[0] for p in center_points]) - center_ys = np.array([p[1] for p in center_points]) - - # 使用多项式拟合改进中心线 - 选择合适的多项式阶数 - if len(center_points) >= 5: # 需要更多点来拟合更高阶多项式 - try: - # 尝试不同阶数的多项式拟合,选择最佳结果 - best_poly_coeffs = None - best_error = float('inf') - - for degree in [1, 2, 3]: # 尝试1-3阶多项式 - try: - # 使用多项式拟合 - poly_coeffs = np.polyfit(center_ys, center_xs, degree) - poly_func = np.poly1d(poly_coeffs) - - # 计算拟合误差 - predicted_xs = poly_func(center_ys) - error = np.mean(np.abs(predicted_xs - center_xs)) - - # 如果误差更小,更新最佳拟合 - if error < best_error: - best_error = error - best_poly_coeffs = poly_coeffs - except: - continue - - # 如果找到了有效的拟合,使用它 - if best_poly_coeffs is not None: - poly_coeffs = best_poly_coeffs - poly_func = np.poly1d(poly_coeffs) - - # 为了提高底部拟合精度,给予底部区域更高的权重重新拟合 - weights = np.ones_like(center_ys) - - # 计算距离底部的归一化距离 (0表示底部,1表示顶部) - normalized_distance = (max_y - center_ys) / max(1, (max_y - min_y)) - - # 设置权重,底部权重更高 - weights = 1.0 + 2.0 * (1.0 - normalized_distance) - - # 使用加权多项式拟合 - try: - weighted_poly_coeffs = np.polyfit(center_ys, center_xs, len(best_poly_coeffs) - 1, w=weights) - weighted_poly_func = np.poly1d(weighted_poly_coeffs) - - # 比较加权拟合与原始拟合 - weighted_predicted_xs = weighted_poly_func(center_ys) - weighted_error = np.mean(np.abs(weighted_predicted_xs - center_xs)) - - # 如果加权拟合更好,则使用它 - if weighted_error < best_error * 1.2: # 允许一定的误差增加,因为我们更关注底部精度 - poly_coeffs = weighted_poly_coeffs - poly_func = weighted_poly_func - except: - pass # 如果加权拟合失败,继续使用未加权的拟合 - - # 使用多项式生成中心线的起点和终点 - center_line_y1 = min_y - center_line_y2 = height # 延伸到图像底部 - - # 计算多项式在这些y值处的x坐标 - center_line_x1 = poly_func(center_line_y1) - center_line_x2 = poly_func(center_line_y2) - - # 计算中心线在图像底部的x坐标 - 用于计算偏离度 - bottom_x = poly_func(height) - - # 确保坐标在图像范围内 - bottom_x = max(0, min(width - 1, bottom_x)) - center_point = (int(bottom_x), int(height)) - - # 计算中心线的加权平均斜率 - 更关注底部区域的斜率 - if abs(center_line_y2 - center_line_y1) < 1: # 防止除零 - center_slope = 0 - else: - # 计算全局斜率 - global_slope = (center_line_x2 - center_line_x1) / (center_line_y2 - center_line_y1) - - # 计算底部区域的斜率,使用多个点来提高精度 - bottom_slopes = [] - bottom_region_start = height - height * 0.3 # 底部30%区域 - - if bottom_region_start > min_y: - # 在底部区域采样多个点计算局部斜率 - bottom_sample_count = 5 - bottom_ys = np.linspace(bottom_region_start, height, bottom_sample_count) - - for i in range(len(bottom_ys) - 1): - y1, y2 = bottom_ys[i], bottom_ys[i+1] - x1, x2 = poly_func(y1), poly_func(y2) - - # 确保坐标有效 - x1 = max(0, min(width - 1, x1)) - x2 = max(0, min(width - 1, x2)) - - local_slope = (x2 - x1) / max(0.1, (y2 - y1)) - bottom_slopes.append(local_slope) - - # 计算底部斜率的加权平均值,越靠近底部权重越高 - if bottom_slopes: - weights = np.linspace(1, 2, len(bottom_slopes)) - bottom_slope = np.average(bottom_slopes, weights=weights) - - # 加权平均,底部斜率权重更高 - center_slope = bottom_slope * 0.8 + global_slope * 0.2 - else: - center_slope = global_slope - else: - center_slope = global_slope - else: - # 如果所有多项式拟合都失败,退回到简单的线性拟合 - raise Exception("多项式拟合失败") - - except Exception as e: - warning(f"多项式拟合失败,使用简单中点计算: {e}", "拟合") - # 如果多项式拟合失败,退回到简单的中点计算方法 - center_line_x1 = (left_x1 + right_x1) / 2 - center_line_y1 = (left_y1 + right_y1) / 2 - center_line_x2 = (left_x2 + right_x2) / 2 - center_line_y2 = (left_y2 + right_y2) / 2 - - # 计算中心线的斜率 - if abs(center_line_x2 - center_line_x1) < 5: - center_slope = 100 # 几乎垂直 - else: - center_slope = (center_line_y2 - center_line_y1) / (center_line_x2 - center_line_x1) - - # 计算中心线延伸到图像底部的点 - if abs(center_slope) < 0.01: # 几乎水平 - bottom_x = center_line_x1 - else: - bottom_x = center_line_x1 + (height - center_line_y1) / center_slope - - bottom_x = max(0, min(width - 1, bottom_x)) - center_point = (int(bottom_x), int(height)) - else: - # 如果点数不足,退回到简单的中点计算方法 - center_line_x1 = (left_x1 + right_x1) / 2 - center_line_y1 = (left_y1 + right_y1) / 2 - center_line_x2 = (left_x2 + right_x2) / 2 - center_line_y2 = (left_y2 + right_y2) / 2 - - # 计算中心线的斜率 - if abs(center_line_x2 - center_line_x1) < 5: - center_slope = 100 # 几乎垂直 - else: - center_slope = (center_line_y2 - center_line_y1) / (center_line_x2 - center_line_x1) - - # 计算中心线延伸到图像底部的点 - if abs(center_slope) < 0.01: # 几乎水平 - bottom_x = center_line_x1 - else: - bottom_x = center_line_x1 + (height - center_line_y1) / center_slope - - bottom_x = max(0, min(width - 1, bottom_x)) - center_point = (int(bottom_x), int(height)) - - # 计算中心线与图像中心线的偏差 - deviation = bottom_x - center_x - - result_img = None - if observe or save_log: - result_img = img.copy() - # 绘制左右轨迹线 - try: - # 确保坐标在图像范围内并且是整数 - left_x1_safe = max(0, min(width - 1, left_x1)) - left_y1_safe = max(0, min(height - 1, left_y1)) - left_x2_safe = max(0, min(width - 1, left_x2)) - left_y2_safe = max(0, min(height - 1, left_y2)) - - cv2.line(result_img, (int(left_x1_safe), int(left_y1_safe)), - (int(left_x2_safe), int(left_y2_safe)), (255, 0, 0), 2) - except Exception as e: - warning(f"绘制左轨迹线错误: {e}", "绘图") - - try: - # 确保坐标在图像范围内并且是整数 - right_x1_safe = max(0, min(width - 1, right_x1)) - right_y1_safe = max(0, min(height - 1, right_y1)) - right_x2_safe = max(0, min(width - 1, right_x2)) - right_y2_safe = max(0, min(height - 1, right_y2)) - - cv2.line(result_img, (int(right_x1_safe), int(right_y1_safe)), - (int(right_x2_safe), int(right_y2_safe)), (0, 0, 255), 2) - except Exception as e: - warning(f"绘制右轨迹线错误: {e}", "绘图") - - # 绘制中心线 - 如果有多项式拟合,绘制拟合曲线 - if len(center_points) >= 3: - # 绘制拟合的中心曲线 - try: - curve_ys = np.linspace(min_y, height, 100) - curve_xs = poly_func(curve_ys) - - for i in range(len(curve_ys) - 1): - try: - # 确保坐标在图像范围内并且是整数 - x1 = max(0, min(width - 1, curve_xs[i])) - y1 = max(0, min(height - 1, curve_ys[i])) - x2 = max(0, min(width - 1, curve_xs[i+1])) - y2 = max(0, min(height - 1, curve_ys[i+1])) - - pt1 = (int(x1), int(y1)) - pt2 = (int(x2), int(y2)) - cv2.line(result_img, pt1, pt2, (0, 255, 0), 2) - except Exception as e: - warning(f"绘制曲线段错误: {e}", "绘图") - continue - - # 绘制采样点 - for pt in center_points: - try: - # 确保坐标在图像范围内并且是整数 - x = max(0, min(width - 1, pt[0])) - y = max(0, min(height - 1, pt[1])) - cv2.circle(result_img, (int(x), int(y)), 3, (0, 255, 255), -1) - except Exception as e: - warning(f"绘制采样点错误: {e}", "绘图") - continue - except Exception as e: - warning(f"绘制曲线错误: {e}", "绘图") - else: - # 绘制简单中心线 - try: - # 确保坐标在图像范围内并且是整数 - x1 = max(0, min(width - 1, center_line_x1)) - y1 = max(0, min(height - 1, center_line_y1)) - x2 = max(0, min(width - 1, center_line_x2)) - y2 = max(0, min(height - 1, center_line_y2)) - - cv2.line(result_img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) - except Exception as e: - warning(f"绘制中心线错误: {e}", "绘图") - - # 绘制图像中心线 - try: - center_x_safe = max(0, min(width - 1, center_x)) - cv2.line(result_img, (int(center_x_safe), 0), (int(center_x_safe), height), (0, 0, 255), 1) - except Exception as e: - warning(f"绘制图像中心线错误: {e}", "绘图") - - # 标记中心点 - try: - # 确保中心点坐标在图像范围内 - center_x_safe = max(0, min(width - 1, center_point[0])) - center_y_safe = max(0, min(height - 1, center_point[1])) - cv2.circle(result_img, (int(center_x_safe), int(center_y_safe)), 10, (255, 0, 255), -1) - except Exception as e: - warning(f"绘制中心点错误: {e}", "绘图") - - # 显示偏差信息 - cv2.putText(result_img, f"Deviation: {deviation:.1f}px", (10, 30), - cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) - cv2.putText(result_img, "Final Result", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) - if 'best_pair_score' in locals() and best_pair_score != -1: - cv2.putText(result_img, f"Pair Score: {best_pair_score:.2f}", (10, 85), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) - current_y_offset = 105 - else: - current_y_offset = 85 - - cv2.putText(result_img, f"L-Slope: {left_line[3]:.2f}, R-Slope: {right_line[3]:.2f}", (10, current_y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) - current_y_offset += 20 - cv2.putText(result_img, f"Track Width: {right_line[1] - left_line[1]:.1f}px", (10, current_y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) - - if observe: - cv2.imshow("轨迹线检测结果", result_img) - cv2.waitKey(delay) - - # 保存日志图像 - if save_log and result_img is not None: - timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f") - log_dir = "logs/image" - os.makedirs(log_dir, exist_ok=True) - - # 保存结果图像 - result_img_path = os.path.join(log_dir, f"dual_track_{timestamp}.jpg") - cv2.imwrite(result_img_path, result_img) - - # 保存原图 - orig_img_path = os.path.join(log_dir, f"dual_track_orig_{timestamp}.jpg") - cv2.imwrite(orig_img_path, img) - - info(f"保存双轨迹线检测结果图像到: {result_img_path}", "日志") - info(f"保存原始图像到: {orig_img_path}", "日志") - - # 保存文本日志信息 - log_info = { - "timestamp": timestamp, - "center_point": (int(center_point[0]), int(center_point[1])), - "deviation": float(deviation), - "left_track_mid_x": float(left_line[1]), - "right_track_mid_x": float(right_line[1]), - "track_width": float(right_line[1] - left_line[1]), - "center_slope": float(center_slope), - } - info(f"双轨迹线检测结果: {log_info}", "日志") - - # 创建左右轨迹线和中心线信息 - left_track_info = { - "line": (left_x1, left_y1, left_x2, left_y2), - "slope": left_line[3], - "x_mid": left_line[1] - } - - right_track_info = { - "line": (right_x1, right_y1, right_x2, right_y2), - "slope": right_line[3], - "x_mid": right_line[1] - } - - center_info = { - "point": (int(center_point[0]), int(center_point[1])), - "deviation": float(deviation), - "slope": float(center_slope), - "is_vertical": abs(center_slope) > 5.0, # 判断是否接近垂直 - "track_width": float(right_line[1] - left_line[1]), # 两轨迹线之间的距离 - } - - return center_info, left_track_info, right_track_info - -def auto_detect_dual_track_lines(image, observe=False, delay=1000, save_log=True, max_retries=2, use_center_based=True): - """ - 自动检测双轨迹线,使用指定方法 - - 参数: - image: 输入图像,可以是文件路径或者已加载的图像数组 - observe: 是否输出中间状态信息和可视化结果,默认为False - delay: 展示每个步骤的等待时间(毫秒) - save_log: 是否保存日志和图像 - max_retries: 最大重试次数,用于处理复杂情况 - use_center_based: 是否使用基于中心线的检测方法,默认为True - - 返回: - tuple: (中心线信息, 左轨迹线信息, 右轨迹线信息) - """ - # 根据参数选择使用的检测方法 - if use_center_based: - info("使用中心线基础检测方法", "检测") - result = detect_center_based_dual_track_lines(image, observe, delay, save_log) - else: - info("使用传统检测方法", "检测") - result = detect_dual_track_lines(image, observe, delay, save_log) - - # 检查结果是否成功 - if result[0] is not None: - info("轨迹线检测成功", "检测") - return result - - # 如果失败且还有重试次数,尝试增强图像后重新检测 - if max_retries > 0: - warning(f"检测失败,尝试调整参数重新检测 (剩余重试次数: {max_retries})", "检测") - - # 对图像进行预处理以增强黄线检测 - if isinstance(image, str): - img = cv2.imread(image) - else: - img = image.copy() - - if img is not None: - # 增强图像对比度 - lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) - l, a, b = cv2.split(lab) - clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8)) - l = clahe.apply(l) - lab = cv2.merge((l, a, b)) - enhanced_img = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR) - - # 使用增强后的图像重新尝试 - return auto_detect_dual_track_lines(enhanced_img, observe, delay, save_log, max_retries-1, use_center_based) - - error("轨迹线检测失败", "检测") - return None, None, None