Advertisement

cv2边缘检测 填充_JetBot AI计算机视觉自动驾驶机器人 -- 车道检测

阅读量:
2b3f0aa0b72c780839d8502f11d17e06.png

通常,汽车道路上都会有白色的分道线,汽车在路上行驶时,必须在车道内行驶,这样才能保持道路秩序;想要自动驾驶,也就需要车道识别,才能让汽车沿着车道行驶。传统的车道识别通过摄像头实时采集道路信息,通过计算机视觉处理,来识别出车道。

具体的做法是,摄像头读入一帧图像,对图像进行畸变校正,变成黑白图片,高斯降噪,然后用二值化边缘检测(canny)或 sobel,分析边缘信息,得到车道信息(角度、是否偏离车道等)。

下面描述 jetbot 下,使用Python 和 OpenCV 做车道检测的具体过程:

  1. 相机校准
  2. 图像采集
  3. 边缘检测
  4. 矩形二维映射
  5. 车道识别与拟合

相机校准

我们知道,摄像头特别是广角摄像头,拍摄的照片通常都会有畸变(桶形畸变、枕形畸变),这样在通过摄像头采集的图片就需要校正,以修正失真。通常的校准方法是通过采集摄像头的棋盘格拍摄图像,然后通过OpenCV进行计算校准。具体算法和原理,大家可以网上搜。

准备图片
258321a44ba308a0bf293e4abd3ad777.png

如上图所示的棋盘格图片,下载后打印到A4纸上,固定贴到墙上或者贴到硬板上,然后按下图的位置,拍摄20张图片,(不同角度、不同位置)

这里代码中提供了独立的摄像头采集图片代码:

复制代码
    python tegra_cam.py --width 320 --height 240 --path cap_imgs

每按一次's'键就会保存一张图片。
b6654dad2d59efd4ddd94c7c62bc843a.png

准备好图片后,就可以运行代码,进行校准了;

复制代码
    python calibrate_camera.py --imgpath cal_imgs --imgfile calibration.jpg

这里假设图片保存在运行代码的cal_imgs目录,图片以calibration1-20.jpg命名。运行后会保存校准参数文件到camera_cal320-240.p中,这里的320 240是之前采集的图片文件的分辨率。

车道检测

图像采集:
c7a97662b5979824d1f1a44ce39ce3cb.png

校正图片:

复制代码
     image=cv2.imread(image_file)

    
 	height = image.shape[0]
    
 	width = image.shape[1]
    
  #读入校准参数
    
 	cal_file = 'ncamera_cal' + str(width) + '-' + str(height) + '.p'
    
 	with open(cal_file, 'rb') as f:
    
 	# with open('camera_cal640-480.p', 'rb') as f:
    
 		save_dict = pickle.load(f)
    
 	mtx = save_dict['mtx']
    
 	dist = save_dict['dist']
    
  #校准图片
    
 	undis_image = cv2.undistort(image, mtx, dist, None, mtx)
c7a97662b5979824d1f1a44ce39ce3cb.png

二值化边缘检测:

复制代码
 canny_image = canny( undis_image )

    
  
    
 def canny( image ):
    
 	gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    
 	#双边滤波,平滑去噪的同时很好得保存边沿
    
 	# blur = cv2.bilateralFilter(gray, 11, 17,17)
    
 	blur = cv2.GaussianBlur(gray, (5,5), 0)
    
 	canny = cv2.Canny(blur, 25, 150)
    
 	return canny
841f238c44329b259b3a68e0ddba094f.png

二维映射:

我们知道,车道是一个长条的矩形,在图片中,按照成像原理,会有会聚效应,看着是个梯型,这样想要准确还原车道的角度,就需要做二维映射,还原,opencv提供了很好的映射算法。

复制代码
  m, m_inv, src = transform_matrix_640()

    
 	if width == 320:
    
 		m, m_inv, src = transform_matrix_320()
    
  
    
 	wraped_image = cv2.warpPerspective(canny_image, m, (width, height), flags=cv2.INTER_LINEAR)
    
  
    
  
    
  
    
 def transform_matrix_320():
    
 	src = np.float32(
    
 		[[0, 239],
    
 		[82,120],  #196,160
    
 		[237,120], #443, 160 223
    
 		[319, 239]])
    
  
    
 	dst = np.float32(
    
 		[[50, 239],
    
 		[50, 0],
    
 		[269, 0],
    
 		[269, 239]])
    
  
    
 	# dst = np.float32(
    
 	# 	[[0, 239],
    
 	# 	[0, 0],
    
 	# 	[319, 0],
    
 	# 	[319, 239]])
    
  
    
 	m = cv2.getPerspectiveTransform(src, dst)
    
 	m_inv = cv2.getPerspectiveTransform(dst, src)
    
 	return m, m_inv, src

