鱼眼图以“镜头球面ROI”方式还原

大致实现思路:

【】引入原始的鱼眼图片,并显示;

【】创建另外几个Mat的大小;

【】确定镜头的参数(焦距,最大半径,最小半径,r = f*theta。1rad = CV_PI/180);

【】原始img的坐标参考点左上角,变化到img中心处,原点O(ox,oy);

【】创建一个二维动态数组(   vector<Point2f> points ),以原点O为参考,创建一个直角坐标系的网格;

【】创建两个数组xp,yp,分别存 points的x,y坐标

【】进行直角坐标到笛卡尔坐标的转换,转换的结果存到对应的xp,yp中;  [ cartToPolar(xp, yp, rou, phi) ]

【】求原图的每个扇形ROI区域夹角边,对应在,球面上的ROI的夹角边,度数;

【】求视点的角度;(这个角度 = 每个扇形ROI两条夹角边之和的一半)

【】选择一个原鱼眼图的ROI,变换成展开的正常图片,并显示;

【】设计算法把原鱼眼图的极坐标映射到球坐标:二维->三维;

【】遍历原鱼眼图的二维数组points里的每一个值,求解它映射到球面后的theta(线面角)、phi(光线与三维坐标里x轴的夹角值),rou;

【】用三个Mat分别存取三维坐标里的值;

【】再用一个3层的Mat合成数据;

【】使用旋转矩阵,实现球面ROI的位置变化;

【】显示原图ROI常规矫正后的图片、显示映射到球面的图片、显示球面图片旋转过后矫正的图片

【源码:】注:以下代码中,变量名与上面举例时列出的没关系

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/types_c.h>

using namespace cv;
using namespace std;

Mat fish, fishROI, nor, nor2; //鱼眼图(原图),ROI区域,正常图(效果图)
Mat mx, my; //remap参数,变换矩阵
Mat Rotation; //旋转后的坐标矩阵
Mat rou, phi; //rou 和 phi

IplImage *i1, *i2;

int ox, oy; //原点xy坐标
const int num_View = 6;

double f = 1, DR = 275, dr = 20, thetaMax = 80, thetaMin = 10, r = 1;//275
double viewPoint = CV_PI / 4; //虚拟视点度数
double view_phi[num_View + 1]; //分割区域的度数
double points[num_View + 1]; //虚拟视点的投影方向

void setO(Mat& m)
{
ox = m.cols / 2;
oy = m.rows / 2;
}

