鱼眼相机模型
最近涉及鱼眼相机模型、标定使用等,作为记录,更新很久不曾更新的博客。
文章目录
- 鱼眼相机模型
- [1 相机成像](#1 相机成像)
- [2 鱼眼模型](#2 鱼眼模型)
- [3 畸变](#3 畸变)
-
- [3.1 适用针孔和MEI](#3.1 适用针孔和MEI)
- [3.2 Kannala-Brandt鱼眼模型](#3.2 Kannala-Brandt鱼眼模型)
- [4 代码实现](#4 代码实现)
1 相机成像
针孔相机:所有光线从一个孔(光心)透过,入射角 θ \theta θ = 出射角 θ d \theta_{d} θd。0-180°的光线汇聚的图像是无穷大的,所以针孔相机的视场角一般都很小。
鱼眼相机:类似于凸透镜成像,光线被聚集,入射角 θ \theta θ > 出射角 θ d \theta_{d} θd。光线汇聚的图像变小,所以鱼眼相机的视场角一般可以达到18-270°。

2 鱼眼模型
常见模型,注意,这里的 θ \theta θ是入射角!! θ d \theta_{d} θd才是出射角。

VINS
、AVP-SLAM
中采用的MEI模型--------

注意,常规鱼眼模型的去畸变和针孔相机不同,可以见
opencv
中解释;但是MEI模型经过变换后,可以直接套用针孔相机模型
3 畸变
畸变包括径向畸变(Radial Distortion )和切向畸变(Tangential Distortion)
鱼眼相机本质是利用了相机的畸变获取无穷远处的投影,主要是径向畸变

3.1 适用针孔和MEI
首先来看下去畸变的本质,如图所示,一共是四步
① 相机坐标系下某一点,是投影到光心矢量射线(相机成像原理),与图像产生交点。归一化这个坐标(如果再乘以焦距就是图像坐标系上的坐标了),获取畸变的
xy
。(这里为什么不乘以f呢,本质是矫正这一条投影到光心的矢量射线,没有本质区别,归一化计算更简单)
② 计算成像半径r
(相机成像半径r理论为r=f*tan(θd)
,3.2中有提到,也有图)
③ 基于畸变参数,计算去畸变后的xy
,此时,该点再图像坐标系中的位置被矫正
④ 然后基于去畸变后的xy
再变换到像素坐标系下,就可以获取一个矫正的像素坐标

但是实际上,我们只知道像素坐标,而非相机系坐标!所以实际中将像素坐标反变换到相机坐标系(归一化),然后就可以按照这里的去畸变流程处理了。
3.2 Kannala-Brandt鱼眼模型
参考opencv,注意,通用模型不是上面哪些等距模型,和哪些完全没关系
相机系
X c = R X + t Xc = RX + t Xc=RX+t
把相机系坐标单独取出来
x = X c 1 y = X c 2 z = X c 3 x = Xc_1\\ y = Xc_2\\ z = Xc_3 x=Xc1y=Xc2z=Xc3
归一化,计算成像半径、夹角
a = x z and b = y z r 2 = a 2 + b 2 θ = arctan ( r / 1 ) a = \frac{x}{z} \quad \text{and} \quad b = \frac{y}{z} \quad r^2 = a^2 + b^2 \quad \theta = \arctan(r/1) a=zxandb=zyr2=a2+b2θ=arctan(r/1)
去畸变,注意这里的 θ d r \frac{\theta_d}{r} rθd实际代码是 r d r \frac{r_d}{r} rrd获取的一个比例关系,
opencv
给出的公式有点误导,可以参考原始论文(成像半径 r d = f ∗ t a n ( θ d ) ≈ f ∗ θ d r_{d} = f*tan(\theta_{d}) ≈ f* \theta_{d} rd=f∗tan(θd)≈f∗θd,归一化,取f=1
,tanx
在x
接近0时候约等于x
)
θ d = θ ( 1 + k 1 θ 2 + k 2 θ 4 + k 3 θ 6 + k 4 θ 8 ) x ′ = ( θ d r ) a y ′ = ( θ d r ) b \theta_d = \theta (1 + k_1 \theta^2 + k_2 \theta^4 + k_3 \theta^6 + k_4 \theta^8) \\ x' = \left(\frac{\theta_d}{r}\right) a \\ y' = \left(\frac{\theta_d}{r}\right) b θd=θ(1+k1θ2+k2θ4+k3θ6+k4θ8)x′=(rθd)ay′=(rθd)b

获取去畸变后的像素坐标(
α
是偏斜系数,默认为0)
u = f x ( x ′ + α y ′ ) + c x v = f y y ′ + c y u = f_x (x' + \alpha y') + c_x \\ v = f_y y' + c_y u=fx(x′+αy′)+cxv=fyy′+cy
注:通过上述模型,也就是棋盘格标定得到的去畸变图像,相对于原图,会小很多!距离中心越远的区域,拉伸会很严重,校正后会被切除
4 代码实现
可以参考opencv中映射矩阵来实现
cpp
bool UnDistortMap()
{
if(is_init){
return true;
}
// 畸变映射,每一张图像的畸变映射是一致的
cv::fisheye::initUndistortRectifyMap(mK, mD, cv::Matx33d::eye(), mK,
cv::Size(Width, Height), CV_16SC2,
m_undistortMapX, m_undistortMapY);
// 通过调整cx,cy来增大去畸变后的视野
// cv::fisheye::initUndistortRectifyMap(mK, mD, cv::Matx33d::eye(), mK,
// cv::Size(mK.at<double>(0,2), mK.at<double>(1,2)), CV_16SC2,
// m_undistortMapX, m_undistortMapY);
// 或者直接把fxfy调小,缩小相机焦距,视野变大了(分辨率dx不变)
if (m_undistortMapX.empty() || m_undistortMapY.empty()) {
std::cerr << "Failed to compute undistort maps!" << std::endl;
return false;
}else{
}
is_init = true;
return true;
}
bool undistortImage(const cv::Mat& src, cv::Mat& dst)
{
if(!is_init)
return false;
cv::remap(src, dst, m_undistortMapX, m_undistortMapY, cv::INTER_LINEAR); // 去畸变
return true;
}
cpp
Computes undistortion and rectification maps for image transform by cv::remap(). If D is empty zero distortion is used, if R or P is empty identity matrixes are used.
Parameters:
K -- Camera matrix \f$K = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{_1}.
D -- Input vector of distortion coefficients k_1, k_2, k_3, k_4).
R -- Rectification transformation in the object space: 3x3 1-channel, or vector: 3x1/1x3 1-channel or 1x1 3-channel
P -- New camera matrix (3x3) or new projection matrix (3x4)
size -- Undistorted image size.
m1type -- Type of the first output map that can be CV_32FC1 or CV_16SC2 . See convertMaps() for details.
map1 -- The first output map.
map2 -- The second output map.