一. 绪论
1.KITTI数据集简介
KITTI数据集由德国卡尔斯鲁厄理工学院和丰田美国技术研究院联合创办,是目前国际上最大的自动驾驶场景下的计算机视觉算法评测数据集。该数据集用于评测立体图像(stereo),光流(optical flow),视觉测距(visual odometry),3D物体检测(object detection)和3D跟踪(tracking)等计算机视觉技术在车载环境下的性能。KITTI包含市区、乡村和高速公路等场景采集的真实图像数据,每张图像中最多达15辆车和30个行人,还有各种程度的遮挡与截断。整个数据集由389对立体图像和光流图,39.2 km视觉测距序列以及超过200k 3D标注物体的图像组成[1] ,以10Hz的频率采样及同步。总体上看,原始数据集被分类为’Road’, ’City’, ’Residential’, ’Campus’ 和 ’Person’。对于3D物体检测,label细分为car, van, truck, pedestrian, pedestrian(sitting), cyclist, tram以及misc组成。
2. 项目介绍
该项目基于KITTI数据集及相应的传感器标定参数,通过在行车过程中的传感器数据融合,实现无人驾驶汽车感知模块中的目标检测跟踪、TTC碰撞检测、车道线检测、运动目标轨迹预测功能;
- 3D目标检测及跟踪:通过比较从传统机器学习SVM分类器,到当下无人车主流的深度学习模型YOLOv3,并验证在KITTI数据集上目标检测的表现
- TTC碰撞检测:应用BRISK/ORB/HARRIS等角点检测器和描述器,赋予目标检测框语义信息,实现前后帧的特征点跟踪,并通过KITTI数据集提供的传感器标定参数,将点云数据投射到2D图像,融合赋予特征点深度信息,利用CAM模型计算ROI区域碰撞时间TTC
- 车道线检测:基于传统视觉处理算法,从颜色空间和梯度空间提取车道线特征
3、项目目标
- 目标检测在KITTI数据集上mAP达到0.80以上
- TTC碰撞检测只针对车辆行驶方向上的ROI区域目标进行检测,单帧处理时间低于Lidar频率
- 车道线检测能够适应白天阴影、弯道,并实现偏离中心检测
4、最终效果图
5、逻辑框架图
6、具体操作流程
- Camera内参标定
- 图像梯度空间特征提取
- 图像颜色空间特征提取
- 颜色空间&梯度空间结合提取特征
- 高斯滤波
- 角点检测器——Harris方法
- 角点检测器——FAST方法
- 目标检测描述器
- 描述子特征匹配
- 多种检测器/描述器及匹配算法组合比较
- 应用机器学习SVM分类器赋予图像语义信息
- 应用YOLOv3进行目标检测
- Camera-Lidar外参标定,点云与2D目标检测融合赋予深度信息
- 将目标检测框设置为ROI区域并对点云进行筛选
- 利用检测器和描述器将连续图像中ROI区域的keypoint匹配起来
- 进行TTC碰撞检测
- 车道线检测
- 将车道线检测与目标检测、TTC计算融合输出
一、对Camera进行标定:
相机成像几何模型(原理) |
1、四大坐标系及目的
四大坐标系:世界坐标系(测量坐标系),相机坐标系,图像坐标系(胶卷坐标系,连续值),像素坐标系。
![94e2e7241126135a8525765d791c430c.png](https://i-blog.csdnimg.cn/blog_migrate/c7c50c1b368abff4163b0997189eee2d.png)
目的:用数学方式描述3D点如何投影到2D像素坐标系中(正投影:Forward projection),以及反过来的投影过程(Back projection)。
计算机视觉的首要任务就是要通过拍摄到的图像信息获取到物体在真实三维世界里相对应的信息,于是,建立物体从三维世界映射到相机成像平面这一过程中的几何模型就显得尤为重要。
另外,描述相机坐标系下的3D点投影到图像坐标系下2D点的过程称为透视投影(perspective projection)。其中,f为相机焦距,(X,Y,Z)为相机坐标系下某点的坐标,(x,y)为图像坐标系下与(X,Y,Z)对应的坐标。
2、从世界坐标系到相机坐标系的变换
世界坐标系:也称测量坐标系,它是一个三维直角坐标系。 在世界坐标系中,可以描述相机和待测物体的空间位置。而世界坐标系的位置根据实际情况自行确定。
相机坐标系:它也是一个三维直角坐标系()。 相机坐标系的原点是镜头的光心,x、y轴分别与像平面两边平行,z轴为镜头的光轴,与像平面垂直。
从世界坐标系到相机坐标系:刚体变换,也就是只改变物体的空间位置(平移)和朝向(旋转),而不改变物体的形状。 用旋转矩阵R和平移向量t可以表示这种变换。 在齐次坐标下,旋转矩阵R是正交矩阵,可通过Rodrigues变换转为只有三个独立变量的旋转向量。因此,刚体变换用6个参数就可以表示(3个旋转向量,3个平移向量),而这6个参数就作为相机的外参。 相机外参实现了空间点从世界坐标系到相机坐标系的变换。
![7ebcab7a63fec6eb19d17bd656226290.png](https://i-blog.csdnimg.cn/blog_migrate/3f6a042faca9b13887800862d5e4d9ff.png)
其中,R 是 ,是。
齐次坐标下,可以表示为:
![ac0a63e9041e5ef4aa653bc7a3126805.png](https://i-blog.csdnimg.cn/blog_migrate/6bf1c71f3d127f400f538505e5ced163.png)
3、从相机坐标系到图像坐标系的变换
图像坐标系:也叫平面坐标系。用物理单位表示像素的位置,单位是mm。坐标原点为相机光轴与成像平面的交点,通常情况下是成像平面的中点。
从相机坐标系到图像坐标系:属于透视投影关系,从3D转换到2D。
![f532ecf0a3c2e6cbfc8346801e9419e0.png](https://i-blog.csdnimg.cn/blog_migrate/74f8dfb12f7d21214f36f2184e325794.png)
根据相似三角形原理:
在齐次坐标下表示为:
![b0ea6ccf3ee42f031dadabd0e722448e.png](https://i-blog.csdnimg.cn/blog_migrate/748debd20929933c0752b14cbbc57d14.png)
其中,
为透视投影矩阵
这样就完成了相机坐标系到理想的图像坐标系的转换(我们默认各个坐标系的变换都是线性的),但实际上,相机镜头中的镜片由于光线的通过产生不规则的折射,总是存在镜头畸变的。畸变的引入使得成像模型中的几何变换关系为非线性。 畸变的类型很多,但通常只考虑径向畸变和切向畸变。 引入畸变之后,理想的图像坐标系到真实的图像坐标系的变换为:
其中, 和 为径向引起的畸变, 和 为切向引起的畸变。
径向畸变形成的原因:镜头本身的缺陷(制造工艺不完美)导致的。 包括枕形畸变和桶形畸变。
![b68a94325343f026083f733d4bf49863.png](https://i-blog.csdnimg.cn/blog_migrate/330e2d8f7aa202b05d67d9d4a58f3804.png)
从图中可以看出:离中心越远的地方,形变越明显(eg:四个角的位置)。 即:镜头的边缘形变更显著。 来张真实的效果图:
![96a9fdc63aa020b0c4b6d8114fea2ead.png](https://i-blog.csdnimg.cn/blog_migrate/cf73bfdda047e0cb10b586196dbf9222.png)
切向畸变:有薄透镜畸变和离心畸变等。薄透镜畸变形成的原因
: 透镜存在一定的细微倾斜。
离心畸变形成的原因
:镜头由多个透镜组合而成,而各透镜的光轴不在同一条中心线上。
4、从图像坐标系到像素坐标系的变换
从图像坐标系到像素坐标系:没有旋转,只是坐标原点和单位不一样。 图像坐标系坐标原点为相机光轴与成像平面的交点,单位是mm,属于物理单位。 像素坐标系坐标原点在左上角,以像素为单位,我们通常描述一个像素点是几行几列。 所以,两者之间的转换如下:
![b1748933391b0508f19ef4c56b2f4459.png](https://i-blog.csdnimg.cn/blog_migrate/9025f588bab497cfa6be0bdbd245dd93.png)
在其次坐标系下:
![ca8521c762f6b28d3748bedd2297f2a3.png](https://i-blog.csdnimg.cn/blog_migrate/8275c3639de188a4679c9f3f4e61f9f4.png)
import glob
#glob模块查找匹配特定表达式的所有路径名
import cv2
#cv2:计算机视觉库,含有图像处理和计算机视觉方面的通用算法
import numpy as np
import matplotlib.pyplot as plt
#plt:绘图函数,用于显示图片
import matplotlib.image as mpimg
#用于读取图片
#from PIL import Image
# 使用标定函数cv2.calibrateCamera()和去除畸变函数cv2.undistort()
def cal_undistort(img, objpoints, imgpoints):
undist = np.copy(img)
ret, mtx,dist,rvecs,tvecs = cv2.calibrateCamera(objpoints,imgpoints,undist.shape[:2],None,None)
undist = cv2.undistort(undist,mtx,dist,None,mtx)
return undist
# 建立objpoints 和 imgpoints空列表
objpoints = []
imgpoints = []
#画网格指定objpoints位置
objp = np.zeros((6*8,3), np.float32)
objp[:,:2] = np.mgrid[0:8,0:6].T.reshape(-1,2)
# 读入图片并转为灰度
fname = "work/test_picture/test_chessboard.jpg"
img = mpimg.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
#应用findchessboard函数找到图像角点
ret, corners = cv2.findChessboardCorners(gray,(8,6),None)
if ret == True:
#分别将角点和目标点位置写入列表,并将角点画出
imgpoints.append(corners)
objpoints.append(objp)
gray = cv2.drawChessboardCorners(gray,(8,6),corners,ret)
#对图片进行去除畸变
image = cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB)
image = cv2.drawChessboardCorners(image,(8,6),corners,ret)
undistorted = cal_undistort(image, objpoints, imgpoints)
#可视化
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(18, 9))
f.tight_layout()
ax1.imshow(img)
ax1.set_title('Original Image', fontsize=30)
ax2.imshow(undistorted)
ax2.set_title('Undistorted Image', fontsize=30)
plt.subplots_adjust(left=0., right=1, top=0.9, bottom=0.)
#plt.rcParams['savefig.dpi'] = 300 #图片像素
#plt.rcParams['figure.dpi'] = 300 #分辨率
plt.show()
![927ad05690824b648b919bb1b6e7540c.png](https://i-blog.csdnimg.cn/blog_migrate/680d9a9598cf4613a82535cbfd0fba3b.png)
可以看出图像中间点undistort效果比较好,边缘处还是存在变形,这是因为目标点objpoints范围未完全覆盖所有角点。
因此,也可以利用感知转换方程,人为地在图像中选点,并决定其目标点位,精确地对图片进行转换:
import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from PIL import Image
def undistort(img, objpoints, imgpoints):
undist = np.copy(img)
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, undist.shape[:2], None, None)
undist = cv2.undistort(undist,mtx,dist,None,mtx)
return undist
# 建立objpoints 和 imgpoints空列表
objpoints = []
imgpoints = []
#画网格指定objpoints位置
objp = np.zeros((6*8,3), np.float32)
objp[:,:2] = np.mgrid[0:8,0:6].T.reshape(-1,2)
# 读入图片并转为灰度
fname = "work/test_picture/test_chessboard.jpg"
nx = 8
ny = 6
#对图片边界的抵偿值
off_set = 100
img_size = (img.shape[1],img.shape[0])
gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
ret, corners = cv2.findChessboardCorners(gray,(nx,ny),None)
if ret == True:
#分别将角点和目标点位置写入列表,并将角点画出
imgpoints.append(corners)
objpoints.append(objp)
gray = cv2.drawChessboardCorners(gray,(8,6),corners,ret)
#对图片进行去除畸变
image = cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB)
image = cv2.drawChessboardCorners(image,(8,6),corners,ret)
undistorted = cal_undistort(image, objpoints, imgpoints)
src = np.float32([corners[0],corners[nx-1],corners[-1],corners[-nx]])
dst = np.float32([[off_set,off_set],[img_size[0]-off_set,off_set],[img_size[0]-off_set,img_size[1]-off_set],[off_set,img_size[1]-off_set]])
M = cv2.getPerspectiveTransform(src,dst)
warped = cv2.warpPerspective(undistorted, M, img_size,flags=cv2.INTER_LINEAR)
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(18, 9))
f.tight_layout()
ax1.imshow(img)
ax1.set_title('Original Image', fontsize=30)
ax2.imshow(warped)
ax2.set_title('Undistorted and Warped Image', fontsize=30)
plt.subplots_adjust(left=0., right=1, top=0.9, bottom=0.)
plt.show()
![9f60f98c7369d643f892a61fc0673e03.png](https://i-blog.csdnimg.cn/blog_migrate/4a15744fee15663d970f97e1f898decb.png)
Part1 end...
作者:joker-wt
来AI Studio互粉吧~等你哦~ https://aistudio.baidu.com/aistudio/personalcenter/thirdview/276642
欢迎大家fork评论