int main()
{
fish = imread("1.jpg", 1);
fishROI.create(fish.size(), fish.type());
//nor.create(fish.rows*2,fish.cols*2,fish.type());
nor.create(fish.size(), fish.type());
nor2.create(fish.size(), fish.type());
mx.create(fish.size(), CV_32FC1);
my.create(fish.size(), CV_32FC1);
setO(fish);
f = DR / (thetaMax / 180 * CV_PI);
thetaMin = dr / f;
//cout<<f;

imshow("【原鱼眼图】",fish);
waitKey(1000);
/*--------------------------------------------------------------------------------------------------------------------*/
vector<Point2f> point;//QQQQQQQQQQQQQQQQQQQ
//i对应x j对应y
for (int j = 0; j<fish.rows; j++)
{
for (int i = 0; i<fish.cols; i++)
{
point.push_back(Point2f(i + 1 - ox, oy - j + 1));
}
}

//极坐标与直角坐标互相转换的xy数组
Mat xp(point.size(), 1, CV_32F, &point[0].x, 2 * sizeof(float));
Mat yp(point.size(), 1, CV_32F, &point[0].y, 2 * sizeof(float));

//极坐标rou与phi
cartToPolar(xp, yp, rou, phi);
//ps:角度转弧度 π/180×角度,弧度变角度 180/π×弧度
//ps:依行遍历
/*--------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------*/
//每个区域的度数
for (int i = 0; i<num_View; i++)
{
view_phi[i] = i * 2 * CV_PI / num_View;//弧度制
}
view_phi[num_View] = view_phi[0] + 2 * CV_PI;

//视点的位置角度
for (int i = 0; i<num_View; i++)
{
points[i] = ((view_phi[i + 1] + view_phi[i]) / 2);//角度制
}
/*--------------------------------------------------------------------------------------------------------------------*/
for (int tmpnum = 0; tmpnum<num_View; tmpnum++)
{
//ROI
Mat xxp(fish.size(), CV_32FC1);
Mat yyp(fish.size(), CV_32FC1);
for (int j = 0; j < fish.rows; j++)
{
for (int i = 0; i < fish.cols; i++)
{
if (phi.at<float>(i + j * fish.cols) >= view_phi[tmpnum] && phi.at<float>(i + j * fish.cols) <= view_phi[tmpnum + 1])
{
xxp.at<float>(j, i) = static_cast<float>(rou.at<float>(i + j * fish.cols) * cos(phi.at<float>(i + j * fish.cols)) + ox);
yyp.at<float>(j, i) = static_cast<float>(rou.at<float>(i + j * fish.cols) * -sin(phi.at<float>(i + j * fish.cols)) + oy);
}
else
{
xxp.at<float>(j, i) = static_cast<float>(fish.cols);
yyp.at<float>(j, i) = static_cast<float>(fish.rows);
}
}
}
remap(fish, fishROI, xxp, yyp, CV_INTER_LINEAR);
imshow("原图里的ROI", fishROI);
/*--------------------------------------------------------------------------------------------------------------------*/
//极坐标映射到球坐标:二维->三维 ***
Mat xx(point.size(), 1, CV_32F);
Mat yy(point.size(), 1, CV_32F);
Mat zz(point.size(), 1, CV_32F);
double *tphi = new double[point.size()];
double *ttheta = new double[point.size()];
for (int i = 0; i<point.size(); i++)
{
ttheta[i] = (CV_PI / 2 - rou.at<float>(i) / f);
tphi[i] = (phi.at<float>(i)-CV_PI);
}
for (int j = 0; j<fish.rows; j++)
{
for (int i = 0; i<fish.cols; i++)
{
if ((CV_PI / 2 - ttheta[i + j*fish.cols])*f <= DR)/**********************DR dr********************************/
{
xx.at<float>(i + j*fish.cols) = static_cast<float>(r*cos(ttheta[i + j*fish.cols])*-cos(tphi[i + j*fish.cols]));
yy.at<float>(i + j*fish.cols) = static_cast<float>(r*cos(ttheta[i + j*fish.cols])*sin(tphi[i + j*fish.cols]));
zz.at<float>(i + j*fish.cols) = static_cast<float>(r*sin(ttheta[i + j*fish.cols]));
}
else
{
xx.at<float>(i + j*fish.cols) = static_cast<float>(0);
yy.at<float>(i + j*fish.cols) = static_cast<float>(0);
zz.at<float>(i + j*fish.cols) = static_cast<float>(0);
}
}
}
/*--------------------------------------------------------------------------------------------------------------------*/
//[x,y,z]
Mat xyz(point.size(), 3, CV_32F);
for (int i = 0; i<point.size(); i++)
{
xyz.at<float>(i * 3) = xx.at<float>(i);
xyz.at<float>(i * 3 + 1) = yy.at<float>(i);
xyz.at<float>(i * 3 + 2) = zz.at<float>(i);
}
//旋转矩阵
double mtheta = -(view_phi[2] - view_phi[tmpnum + 1]), mphi = CV_PI / 4;
double r1[3][3] = { cos(mtheta), -sin(mtheta), 0, sin(mtheta), cos(mtheta), 0, 0, 0, 1 };//绕Z轴
double r2[3][3] = { 1, 0, 0, 0, cos(mphi), sin(mphi), 0, -sin(mphi), cos(mphi) };//绕X轴
double **db_xyz = new double *[point.size()];
double **db_xyz_2 = new double *[point.size()];
double **db_xyz_3 = new double *[point.size()];
for (int i = 0; i<point.size(); i++)
{
db_xyz[i] = new double[3];
db_xyz_2[i] = new double[3];
db_xyz_3[i] = new double[3];
db_xyz[i][0] = xyz.at<float>(i * 3);
db_xyz[i][1] = xyz.at<float>(i * 3 + 1);
db_xyz[i][2] = xyz.at<float>(i * 3 + 2);
}
for (int i = 0; i<point.size(); i++)
{
db_xyz_2[i][0] = db_xyz[i][0] * r2[0][0] + db_xyz[i][1] * r2[1][0] + db_xyz[i][2] * r2[2][0];
db_xyz_2[i][1] = db_xyz[i][0] * r2[0][1] + db_xyz[i][1] * r2[1][1] + db_xyz[i][2] * r2[2][1];
db_xyz_2[i][2] = db_xyz[i][0] * r2[0][2] + db_xyz[i][1] * r2[1][2] + db_xyz[i][2] * r2[2][2];
}
for (int i = 0; i<point.size(); i++)
{
db_xyz_3[i][0] = db_xyz_2[i][0] * r1[0][0] + db_xyz_2[i][1] * r1[1][0] + db_xyz_2[i][2] * r1[2][0];
db_xyz_3[i][1] = db_xyz_2[i][0] * r1[0][1] + db_xyz_2[i][1] * r1[1][1] + db_xyz_2[i][2] * r1[2][1];
db_xyz_3[i][2] = db_xyz_2[i][0] * r1[0][2] + db_xyz_2[i][1] * r1[1][2] + db_xyz_2[i][2] * r1[2][2];
}
for (int i = 0; i<point.size(); i++)
{
xx.at<float>(i) = static_cast<float>(db_xyz_3[i][0]);
yy.at<float>(i) = static_cast<float>(db_xyz_3[i][1]);
zz.at<float>(i) = static_cast<float>(db_xyz_3[i][2]);
}
/*--------------------------------------------------------------------------------------------------------------------*/
//球坐标映射到平面坐标:三维->二维
Mat nrou(point.size(), 1, CV_32F);
Mat nphi(point.size(), 1, CV_32F);
double PY = 0;

for (int j = 0; j<fish.rows; j++)
{
for (int i = 0; i<fish.cols; i++)
{
double ntheta = (CV_PI / 2 - atan((zz.at<float>(i + j*fish.cols) / (sqrt(xx.at<float>(i + j*fish.cols)*xx.at<float>(i + j*fish.cols) + yy.at<float>(i + j*fish.cols)*yy.at<float>(i + j*fish.cols))))));
nrou.at<float>(i + j*fish.cols) = static_cast<float>((ntheta)*f);
nphi.at<float>(i + j*fish.cols) = static_cast<float>(-(atan2(yy.at<float>(i + j*fish.cols), xx.at<float>(i + j*fish.cols)) - CV_PI));
mx.at<float>(j, i) = static_cast<float>(ox - nrou.at<float>(i + j*fish.cols)*cos(nphi.at<float>(i + j*fish.cols)));
my.at<float>(j, i) = static_cast<float>(oy - nrou.at<float>(i + j*fish.cols)*-sin(nphi.at<float>(i + j*fish.cols)));

if (PY<nrou.at<float>(i + j*fish.cols)*-sin(nphi.at<float>(i + j*fish.cols)))
{
PY = nrou.at<float>(i + j*fish.cols)*-sin(nphi.at<float>(i + j*fish.cols));
}
// double range=sqrt((i-ox)*(i-ox)+(j-oy)*(j-oy));
// mx.at<float>(j,i) = static_cast<float>((ox+((i-ox)/((range/f)/atan(range/f)))));
// my.at<float>(j,i) = static_cast<float>((oy+((j-oy)/((range/f)/atan(range/f)))));
}
}
cout << PY << endl;
remap(fishROI, nor, mx, my, CV_INTER_LINEAR);
imshow("【常规矫正过的图】", nor);

Mat ans;
ans.create(nor.size(), nor.type());

for (int j = 0; j<ans.rows; j++)
{
for (int i = 0; i<ans.cols; i++)
{
double range = sqrt((i - ox)*(i - ox) + (j - oy)*(j - oy));
mx.at<float>(j, i) = static_cast<float>((ox + ((i - ox) / ((range / f) / atan(range / f)))));
my.at<float>(j, i) = static_cast<float>((oy + ((j - oy) / ((range / f) / atan(range / f)))));
}
}
remap(nor, ans, mx, my, CV_INTER_LINEAR);
imshow("【映射后的球面图】", ans);
/*--------------------------------------------------------------------------------------------------------------------*/
Point2f center = Point2f(ans.cols / 2, ans.rows / 2);
double angle = 60 * (tmpnum - 1);
double scale = 1;
int nThresholdEdge = 1;
Mat rotateMat = getRotationMatrix2D(center, angle, scale);
Mat rotateImg;
warpAffine(ans, rotateImg, rotateMat, ans.size());
imshow("【球面旋转过后矫正结果图】", rotateImg);
/*--------------------------------------------------------------------------------------------------------------------*/
delete[](tphi);
delete[](ttheta);
delete[](db_xyz);
delete[](db_xyz_2);
delete[](db_xyz_3);
cvWaitKey();
}

//cvWaitKey();
return 0;
}

