zoukankan      html  css  js  c++  java
  • OpenCV 经纬法将鱼眼图像展开


    this demo on github

    前言

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

    理论部分

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

    鱼眼展开流程

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

    鱼眼标准坐标计算

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

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

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

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

    代码实现

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

    FishEye& FishEye::ImgExpand2()
    {
    	if (mImg.empty()) {
    		return *this;
    	}
    	printf("Current x is (%d ,%d)
    ", 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;
    }
    

    测试效果如下图

    在这里插入图片描述

    总结

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

  • 相关阅读:
    JAVA 数据结构(14):LIST(一)Java ArrayList
    JAVA 数据结构(13):数据结构主要种接口和类
    数据可视化基础专题(51):NUMPY基础(16)numpy 函数 (五)线性代数
    数据可视化基础专题(50):NUMPY基础(15)numpy 函数 (四)排序、条件刷选函数
    数据可视化基础专题(49):NUMPY基础(14)numpy 函数 (三)统计函数
    数据可视化基础专题(48):NUMPY基础(13)numpy 函数 (二)数学函数
    数据可视化基础专题(47):NUMPY基础(12)numpy 函数 (一)字符串函数
    数据可视化基础专题(46):NUMPY基础(11)数组操作(3) 分割数组/数组元素的添加与删除
    数据可视化基础专题(45):NUMPY基础(10)数组操作(2) 修改数组维度/连接数组
    数据可视化基础专题(44):NUMPY基础(9)数组操作(1)修改数组形状/翻转数组
  • 原文地址:https://www.cnblogs.com/unclemac/p/12783421.html
Copyright © 2011-2022 走看看