平面在球面上的投影

本文探讨了平面在相机坐标系和世界坐标系中的投影转换,特别是如何在计算机视觉中将观察平面上的点映射到球面上。通过设定观察视角和分辨率,对平面进行采样和量化,计算每个点的世界坐标。利用坐标比例关系,实现了从平面到球面的投影算法,并展示了输出效果。调整参数可以影响最终投影的大小。

平面在球面上的投影

相机的,像素坐标,图像坐标,相机坐标,世界坐标如下示意图,是一个右手坐标系
在这里插入图片描述
假设在世界坐标系有一个观察平面,和对应到相机坐标系,像素坐标的示意图。
在这里插入图片描述
假定观察者垂直视角宽度为2β,水平视角宽度为2α,初始状态下P0,P1,P2,P3,P4各点坐标如下:
在这里插入图片描述

用户需设置显示分辨率(水平分辨率/垂直分辨率),设置完分辨率后,可对世界坐标系中的点 构成的平面进行图像输出,为了求得整个平面上的坐标点在世界坐标系的坐标,需要对每一个坐标点进行换算,由于在cpu里,三角计算,乘法和除法相对于加减运算运行,比较耗时浪费资源。可对 四点间按照 进行等间隔采样,进行量化,求得平面内所有点的世界坐标。

假设设置输出分辨率为800 x 600,则
= |(Px3 – Px1)|/600
= |(Py2 – Py1)|/ 800
Pxn = Px1+ n.
Pyn = Py1+ n.
Pzn = r

最后将平面坐标投影到球面,
在这里插入图片描述
公式为啥是这样的,
外切平面与圆交点0,距离半径就是r,
根据r /R比例关系,对x,y,z进行比例计算, 求得圆面坐标。

实现算法

//
	double viewAngleDegV = 0;
	double viewAngleDegH = 0;
	double arfa   = 0;
	double berta = 0;

	viewAngleDegV = 96; //deg
	viewAngleDegH = 80; //deg
	arfa   = (viewAngleDegV /2) *PI/180;  //c++ deg
	berta = (viewAngleDegH /2) *PI/180;  //c++ deg
//
	double r = 0;  //mm
	int w = 0, h = 0;
	
	r = 2000;   //mm
	w = 480;   //pix
	h = 480;   //pix
	
	if((w % 2) != 0)
		w = w -1;
	if((h % 2) != 0)
		h = h -1;
	//
	//先用opcv读取一张照片,如果没有opcv,就是自己写读取像素值。
	cv::Mat Im0 = cv::imread("E:\\zcb_work\\2113\\pic\\ch1\\left01.jpg", 0);
	cv::Mat Im1 = cv::imread("E:\\zcb_work\\2113\\pic\\ch2\\right01.jpg", 0);

	if (Im0.empty())
		return -1;

	if (Im1.empty())
		return -1;	
	printf("++++ open image succesful +++\r\n");
	
	cv::Mat Im2 = cv::Mat::zeros(h, w, CV_8UC1);
	cv::Mat Im3 = cv::Mat::zeros(h, w, CV_8UC1);	

//
	double *Wxyz = (double *)malloc(sizeof(double)* w * h * 3);
	double *Cxyz1 = (double *)malloc(sizeof(double)* w * h * 3);
	double *Pxy1 = (double *)malloc(sizeof(double)* w * h * 2);
//
	double fx1 = 534.10766364;//mm
	double fy1 = 534.01052742;//mm
	double cx1 = 341.14525437;//mm
	double cy1 = 234.85237461;//mm

	double fx2 = 537.31809141;//mm
	double fy2 = 536.93651045;//mm
	double cx2 = 325.99098046;//mm
	double cy2 = 250.01009254;//mm
//	
	double Wxyz0[3], Wxyz1[3], Wxyz2[3], Wxyz3[3], Wxyz4[3];
	Wxyz0[0] = 0;
	Wxyz0[1] = 0;
	Wxyz0[2] =  r;

	Wxyz1[0] = -r*tan(arfa);//mm
	Wxyz1[1] = -r*tan(berta);//mm
	Wxyz1[2] =  r;//mm

	Wxyz2[0] = -r*tan(arfa);//mm
	Wxyz2[1] = r*tan(berta);//mm
	Wxyz2[2] =  r;//mm
	
	Wxyz3[0] = r*tan(arfa);//mm
	Wxyz3[1] = -r*tan(berta);//mm
	Wxyz3[2] =  r;//mm
	
	Wxyz4[0] = r*tan(arfa);//mm
	Wxyz4[1] = r*tan(berta);//mm
	Wxyz4[2] =  r;//mm//mm

//
	double dertaX = abs(Wxyz3[0] - Wxyz1[0]) / (w -1);
	double dertaY = abs(Wxyz2[1] - Wxyz1[1]) / (h - 1);
	double tempx[1200] = { 0 };
	double tempy[1200] = { 0 };

