删除测试图像结果文件,并在黄色赛道检测演示程序中更新输入和输出路径,同时在边缘检测算法中引入新的检测函数以提升检测准确性和稳定性。

This commit is contained in:
Havoc 2025-05-22 12:35:14 +08:00
parent 2793daf8fe
commit 968471471f
5 changed files with 483 additions and 20 deletions

90
logs/robot_2025-05-22.log Normal file
View File

@ -0,0 +1,90 @@
2025-05-22 12:20:16 | DEBUG | utils.log_helper - 🐞 步骤1: 原始图像已加载
2025-05-22 12:20:18 | DEBUG | utils.log_helper - 🐞 步骤2: 创建黄色掩码
2025-05-22 12:20:19 | DEBUG | utils.log_helper - 🐞 步骤3: 提取黄色部分
2025-05-22 12:20:20 | DEBUG | utils.log_helper - 🐞 检测底部和顶部边缘点
2025-05-22 12:20:21 | DEBUG | utils.log_helper - 🐞 步骤4: 边缘检测
2025-05-22 12:20:22 | DEBUG | utils.log_helper - 🐞 步骤5: 检测到 8 条直线
2025-05-22 12:20:23 | DEBUG | utils.log_helper - 🐞 步骤6: 找到 5 条水平线 (下方: 5, 上方: 0)
2025-05-22 12:20:24 | DEBUG | utils.log_helper - 🐞 选择下方水平线
2025-05-22 12:20:24 | DEBUG | utils.log_helper - 🐞 显示边缘斜率和中线交点
2025-05-22 12:20:25 | INFO | utils.log_helper - 保存横向边缘检测结果图像到: logs/image/horizontal_edge_20250522_122025_111891.jpg
2025-05-22 12:20:25 | INFO | utils.log_helper - 横向边缘检测结果: {'timestamp': '20250522_122025_111891', 'edge_point': (1919, 862), 'distance_to_center': 959, 'slope': 0.03486394557823129, 'distance_to_bottom': 251.43452380952385, 'intersection_point': (960, 828), 'score': 0.6679311933106575, 'valid': True, 'reason': '', 'is_lower_line': array([ True, True, True, True])}
2025-05-22 12:20:38 | DEBUG | utils.log_helper - 🐞 步骤1: 原始图像已加载
2025-05-22 12:20:39 | DEBUG | utils.log_helper - 🐞 步骤2: 创建黄色掩码
2025-05-22 12:20:40 | DEBUG | utils.log_helper - 🐞 步骤3: 提取黄色部分
2025-05-22 12:20:41 | DEBUG | utils.log_helper - 🐞 检测底部和顶部边缘点
2025-05-22 12:20:42 | DEBUG | utils.log_helper - 🐞 步骤4: 边缘检测
2025-05-22 12:20:43 | DEBUG | utils.log_helper - 🐞 步骤5: 检测到 8 条直线
2025-05-22 12:20:44 | DEBUG | utils.log_helper - 🐞 步骤6: 找到 5 条水平线 (下方: 5, 上方: 0)
2025-05-22 12:20:45 | DEBUG | utils.log_helper - 🐞 选择下方水平线
2025-05-22 12:20:45 | DEBUG | utils.log_helper - 🐞 显示边缘斜率和中线交点
2025-05-22 12:20:47 | INFO | utils.log_helper - 保存横向边缘检测结果图像到: logs/image/horizontal_edge_20250522_122046_987809.jpg
2025-05-22 12:20:47 | INFO | utils.log_helper - 横向边缘检测结果: {'timestamp': '20250522_122046_987809', 'edge_point': (1919, 862), 'distance_to_center': 959, 'slope': 0.03486394557823129, 'distance_to_bottom': 251.43452380952385, 'intersection_point': (960, 828), 'score': 0.6679311933106575, 'valid': True, 'reason': '', 'is_lower_line': array([ True, True, True, True])}
2025-05-22 12:21:15 | DEBUG | utils.log_helper - 🐞 步骤1: 原始图像已加载
2025-05-22 12:21:16 | DEBUG | utils.log_helper - 🐞 步骤2: 创建黄色掩码
2025-05-22 12:21:17 | DEBUG | utils.log_helper - 🐞 步骤3: 提取黄色部分
2025-05-22 12:21:18 | DEBUG | utils.log_helper - 🐞 检测底部和顶部边缘点
2025-05-22 12:21:19 | DEBUG | utils.log_helper - 🐞 步骤4: 边缘检测
2025-05-22 12:21:20 | DEBUG | utils.log_helper - 🐞 步骤5: 检测到 8 条直线
2025-05-22 12:21:21 | DEBUG | utils.log_helper - 🐞 步骤6: 找到 5 条水平线 (下方: 5, 上方: 0)
2025-05-22 12:21:22 | DEBUG | utils.log_helper - 🐞 选择下方水平线
2025-05-22 12:21:22 | DEBUG | utils.log_helper - 🐞 显示边缘斜率和中线交点
2025-05-22 12:21:23 | INFO | utils.log_helper - 保存横向边缘检测结果图像到: logs/image/horizontal_edge_20250522_122123_802326.jpg
2025-05-22 12:21:23 | INFO | utils.log_helper - 横向边缘检测结果: {'timestamp': '20250522_122123_802326', 'edge_point': (1919, 862), 'distance_to_center': 959, 'slope': 0.03486394557823129, 'distance_to_bottom': 251.43452380952385, 'intersection_point': (960, 828), 'score': 0.6679311933106575, 'valid': True, 'reason': '', 'is_lower_line': array([ True, True, True, True])}
2025-05-22 12:22:26 | DEBUG | utils.log_helper - 🐞 步骤1: 原始图像已加载
2025-05-22 12:22:28 | DEBUG | utils.log_helper - 🐞 步骤2: 创建黄色掩码
2025-05-22 12:22:29 | DEBUG | utils.log_helper - 🐞 步骤3: 提取黄色部分
2025-05-22 12:22:30 | DEBUG | utils.log_helper - 🐞 检测底部和顶部边缘点
2025-05-22 12:22:31 | DEBUG | utils.log_helper - 🐞 步骤4: 边缘检测
2025-05-22 12:22:32 | DEBUG | utils.log_helper - 🐞 步骤5: 检测到 8 条直线
2025-05-22 12:22:33 | DEBUG | utils.log_helper - 🐞 步骤6: 找到 5 条水平线 (下方: 5, 上方: 0)
2025-05-22 12:22:34 | DEBUG | utils.log_helper - 🐞 选择下方水平线
2025-05-22 12:22:34 | DEBUG | utils.log_helper - 🐞 显示边缘斜率和中线交点
2025-05-22 12:22:35 | INFO | utils.log_helper - 保存横向边缘检测结果图像到: logs/image/horizontal_edge_20250522_122235_241410.jpg
2025-05-22 12:22:35 | INFO | utils.log_helper - 横向边缘检测结果: {'timestamp': '20250522_122235_241410', 'edge_point': (1919, 862), 'distance_to_center': 959, 'slope': 0.03486394557823129, 'distance_to_bottom': 251.43452380952385, 'intersection_point': (960, 828), 'score': 0.6679311933106575, 'valid': True, 'reason': '', 'is_lower_line': array([ True, True, True, True])}
2025-05-22 12:24:10 | DEBUG | utils.log_helper - 🐞 步骤1: 原始图像已加载
2025-05-22 12:24:11 | DEBUG | utils.log_helper - 🐞 步骤2: 创建黄色掩码
2025-05-22 12:24:12 | DEBUG | utils.log_helper - 🐞 步骤3: 提取黄色部分
2025-05-22 12:24:13 | DEBUG | utils.log_helper - 🐞 检测底部和顶部边缘点
2025-05-22 12:24:14 | DEBUG | utils.log_helper - 🐞 步骤4: 边缘检测
2025-05-22 12:24:15 | DEBUG | utils.log_helper - 🐞 步骤5: 检测到 28 条直线
2025-05-22 12:24:16 | DEBUG | utils.log_helper - 🐞 步骤6: 找到 6 条水平线 (下方: 6, 上方: 0)
2025-05-22 12:24:17 | DEBUG | utils.log_helper - 🐞 选择下方水平线
2025-05-22 12:24:17 | DEBUG | utils.log_helper - 🐞 显示边缘斜率和中线交点
2025-05-22 12:24:18 | INFO | utils.log_helper - 保存横向边缘检测结果图像到: logs/image/horizontal_edge_20250522_122418_738782.jpg
2025-05-22 12:24:18 | INFO | utils.log_helper - 横向边缘检测结果: {'timestamp': '20250522_122418_738782', 'edge_point': (91, 1078), 'distance_to_center': -869, 'slope': -0.07092198581560284, 'distance_to_bottom': 63.631205673758814, 'intersection_point': (960, 1016), 'score': 0.8014868667518458, 'valid': True, 'reason': '', 'is_lower_line': array([ True, True, True, True])}
2025-05-22 12:24:54 | DEBUG | utils.log_helper - 🐞 步骤1: 原始图像已加载
2025-05-22 12:24:55 | DEBUG | utils.log_helper - 🐞 步骤2: 创建黄色掩码
2025-05-22 12:24:56 | DEBUG | utils.log_helper - 🐞 步骤3: 提取黄色部分
2025-05-22 12:24:57 | DEBUG | utils.log_helper - 🐞 检测底部和顶部边缘点
2025-05-22 12:24:58 | DEBUG | utils.log_helper - 🐞 步骤4: 边缘检测
2025-05-22 12:24:59 | DEBUG | utils.log_helper - 🐞 步骤5: 检测到 11 条直线
2025-05-22 12:25:00 | DEBUG | utils.log_helper - 🐞 步骤6: 找到 1 条水平线 (下方: 1, 上方: 0)
2025-05-22 12:25:01 | DEBUG | utils.log_helper - 🐞 选择下方水平线
2025-05-22 12:25:01 | DEBUG | utils.log_helper - 🐞 显示边缘斜率和中线交点
2025-05-22 12:25:02 | INFO | utils.log_helper - 保存横向边缘检测结果图像到: logs/image/horizontal_edge_20250522_122502_339369.jpg
2025-05-22 12:25:02 | INFO | utils.log_helper - 横向边缘检测结果: {'timestamp': '20250522_122502_339369', 'edge_point': (1439, 899), 'distance_to_center': 479, 'slope': -0.07083333333333333, 'distance_to_bottom': 147.07083333333333, 'intersection_point': (960, 932), 'score': 0.5594172208352652, 'valid': True, 'reason': '', 'is_lower_line': array([ True, True, True, True])}
2025-05-22 12:33:02 | INFO | utils.log_helper - 保存横向边缘检测结果图像到: logs/image/horizontal_edge_20250522_123302_872381.jpg
2025-05-22 12:33:02 | INFO | utils.log_helper - 横向边缘检测结果: {'timestamp': '20250522_123302_872381', 'edge_point': (320, 1009), 'distance_to_center': -640, 'slope': -0.07331047777324741, 'distance_to_bottom': 117.91870577487839, 'intersection_point': (960, 962)}
2025-05-22 12:33:26 | INFO | utils.log_helper - 保存横向边缘检测结果图像到: logs/image/horizontal_edge_20250522_123326_235741.jpg
2025-05-22 12:33:26 | INFO | utils.log_helper - 横向边缘检测结果: {'timestamp': '20250522_123326_235741', 'edge_point': (320, 1009), 'distance_to_center': -640, 'slope': -0.07331047777324741, 'distance_to_bottom': 117.91870577487839, 'intersection_point': (960, 962)}
2025-05-22 12:34:09 | DEBUG | utils.log_helper - 🐞 步骤1: 原始图像已加载
2025-05-22 12:34:10 | DEBUG | utils.log_helper - 🐞 步骤2: 创建黄色掩码
2025-05-22 12:34:10 | DEBUG | utils.log_helper - 🐞 步骤3: 提取黄色部分
2025-05-22 12:34:11 | DEBUG | utils.log_helper - 🐞 正在处理底部边缘点
2025-05-22 12:34:12 | DEBUG | utils.log_helper - 🐞 显示拟合线段
2025-05-22 12:34:13 | DEBUG | utils.log_helper - 👁️ 步骤5: 找到边缘点 (320, 1009)
2025-05-22 12:34:14 | DEBUG | utils.log_helper - 🐞 显示边缘斜率和中线交点
2025-05-22 12:34:14 | INFO | utils.log_helper - 保存横向边缘检测结果图像到: logs/image/horizontal_edge_20250522_123414_979258.jpg
2025-05-22 12:34:14 | INFO | utils.log_helper - 横向边缘检测结果: {'timestamp': '20250522_123414_979258', 'edge_point': (320, 1009), 'distance_to_center': -640, 'slope': -0.07331047777324741, 'distance_to_bottom': 117.91870577487839, 'intersection_point': (960, 962)}
2025-05-22 12:34:34 | DEBUG | utils.log_helper - 🐞 步骤1: 原始图像已加载
2025-05-22 12:34:35 | DEBUG | utils.log_helper - 🐞 步骤2: 创建黄色掩码
2025-05-22 12:34:36 | DEBUG | utils.log_helper - 🐞 步骤3: 提取黄色部分
2025-05-22 12:34:36 | DEBUG | utils.log_helper - 🐞 检测底部和顶部边缘点
2025-05-22 12:34:37 | DEBUG | utils.log_helper - 🐞 步骤4: 边缘检测
2025-05-22 12:34:38 | DEBUG | utils.log_helper - 🐞 步骤5: 检测到 11 条直线
2025-05-22 12:34:39 | DEBUG | utils.log_helper - 🐞 步骤6: 找到 1 条水平线 (下方: 1, 上方: 0)
2025-05-22 12:34:40 | DEBUG | utils.log_helper - 🐞 选择下方水平线
2025-05-22 12:34:40 | DEBUG | utils.log_helper - 🐞 显示边缘斜率和中线交点
2025-05-22 12:34:41 | INFO | utils.log_helper - 保存横向边缘检测结果图像到: logs/image/horizontal_edge_20250522_123441_043307.jpg
2025-05-22 12:34:41 | INFO | utils.log_helper - 横向边缘检测结果: {'timestamp': '20250522_123441_043307', 'edge_point': (1439, 899), 'distance_to_center': 479, 'slope': -0.07083333333333333, 'distance_to_bottom': 147.07083333333333, 'intersection_point': (960, 932), 'score': 0.5594172208352652, 'valid': True, 'reason': '', 'is_lower_line': array([ True, True, True, True])}

