图像的柱面投影纠正

  我经常问自己,生命的界限到底是什么。
        死亡带给生命意义,但是却没人想接近它。
        不停地死亡却又不停重生,二者的界限模糊不清。
        为了追寻这个答案,年纪不大的我曾一度陷入疯狂。
        后来我明白,原来是我的胸量太小,目光亦是狭窄。
        正因为我看不到共性,所以世界在我眼中变得分崩离析。

        雅尼在帕特农神庙前的音乐会上说,不久前他收看了一对刚从航天飞机上下来的宇航员的采访。采访中,宇航员描述了它环绕这颗星球的经历。他说,从那高高的地方观赏下来,地球是如此美丽。他又说,当他经过欧洲上方时,他发现他很难把国家们区分开来。他说这当中的原因,是因为那些地图上的"线"根本就不曾存在于地球上。
        这些界限,根本就不存在。
        土地,是相连的!
        原来如此,假象这些界限的存在根本就是自找麻烦。
        正确就是错误、重生就是死亡,它们本来就是同一个东西。
        我们总是受着“彼此各不相同”的幻想所束缚,如果我们哪一天停止这些假象,而把目光转到我们的相似之处上,这个世界该变得多么美好啊?我们把地球划分成不同的国家,却忘了彼此相似的事实。
        其实我们每个人都有着相同的胸量,因为,我们都是一样的。

        好了,我们进入主题。
        写了一段代码完成了图像的柱面投影纠正。 采用的理论方法是我的恩师邓飞邓老师的论文<基于SURF算法的柱面全景影像拼接方法>中的节选。因为最近要做一个全景影像的贴图,没有思路,所以就旁敲侧击一下想看看有什么发现,于是乎就看到了这个。 
 
        公式(1)中的第二行中的三角函数代表像点在图像中究竟移动了多少。像点的移动将会影响到图(b)中的P‘和Q‘的左移或者右移,这样就引起了投影后图像的像点位置在y方向上的移动,这也就是为什么在公式(1)中y‘的数值与x的数值相关。
        具体操作的时候实际上使用的是反解公式,即从结果影像的每一个像素值反解出其在原始影像中的位置。反解公式由本人自己推导,公式如下:

 
用到了opencv直接上代码:
 double Plane2Cylin_X(double x,double theta,double W,double r)
{
double xp = (theta / 2.0 + atan((x - W / 2.0) / r))*r;
return xp;
}
double Plane2Cylin_Y(double x,double y,double H,double W,double r)
{
//double yp = H / 2.0 + cos(atan((x - W / 2.0) / r))*(y - H / 2.0);
double yp = y;
return yp;
}

double Cylin2Plane_X(double xp,double theta,double W,double r)
{
double x = r*tan(xp / r - theta / 2) + W / 2.0;
return x;
}
double Cylin2Plane_Y(double xp,double yp,double H,double r,double theta)
{
//double y = (yp - H / 2) / (cos(xp / r - theta / 2)) + H / 2;
double y = yp;
return y;
}

