学习鱼眼相机的总结

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


理论知识

1.鱼眼镜头的投影模型

  鱼眼镜头一般是由十几个不同的透镜组合而成的,如下图所示,在成像的过程中,入射光线经过不同程度的折射,投影到尺寸有限的成像平面上,使得鱼眼镜头与普通镜头相比起来拥有了更大的视野范围。
在这里插入图片描述
鱼眼相机的成像模型近似为球面投影模型

鱼眼相机的成像过程可近似分解成两步:

  1. 将三维空间上的点投影到虚拟球面上(线性)
  2. 将球面上的点投影到图像平面上(非线性)
    在这里插入图片描述

由上图可知,鱼眼相机的设计引入了畸变,而且所成影像存在非常严重的畸变,特别是径向畸变,因此畸变模型主要考虑径向畸变。

鱼眼相机的投影函数是为了尽可能将庞大的场景投影到有限的图像平面所设计的。根据投影函数的不同将鱼眼相机的设计模型大致分为等距投影模型、等立体角投影模型、正交投影模型和体视投影模型四种。相机的成像模型实际上表征的是成像的像高与入射角之间的映射关系。

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=Rxwywzw+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 n0limtanθ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=(ucx)/fxy=(vcy)/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点等,在后续的工作部分起到了很大的作用,真的是我学到了很多。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值