BIN
res/path/test-3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

View File

@ -9,7 +9,7 @@ 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_horizontal_track_edge
from utils.detect_track import detect_horizontal_track_edge, detect_horizontal_track_edge_v2
def process_image(image_path, save_dir=None, show_steps=False):
"""处理单张图像"""
@ -17,7 +17,7 @@ def process_image(image_path, save_dir=None, show_steps=False):
# 检测赛道并估算距离
start_time = time.time()
edge_point, edge_info = detect_horizontal_track_edge(image_path, observe=show_steps, save_log=True)
edge_point, edge_info = detect_horizontal_track_edge(image_path, observe=show_steps, save_log=True, delay=800)
processing_time = time.time() - start_time
# 输出结果
@ -44,8 +44,8 @@ def process_image(image_path, save_dir=None, show_steps=False):
def main():
parser = argparse.ArgumentParser(description='黄色赛道检测演示程序')
parser.add_argument('--input', type=str, default='res/path/test-2.jpg', help='输入图像或视频的路径')
parser.add_argument('--output', type=str, default='res/path/test/result_image_20250514_024313.png', help='输出结果的保存路径')
parser.add_argument('--input', type=str, default='res/path/image_20250513_162556.png', help='输入图像或视频的路径')
parser.add_argument('--output', type=str, default='res/path/test/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='显示处理步骤')

View File

@ -6,9 +6,346 @@ from sklearn import linear_model
from utils.log_helper import get_logger, debug, info, warning, error, success
def detect_horizontal_track_edge(image, observe=False, delay=1000, save_log=True):
"""
检测正前方横向黄色赛道的边缘并返回y值最大的边缘点
# INFO 原来的版本
参数:
image: 输入图像可以是文件路径或者已加载的图像数组
observe: 是否输出中间状态信息和可视化结果默认为False
delay: 展示每个步骤的等待时间(毫秒)
save_log: 是否保存日志和图像
返回:
edge_point: 赛道前方边缘点的坐标 (x, y)
edge_info: 边缘信息字典
"""
# observe = False # TSET
# 如果输入是字符串(文件路径),则加载图像
if isinstance(image, str):
img = cv2.imread(image)
else:
img = image.copy()
if img is None:
error("无法加载图像", "失败")
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:
debug("步骤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)
# 添加形态学操作以改善掩码
kernel = np.ones((3, 3), np.uint8)
mask = cv2.dilate(mask, kernel, iterations=1)
if observe:
debug("步骤2: 创建黄色掩码", "处理")
cv2.imshow("黄色掩码", mask)
cv2.waitKey(delay)
# 应用掩码,只保留黄色部分
yellow_only = cv2.bitwise_and(img, img, mask=mask)
if observe:
debug("步骤3: 提取黄色部分", "处理")
cv2.imshow("只保留黄色", yellow_only)
cv2.waitKey(delay)
# 裁剪掩码到搜索区域
search_mask = mask[top_bound:bottom_bound, left_bound:right_bound]
# 找到掩码在搜索区域中最底部的非零点位置
bottom_points = []
non_zero_cols = np.where(np.any(search_mask, axis=0))[0]
# 寻找每列的最底部点
for col in non_zero_cols:
col_points = np.where(search_mask[:, col] > 0)[0]
if len(col_points) > 0:
bottom_row = np.max(col_points)
bottom_points.append((left_bound + col, top_bound + bottom_row))
if len(bottom_points) < 3:
# 如果找不到足够的底部点使用canny+霍夫变换
edges = cv2.Canny(mask, 50, 150, apertureSize=3)
if observe:
debug("步骤3.1: 边缘检测", "处理")
cv2.imshow("边缘检测", edges)
cv2.waitKey(delay)
# 使用霍夫变换检测直线 - 调低阈值以检测短线段
lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=30,
minLineLength=width*0.1, maxLineGap=30)
if lines is None or len(lines) == 0:
if observe:
error("未检测到直线", "失败")
return None, None
if observe:
debug(f"步骤4: 检测到 {len(lines)} 条直线", "处理")
lines_img = img.copy()
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)
# 筛选水平线,但放宽斜率条件
horizontal_lines = []
for line in lines:
x1, y1, x2, y2 = line[0]
# 计算斜率 (避免除零错误)
if abs(x2 - x1) < 5: # 几乎垂直的线
continue
slope = (y2 - y1) / (x2 - x1)
# 筛选接近水平的线 (斜率接近0),但容许更大的倾斜度
if abs(slope) < 0.3:
# 确保线在搜索区域内
if ((left_bound <= x1 <= right_bound and top_bound <= y1 <= bottom_bound) or
(left_bound <= x2 <= right_bound and top_bound <= y2 <= bottom_bound)):
# 计算线的中点y坐标
mid_y = (y1 + y2) / 2
line_length = np.sqrt((x2-x1)**2 + (y2-y1)**2)
# 保存线段、其y坐标和长度
horizontal_lines.append((line[0], mid_y, slope, line_length))
if not horizontal_lines:
if observe:
error("未检测到水平线", "失败")
return None, None
if observe:
debug(f"步骤4.1: 找到 {len(horizontal_lines)} 条水平线", "处理")
h_lines_img = img.copy()
for line_info in horizontal_lines:
line, _, slope, _ = line_info
x1, y1, x2, y2 = line
cv2.line(h_lines_img, (x1, y1), (x2, y2), (0, 255, 255), 2)
# 显示斜率
cv2.putText(h_lines_img, f"{slope:.2f}", ((x1+x2)//2, (y1+y2)//2),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
cv2.imshow("水平线", h_lines_img)
cv2.waitKey(delay)
# 按y坐标排序 (从大到小,底部的线排在前面)
horizontal_lines.sort(key=lambda x: x[1], reverse=True)
# 取最靠近底部且足够长的线作为横向赛道线
selected_line = None
selected_slope = 0
for line_info in horizontal_lines:
line, _, slope, length = line_info
if length > width * 0.1: # 确保线足够长
selected_line = line
selected_slope = slope
break
if selected_line is None and horizontal_lines:
# 如果没有足够长的线,就取最靠近底部的线
selected_line = horizontal_lines[0][0]
selected_slope = horizontal_lines[0][2]
if selected_line is None:
if observe:
error("无法选择合适的线段", "失败")
return None, None
x1, y1, x2, y2 = selected_line
else:
# 使用底部点拟合直线
if observe:
debug("正在处理底部边缘点", "处理")
bottom_points_img = img.copy()
for point in bottom_points:
cv2.circle(bottom_points_img, point, 3, (0, 255, 0), -1)
cv2.imshow("底部边缘点", bottom_points_img)
cv2.waitKey(delay)
# 使用RANSAC拟合直线以去除异常值
x_points = np.array([p[0] for p in bottom_points]).reshape(-1, 1)
y_points = np.array([p[1] for p in bottom_points])
# 如果点过少或分布不够宽返回None
if len(bottom_points) < 3 or np.max(x_points) - np.min(x_points) < width * 0.1:
if observe:
warning("底部点太少或分布不够宽", "警告")
return None, None
ransac = linear_model.RANSACRegressor(residual_threshold=5.0)
ransac.fit(x_points, y_points)
# 获取拟合参数
selected_slope = ransac.estimator_.coef_[0]
intercept = ransac.estimator_.intercept_
# 检查斜率是否在合理范围内
if abs(selected_slope) > 0.3:
if observe:
warning(f"拟合斜率过大: {selected_slope:.4f}", "警告")
return None, None
# 使用拟合的直线参数计算线段端点
x1 = left_bound
y1 = int(selected_slope * x1 + intercept)
x2 = right_bound
y2 = int(selected_slope * x2 + intercept)
if observe:
debug("显示拟合线段", "处理")
fitted_line_img = img.copy()
cv2.line(fitted_line_img, (x1, y1), (x2, y2), (0, 255, 255), 2)
cv2.imshow("拟合线段", fitted_line_img)
cv2.waitKey(delay)
# 确保x1 < x2
if x1 > x2:
x1, x2 = x2, x1
y1, y2 = y2, y1
# 找到线上y值最大的点作为边缘点(最靠近相机的点)
if y1 > y2:
bottom_edge_point = (x1, y1)
else:
bottom_edge_point = (x2, y2)
# 获取线上的更多点
selected_points = []
step = 5 # 每5个像素取一个点
for x in range(max(left_bound, int(min(x1, x2))), min(right_bound, int(max(x1, x2)) + 1), step):
y = int(selected_slope * (x - x1) + y1)
if top_bound <= y <= bottom_bound:
selected_points.append((x, y))
if observe:
debug(f"步骤5: 找到边缘点 {bottom_edge_point}", "检测")
edge_img = img.copy()
# 画线
cv2.line(edge_img, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 绘制所有点
for point in selected_points:
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
# 计算中线与检测到的横向线的交点
# 横向线方程: y = slope * (x - x1) + y1
# 中线方程: x = center_x
# 解这个方程组得到交点坐标
intersection_x = center_x
intersection_y = selected_slope * (center_x - x1) + y1
intersection_point = (int(intersection_x), int(intersection_y))
# 计算交点到图像底部的距离(以像素为单位)
distance_to_bottom = height - intersection_y
result_img = None
if observe or save_log:
slope_img = img.copy()
# 画出检测到的线
cv2.line(slope_img, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 标记边缘点
cv2.circle(slope_img, bottom_edge_point, 10, (0, 0, 255), -1)
# 画出中线
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: {selected_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)
if observe:
debug("显示边缘斜率和中线交点", "显示")
cv2.imshow("边缘斜率和中线交点", slope_img)
cv2.waitKey(delay)
result_img = slope_img
# 保存日志图像
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)
img_path = os.path.join(log_dir, f"horizontal_edge_{timestamp}.jpg")
cv2.imwrite(img_path, result_img)
info(f"保存横向边缘检测结果图像到: {img_path}", "日志")
# 保存文本日志信息
log_info = {
"timestamp": timestamp,
"edge_point": bottom_edge_point,
"distance_to_center": distance_to_center,
"slope": selected_slope,
"distance_to_bottom": distance_to_bottom,
"intersection_point": intersection_point
}
info(f"横向边缘检测结果: {log_info}", "日志")
# 创建边缘信息字典
edge_info = {
"x": bottom_edge_point[0],
"y": bottom_edge_point[1],
"distance_to_center": distance_to_center,
"slope": selected_slope,
"is_horizontal": abs(selected_slope) < 0.05, # 判断边缘是否接近水平
"points_count": len(selected_points), # 该组中点的数量
"intersection_point": intersection_point, # 中线与横向线的交点
"distance_to_bottom": distance_to_bottom, # 交点到图像底部的距离
# "points": selected_points # 添加选定的点组
}
return bottom_edge_point, edge_info
def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=True):
"""
检测正前方横向黄色赛道的边缘并返回y值最大的边缘点
优先检测下方横线但在遇到下方线截断的情况时会考虑上边缘
但容易识别到上面的线
参数:
image: 输入图像可以是文件路径或者已加载的图像数组
@ -143,6 +480,13 @@ def detect_horizontal_track_edge(image, observe=False, delay=1000, save_log=True
# 筛选水平线,但放宽斜率条件
horizontal_lines = []
# 分别存储上方和下方的水平线
lower_horizontal_lines = []
upper_horizontal_lines = []
# 定义上下分界线位置 (以图像中部再下移一点作为分界)
lower_upper_boundary = height * 0.65
for line in lines:
x1, y1, x2, y2 = line[0]
@ -176,20 +520,35 @@ def detect_horizontal_track_edge(image, observe=False, delay=1000, save_log=True
mid_x = (x1 + x2) / 2
center_score = max(0.0, 1.0 - abs(mid_x - center_x) / (width * 0.3))
# 计算综合得分
quality_score = position_score * 0.4 + length_score * 0.3 + slope_score * 0.2 + center_score * 0.1
# 计算综合得分,增加位置得分的权重,更强调下方线
quality_score = position_score * 0.6 + length_score * 0.2 + slope_score * 0.15 + center_score * 0.05
# 保存线段、其y坐标、斜率、长度和质量得分
horizontal_lines.append((line[0], mid_y, slope, line_length, quality_score))
line_info = (line[0], mid_y, slope, line_length, quality_score)
# 区分上方和下方的线
if mid_y >= lower_upper_boundary:
lower_horizontal_lines.append(line_info)
else:
upper_horizontal_lines.append(line_info)
# 同时保存到总列表中
horizontal_lines.append(line_info)
if not horizontal_lines:
if observe:
error("未检测到合格的水平线", "失败")
return None, None
# 根据质量得分排序上方和下方的水平线
lower_horizontal_lines.sort(key=lambda x: x[4], reverse=True)
upper_horizontal_lines.sort(key=lambda x: x[4], reverse=True)
if observe:
debug(f"步骤6: 找到 {len(horizontal_lines)} 条水平线", "处理")
debug(f"步骤6: 找到 {len(horizontal_lines)} 条水平线 (下方: {len(lower_horizontal_lines)}, 上方: {len(upper_horizontal_lines)})", "处理")
h_lines_img = img.copy()
# 绘制所有水平线
for line_info in horizontal_lines:
line, _, slope, _, score = line_info
x1, y1, x2, y2 = line
@ -199,16 +558,30 @@ def detect_horizontal_track_edge(image, observe=False, delay=1000, save_log=True
# 显示斜率和得分
cv2.putText(h_lines_img, f"{slope:.2f}|{score:.2f}", ((x1+x2)//2, (y1+y2)//2),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1)
# 绘制上下分界线
cv2.line(h_lines_img, (0, int(lower_upper_boundary)), (width, int(lower_upper_boundary)), (255, 0, 255), 1)
cv2.imshow("水平线", h_lines_img)
cv2.waitKey(delay)
# 根据质量得分排序水平线
horizontal_lines.sort(key=lambda x: x[4], reverse=True)
# 取质量最高的线段作为最终选择
selected_line = horizontal_lines[0][0]
selected_slope = horizontal_lines[0][2]
selected_score = horizontal_lines[0][4]
# 优先选择下方的线,如果没有下方的线才考虑上方的线
if lower_horizontal_lines:
selected_line = lower_horizontal_lines[0][0]
selected_slope = lower_horizontal_lines[0][2]
selected_score = lower_horizontal_lines[0][4]
if observe:
debug("选择下方水平线", "处理")
elif upper_horizontal_lines:
selected_line = upper_horizontal_lines[0][0]
selected_slope = upper_horizontal_lines[0][2]
selected_score = upper_horizontal_lines[0][4]
if observe:
debug("没有合适的下方线,选择上方水平线", "处理")
else:
# 理论上不会进入这个分支因为前面已经检查过horizontal_lines非空
if observe:
error("未检测到合格的水平线", "失败")
return None, None
# 提取线段端点
x1, y1, x2, y2 = selected_line
@ -348,6 +721,8 @@ def detect_horizontal_track_edge(image, observe=False, delay=1000, save_log=True
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.line(slope_img, (0, int(lower_upper_boundary)), (width, int(lower_upper_boundary)), (255, 0, 255), 1)
cv2.putText(slope_img, f"Slope: {selected_slope:.4f}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, line_color, 2)
@ -390,7 +765,8 @@ def detect_horizontal_track_edge(image, observe=False, delay=1000, save_log=True
"intersection_point": intersection_point,
"score": selected_score,
"valid": valid_result,
"reason": reason if not valid_result else ""
"reason": reason if not valid_result else "",
"is_lower_line": len(lower_horizontal_lines) > 0 and selected_line == lower_horizontal_lines[0][0]
}
info(f"横向边缘检测结果: {log_info}", "日志")
@ -409,15 +785,12 @@ def detect_horizontal_track_edge(image, observe=False, delay=1000, save_log=True
"intersection_point": intersection_point, # 中线与横向线的交点
"distance_to_bottom": distance_to_bottom, # 交点到图像底部的距离
"score": selected_score, # 线段质量得分
"is_lower_line": len(lower_horizontal_lines) > 0 and selected_line == lower_horizontal_lines[0][0] # 是否为下方线
# "points": selected_points # 添加选定的点组
}
return bottom_edge_point, edge_info
# 用法示例
if __name__ == "__main__":
pass
def detect_dual_track_lines(image, observe=False, delay=1000, save_log=True):
"""
检测左右两条平行的黄色轨道线