360全景图拼接

最近做了一个关于全景拼接的,觉得这是一个比较简单地项目吧,但是真正做起来还是有很多问题,期间也再网上看大家做的,基本上都只拼接了两张图,当我拼接全景的时候,畸变就会指数式的增长,后来发现大家通常用的方法有点问题,在这里我说一下我的思路。

  • 柱面投影
  • 特征点的提取和匹配
  • 图像融合

柱面投影

具体的原理和推导过程百度百科就有,这里就不说了。
这是柱面投影公式:

⎧⎩⎨⎪⎪x′=r∗W2−r(arctan(W2−xr))y′=H2−r(H2−y)d

\begin{cases} x'=r*\frac{W}{2}-r(arctan(\frac{\frac{W}{2}-x}{r}))\\y'=\frac{H}{2}-\frac{r(\frac{H}{2}-y)}{d} \end{cases}
其中,

⎧⎩⎨⎪⎪⎪⎪r=W2tana2w=2arctan(W2r)d=H2+(W2−x)2‾‾‾‾‾‾‾‾‾‾‾‾‾‾√

\begin{cases} r=\frac{W}{2tan\frac{a}{2}} \\w=2arctan(\frac{W}{2r}) \\d=\sqrt{\frac{H}{2}+(\frac{W}{2}-x)^2} \end{cases}
但是在使用整个投影公式的时候回出现一个问题,因为经过数值计算以后,对应的坐标是小数,做近似处理后,投影后的图片会有锯齿现象。所以可以使用柱面反投影,遍历柱面图像中的每一个像素点,得到每一点在原始图像中的采样点的像素坐标值,虽然得到的采样点的坐标也是小数值,但是可以根据双线性插值运算来近似求得该小数位置的颜色值。
反投影公式:

⎧⎩⎨⎪⎪x=W2+r∗tan(x′r−w2)y=H2+d∗(y′−H2)r

\begin{cases} x=\frac{W}{2}+r*tan(\frac{x'}{r}-\frac{w}{2}) \\y=\frac{H}{2}+\frac{d*(y'-\frac{H}{2})}{r} \end{cases}

Mat cyP(Mat imageIn, int n){const float PI = 3.1415926;int w = imageIn.cols;int h = imageIn.rows;Mat imageOut(imageIn.rows, imageIn.cols, imageIn.type());//新建一张与imagIn相同尺寸类型的图片double sita = 2 * PI / n;double f = w / 2 / tan(sita / 2);for (int i = 0; i < h; i++){for (int j = 0; j < w; j++){double x = f*tan(j / f - sita / 2) + w / 2;double y = (i - h / 2)*sqrt((x - w / 2)*(x - w / 2) + f*f) / f + h / 2;//柱面反投影(i,j为柱面图上坐标;x,y为原图上坐标)if (x > 0 && x < w - 1 && y > 0 && y < h - 1){double u, v;u = x - int(x);v = y - int(y);Vec3b s0, s1, s2, s3, s4;s1 = imageIn.at<Vec3b>(int(y), int(x));s2 = imageIn.at<Vec3b>(int(y), int(x) + 1);s3 = imageIn.at<Vec3b>(int(y) + 1, int(x));s4 = imageIn.at<Vec3b>(int(y) + 1, int(x) + 1);s0 = (1 - u)*(1 - v)*s1 + (1 - u)*v*s2 + u*(1 - v)*s2 + u*v*s3;//使用双线性插值计算对应点的像素值imageOut.at<Vec3b>(i, j) = s0;}if (x == w - 1 || y == h - 1){imageOut.at<Vec3b>(i, j) = imageIn.at<Vec3b>(x, y);}}}return imageOut;
}

特征点的匹配和筛选

这里就直接使用了 SURF 特征点提取,先通过两个特征向量之间的欧氏距离进行一次筛选,接着用 RANSAC方法计算这些关键点之间的变换矩阵,计算出内点和野点。

