117 lines
4.6 KiB
Python
117 lines
4.6 KiB
Python
|
import cv2
|
|||
|
import numpy as np
|
|||
|
|
|||
|
def detect_bar_top_distance(image, observe=False, delay=800):
|
|||
|
"""
|
|||
|
识别图片中灰色栏杆到图像顶部的距离(像素)
|
|||
|
:param image: 输入BGR格式图片
|
|||
|
:param observe: 是否显示中间处理结果
|
|||
|
:param delay: 显示结果的延迟时间(ms)
|
|||
|
:return: 距离(像素),未检测到返回None
|
|||
|
"""
|
|||
|
# 转为HSV,便于分离灰色
|
|||
|
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
|
|||
|
|
|||
|
# 调整灰色范围 - 更精确地针对多种灰色
|
|||
|
lower_gray = np.array([0, 0, 30]) # 降低亮度下限以捕获更暗的灰色
|
|||
|
upper_gray = np.array([180, 40, 120]) # 增加饱和度和亮度上限以捕获更多灰色变体
|
|||
|
|
|||
|
# 创建掩码
|
|||
|
mask = cv2.inRange(hsv, lower_gray, upper_gray)
|
|||
|
|
|||
|
# 尝试使用边缘检测作为辅助方法
|
|||
|
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
|||
|
edges = cv2.Canny(gray, 50, 150)
|
|||
|
|
|||
|
# 仅保留图像上半部分的边缘,因为栏杆通常在上部
|
|||
|
height = edges.shape[0]
|
|||
|
edges[int(height*0.6):, :] = 0
|
|||
|
|
|||
|
# 使用霍夫变换检测水平线条
|
|||
|
lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=50, minLineLength=100, maxLineGap=20)
|
|||
|
|
|||
|
# 如果找到线条,创建线条掩码
|
|||
|
line_mask = np.zeros_like(gray)
|
|||
|
if lines is not None:
|
|||
|
for line in lines:
|
|||
|
x1, y1, x2, y2 = line[0]
|
|||
|
# 仅保留接近水平的线条
|
|||
|
if abs(y2 - y1) < 20: # 水平线的y差异很小
|
|||
|
cv2.line(line_mask, (x1, y1), (x2, y2), 255, 5)
|
|||
|
|
|||
|
# 合并HSV掩码和线条掩码
|
|||
|
combined_mask = cv2.bitwise_or(mask, line_mask)
|
|||
|
|
|||
|
# 增强形态学操作以更好地提取横杆
|
|||
|
# 先进行闭操作连接相近区域
|
|||
|
kernel_close = np.ones((5, 30), np.uint8)
|
|||
|
combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_CLOSE, kernel_close)
|
|||
|
|
|||
|
# 使用开操作去除小噪点
|
|||
|
kernel_open = np.ones((3, 3), np.uint8)
|
|||
|
combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_OPEN, kernel_open)
|
|||
|
|
|||
|
# 再次进行水平方向的膨胀,增强横杆连通性
|
|||
|
kernel_dilate = np.ones((1, 20), np.uint8)
|
|||
|
combined_mask = cv2.dilate(combined_mask, kernel_dilate, iterations=1)
|
|||
|
|
|||
|
if observe:
|
|||
|
cv2.imshow("HSV Mask", mask)
|
|||
|
cv2.imshow("Edge Detection", edges)
|
|||
|
cv2.imshow("Combined Mask", combined_mask)
|
|||
|
cv2.waitKey(delay)
|
|||
|
|
|||
|
# 查找轮廓
|
|||
|
contours, _ = cv2.findContours(combined_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|||
|
if not contours:
|
|||
|
# 如果没有在组合掩码中找到轮廓,尝试在原始掩码中查找
|
|||
|
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|||
|
if not contours:
|
|||
|
return None
|
|||
|
|
|||
|
# 按面积排序找到最大的几个轮廓
|
|||
|
contours = sorted(contours, key=cv2.contourArea, reverse=True)
|
|||
|
|
|||
|
# 从最大的轮廓中筛选出形状类似横杆的轮廓
|
|||
|
for contour in contours[:5]: # 只检查最大的5个轮廓
|
|||
|
x, y, w, h = cv2.boundingRect(contour)
|
|||
|
|
|||
|
# 横杆特征:宽度远大于高度,且宽度占据图像较大比例
|
|||
|
aspect_ratio = float(w) / h if h > 0 else 0
|
|||
|
width_ratio = float(w) / image.shape[1]
|
|||
|
|
|||
|
# 放宽轮廓筛选条件
|
|||
|
if aspect_ratio > 3 and width_ratio > 0.2 and h < image.shape[0] * 0.3:
|
|||
|
if observe:
|
|||
|
debug_img = image.copy()
|
|||
|
cv2.rectangle(debug_img, (x, y), (x+w, y+h), (0, 255, 0), 2)
|
|||
|
cv2.imshow("Detected Bar", debug_img)
|
|||
|
cv2.waitKey(delay)
|
|||
|
|
|||
|
# 返回栏杆顶部到图像顶部的距离
|
|||
|
return y
|
|||
|
|
|||
|
# 如果基于轮廓没有找到合适的栏杆,尝试使用霍夫线检测结果
|
|||
|
if lines is not None:
|
|||
|
horizontal_lines = []
|
|||
|
for line in lines:
|
|||
|
x1, y1, x2, y2 = line[0]
|
|||
|
# 筛选水平线条
|
|||
|
if abs(y2 - y1) < 10 and abs(x2 - x1) > image.shape[1] * 0.3:
|
|||
|
horizontal_lines.append((min(y1, y2), abs(x2 - x1)))
|
|||
|
|
|||
|
# 按线条长度排序
|
|||
|
horizontal_lines.sort(key=lambda x: x[1], reverse=True)
|
|||
|
|
|||
|
if horizontal_lines and len(horizontal_lines) > 0:
|
|||
|
# 返回最长的水平线的y坐标
|
|||
|
if observe:
|
|||
|
debug_img = image.copy()
|
|||
|
y = horizontal_lines[0][0]
|
|||
|
cv2.line(debug_img, (0, y), (image.shape[1], y), (0, 255, 0), 2)
|
|||
|
cv2.imshow("Detected Bar (Hough)", debug_img)
|
|||
|
cv2.waitKey(delay)
|
|||
|
|
|||
|
return horizontal_lines[0][0]
|
|||
|
|
|||
|
return None
|