//one row world position
	{
		for (int i = 0; i < w /2;i++)
		{
			tempx[i] = Wxyz1[0] + i * dertaX;
			//printf("+ %d x:%f\r\n",i, tempx[i]);
		}

		for (int i = w /2; i < w;i++)
		{
			tempx[i] = Wxyz3[0] - (w-1 -i) * dertaX;
			//printf("- %d x:%f\r\n",i, tempx[i]);
		}
	}
//one col world position	
	{
		for (int i = 0; i < h /2 ;i++)
		{
			tempy[i] = Wxyz1[1] + i * dertaY;
			//printf("+ %d y:%f\r\n",i, tempy[i]);
		}

		for (int i = h /2; i < h;i++)
		{
			tempy[i] = Wxyz2[1] - (h-1 -i) * dertaY;
			//printf("- %d y:%f\r\n",i, tempy[i]);
		}
	}

//all world position
	{
		for(int i = 0; i < h;i++)
		{
			for(int j = 0; j < w ;j++)
			{
				//get Wxyz
				{
					Wxyz[3*(i * w + j) + 0] = tempx[j];
					Wxyz[3*(i * w + j) + 1] = tempy[i];
					Wxyz[3*(i * w + j) + 2] = r;
				}
				
				//gloable Wxyz
				double temp = 0;
				{
					temp   = pow(Wxyz[3*(i * w + j) + 0], 2);
					temp += pow(Wxyz[3*(i * w + j) + 1], 2);
					temp += pow(Wxyz[3*(i * w + j) + 2], 2);
					temp = sqrt(temp);				
					cout << "r:    " << r<< "    R:    " << temp<< endl;
					cout << "Wxyz[3*(i * w + j) + 0]:    " << Wxyz[3*(i * w + j) + 0]<< "    Wxyz[3*(i * w + j) + 1]:    " << Wxyz[3*(i * w + j) + 1]<< "    Wxyz[3*(i * w + j) + 2]:    " << Wxyz[3*(i * w + j) + 2]<< endl;
					
					temp = Wxyz[3*(i * w + j) + 2]/temp;
					cout << "r:    " << r<< "    r/R:    " << temp<< endl;
					
					Wxyz[3*(i * w + j) + 0] = Wxyz[3*(i * w + j) + 0] * temp;
					Wxyz[3*(i * w + j) + 1] = Wxyz[3*(i * w + j) + 1] * temp;
					Wxyz[3*(i * w + j) + 2] = Wxyz[3*(i * w + j) + 2] * temp;
					
					//验证半径是不是等于r,
					temp   = pow(Wxyz[3*(i * w + j) + 0], 2);
					temp += pow(Wxyz[3*(i * w + j) + 1], 2);
					temp += pow(Wxyz[3*(i * w + j) + 2], 2);
					temp = sqrt(temp);
					cout << "r:    " << r<< "     R':    " << temp<< endl;
					//和原来平面上的坐标对比
					cout << "Wxyz[3*(i * w + j) + 0]:    " << Wxyz[3*(i * w + j) + 0]<< "    Wxyz[3*(i * w + j) + 1]:    " << Wxyz[3*(i * w + j) + 1]<< "    Wxyz[3*(i * w + j) + 2]:    " 		  << Wxyz[3*(i * w + j) + 2]<< endl;
				}
			}
		}
	}
	//计算像素坐标
	{
		int i = 0;
		int j = 0;
		printf("pic2 camera position\r\n");	
		for( i = 0; i < h;i++)
		{			
			for( j = 0; j < w ;j++)
			{		
				Cxyz1[3*(i * w + j) + 0] = Wxyz[3*(i * w + j) + 0];
				Cxyz1[3*(i * w + j) + 1] = Wxyz[3*(i * w + j) + 1];
				Cxyz1[3*(i * w + j) + 2] = Wxyz[3*(i * w + j) + 2] ;

				Pxy1[2*(i * w + j) + 0] = (fx1 * Cxyz1[3*(i * w + j) + 0])/ Cxyz1[3*(i * w + j) + 2] +cx1;
				Pxy1[2*(i * w + j) + 1] = (fy1 * Cxyz1[3*(i * w + j) + 1])/ Cxyz1[3*(i * w + j) + 2] +cy1;

				if((0 < Pxy1[2*(i * w + j) + 0]  &&  Pxy1[2*(i * w + j) + 0]< Im0.cols -1) && (0 < Pxy1[2*(i * w + j) + 1]  &&  Pxy1[2*(i * w + j) + 1]< Im0.rows -1))
				{
					Im2.at<uchar>(i, j)= Im0.at<uchar>(round(Pxy1[2*(i * w + j) + 1]),round(Pxy1[2*(i * w + j) + 0]));
					//printf(" px1:%d,py1:%d", int(Pxy1[i * w + j + 0]), int(Pxy1[i * w + j + 1]));
				}
			}
		}
	}
//显示图片
	imshow("input   image0", Im0);
    imshow("output image2", Im2);
    
	cv::waitKey(0);
	return 0;

原图
在这里插入图片描述

输出后
在这里插入图片描述
输出的结果大小,可以通过改变w,h的值,和r的值,和h,v的值

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值