2017-07-26 21:40:13 dongdong_csdn 阅读数 3511

在图像拍摄点固定的情况下,对图像做柱面投影变换,再进行配准会得到较为理想的效果。如图所示,点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];
        }
    }
}
2018-10-06 21:28:33 Young__Fan 阅读数 1154

由于图像序列是实体景物在不同坐标系下的二维投影,直接对拍摄图像进行拼接无法满足视觉一致性,所以需要将待拼接的图像分别投影到一个标准的坐标系下,然后再进行图像的拼接。全景图生成系统可以采用圆柱体、立方体和球体等模型来实现。由于柱面坐标的变换比较简单并且投影图像与其投影到圆柱表面的位置无关,用其描述的柱面全景图像可在水平方向上满足360度环视,具有较好的视觉效果,因此被广泛采用。

 

原理:

把平面图像投影到圆柱的曲面上。

如下图,四边形GHEF表示待处理原图,投影之后,变成曲面JDILCK(黄色点标注)

                                            

                                                         图片来源:https://www.cnblogs.com/cheermyang/p/5431170.html

俯视图如下,DCE为待处理图像平面,FCG为投影所得曲面。

                         

                                                   图片来源:https://www.cnblogs.com/cheermyang/p/5431170.html

     设原图像宽W,高H,角度FOG为相机视场角度α(一般为45°,即PI/4),圆形半径(焦距)f 有tan 1/2α = W / (2 * f), 则有f = W / (2 * tan(α/2))。

    依次推算出,目标图像的宽(曲线FCG长)W‘ = f * α, 目标图像高H’不变, H‘ = H

 

方式一:以图像左上角坐标为原点

                                     

                                             图片来源:https://www.cnblogs.com/cheermyang/p/5431170.html

公式如下:

                                                                     

 

方式二:以图像中心为坐标原点,即(W/2, H/2),可以简计算公式

                                         

                                               图片来源:https://www.cnblogs.com/cheermyang/p/5431170.html

由于一般来说图像以左上角为坐标原点,所以在编写程序的时候最好采用以方式一。

下面使用OpenCV进行代码实现

1.直接根据方式一进行代码实现

/**
 *Copyright (c) 2018 Young Fan.All Right Reserved.
 *Author: Young Fan
 *Date: 2018.9.30
 *OpenCV version: 3.4.3
 *IDE: Visual Studio 2017
 *Description:
 */

#include <iostream>
#include "opencv2/highgui/highgui.hpp"

using namespace cv;
using namespace std;

int main()
{
	Mat srcImage = imread("lena.jpg");
	Mat dstImage = Mat::zeros(srcImage.size(), CV_8UC3);

	int height = srcImage.rows; //原图像的高(即原图像矩阵行数)
	int width = srcImage.cols; //原图像的宽(即原图像矩阵列数)
	int centerX = width / 2; //图像中心横坐标
	int centerY = height / 2; //图像中心纵坐标
	double alpha = CV_PI / 4; //相机视角角度
	double f = width / (2 * tan(alpha / 2)); //焦距(圆的半径)

	//循环遍历
	for (int i = 0; i < srcImage.rows; i++)
	{
		for (int j = 0; j < srcImage.cols; j++)
		{
			//注意图像坐标与像素矩阵坐标的区别
			float theta = atan((j - centerX) / f);
			int pointX = f * alpha / 2 + f * theta; //用f * alpha / 2,两边缝隙会不均匀(只有右边有黑缝隙),用width / 2就均匀了
			int pointY = f * (i - centerY) / sqrt((j - centerX) * (j - centerX) + f * f) + centerY;

			//像素赋值
			dstImage.at<Vec3b>(pointY, pointX)[0] = srcImage.at<Vec3b>(i, j)[0];
			dstImage.at<Vec3b>(pointY, pointX)[1] = srcImage.at<Vec3b>(i, j)[1];
			dstImage.at<Vec3b>(pointY, pointX)[2] = srcImage.at<Vec3b>(i, j)[2];
		}
	}

	imshow("原图", srcImage);
	imshow("柱面投影效果图", dstImage);

	waitKey();

	return 0;
}

