最近懵逼了,我还连小孔成像的畸变矫正公式还没弄清楚,很有必要把张正友论文读一遍《Flexible camera calibration by viewing a plane from unknown orientations》,很多文章梦棱两可,建议大家还是首先读了论文了解情况。
开局一张框图
论文截图
由上述论文可见, 畸变公式左侧 x^ 和 y^ 是畸变矫正前的像素坐标,右侧的 x ,y 是理想情况下无畸变时的像素坐标:
这个是K1,K2的情况。如果是K1,K2,K3的情况:
径向畸变
x
d
i
s
t
o
r
t
e
d
x_{distorted}
xdistorted是畸变的像素。
x
x
x是理想的像素。
OpenCV 针对不同的使用场景提供了几个不同用法的畸变校正函数
OpenCV 针对不同的使用场景提供了几个不同用法的畸变校正函数。
主要有以下几种:
initUndistortRectifyMap() remap()组合
undistort()
undistortPoints()
1.1 initUndistortRectifyMap() remap()组合
通过映射的方式逐个找出理想点在有畸变原图的位置。initUndistortRectifyMap()用于产生映射表,remap()用于执行映射。
适用场景:
当要进行多次畸变校正时,使用initUndistortRectifyMap() remap()组合比较有效率,只需要执行一次initUndistortRectifyMap(),后面畸变校正只需要执行remap()即可
1.2 undistort()
本质是initUndistortRectifyMap() remap()组合,写在了一个函数里。方便只校正一次。
适用场景:
当只需要执行一次畸变校正时,用undistort()比用组合形式更方便一些。
1.3 undistortPoints()
适用场景:
当只需要找出有畸变原图中的少数几个点经过畸变校正后的理想位置时,使用undistortPoints()可达到目的。
计算公式
3.1 initUndistortRectifyMap()
https://docs.opencv.org/master/d9/d0c/group__calib3d.html#ga7dfb72c9cf9780a347fbe3d1c47e5d5a
作用: 用于生成remap()需要用到的map。
函数签名:
计算公式如下
(u, v)是理想点坐标,求出畸变后移到哪里去了,属于正向求解:
参数说明:
newCameraMatrix :
如果是单目,newCameraMatrix可以直接取cameraMatrix,或者是经过getOptimalNewCameraMatrix改变视野后的内参矩阵。
如果是双目,可能要做共面行对准,就用stereoRectify计算后的P。
m1type参数 :
是指定map1的类型,可以是CV_32FC1 、 CV_32FC2 或者 CV_16SC2,如果map1是双通道的,相当于x,y的映射关系都在map1里了。
用法示例:
cv::Mat image_undistort;
cv::Size imageSize = image.size();
cv::Mat map1, map2;
initUndistortRectifyMap(cameraMatrix, distCoeff, cv::Mat(), cameraMatrix, imageSize, CV_32FC1, map1, map2);
remap(image, image_undistort, map1, map2, cv::INTER_LINEAR);
remap函数签名
映射函数,注意插值方法和边界处理。
3.2 undistort()
https://docs.opencv.org/master/d9/d0c/group__calib3d.html#ga55c716492470bfe86b0ee9bf3a1f0f7e
作用: 是initUndistortRectifyMap()和remap()组合, initUndistortRectifyMap()参数的R是单位阵,remap()的插值方法是双线性插值。
用法示例:
cv::Mat image_undistort2;
undistort(image, image_undistort2, cameraMatrix, distCoeff);
3.3 undistortPoints()
https://docs.opencv.org/master/d9/d0c/group__calib3d.html#ga55c716492470bfe86b0ee9bf3a1f0f7e
作用: 根据有畸变的观察点计算出其理想点的位置。
函数签名:
计算公式:
其中undistort过程是根据畸变公式反向迭代计算。
参数说明:
R是指定另外的旋转,一般是双目的共面行对准时需要用到。单目可不设置,或者使用单位矩阵。
P是指定新的相机内参矩阵或者投影矩阵。
用法注意事项
如果P没有指定,那么结果dst就是物理坐标(x, y),而不是像素坐标(u’, v’).
如果设置了P,就按照新的内参矩阵计算(u’, v’),这时是像素坐标
用法示例:
// create pts
cv::Size imageSize = image.size();
std::vector<cv::Point2f> pts, upts;
int x, y;
int N = 21;
for (y = 0; y < N; y++) {
for (x = 0; x < N; x++) {
pts.push_back(cv::Point2f((float)x*imageSize.width / (N - 1), (float)y*imageSize.height / (N - 1)));
}
}
undistortPoints(pts, upts, cameraMatrix, distCoeff); // output is x,y with z = 1
// undistortPoints(pts, upts, cameraMatrix, distCoeff, cv::Mat(), cameraMatrix); // output is u' v'
传统的张正友不适合鱼眼
如果按照小孔成像,配合K1,K2的系数,可能难以匹配鱼眼的情况。
比较“camera calibration toolbox” 和“fisheye camera calibration”的结果
图像来源:https://www.cnblogs.com/yongjieShi/p/11327414.html
理论上需要修改,一般的鱼眼镜头基于等距投影模型(r=焦距×入射角),需要考虑入射角,优化再sensor上的r值。
以入射角为输入量,(张正友是以位置x为输入量)
输出的是投影点到图像中心的距离rho
r
h
o
(
t
h
e
t
a
)
=
k
1
∗
t
h
e
t
a
+
k
2
∗
t
h
e
t
a
∗
∗
2
+
k
3
∗
t
h
e
t
a
∗
∗
3
+
k
4
∗
t
h
e
t
a
∗
∗
4
,
rho(theta)=k1∗theta+k2∗theta∗∗2+k3∗theta∗∗3+k4∗theta∗∗4,
rho(theta)=k1∗theta+k2∗theta∗∗2+k3∗theta∗∗3+k4∗theta∗∗4,
根据rho计算u v值
u' = rho * X / sqrt( X ** 2 + Y ** 2)
v' = rho * Y / sqrt( X ** 2 + Y ** 2)
u = u' + cx + width/2 - 0.5
v = v' + cy + height/2 - 0.5
两种方案:
方案1:
我们选用了小孔模型进行了映射表。其实我们可以采用别的投影模型进行映射,例如我们可以采用等距投影映射:
参考:https://blog.csdn.net/Gavinv/article/details/78386465
方案2:
我们选用了小孔模型,或者其他模型进行了映射表。