提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
理论知识
1.鱼眼镜头的投影模型
鱼眼镜头一般是由十几个不同的透镜组合而成的,如下图所示,在成像的过程中,入射光线经过不同程度的折射,投影到尺寸有限的成像平面上,使得鱼眼镜头与普通镜头相比起来拥有了更大的视野范围。
鱼眼相机的成像模型近似为球面投影模型
鱼眼相机的成像过程可近似分解成两步:
- 将三维空间上的点投影到虚拟球面上(线性)
- 将球面上的点投影到图像平面上(非线性)
由上图可知,鱼眼相机的设计引入了畸变,而且所成影像存在非常严重的畸变,特别是径向畸变,因此畸变模型主要考虑径向畸变。
鱼眼相机的投影函数是为了尽可能将庞大的场景投影到有限的图像平面所设计的。根据投影函数的不同将鱼眼相机的设计模型大致分为等距投影模型、等立体角投影模型、正交投影模型和体视投影模型四种。相机的成像模型实际上表征的是成像的像高与入射角之间的映射关系。
1.1 几种投影模型的特点
1.1.1 针孔投影模型
针孔投影模型为相似性投影模型,其满足物点、成像中心、像点的共线关系,其理想成像公式为:
r
d
=
f
t
a
n
θ
r_d=ftan\theta
rd=ftanθ
1.1.2 体视投影模型
体视投影模型相比之下畸变最小,球形物面上的微小面元经过体视投影后,其像仍然是一个小圆。所以体视投影对微小物体成像有相似性。但正是这种成像的相似性,使得该投影方式不能提供足够的桶形畸变,影响成像视场。其模型如下:
r
d
=
2
f
t
a
n
θ
2
r_d=2ftan\frac \theta2
rd=2ftan2θ
1.1.3 等距投影模型
等距投影模型的投影关系在入射光线之间的角度相同时,保持其对应各投影点之间的间距相同。其投影模型为:
r
d
=
f
θ
r_d=f\theta
rd=fθ
1.1.4 等积投影模型
等积投影模型的特点是相等立体角的入射面会产生相等面积的像,其畸变程度介于等距模型与正交模型之间。其模型如下:
r
d
=
2
f
s
i
n
θ
2
r_d=2fsin\frac \theta2
rd=2fsin2θ
1.1.5 正交投影模型
正交投影模型的畸变很大,近180度处的图像信息几乎全部丢失,且180度之外的场景区域将无法进行描述。采用这种模型的镜头拍出的图片即使在视角较小的区域也会比其他模型镜头的畸变更明显。因此,针对艺术摄影类的广角镜头,该模型的校正效果更好。其模型如下:
r
d
=
f
s
i
n
θ
r_d=fsin\theta
rd=fsinθ
投影模型 | 投影函数 | 特征 |
---|---|---|
透视投影 | r d = f t a n θ r_d=ftan\theta rd=ftanθ | 针孔模型 |
体视投影 | r d = 2 f t a n θ 2 r_d=2ftan\frac \theta2 rd=2ftan2θ | 任何直线相交的角度,在变换后保持不变 |
等距投影 | r d = f θ r_d=f\theta rd=fθ | 入射光线之间的角度相同时,保持其对应各投影点之间的间距相同 |
等积投影 | r d = 2 f s i n θ 2 r_d=2fsin\frac \theta2 rd=2fsin2θ | 相等立体角的入射面会产生相等面积的像 |
正交投影 | r d = f s i n θ r_d=fsin\theta rd=fsinθ | 视角不能大于180° |
1.2 几种投影模型的区别
四种畸变模型畸变量从大到小依次为:正交投影、等立体角投影、等距投影、体视投影
2.鱼眼相机成像过程
假设
P
w
(
x
w
,
y
w
,
z
w
)
P_w(x_w,y_w,z_w)
Pw(xw,yw,zw)是世界坐标系下的一3D点,
P
c
(
x
c
,
y
c
,
z
c
)
P_c(x_c,y_c,z_c)
Pc(xc,yc,zc)是
P
w
P_w
Pw在相机坐标系下的点:
P
c
=
R
P
w
+
t
P_c=RP_w+t
Pc=RPw+t
[
x
c
y
c
z
c
]
=
R
[
x
w
y
w
z
w
]
+
t
\begin{bmatrix} x_c \\ y_c \\z_c \\ \end{bmatrix}=R \begin{bmatrix} x_w \\ y_w \\z_w \\ \end{bmatrix}+t
⎣⎡xcyczc⎦⎤=R⎣⎡xwywzw⎦⎤+t
如果没有畸变,直接按照小孔成像投影到像平面上投影点为
P
0
P_0
P0,
P
0
P_0
P0到成像中心
O
O
O的距离为
r
r
r:
r
=
a
2
+
b
2
r=\sqrt{a^2+b^2}
r=a2+b2
由相似三角形得:
x
c
a
=
y
c
b
=
z
c
f
\frac {x_c}a=\frac {y_c}b=\frac {z_c}f
axc=byc=fzc
因此:
r = f ( x c z c ) 2 + ( y c z c ) 2 r=f\sqrt{(\frac {x_c}{z_c})^2+(\frac {y_c}{z_c})^2} r=f(zcxc)2+(zcyc)2
所以,可以求出入射角
θ
\theta
θ,也可以直接由
P
c
P_c
Pc的坐标直接求出:
t
a
n
θ
=
r
f
=
(
x
c
z
c
)
2
+
(
y
c
z
c
)
2
=
x
c
2
+
y
c
2
z
c
tan\theta=\frac rf=\sqrt{(\frac {x_c}{z_c})^2+(\frac {y_c}{z_c})^2}=\frac {\sqrt{x_c^2+y_c^2}}{z_c}
tanθ=fr=(zcxc)2+(zcyc)2=zcxc2+yc2
由于发生畸变,3D点投影时不可能按照小孔成像的模型进行投影,投影点
P
0
P_0
P0到成像中心的距离
r
r
r就被压缩成
r
d
r_d
rd,像点的位置从
P
0
(
a
,
b
)
P_0(a,b)
P0(a,b)变成实际位置
p
′
(
x
′
,
y
′
)
p'(x',y')
p′(x′,y′)
因此:
t a n θ d = r d f tan\theta_d=\frac {r_d}f tanθd=frd
由于
x
x
x趋于0时,
t
a
n
x
=
x
tanx=x
tanx=x,成像过程中,实际得相机成像面很小只有几毫米,
θ
d
\theta_d
θd比较小,
t
a
n
θ
d
tan\theta_d
tanθd可近似与
θ
d
\theta_d
θd
因此:
lim
n
→
0
t
a
n
θ
d
=
θ
d
\lim_{n \to 0} tan\theta_d =\theta_d
n→0limtanθd=θd
所以:
r
d
=
f
t
a
n
θ
d
=
θ
d
r_d=ftan\theta_d=\theta_d
rd=ftanθd=θd
可根据等距投影模型
r
d
=
f
θ
r_d=f\theta
rd=fθ,由于鱼眼相机的四种投影模型都是奇函数,使用泰勒展开式
r
d
=
f
θ
≈
k
0
θ
+
k
1
θ
3
+
k
2
θ
+
k
3
θ
7
+
k
4
θ
9
r_d=f\theta\approx k_0\theta+k_1\theta^3+k_2\theta+k_3\theta^7+k_4\theta^9
rd=fθ≈k0θ+k1θ3+k2θ+k3θ7+k4θ9
第一个系数
k
0
k_0
k0可以设置成1,所以:
θ
d
=
θ
(
1
+
k
1
θ
2
+
k
2
θ
4
+
k
3
θ
6
+
k
4
θ
8
)
\theta_d=\theta(1+k_1\theta^2+k_2\theta^4+k_3\theta^6+k_4\theta^8)
θd=θ(1+k1θ2+k2θ4+k3θ6+k4θ8)
由相识三角形得:
r
d
r
=
x
′
a
=
y
′
b
\frac {r_d}r=\frac {x'}a=\frac {y'}b
rrd=ax′=by′
这里
r
d
{r_d}
rd与
r
r
r的比值直接消去焦距
f
f
f,说明与
f
f
f无关,上面的公式可以直接把
f
f
f当成单位1.
所以可以得到:
r
d
r
=
θ
d
r
\frac {r_d}r=\frac {\theta_d}r
rrd=rθd
畸变后的点
p
′
p'
p′的坐标为:
{
x
′
=
θ
d
r
a
y
′
=
θ
d
r
b
\begin{cases} x'=\frac {\theta_d}r a \\ y'=\frac {\theta_d}r b \end{cases}
{x′=rθday′=rθdb
利用相机内参将像平面上的点转换到像素坐标系得到最终图像上的点:
{
u
=
f
x
x
′
+
c
x
v
=
f
y
y
′
+
c
y
\begin{cases} u=f_xx'+c_x \\ v=f_yy'+c_y \end{cases}
{u=fxx′+cxv=fyy′+cy
3.鱼眼图像的畸变矫正
输入:鱼眼图像中一像点
p
(
u
,
v
)
p(u,v)
p(u,v)
输出:矫正后的像点坐标
step1:已知相机内参,把像点
p
p
p转为物理距离(由像素转为米)
{
x
=
(
u
−
c
x
)
/
f
x
y
=
(
v
−
c
y
)
/
f
y
\begin{cases} x=(u-cx)/f_x \\ y=(v-cy)/f_y \end{cases}
{x=(u−cx)/fxy=(v−cy)/fy
step2:求出实际像点到成像中心的距离
r
d
r_d
rd,以及等效的折射角
{
r
d
=
x
2
+
y
2
θ
d
=
a
r
c
t
a
n
(
r
d
)
\begin{cases} r_d=\sqrt{x^2+y^2} \\ \theta_d=arctan(r_d) \end{cases}
{rd=x2+y2θd=arctan(rd)
step3:已知相机畸变系数
(
k
1
,
k
2
,
k
3
,
k
4
)
(k_1,k_2,k_3,k_4)
(k1,k2,k3,k4),和step2得到的
θ
d
\theta_d
θd,求出实际得
θ
\theta
θ
θ
d
=
θ
(
1
+
k
1
θ
2
+
k
2
θ
4
+
k
3
θ
6
+
k
4
θ
8
)
\theta_d=\theta(1+k_1\theta^2+k_2\theta^4+k_3\theta^6+k_4\theta^8)
θd=θ(1+k1θ2+k2θ4+k3θ6+k4θ8)
可以使用牛顿迭代法求解一元高次方程:
令
f
(
θ
)
=
θ
(
1
+
k
1
θ
2
+
k
2
θ
4
+
k
3
θ
6
+
k
4
θ
8
)
−
θ
d
f(\theta)=\theta(1+k_1\theta^2+k_2\theta^4+k_3\theta^6+k_4\theta^8)-\theta_d
f(θ)=θ(1+k1θ2+k2θ4+k3θ6+k4θ8)−θd
{
θ
0
=
θ
d
θ
n
+
1
=
θ
n
f
(
θ
n
)
f
′
(
θ
n
)
\begin{cases} \theta_0=\theta_d \\ \theta_{n+1}=\theta_n \frac {f(\theta_n)}{f'(\theta_n)}\end{cases}
{θ0=θdθn+1=θnf′(θn)f(θn)
循环迭代求出
θ
\theta
θ
step4:得到
θ
\theta
θ后可以求得未畸变得像点带成像中心得距离
r
r
r
r
=
t
a
n
θ
r=tan\theta
r=tanθ
step5:求出未畸变像点的坐标
{
x
′
=
r
θ
d
x
y
′
=
r
θ
d
y
\begin{cases}x'=\frac r{\theta_d}x \\ y'=\frac r{\theta_d}y \end{cases}
{x′=θdrxy′=θdry
step6:根据相机内参得到矫正后的坐标
{
u
=
f
x
x
′
+
c
x
v
=
f
y
y
′
+
c
y
\begin{cases} u=f_xx'+c_x \\ v=f_yy'+c_y \end{cases}
{u=fxx′+cxv=fyy′+cy
程序
按照鱼眼相机的成像模型写的畸变矫正的函数
/*已知无畸变图像的像素点,通过鱼眼相机投影模型,找到畸变图像上的位置,随后赋值计算出无畸变图像*/
//srcImage 输入的鱼眼图像
//dstImage 输出矫正后的图像
void undistortImagess(cv::Mat& srcImage, cv::Mat& dstImage)
{
cv::Mat src = srcImage.clone();
cv::cvtColor(src, src, cv::COLOR_BGR2GRAY);
double k1 = distCoeffs.at<double>(0, 0);
double k2 = distCoeffs.at<double>(1, 0);
double k3 = distCoeffs.at<double>(2, 0);
double k4 = distCoeffs.at<double>(3, 0);
double fx = cameraMatrix.at<double>(0, 0);
double fy = cameraMatrix.at<double>(1, 1);
double cx = cameraMatrix.at<double>(0, 2);
double cy = cameraMatrix.at<double>(1, 2);
int rows = src.rows;
int cols = src.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;
//(x1,y1)相机坐标系下的坐标 (x2,y2)畸变后的坐标
double x1, y1, x2, y2;
x1 = (u - cx) / fx;
y1 = (v - cy) / fy;
double r, theta, thetad;
r = sqrt(pow(x1, 2) + pow(y1, 2));
theta = atan(r);
double theta2 = theta * theta;
double theta4 = theta2 * theta2;
double theta6 = theta2 * theta2 * theta2;
double theta8 = theta4 * theta4;
thetad = theta * (1 + k1 * theta2 + k2 * theta4 + k3 * theta6 + k4 * theta8);
x2 = thetad / r * x1;
y2 = thetad / r * y1;;
u_distorted = fx * x2 + cx;
v_distorted = fy * y2 + cy;
// 赋值 (最近邻插值)
if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < cols && v_distorted < rows)
{
image_undistort.at<uchar>(v, u) = ROIimage.at<uchar>((int)v_distorted, (int)u_distorted);
}
else {
image_undistort.at<uchar>(v, u) = 0;
}
}
}
dstImage = image_undistort.clone();
}
/*已知畸变图像的像素点,通过反投影模型,计算出投影到无畸变图像的位置,随后赋值计算出无畸变图像*/
//srcImage 输入的鱼眼图像
//dstImage 输出矫正后的图像
void unfold::undistortImagess(cv::Mat& srcImage, cv::Mat& dstImage)
{
cv::Mat src = srcImage.clone();
cv::cvtColor(src, src, cv::COLOR_BGR2GRAY);
double fx = cameraMatrix.at<double>(0, 0);
double fy = cameraMatrix.at<double>(1, 1);
double cx = cameraMatrix.at<double>(0, 2);
double cy = cameraMatrix.at<double>(1, 2);
double k1 = distCoeffs.at<double>(0, 0);
double k2 = distCoeffs.at<double>(1, 0);
double k3 = distCoeffs.at<double>(2, 0);
double k4 = distCoeffs.at<double>(3, 0);
int rows = src.rows;
int cols = src.cols;
cv::Mat image_undistort = cv::Mat(rows, rows, CV_8UC1);
for (int v = 0; v < rows; v++)
{
for (int u = 0; u < cols; u++)
{
double uu = 0, vv = 0;
//(x1,y1)相机坐标系下的坐标 (x2,y2)投影到球面的坐标
double x1, y1, x2, y2;
x1 = (u - cx) / fx;
y1 = (v - cy) / fy;
double rd, thetad;
rd = sqrt(pow(x1, 2) + pow(y1, 2));
thetad = atan(rd);
double theta = thetad;
double xx = NewTonFunction(theta, thetad);
double rrr = tan(xx);//
x2 = rrr * x1 / rd;
y2 = rrr * y1 / rd;
uu = fx * x2 + cx;
vv = fy * y2 + cy;
// 赋值 (最近邻插值)
if (uu >= 0 && vv >= 0 && uu < cols && vv < rows) {
image_undistort.at<uchar>(vv, uu) = ROIimage.at<uchar>((int)v, (int)u);
}
else {
image_undistort.at<uchar>(vv, uu) = 0;
}
}
}
dstImage = image_undistort.clone();
}
总结
由于我做的项目,需要把鱼眼图片展开,然后拼接,但是鱼眼图片的畸变矫正,直接把边缘部分给去掉了,导致最有用的部分丢失,因此,我觉得鱼眼相机通过畸变矫正后把它最大的优势给丢掉了(视野广),不太实用。最后也没有用畸变矫正的程序,但是通过学习了解鱼眼相机的投影模型,使我知道3D点如何通过投影得到鱼眼图片,已知鱼眼图片如何通过反投影得到3D点等,在后续的工作部分起到了很大的作用,真的是我学到了很多。