点击下方卡片,关注“OpenCV与AI深度学习”
视觉/图像重磅干货,第一时间送达!
背景介绍
畸变校正 透视变换 Sobel滤波 直方图峰值检测 滑动窗口搜索 曲线拟合 覆盖检测车道 应用于视频
畸变矫正
相机镜头扭曲入射光以将其聚焦在相机传感器上。尽管这对于我们捕捉环境图像非常有用,但它们最终往往会稍微不准确地扭曲光线。这可能导致计算机视觉应用中的测量不准确。然而,我们可以很容易地纠正这种失真。 我们可以使用棋盘格来标定相机然后做畸变校正:
畸变矫正
测试视频中使用的相机用于拍摄棋盘格的 20 张照片,用于生成畸变模型。我们首先将图像转换为灰度,然后应用cv2.findChessboardCorners()函数。我们已经知道这个棋盘是一个只有直线的二维对象,所以我们可以对检测到的角应用一些变换来正确对齐它们。用 cv2.CalibrateCamera() 来获取畸变系数和相机矩阵。相机已校准!
然后,您可以使用它cv2.undistort()来矫正其余的输入数据。您可以在下面看到棋盘的原始图像和校正后的图像之间的差异:
实现代码:
def undistort_img():
# Prepare object points 0,0,0 ... 8,5,0
obj_pts = np.zeros((6*9,3), np.float32)
obj_pts[:,:2] = np.mgrid[0:9, 0:6].T.reshape(-1,2)
# Stores all object points & img points from all images
objpoints = []
imgpoints = []
# Get directory for all calibration images
images = glob.glob('camera_cal/*.jpg')
for indx, fname in enumerate(images):
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, (9,6), None)
if ret == True:
objpoints.append(obj_pts)
imgpoints.append(corners)
# Test undistortion on img
img_size = (img.shape[1], img.shape[0])
# Calibrate camera
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img_size, None,None)
dst = cv2.undistort(img, mtx, dist, None, mtx)
# Save camera calibration for later use
dist_pickle = {}
dist_pickle['mtx'] = mtx
dist_pickle['dist'] = dist
pickle.dump( dist_pickle, open('camera_cal/cal_pickle.p', 'wb') )
def undistort(img, cal_dir='camera_cal/cal_pickle.p'):
#cv2.imwrite('camera_cal/test_cal.jpg', dst)
with open(cal_dir, mode='rb') as f:
file = pickle.load(f) mtx = file['mtx']
dist = file['dist']
dst = cv2.undistort(img, mtx, dist, None, mtx)
return dst
undistort_img()
img = cv2.imread('camera_cal/calibration1.jpg')
dst = undistort(img) # Undistorted image
这是应用于道路图像的失真校正。您可能无法注意到细微的差异,但它会对图像处理产生巨大影响。
透视变换
在相机空间中检测弯曲车道并不是很容易。如果我们想鸟瞰车道怎么办?这可以通过对图像应用透视变换来完成。这是它的样子:
透视变换
注意到什么了吗?通过假设车道位于平坦的 2D 表面上,我们可以拟合一个多项式,该多项式可以准确地表示车道空间中的车道!这不是很酷吗?
您可以使用cv2.getPerspectiveTransform()函数将这些变换应用于任何图像,以获取变换矩阵,并将cv2.warpPerspective()其应用于图像。下面是代码:
def perspective_warp(img,
dst_size=(1280,720),
src=np.float32([(0.43,0.65),(0.58,0.65),(0.1,1),(1,1)]),
dst=np.float32([(0,0), (1, 0), (0,1), (1,1)])):
img_size = np.float32([(img.shape[1],img.shape[0])])
src = src* img_size
# For destination points, I'm arbitrarily choosing some points to be
# a nice fit for displaying our warped result
# again, not exact, but close enough for our purposes
dst = dst * np.float32(dst_size)
# Given src and dst points, calculate the perspective transform matrix
M = cv2.getPerspectiveTransform(src, dst)
# Warp the image using OpenCV warpPerspective()
warped = cv2.warpPerspective(img, M, dst_size)
return warped
Sobel滤波
在之前的版本中,我使用颜色过滤掉了车道线。然而,这并不总是最好的选择。如果道路使用浅色混凝土代替沥青,道路很容易通过彩色滤光片,管道会将其感知为白色车道线,此方法不够稳健。
Sobel滤波
相反,我们可以使用类似于边缘检测器的方法,这次过滤掉道路。车道线通常与道路具有高对比度,因此我们可以利用这一点。之前版本 1 中使用的Canny边缘检测器利用Sobel 算子来获取图像函数的梯度。OpenCV 文档对它的工作原理有很好的解释。我们将使用它来检测高对比度区域以过滤车道标记并忽略道路。
我们仍将再次使用 HLS 色彩空间,这一次是为了检测饱和度和亮度的变化。sobel 算子应用于这两个通道,我们提取相对于 x 轴的梯度,并将通过梯度阈值的像素添加到表示图像中像素的二进制矩阵中。这是它在相机空间和车道空间中的样子:
直方图峰值检测
我们将应用一种称为滑动窗口算法的特殊算法来检测我们的车道线。但是,在我们应用它之前,我们需要为算法确定一个好的起点。如果它从存在车道像素的位置开始,它会很好地工作,但是我们如何首先检测这些车道像素的位置呢?其实很简单!
直方图峰值检测
我们将获得图像相对于 X 轴的直方图。下面直方图的每个部分都显示了图像每列中有多少个白色像素。然后我们取图像每一侧的最高峰,每条车道线一个。这是直方图的样子,在二值图像旁边:
完整代码:
https://github.com/kemfic/Curved-Lane-Lines/blob/master/P4.ipynb
参考链接:
https://www.hackster.io/kemfic/curved-lane-detection-34f771
下载1:Pytorch常用函数手册
CV相关内容讨论交流欢迎加入微信交流群!