cv::Point2f PlaneP2CylinP(cv::Point2f PlaneP, double theta, double H, double W, double r)
{
double x = PlaneP.x;
double y = PlaneP.y;
double xp = Plane2Cylin_X(x, theta, W, r);
double yp = Plane2Cylin_Y(x, y, H, W, r);
cv::Point2f xpyp(xp, yp);
return xpyp;
}
void Panoramic_2_Cylindrical()
{
//std::string PanoramaIMGFile = std::string(".\\Data\\picture\\PanoramaIMG.jpg");//全景
std::string SinglePanoramaIMGFile = std::string(".\\Data\\picture\\SingleSphericalIMG.JPG");//单张
//cv::Mat PanoramaIMG = imread(PanoramaIMGFile);
cv::Mat SinglePanoramaIMG = imread(SinglePanoramaIMGFile);
//首先计算并猜测全景相机的CCD宽度
double CCDW_mm = 35.9;
double f_mm = 20;//同样满足焦距越大投影出来畸变越小的原理
double MaxLen = SinglePanoramaIMG.cols > SinglePanoramaIMG.rows ? SinglePanoramaIMG.cols : SinglePanoramaIMG.rows;
double f_pix = MaxLen / CCDW_mm*f_mm;
cout << "f_pix:" << f_pix << endl;
double r = f_pix;//一般投影的半径就取r
int W_col = SinglePanoramaIMG.cols;
int H_row = SinglePanoramaIMG.rows;
double theta = 2 * atan(W_col / (2 * r));
cout << "theta:" << theta << endl;//每张影像在全景影像中的视场角

//原始四个角点的位置:
cv::Point2f leftUp = cv::Point2f(0.0, 0.0);//x,y
cv::Point2f leftDown = cv::Point2f(0.0, H_row);//也许位置应该扩大一个像素比较好
cv::Point2f rightUp = cv::Point2f(W_col, 0.0);
cv::Point2f rightDown = cv::Point2f(W_col, H_row);
//变换后四个角点的位置:
cv::Point2f leftUp_Cylin = PlaneP2CylinP(leftUp, theta, H_row, W_col, r);
cv::Point2f leftDown_Cylin = PlaneP2CylinP(leftDown, theta, H_row, W_col, r);
cv::Point2f rightUp_Cylin = PlaneP2CylinP(rightUp, theta, H_row, W_col, r);
cv::Point2f rightDown_Cylin = PlaneP2CylinP(rightDown, theta, H_row, W_col, r);
cout << "leftUp_Cylin " << leftUp_Cylin << endl;
cout << "leftDown_Cylin " << leftDown_Cylin << endl;
cout << "rightUp_Cylin " << rightUp_Cylin << endl;
cout << "rightDown_Cylin " << rightDown_Cylin << endl;
int maxX = cvRound(rightDown_Cylin.x + 0.5);
int maxY = cvRound(rightDown_Cylin.y + 0.5);
cout << "maxX 、Y:" << maxX << " " << maxY << endl;
cv::Mat CylinImg(maxY, maxX, CV_8UC3, Scalar(0, 0, 0));//row cols
//每张图像翻解出原来图像的位置:
for (int r_y = 0; r_y<maxY; r_y++)
{
for (int c_x = 0; c_x<maxX; c_x++)
{
cv::Point2f CylinPoint = cv::Point2f(c_x, r_y); 
//double Cylin2Plane_X(double xp,double theta,double W,double r)
//double Cylin2Plane_Y(double xp,double yp,double H,double r,double theta)
int findPlane_x = cvRound(Cylin2Plane_X(c_x, theta, W_col, r));
int findPlane_y = cvRound(Cylin2Plane_Y(c_x, r_y, H_row, r, theta));
//越界判断
if (findPlane_x < 0 || findPlane_x >= maxX)continue;
if (findPlane_y < 0 || findPlane_y >= maxY)continue;
//原来图像上对应的像素值
int color_b = SinglePanoramaIMG.at<cv::Vec3b>(findPlane_y, findPlane_x)[0];
int color_g = SinglePanoramaIMG.at<cv::Vec3b>(findPlane_y, findPlane_x)[1];
int color_r = SinglePanoramaIMG.at<cv::Vec3b>(findPlane_y, findPlane_x)[2];
CylinImg.at<cv::Vec3b>(r_y, c_x)[0] = color_b;
CylinImg.at<cv::Vec3b>(r_y, c_x)[1] = color_g;
CylinImg.at<cv::Vec3b>(r_y, c_x)[2] = color_r;
}
}
std::string SaveStr = std::string(".\\Output\\OutCylin.jpg");
imwrite(SaveStr, CylinImg);
}
原始影像和转换结果如下:
 
下一步要做的是借鉴这一理论,制作出球面投影和平面投影的转换代码。 
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
全景图像拼接是将多幅有重叠区域的图像拼接成全视角360o的平面图像。全景图像拼接技术是一种基于图像绘制技术、图像处理及计算机几何学等多领域的综合技术,由于对硬件要求低,真实感较好等优点,是目前虚拟现实、三维重建等的重点研究方向。鱼眼图像是通过在相机上安装超广视角的鱼眼镜头所拍摄,具有视域广,一次成像为非线性图像的特点,比普通视觉图像拼接所需的原图像少,效率高,但拼接难度大。 本文介绍了鱼眼图像拼接技术的研究背景、应用领域。重点研究了图像拼接技术的鱼眼桶形畸变校正、投影变换理论、SIFT匹配以及图像融合等技术,对比分析了它们的理论基础、实现方式、运算性能以及还有的不足。 鱼眼图像形变严重,首先需要校正为符合人类视觉的线性图像。针对传统的经纬度校正法还存在一定拱形失真的问题,本文提出了渐进方程校正法,此方法不考虑用镜头参数来构建复杂的投影模型,完成鱼眼图像的校正,单帧图像视觉效果较好且更利于拼接;针对多帧图像的拼接问题,考虑到鱼眼图像的特点,只截取每帧图像的部分区域进行定位匹配,在此区域进行SIFT特征的全景图像拼接,运算量比全局SIFT算法大幅减少,耗时相应降低;为生成实际景物连贯的全景图像,本文采用柱面投影变换,为了解决投影变换后图像产生阶梯现象,文对比了几种常用算法,并采用双线性插值算法有效解决该问题;在图像融合过程,分析比较了几种经典算法,选择渐入渐出融合算法对待拼接的图像进行处理,使得拼接后图像重叠区域过渡平滑,基本实现无缝拼接,在图像处理的速度和效果达到一定的均衡。 最后,为了便于全景图像的理解和观察,本文还设计了全景柱形环绕浏览人机交互界面,实现了垂直180o,水平360o空间的动态环绕视觉观察。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值