horizon line detect

This commit is contained in:
Havoc 2025-05-14 12:42:01 +08:00
parent 2072b197e0
commit 16a7ccd101
12 changed files with 1481 additions and 37 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

131
README.md
View File

@ -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) ```bash
![alt text](./res/readme/image-7.png) 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) 1. 将图像转换为HSV颜色空间
![alt text](./res/readme/image-10.png) 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
```
## 示例结果
处理后的图像将包含以下信息:
- 红色点:表示赛道的顶部和底部中心点
- 绿色线:表示赛道的中心线
- 文本信息:距离、角度和方向
## 注意事项
- 该方法在光照条件稳定的环境中效果最佳
- 黄色赛道需要与背景有足够的对比度
- 摄像机应保持相对稳定的高度和角度
- 实际应用中需要根据具体场景调整参数

BIN
res/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -0,0 +1,113 @@
# 黄色赛道检测与距离估算
这个模块提供了一套用于检测黄色赛道并估算距离的工具。基于OpenCV的计算机视觉技术能够从图像中识别黄色赛道计算方向角度并估算摄像机到赛道前方的距离。
## 功能特点
- 黄色赛道检测与分割
- 赛道方向和角度计算
- 赛道距离估算
- 结果可视化展示
- 提供易于使用的测试工具
## 安装依赖
确保已安装以下依赖库:
```bash
pip install opencv-python numpy
```
## 使用方法
### 基本用法
```python
from utils.detect_track import detect_yellow_track, visualize_track_detection
# 检测赛道并估算距离
image_path = "path/to/your/image.jpg"
distance, path_info = detect_yellow_track(image_path)
if distance is not None:
print(f"估算距离: {distance:.2f}米")
print(f"赛道角度: {path_info['track_angle']:.2f}°")
print(f"转向方向: {path_info['turn_direction']}")
# 可视化检测过程
visualize_track_detection(image_path, save_path="result.jpg")
```
### 使用测试脚本
项目提供了一个方便的测试脚本:
```bash
python test_track_detection.py --image path/to/your/image.jpg --observe --save result.jpg
```
参数说明:
- `--image`: 要处理的图像路径(必需)
- `--save`: 结果图像保存路径(可选)
- `--observe`: 显示处理步骤的中间结果
- `--delay`: 中间步骤显示的延迟时间单位为毫秒默认500ms
## 技术细节
### 检测原理
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
```
## 示例结果
处理后的图像将包含以下信息:
- 红色点:表示赛道的顶部和底部中心点
- 绿色线:表示赛道的中心线
- 文本信息:距离、角度和方向
## 注意事项
- 该方法在光照条件稳定的环境中效果最佳
- 黄色赛道需要与背景有足够的对比度
- 摄像机应保持相对稳定的高度和角度
- 实际应用中需要根据具体场景调整参数

View File

@ -0,0 +1 @@
True

View File

@ -0,0 +1,178 @@
import cv2
import os
import sys
import time
import argparse
# 添加父目录到系统路径
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(os.path.dirname(current_dir))
sys.path.append(project_root)
from utils.detect_track import detect_yellow_track, visualize_track_detection, detect_horizontal_track_edge, visualize_horizontal_track_edge
def process_image(image_path, save_dir=None, show_steps=False):
"""处理单张图像"""
print(f"处理图像: {image_path}")
# 检测赛道并估算距离
start_time = time.time()
edge_point, edge_info = detect_horizontal_track_edge(image_path, observe=show_steps)
processing_time = time.time() - start_time
# 输出结果
if edge_point is not None and edge_info is not None:
print(f"处理时间: {processing_time:.3f}")
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']}")
print(f"点数量: {edge_info['points_count']}")
print(f"中线交点: ({edge_info['intersection_point'][0]}, {edge_info['intersection_point'][1]})")
print(f"交点到底部距离: {edge_info['distance_to_bottom']:.1f}像素")
print(f"注意: 中线交点是垂直中线与边缘横线的交点")
print("-" * 30)
# 如果指定了保存目录,保存结果
if save_dir:
if not os.path.exists(save_dir):
os.makedirs(save_dir)
base_name = os.path.basename(image_path)
save_path = os.path.join(save_dir, f"result_{base_name}")
# 可视化并保存
visualize_horizontal_track_edge(image_path, save_path=save_path, observe=show_steps)
print(f"结果已保存到: {save_path}")
else:
print("未能检测到黄色赛道")
return edge_point, edge_info
def process_video(video_path, save_path=None, show_output=True):
"""处理视频"""
print(f"处理视频: {video_path}")
# 打开视频文件
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
print("无法打开视频文件")
return
# 获取视频基本信息
fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print(f"视频信息: {width}x{height}, {fps} FPS, 总帧数: {total_frames}")
# 如果需要保存创建VideoWriter
if save_path:
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter(save_path, fourcc, fps, (width, height))
# 处理计数器
frame_count = 0
processed_count = 0
start_time = time.time()
while True:
ret, frame = cap.read()
if not ret:
break
frame_count += 1
# 每5帧处理一次以提高性能
if frame_count % 5 == 0:
processed_count += 1
# 检测赛道
edge_point, edge_info = detect_horizontal_track_edge(frame, observe=False)
# 创建结果图像使用visualize_horizontal_track_edge函数
if edge_point is not None and edge_info is not None:
result_frame = visualize_horizontal_track_edge(frame, observe=False)
else:
# 如果未检测到赛道,显示原始帧并添加警告
result_frame = frame.copy()
cv2.putText(result_frame, "未检测到横向赛道", (width//4, height//2),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 2)
# 添加帧计数
cv2.putText(result_frame, f"帧: {frame_count}/{total_frames}",
(width - 200, height - 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
# 保存或显示结果
if save_path:
out.write(result_frame)
if show_output:
# 将交点信息显示在窗口标题上
if edge_point is not None and edge_info is not None:
intersection_point = edge_info['intersection_point']
distance_to_bottom = edge_info['distance_to_bottom']
title = f'赛道检测 - 交点:({intersection_point[0]},{intersection_point[1]}) 底距:{distance_to_bottom:.1f}px'
else:
title = '赛道检测 - 未检测到交点'
cv2.imshow(title, result_frame)
key = cv2.waitKey(1) & 0xFF
if key == 27: # ESC键退出
break
# 每秒更新一次处理进度
if frame_count % int(fps) == 0:
elapsed = time.time() - start_time
percent = frame_count / total_frames * 100
print(f"进度: {percent:.1f}% ({frame_count}/{total_frames}), 已用时间: {elapsed:.1f}")
# 清理
cap.release()
if save_path:
out.release()
cv2.destroyAllWindows()
# 输出统计信息
total_time = time.time() - start_time
print(f"视频处理完成,总时间: {total_time:.2f}")
print(f"实际处理帧数: {processed_count}/{frame_count}")
if processed_count > 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()

38
test_track_detection.py Normal file
View File

@ -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()

1057
utils/detect_track.py Normal file

File diff suppressed because it is too large Load Diff