OpenCV 经纬法将鱼眼图像展开

举报
小麦大叔 发表于 2021/12/30 00:06:58 2021/12/30
【摘要】 文章目录 前言理论部分鱼眼展开流程鱼眼标准坐标计算标准坐标系与球坐标的转换 代码实现测试效果如下图 总结 this demo on github 前言 鱼眼镜头相比传统的镜头...


this demo on github

前言

鱼眼镜头相比传统的镜头,视角更广,采集的图像信息更加丰富,但是如果要对图像进行处理,就需要对其进行展开的操作,理论部分在很多论文中都已经有所提及,已经有比较成熟的方案,也不是什么比较新鲜的事情,笔者在此梳理整体的思路,总结一下所学知识,最后简单实现一下这个功能,如有错误之处,希望各位指正。

理论部分

鱼眼图像的展开涉及到各个坐标系的转换,其过程大致如下图所示,过程还是比较繁琐的,可以想象一下如何将地球仪展开成平面图,可能会比较容易理解一点。
复杂的理论部分

鱼眼展开流程

Created with Raphaël 2.2.0 开始 设置输出图片尺寸 获取输出图片上的单像素点标准坐标A 将标准坐标A转换空间坐标P(x,y,z) 通过坐标P求出鱼眼标准坐标FishCoord 标准坐标FishCoord求出鱼眼图片上对应的坐标 结束

鱼眼标准坐标计算

OpenCv读取一张鱼眼图片,其图片数据可以想象成发布在一个2D的笛卡尔坐标中的像素点的集合: x ∈ [ 0 , c o l s − 1 ] x \in [ 0,cols-1] x[0,cols1] y ∈ [ 0 , c o l s − 1 ] y \in [ 0,cols-1] y[0,cols1]将输入的鱼眼图片坐标换算成标准鱼眼坐标: n o r m a l C o o r d . x = ( f l o a t ) ( ( f l o a t ) x ∗ 2 / c o l s − 1 ) normalCoord.x = (float)((float)x * 2 / cols - 1 ) normalCoord.x=(float)((float)x2/cols1) n o r m a l C o o r d . y = ( f l o a t ) ( ( f l o a t ) y ∗ 2 / r o w s − 1 ) normalCoord.y = (float)((float)y * 2 / rows - 1 ) normalCoord.y=(float)((float)y2/rows1)
鱼眼坐标系相当于球体在一个平面上的2D投影,坐标范围是[ -1, 1],现需要将2D的坐标A(x,y)转化为空间坐标P(x,y,z)

标准坐标系与球坐标的转换

球坐标的表示方式: P ( r , θ , ϕ ) ⋯ ⋯ ( 1 ) P(r,\theta,\phi) \cdots\cdots(1) P(r,θ,ϕ)(1) P ( p , ϕ , θ ) ⋯ ⋯ ( 2 ) P(p,\phi,\theta) \cdots\cdots(2) P(p,ϕ,θ)(2)
其中(1)为物理中普遍的表示方式,式(2)为数学中约定的表示方式。
p p p :点P与原点O连线的径向距离,下面即用OP表示;
θ \theta θ :OP与Z轴之间的夹角;
ϕ \phi ϕ :OP在XOY平面的投影与正X轴的夹角;**
这里有空间坐标点 A ( x , y , z ) A(x,y,z) Ax,y,z,以下公式将球坐标系P点转换成笛卡尔坐标系:
x = p ∗ s i n θ ∗ c o s ϕ x = p * sin\theta * cos\phi x=psinθcosϕ y = p ∗ s i n θ ∗ s i n ϕ y = p * sin\theta * sin \phi y=psinθsinϕ z = p ∗ c o s θ z = p * cos\theta z=pcosθ

以上公式在图片中未提及,这里提及以加深理解。

代码实现

这里主要贴一下坐标的转换代码,整个项目工程请转到 github

FishEye& FishEye::ImgExpand2()
{
	if (mImg.empty()) {
		return *this;
	}
	printf("Current x is (%d ,%d)\n", mImg.cols, mImg.rows);

	Point2i circleCoord;
	circleCoord.x = mImg.cols / 2 + 2;
	circleCoord.y = mImg.rows / 2 + 5;
	int R = mImg.cols / 2;

	for (int i = 0; i < mDesImg.cols; i++) {
		for (int j = 0; j < mDesImg.rows; j++) {

			Point2f  pointDst = this->Shpere2Fish(i, j);
			int cols = (int)((pointDst.x + 1) * mImg.cols / 2);
			int rows = (int)((pointDst.y + 1) * mImg.rows / 2);
			printf("Current %d %d is (%d, %d) ", i, j, cols, rows);
			if (rows < mImg.rows && cols < mImg.cols
				&& rows >= 0 && cols >= 0) {
				Vec3b color_value = mImg.at<Vec3b>(rows, cols);
				mDesImg.at<Vec3b>(j, i) = color_value;
				std::cout << color_value << std::endl;
			}
		}
	}
	return *this;
}

cv::Point2f FishEye::Shpere2Fish(int x, int y)
{
	//归一化
	Point2f normalCoord;
	normalCoord.x = (float)(x * 2.0f / mFishMapImg.cols - 1);
	normalCoord.y = (float)(y * 2.0f / mFishMapImg.rows - 1);

	float longitude = (float)(normalCoord.x * PI);
	float latitude = (float)(normalCoord.y * PI/2);

	Point3f coordP;
	coordP.x = cos(latitude)*cos(longitude);
	coordP.y = cos(latitude)*sin(longitude);
	coordP.z = sin(latitude);
	float r = 2 * atan2(sqrt(coordP.x*coordP.x + coordP.z*coordP.z), coordP.y) / MFOV;
	float theta = atan2(coordP.z, coordP.x);

	Point2f fishCoord;
	fishCoord.x = r * cos(theta);
	fishCoord.y = r * sin(theta);

	return fishCoord;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

测试效果如下图

在这里插入图片描述

总结

整个过程中,遇到过一个较大的问题,之前一直难以理解。如果先输入一张鱼眼图像,通过鱼眼图像的坐标映射到展开图片,会出现很多像素点缺失的情况,可以测试一下,仓库对应的方法为ImgExpand()。在ImgExpand2中解决了这个问题,是从输出图片的坐标去推算并寻找鱼眼图片上相应的像素点,同样的,还有一些参数需要提取进行接口化的设计。比如提取鱼眼图像的有效区域有效区域半径的计算读取exif获取镜头视角或者预留接口展开图片后的抗锯齿化处理等等,还需要完善,另外有什么问题,望不吝赐教。

文章来源: great.blog.csdn.net,作者:小麦大叔,版权归原作者所有,如需转载,请联系作者。

原文链接:great.blog.csdn.net/article/details/82875775

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。