在图像拍摄点固定的情况下,对图像做柱面投影变换,再进行配准会得到较为理想的效果。如图所示,点P在相机所处的坐标系下的坐标为,在像素坐标系下表示为P(x,y),其中W 为图像的宽度,H 为图像的高度, f为相机的焦距。设相机中心为圆柱横截面的圆心,也就是相机坐标的原点,柱面坐标下的坐标为 。 原点与像素P(x,y)所在的直线方程表示为参数坐标的形式如下: 其中t为参数 联立得: 点Q(u,v,w)是点P(x,y)在柱面上对应的点,将三维的Q点转化为二维图像中的坐标为: 其中, 为相机水平视角。由以上公式可得P(x,y)到的投影变换公式: 其中,f为相机焦距。 相机的焦距f通常是未知的,如果拍摄的图像刚好可以拼接成为一幅水平360°的全景图像,那么可以利用拼接后的全景图的总长度近似的估计出圆柱的截面的周长L,利用公式 可以估计出圆柱截面的半径即相机焦距,但是这种估计的方式十分不准确的,并且对于不同场景的图片,相机自动对焦后的焦距也有很大的不同,由于本文处理的图像都是手机的相机拍摄的,其摄像头的视野是固定的,即水平和垂直方向的视角不变,所以在实际计算时使用相机的水平视角的来计算。处理的图片尺寸大小为512×512像素,得到的结果如图: 柱面坐标投影效果图 图像在投影之后右侧出现了黑边,对于这种投影后出现的情况有两种处理方法。一是将黑边直接裁掉,另一种方法是对图像有效的部分进行拉伸。直接裁掉黑边的方式会导致图片的尺寸减小,水平方向的像素会改变,如果水平像素出现奇数的情况就会不满足后续处理用到的基二快速傅里叶变换算法的条件,实际上会有很大的概率出现奇数的情况,为了防止这种出现奇数的情况发生,所以采用第二种方式,即对图像水平方向进行拉伸,效果如图。 柱面坐标投影拉伸后的效果图
以下是C语言程序:
void cylinder() { int i,j; double f; int x,y; double radio,len; double halfIMGROWS,halfIMGCOLS; halfIMGROWS=IMGROWS/2; halfIMGCOLS=IMGCOLS/2; f=halfIMGCOLS/(tan(CYLINDERF/2)); //可调整参数CYLINDERF for(i=0;i<IMGROWS;++i) { for(j=0;j<IMGCOLS;++j) { x=(int)((i-halfIMGROWS)*sqrt((j-halfIMGCOLS)*(j-halfIMGCOLS)+f*f)/f+halfIMGROWS+0.5); y=(int)(f*tan((j-f*atan(halfIMGCOLS/f))/f)+halfIMGCOLS+0.5); if(x<IMGROWS&&y<IMGCOLS) {//彩色图像,BGR三个通道 cylinderMatrix1[i][j*3]=BMP1.mateix[x][y*3]; cylinderMatrix1[i][j*3+1]=BMP1.mateix[x][y*3+1]; cylinderMatrix1[i][j*3+2]=BMP1.mateix[x][y*3+2]; } } } len=2*(f*atan(-halfIMGCOLS/f)+halfIMGCOLS); // printf("%f",len); ///以下是拉伸/// radio=(IMGCOLS-len)/IMGCOLS; for(i=0;i<IMGROWS;++i) { for(j=0;j<IMGCOLS;++j) { y=((int)((float)j*radio+0.5))/3*9; BMP1.mateix[i][j*3]=cylinderMatrix1[i][y]; BMP1.mateix[i][j*3+1]=cylinderMatrix1[i][y+1]; BMP1.mateix[i][j*3+2]=cylinderMatrix1[i][y+2]; } } }