initModule_nonfree();//初始化模块,使用SIFT或SURF时用到 Ptr<FeatureDetector> detector = FeatureDetector::create("SURF");//创建SIFT特征检测器,可改成SURF/ORBPtr<DescriptorExtractor> descriptor_extractor = DescriptorExtractor::create("SURF");//创建特征向量生成器,可改成SURF/ORBPtr<DescriptorMatcher> descriptor_matcher = DescriptorMatcher::create("BruteForce");//创建特征匹配器  Mat img2ROI = img2(Rect(0, 0, img1.cols, img1.rows));//对image2提取感兴趣区域,为了减少计算量和避免最后一张计算矩阵出错//特征点检测vector<KeyPoint> m_LeftKey, m_RightKey;detector->detect(img1, m_LeftKey);detector->detect(img2ROI, m_RightKey);//根据特征点计算特征描述子矩阵,即特征向量矩阵  Mat descriptors1, descriptors2;descriptor_extractor->compute(img1, m_LeftKey, descriptors1);descriptor_extractor->compute(img2ROI, m_RightKey, descriptors2);//特征匹配  vector<DMatch> matches;//匹配结果  descriptor_matcher->match(descriptors1, descriptors2, matches); //计算匹配结果中距离的最大和最小值,距离是指两个特征向量间的欧式距离,表明两个特征的差异,值越小表明两个特征点越接近  double max_dist = 0;double min_dist = 100;for (int i = 0; i<matches.size(); i++){double dist = matches[i].distance;if (dist < min_dist) min_dist = dist;if (dist > max_dist) max_dist = dist;}//筛选出较好的匹配点  vector<DMatch> goodMatches;for (int i = 0; i<matches.size(); i++){if (matches[i].distance < 0.2 * max_dist)//0.2这个阈值可以调整{goodMatches.push_back(matches[i]);}}//RANSAC匹配过程vector<DMatch> m_Matches = goodMatches;// 分配空间int ptCount = (int)m_Matches.size();Mat p1(ptCount, 2, CV_32F);Mat p2(ptCount, 2, CV_32F);// 把Keypoint转换为MatPoint2f pt;for (int i = 0; i<ptCount; i++){pt = m_LeftKey[m_Matches[i].queryIdx].pt;p1.at<float>(i, 0) = pt.x;p1.at<float>(i, 1) = pt.y;pt = m_RightKey[m_Matches[i].trainIdx].pt;p2.at<float>(i, 0) = pt.x;p2.at<float>(i, 1) = pt.y;}// 用RANSAC方法计算FMat m_Fundamental;vector<uchar> m_RANSACStatus;       // 这个变量用于存储RANSAC后每个点的状态,0表示野点,1表示内点findFundamentalMat(p1, p2, m_RANSACStatus, FM_RANSAC);// 计算野点个数int OutlinerCount = 0;for (int i = 0; i<ptCount; i++){if (m_RANSACStatus[i] == 0)    // 状态为0表示野点{OutlinerCount++;}}int InlinerCount = ptCount - OutlinerCount;   // 计算内点个数

图像的融合

这里就是比较重要的部分,之前大家都会计算一个单映性矩阵,H=findHomography, 我们会使用这个来筛选匹配点是没有问题的,就像上面的标准矩阵是一样的功能,但是后来大家在融合的时候用这个矩阵去变换整个图片就不正确了,因为这个矩阵式适合在平面上的计算,而我们这边是360柱面的,在计算矩阵之前我们已经进行了柱面投影,不再适用这个变换矩阵了。

其实我们接下来需要做的更简单,只要求出对应匹配点之间的平移向量,然后通过平移,将图片融合就可以了

融合的时候,采用以下的公式,让融合部分更加平滑一点:
d1d1+d2∗img1+d2d1+d2∗img2 \frac{d1}{d1+d2}*img1+\frac{d2}{d1+d2}*img2

