您的位置:首页 > 其它

相机标定:单目图像矫正分析

2016-06-12 23:45 183 查看
图像矫正的本质,其实就是重投影的过程,即【像素坐标→物理坐标→像素坐标】的过程。只不过在重投影过程中我们可以改变投影矩阵(修改后的投影矩阵我把它称为扩展投影矩阵)从而模拟镜头缩放和平移的效果。

图像矫正可通过两种方式执行,我称之为正向矫正和逆向矫正。正向矫正是通过畸变坐标算出标准坐标,而逆向矫正是通过标准坐标算出畸变坐标。Opencv中UndistortPoints就是执行的正向矫正过程,而initUndistortRectifyMap执行的是逆向矫正过程。

正向矫正的流程为:畸变像素坐标→畸变物理坐标→标准物理坐标→标准像素坐标

逆向矫正的流程为:标准像素坐标→标准物理坐标→畸变物理坐标→畸变像素坐标

从流程可以看出,不管正向矫正还是逆向矫正都要先过渡到物理坐标,然后再转换为像素坐标,这也是为什么说图像矫正的本质其实就是重投影的原因。

畸变物理坐标和标准物理坐标之间的桥梁是畸变向量。而物理坐标和像素坐标之间的桥梁是投影矩阵。其中,畸变像素坐标和畸变物理坐标之间的桥梁是相机原生的投影矩阵,而标准像素坐标和标准物理坐标之间的桥梁既可以是相机的原生的投影矩阵也可以是自定义的扩展投影矩阵,扩展投影矩阵的目的是模拟镜头缩放和平移的效果,从而满足个性化需求。

OpenCV中单图像矫正的主要函数有:

undistortPoints

icvGetRectangles

getOptimalNewCameraMatrix

initUndistortRectifyMap

为了代码和公式更好地对应,这四函数我都移除对双目图像矫正的支持,并对代码进行了整理和简化。

对于undistortPoints和initUndistortRectifyMap,查看源查源码及源码中的注释即可与理论对应上。这里对讲解一下icvGetRectangles和getOptimalNewCameraMatrix的功能和原理

icvGetRectangles功能是获取标准图像的有效像素(有效像素指在畸变图像中有对应像素的像素)所构成的区域的最大内接矩阵和最小外接矩阵,其实现方式是:取畸变图像中的一些特殊点(四条边上的点和中间区域的一些点,详见源码),调用UndistortPoints算出这些点的标准坐标,通过这些标准坐标即可算出标准图像的有效像素所构成的区域的最大内接矩阵和最小外接矩阵。

icvGetOptimalNewCameraMatrix功能是设计扩展投影矩阵,其中个性化参数包括:是否使主点是矫正后的图像的中点,以及是否使矫正后的图像包含畸变图像映射过来的所有点。这里要注意的是,若矫正后的图像包含畸变图像映射过来的所有点,则也包含一些在畸变图像中图像中无对应的点;若矫正后的图像只包含畸变图像映射过来的点,则也会遗漏一些畸变图像映射过来的点

1 cv_UndistortPoints_monocular

void cv_InitUndistortRectifyMap_monocular(Mat_<double> &A, Mat_<double> &D, Mat_<double> &AA, Mat_<float> &map1, Mat_<float> &map2)
{
//1.获取投影参数
double cx = A(0, 2);
double cy = A(1, 2);
double fx = A(0, 0);
double fy = A(1, 1);

//2.获取扩展投影参数
double iffx = 1.0 / AA(0, 0);
double iffy = 1.0 / AA(1, 1);
double iccx = -iffx * AA(0, 2);
double iccy = -iffy * AA(1, 2);

//3.获取畸变参数
double k1 = D(0);
double k2 = D(1);
double p1 = D(2);
double p2 = D(3);
double k3 = D.total() >= 5 ? D(4) : 0.;
double k4 = D.total() >= 8 ? D(5) : 0.;
double k5 = D.total() >= 8 ? D(6) : 0.;
double k6 = D.total() >= 8 ? D(7) : 0.;
double s1 = D.total() >= 12 ? D(8) : 0.;
double s2 = D.total() >= 12 ? D(9) : 0.;
double s3 = D.total() >= 12 ? D(10) : 0.;
double s4 = D.total() >= 12 ? D(11) : 0.;
//double a = D.total() >= 14 ? D(12) : 0.;
//double b = D.total() >= 14 ? D(13) : 0.;

//4.根据标准像素坐标计算畸变像素坐标
for (int vv = 0; vv < map1.size().height; vv++)
for (int uu = 0; uu < map2.size().width; uu++)
{
//4.1根据标准像素坐标(uu,vv)和扩展投影矩阵AA计算标准物理坐标(xx,yy)
double xx = uu * iffx + iccx;
double yy = vv * iffy + iccy;
//4.2根据标准物理坐标(xx,yy)和畸变向量D计算畸变物理坐标(xxx, yyy)
double tm1 = xx * xx;
double tm2 = yy * yy;
double tm3 = tm1 + tm2;
double tm4 = 2 * xx * yy;
double tm5 = (1 + ((k3*tm3 + k2)*tm3 + k1)*tm3) / (1 + ((k6*tm3 + k5)*tm3 + k4)*tm3);
double xxx = (xx*tm5 + p1*tm4 + p2*(tm3 + 2 * tm1) + s1*tm3 + s2*tm3*tm3);
double yyy = (yy*tm5 + p1*(tm3 + 2 * tm2) + p2*tm4 + s3*tm3 + s4*tm3*tm3);
//4.3根据畸变物理坐标(xxx,yyy)和投影矩阵矩阵A计算畸变像素坐标(u,v)
double u = fx*xxx + cx;
double v = fy*yyy + cy;
map1(vv, uu) = (float)u;
map2(vv, uu) = (float)v;
}
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: