irpas技术客

opencv入门项目——车道线检测_-素心向暖_opencv实现车道线检测

网络 1414

文章目录 Canny 边缘检测小程序 roi_mask理论实现 霍夫变换基本原理API实现 离群值过滤最小二乘拟合API实现 直线绘制API 视频流读写API实现

Canny 边缘检测 import cv2 img = cv2.imread('img.jpg', cv2.IMREAD_GRAYSCALE) edge_img = cv2.Canny(img, 50, 100) cv2.imshow('edges', edge_img) cv2.waitkey(0)

有小噪点,可通过调高阈值解决

edge_img = cv2.Canny(img, 70, 120) cv2.imshow('edges', edge_img) cv2.waitkey(0)

相比于之前,边缘明显减少

小程序 import cv2 cv2.namedWindow('edge_detection') cv2.createTrackbar('minThreshold', 'edge_detection', 50, 1000, lambda x: x) cv2.createTrackbar('maxThreshold', 'edge_detection', 100, 1000, lambda x: x) img = cv2.imread('img.jpg', cv2.IMREAD_GRAYSCALE) while True: minThreshold = cv2.getTrackbarPos('minThreshold', 'edge_detection') maxThreshold = cv2.getTrackbarPos('maxThreshold', 'edge_detection') edges = cv2.Canny(img, minThreshold, maxThreshold) cv2.imshow('edge_detection', edges) cv2.waitKey(10)

阈值增加,强边缘减少,随之也会导致弱边缘减少

roi_mask 理论

通过 Canny 边缘检测算法,获取了边缘信息。但还有很多无关信息,对于车道信息没有意义,还会造成干扰,所以要将其剔除。

在C++版本中有实现ROI这个函数 但在Python版本中,没有实现图片数据结构,而是将图片保存为 numpy 数组。 所以在python 版本中实现ROI,有以上两种方法。 数组切片,取出来的是个矩形区域

实现 ROI(region of interest,感兴趣的区域) 数组切片布尔运算(与运算) 图像以矩阵 np.array 形式存储在内存中 np.zeros_like cv2.fillPolycv2.bitwise_and / np.bitwise_and import cv2 import numpy as np edge_img = cv2.imread('edges_img.jpg', cv2.IMREAD_GRAYSCALE) mask = np.zeros_like(edge_img) cv2.fillPoly(mask, np.array([[[0 ,368], [240, 210], [300, 210], [640, 368]]], color=255)

注意输入点的顺序

masked_edge_img = cv2.bitwise_and(edge_img, mask) cv2.imshow('masked', masked_edge_img) cv2.waitKey(0)

生成图片只保留车道线所需要的信息,当然 ROI 这里没有提取好。

霍夫变换 基本原理

API

官方文档

实现 import cv2 import numpy as np img = np.zeros((200, 400)) cv2.line(img, (10, 10), (200, 100), 255, 3) cv2.line(img, (30, 50), (350, 10), 255, 2) cv2.imshow('img', img) cv2.imwrite('lines.jpg', img) cv2.waitKey(0)

霍夫变换只针对灰度图

import cv2 import numpy as np cv2.imread('lines.jpg', cv2.IMREAD_GRAYSCALE) lines = cv2.HoughLinesP(img, 1, np.pi/180, 15, minLineLength=40, maxLineGap=20) print(len(lines))

32

图像中只有两条直线,却找到32条直线。 这是因为画出的直线是有一定宽度的,在这两条直线之间,拟合出来来了很多直线

解决: 根据斜率进行分类 再通过最小二乘法拟合成一条直线

def calculate_slope(line): """ 计算线段line的斜率 :param line: np.array([[x_1, y_1, x_2, y_2]]) :return: """ x_1, y_1, x_2, y_2 = line[0] if x_1 == x_2: return (y_2-y_1) / (x_2-x_1+1) else: return (y_2-y_1) / (x_2-x_1) edge_img = cv2.imread('masked_edge_img.jpg', cv2.IMREAD_GRAYSCALE) # 获取所有线段 lines = cv2.HoughlinesP(edge_img, 1, np.pi/180, 15, minLineLength=40, maxLineGap=20) # 按照斜率分成车道线 left_lines = [line for line in lines if calculate_slope(line) > 0] right_lines = [line for line in lines if calculate_slope(line) < 0] 离群值过滤

因为误差,有些直线会被误识别,但他们可能不属于左直线和右直线。 通过特征,来排除。直线的特征就是斜率。 将与平均斜率相差太大的线排除。

def reject_abnormal_lines(lines, threshold): """ 剔除斜率不一致的线段 :param lines:线段集合,[np.array([[x_1, y_1, x_2, y_2]]), np.array([[x_1, y_1, x_2, y_2]]) """ 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 reject_abnormal_lines(left_lines, threshold=0.2) reject_abnormal_lines(right_lines, threshold=0.2)

最小二乘拟合 API np.ravel 将高维数组拉成一维np.polyfit 多项式拟合np.polyval 多项式求值 a = np.array([[2, 3], [4, 5]]) print(a)

a.ravel()

poly = np.polyfit([0, 3, 6, 9], [0, 5, 9, 14], deg=1) # deg 为几维矩阵 print(poly)

得到一个数对,两个参数

x = 0 print(poly[0] * x + poly[1]) np.polyval(poly, 10) # 上下两种形式一样 实现 def least_squares_fit(lines): """ 将lines中的线段拟合成一条线段 :param lines:线段集合,[np.array([[x_1, y_1, x_2, y_2]]), np.array([[x_1, y_1, x_2, y_2]]),...,np.array([[x_1, y_1, x_2, y_2]])] :return:线段上的两点,np.array([[xmin, ymin], [xmax, ymax]]) """ # 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) print("left lane") print(least_squares_fit(left_lines)) print("right lane") print(least_squares_fit(right_lines))

直线绘制 API 绘制直线:cv2.line img = cv2.imread('img.jpg', cv2.IMREAD_COLOR) cv2.line(img, tuple(left_line[0], tuple(left_line[1]), color=(0, 255, 255), thickness=5) cv2.line(img, tuple(right_line[0], tuple(left_line[1]), color=(0, 255, 255), thickness=5) cv2.imshow('lane', img) cv2.waitKey(0)

视频流读写 API

cv2.VideoCapture

capture.read

cv2.VideoWriter

writer.write

如果要用C++或python 处理 视频流的话,建议使用ffmpeg

实现 def show_lane(color_img): """ 在 color_img 上画出车道线 :param color_img:彩色图,channels=3 :return: """ edge_img = get_edge_img(color_img) mask_gray_img = roi_mask(edge_img) lines = get_lines(mask_gray_img) draw_lines(color_img, lines) return color_img capture = cv2.VideoCapture('video.mp4') # capture = cv2.VideoCapture(0) # 读取当前设备 while True: ret, frame = capture.read() # ret 表示当前图像是否关闭,frame 当前帧 frame = show_lane(frame) cv2.imshow('frame', frame) cv2.waitKey(10) # 可以表示当前视频帧率


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #opencv实现车道线检测 #roi #霍夫变换 #离群值过滤