时间: 2024-10-15 13:30:53

鱼眼图以“镜头球面ROI”方式还原的相关文章

通过导入虚拟电脑的方式还原centos

通过oracle vm VirtualBox安装完成一台centos,然后导出虚拟电脑,再通过导入虚拟电脑的方式还原一台centos,还原的时候改一下机器名,不要选择重新初始化所有网卡mac,还原完成后用setup设置ip 如果做mysql的主从,单单更改server_id 是不行的, 会报Last_IO_Error: Fatal error: The slave I/O thread stops because master and slave have equal mysql server

图的邻接表存储方式的建立

图的邻接表存储方式,主要由表节点与头结点组成. 头结点中主要包含两个域: 1)存放顶点信息 2)存放与顶点相连的第一个表节点的指针 表节点中主要包含两个域: 1)存放相连的节点的序号 2)指向下一个节点的指针 #define MAXNUM 100; //表节点 typedef struct ArcNode{ int adjvex;//邻接顶点编号 struct ArcNode *next;//下一邻接顶点 }ArcNode; //头结点 typedef struct AdjList{ char

KRPano资源分析工具使用说明(KRPano XML/JS解密 切片图批量下载 球面图还原 加密混淆JS还原美化)

软件交流群:571171251(软件在群内提供) krpano技术交流群:551278936(软件在群内提供) 软件功能介绍 KRPano资源分析工具具有以下特性: 1.分析保存网站资源,包括查看网站资源树形(平铺)结构,单个资源文件实时打开,整站资源批量保存,资源过滤筛选保存等功能 2.一键解密被KRPano加密的XML文件 3.一键解密被KRPano加密后的JS文件 4.解密/美化被加密或混淆的JS文件 5.KRPano网站切片图批量下载 6.KRPano切片图一键还原球面图 7.批量下载多

