Canny边缘检测算法可以分为以下5个步骤:
通过图像像素变化的梯度来确定这个点是否是边缘
双阈值
一般的边缘检测算法用一个阀值来滤除噪声或颜色变化引起的小的梯度值,而保留大的梯度值。Canny算法应用双阀值,即一个高阀值和一个低阀值来区分边缘像素。如果边缘像素点梯度值大于高阀值,则被认为是强边缘点。如果边缘梯度值小于高阀值,大于低阀值,则标记为弱边缘点。小于低阀值的点则被抑制掉。
如果弱边缘点与强边缘线相连,则我们认为该点是边缘,如C点,否则不是边缘,如B点。
import cv2
import numpy as np
# 得到灰度图
img = cv2.imread("images/2.png", cv2.IMREAD_GRAYSCALE)
# Canny边缘检测
edge_img = cv2.Canny(img, 50, 100)
# ROI_mask感兴趣区域检测
mask = np.zeros_like(edge_img)
roi_mask = cv2.fillPoly(mask, np.array([[[439, 290], [588, 283], [130, 510], [897, 530]]]), color=255)
roi_mask_img = cv2.bitwise_and(edge_img, roi_mask)
# 霍夫变换(提取所有线段)
lines = cv2.HoughLinesP(roi_mask_img, 1, np.pi / 180, 15, minLineLength=40, maxLineGap=20)
print(type(lines))
# 计算线段斜率
def calculate_slope(line):
x_1, y_1, x_2, y_2 = line[0, :]
return (y_2 - y_1) / (x_2 - x_1)
# 按照斜率划分车道线
left_lines = [line for line in lines if calculate_slope(line) > 0]
right_lines = [line for line in lines if calculate_slope(line) < 0]
print("去除斜率不一致的线段前的数组长度:{},{}".format(len(left_lines), len(right_lines)))
# 去除斜率不一致的线段
def reject_abnormal_lines(lines, threshold):
slopes = [calculate_slope(line) for line in lines]
while len(lines) > 0:
mean = np.mean(slopes)
diff = [abs(s - mean) for s in slopes]
idx = np.argmax(diff)
if diff[idx] > threshold:
slopes.pop(idx)
lines.pop(idx)
else:
break
return lines
left_lines_reject = reject_abnormal_lines(left_lines, threshold=0.2)
right_lines_reject = reject_abnormal_lines(right_lines, threshold=0.2)
print("去除斜率不一致的线段后的数组长度:{},{}".format(len(left_lines_reject), len(right_lines_reject)))
# 将lines中的线段拟合成一条直线
def least_squares_fit(lines):
# 1、取出所有坐标点
x_coords = np.ravel([[line[0][0], line[0][2]] for line in lines])
y_coords = np.ravel([[line[0][1], line[0][3]] for line in lines])
# 2、 进行直线拟合,得到多项式系数
poly = np.polyfit(x_coords, y_coords, deg=1)
# 3、根据多项式系数,计算两条直线上的点,用于唯一确定这条直线
point_min = (np.min(x_coords), np.polyval(poly, np.min(x_coords)))
point_max = (np.max(x_coords), np.polyval(poly, np.max(x_coords)))
return np.array([point_min, point_max], dtype=np.int)
least_left = least_squares_fit(left_lines)
least_right = least_squares_fit(right_lines)
print("坐标left{}".format(least_left))
print("坐标right{}".format(least_right))
color_img = cv2.imread("images/1.png", cv2.IMREAD_COLOR)
cv2.line(color_img, tuple(least_left[0]), tuple(least_left[1]), color=(0, 255, 255), thickness=5)
cv2.line(color_img, tuple(least_right[0]), tuple(least_right[1]), color=(0, 255, 255), thickness=5)
cv2.imshow("roi_mask_img", color_img)
cv2.waitKey(0)
参考资料