文章目录
1. 前言
前言,关于相机的畸变模型及其推导,很多文章都已经写得很详细了,我这里只总结结论,把重点放在理解以及使用上。这篇文章总结了径向切向畸变,鱼眼畸变,FOV畸变;同时,还根据径向切向畸变的模型,以Euroc数据集为例,提供了畸变矫正的Matlab和C++代码示例。
2. 针孔模型(pinhole model)
针孔相机模型(即直线投影模型,是相机在理想情况下的投影模型)是消费类相机中最常见的相机模型。在此模型中,图像通过透视投影映射到平面上。当相机坐标系(原点在相机光心,向前为Z,向右为X,向下为Y)下的一个三维空间点
(
X
c
,
Y
c
,
Z
c
)
(X_c, Y_c, Z_c)
(Xc,Yc,Zc)通过针孔相机模型投影得到图像坐标系(图像的左上角为坐标原点,向右为x,向下为y,单位为像素)下的一个像素点
(
u
,
v
)
(u, v)
(u,v)时,它们的对应关系如下:
{
x
=
f
x
X
c
Z
c
+
c
x
=
f
x
⋅
x
+
c
x
y
=
f
y
Y
c
Z
c
+
c
y
=
f
y
⋅
y
+
c
y
(1.1)
\left\{ \begin{aligned} x = f_x \frac{X_c}{Z_c} + c_x = f_x \cdot x + c_x \\ y = f_y \frac{Y_c}{Z_c} + c_y = f_y \cdot y + c_y \end{aligned} \right. \tag{1.1}
⎩⎪⎪⎨⎪⎪⎧x=fxZcXc+cx=fx⋅x+cxy=fyZcYc+cy=fy⋅y+cy(1.1)
其中,
x
=
X
c
Z
c
,
y
=
Y
c
Z
c
x = \frac{X_c}{Z_c}, y = \frac{Y_c}{Z_c}
x=ZcXc,y=ZcYc是归一化的坐标(没有单位),上式子也可写成齐次坐标的形式:
[
u
v
1
]
=
1
Z
c
[
f
x
0
c
x
0
f
y
c
y
0
0
1
]
[
X
c
Y
c
Z
c
]
(1.2)
\left[\begin{matrix} u \\ v \\ 1 \end{matrix}\right] = \frac{1}{Z_c} \left[\begin{matrix} f_x & 0 & c_x \\0 & f_y & c_y \\ 0 & 0 & 1 \end{matrix}\right] \left[\begin{matrix} X_c \\ Y_c \\ Z_c \end{matrix}\right] \tag{1.2}
⎣⎡uv1⎦⎤=Zc1⎣⎡fx000fy0cxcy1⎦⎤⎣⎡XcYcZc⎦⎤(1.2)
其中,
f
x
,
f
y
,
c
x
,
c
y
f_x, f_y, c_x, c_y
fx,fy,cx,cy 为相机内参(单位:像素),上面模型的推导与相机内参的具体含义这里不过多展开,详情请见高博的《视觉SLAM十四讲》,或者链接:SLAM之相机标定,需要说明的是,该链接中讲的相机标定和畸变部分与高博的那本书一致,它同时还介绍了基于ROS的相机内参标定过程,以及校准的代码。但是,那里面只提到径向切向畸变,校准出来几个畸变参数也没有详细说明,而且代码写的有点小问题:(他写的显然是C++的代码,但是下面那两行却实matlab的语法,而且“^”在C++中的含义是异或操作)
double x_distorted = x*(1+k1*r^2+k2*r^4)+2*p1*x*y+p2*(r^2+2*x^2);
double y_distorted = y*(1+k1*r^2+k2*r^4)+2*p2*x*y+p1*(r^2+2*y^2);
因此,我想在此基础上做一点补充。
3. 针孔模型+径向切向畸变(rectilinear projection distortion)
{
x
d
i
s
t
o
r
t
e
d
=
x
(
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
)
+
2
p
1
x
y
+
p
2
(
r
2
+
2
x
2
)
y
d
i
s
t
o
r
t
e
d
=
y
(
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
)
+
2
p
2
x
y
+
p
1
(
r
2
+
2
y
2
)
(2.1)
\left\{ \begin{aligned} x_{distorted} = x(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) + 2p_1 xy + p_2(r^2 + 2x^2) \\ y_{distorted} = y(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) + 2p_2 xy + p_1(r^2 + 2y^2) \end{aligned} \right. \tag{2.1}
{xdistorted=x(1+k1r2+k2r4+k3r6)+2p1xy+p2(r2+2x2)ydistorted=y(1+k1r2+k2r4+k3r6)+2p2xy+p1(r2+2y2)(2.1)
其中,式(2.1)中的
x
,
y
x, y
x,y与式
(
1.1
)
(1.1)
(1.1)中的
x
,
y
x, y
x,y含义相同,都是归一化后的坐标,且
r
=
(
x
2
+
y
2
)
r = \sqrt{(x^2 +y^2)}
r=(x2+y2) 。可以得到校准后的像素位置
(
u
d
i
s
t
o
r
t
e
d
,
v
d
i
s
t
o
r
t
e
d
)
(u_{distorted}, v_{distorted})
(udistorted,vdistorted):
{
u
d
i
s
t
o
r
t
e
d
=
f
x
x
d
i
s
t
o
r
t
e
d
+
c
x
v
d
i
s
t
o
r
t
e
d
=
f
y
y
d
i
s
t
o
r
t
e
d
+
c
y
(2.2)
\left\{ \begin{aligned} u_{distorted} = f_x x_{distorted} + c_x \\ v_{distorted} = f_y y_{distorted} + c_y \end{aligned} \right.\tag{2.2}
{udistorted=fxxdistorted+cxvdistorted=fyydistorted+cy(2.2)
写成矩阵的形式有:
[
u
d
i
s
t
o
r
t
e
d
v
d
i
s
t
o
r
t
e
d
1
]
=
[
f
x
0
c
x
0
f
y
c
y
0
0
1
]
[
x
d
i
s
t
o
r
t
e
d
y
d
i
s
t
o
r
t
e
d
1
]
(2.3)
\left[\begin{matrix} u_{distorted} \\ v_{distorted} \\ 1 \end{matrix}\right] = \left[\begin{matrix} f_x & 0 & c_x \\0 & f_y & c_y \\ 0 & 0 & 1 \end{matrix}\right] \left[\begin{matrix} x_{distorted} \\ y_{distorted} \\ 1 \end{matrix}\right] \tag{2.3}
⎣⎡udistortedvdistorted1⎦⎤=⎣⎡fx000fy0cxcy1⎦⎤⎣⎡xdistortedydistorted1⎦⎤(2.3)
利用ROS的 MonocularCalibration 校准得到两个参数文件,这是同一个相机参数的两种不同文件格式:一个ini格式和一个yaml格式的文件(yaml格式的文件是用于OpenCV校准程序),在参数文件中,它们的含义都比较明显:
image_width: 640
image_height: 480
camera_name: narrow_stereo
camera_matrix:
rows: 3
cols: 3
data: [634.706989, 0.000000, 323.354857, 0.000000, 634.489915, 213.107721, 0.000000, 0.000000, 1.000000]
distortion_model: plumb_bob
distortion_coefficients:
rows: 1
cols: 5
data: [-0.068953, 0.342004, -0.002181, 0.005728, 0.000000]
rectification_matrix:
rows: 3
cols: 3
data: [1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000]
projection_matrix:
rows: 3
cols: 4
data: [650.866821, 0.000000, 325.423266, 0.000000, 0.000000, 650.506226, 212.983124, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000]
上面的畸变模型参数值"plumb_bob"表示该相机的畸变模型是:径向和切向变形的5参数多项式逼近。而 distortion_coefficients 中的五个参数肯定是式(2.1)中
k
1
,
k
2
,
k
3
,
p
1
,
p
2
k_1, k_2, k_3, p_1, p_2
k1,k2,k3,p1,p2这五个,但它们的对应关系是怎么样的呢。根据参考 https://www.ros.org/reps/rep-0104.html#plumbbob 中的文献[2]:

虽然其中的字符定义和我们习惯性用的不太一致,但是二者含义是一样的。从上面图中可以看出,畸变参数中,第1,2,5个参数对应的是式(2.1)中的
k
1
,
k
2
,
k
3
k_1,k_2,k_3
k1,k2,k3,第3,4个参数对应的是式(2.1)中的
p
1
,
p
2
p_1, p_2
p1,p2。因此我们可以知道:distortion_coefficients中的五个参数从前到后分别对应的是:
k
1
,
k
2
,
p
1
,
p
2
,
k
3
k_1, k_2, p_1, p_2, k_3
k1,k2,p1,p2,k3。
说句题外话,将文献[2]中的字符翻译成和本文中的字符一致,可得到如式(2.3)所示的对应关系,其中
α
\alpha
α定义了x和y像素轴之间的角度,但很多文章貌似都没有提起这一项畸变,不知道是为什么。
[
u
d
i
s
t
o
r
t
e
d
v
d
i
s
t
o
r
t
e
d
1
]
=
[
f
x
α
f
x
c
x
0
f
y
c
y
0
0
1
]
[
x
d
i
s
t
o
r
t
e
d
y
d
i
s
t
o
r
t
e
d
1
]
(2.3)
\left[\begin{matrix} u_{distorted} \\ v_{distorted} \\ 1 \end{matrix}\right] = \left[\begin{matrix} f_x & \alpha f_x & c_x \\0 & f_y & c_y \\ 0 & 0 & 1 \end{matrix}\right] \left[\begin{matrix} x_{distorted} \\ y_{distorted} \\ 1 \end{matrix}\right] \tag{2.3}
⎣⎡udistortedvdistorted1⎦⎤=⎣⎡fx00αfxfy0cxcy1⎦⎤⎣⎡xdistortedydistorted1⎦⎤(2.3)
4. 径向切向畸变校准实例
根据上述畸变模型的含义,现在尝试根据它来校准畸变后的图像。我从Euroc数据集中,下载了示例图片数据以及对应的参数文件,其中参数文件内容如下(该数据集的校准文件格式与上面提到的ROS校准工具生成的格式不太一致,但该有的参数都有,其中的校准参数中没有 k 3 k_3 k3参数):
# General sensor definitions.
sensor_type: camera
comment: VI-Sensor cam0 (MT9M034)
# Sensor extrinsics wrt. the body-frame.
T_BS:
cols: 4
rows: 4
data: [0.0148655429818, -0.999880929698, 0.00414029679422, -0.0216401454975,
0.999557249008, 0.0149672133247, 0.025715529948, -0.064676986768,
-0.0257744366974, 0.00375618835797, 0.999660727178, 0.00981073058949,
0.0, 0.0, 0.0, 1.0]
# Camera specific definitions.
rate_hz: 20
resolution: [752, 480]
camera_model: pinhole
intrinsics: [458.654, 457.296, 367.215, 248.375] #fu, fv, cu, cv
distortion_model: radial-tangential
distortion_coefficients: [-0.28340811, 0.07395907, 0.00019359, 1.76187114e-05]
根据上述参数,对其中的畸变图像进行校准,基于matlab的校准代码如下:
close all; clear all; clc
img1 = imread('1403638225195097088.png');
intrinsic.fx = 458.654; intrinsic.fy = 457.296;
intrinsic.cx = 367.215; intrinsic.cy = 248.375;
intrinsic.k1 = -0.28340811; intrinsic.k2 = 0.07395907; intrinsic.k3 = 0;
intrinsic.p1 = 0.00019359; intrinsic.p2 = 1.76187114e-05;
img0 = calibration(img1, intrinsic, 1);
上面代码中calibration函数内容如下所示:
function [image_undistorted] = calibration(image_distorted, intrinsic, is_show)
%calibration 用于根据相机内参,对图像进行校准
%INPUT----------------------------------------
% image_distorted : 输入(失真)图像矩阵;
% intrinsic : 相机内参;
% is_show : 是否显示校准前后的图像(默认为true);
%OUTPUT---------------------------------------
% image_undistorted : 输出的校准后的图像矩阵;
if nargin <= 2
is_show = 1;
end
fx = intrinsic.fx; fy = intrinsic.fy; % x<-->u; y<-->v
cx = intrinsic.cx; cy = intrinsic.cy;
k1 = intrinsic.k1; k2 = intrinsic.k2; k3 = intrinsic.k3;
p1 = intrinsic.p1; p2 = intrinsic.p2;
image_undistorted = image_distorted;
[h, w] = size(image_distorted);
for v = 1 : h
for u = 1 : w
x = (u - cx) / fx;
y = (v - cy) / fy;
r = sqrt(x*x + y*y);
x_d = x*(1+k1*r*r + k2*r*r*r*r + k3*r*r*r*r*r*r)+ 2*p1*x*y + p2*(r*r + 2*x*x);
y_d = y*(1+k1*r*r + k2*r*r*r*r + k3*r*r*r*r*r*r)+ 2*p2*x*y + p1*(r*r + 2*y*y);
u_d = fx*x_d+cx;
v_d = fy*y_d+cy;
if u_d >=1 && u_d <= w && v_d >=1 && v_d <= h
image_undistorted(v, u) = image_distorted(floor(v_d), floor(u_d));
else
image_undistorted(v, u) = 0;
end
end
end
if is_show
figure('Name', '校准前')
imshow(image_distorted);
figure('Name', '校准后')
imshow(image_undistorted);
end
end
基于C++的校准代码如下(只需将 SLAM之相机标定 中有问题的两行代码改过来就能实现校准的功能了):
使用方法:生成的可执行文件名 图片路径
#include <opencv2/opencv.hpp>
int main(int argc, char **argv) {
if (argc < 2)
{
std::cout << "Usage: exe_name image_name \n";
return -1;
}
std::string image_file(argv[1]); // 请确保路径正确
double k1 = -0.28340811, k2 = 0.07395907, p1 = 0.00019359, p2 = 1.76187114e-05; // 畸变参数
double fx = 458.654, fy = 457.296, cx = 367.215, cy = 248.375; // 内参
cv::Mat image = cv::imread(image_file, 0); // 图像是灰度图,CV_8UC1
int rows = image.rows, cols = image.cols;
cv::Mat image_undistort = cv::Mat(rows, cols, CV_8UC1); // 去畸变以后的图
// 计算去畸变后图像的内容
for (int v = 0; v < rows; v++)
for (int u = 0; u < cols; u++) {
double u_distorted = 0, v_distorted = 0;
//按照公式,计算点(u,v)对应到畸变图像中的坐标(u_distorted, v_distorted)
double x = (u - cx) / fx;
double y = (v - cy) / fy;
double r = sqrt(x*x + y*y);
double x_distorted = x * (1 + k1 * r*r + k2 * r*r*r*r) + 2 * p1*x*y + p2 * (r*r + 2*x*x);
double y_distorted = y * (1 + k1 * r*r + k2 * r*r*r*r) + 2 * p2*x*y + p1 * (r*r + 2*y*y);
u_distorted = fx * x_distorted + cx;
v_distorted = fy * y_distorted + cy;
// 赋值 (最近邻插值)
if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < cols && v_distorted < rows)
{
image_undistort.at<uchar>(v, u) = image.at<uchar>((int)v_distorted, (int)u_distorted);
}
else
{
image_undistort.at<uchar>(v, u) = 0;
}
}
// 校准前图像:
cv::imshow("图像矫正前", image);
// 校准后图像
cv::imshow("图像矫正后", image_undistort);
cv::waitKey();
return 0;
}
校准前后的效果如图所示:
5. 针孔模型+鱼眼畸变(fisheye distortion)
鱼眼相机模型是用于宽广视场相机的相机模型。因为当视野接近180度时,针孔相机模型无法对图像投影进行建模,所以需要该模型:
{
r
=
x
2
+
y
2
θ
=
a
t
a
n
2
(
(
X
c
2
+
Y
c
2
)
,
Z
c
)
=
a
t
a
n
2
(
r
,
1
)
=
a
r
c
t
a
n
(
r
)
θ
d
=
θ
(
1
+
k
1
θ
2
+
k
2
θ
4
+
k
3
θ
6
+
k
4
θ
8
)
x
d
=
θ
d
r
⋅
x
y
d
=
θ
d
r
⋅
y
(4.1)
\left\{ \begin{aligned} r &= \sqrt{x^2 + y^2} \\ \theta &= atan2(\sqrt{(X_c^2 + Y_c^2)}, Z_c) = atan2(r, 1) = arctan(r) \\ \\ \theta_d &= \theta(1 + k_1 \theta^2 + k_2 \theta^4 + k_3 \theta^6 + k_4 \theta^8) \\ x_d &= \frac{\theta_d}{r} \cdot x \\ y_d &= \frac{\theta_d}{r} \cdot y \end{aligned} \right. \tag{4.1}
⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧rθθdxdyd=x2+y2=atan2((Xc2+Yc2),Zc)=atan2(r,1)=arctan(r)=θ(1+k1θ2+k2θ4+k3θ6+k4θ8)=rθd⋅x=rθd⋅y(4.1)
将矫正后的归一化坐标转换回像素坐标:
{
u
d
=
f
x
(
x
d
+
α
y
d
)
+
c
x
v
d
=
f
y
⋅
y
d
+
c
y
(4.2)
\left\{ \begin{aligned} u_d &= f_x ( x_d + \alpha y_d) + c_x \\ v_d &= f_y \cdot y_d + c_y \end{aligned} \right. \tag{4.2}
{udvd=fx(xd+αyd)+cx=fy⋅yd+cy(4.2)
写成矩阵的形式有:
[
u
d
v
d
1
]
=
[
f
x
α
f
x
c
x
0
f
y
c
y
0
0
1
]
[
x
d
y
d
1
]
(4.3)
\left[\begin{matrix} u_{d} \\ v_{d} \\ 1 \end{matrix}\right] = \left[\begin{matrix} f_x & \alpha f_x & c_x \\0 & f_y & c_y \\ 0 & 0 & 1 \end{matrix}\right] \left[\begin{matrix} x_{d} \\ y_{d} \\ 1 \end{matrix}\right] \tag{4.3}
⎣⎡udvd1⎦⎤=⎣⎡fx00αfxfy0cxcy1⎦⎤⎣⎡xdyd1⎦⎤(4.3)
6. 针孔模型+FOV畸变(FOV distortion)
这是具有较大径向变形的相机模型(例如鱼眼相机)的替代表示,其中图像点和图像中心点(principal point)之间的距离大致与相机坐标系下的3维空间点和光轴之间的角度成比例。该模型首先在论文“Straight Lines Have to be Straight: Automatic Calibration and Removal of Distortion from Scenes of Structured Environments.”中提出。
FOV 畸变矫正模型:
{
r
=
(
x
2
+
y
2
)
r
d
=
1
ω
a
r
c
t
a
n
(
2
r
⋅
t
a
n
(
ω
2
)
)
x
d
=
r
d
r
⋅
x
c
y
d
=
r
d
r
⋅
y
c
(5.1)
\left\{\begin{aligned} r &= \sqrt{(x^2 + y^2)} \\ r_d &= \frac{1}{\omega} arctan(2r \cdot tan(\frac{\omega}{2})) \\ x_d &= \frac{r_d}{r} \cdot x_c \\ y_d &= \frac{r_d}{r} \cdot y_c \end{aligned} \right. \tag{5.1}
⎩⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎧rrdxdyd=(x2+y2)=ω1arctan(2r⋅tan(2ω))=rrd⋅xc=rrd⋅yc(5.1)
其中
ω
\omega
ω是FOV畸变系数,将矫正后的归一化坐标转换回像素坐标:
[
u
d
v
d
1
]
=
[
f
x
0
c
x
0
f
y
c
y
0
0
1
]
[
x
d
y
d
1
]
(5.2)
\left[\begin{matrix} u_{d} \\ v_{d} \\ 1 \end{matrix}\right] = \left[\begin{matrix} f_x & 0 & c_x \\0 & f_y & c_y \\ 0 & 0 & 1 \end{matrix}\right] \left[\begin{matrix} x_{d} \\ y_{d} \\ 1 \end{matrix}\right] \tag{5.2}
⎣⎡udvd1⎦⎤=⎣⎡fx000fy0cxcy1⎦⎤⎣⎡xdyd1⎦⎤(5.2)
参考:
https://blog.youkuaiyun.com/learning_tortosie/article/details/79901255
http://wiki.ros.org/camera_calibration/Tutorials/MonocularCalibration
https://docs.ros.org/api/sensor_msgs/html/distortion__models_8h_source.html#l00045
http://wiki.ros.org/camera_calibration_parsers
https://www.ros.org/reps/rep-0104.html#plumbbob
http://www.vision.caltech.edu/bouguetj/calib_doc/htmls/parameters.html
https://cggos.github.io/computervision/camera-models.html
https://zhuanlan.zhihu.com/p/93822726
相机畸变校准详解及实例

这篇博客总结了相机的畸变模型,包括径向切向畸变、鱼眼畸变和FOV畸变,并提供了校准示例。文章讨论了针孔模型及其与畸变的关系,详细介绍了畸变模型的数学表达式,并给出了基于Matlab和C++的畸变矫正代码,以Euroc数据集为例进行了校准演示。
4214

被折叠的 条评论
为什么被折叠?