图的五种种存储方式【图论】

运用三种方式来实现图的存储,以适应不同的情况. 参考:ACM-ICPC程序设计系列--图论及应用 方式1:邻接矩阵 邻接矩阵是表示图的数据结构中最简单也是最常用的一种. 实现:二维数组Map[MAXN][MAXN],Map[i][j]表示点i到点j的距离. 初始化:Map[i][i] = 0,Map[i][j] = INF(i!=j),读入数据Map[i][j] = w. 时间复杂度:初始化O(n^2),建图需要O(m),总时间复杂度O(n^2). 优缺点:简单直观,可直接查询点i和点j之间是否

图的3种储存方式

图的储存方式有三种 一.邻接矩阵 优点:简洁明了,调用方便,简单易写: 缺点:内存占用大,而且没办法存重边(可能可以,但我不会),点的个数超过 3000 直接爆炸 适用范围:点的个数少,稠密图,一般结合floyed使用,可以传递闭包. 代码: scanf("%d%d",&u,&v,&w); a[u][v]=w; a[v][u]=w;// 双向边 二.邻接表 优点:占用空间小,可以快速查找每个点的出度,重边可以存,写着较为方便 缺点:查找和删除边很不方便,对于无向

图的两种遍历方式

图的遍历有两种:深度优先和广度优先.本文中,深度优先使用递归实现,每次递归找到第一个与当前结点相连且未输出过的结点继续往下递归,直至所有结点都已输出.广度优先将开始结点的所有邻接结点全部压入栈,当栈不为空时一直循环将栈首的结点的所有相邻结点压入栈. 具体代码实现如下: 1 //邻接链表 2 class Graph { 3 private: 4 //n: number of nodes 5 int n; 6 vector<int> *edges; 7 bool *visited; 8 publi

SQL Server 通过重建方式还原 master 数据库

1,备份master数据库 2,停止服务,直接删除master数据文件 3,用安装程序重建master数据文件 4,单用户模式启动SQL Server 服务,利用备份文件还原 如果有master文件本身没有损坏,有master的备份,只需要步骤4还原master即可 5,重启数据库服务,之后可以正常访问

图的种类及储存方式

一.图的种类(以下的分类不是并列的) 1.有向图:图中边的方向是一定的,不能逆序走. 2.无向图:图中的边没有方向,可以逆序走.没有正负方向 3.完全图:完全图:对于顶中的每一个顶点,都与其他的点有边直接相连 无向完全图:编辑任意一个具有n个结点的无向简单图,其边数小于等于n*(n-1)/2;我们把边数恰好等于n*(n-1)/2的n个结点的无向图称为完全图. 有向完全图:在一个n个结点的有向图中,最大边数为n*(n-1). 4.稀疏图和稠密图:一般的对于一个图来说,边的数目多的就是稠密图,边的数

图的几种存储方式

1,     邻接矩阵: struct MGraph { int ver[Max]; int arc[Max][Max]; int vers; int Eges; }; int Locate(MGraph G,int x) { for(int i=1;;i++) if(G.ver[i]==x) return i; } void CreateUDN_MG(MGraph &G) { cin>>G.vers>>G.Eges; for(int i=1;i<=G.vers;i+