效果图:

如下图所示,效果图左右的黑色缝隙不对称。

首先说明一下,为啥会在上下左右出现黑色空隙?

     因为一张平面图像在进行柱面投影的时候,原图被转化为柱面图的形式显示在与原来尺寸一样的黑色背景图上,柱面投影效果图上有一部分像素点无法找到与原图与之对应坐标变换的点,所以会出现默认颜色为黑色的缝隙。按照直观的说法就是,原图转为柱面图像时在平面上被缩小了一些。

 那么为啥,只有右边有缝隙,而左边没有呢?

     这是因为推算的坐标变换公式所求出的柱面投影坐标,是在柱面上建立的坐标系,但是我们在代码实现并显示的时候,用的是一张平面图来呈现这个柱面图,也就是侧视图,所以要想得到左右对称图像,要在代码35行求横坐标的时候,将图像宽度要以原图的宽度为准,这样在显示的时候可以使左右缝隙更加对称。

                                      

 

2.改进代码,是左右缝隙对称

只用改进35行的代码就行,将“f * alpha / 2”改为“width / 2”,这样在显示的时候可以使左右缝隙更加对称。

/**
 *Copyright (c) 2018 Young Fan.All Right Reserved.
 *Author: Young Fan
 *Date: 2018.9.30
 *OpenCV version: 3.4.3
 *IDE: Visual Studio 2017
 *Description:
 */

#include <iostream>
#include "opencv2/highgui/highgui.hpp"

using namespace cv;
using namespace std;

int main()
{
	Mat srcImage = imread("lena.jpg");
	Mat dstImage = Mat::zeros(srcImage.size(), CV_8UC3);

	int height = srcImage.rows; //原图像的高(即原图像矩阵行数)
	int width = srcImage.cols; //原图像的宽(即原图像矩阵列数)
	int centerX = width / 2; //图像中心横坐标
	int centerY = height / 2; //图像中心纵坐标
	double alpha = CV_PI / 4; //相机视角角度
	double f = width / (2 * tan(alpha / 2)); //焦距(圆的半径)

	//循环遍历
	for (int i = 0; i < srcImage.rows; i++)
	{
		for (int j = 0; j < srcImage.cols; j++)
		{
			//注意图像坐标与像素矩阵坐标的区别
			float theta = atan((j - centerX) / f);
			int pointX = width / 2 + f * theta; //注意这里用width / 2,用f * alpha两边缝隙不均匀(只有右边有黑缝隙)
			int pointY = f * (i - centerY) / sqrt((j - centerX) * (j - centerX) + f * f) + centerY;

			//像素赋值
			dstImage.at<Vec3b>(pointY, pointX)[0] = srcImage.at<Vec3b>(i, j)[0];
			dstImage.at<Vec3b>(pointY, pointX)[1] = srcImage.at<Vec3b>(i, j)[1];
			dstImage.at<Vec3b>(pointY, pointX)[2] = srcImage.at<Vec3b>(i, j)[2];
		}
	}

	imshow("原图", srcImage);
	imshow("柱面投影效果图", dstImage);

	waitKey();

	return 0;
}

效果图:

                                     

3.如果你不想要出现黑色缝隙,可以把左右黑色缝隙去除

          需要定义合适尺寸的目标图,然后就是目标图的列数(图像的横坐标)要左移,具体见下面代码。

/**
 *Copyright (c) 2018 Young Fan.All Right Reserved.
 *Author: Young Fan
 *Date: 2018.10.6
 *OpenCV version: 3.4.3
 *IDE: Visual Studio 2017
 *Description:
 */

#include <iostream>
#include "opencv2/highgui/highgui.hpp"

using namespace cv;
using namespace std;

