逆透视变换IPM
更多内容,请关注:
github:https://github.com/gotonote/Autopilot-Notes.git
在自动/辅助驾驶中,车道线的检测非常重要。在前视摄像头拍摄的图像中,由于透视效应的存在,本来平行的事物,在图像中确实相交的。而IPM变换就是消除这种透视效应,所以也叫逆透视。
一、IPM变换方法
(一)对应点对单应变换方法
- 输入:至少四个对应点对,不能有三点及以上共线,不需要知道摄相机参数或者平面位置的任何信息。
- 数学原理:利用点对,求解透视变换矩阵,其中map_matrix是一个3×3矩阵,所以可以构建一个线性方程组进行求解。如果大于4个点,可采用 r a n s a c ransac ransac 的方法进行求解,一边具有更好的稳定性。
- 选点方法:一般采取手动选取,或者利用消影点(图像上平行线的交点,也叫消失点,vanish point)选取。
[ t i x i ′ t i y i ′ t i ] = m a p _ m a t r i x ⋅ [ x i y i 1 ] \begin{bmatrix} t_i x_i' \\ t_i y_i' \\ t_i \\ \end{bmatrix} = map\_matrix \cdot \begin{bmatrix} x_i \\ y_i \\ 1 \\ \end{bmatrix} tixi′tiyi′ti =map_matrix⋅ xiyi1
d s t ( i ) = ( x i ′ , y i ′ ) s r c ( i ) = ( x i , y i ) i = 0 , 1 , 2 , 3 dst(i) = (x_i', y_i') \\ src(i) = (x_i, y_i) \\ i = 0, 1, 2, 3 dst(i)=(xi′,yi′)src(i)=(xi,yi)i=0,1,2,3
- 代码实现:代码实现比较简单,可以很容易实现IPM变换
- 计算变换矩阵: H = getPerspectiveTransform()
- 获取IPM图像: warpPerspective();
void warpPerspective(Mat src, Mat &dst, Mat T){
//此处注意计算模型的坐标系与Mat的不同
//图像以左上点为(0,0),向左为x轴,向下为y轴,所以前期搜索到的特征点 存的格式是(图像x,图像y)---(rows,cols)
//而Mat矩阵的是向下为x轴,向左为y轴,所以存的方向为(图像y,图像x)----(cols,rows)----(width,height)
//创建原图的四个顶点的3*4矩阵(此处我的顺序为左上,右上,左下,右下)
Mat tmp(3, 4, CV_64FC1, 1);
tmp.at<double>(0, 0) = 0;
tmp.at<double>(1, 0) = 0;
tmp.at<double>(0, 1) = src.cols;
tmp.at<double>(1, 1) = 0;
tmp.at<double>(0, 2) = 0;
tmp.at<double>(1, 2) = src.rows;
tmp.at<double>(0, 3) = src.cols;
tmp.at<double>(1, 3) = src.rows;
//获得原图四个顶点变换后的坐标,计算变换后的图像尺寸
Mat corner = T * tmp; //corner=(x,y)=(cols,rows)
int width = 0, height = 0;
double maxw = corner.at<double>(0, 0) / corner.at<double>(2,0);
double minw = corner.at<double>(0, 0) / corner.at<double>(2,0);
double maxh = corner.at<double>(1, 0) / corner.at<double>(2,0);
double minh = corner.at<double>(1, 0) / corner.at<double>(2,0);
for (int i = 1; i < 4; i++) {
maxw = max(maxw, corner.at<double>(0, i) / corner.at<double>(2, i));
minw = min(minw, corner.at<double>(0, i) / corner.at<double>(2, i));
maxh = max(maxh, corner.at<double>(1, i) / corner.at<double>(2, i));
minh = min(minh, corner.at<double>(1, i) / corner.at<double>(2, i));
}
//创建向前映射矩阵 map_x, map_y
//size(height,width)
dst.create(int(maxh - minh), int(maxw - minw), src.type());
Mat map_x(dst.size(), CV_32FC1);
Mat map_y(dst.size(), CV_32FC1);
Mat proj(3,1, CV_32FC1,1);
Mat point(3,1, CV_32FC1,1);
T.convertTo(T, CV_32FC1);
//本句是为了令T与point同类型【同类型才可以相乘,否则报错,也可以使用 T.convertTo(T, point.type());】
Mat Tinv=T.inv();
for (int i = 0; i < dst.rows; i++) {
for (int j = 0; j < dst.cols; j++) {
point.at<float>(0) = j + minw;
point.at<float>(1) = i + minh;
proj = Tinv * point;
map_x.at<float>(i, j) = proj.at<float>(0) / proj.at<float>(2);
map_y.at<float>(i, j) = proj.at<float>(1) / proj.at<float>(2);
}
}
remap(src, dst, map_x, map_y, CV_INTER_LINEAR);
}
(二)简化相机模型的逆透视变换
利用相机成像过程当中各种坐标系之间的转换关系,对其基本原理进行抽象和简化,从而得到世界坐标系和图像坐标系之间坐标的对应关系,并对逆透视变换的坐标关系进行公式化描述。这种逆透视变换形式简单,计算速度快,并且适用于复杂道路场景。
1. Paper:Stereo inverse perspective mapping: theory and applications 文章地址
{ u ( x , y , 0 ) = arctan { h sin [ arctan ( y − d x − l ) ] y − d } − ( θ ~ − α ) 2 α m − 1 v ( x , y , 0 ) = arctan [ y − d x − l ] − ( γ ~ − α ) 2 α n − 1 (World to Image) \left\{ \begin{aligned} u(x,y,0)&=\frac{\arctan{\Bigg\lbrace\frac{h\sin\big[\arctan\big(\frac{y-d}{x-l}\big)\big]}{y-d}\Bigg\rbrace}-(\tilde{\theta}-\alpha)}{\frac{2\alpha}{m-1}}\\ v(x,y,0)&=\frac{\arctan\big[\frac{y-d}{x-l}\big]-(\tilde{\gamma}-\alpha)}{\frac{2\alpha}{n-1}}\\ \end{aligned} \right. \tag{World to Image} ⎩ ⎨ ⎧u(x,y,0)v(x,y,0)=m−12αarctan{ y−dhsin[arctan(x−ly−d)]}−(θ~−α)=n−12αarctan[x−ly−d]−(γ~−α)(World to Image)
{ x ( u , v ) = h × cot [ ( θ ~ − α ) + u 2 α m − 1 ] × cos [ ( γ ~ − α ) + v 2 α n − 1 ] + l y ( u , v ) = h × cot [ ( θ ~ − α ) + u 2 α