From 16a7ccd1013d6730ed96a5a554c8f5694b8b5231 Mon Sep 17 00:00:00 2001 From: Havoc <2993167370@qq.com> Date: Wed, 14 May 2025 12:42:01 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20horizon=20line=20detect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 0 -> 6148 bytes README.md | 131 +- res/.DS_Store | Bin 0 -> 6148 bytes res/path/image_20250513_162556.png | Bin 0 -> 32653 bytes res/path/image_20250514_024313.png | Bin 0 -> 56923 bytes res/path/image_20250514_024347.png | Bin 0 -> 46744 bytes .../result_image_20250513_162556.png | Bin 0 -> 58629 bytes test/task-path-track/README_yellow_track.md | 113 ++ test/task-path-track/tempCodeRunnerFile.py | 1 + test/task-path-track/yellow_track_demo.py | 178 +++ test_track_detection.py | 38 + utils/detect_track.py | 1057 +++++++++++++++++ 12 files changed, 1481 insertions(+), 37 deletions(-) create mode 100644 .DS_Store create mode 100644 res/.DS_Store create mode 100644 res/path/image_20250513_162556.png create mode 100644 res/path/image_20250514_024313.png create mode 100644 res/path/image_20250514_024347.png create mode 100644 res/path/test/result_image_20250513_162556.png/result_image_20250513_162556.png create mode 100644 test/task-path-track/README_yellow_track.md create mode 100644 test/task-path-track/tempCodeRunnerFile.py create mode 100644 test/task-path-track/yellow_track_demo.py create mode 100644 test_track_detection.py create mode 100644 utils/detect_track.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..1870410e0157552b40b44f2478991a55580b303e GIT binary patch literal 6148 zcmeH~J8r{33`B>q3j=9Xy4=PG$PE^TeF9$~g@ZJP4LC^ZJ$i zuh5DBZ2x(F0VV(&x+``bW@gM+xZ@2UU#I)^a=X5>;#J@&VrHyNnC;iLL0pQvfVyTmjO&;ssLc!1UOG})p;=82 zR;?Ceh}WZ?+UmMqI#RP8R>OzYoz15hnq@nzF`-!xQ4j$Um=RcIKKc27r2jVm&svm< zfC&6E0=7P!4tu^-ovjbA=k?dB`g+i*aXG_}p8zI)6mRKa+;6_1_R^8c3Qa!(fk8n8 H{*=Hsc8d{{ literal 0 HcmV?d00001 diff --git a/README.md b/README.md index f90d146..eec9a61 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,113 @@ -# 任务模块 +# 黄色赛道检测与距离估算 -## top +这个项目提供了一套用于检测黄色赛道并估算距离的工具。基于OpenCV的计算机视觉技术,能够从图像中识别黄色赛道,计算方向角度,并估算摄像机到赛道前方的距离。 -1. “装货 / qrcode” -2. 曲线赛道 -3. 上下坡 - - 存在两种可能性 -4. 石板道 -5. 栅栏 / 栏杆 +## 功能特点 -## Task-1:出生点 - 取货 +- 黄色赛道检测与分割 +- 赛道方向和角度计算 +- 赛道距离估算 +- 结果可视化展示 +- 提供易于使用的测试工具 -![alt text](./res/readme/image.png) +## 安装依赖 -从二维码取货完,然后走到弯道入口。 +确保已安装以下依赖库: -![alt text](./res/readme/image-1.png) +```bash +pip install opencv-python numpy +``` -## Task-2:弯道任务 -过去,以及回来。两个方向都需要。 +## 使用方法 -![alt text](./res/readme/image-2.png) -![alt text](./res/readme/image-3.png) +### 基本用法 -## Task-2.5:识别 -> 根据箭头决定方向。 +```python +from utils.detect_track import detect_yellow_track, visualize_track_detection -这里采取传统cv的基于凸包计算的算法。 +# 检测赛道并估算距离 +image_path = "path/to/your/image.jpg" +distance, path_info = detect_yellow_track(image_path) -## Task-3:上下坡 -同样需要考虑两个方向。(正式计时各有一半的概率) -> 感觉两个坡度稍微有一点点区别。 +if distance is not None: + print(f"估算距离: {distance:.2f}米") + print(f"赛道角度: {path_info['track_angle']:.2f}°") + print(f"转向方向: {path_info['turn_direction']}") -![alt text](./res/readme/image-4.png) -![alt text](./res/readme/image-5.png) +# 可视化检测过程 +visualize_track_detection(image_path, save_path="result.jpg") +``` -以及反方向。 +### 使用测试脚本 -## Task-4:石板路 +项目提供了一个方便的测试脚本: -![alt text](./res/readme/image-6.png) -![alt text](./res/readme/image-7.png) +```bash +python test_track_detection.py --image path/to/your/image.jpg --observe --save result.jpg +``` -## Task-5:过栅栏 -![alt text](./res/readme/image-8.png) +参数说明: +- `--image`: 要处理的图像路径(必需) +- `--save`: 结果图像保存路径(可选) +- `--observe`: 显示处理步骤的中间结果 +- `--delay`: 中间步骤显示的延迟时间,单位为毫秒(默认500ms) -## Task-5.5:走向卸货 -从上一个赛道结束到 B 二维码。 +## 技术细节 -## Task-6:卸货 -也可能在另一边。 -这里是感觉在走过去的过程中就能判断二维码。 +### 检测原理 -![alt text](./res/readme/image-9.png) -![alt text](./res/readme/image-10.png) \ No newline at end of file +1. 将图像转换为HSV颜色空间 +2. 使用颜色阈值提取黄色区域 +3. 寻找轮廓并合并相关区域 +4. 计算赛道的上部和下部中心点 +5. 基于中心点计算方向角度 +6. 根据黄色区域在图像中的占比估算距离 + +### 距离估算 + +距离估算基于以下假设: +- 黄色赛道在图像中的占比与实际距离有反比关系 +- 占比越大,距离越近;占比越小,距离越远 + +注意:实际应用中,建议根据相机参数和实际测量进行标定,以获得更准确的结果。 + +### 函数说明 + +- `preprocess_image(image)`: 预处理图像,进行边缘检测 +- `detect_yellow_track(image, observe=False, delay=500)`: 检测黄色赛道并估算距离 +- `visualize_track_detection(image, save_path=None, observe=False, delay=500)`: 可视化检测过程 +- `estimate_distance_to_track(image)`: 估算到赛道的距离(简化版API) + +## 调整参数 + +如果检测效果不理想,可以尝试调整以下参数: + +1. HSV颜色范围(针对不同光照条件): +```python +lower_yellow = np.array([20, 100, 100]) # 调整黄色的下限 +upper_yellow = np.array([30, 255, 255]) # 调整黄色的上限 +``` + +2. 轮廓近似参数: +```python +epsilon = 0.01 * cv2.arcLength(all_contours, False) # 调整系数(0.01) +``` + +3. 距离估算系数: +```python +normalized_distance = min(10.0, max(0.0, estimated_distance / 100.0)) # 调整系数(100.0) +``` + +## 示例结果 + +处理后的图像将包含以下信息: +- 红色点:表示赛道的顶部和底部中心点 +- 绿色线:表示赛道的中心线 +- 文本信息:距离、角度和方向 + +## 注意事项 + +- 该方法在光照条件稳定的环境中效果最佳 +- 黄色赛道需要与背景有足够的对比度 +- 摄像机应保持相对稳定的高度和角度 +- 实际应用中需要根据具体场景调整参数 \ No newline at end of file diff --git a/res/.DS_Store b/res/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f5d0f1d605e17332f39090949d5c30a9a0171244 GIT binary patch literal 6148 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 bj1EtsCw_m#KGv8rMCJy|jaYF@w;cLE6hfx0 literal 0 HcmV?d00001 diff --git a/res/path/image_20250514_024313.png b/res/path/image_20250514_024313.png new file mode 100644 index 0000000000000000000000000000000000000000..a2cf7829bc1e53ed4c989726a1756850abf9a615 GIT binary patch literal 56923 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⪚G{HgO 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

i-Y>(APBiF38D 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 oF*hIr{J8v!`RMy`?&yrmo 0: + print(f"平均每帧处理时间: {total_time/processed_count:.3f}秒") + +def main(): + parser = argparse.ArgumentParser(description='黄色赛道检测演示程序') + parser.add_argument('--input', type=str, default='res/path/image_20250513_162556.png', help='输入图像或视频的路径') + parser.add_argument('--output', type=str, default='res/path/test/result_image_20250513_162556.png', help='输出结果的保存路径') + parser.add_argument('--type', type=str, choices=['image', 'video'], help='输入类型,不指定会自动检测') + parser.add_argument('--show', default=True, action='store_true', help='显示处理步骤') + + args = parser.parse_args() + + # 检查输入路径 + if not os.path.exists(args.input): + print(f"错误:文件 '{args.input}' 不存在") + return + + # 如果未指定类型,根据文件扩展名判断 + if args.type is None: + ext = os.path.splitext(args.input)[1].lower() + if ext in ['.jpg', '.jpeg', '.png', '.bmp']: + args.type = 'image' + elif ext in ['.mp4', '.avi', '.mov']: + args.type = 'video' + else: + print(f"错误:无法确定文件类型 '{ext}'") + return + + # 根据类型处理 + if args.type == 'image': + # 获取输出目录 + output_dir = os.path.dirname(args.output) + process_image(args.input, output_dir, args.show) + else: # video + process_video(args.input, args.output, args.show) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test_track_detection.py b/test_track_detection.py new file mode 100644 index 0000000..1681bc3 --- /dev/null +++ b/test_track_detection.py @@ -0,0 +1,38 @@ +import cv2 +import argparse +from utils.detect_track import detect_yellow_track, visualize_track_detection + +def main(): + # 创建命令行参数解析器 + parser = argparse.ArgumentParser(description='黄色赛道检测测试程序') + parser.add_argument('--image', type=str, required=True, help='要处理的图像路径') + parser.add_argument('--save', type=str, help='保存结果图像的路径(可选)') + parser.add_argument('--observe', action='store_true', help='显示中间处理步骤') + parser.add_argument('--delay', type=int, default=500, help='步骤间延迟时间(毫秒)') + + # 解析命令行参数 + args = parser.parse_args() + + # 检测赛道并估算距离 + print("正在处理图像...") + distance, path_info = detect_yellow_track(args.image, observe=args.observe, delay=args.delay) + + if distance is not None and path_info is not None: + print("\n===== 赛道分析结果 =====") + print(f"估算距离: {distance:.2f}米") + print(f"赛道角度: {path_info['track_angle']:.2f}°") + print(f"转向方向: {path_info['turn_direction']}") + print(f"赛道是否笔直: {'是' if path_info['is_straight'] else '否'}") + print(f"黄色区域占比: {path_info['area_ratio']:.4f}") + print("========================\n") + else: + print("未能检测到黄色赛道,请检查图像或调整参数") + + # 可视化检测过程 + print("开始可视化检测过程...") + visualize_track_detection(args.image, save_path=args.save, observe=args.observe, delay=args.delay) + + print("处理完成") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/utils/detect_track.py b/utils/detect_track.py new file mode 100644 index 0000000..c6ceb31 --- /dev/null +++ b/utils/detect_track.py @@ -0,0 +1,1057 @@ +import cv2 +import numpy as np +from sklearn.cluster import KMeans +from sklearn.metrics import silhouette_score + +def preprocess_image(image): + """ + 预处理图像以便进行赛道检测 + + 参数: + image: 输入图像,可以是文件路径或者已加载的图像数组 + + 返回: + edges: 处理后的边缘图像 + """ + # 如果输入是字符串(文件路径),则加载图像 + if isinstance(image, str): + img = cv2.imread(image) + else: + img = image.copy() + + if img is None: + print("无法加载图像") + return None + + # 使用提供的预处理步骤 + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + blurred = cv2.GaussianBlur(gray, (5, 5), 0) + edges = cv2.Canny(blurred, 50, 150) # 调整阈值以适应不同光照条件 + + cv2.imshow("edges", edges) + key = cv2.waitKey(0) + if key == ord('q'): # 按q键退出 + cv2.destroyAllWindows() + else: + cv2.destroyAllWindows() + + return edges + +def detect_yellow_track(image, observe=False, delay=1500): + """ + 从图像中提取黄色赛道并估算距离 + + 参数: + image: 输入图像,可以是文件路径或者已加载的图像数组 + observe: 是否输出中间状态信息和可视化结果,默认为False + delay: 展示每个步骤的等待时间(毫秒),默认为1500ms + + 返回: + distance: 估算的赛道到摄像机的距离 + path_info: 赛道路径信息字典 + """ + # 如果输入是字符串(文件路径),则加载图像 + if isinstance(image, str): + img = cv2.imread(image) + else: + img = image.copy() + + if img is None: + print("无法加载图像") + return None, None + + if observe: + print("步骤1: 原始图像已加载") + cv2.imshow("原始图像", img) + cv2.waitKey(delay) + + # 转换到HSV颜色空间以便更容易提取黄色 + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + + if observe: + print("步骤2: 转换到HSV颜色空间") + cv2.imshow("HSV图像", hsv) + cv2.waitKey(delay) + + # 黄色的HSV范围 + # 调整这些值以匹配图像中黄色的具体色调 + lower_yellow = np.array([20, 100, 100]) + upper_yellow = np.array([30, 255, 255]) + + # 创建黄色的掩码 + mask = cv2.inRange(hsv, lower_yellow, upper_yellow) + + if observe: + print("步骤3: 创建黄色掩码") + cv2.imshow("黄色掩码", mask) + cv2.waitKey(delay) + + # 应用掩码,只保留黄色部分 + yellow_only = cv2.bitwise_and(img, img, mask=mask) + + if observe: + print("步骤4: 提取黄色部分") + cv2.imshow("只保留黄色", yellow_only) + cv2.waitKey(delay) + + # 将掩码转为灰度图 + gray = mask.copy() + + # 查找轮廓 + contours, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + + # 如果没有找到轮廓,返回None + if not contours: + if observe: + print("未找到轮廓") + return None, None + + if observe: + print(f"步骤5: 找到 {len(contours)} 个轮廓") + contour_img = img.copy() + cv2.drawContours(contour_img, contours, -1, (0, 255, 0), 2) + cv2.imshow("所有轮廓", contour_img) + cv2.waitKey(delay) + + # 获取图像尺寸 + height, width = img.shape[:2] + + # 在图像上绘制三条竖线并计算与轮廓的交点 + vertical_lines = [ + width // 2, # 中线 + int(width * 2 / 5), # 2/5位置的线 + int(width * 3 / 5) # 3/5位置的线 + ] + + intersection_points = [] + vertical_line_images = [] + + if observe: + print("步骤5.1: 绘制三条竖线并计算与轮廓的交点") + + for i, x in enumerate(vertical_lines): + # 为每条竖线创建单独的图像用于可视化 + line_img = img.copy() + cv2.line(line_img, (x, 0), (x, height), (0, 0, 255), 2) + + # 记录每条线与轮廓的交点 + line_intersections = [] + + # 对于每个轮廓 + for contour in contours: + # 遍历轮廓中的所有点对 + for j in range(len(contour) - 1): + pt1 = contour[j][0] + pt2 = contour[j+1][0] + + # 检查两点是否在竖线两侧 + if (pt1[0] <= x and pt2[0] >= x) or (pt1[0] >= x and pt2[0] <= x): + # 计算交点的y坐标(线性插值) + if pt2[0] == pt1[0]: # 避免除以零 + y_intersect = pt1[1] + else: + slope = (pt2[1] - pt1[1]) / (pt2[0] - pt1[0]) + y_intersect = pt1[1] + slope * (x - pt1[0]) + + # 添加交点 + line_intersections.append((x, int(y_intersect))) + + # 在每条线上标记所有交点 + for point in line_intersections: + cv2.circle(line_img, point, 5, (255, 0, 0), -1) + + vertical_line_images.append(line_img) + + # 寻找最底部的交点(y坐标最大) + if line_intersections: + bottom_most = max(line_intersections, key=lambda p: p[1]) + intersection_points.append(bottom_most) + else: + intersection_points.append(None) + + if observe: + for i, line_img in enumerate(vertical_line_images): + cv2.imshow(f"竖线 {i+1} 与轮廓的交点", line_img) + cv2.waitKey(delay) + + # 找出底部最近的点(y坐标最大的点) + valid_intersections = [p for p in intersection_points if p is not None] + + if not valid_intersections: + if observe: + print("未找到任何竖线与轮廓的交点") + return None, None + + bottom_most_point = max(valid_intersections, key=lambda p: p[1]) + bottom_most_index = intersection_points.index(bottom_most_point) + + # 计算目标线的斜率和中线距离 + target_line_x = vertical_lines[bottom_most_index] + center_line_x = vertical_lines[0] # 中线x坐标 + + # 计算目标线到中线的距离(正值表示在右侧,负值表示在左侧) + distance_to_center = target_line_x - center_line_x + + # 计算目标线的斜率 + # 为了计算斜率,我们需要在目标线上找到两个点 + # 首先找到与目标线相同轮廓上的所有点 + target_contour_points = [] + for contour in contours: + for point in contour: + # 检查点是否接近目标线(允许一定误差) + if abs(point[0][0] - target_line_x) < 5: # 5像素的误差范围 + target_contour_points.append((point[0][0], point[0][1])) + + # 如果找到了足够的点,计算斜率 + slope = 0 # 默认斜率为0(水平线) + if len(target_contour_points) >= 2: + # 使用线性回归计算斜率 + x_coords = np.array([p[0] for p in target_contour_points]) + y_coords = np.array([p[1] for p in target_contour_points]) + + if np.std(x_coords) > 0: # 避免除以零 + slope, _ = np.polyfit(x_coords, y_coords, 1) + + if observe: + result_img = img.copy() + # 标记底部最近的点 + cv2.circle(result_img, bottom_most_point, 10, (255, 255, 0), -1) + # 绘制三条竖线 + for x in vertical_lines: + cv2.line(result_img, (x, 0), (x, height), (0, 0, 255), 2) + # 显示目标线到中线的距离和斜率 + cv2.putText(result_img, f"Distance to center: {distance_to_center}px", (10, 30), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + cv2.putText(result_img, f"Slope: {slope:.4f}", (10, 70), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + cv2.imshow("目标线分析", result_img) + cv2.waitKey(delay) + + # 更新路径信息字典,包含新的目标线信息 + path_info = { + "target_line_x": target_line_x, + "distance_to_center": distance_to_center, + "target_line_slope": slope, + "bottom_most_point": bottom_most_point + } + + # 合并所有轮廓或选择最大的轮廓 + # 在这个场景中,我们可能有多个赛道段落,所以合并它们 + all_contours = np.vstack([contours[i] for i in range(len(contours))]) + all_contours = all_contours.reshape((-1, 1, 2)) + + # 使用多边形近似轮廓 + epsilon = 0.01 * cv2.arcLength(all_contours, False) + approx = cv2.approxPolyDP(all_contours, epsilon, False) + + if observe: + print(f"步骤6: 多边形近似,顶点数: {len(approx)}") + approx_img = img.copy() + cv2.drawContours(approx_img, [approx], -1, (255, 0, 0), 2) + cv2.imshow("多边形近似", approx_img) + cv2.waitKey(delay) + + # 获取图像尺寸 + height, width = img.shape[:2] + + # 提取赛道底部的点(靠近摄像机的点) + bottom_points = [p[0] for p in approx if p[0][1] > height * 0.7] + if not bottom_points: + if observe: + print("未找到靠近摄像机的赛道点") + return None, None + + # 计算底部点的平均x坐标,作为赛道中心线 + bottom_center_x = sum(p[0] for p in bottom_points) / len(bottom_points) + + # 提取赛道顶部的点(远离摄像机的点) + top_points = [p[0] for p in approx if p[0][1] < height * 0.3] + if not top_points: + if observe: + print("未找到远离摄像机的赛道点") + return None, None + + # 计算顶部点的平均x坐标 + top_center_x = sum(p[0] for p in top_points) / len(top_points) + + # 计算赛道方向(从底部到顶部的角度) + delta_x = top_center_x - bottom_center_x + track_angle = np.arctan2(height * 0.4, delta_x) * 180 / np.pi + + # 估算距离 + # 这里使用简化的方法:基于黄色区域在图像中的占比来估算距离 + yellow_area = cv2.countNonZero(mask) + total_area = height * width + area_ratio = yellow_area / total_area + + # 假设:区域比例与距离成反比(实际应用中需要标定) + # 这里使用一个简单的比例关系,实际应用需要根据相机参数和实际测量进行标定 + estimated_distance = 1.0 / (area_ratio + 0.01) # 避免除以零 + + # 将距离标准化到一个合理的范围(例如0-10米) + normalized_distance = min(10.0, max(0.0, estimated_distance / 100.0)) + + # 创建路径信息字典 + path_info = { + "bottom_center_x": bottom_center_x, + "top_center_x": top_center_x, + "track_angle": track_angle, + "area_ratio": area_ratio, + "is_straight": abs(track_angle) < 10, # 判断赛道是否笔直 + "turn_direction": "left" if delta_x < 0 else "right" if delta_x > 0 else "straight", + "target_line_x": target_line_x, + "distance_to_center": distance_to_center, + "target_line_slope": slope, + "bottom_most_point": bottom_most_point + } + + if observe: + print(f"步骤7: 路径分析 - 角度: {track_angle:.2f}°, 距离: {normalized_distance:.2f}m, 方向: {path_info['turn_direction']}") + result_img = img.copy() + # 绘制底部中心点 + cv2.circle(result_img, (int(bottom_center_x), int(height * 0.8)), 10, (0, 0, 255), -1) + # 绘制顶部中心点 + cv2.circle(result_img, (int(top_center_x), int(height * 0.2)), 10, (0, 0, 255), -1) + # 绘制路径线 + cv2.line(result_img, (int(bottom_center_x), int(height * 0.8)), + (int(top_center_x), int(height * 0.2)), (0, 255, 0), 2) + # 添加信息文本 + cv2.putText(result_img, f"Distance: {normalized_distance:.2f}m", (10, 30), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + cv2.putText(result_img, f"Angle: {track_angle:.2f}°", (10, 70), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + cv2.putText(result_img, f"Direction: {path_info['turn_direction']}", (10, 110), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + + cv2.imshow("赛道分析结果", result_img) + cv2.waitKey(delay) + + return normalized_distance, path_info + +def visualize_track_detection(image, save_path=None, observe=False, delay=500): + """ + 可视化赛道检测过程,显示中间结果和最终分析 + + 参数: + image: 输入图像,可以是文件路径或者已加载的图像数组 + save_path: 保存结果图像的路径(可选) + observe: 是否输出中间状态信息和可视化结果,默认为False + delay: 展示每个步骤的等待时间(毫秒),默认为500ms + """ + # 如果输入是字符串(文件路径),则加载图像 + if isinstance(image, str): + img = cv2.imread(image) + else: + img = image.copy() + + if img is None: + print("无法加载图像") + return + + # 获取图像尺寸 + height, width = img.shape[:2] + + # 创建输出图像 + output = img.copy() + + # 转换到HSV颜色空间 + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + + # 黄色的HSV范围 + lower_yellow = np.array([20, 100, 100]) + upper_yellow = np.array([30, 255, 255]) + + # 创建黄色的掩码 + mask = cv2.inRange(hsv, lower_yellow, upper_yellow) + + # 应用掩码,只保留黄色部分 + yellow_only = cv2.bitwise_and(img, img, mask=mask) + + # 进行边缘检测 + edges = preprocess_image(img) + + # 组合黄色掩码和边缘检测 + combined_mask = cv2.bitwise_and(mask, edges) if edges is not None else mask + + # 查找轮廓 + contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + + # 如果找到轮廓,绘制并分析 + if contours: + # 绘制所有轮廓 + cv2.drawContours(output, contours, -1, (0, 255, 0), 2) + + # 绘制三条竖线 + vertical_lines = [ + width // 2, # 中线 + int(width * 2 / 5), # 2/5位置的线 + int(width * 3 / 5) # 3/5位置的线 + ] + + # 找出与各条竖线的交点 + intersection_points = [] + + for x in vertical_lines: + # 绘制竖线 + cv2.line(output, (x, 0), (x, height), (0, 0, 255), 2) + + # 记录与当前竖线的交点 + line_intersections = [] + + # 对于每个轮廓 + for contour in contours: + # 遍历轮廓中的所有点对 + for j in range(len(contour) - 1): + pt1 = contour[j][0] + pt2 = contour[j+1][0] + + # 检查两点是否在竖线两侧 + if (pt1[0] <= x and pt2[0] >= x) or (pt1[0] >= x and pt2[0] <= x): + # 计算交点的y坐标 + if pt2[0] == pt1[0]: # 避免除以零 + y_intersect = pt1[1] + else: + slope = (pt2[1] - pt1[1]) / (pt2[0] - pt1[0]) + y_intersect = pt1[1] + slope * (x - pt1[0]) + + # 添加交点 + line_intersections.append((x, int(y_intersect))) + + # 在图像上标记交点 + for point in line_intersections: + cv2.circle(output, point, 5, (255, 0, 0), -1) + + # 寻找最底部的交点 + if line_intersections: + bottom_most = max(line_intersections, key=lambda p: p[1]) + intersection_points.append(bottom_most) + else: + intersection_points.append(None) + + # 找出底部最近的点 + valid_intersections = [p for p in intersection_points if p is not None] + + if valid_intersections: + bottom_most_point = max(valid_intersections, key=lambda p: p[1]) + bottom_most_index = intersection_points.index(bottom_most_point) + + # 计算目标线信息 + target_line_x = vertical_lines[bottom_most_index] + center_line_x = vertical_lines[0] + distance_to_center = target_line_x - center_line_x + + # 标记底部最近的点 + cv2.circle(output, bottom_most_point, 10, (255, 255, 0), -1) + + # 计算目标线的斜率 + target_contour_points = [] + for contour in contours: + for point in contour: + if abs(point[0][0] - target_line_x) < 5: + target_contour_points.append((point[0][0], point[0][1])) + + slope = 0 + if len(target_contour_points) >= 2: + x_coords = np.array([p[0] for p in target_contour_points]) + y_coords = np.array([p[1] for p in target_contour_points]) + + if np.std(x_coords) > 0: + slope, _ = np.polyfit(x_coords, y_coords, 1) + + # 在图像上添加目标线信息 + cv2.putText(output, f"Distance to center: {distance_to_center}px", (10, 150), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + cv2.putText(output, f"Slope: {slope:.4f}", (10, 190), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + + # 合并所有轮廓 + all_contours = np.vstack([contours[i] for i in range(len(contours))]) + all_contours = all_contours.reshape((-1, 1, 2)) + + # 使用多边形近似轮廓 + epsilon = 0.01 * cv2.arcLength(all_contours, False) + approx = cv2.approxPolyDP(all_contours, epsilon, False) + + # 提取赛道底部和顶部的点 + bottom_points = [p[0] for p in approx if p[0][1] > height * 0.7] + top_points = [p[0] for p in approx if p[0][1] < height * 0.3] + + # 如果找到了顶部和底部的点,计算中心线和方向 + if bottom_points and top_points: + bottom_center_x = sum(p[0] for p in bottom_points) / len(bottom_points) + top_center_x = sum(p[0] for p in top_points) / len(top_points) + + # 绘制底部和顶部中心点 + cv2.circle(output, (int(bottom_center_x), int(height * 0.8)), 10, (0, 0, 255), -1) + cv2.circle(output, (int(top_center_x), int(height * 0.2)), 10, (0, 0, 255), -1) + + # 绘制路径线 + cv2.line(output, (int(bottom_center_x), int(height * 0.8)), + (int(top_center_x), int(height * 0.2)), (0, 255, 0), 2) + + # 计算方向角度 + delta_x = top_center_x - bottom_center_x + track_angle = np.arctan2(height * 0.6, delta_x) * 180 / np.pi + + # 估算距离 + yellow_area = cv2.countNonZero(mask) + total_area = height * width + area_ratio = yellow_area / total_area + estimated_distance = 1.0 / (area_ratio + 0.01) + normalized_distance = min(10.0, max(0.0, estimated_distance / 100.0)) + + # 确定转向方向 + turn_direction = "left" if delta_x < 0 else "right" if delta_x > 0 else "straight" + + # 在图像上添加信息 + cv2.putText(output, f"Distance: {normalized_distance:.2f}m", (10, 30), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + cv2.putText(output, f"Angle: {track_angle:.2f}°", (10, 70), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + cv2.putText(output, f"Direction: {turn_direction}", (10, 110), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + + # 如果提供了保存路径,保存结果图像 + if save_path: + cv2.imwrite(save_path, output) + if observe: + print(f"结果已保存到: {save_path}") + + # 创建包含所有处理步骤的结果图像 + result = np.hstack((img, yellow_only, output)) + + # 调整大小以便查看 + scale_percent = 50 # 缩放到原来的50% + width = int(result.shape[1] * scale_percent / 100) + height = int(result.shape[0] * scale_percent / 100) + dim = (width, height) + resized = cv2.resize(result, dim, interpolation=cv2.INTER_AREA) + + # 显示结果 + cv2.imshow('Track Detection Process', resized) + cv2.waitKey(0) + cv2.destroyAllWindows() + +# 距离估算辅助函数 +def estimate_distance_to_track(image): + """ + 估算摄像机到赛道前方的距离 + + 参数: + image: 输入图像,可以是文件路径或者已加载的图像数组 + + 返回: + distance: 估算的距离(米) + path_angle: 赛道角度(度),正值表示向右转,负值表示向左转 + target_line_info: 目标线信息字典,包含到中线的距离和斜率 + """ + distance, path_info = detect_yellow_track(image, observe=False) + + if distance is None or path_info is None: + return None, None, None + + # 提取目标线信息 + target_line_info = { + "distance_to_center": path_info["distance_to_center"], + "slope": path_info["target_line_slope"] + } + + return distance, path_info["track_angle"], target_line_info + +def detect_horizontal_track_edge(image, observe=False, delay=1500): + """ + 检测正前方横向黄色赛道的边缘,并返回y值最大的边缘点 + + 参数: + image: 输入图像,可以是文件路径或者已加载的图像数组 + observe: 是否输出中间状态信息和可视化结果,默认为False + delay: 展示每个步骤的等待时间(毫秒),默认为1500ms + + 返回: + edge_point: 赛道前方边缘点的坐标 (x, y) + edge_info: 边缘信息字典 + """ + # 如果输入是字符串(文件路径),则加载图像 + if isinstance(image, str): + img = cv2.imread(image) + else: + img = image.copy() + + if img is None: + print("无法加载图像") + return None, None + + # 获取图像尺寸 + height, width = img.shape[:2] + + # 计算图像中间区域的范围(用于专注于正前方的赛道) + center_x = width // 2 + search_width = int(width * 2/3) # 搜索区域宽度为图像宽度的2/3 + search_height = height # 搜索区域高度为图像高度的1/1 + left_bound = center_x - search_width // 2 + right_bound = center_x + search_width // 2 + bottom_bound = height + top_bound = height - search_height + + if observe: + print("步骤1: 原始图像已加载") + search_region_img = img.copy() + # 绘制搜索区域 + cv2.rectangle(search_region_img, (left_bound, top_bound), (right_bound, bottom_bound), (255, 0, 0), 2) + cv2.line(search_region_img, (center_x, 0), (center_x, height), (0, 0, 255), 2) # 中线 + cv2.imshow("搜索区域", search_region_img) + cv2.waitKey(delay) + + # 转换到HSV颜色空间以便更容易提取黄色 + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + + # 黄色的HSV范围 + lower_yellow = np.array([20, 100, 100]) + upper_yellow = np.array([30, 255, 255]) + + # 创建黄色的掩码 + mask = cv2.inRange(hsv, lower_yellow, upper_yellow) + + if observe: + print("步骤2: 创建黄色掩码") + cv2.imshow("黄色掩码", mask) + cv2.waitKey(delay) + + # 使用形态学操作改善掩码质量 + kernel = np.ones((5, 5), np.uint8) + mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 闭操作填充小空洞 + mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 开操作移除小噪点 + + if observe: + print("步骤2.1: 形态学处理后的掩码") + cv2.imshow("处理后的掩码", mask) + cv2.waitKey(delay) + + # 应用掩码,只保留黄色部分 + yellow_only = cv2.bitwise_and(img, img, mask=mask) + + if observe: + print("步骤3: 提取黄色部分") + cv2.imshow("只保留黄色", yellow_only) + cv2.waitKey(delay) + + # 查找轮廓 + contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + + # 如果没有找到轮廓,返回None + if not contours: + if observe: + print("未找到轮廓") + return None, None + + if observe: + print(f"步骤4: 找到 {len(contours)} 个轮廓") + contour_img = img.copy() + cv2.drawContours(contour_img, contours, -1, (0, 255, 0), 2) + cv2.imshow("所有轮廓", contour_img) + cv2.waitKey(delay) + + # 筛选可能属于横向赛道的轮廓 + horizontal_contours = [] + + for contour in contours: + # 计算轮廓的边界框 + x, y, w, h = cv2.boundingRect(contour) + + # 计算轮廓的宽高比 + aspect_ratio = float(w) / max(h, 1) + + # 在搜索区域内且宽高比大于1(更宽而非更高)的轮廓更可能是横向线段 + if (left_bound <= x + w // 2 <= right_bound and + top_bound <= y + h // 2 <= bottom_bound and + aspect_ratio > 1.0): + horizontal_contours.append(contour) + + if not horizontal_contours: + if observe: + print("未找到符合条件的横向轮廓") + + # 如果没有找到符合条件的横向轮廓,尝试使用所有在搜索区域内的轮廓 + for contour in contours: + x, y, w, h = cv2.boundingRect(contour) + if (left_bound <= x + w // 2 <= right_bound and + top_bound <= y + h // 2 <= bottom_bound): + horizontal_contours.append(contour) + + if not horizontal_contours: + if observe: + print("在搜索区域内未找到任何轮廓") + return None, None + + if observe: + print(f"步骤4.1: 找到 {len(horizontal_contours)} 个可能的横向轮廓") + horizontal_img = img.copy() + cv2.drawContours(horizontal_img, horizontal_contours, -1, (0, 255, 0), 2) + cv2.imshow("横向轮廓", horizontal_img) + cv2.waitKey(delay) + + # 收集所有可能的横向轮廓点 + all_horizontal_points = [] + for contour in horizontal_contours: + for point in contour: + x, y = point[0] + if (left_bound <= x <= right_bound and + top_bound <= y <= bottom_bound): + all_horizontal_points.append((x, y)) + + if not all_horizontal_points: + if observe: + print("在搜索区域内未找到有效点") + return None, None + + # 按y值对点进行分组(针对不同的水平线段) + # 使用聚类方法将点按y值分组 + y_values = np.array([p[1] for p in all_horizontal_points]) + y_values = y_values.reshape(-1, 1) # 转换为列向量 + + # 如果点较少,直接按y值简单分组 + if len(y_values) < 10: + # 简单分组:通过y值差异判断是否属于同一水平线 + y_groups = [] + current_group = [all_horizontal_points[0]] + current_y = all_horizontal_points[0][1] + + for i in range(1, len(all_horizontal_points)): + point = all_horizontal_points[i] + if abs(point[1] - current_y) < 10: # 如果y值接近当前组的y值 + current_group.append(point) + else: + y_groups.append(current_group) + current_group = [point] + current_y = point[1] + + if current_group: + y_groups.append(current_group) + else: + # 使用K-means聚类按y值将点分为不同组 + max_clusters = min(5, len(y_values) // 2) # 最多5个聚类或点数的一半 + + # 尝试不同数量的聚类,找到最佳分组 + best_score = -1 + best_labels = None + + for n_clusters in range(1, max_clusters + 1): + kmeans = KMeans(n_clusters=n_clusters, n_init=10, random_state=0).fit(y_values) + score = silhouette_score(y_values, kmeans.labels_) if n_clusters > 1 else 0 + + if score > best_score: + best_score = score + best_labels = kmeans.labels_ + + # 根据聚类结果分组 + y_groups = [[] for _ in range(max(best_labels) + 1)] + for i, point in enumerate(all_horizontal_points): + group_idx = best_labels[i] + y_groups[group_idx].append(point) + + if observe: + clusters_img = img.copy() + colors = [(0, 0, 255), (0, 255, 0), (255, 0, 0), (255, 255, 0), (0, 255, 255)] + + for i, group in enumerate(y_groups): + color = colors[i % len(colors)] + for point in group: + cv2.circle(clusters_img, point, 3, color, -1) + + cv2.imshow("按Y值分组的点", clusters_img) + cv2.waitKey(delay) + + # 为每个组计算平均y值 + avg_y_values = [] + for group in y_groups: + avg_y = sum(p[1] for p in group) / len(group) + avg_y_values.append((avg_y, group)) + + # 按平均y值降序排序(越大的y值越靠近底部,也就是越靠近相机) + avg_y_values.sort(reverse=True) + + # 从y值最大的组开始分析,找到符合横向赛道特征的组 + selected_group = None + selected_slope = 0 + + for avg_y, group in avg_y_values: + # 计算该组点的斜率 + if len(group) < 2: + continue + + x_coords = np.array([p[0] for p in group]) + y_coords = np.array([p[1] for p in group]) + + if np.std(x_coords) <= 0: + continue + + slope, _ = np.polyfit(x_coords, y_coords, 1) + + # 判断该组是否可能是横向赛道 + # 横向赛道的斜率应该比较小(接近水平) + if abs(slope) < 0.5: # 允许一定的倾斜 + selected_group = group + selected_slope = slope + break + + # 如果没有找到符合条件的组,使用y值最大的组 + if selected_group is None and avg_y_values: + selected_group = avg_y_values[0][1] + + # 重新计算斜率 + if len(selected_group) >= 2: + x_coords = np.array([p[0] for p in selected_group]) + y_coords = np.array([p[1] for p in selected_group]) + + if np.std(x_coords) > 0: + selected_slope, _ = np.polyfit(x_coords, y_coords, 1) + + if selected_group is None: + if observe: + print("未能找到有效的横向赛道线") + return None, None + + # 找出选定组中y值最大的点(最靠近相机的点) + bottom_edge_point = max(selected_group, key=lambda p: p[1]) + + if observe: + print(f"步骤5: 找到边缘点 {bottom_edge_point}") + edge_img = img.copy() + # 绘制选定的组 + for point in selected_group: + cv2.circle(edge_img, point, 3, (255, 0, 0), -1) + # 标记边缘点 + cv2.circle(edge_img, bottom_edge_point, 10, (0, 0, 255), -1) + cv2.imshow("选定的横向线和边缘点", edge_img) + cv2.waitKey(delay) + + # 计算这个点到中线的距离 + distance_to_center = bottom_edge_point[0] - center_x + + # 使用选定组的斜率 + slope = selected_slope + + # 计算中线与检测到的横向线的交点 + # 横向线方程: y = slope * (x - edge_x) + edge_y + # 中线方程: x = center_x + # 解这个方程组得到交点坐标 + edge_x, edge_y = bottom_edge_point + intersection_x = center_x + intersection_y = slope * (center_x - edge_x) + edge_y + intersection_point = (int(intersection_x), int(intersection_y)) + + # 计算交点到图像底部的距离(以像素为单位) + distance_to_bottom = height - intersection_y + + if observe: + slope_img = img.copy() + # 画出底部边缘点 + cv2.circle(slope_img, bottom_edge_point, 10, (0, 0, 255), -1) + # 画出选定组中的所有点 + for point in selected_group: + cv2.circle(slope_img, point, 3, (255, 0, 0), -1) + # 使用斜率画一条线来表示边缘方向 + line_length = 200 + end_x = bottom_edge_point[0] + line_length + end_y = int(bottom_edge_point[1] + slope * line_length) + start_x = bottom_edge_point[0] - line_length + start_y = int(bottom_edge_point[1] - slope * line_length) + cv2.line(slope_img, (start_x, start_y), (end_x, end_y), (0, 255, 0), 2) + + # 画出中线 + cv2.line(slope_img, (center_x, 0), (center_x, height), (0, 0, 255), 2) + + # 标记中线与横向线的交点 (高亮显示) + cv2.circle(slope_img, intersection_point, 12, (255, 0, 255), -1) + cv2.circle(slope_img, intersection_point, 5, (255, 255, 255), -1) + + # 画出交点到底部的距离线 + cv2.line(slope_img, intersection_point, (intersection_x, height), (255, 255, 0), 2) + + cv2.putText(slope_img, f"Slope: {slope:.4f}", (10, 30), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + cv2.putText(slope_img, f"Distance to center: {distance_to_center}px", (10, 70), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + cv2.putText(slope_img, f"Distance to bottom: {distance_to_bottom:.1f}px", (10, 110), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + cv2.putText(slope_img, f"中线交点: ({intersection_point[0]}, {intersection_point[1]})", (10, 150), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + cv2.imshow("边缘斜率和中线交点", slope_img) + cv2.waitKey(delay) + + # 创建边缘信息字典 + edge_info = { + "x": bottom_edge_point[0], + "y": bottom_edge_point[1], + "distance_to_center": distance_to_center, + "slope": slope, + "is_horizontal": abs(slope) < 0.1, # 判断边缘是否接近水平 + "points_count": len(selected_group), # 该组中点的数量 + "intersection_point": intersection_point, # 中线与横向线的交点 + "distance_to_bottom": distance_to_bottom, # 交点到图像底部的距离 + "points": selected_group # 添加选定的点组 + } + + return bottom_edge_point, edge_info + +def visualize_horizontal_track_edge(image, save_path=None, observe=False, delay=500): + """ + 可视化横向赛道边缘检测过程,显示中间结果和最终分析 + + 参数: + image: 输入图像,可以是文件路径或者已加载的图像数组 + save_path: 保存结果图像的路径(可选) + observe: 是否输出中间状态信息和可视化结果,默认为False + delay: 展示每个步骤的等待时间(毫秒),默认为500ms + """ + # 如果输入是字符串(文件路径),则加载图像 + if isinstance(image, str): + img = cv2.imread(image) + else: + img = image.copy() + + if img is None: + print("无法加载图像") + return + + # 获取图像尺寸 + height, width = img.shape[:2] + + # 创建输出图像 + output = img.copy() + + # 执行横向赛道检测 + edge_point, edge_info = detect_horizontal_track_edge(img, observe=False) + + # 如果成功检测到边缘 + if edge_point is not None and edge_info is not None: + # 获取信息 + center_x = width // 2 + + # 绘制中线 + cv2.line(output, (center_x, 0), (center_x, height), (0, 0, 255), 2) + + # 获取关键信息 + selected_slope = edge_info['slope'] + distance_to_center = edge_info['distance_to_center'] + intersection_point = edge_info['intersection_point'] + distance_to_bottom = edge_info['distance_to_bottom'] + is_horizontal = edge_info['is_horizontal'] + + # 绘制检测到的点,兼容旧版本的edge_info字典 + if 'points' in edge_info and edge_info['points']: + for point in edge_info['points']: + cv2.circle(output, point, 3, (0, 255, 0), -1) + + # 标记边缘点 + cv2.circle(output, edge_point, 10, (0, 0, 255), -1) + + # 使用斜率画一条线来表示边缘方向 + line_length = 200 + end_x = edge_point[0] + line_length + end_y = int(edge_point[1] + selected_slope * line_length) + start_x = edge_point[0] - line_length + start_y = int(edge_point[1] - selected_slope * line_length) + cv2.line(output, (start_x, start_y), (end_x, end_y), (0, 255, 0), 2) + + # 标记中线与横向线的交点 (高亮显示) + cv2.circle(output, intersection_point, 12, (255, 0, 255), -1) + cv2.circle(output, intersection_point, 5, (255, 255, 255), -1) + + # 画出交点到底部的距离线 + cv2.line(output, intersection_point, (intersection_point[0], height), (255, 255, 0), 2) + + # 添加信息文本 + cv2.putText(output, f"边缘点: ({edge_point[0]}, {edge_point[1]})", (10, 30), + cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) + cv2.putText(output, f"到中线距离: {distance_to_center}像素", (10, 60), + cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) + cv2.putText(output, f"斜率: {selected_slope:.4f}", (10, 90), + cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) + cv2.putText(output, f"是否水平: {is_horizontal}", (10, 120), + cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) + cv2.putText(output, f"点数量: {edge_info['points_count']}", (10, 150), + cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) + cv2.putText(output, f"到底部距离: {distance_to_bottom:.1f}像素", (10, 180), + cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) + cv2.putText(output, f"中线交点: ({intersection_point[0]}, {intersection_point[1]})", (10, 210), + cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) + else: + # 如果没有检测到,添加提示 + cv2.putText(output, "未检测到横向赛道", (width//4, height//2), + cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 2) + + # 如果提供了保存路径,保存结果图像 + if save_path: + cv2.imwrite(save_path, output) + if observe: + print(f"结果已保存到: {save_path}") + + # 显示结果 + if observe: + # 转换到HSV颜色空间 + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + + # 黄色的HSV范围 + lower_yellow = np.array([20, 100, 100]) + upper_yellow = np.array([30, 255, 255]) + + # 创建黄色的掩码 + mask = cv2.inRange(hsv, lower_yellow, upper_yellow) + + # 应用形态学操作 + kernel = np.ones((5, 5), np.uint8) + mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) + mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) + + # 应用掩码,只保留黄色部分 + yellow_only = cv2.bitwise_and(img, img, mask=mask) + + # 创建包含所有处理步骤的结果图像 + result = np.hstack((img, yellow_only, output)) + + # 调整大小以便查看 + scale_percent = 50 # 缩放到原来的50% + width = int(result.shape[1] * scale_percent / 100) + height = int(result.shape[0] * scale_percent / 100) + dim = (width, height) + resized = cv2.resize(result, dim, interpolation=cv2.INTER_AREA) + + # 显示结果 + cv2.imshow('横向赛道边缘检测', resized) + cv2.waitKey(delay) + if save_path is None: # 如果没有保存,则等待按键 + cv2.waitKey(0) + cv2.destroyAllWindows() + + return output + +# 用法示例 +if __name__ == "__main__": + # 替换为实际图像路径 + image_path = "path/to/track/image.png" + + # 检测赛道并估算距离 + distance, path_info = detect_yellow_track(image_path, observe=True, delay=1500) + if distance is not None: + print(f"估算距离: {distance:.2f}米") + print(f"赛道角度: {path_info['track_angle']:.2f}°") + print(f"转向方向: {path_info['turn_direction']}") + print(f"目标线到中线距离: {path_info['distance_to_center']}像素") + print(f"目标线斜率: {path_info['target_line_slope']:.4f}") + + # 可视化检测过程 + visualize_track_detection(image_path, observe=True, delay=1500) + + # 检测横向赛道边缘 + edge_point, edge_info = detect_horizontal_track_edge(image_path, observe=True, delay=1500) + if edge_point is not None: + print(f"边缘点坐标: ({edge_point[0]}, {edge_point[1]})") + print(f"到中线距离: {edge_info['distance_to_center']}像素") + print(f"边缘斜率: {edge_info['slope']:.4f}") + print(f"是否水平: {edge_info['is_horizontal']}") + + # 可视化横向赛道边缘检测 + visualize_horizontal_track_edge(image_path, observe=True, delay=1500) + + # 其他检测和可视化... \ No newline at end of file