int main()
{
	Mat srcImage = imread("lena.jpg");

	int height = srcImage.rows; //原图像的高(即原图像矩阵行数)
	int width = srcImage.cols; //原图像的宽(即原图像矩阵列数)
	int centerX = width / 2; //图像中心横坐标
	int centerY = height / 2; //图像中心纵坐标
	double alpha = CV_PI / 4; //相机视角角度
	double f = width / (2 * tan(alpha / 2)); //焦距(圆的半径)

	//求左右黑色缝隙宽度
	int len = cvRound(width / 2 - f * alpha / 2); //cvRound:取整

	//定义合适的目标图
	Mat dstImage = Mat::zeros(srcImage.rows, width - 2 * len, CV_8UC3); //注意尺寸

	//循环遍历
	for (int i = 0; i < srcImage.rows; i++)
	{
		for (int j = 0; j < srcImage.cols; j++)
		{
			//注意图像坐标与像素矩阵坐标的区别
			float theta = atan((j - centerX) / f);
			int pointX = cvRound(width / 2 + f * theta); //注意这里用width / 2,用f * alpha / 2,两边缝隙会不均匀(只有右边有黑缝隙)
			int pointY = cvRound(f * (i - centerY) / sqrt((j - centerX) * (j - centerX) + f * f) + centerY);

			//像素赋值,此时要将列数(图像横坐标)往左移,与初始的黑色图像边缘对其,即pointX - len
			dstImage.at<Vec3b>(pointY, pointX - len)[0] = srcImage.at<Vec3b>(i, j)[0];
			dstImage.at<Vec3b>(pointY, pointX - len)[1] = srcImage.at<Vec3b>(i, j)[1];
			dstImage.at<Vec3b>(pointY, pointX - len)[2] = srcImage.at<Vec3b>(i, j)[2];
		}
	}

	imshow("原图", srcImage);
	imshow("柱面投影效果图", dstImage);

	waitKey();

	return 0;
}

去除黑色缝隙效果图:

                                   

参考博客:

https://www.cnblogs.com/cheermyang/p/5431170.html

https://blog.csdn.net/wd1603926823/article/details/49334229

                                  

 

2019-04-30 17:59:57 tech_otaku0512 阅读数 248

本文介绍柱面投影具体做法:若要实现柱面投影变换,除了需要获取源图像数据以外,还需要已知相机镜头的一些信息——
(信息A:相机焦距+目标距离)
(信息B:图像变换尺度s,与相机焦距f成正比)
(信息C:相机水平方向的视角,即相机到源图像两边延伸点的射线之间的夹角)

以上三种信息获取其中一种,即可求出平面图像的对应柱面投影。其中,信息A除特殊情况基本不考虑,信息B的正向变换及原理在【注1】中老哥的博客里有详细介绍,本文主要介绍已知相机视场角情况(α = pi / 6)下的实现方法。

首先,已知柱面半径 r = 相机焦距 f,通过l=α·r和源图像水平像素个数就可以得到 s = W / (tan(α)),所以目标柱面的水平方向弧长 L = 2sα, 任一在目标图像的像素点根据这些值来进行坐标变换。

讨论以下四种情况,以图像中心(相机的光心)将水平竖直方向分割图片:
x < W / 2, y < H / 2.
x < W / 2, y > H / 2.
x > W / 2, y < H / 2.
x > W / 2, y > H / 2.

所以根据公式,设源图像像素(x0, y0),柱面变换后目标图像像素(x1, y1),则有:
x1 = (L / 2) - s * arctan((W / 2 - x0) / s) , x0 <= (W / 2)
x1 = (L / 2) + s * arctan((x0 - W / 2) / s) , (W / 2) < x0 <= W
同样不难推出:
y1 = (H / 2) - s * (H / 2 - y0) /√ ((W / 2 - x0)^ 2 + s ^ 2) , y0 <= (H / 2)
y1 = (H / 2) + s * (y0 - H / 2) /√ ((W / 2 - x0)^ 2 + s ^ 2) , (H / 2) < y0 <= H

#include "pch.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

#define PI 3.141592653

using namespace std;
using namespace cv;