vector<float> m_DiffX;//用来储存对应匹配点之间X的距离vector<float> m_DiffY;//用来储存对应匹配点之间Y的距离m_DiffX.resize(InlinerCount);m_DiffY.resize(InlinerCount);InlinerCount = 0;for (int i = 0; i<ptCount; i++){if (m_RANSACStatus[i] != 0){float x1 = p1.at<float>(i, 0);float y1 = p1.at<float>(i, 1);float x2 = p2.at<float>(i, 0);float y2 = p2.at<float>(i, 1);m_DiffX[InlinerCount] = img1.cols - x1 + x2;m_DiffY[InlinerCount] = y1 - y2;//冒泡排序对应匹配点X和Y的距离for (int k = 0; k < InlinerCount; k++){if (m_DiffX[InlinerCount - k] < m_DiffX[InlinerCount - k -1]){ //判断所取点是否在图片范围内float t1 = m_DiffX[InlinerCount - k];m_DiffX[InlinerCount - k] = m_DiffX[InlinerCount - k -1];m_DiffX[InlinerCount - k -1] = t1;}if (m_DiffY[InlinerCount - k] < m_DiffY[InlinerCount - k - 1]){float t2 = m_DiffY[InlinerCount - k];m_DiffY[InlinerCount - k] = m_DiffY[InlinerCount - k - 1];m_DiffY[InlinerCount - k - 1] = t2;}}InlinerCount++;}}//取中值作为平移的向量int diffX = m_DiffX[int(InlinerCount/2)];int diffY = m_DiffY[int(InlinerCount / 2)];//cout << "diffY=" << diffY << endl;int h = img1.rows;int w = img2.cols + img1.cols - diffX;Mat img_result = Mat::zeros(h, w, img1.type());//新建融合效果图空间int nc = w*img_result.channels();int nc_lift = (img1.cols - diffX)*img1.channels();//左边部分的宽int nc_right = img1.cols*img1.channels();//右边比分的宽for (int i = 0; i < h; i++){for (int j = 0; j < nc; j++){if (j < nc_lift){img_result.at<uchar>(i, j) = img1.at<uchar>(i, j);//左边部分直接取img1的点}else if (j >= nc_right){if (i - diffY >= 0 && i - diffY < img_result.rows){img_result.at<uchar>(i, j) = img2.at<uchar>(i - diffY, j - nc_lift);//右边部分直接取img2的点}}else {if (i - diffY >= 0 && i - diffY < img_result.rows){float percentRight = (j - nc_lift) / (float)(diffX*img1.channels());//中间融合部分的点两张图进行融合处理img_result.at<uchar>(i, j) = img1.at<uchar>(i, j)*(1 - percentRight) + img2.at<uchar>(i - diffY, j - nc_lift)*percentRight;}}}}

