任务:求如下位置交叉点的坐标
实现效果
思路:
- 通过YOLOv5 检测出目标位置。
- 再通过传统图像方法处理。
传统图像方法的处理思路:
- HSV提取红颜色,得到位置X形状的图案。
- 二值化处理,得到黑白图像。
- 膨胀腐蚀处理,去除噪点,得到干净的图案。
- 边缘检测。
- 霍夫曼变换,得到许多直线。
- 过滤多余直线,得到两条交叉的直线。
- 联立方程组得到中心点坐标。
HSV 颜色提取
标志颜色分明,因此用HSV来提取红色区域。使用网站来提取图片颜色所在的色调,饱和度和明度。
http://color.lukas-stratmann.com/color-systems/hsv.html
也可以写个小脚本通过鼠标来获取图像的HSV。
def get_hsv_value(self, data_path):
def getpos(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN: # 定义一个鼠标左键按下去的事件
print(HSV[y, x])
for img_name in os.listdir(data_path):
img_path = os.path.join(data_path, img_name)
image = cv2.imread(img_path)
image = cv2.resize(image, (960, 960))
HSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
cv2.imshow("imageHSV", HSV)
cv2.imshow('image', image)
cv2.setMouseCallback("imageHSV", getpos)
cv2.waitKey(0)
cv2.setMouseCallback("image", getpos)
cv2.waitKey(0)
如下图,第二幅即为通过HSV提取的图像。
二值化处理
得到第三幅图像。
def RGB2Black(self, img, thresh=80):
thresh = 10
# assign blue channel to zeros
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.threshold(gray_img, thresh, 255, cv2.THRESH_BINARY)[1]
return img
膨胀腐蚀
通过两次膨胀操作和一次腐蚀操作得到最后一张图像,这个主要根据图像的实际情况调整。膨胀腐蚀操作通常是为了去除噪点,得到干净清晰的轮廓。
def dilate_img(self, img):
kernel = np.ones((5, 5), np.uint8)
img = cv2.dilate(img, kernel, iterations=1)
return img
def erode_img(self, img):
kernel = np.ones((5, 5), np.uint8)
img = cv2.erode(img, kernel, iterations=1)
return img
边缘检测 霍夫曼变换
下面对Canny和HoughLines方法的参数做一点解读。
cv2.Canny
- image 为 8 位输入图像。
- threshold1 表示处理过程中的第一个阈值。
- threshold2 表示处理过程中的第二个阈值。
第2,3个参数越小,那么获取得到的细节信息就越丰富。
cv2.HoughLines
- image 为 8 位输入图像。
- 第二个和第三个参数分别是rho 和theta的精度。
- 第四个参数是线长阈值。
dst = cv2.Canny(src, 50, 200, None, 3)
cdst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)
lines = cv2.HoughLines(dst, 1, np.pi / 180, 80, None, 0, 0)
多余线过滤
过滤条件
- 角度小于10度。
- 边缘的线条不要,只要中间两条交叉线。
- 最后得到的线条如果大于两条,那么不要。
##########nhuk#################################### angle inspection
logger.debug("######################################## angle inspection")
angle_approve_dict = {
}
for i, (pt1, pt2) in enumerate(pts_lines):
# calculate the distance between the line and the center of the image
# calculate the angle of the line
angle = int(math.atan2(pt2[1] - pt1[1], pt2[0] - pt1[0]) * (180 / math.pi))
if angle_approve_dict == {
}:
angle_approve_dict[str(angle)] = [(pt1, pt2)]
else:
# if the angle is too close to the previous angle, ignore it
for an in angle_approve_dict:
int_an = int(an)
if abs(angle - int_an) < simi_angle_thres or 180 - abs(angle - int_an) < simi_angle_thres:
angle_approve_dict[an].append((pt1, pt2))
break
else:
angle_approve_dict[str(angle)] = [(pt1, pt2)]
logger.debug(f"angle_approve_dict:{
angle_approve_dict}")
##########nhuk####################################
##########nhuk#################################### get the line with the smallest distance to the center
angle_approved_dist_max = []
logger.debug("######################################## dist min after angle inspection")
for an in angle_approve_dict:
dist_line2center_list = [get_dist_line2center(center_x, pt1, pt2) for (pt1, pt2) in angle_approve_dict[an]]
logger.debug("dist_line2center_list:{}".format(dist_line2center_list))
logger.debug("np.argmin(dist_line2center_list):{}".format(np.argmin(dist_line2center_list)))
angle_approved_dist_max.append(angle_approve_dict[an][np.argmin(dist_line2center_list)])
##########nhuk####################################
##########nhuk#################################### remove the line that is too far from the center
logger.debug("######################################## remove the line that is too far from the center")
angle_approved_dist_max_2 = angle_approved_dist_max.copy()
for (pt1, pt2) in angle_approved_dist_max_2:
logger.debug("##############################")
if len(angle_approved_dist_max) == 2:
break
dist_line2center = get_dist_line2center(center_x, pt1, pt2)
if dist_line2center > center_radius_thres:
del angle_approved_dist_max[angle_approved_dist_max.index((pt1, pt2))]
logger.debug(f"pt1:{
pt1}")
logger.debug(f"pt2:{
pt2}")
logger