计算机视觉处理:OpenCV基础教学:车道线检测实战(一):透视变换技术详解
OpenCV车道线检测实战(一):透视变换技术详解
车道线检测是自动驾驶和辅助驾驶系统中的基础技术,它能够帮助车辆识别道路边界,实现车道保持和车道偏离预警等功能。在这个系列文章中,我们将通过四个部分完整实现一个车道线检测系统。今天先讲解第一部分——透视变换技术。
一、透视变换在车道线检测中的作用
1.1 为什么需要透视变换?
在普通相机拍摄的图像中,由于透视效应,原本平行的车道线在图像中会交汇于一点(消失点)。这种透视效果会给车道线检测带来困难,因为:
- 车道线不是平行线,难以用统一的数学模型处理
- 距离相机越远,车道线像素越密集,难以准确提取
透视变换可以将图像从前视视角转换为鸟瞰视角(俯视图),这样:
- 原本交汇的车道线变成平行线
- 车道线的曲率变化更加直观
- 便于后续的车道线提取和拟合
二、透视变换实现步骤详解
下面我们将一步步实现透视变换功能,让你清楚地了解每个步骤的作用。
2.1 获取图像尺寸
首先,我们需要知道输入图像的尺寸,以便正确设置变换参数:
def perspective_transform(image):
# 获取图像的宽度和高度
# image.shape[1] 是宽度,image.shape[0] 是高度
image_size = (image.shape[1], image.shape[0])
代码解释:
image.shape返回一个三元组(高度, 宽度, 通道数)image.shape[1]获取图像的宽度image.shape[0]获取图像的高度- 我们将尺寸存储在
image_size变量中,格式为(宽度, 高度)
2.2 定义源点(原始图像中的梯形区域)
源点是原始图像中我们想要变换的梯形区域的四个顶点。这个梯形应该覆盖我们感兴趣的道路区域:
src = np.float32(
[[80, image_size[1]], # 左下点
[450, image_size[1]], # 右下点
[image_size[0] // 2 + 40, image_size[1] // 2 - 20], # 右上点
[image_size[0] // 2 - 40, image_size[1] // 2 - 20]] # 左上点
)
代码解释:
np.float32():创建32位浮点数数组,OpenCV的几何变换函数需要这种数据类型- 左下点
[80, image_size[1]]:图像底部左侧的车道线起点 - 右下点
[450, image_size[1]]:图像底部右侧的车道线起点 - 右上点
[image_size[0]//2+40, image_size[1]//2-20]:图像中上部右侧的车道线终点 - 左上点
[image_size[0]//2-40, image_size[1]//2-20]:图像中上部左侧的车道线终点
这四个点构成了一个梯形区域,代表我们关心的道路部分。
2.3 定义目标点(变换后的矩形区域)
目标点是鸟瞰图中对应的矩形区域的四个顶点:
dst = np.float32(
[[image_size[0] / 4, image_size[1]], # 左下点
[image_size[0] * 3 / 4, image_size[1]], # 右下点
[image_size[0] * 3 / 4, 0], # 右上点
[image_size[0] / 4, 0]] # 左上点
)
代码解释:
- 左下点
[image_size[0]/4, image_size[1]]:变换后图像的左下角,x坐标为图像宽度的1/4 - 右下点
[image_size[0]*3/4, image_size[1]]:变换后图像的右下角,x坐标为图像宽度的3/4 - 右上点
[image_size[0]*3/4, 0]:变换后图像的右上角,y坐标为0(图像顶部) - 左上点
[image_size[0]/4, 0]:变换后图像的左上角
这样定义的目标点确保变换后的车道线是平行的垂直线。
2.4 计算透视变换矩阵
有了源点和目标点,我们可以计算透视变换矩阵:
# 获取透视变换矩阵
M = cv2.getPerspectiveTransform(src, dst)
# 获取逆透视变换的矩阵
minv = cv2.getPerspectiveTransform(dst, src)
代码解释:
cv2.getPerspectiveTransform(src, dst):计算从源点到目标点的透视变换矩阵cv2.getPerspectiveTransform(dst, src):计算从目标点到源点的逆透视变换矩阵- 为什么需要两个矩阵?
M:将原始图像变换为鸟瞰图(用于车道线检测)minv:将鸟瞰图变换回原始视角(用于将检测结果叠加到原始图像)
2.5 执行透视变换
使用计算得到的变换矩阵对图像进行透视变换:
# 调用函数进行透视变换
image_warp = cv2.warpPerspective(image, M, image_size, flags=cv2.INTER_LINEAR)
return image_warp, minv
代码解释:
cv2.warpPerspective():执行透视变换- 参数说明:
image:输入图像M:3×3的透视变换矩阵image_size:输出图像的大小(宽度, 高度)flags=cv2.INTER_LINEAR:使用线性插值方法
- 函数返回变换后的图像和逆变换矩阵
三、完整透视变换代码
将上述步骤整合,得到完整的透视变换函数:
import cv2
import numpy as np
def perspective_transform(image):
"""
对输入图像进行透视变换,将前视图转换为鸟瞰图
参数:
image: 输入图像(BGR格式)
返回:
image_warp: 透视变换后的图像(鸟瞰图)
minv: 逆透视变换矩阵
"""
# 获取图像尺寸:宽度和高度
image_size = (image.shape[1], image.shape[0])
# 定义源点(原始图像中的梯形区域)
src = np.float32(
[[80, image_size[1]], # 左下点
[450, image_size[1]], # 右下点
[image_size[0] // 2 + 40, image_size[1] // 2 - 20], # 右上点
[image_size[0] // 2 - 40, image_size[1] // 2 - 20]] # 左上点
)
# 定义目标点(变换后的矩形区域)
dst = np.float32(
[[image_size[0] / 4, image_size[1]], # 左下点
[image_size[0] * 3 / 4, image_size[1]], # 右下点
[image_size[0] * 3 / 4, 0], # 右上点
[image_size[0] / 4, 0]] # 左上点
)
# 获取透视变换矩阵
M = cv2.getPerspectiveTransform(src, dst)
# 获取逆透视变换矩阵
minv = cv2.getPerspectiveTransform(dst, src)
# 执行透视变换
image_warp = cv2.warpPerspective(image, M, image_size, flags=cv2.INTER_LINEAR)
return image_warp, minv
# 测试代码
if __name__ == "__main__":
# 读取测试图像
image = cv2.imread('./test_image.png')
if image is not None:
# 执行透视变换
warped_image, inverse_matrix = perspective_transform(image)
# 显示结果
cv2.imshow('原始图像', image)
cv2.imshow('鸟瞰图', warped_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print("无法读取图像,请检查文件路径")
四、关键OpenCV函数详解
4.1 cv2.getPerspectiveTransform()
cv2.getPerspectiveTransform(src, dst)
功能:计算两个四边形之间的透视变换矩阵
参数:
src:源图像中四边形顶点的坐标,形状为(4, 2)的浮点数数组dst:目标图像中四边形顶点的坐标,形状为(4, 2)的浮点数数组
返回值:
- 一个3×3的透视变换矩阵
使用注意事项:
- 源点和目标点的顺序必须一致(都是顺时针或都是逆时针)
- 至少需要4个点对来计算变换矩阵
- 返回的矩阵是3×3的,因为透视变换在齐次坐标下表示
4.2 cv2.warpPerspective()
cv2.warpPerspective(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]])
功能:对图像进行透视变换
参数说明:
src:输入图像,可以是单通道或多通道M:3×3的透视变换矩阵dsize:输出图像的大小,格式为(宽度, 高度)flags:插值方法,常用的有:cv2.INTER_LINEAR:线性插值(默认)cv2.INTER_NEAREST:最近邻插值cv2.INTER_CUBIC:三次样条插值
borderMode:边界像素处理方式borderValue:当borderMode为cv2.BORDER_CONSTANT时的边界填充值
返回值:
- 变换后的图像
工作流程:
- 对于输出图像中的每个像素点
(x', y') - 计算其在输入图像中的对应位置
(x, y) - 使用插值方法计算
(x, y)处的像素值 - 将该像素值赋给输出图像的
(x', y')位置
五、透视变换的数学原理
可以看up之前写的
计算机视觉处理(OpenCV基础教学(十二):图像透视变换基础)
5.1 基本概念
透视变换是射影几何中的一种变换,用于描述三维空间中的物体在二维平面上的投影。在图像处理中,它可以将一个视角下的图像变换到另一个视角。
5.2 变换公式
透视变换可以用一个3×3的矩阵表示:
| x' | | a11 a12 a13 | | x |
| y' | = | a21 a22 a23 | * | y |
| w' | | a31 a32 1 | | 1 |
其中:
(x, y)是原始图像中的点(齐次坐标(x, y, 1))(x', y', w')是变换后的齐次坐标- 实际图像坐标:
(x'/w', y'/w')
5.3 计算变换矩阵
给定4个点对(xi, yi) ↔ (xi', yi'),可以建立8个方程:
xi' = (a11*xi + a12*yi + a13) / (a31*xi + a32*yi + 1)
yi' = (a21*xi + a22*yi + a23) / (a31*xi + a32*yi + 1)
通过解这个方程组,可以求出8个未知数a11, a12, a13, a21, a22, a23, a31, a32。
总结
透视变换是车道线检测系统中的关键预处理步骤,它将前视道路图像转换为鸟瞰图,为后续的车道线提取和拟合奠定基础。通过本文的学习,你应该掌握:
- 透视变换的基本原理:理解为什么需要将前视图转换为鸟瞰图
- 实现步骤:从获取图像尺寸到执行变换的完整流程
- 关键函数用法:
cv2.getPerspectiveTransform()和cv2.warpPerspective()的使用方法 - 代码实现:能够独立实现透视变换功能
在实际的车道线检测系统中,透视变换参数需要根据具体的相机安装位置和道路情况进行调整。一个好的透视变换应该能够将车道线转换为近似平行的直线,便于后续处理。
下一篇预告:在下一篇文章中,我们将讲解如何从鸟瞰图中提取车道线,包括基于梯度的方法和基于颜色的方法。我们将详细介绍Sobel算子、颜色空间转换等关键技术,敬请期待!
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)