更新黄色赛道检测演示程序的输入输出路径,替换边缘检测函数为新版本以提高检测准确性,并优化线段合并和选择逻辑,增强了对中上部线段的偏好。
This commit is contained in:
parent
924a8e07c6
commit
1c3c07d68a
BIN
res/path/test-v2/2.jpg
Normal file
BIN
res/path/test-v2/2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 95 KiB |
BIN
res/path/test-v2/3.jpg
Normal file
BIN
res/path/test-v2/3.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
@ -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, delay=800)
|
||||
edge_point, edge_info = detect_horizontal_track_edge_v2(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/image_20250513_162556.png', help='输入图像或视频的路径')
|
||||
parser.add_argument('--output', type=str, default='res/path/test/image_20250513_162556.png', help='输出结果的保存路径')
|
||||
parser.add_argument('--input', type=str, default='res/path/test-1.jpg', help='输入图像或视频的路径')
|
||||
parser.add_argument('--output', type=str, default='res/path/test-v2/2-end.jpg', help='输出结果的保存路径')
|
||||
parser.add_argument('--type', type=str, choices=['image', 'video'], help='输入类型,不指定会自动检测')
|
||||
parser.add_argument('--show', default=True, action='store_true', help='显示处理步骤')
|
||||
|
||||
|
@ -19,7 +19,7 @@ def detect_horizontal_track_edge(image, observe=False, delay=1000, save_log=True
|
||||
edge_point: 赛道前方边缘点的坐标 (x, y)
|
||||
edge_info: 边缘信息字典
|
||||
"""
|
||||
observe = False # TEST
|
||||
# observe = False # TEST
|
||||
# 如果输入是字符串(文件路径),则加载图像
|
||||
if isinstance(image, str):
|
||||
img = cv2.imread(image)
|
||||
@ -350,8 +350,7 @@ def detect_horizontal_track_edge(image, observe=False, delay=1000, save_log=True
|
||||
def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=True):
|
||||
"""
|
||||
检测正前方横向黄色赛道的边缘,并返回y值最大的边缘点
|
||||
优先检测下方横线,但在遇到下方线截断的情况时会考虑上边缘
|
||||
但容易识别到上面的线。
|
||||
优先检测中部和上部的横线,特别是对于远处的横线
|
||||
|
||||
参数:
|
||||
image: 输入图像,可以是文件路径或者已加载的图像数组
|
||||
@ -362,7 +361,7 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
edge_point: 赛道前方边缘点的坐标 (x, y)
|
||||
edge_info: 边缘信息字典
|
||||
"""
|
||||
observe = False # TEST
|
||||
# observe = False # TEST
|
||||
# 如果输入是字符串(文件路径),则加载图像
|
||||
if isinstance(image, str):
|
||||
img = cv2.imread(image)
|
||||
@ -385,9 +384,9 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
bottom_bound = height
|
||||
top_bound = height - search_height
|
||||
|
||||
# 定义合理的值范围
|
||||
valid_y_range = (height * 0.5, height) # 有效的y坐标范围(下半部分图像)
|
||||
max_slope = 0.15 # 最大允许斜率(接近水平)
|
||||
# 定义合理的值范围 - 修改为更关注中上部区域
|
||||
valid_y_range = (height * 0.1, height * 0.6) # 有效的y坐标范围(中上部分图像),扩大范围到90%
|
||||
max_slope = 0.2 # 最大允许斜率(接近水平)
|
||||
min_line_length = width * 0.2 # 最小线长度
|
||||
|
||||
if observe:
|
||||
@ -467,7 +466,7 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
cv2.waitKey(delay)
|
||||
|
||||
# 使用霍夫变换检测直线,降低阈值以检测更多线段
|
||||
lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=25,
|
||||
lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=30,
|
||||
minLineLength=width*0.1, maxLineGap=30)
|
||||
|
||||
if lines is None or len(lines) == 0:
|
||||
@ -477,21 +476,147 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
|
||||
if observe:
|
||||
debug(f"步骤5: 检测到 {len(lines)} 条直线", "处理")
|
||||
print(f"len(lines): {len(lines)}")
|
||||
lines_img = img.copy()
|
||||
for line in lines:
|
||||
for i, line in enumerate(lines):
|
||||
x1, y1, x2, y2 = line[0]
|
||||
cv2.line(lines_img, (x1, y1), (x2, y2), (0, 255, 0), 2)
|
||||
# 根据线段长度使用不同颜色
|
||||
line_length = np.sqrt((x2-x1)**2 + (y2-y1)**2)
|
||||
# 使用HSV颜色空间生成不同的颜色
|
||||
hue = (i * 30) % 180 # 每30度一个颜色
|
||||
color = cv2.cvtColor(np.uint8([[[hue, 255, 255]]]), cv2.COLOR_HSV2BGR)[0][0]
|
||||
color = (int(color[0]), int(color[1]), int(color[2]))
|
||||
cv2.line(lines_img, (x1, y1), (x2, y2), color, 2)
|
||||
cv2.putText(lines_img, f"{i}", ((x1+x2)//2, (y1+y2)//2),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
|
||||
cv2.imshow("检测到的直线", lines_img)
|
||||
cv2.waitKey(delay)
|
||||
|
||||
# 过滤和合并相似的线段
|
||||
filtered_lines = []
|
||||
for line in lines:
|
||||
x1, y1, x2, y2 = line[0]
|
||||
# 确保x1 < x2
|
||||
if x1 > x2:
|
||||
x1, x2 = x2, x1
|
||||
y1, y2 = y2, y1
|
||||
filtered_lines.append([x1, y1, x2, y2])
|
||||
|
||||
# 合并相似线段
|
||||
merged_lines = []
|
||||
used_indices = set()
|
||||
|
||||
for i, line1 in enumerate(filtered_lines):
|
||||
if i in used_indices:
|
||||
continue
|
||||
|
||||
x1, y1, x2, y2 = line1
|
||||
similar_lines = [line1]
|
||||
used_indices.add(i)
|
||||
|
||||
# 查找与当前线段相似的其他线段
|
||||
for j, line2 in enumerate(filtered_lines):
|
||||
if j in used_indices or i == j:
|
||||
continue
|
||||
|
||||
x3, y3, x4, y4 = line2
|
||||
|
||||
# 计算两条线段的斜率
|
||||
slope1 = (y2 - y1) / (x2 - x1) if abs(x2 - x1) > 5 else 100
|
||||
slope2 = (y4 - y3) / (x4 - x3) if abs(x4 - x3) > 5 else 100
|
||||
|
||||
# 计算两条线段的中点
|
||||
mid1_x, mid1_y = (x1 + x2) / 2, (y1 + y2) / 2
|
||||
mid2_x, mid2_y = (x3 + x4) / 2, (y3 + y4) / 2
|
||||
|
||||
# 计算中点之间的距离
|
||||
mid_dist = np.sqrt((mid2_x - mid1_x)**2 + (mid2_y - mid1_y)**2)
|
||||
|
||||
# 计算线段端点之间的最小距离
|
||||
end_dists = [
|
||||
np.sqrt((x1-x3)**2 + (y1-y3)**2),
|
||||
np.sqrt((x1-x4)**2 + (y1-y4)**2),
|
||||
np.sqrt((x2-x3)**2 + (y2-y3)**2),
|
||||
np.sqrt((x2-x4)**2 + (y2-y4)**2)
|
||||
]
|
||||
min_end_dist = min(end_dists)
|
||||
|
||||
# 判断两条线段是否相似:满足以下条件之一
|
||||
# 1. 斜率接近且中点距离不太远
|
||||
# 2. 斜率接近且端点之间距离很近(可能是连接的线段)
|
||||
# 3. 端点非常接近(几乎连接),且斜率差异不太大
|
||||
if (abs(slope1 - slope2) < 0.15 and mid_dist < height * 0.15) or \
|
||||
(abs(slope1 - slope2) < 0.1 and min_end_dist < height * 0.05) or \
|
||||
(min_end_dist < height * 0.03 and abs(slope1 - slope2) < 0.25):
|
||||
similar_lines.append(line2)
|
||||
used_indices.add(j)
|
||||
|
||||
# 如果找到相似线段,合并它们
|
||||
if len(similar_lines) > 1:
|
||||
# 合并所有相似线段的端点
|
||||
all_points = []
|
||||
for line in similar_lines:
|
||||
all_points.append((line[0], line[1])) # 起点
|
||||
all_points.append((line[2], line[3])) # 终点
|
||||
|
||||
# 找出x坐标的最小值和最大值
|
||||
min_x = min(p[0] for p in all_points)
|
||||
max_x = max(p[0] for p in all_points)
|
||||
|
||||
# 使用所有点拟合一条直线
|
||||
x_points = np.array([p[0] for p in all_points]).reshape(-1, 1)
|
||||
y_points = np.array([p[1] for p in all_points])
|
||||
|
||||
# 使用RANSAC拟合更稳定的直线
|
||||
ransac = linear_model.RANSACRegressor(residual_threshold=5.0)
|
||||
ransac.fit(x_points, y_points)
|
||||
|
||||
# 获取拟合的斜率和截距
|
||||
merged_slope = ransac.estimator_.coef_[0]
|
||||
merged_intercept = ransac.estimator_.intercept_
|
||||
|
||||
# 计算新的端点
|
||||
y_min = int(merged_slope * min_x + merged_intercept)
|
||||
y_max = int(merged_slope * max_x + merged_intercept)
|
||||
|
||||
# 添加合并后的线段
|
||||
merged_lines.append([min_x, y_min, max_x, y_max])
|
||||
else:
|
||||
# 如果没有相似线段,直接添加原线段
|
||||
merged_lines.append(line1)
|
||||
|
||||
# 将合并后的线段转换为霍夫变换的格式
|
||||
merged_hough_lines = []
|
||||
for line in merged_lines:
|
||||
merged_hough_lines.append(np.array([[line[0], line[1], line[2], line[3]]]))
|
||||
|
||||
if observe:
|
||||
debug(f"步骤5.1: 合并后剩余 {len(merged_hough_lines)} 条线", "处理")
|
||||
merged_img = img.copy()
|
||||
for i, line in enumerate(merged_hough_lines):
|
||||
x1, y1, x2, y2 = line[0]
|
||||
# 使用HSV颜色空间生成不同的颜色
|
||||
hue = (i * 50) % 180 # 每50度一个颜色
|
||||
color = cv2.cvtColor(np.uint8([[[hue, 255, 255]]]), cv2.COLOR_HSV2BGR)[0][0]
|
||||
color = (int(color[0]), int(color[1]), int(color[2]))
|
||||
cv2.line(merged_img, (x1, y1), (x2, y2), color, 3)
|
||||
# 显示线段编号
|
||||
cv2.putText(merged_img, f"{i}", ((x1+x2)//2, (y1+y2)//2),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
|
||||
cv2.imshow("合并后的线段", merged_img)
|
||||
cv2.waitKey(delay)
|
||||
|
||||
# 使用合并后的线段继续处理
|
||||
lines = merged_hough_lines
|
||||
|
||||
# 筛选水平线,但放宽斜率条件
|
||||
horizontal_lines = []
|
||||
# 分别存储上方和下方的水平线
|
||||
lower_horizontal_lines = []
|
||||
upper_horizontal_lines = []
|
||||
|
||||
# 定义上下分界线位置 (以图像中部再下移一点作为分界)
|
||||
lower_upper_boundary = height * 0.65
|
||||
# 定义上下分界线位置 - 修改为图像的60%处,使上方区域更大
|
||||
lower_upper_boundary = height * 0.6
|
||||
|
||||
for line in lines:
|
||||
x1, y1, x2, y2 = line[0]
|
||||
@ -511,10 +636,12 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
mid_y = (y1 + y2) / 2
|
||||
line_length = np.sqrt((x2-x1)**2 + (y2-y1)**2)
|
||||
|
||||
# 过滤掉短线段和太靠近图像上部的线
|
||||
if line_length >= min_line_length and mid_y >= valid_y_range[0]:
|
||||
# 计算线段在图像中的位置得分(越靠近底部得分越高)
|
||||
position_score = min(1.0, (mid_y - valid_y_range[0]) / (valid_y_range[1] - valid_y_range[0]))
|
||||
# 过滤掉短线段
|
||||
if line_length >= min_line_length:
|
||||
# 计算线段在图像中的位置得分
|
||||
# 修改:更偏好中部和上部的线段,使用高斯函数来优化位置评分
|
||||
optimal_y = height * 0.45 # 最佳高度在图像45%处
|
||||
position_score = np.exp(-0.5 * ((mid_y - optimal_y) / (height * 0.2))**2)
|
||||
|
||||
# 计算长度得分(越长越好)
|
||||
length_score = min(1.0, line_length / (width * 0.5))
|
||||
@ -526,8 +653,8 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
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.6 + length_score * 0.2 + slope_score * 0.15 + center_score * 0.05
|
||||
# 计算综合得分,增加位置得分的权重,强调中上部位置
|
||||
quality_score = position_score * 0.5 + length_score * 0.15 + slope_score * 0.25 + center_score * 0.1
|
||||
|
||||
# 保存线段、其y坐标、斜率、长度和质量得分
|
||||
line_info = (line[0], mid_y, slope, line_length, quality_score)
|
||||
@ -541,6 +668,9 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
# 同时保存到总列表中
|
||||
horizontal_lines.append(line_info)
|
||||
|
||||
if observe:
|
||||
print(f"horizontal_lines: {horizontal_lines}")
|
||||
|
||||
if not horizontal_lines:
|
||||
if observe:
|
||||
error("未检测到合格的水平线", "失败")
|
||||
@ -570,19 +700,19 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
cv2.imshow("水平线", h_lines_img)
|
||||
cv2.waitKey(delay)
|
||||
|
||||
# 优先选择下方的线,如果没有下方的线才考虑上方的线
|
||||
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:
|
||||
# 修改:优先选择上方的线,如果没有上方的线才考虑下方的线
|
||||
if 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("没有合适的下方线,选择上方水平线", "处理")
|
||||
debug("选择上方水平线", "处理")
|
||||
elif 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("没有合适的上方线,选择下方水平线", "处理")
|
||||
else:
|
||||
# 理论上不会进入这个分支,因为前面已经检查过horizontal_lines非空
|
||||
if observe:
|
||||
@ -604,17 +734,17 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
bottom_edge_point = (x2, y2)
|
||||
|
||||
# 如果得分过低,可能是错误识别,尝试使用边缘点拟合
|
||||
if selected_score < 0.4 and len(bottom_points) >= 5:
|
||||
if selected_score < 0.4 and len(top_points) >= 5: # 修改:优先使用顶部点进行拟合
|
||||
if observe:
|
||||
debug(f"线段质量得分过低: {selected_score:.2f},尝试使用边缘点拟合", "处理")
|
||||
|
||||
# 筛选下半部分的点
|
||||
valid_bottom_points = [p for p in bottom_points if p[1] >= valid_y_range[0]]
|
||||
# 筛选中上部分的点
|
||||
valid_points = [p for p in top_points if valid_y_range[0] <= p[1] <= valid_y_range[1]]
|
||||
|
||||
if len(valid_bottom_points) >= 5:
|
||||
if len(valid_points) >= 5:
|
||||
# 使用RANSAC拟合直线以去除异常值
|
||||
x_points = np.array([p[0] for p in valid_bottom_points]).reshape(-1, 1)
|
||||
y_points = np.array([p[1] for p in valid_bottom_points])
|
||||
x_points = np.array([p[0] for p in valid_points]).reshape(-1, 1)
|
||||
y_points = np.array([p[1] for p in valid_points])
|
||||
|
||||
ransac = linear_model.RANSACRegressor(residual_threshold=5.0)
|
||||
ransac.fit(x_points, y_points)
|
||||
@ -650,7 +780,7 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
debug(f"使用拟合直线,斜率: {fitted_slope:.4f}, 内点比例: {inlier_ratio:.2f}", "处理")
|
||||
fitted_line_img = img.copy()
|
||||
cv2.line(fitted_line_img, (x1, y1), (x2, y2), (0, 255, 255), 2)
|
||||
for i, point in enumerate(valid_bottom_points):
|
||||
for i, point in enumerate(valid_points):
|
||||
color = (0, 255, 0) if inlier_mask[i] else (0, 0, 255)
|
||||
cv2.circle(fitted_line_img, point, 3, color, -1)
|
||||
cv2.imshow("拟合线和内点", fitted_line_img)
|
||||
@ -668,8 +798,12 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
valid_result = True
|
||||
reason = ""
|
||||
|
||||
# 检查边缘点是否在有效范围内
|
||||
# 修改:调整有效范围检查,适应中上部分的线
|
||||
if not (valid_y_range[0] <= bottom_edge_point[1] <= valid_y_range[1]):
|
||||
# 如果线在图像更上方,只要不太高也可以接受
|
||||
if bottom_edge_point[1] < valid_y_range[0] and bottom_edge_point[1] > height * 0.2:
|
||||
pass # 接受较高的线
|
||||
else:
|
||||
valid_result = False
|
||||
reason += "边缘点y坐标超出有效范围; "
|
||||
|
||||
@ -700,8 +834,8 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
intersection_y = selected_slope * (center_x - x1) + y1
|
||||
intersection_point = (int(intersection_x), int(intersection_y))
|
||||
|
||||
# 检查交点的y坐标是否在有效范围内
|
||||
if not (valid_y_range[0] <= intersection_y <= valid_y_range[1]):
|
||||
# 修改:放宽交点y坐标的有效范围检查
|
||||
if intersection_y < height * 0.2 or intersection_y > height * 0.95:
|
||||
valid_result = False
|
||||
reason += "交点y坐标超出有效范围; "
|
||||
|
||||
@ -729,6 +863,9 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
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.line(slope_img, (0, int(valid_y_range[0])), (width, int(valid_y_range[0])), (255, 255, 0), 1)
|
||||
cv2.line(slope_img, (0, int(valid_y_range[1])), (width, int(valid_y_range[1])), (255, 255, 0), 1)
|
||||
|
||||
cv2.putText(slope_img, f"Slope: {selected_slope:.4f}", (10, 30),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 1, line_color, 2)
|
||||
@ -778,10 +915,16 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
"score": selected_score,
|
||||
"valid": valid_result,
|
||||
"reason": reason if not valid_result else "",
|
||||
"is_lower_line": len(lower_horizontal_lines) > 0 and selected_line == lower_horizontal_lines[0][0]
|
||||
"is_upper_line": len(upper_horizontal_lines) > 0 and selected_line == upper_horizontal_lines[0][0]
|
||||
}
|
||||
info(f"横向边缘检测结果: {log_info}", "日志")
|
||||
|
||||
# 如果结果无效,但检测到了一些线,仍然返回结果,不拒绝太靠近底部的线
|
||||
if not valid_result and "边缘点y坐标超出有效范围" in reason and bottom_edge_point[1] > height * 0.8:
|
||||
# 仍然接受靠近底部的线
|
||||
valid_result = True
|
||||
reason = ""
|
||||
|
||||
# 如果结果无效,可能需要返回失败
|
||||
if not valid_result:
|
||||
return None, None
|
||||
@ -797,7 +940,7 @@ def detect_horizontal_track_edge_v2(image, observe=False, delay=1000, save_log=T
|
||||
"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] # 是否为下方线
|
||||
"is_upper_line": len(upper_horizontal_lines) > 0 and selected_line == upper_horizontal_lines[0][0] # 是否为上方线
|
||||
# "points": selected_points # 添加选定的点组
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user