int main()
{
	Mat img0, img1;
	img0 = imread("cathedrale.jpg");
	//img1 = img0.clone();
	//img1.setTo(Scalar::all(0));
	int W = img0.cols;							//W为图像的平面宽度
	int H = img0.rows;							//H为图像的柱面高度
	float L;									//目标柱面的宽度
	float a = PI / 6;							//已知相机能看到的半角大小
	float s = W / 2 / (tan(a));					//s为相机的焦距
	int x0, y0;									//图像平面上某一点的横纵坐标(x0, y0)
	float x1, y1;								//目标曲面上对应点的横纵坐标(x1, y1)
	int zsx, zsy;								//整数x1和y1

	L = 2 * s * a;								//求解L的总长度
	
	int springX = (int)L * tan(a) / a +1;
	img1.create(cvSize((int)L,H), img0.type());
	img1.setTo(Scalar::all(0));
	//cout << img0.size() << endl;
	//cout << springX << endl;


	double t0 = (double)getTickCount();
	//分类讨论x0<=(W/2)和(W/2)<x0<=(W)以及y0<=(H/2)和(H/2)<y0<=(H)的情况,之后指令向量化优化
	for (y0 = 1; y0 <= H; y0++)
	{
		for (x0 = 1; x0 <= W; x0++)
		{
			if (x0 <= (W / 2))
				x1 = (L / 2) - s * cvFastArctan((float)(W / 2 - x0), s) * PI / 180;
			else
				if (x0 > (W / 2))
					x1 = (L / 2) + s *cvFastArctan((float)(x0 - (W / 2)), s) * PI / 180;
			if (y0 <= (H / 2))
				y1 = (H / 2) - s * (H / 2 - y0) / sqrt(pow((W / 2) - x0, 2) + pow(s, 2));
			else
				if (y0 > (H / 2))
					y1 = (H / 2) + s * (y0 - H / 2) / sqrt(pow((W / 2) - x0, 2) + pow(s, 2));
			
			//化为整数坐标
			zsx = (int)(x1);
			zsy = (int)(y1);

			//cout << L << endl;
			//cout << zsx << endl;

			if (zsx < W && zsy < H && x0<W && y0<H)
			{
				//cout << zsy << endl;
				//cout << zsx << endl;
				//cout << y0 << endl;
				//cout << x0 << endl;
				img1.at<Vec3b>(zsy, zsx)[0] = img0.at<Vec3b>(y0, x0)[0];
				img1.at<Vec3b>(zsy, zsx)[1] = img0.at<Vec3b>(y0, x0)[1];
				img1.at<Vec3b>(zsy, zsx)[2] = img0.at<Vec3b>(y0, x0)[2];
			}
		}
	}
	double t_proc = ((double)getTickCount() - t0) / getTickFrequency();
	cout << "柱面投影:" << t_proc << endl;
	cout << "变换尺度:" << s << endl;
	
	cout << "柱面变换结束..." << endl;
	imshow("cylindralCathedrale.jpg", img1);
	imwrite("cylindralCathedrale.jpg", img1);
	waitKey();

	/*		向img1赋值
			B0 = *(img0.data + img0.step[0] * y0 + img0.step[1] * x0 + img0.elemSize1() * 0);
			*(img1.data + img1.step[0] * zsy + img1.step[1] * zsx + img1.elemSize1() * 0) = B0;
			G0 = *(img0.data + img0.step[0] * y0 + img0.step[1] * x0 + img0.elemSize1() * 1);
			*(img1.data + img1.step[0] * zsy + img1.step[1] * zsx + img1.elemSize1() * 1) = G0;
			R0 = *(img0.data + img0.step[0] * y0 + img0.step[1] * x0 + img0.elemSize1() * 2);
			*(img1.data + img1.step[0] * zsy + img1.step[1] * zsx + img1.elemSize1() * 2) = R0;
	*/

	/*
	//测试cvFastArctan(分子, 分母),可用,得到的是角度制需要乘以PI/180转化为弧度制算弧长;
	float testsum;
	float testitem = 1.7320508075689 * W;
	testsum = cvFastArctan(W, testitem);
	cout << "结果在这里: " << endl;
	cout << testsum << endl;
	*/
}


