(一)实现流程
今天写一个车道线检测,本来打算写两种的,但是另一种比较麻烦且效果不咋地,所以就写这个了。
下面先附上车道线的检测结果视频:
传送门
PS:下面的程序也是借鉴了一些网上现有的例子
下面来写程序的实现流程
这里展示了处理过程中的一些图像,程序就不一一解读了,都有注释。
import cv2
import numpy as np
import matplotlib.pyplot as plt
cap=cv2.VideoCapture('3.mp4')
while True:
success,img=cap.read()
#这几步可以理解为预处理
#########################################################################
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
#clahe=cv2.createCLAHE(clipLimit=2.0,tileGridSize=(8,8))#这是自适应均衡化
#gray=clahe.apply(gray)
blur = cv2.GaussianBlur(gray, (5,5), 1)
#edges = cv2.Canny(blur, 50, 150)#这里没有选择边缘检测
#########################################################################
# 设置四个坐标点用于ROI截取,去除干扰信息(这四个点很重要)
#########################################################################
y, x = blur.shape
#print(y,x)
points = np.array([[(257, 238),(0,y), (x, y), (400, 238) ]])#这四个点需要具体修改
mask = np.zeros_like(blur)
cv2.fillPoly(mask, points, 255)
roi = cv2.bitwise_and(blur,blur, mask=mask)
cv2.imshow('roi',roi)
#########################################################################
ret,th1=cv2.threshold(roi,135,225,cv2.THRESH_BINARY)#图像二值化,这里的值需要视情况修改
#这里生成投射变换的矩阵与逆矩阵
##############################################################################
height,width = img.shape[:2] #这里是预设提取出的图片大小
pts1 = np.float32([[(257, 238), (400, 238),(0,y), (x, y) ]]) #这里是提取目标的四个坐标点
pts2 = np.float32([[0,0],[width,0],[0,height],[width,height]]) #这里是定义上面四个点在像新窗口中的对应位置
matrix = cv2.getPerspectiveTransform(pts1,pts2) #这里是通过cv2.getPerspectiveTransform得到变换矩阵,等一会传入cv2.warpPerspective函数
matrix_inv = cv2.getPerspectiveTransform(pts2,pts1)#获得上面透射变换的逆矩阵
imgOutput = cv2.warpPerspective(th1,matrix,(width,height))
##############################################################################
#cv2.imshow('th1',th1)
#cv2.imshow('roi',roi)
#cv2.imshow('imgOutput',imgOutput)
hist = np.sum(imgOutput, axis=0)#统计图片信息
#plt.plot(hist)
#plt.show()
# 在统计结果中找到左右最大的点的位置,作为左右车道检测的开始点
# 将统计结果一分为二,划分为左右两个部分,分别定位峰值位置,即为两条车道的搜索位置
middle = np.int(hist.shape[0] / 2)
leftx_starting = np.argmax(hist[:middle])
rightx_starting = np.argmax(hist[middle:]) + middle
# 设置窗口检测车道线
# 设置滑动窗口的数量,计算每一个窗口的高度
window_height = imgOutput.shape[0]
# 获取图像中不为0的点
nonzero = imgOutput.nonzero()
nonzeroy = np.array(nonzero[0])
nonzerox = np.array(nonzero[1])
cv2.imshow('imgOutput',imgOutput)
# 设置x的检测范围,设置检测半径为100
radius = 100
# 设置窗口的y的检测范围,因为图像是(行列),shape[0]表示y方向的结果,上面是0
win_y_low = 0
win_y_high = imgOutput.shape[0]
# 左车道x的范围
left_min = leftx_starting - radius
left_max = leftx_starting + radius
# 右车道x的范围
right_min = rightx_starting - radius
right_max = rightx_starting + radius
# 确定非零点的位置x,y是否在搜索窗口中,将在搜索窗口内的x,y的索引存入left_inds和right_inds中
left_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) &
(nonzerox >= left_min) & (nonzerox < left_max)).nonzero()[0]
right_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) &
(nonzerox >= right_min) & (nonzerox < right_max)).nonzero()[0]
# 获取检测出的左右车道点在图像中的位置
left_x = nonzerox[left_inds]
left_y = nonzeroy[left_inds]
right_x = nonzerox[right_inds]
right_y = nonzeroy[right_inds]
#用曲线拟合检测出的点,二次多项式拟合
left_fit = np.polyfit(left_y, left_x, 2)
right_fit = np.polyfit(right_y, right_x, 2)
# 设置输出图像的大小,将imgOutput进行堆叠成三通道的图片
out_img = np.dstack((imgOutput, imgOutput, imgOutput))
# 在拟合曲线中获取左右车道线的像素位置
y_max = imgOutput.shape[0]
left_points = [[left_fit[0] * y ** 2 + left_fit[1] * y + left_fit[2], y] for y in range(y_max)]
right_points = [[right_fit[0] * y ** 2 + right_fit[1] * y + right_fit[2], y] for y in range(y_max - 1, -1, -1)]
# 将左右车道的像素点进行合并
line_points = np.concatenate((left_points, right_points))
# 根据左右车道线的像素位置绘制多边形
out_img=cv2.fillPoly(out_img, np.int_([line_points]), (0, 255, 0))
#这里绘制像素点的拟合范围
cv2.rectangle(out_img,(left_min,0),(left_max,win_y_high),(0,0,255),3)
cv2.rectangle(out_img, (right_min, 0), (right_max, win_y_high), (0, 0, 255), 3)
cv2.imshow('out_img',out_img)
#这里将处理过的透射变换图像通过上面的透射变换的逆矩阵返回成原来的样子
transform_img_inv=cv2.warpPerspective(out_img, matrix_inv,(width,height))
#将原图与处理后的图像进行叠加
result = cv2.addWeighted(img, 1, transform_img_inv, 0.5, 0)
cv2.imshow('result', result)
if cv2.waitKey(0)& 0xFF==ord('q'):
break
PS:跑程序时需按住键盘上的“ESC”键或空格键,回车键。因为当时为了调试程序。如果小伙伴们不想这样,只需将程序倒数第二行cv2.waitkey括号里的0改为1即可
(二)备注
下面是上面程序中用到的几个函数用法
np.sum的用法
np.nonzero的用法
np.concatenate的用法
(三)结语
学习opencv有很多的方法,我的建议是你可以加一些群,可以充分利用B站,CSDN,和百度。
在我的博客中,我不会讲解opencv的算法实现(当然我也不太会),我只会讲解一些函数的调用,不理解就多改一些参数,多尝试尝试,慢慢你就理解来。相信你总有一天可以说opencv不过“Ctrl+C,Crtl+V”
如果有什么错误的地方,还请大家批评指正,最后,希望小伙伴们都能有所收获。