这里的转换矩阵是如何确定的呢,首先我们可以使用二值化边缘检测后的图片(确保采集时摄像头是正的,且居中)。象图中红线那样,确定四个角点即可。
bc3675d7af758ffe8e55f67f51edbc98.png
c2d599a0e6b771e838ebc28e23109c14.png

映射后得到的车道图片

车道检测:

上图中的二值图片,我们有几个方式得到车道的几何信息,比如:可以通过 cv2.HoughLinesP 来得到车道的几何信息。

这里我们通过点的二阶拟合的方式得到车道信息,首先需要知道车道点在哪里,采用直方图的方式沿x轴统计点数,在左右车道处会有峰值,得到起始点,在此起始点上,通过滑动窗口的方式,得到两个车道的所有点,然后通过二阶拟合,得到车道信息。
1f8f0d4a160eb9b994abc90d4091c395.png

复制代码
 def line_fit_with_image( image ):

    
 	width = image.shape[1]
    
 	height = image.shape[0]
    
 	# Take a histogram of the bottom half of the image
    
 	histogram = np.sum(image[200:,:], axis=0)  #height//2
    
 	midpoint = np.int(histogram.shape[0]/2)
    
 	leftx_base = np.argmax(histogram[0:midpoint])
    
 	rightx_base = np.argmax(histogram[midpoint:]) + midpoint
    
 	# Choose the number of sliding windows
    
 	nwindows = 8
    
 	# Set height of windows
    
 	window_height = np.int(height/nwindows)
    
 	# Identify the x and y positions of all nonzero pixels in the image
    
 	nonzero = image.nonzero()
    
 	nonzeroy = np.array(nonzero[0])
    
 	nonzerox = np.array(nonzero[1])
    
 	# Current positions to be updated for each window
    
 	leftx_current = leftx_base
    
 	rightx_current = rightx_base
    
 	# Set the width of the windows +/- margin
    
 	margin = int(width/16)
    
 	# Set minimum number of pixels found to recenter window
    
 	minpix = 15
    
 	# Create empty lists to receive left and right lane pixel indices
    
 	left_lane_inds = []
    
 	right_lane_inds = []
    
  
    
 	# Step through the windows one by one
    
 	for window in range(nwindows):
    
 		# Identify window boundaries in x and y (and right and left)
    
 		win_y_low =height - (window+1)*window_height
    
 		win_y_high =height - window*window_height
    
 		win_xleft_low = leftx_current - margin
    
 		win_xleft_high = leftx_current + margin
    
 		win_xright_low = rightx_current - margin
    
 		win_xright_high = rightx_current + margin
    
 		# Identify the nonzero pixels in x and y within the window
    
 		good_left_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) & (nonzerox >= win_xleft_low) & (nonzerox < win_xleft_high)).nonzero()[0]
    
 		good_right_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) & (nonzerox >= win_xright_low) & (nonzerox < win_xright_high)).nonzero()[0]
    
 		# Append these indices to the lists
    
 		left_lane_inds.append(good_left_inds)
    
 		right_lane_inds.append(good_right_inds)
    
 		# If you found > minpix pixels, recenter next window on their mean position
    
 		if len(good_left_inds) > minpix:
    
 			leftx_current = np.int(np.mean(nonzerox[good_left_inds]))
    
 		if len(good_right_inds) > minpix:
    
 			rightx_current = np.int(np.mean(nonzerox[good_right_inds]))
    
 	# Concatenate the arrays of indices
    
 	left_lane_inds = np.concatenate(left_lane_inds)
    
 	right_lane_inds = np.concatenate(right_lane_inds)
    
  
    
 	# Extract left and right line pixel positions
    
 	leftx = nonzerox[left_lane_inds]
    
 	lefty = nonzeroy[left_lane_inds]
    
 	rightx = nonzerox[right_lane_inds]
    
 	righty = nonzeroy[right_lane_inds]
    
  
    
 	if lefty is None or righty is None:
    
 		return None, 0
    
 	if len(lefty ) == 0 or len(righty ) == 0 :
    
 		return None, 0
    
 	line_image = np.zeros((height, width, 3), dtype=np.uint8)
    
 	# line_image = (np.dstack((image, image, image))*255).astype('uint8')
    
 	window_img = np.zeros_like(line_image)
    
  
    
  
    
 #二阶拟合,求切线
    
 	left_line, left_fit, left_x0, kl  = line_of_poly( leftx, lefty,   int(height-height/4-1),  height -1, int( height / 6)   )  # x = ay^2 + by + c
    
 	right_line, right_fit, right_x0, kr  = line_of_poly( rightx, righty,  int(height-height/4-1), height-1, int(height/6)  )
    
  
    
 #中间目标线
    
 	k0 = (kl + kr)/2
    
 	x0 = (left_x0 + right_x0)/2
    
 	y0 = height - 1
    
 	y1 =  int(height/6) 
    
 	x1 = k0 * (y1 - y0) + x0
    
 	avg_theta = math.atan2( y1 - y0, x1-x0 )
    
 	x0 = int(x0)
    
 	x1 = int( x1 )
    
 	goal_line = np.array([x0, y0, x1, y1 ])
    
 	
    
 	# Color in left and right line pixels
    
 	line_image[nonzeroy[left_lane_inds], nonzerox[left_lane_inds]] = [234, 217, 53]
    
 	line_image[nonzeroy[right_lane_inds], nonzerox[right_lane_inds]] = [234, 217, 53]
    
  
    
 	cnt = int(height / 10)
    
 	ploty = np.linspace(0,  height -1, cnt  )
    
 	left_fitx = left_fit[0]*ploty**2 + left_fit[1]*ploty + left_fit[2]
    
 	right_fitx = right_fit[0]*ploty**2 + right_fit[1]*ploty + right_fit[2]
    
 	for i in range(cnt - 1) :
    
 		xl0 = int( left_fitx[i])
    
 		xr0 = int(right_fitx[i])
    
 		y0 = int(ploty[i])
    
 		xl1 = int(left_fitx[i + 1])
    
 		xr1 = int(right_fitx[i+1])
    
 		y1 = int(ploty[i+1])
    
 		cv2.line( line_image, (xl0,y0), (xl1, y1), (76, 177, 34), 1 )
    
 		cv2.line( line_image, (xr0,y0), (xr1, y1), (76, 177, 34),  1  )
    
 	# x1,y1,x2,y2 =  lines[0].reshape(4)
    
 	# cv2.line( line_image, (x1,y1), (x2, y2), (0, 0, 255), 2 )
    
 	# x1,y1,x2,y2 =  lines[1].reshape(4)
    
 	# cv2.line( line_image, (x1,y1), (x2, y2), (0, 0, 255), 2 )
    
 	x1,y1,x2,y2 =  goal_line.reshape(4)
    
 	cv2.line( line_image, (x1,y1), (x2, y2), (36, 28, 237), 3 )
    
  
    
 	left_line_window1 = np.array([np.transpose(np.vstack([left_fitx-margin, ploty]))])
    
 	left_line_window2 = np.array([np.flipud(np.transpose(np.vstack([left_fitx+margin, ploty])))])
    
 	left_line_pts = np.hstack((left_line_window1, left_line_window2))
    
 	right_line_window1 = np.array([np.transpose(np.vstack([right_fitx-margin, ploty]))])
    
 	right_line_window2 = np.array([np.flipud(np.transpose(np.vstack([right_fitx+margin, ploty])))])
    
 	right_line_pts = np.hstack((right_line_window1, right_line_window2))
    
  
    
 	# Draw the lane onto the warped blank image
    
 	cv2.fillPoly(window_img, np.int_([left_line_pts]), (29,230, 181))
    
 	cv2.fillPoly(window_img, np.int_([right_line_pts]), (29,230, 181))
    
 	line_image = cv2.addWeighted(line_image, 1, window_img, 0.3, 0)
    
 	d_center =int( width/2 - (left_x0 + right_x0)/2)
    
 	return line_image, avg_theta, d_center
1a88a7ac2b3d5ef34f46faea809af0d0.png

控制计算,还原:
8b212a64a7f63e2ecbbc49e56ffe0c63.png

代码:

https://github.com/zhongmc/lane-pilot

全部评论 (0)

还没有任何评论哟~