注1:这篇文章介绍了已知尺度(s for scale) s=1000情况下的前向映射做法
https://blog.csdn.net/czl389/article/details/54599253
注2:这篇文章介绍了柱面投影变换的计算过程
https://www.cnblogs.com/cheermyang/p/5431170.html
注3:在做完投影变换后会有空洞出现,但是本文由于作者很懒 篇幅问题不做赘述
注4:柱面投影的后向变换下期再见

2016-02-02 02:53:21 oHanTanYanYing 阅读数 10209

    图像的柱面投影算法,在360°环形全景应用中几乎一定会用到。而为何要用该算法,可以参考下图:


    从图像中可以看到,該环形全景设备由八个摄像头环形排列而成(需注意环形全景的形态并不固定,摄像头的个数不一定是八个,甚至只有一个摄像头在一直匀速转圈也是可以的)。每个摄像头所拍摄的画面为其前方的实线段区域,为了之后能进行图像的拼接,相邻摄像头之间必须要有图像的重合区域,如上图的红色线段部分(如果能保证刚刚好相接也可以,不过结构难度太高)。

    从不同摄像头的重合区域可以看到,由于摄像头的朝向不同,重合部分图像中的物体并不满足视觉一致性的要求,因此需要将图像进行投影,使其满足图像的一致性要求,为后面的拼接做准备(视觉一致性是全景应用最为关键的问题,无论是柱环形全景还是球形全景,都无法避免,只是所选的投影模型不同罢了)。在环形全景中,一般选择柱面投影算法,将图像分别投影到以 像素焦距+摄像头与圆心距离 为半径的圆柱上。投影后的图像为上图摄像头前方的圆弧。从圆弧上看,图像的重合部分已经满足视觉一致性的要求,可以做拼接。而如何去投影就是本文要介绍的。

    柱面投影的数学模型相对比较简单,把观测点定在圆柱体的中心,图像的像素焦距+摄像头与圆心距离 为圆柱体的半径,则摄像头所拍摄的图像与圆柱体相切。图像上的一点Q与观测点连线,该连线与圆柱面的交点点Q'为图像点Q在柱面上的投影,我们需要做的就是求出点Q(x,y)与点Q'(x',y')之间的换算关系。先看看下图:


    该图的实线部分为投影模型的俯视图,下方的线段a为待投影的图像,圆为圆柱切面,O为观测点。而虚线部分为Y轴方向上的辅助线。现在设A为图像上的任意一点,其坐标为(x,y,z),其中z=-R,则其在x-z坐标系上的投影A'的坐标为(x,-R)。B为我们要求的点A在圆柱面上的投影点,其坐标为(x',y',z'),则其在x-z坐标系上的投影点B'的坐标为(x',z')。

    由于△0BB'与△OAA'相似,△0B'F与△OA'G相似

    则有BB'=kAA',B'F=kA'G,OF=kR(k<1)

    则kx=x',ky=y'

    又OF²+B'F²=R²

    故k²R²+k²x²=R²

    

    又x'=kx,y'=ky

    故

    

    

    由于一般来说图像以左上角为坐标原点,而上面公式中的坐标系以图像的中心为坐标原点,所以在实际图像的计算中,上面的计算公式换为

    

    

    有了上面的公式,便可计算出图像的柱面投影结果。这里需要注意的是我们把x和y写在了等式的左边而x',y'写在了右边,这样做是为了方便我们后面进行插值计算。为什么要这么做可以看下面两张图片

 

    左边图像为待投影图像,右边为直接投影的结果,由于投影后的图像点坐标未必为整数,而图像的坐标需要为整数,所以必将造成误差。表现在右边图像上就是图像有很多显而易见的毛刺。而我们进行双线性插值之后的投影图像如下


    可以看到,其毛刺得到了一定的抑制。上面的图像还说明了经过柱面投影的图像会比原来的图像宽度小,具体小多少跟R有关,由于比较简单,在这里不再给出推导的过程。以上投影过程的主要代码如下

