- 操作系统:ubuntu22.04
- OpenCV版本:OpenCV4.9
- IDE:Visual Studio Code
- 编程语言:C++11
算法描述
计算畸变矫正和校正变换映射。
该函数计算联合的畸变矫正和校正变换,并以 remap 所需的地图形式表示结果。矫正后的图像看起来像是原始图像,仿佛它是使用 cameraMatrix = newCameraMatrix 和零畸变的相机捕获的一样。对于单目相机,newCameraMatrix 通常等于 cameraMatrix,或者可以通过 getOptimalNewCameraMatrix 计算以更好地控制缩放。对于立体相机,newCameraMatrix 通常设置为由 stereoRectify 计算得到的 P1 或 P2。
此外,这个新的相机在坐标空间中的方向根据 R 不同。例如,这有助于对齐立体相机的两个头,使得两幅图像上的极线变得水平并且具有相同的 y 坐标(对于水平对齐的立体相机而言)。
该函数实际上构建了用于 remap 的逆映射算法的地图。也就是说,对于目标图像(即矫正和校正后的图像)中的每个像素 (u, v),该函数计算源图像(即来自相机的原始图像)中对应的坐标。应用以下过程:
x
←
(
u
−
c
′
x
)
/
f
′
x
y
←
(
v
−
c
′
y
)
/
f
′
y
[
X
Y
W
]
T
←
R
−
1
⋅
[
x
y
1
]
T
x
′
←
X
/
W
y
′
←
Y
/
W
r
2
←
x
′
2
+
y
′
2
x
′
′
←
x
′
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
1
+
k
4
r
2
+
k
5
r
4
+
k
6
r
6
+
2
p
1
x
′
y
′
+
p
2
(
r
2
+
2
x
′
2
)
+
s
1
r
2
+
s
2
r
4
y
′
′
←
y
′
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
1
+
k
4
r
2
+
k
5
r
4
+
k
6
r
6
+
p
1
(
r
2
+
2
y
′
2
)
+
2
p
2
x
′
y
′
+
s
3
r
2
+
s
4
r
4
s
[
x
′
′
′
y
′
′
′
1
]
=
[
R
33
(
τ
x
,
τ
y
)
0
−
R
13
(
τ
x
,
τ
y
)
0
R
33
(
τ
x
,
τ
y
)
−
R
23
(
τ
x
,
τ
y
)
0
0
1
]
⋅
R
(
τ
x
,
τ
y
)
⋅
[
x
′
′
y
′
′
1
]
m
a
p
x
(
u
,
v
)
←
x
′
′
′
f
x
+
c
x
m
a
p
y
(
u
,
v
)
←
y
′
′
′
f
y
+
c
y
\begin{array}{l} x \leftarrow (u - {c'}_x) / {f'}_x \\ y \leftarrow (v - {c'}_y) / {f'}_y \\ [X\,Y\,W]^T \leftarrow R^{-1} \cdot [x \, y \, 1]^T \\ x' \leftarrow X/W \\ y' \leftarrow Y/W \\ r^2 \leftarrow x'^2 + y'^2 \\ x'' \leftarrow x' \frac{1 + k_1 r^2 + k_2 r^4 + k_3 r^6}{1 + k_4 r^2 + k_5 r^4 + k_6 r^6} + 2p_1 x' y' + p_2(r^2 + 2 x'^2) + s_1 r^2 + s_2 r^4 \\ y'' \leftarrow y' \frac{1 + k_1 r^2 + k_2 r^4 + k_3 r^6}{1 + k_4 r^2 + k_5 r^4 + k_6 r^6} + p_1 (r^2 + 2 y'^2) + 2 p_2 x' y' + s_3 r^2 + s_4 r^4 \\ s \begin{bmatrix} x''' \\ y''' \\ 1 \end{bmatrix} = \begin{bmatrix} R_{33}(\tau_x, \tau_y) & 0 & -R_{13}(\tau_x, \tau_y) \\ 0 & R_{33}(\tau_x, \tau_y) & -R_{23}(\tau_x, \tau_y) \\ 0 & 0 & 1 \end{bmatrix} \cdot R(\tau_x, \tau_y) \cdot \begin{bmatrix} x'' \\ y'' \\ 1 \end{bmatrix} \\ map_x(u,v) \leftarrow x''' f_x + c_x \\ map_y(u,v) \leftarrow y''' f_y + c_y \end{array}
x←(u−c′x)/f′xy←(v−c′y)/f′y[XYW]T←R−1⋅[xy1]Tx′←X/Wy′←Y/Wr2←x′2+y′2x′′←x′1+k4r2+k5r4+k6r61+k1r2+k2r4+k3r6+2p1x′y′+p2(r2+2x′2)+s1r2+s2r4y′′←y′1+k4r2+k5r4+k6r61+k1r2+k2r4+k3r6+p1(r2+2y′2)+2p2x′y′+s3r2+s4r4s
x′′′y′′′1
=
R33(τx,τy)000R33(τx,τy)0−R13(τx,τy)−R23(τx,τy)1
⋅R(τx,τy)⋅
x′′y′′1
mapx(u,v)←x′′′fx+cxmapy(u,v)←y′′′fy+cy
其中 (k1, k2, p1, p2[, k3[, k4, k5, k6[, s1, s2, s3, s4[, τx, τy]]]]) 是畸变系数。
对于立体相机,此函数会被调用两次:每次针对一个相机头,在 stereoRectify 之后调用,而 stereoRectify 又是在 stereoCalibrate 之后调用的。但如果立体相机未经过校准,仍然可以直接从基本矩阵使用 stereoRectifyUncalibrated 计算校正变换。对于每个相机,该函数以像素域中的单应性矩阵 H 而不是三维空间中的旋转矩阵 R 来计算校正变换。R 可以通过 H 按照以下方式计算:
R
=
cameraMatrix
−
1
⋅
H
⋅
cameraMatrix
\texttt{R} = \texttt{cameraMatrix} ^{-1} \cdot \texttt{H} \cdot \texttt{cameraMatrix}
R=cameraMatrix−1⋅H⋅cameraMatrix
其中 cameraMatrix 可以任意选择。
函数原型
void cv::initUndistortRectifyMap
(
InputArray cameraMatrix,
InputArray distCoeffs,
InputArray R,
InputArray newCameraMatrix,
Size size,
int m1type,
OutputArray map1,
OutputArray map2
)
参数
- 参数cameraMatrix 输入相机矩阵 cameraMatrix = A = [ f x 0 c x 0 f y c y 0 0 1 ] \text{cameraMatrix} = A = \begin{bmatrix}f_x & 0 & c_x \\0 & f_y & c_y \\0 & 0 & 1\end{bmatrix} cameraMatrix=A= fx000fy0cxcy1 。
- 参数distCoeffs 输入畸变系数向量 (k1, k2, p1, p2[, k3[, k4, k5, k6[, s1, s2, s3, s4[, τx, τy]]]]),包含 4、5、8、12 或 14 个元素。如果该向量为 NULL/空,则假设畸变系数为零。
- 参数R 可选的对象空间中的校正变换(3x3 矩阵)。可以传递由 stereoRectify 计算得到的 R1 或 R2。如果该矩阵为空,则假定为单位变换。在 initUndistortRectifyMap 中,R 假设为单位矩阵。
- 参数newCameraMatrix 新的相机矩阵 $ newCameraMatrix = A ′ = [ f x ′ 0 c x ′ 0 f y ′ c y ′ 0 0 1 ] \text{newCameraMatrix} = A' = \begin{bmatrix} f'_x & 0 & c'_x \\ 0 & f'_y & c'_y \\ 0 & 0 & 1 \end{bmatrix} newCameraMatrix=A′= fx′000fy′0cx′cy′1 。
- 参数size 矫正后图像的尺寸。
- 参数m1type 第一个输出映射的类型,可以是 CV_32FC1, CV_32FC2 或 CV_16SC2,参见 convertMaps。
- 参数map1 第一个输出映射。
- 参数map2 第二个输出映射。
代码示例
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
// 假设的相机矩阵和畸变系数
Mat cameraMatrix = ( Mat_< double >( 3, 3 ) << 500, 0, 320, 0, 500, 240, 0, 0, 1 );
Mat distCoeffs = ( Mat_< double >( 5, 1 ) << 0.1, 0.05, -0.01, 0.005, 0 );
// 假设的旋转矩阵 R 和新的相机矩阵 newCameraMatrix
Mat R = Mat::eye( 3, 3, CV_64F ); // 单位矩阵作为示例
Mat newCameraMatrix = cameraMatrix.clone();
// 图像尺寸
Size imageSize( 640, 480 );
// 定义输出映射类型
int m1type = CV_32FC1;
// 初始化矫正映射
Mat map1, map2;
initUndistortRectifyMap( cameraMatrix, distCoeffs, R, newCameraMatrix, imageSize, m1type, map1, map2 );
cout << "Undistortion and rectification maps created." << endl;
// 使用 remap 对图像进行矫正
Mat originalImage = imread( "/media/dingxin/data/study/OpenCV/sources/images/remap.png" ); // 加载原始图像
if ( originalImage.empty() )
{
cerr << "Error: Could not open or find the image!" << endl;
return -1;
}
Mat undistortedImage;
remap( originalImage, undistortedImage, map1, map2, INTER_LINEAR );
// 显示结果
imshow( "Original Image", originalImage );
imshow( "Undistorted Image", undistortedImage );
waitKey( 0 );
return 0;
}