360柱状全景图拼接相关推荐

  1. 全景效果图html5,HTML5+JS实现可交互360°柱状全景图浏览

    以前写文章总喜欢把自己折腾的过程和思路都写出来 不知道为什么 懒,现在都喜欢直接上干货了,大概是为了节约大家的时间吧 看标题是不是以为我要手动canvas秒天秒地?别瞎想了就我这样的菜逼只有调库的份 ...

  2. 什么是酷雷曼VR全景视图?全景图拼接的原理是什么?

    随着社会和科技的发展,我们对企业的宣传以及产品的介绍,都是以网站的方式展示给客户的.试想一下,如果两个企业,一个是普通的平面展示,一个是全方面360度的VR全景视图,客户会更喜欢哪个呢?当然是全景视图 ...

  3. VML 画统计 柱状、饼图、折线

    <!-- --> <!-- 涉及文件 alt.js / function.asp--> <!-- 必须包含页面所有代码 --> <!-- 高度定义有待改进 c ...

  4. 使用 Vml 制作立体柱状投票统计图的完整程序

    作者:lshdic   http://blog.csdn.net/lshdic/ <!--以下便是完整的 Js+Vml 制作柱状投票统计图的完整程序,保存为HTM文件运行即可看到效果 其中 ar ...

  5. 全景图拼接算法实现与改进

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 本文由知乎作者z.defying授权转载,不得擅自二次转载.原文链接:https://zhuanlan ...

  6. javascript柱状统计图表

    工作需要,用javascript做一个统计图表: 完成后,做个笔记,大家分享一下,互相学习.其中还有点问题,还不是很完善. 其中参考了百度空间,中管理中心,访问统计,的js统计图表.但是应用上还是有差 ...

  7. python横向柱状图-python绘制横向水平柱状条形图Bar

    python绘制横向水平柱状条形图Bar import matplotlib import random import matplotlib.pyplot as plt # 中文乱码和坐标轴负号处理. ...

  8. 柱状折线图2-双柱状重合堆积折线-重写图例点击事件

    本例子: 使用了formatter方法重写了提示层的展示数据 使用了双x轴实现重合 使用了stack实现堆积 使用了legendselectchanged和dispatchAction重写了图例点击事 ...

  9. Python的可视化包 – Matplotlib 2D图表(点图和线图,.柱状或饼状类型的图),3D图表(曲面图,散点图和柱状图)...

    Python的可视化包 – Matplotlib Matplotlib是Python中最常用的可视化工具之一, 可以非常方便地创建海量类型地2D图表和一些基本的3D图表.Matplotlib最早是为了 ...

最新文章

  1. PHP+MySQL手工注入问题及修复
  2. c++中把一个函数中的语句复制到另一个语句中报错_从底层看前端(十一)—— JavaScript语法:脚本,模块和函数体。...
  3. OpenCV 升降维度
  4. 使用TreadPool时的ThreadLocal示例
  5. httpclient4 中文版帮助文档,最新官方版翻译版(第一章 上)
  6. vim显示python嵌套级_在Vim中为Python突出显示语法
  7. hive 导出json格式 文件_hive中创建hive-json格式的表及查询
  8. mysql的配置文件适用5.6与5.7
  9. qq飞车手游微信新服务器失败,关于QQ飞车手游部分玩家更新失败的问题说明
  10. MySQL Study之--MySQL下图形工具的使用(phpMyAdmin)
  11. mysql 小辉_小辉-MySQL数据库教程 完整高清版40集全!(主流)
  12. linux信号常用函数
  13. PWA--未来式app
  14. 胡晓博:3月21日阿里云北京峰会ECS大神
  15. quickpcb添加pcb库_QuickPcb元件库下载
  16. 书城项目 软件可行性分析报告
  17. c语言编程悬臂梁受力分析,悬臂梁受力分析.doc
  18. 化繁为简,弱监督目标定位领域的新SOTA - 伪监督目标定位方法(PSOL) | CVPR 2020
  19. 巅峰之战,一“码”当先!第三届“先导杯”强势来袭
  20. Win10系统开机后任务栏卡死解决方法

热门文章

  1. 血与荣耀(第一章-激战)
  2. 简练软考知识点整理-估算活动资源
  3. JS: || 和 ??
  4. R语言使用mgcv包中的gam函数拟合广义加性模型(Generalized Additive Model,GAMs):从广义加性模型GAM中抽取学习到的样条函数(spline function)
  5. 【学习笔记】群论入门
  6. 害怕,刷人超过70%?3招应对校招笔试|大厂笔试自救指南|应届生必看
  7. 2021年三门中学高考成绩查询,2021长沙市地区高考成绩排名查询,长沙市高考各高中成绩喜报榜单...
  8. 2021湖南省地区高考成绩排名查询,2021长沙市地区高考成绩排名查询,长沙市高考各高中成绩喜报榜单...
  9. Unity3d 网页插件BestHttp使用介绍
  10. python Matplotlib 控制x轴和y轴都为整数