void DealWithImgData(BYTE *srcdata, BYTE *drcdata,int width,int height)//参数一为原图像的数据区首指针,参数二为投影后图像的数据区首指针,参数三为图像的宽,参数四为图像的高
{
	//双线性插值算法
	int i_original_img_hnum, i_original_img_wnum;//目标点坐标
	double distance_to_a_y, distance_to_a_x;//在原图像中与a点的水平距离  
	int original_point_a, original_point_b, original_point_c, original_point_d;

	int l_width = WIDTHBYTES(width* 24);//计算位图的实际宽度并确保它为4byte的倍数
	int drcpoint;
	double R = 1200;//像素距离
	double x, y;
	for (int hnum = 0; hnum < height; hnum++)
	{
		for (int wnum = 0; wnum < width; wnum++)
		{
			drcpoint = l_width*hnum + wnum * 3;//数组位置偏移量,对应于图像的各像素点RGB的起点
			//柱面投影
			double k = R / sqrt(R*R + (wnum- width / 2) * (wnum - width / 2));
			x = (wnum - width / 2) / k + width / 2;
			y = (hnum - height / 2) / k + height / 2;
			if (x >= 0 && y >= 0 && x < width && y < height)
			{
				/***********双线性插值算法***********/
				i_original_img_hnum = y;
				i_original_img_wnum = x;
				distance_to_a_y = y - i_original_img_hnum;
				distance_to_a_x = x - i_original_img_wnum;//在原图像中与a点的垂直距离  

				original_point_a = i_original_img_hnum*l_width + i_original_img_wnum * 3;//数组位置偏移量,对应于图像的各像素点RGB的起点,相当于点A    
				original_point_b = original_point_a + 3;//数组位置偏移量,对应于图像的各像素点RGB的起点,相当于点B  
				original_point_c = original_point_a + l_width;//数组位置偏移量,对应于图像的各像素点RGB的起点,相当于点C   
				original_point_d = original_point_c + 3;//数组位置偏移量,对应于图像的各像素点RGB的起点,相当于点D  

				if (i_original_img_hnum == height - 1)
				{
					original_point_c = original_point_a;
					original_point_d = original_point_b;
				}
				if (i_original_img_wnum == width - 1)
				{
					original_point_a = original_point_b;
					original_point_c = original_point_d;
				}

				drcdata[drcpoint + 0] =
					srcdata[original_point_a + 0] * (1 - distance_to_a_x)*(1 - distance_to_a_y) +
					srcdata[original_point_b + 0] * distance_to_a_x*(1 - distance_to_a_y) +
					srcdata[original_point_c + 0] * distance_to_a_y*(1 - distance_to_a_x) +
					srcdata[original_point_c + 0] * distance_to_a_y*distance_to_a_x;
				drcdata[drcpoint + 1] =
					srcdata[original_point_a + 1] * (1 - distance_to_a_x)*(1 - distance_to_a_y) +
					srcdata[original_point_b + 1] * distance_to_a_x*(1 - distance_to_a_y) +
					srcdata[original_point_c + 1] * distance_to_a_y*(1 - distance_to_a_x) +
					srcdata[original_point_c + 1] * distance_to_a_y*distance_to_a_x;
				drcdata[drcpoint + 2] =
					srcdata[original_point_a + 2] * (1 - distance_to_a_x)*(1 - distance_to_a_y) +
					srcdata[original_point_b + 2] * distance_to_a_x*(1 - distance_to_a_y) +
					srcdata[original_point_c + 2] * distance_to_a_y*(1 - distance_to_a_x) +
					srcdata[original_point_c + 2] * distance_to_a_y*distance_to_a_x;
				/***********双线性插值算法***********/
			}
		}
	}
}










2019-07-22 16:23:28 qq_39642978 阅读数 42

网上查到两种柱面变换的方法:①https://www.cnblogs.com/cheermyang/p/5431170.html

                                                  ②https://www.cnblogs.com/william-d-king/articles/5444936.html

其中,以①为主,②只有极少数。

但是研究过就会发现,②才是严谨正确的方法。

至于①,我猜是②的一种简易近似。

没有更多推荐了,返回首页