(一)函数原型
void undistortPoints(InputArray src, OutputArray dst,
InputArray cameraMatrix, InputArray distCoeffs,
InputArray R = noArray(), InputArray P = noArray());
1.1函数解释:
这个函数的作用是根据观测到的点计算理想点的坐标。类似于undistort
和#initUndistortRectifyMap
,不同的是undistortPoints
操作的是稀疏点(对点去畸变)而不是整张图像。此外该函数还执行了一个反向变换来进行投影,对于三维物体并不会重建其三维坐标,对于平面物体,如果提供合适的R,t
,可以在一定程度上恢复三维坐标。
1.2原理
对于每一个观察到的像素坐标点
(
u
,
v
)
,
(u,v),
(u,v),:
首先得到归一化坐标:
x
"
←
u
−
c
x
f
x
,
y
"
←
v
−
c
y
f
y
x^{"} \leftarrow \frac{u - c_x}{f_x} ,y^{"} \leftarrow \frac{v - c_y}{f_y}
x"←fxu−cx,y"←fyv−cy
对归一化坐标调用undistort进行去畸变:
(
x
′
,
y
′
)
=
undistort
(
x
"
,
y
"
,
distCoeffs
)
(x', y') = \text{undistort}(x^{"}, y^{"}, \texttt{distCoeffs})
(x′,y′)=undistort(x",y",distCoeffs)
如果定义了旋转矩阵:
[
X
Y
W
]
T
←
R
⋅
[
x
′
y
′
1
]
T
[X \, Y \, W]^T \leftarrow R \cdot [x' \, y' \, 1]^T
[XYW]T←R⋅[x′y′1]T
旋转之后的归一化坐标:
x
←
X
W
,
y
←
Y
W
x \leftarrow \frac{X}{W},y \leftarrow \frac{Y}{W}
x←WX,y←WY
如果指定了P则将归一化平面的点转到新的像素平面:
u
′
←
x
⋅
f
x
′
+
c
x
′
,
v
′
←
y
⋅
f
y
′
+
c
y
′
u' \leftarrow x \cdot f'_x + c'_x,v' \leftarrow y \cdot f'_y + c'_y
u′←x⋅fx′+cx′,v′←y⋅fy′+cy′
1.3参数
src:观测点坐标,格式为 2xN / Nx2 的单通道或 1xN / Nx1 的双通道(CV_32FC2 或 CV_64FC2),也可以是 vector 类型。
dst :输出的理想点坐标,1xN / Nx1 的双通道或者vector类型,经过去畸变和反向透视变换后的结果。如果P为单位矩阵或未指定,dst 将包含归一化后的点坐标。
cameraMatrix: 相机的内参矩阵。
distCoeffs: 相机畸变系数。
R:矫正变换矩阵,如果不为空则会对归一化平面的点进行变换。
P:新的内参矩阵,如果不为空则会按照新的内参矩阵将归一化平面上的点投影到相机平面。
(二)使用示例
以ORB-SLAM中去畸变函数为例:
void Frame::UndistortKeyPoints()
{
// 判断畸变系数(如果是从配置文件中读取)
if(mDistCoef.at<float>(0)==0.0)
{
// 如果畸变系数第一个为0,通常全为0,不需要去畸变
mvKeysUn=mvKeys;
return;
}
// 定义N*2单通道,类型为32F
cv::Mat mat(N,2,CV_32F);
for(int i=0; i<N; i++)
{
mat.at<float>(i,0)=mvKeys[i].pt.x;
mat.at<float>(i,1)=mvKeys[i].pt.y;
}
// reshape不会改变数据的内容,只会改变维度和通道等
mat=mat.reshape(2);
// P矩阵给的是原来的相机内参,因此结果是像素坐标
cv::undistortPoints(mat,mat,mK,mDistCoef,cv::Mat(),mK);
mat=mat.reshape(1);
// 得到去畸变之后的结果
mvKeysUn.resize(N);
for(int i=0; i<N; i++)
{
cv::KeyPoint kp = mvKeys[i];
kp.pt.x=mat.at<float>(i,0);
kp.pt.y=mat.at<float>(i,1);
mvKeysUn[i]=kp;
}
}