Opencv實現全景圖像展開之柱面展開和透視展開法

 

有一篇論文介紹了幾種展開的方法:

《基於3D全景視覺的智能三維立體攝像設備的設計》

原始全景圖像,我從另一篇博客中下載下來的:

展示一下結果:

柱面展開圖:

 

標題

 

 透視展開結果:

 

一  圓柱展開 

cylinderOn

void  cylinderOn(Mat Src)
{
	int nbottom = 0;
	int ntop = 0;
	int nright = 0;
	int nleft = 0;

	//根據邊界值來獲得直徑
    nright = Src.cols;
	nleft = 0;
	nbottom = Src.rows;
	ntop =0;
	int d = min(nright - nleft, nbottom - ntop);

	Mat imgRoi;
	imgRoi = Src(Rect(nleft, ntop, d, d));
	imshow("ROI", imgRoi);
	imwrite("ROI.jpg", imgRoi);

	Mat dst(imgRoi.size(), CV_8UC3, Scalar(255, 255, 255));

	//建立映射表
	Mat map_x, map_y;
	map_x.create(imgRoi.size(), CV_32FC1);
	map_y.create(imgRoi.size(), CV_32FC1);
	for (int j = 0; j < d - 1; j++)
	{
		for (int i = 0; i < d - 1; i++)
		{
			map_x.at<float>(i, j) = static_cast<float>(d / 2.0 + i / 2.0*cos(1.0*j / d * 2 * CV_PI));//計算映射後的座標
			map_y.at<float>(i, j) = static_cast<float>(d / 2.0 + i / 2.0*sin(1.0*j / d * 2 * CV_PI));
		}
	}
	//opencv自帶的重映射函數
	remap(imgRoi, dst, map_x, map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));//用線性插值
	//重設大小
	resize(dst, dst, Size(), 2.0, 1.0);

	rotateImage(dst);//將圖像旋轉180度 映射之後是倒着的


	imshow("柱面投影結果", dst);
	imwrite("result.jpg", dst);
}

映射完之後旋轉180度  rotateImage 

void rotateImage(Mat &dst)//旋轉圖像
{
	Point center(dst.cols / 2, dst.rows / 2);
	double angle = 180;//旋轉180度
	double scale = 1.0;//不縮放
	Mat rotMat = getRotationMatrix2D(center, angle, scale);//計算旋轉矩陣
	warpAffine(dst, dst, rotMat, dst.size());//生成圖像
}

二 透視展開

double GetAngle(int i_ExpandWidth, int i_ExpandHeight,int outR)//獲取角度
{
	double dw_Angle = (double)i_ExpandWidth / (double)outR;
	return dw_Angle;
}

int GetRadius(int i_ExpandWidth, int i_ExpandHeight)
{
	return i_ExpandHeight;
}

CvPoint FindPoint(double dw_Angle, int i_Radius, int innerR, int x_dot, int y_dot, IplImage* src)
{
	double x, y;
	i_Radius += innerR;
	x = i_Radius * cos(dw_Angle) + x_dot;//計算新的座標 
	y = i_Radius * sin(dw_Angle) + y_dot;

	if (x < 0)x = 0;//判斷是否超過邊界 超過左邊設置爲0  超過右邊 設置爲右邊座標
	if (x >= src->width)x = src->width - 1;
	if (y < 0)y = 0;
	if (y >= src->height)y = src->width - 1;

	CvPoint pt = { (int)x,(int)y };//將點轉化爲整數座標

	return pt;
}


void perspectOn(IplImage* src)
{

	int x_dot = 314;
	int y_dot = 295;
	int innerR = 50;
	int outR = 310;

	int Width = int(2 * PI * outR);   //展開圖像的寬
	int Height = outR - innerR; //展開圖像的高

	cout << "展開圖像寬:" << Width << " 高:" << Height << endl;
	int i, j;
	double dw_Angle;
	int i_Radius;
	CvPoint pt;
	IplImage*dst;

	dst = cvCreateImage(cvSize(Width, Height), 8, 3);
	dst->origin = 0;
	cvZero(dst);

	uchar *dstData = (uchar*)dst->imageData;
	int step = dst->widthStep / sizeof(uchar);
	uchar *data1 = (uchar*)src->imageData;
	int step1 = src->widthStep / sizeof(uchar);
	int channels = src->nChannels;

	for (i = 0; i < Width-1; i++)
	{
		for (j = 0; j < Height-1; j++)
		{
			dw_Angle = GetAngle(i, j, outR);
			i_Radius = GetRadius(i, j);//獲取半徑
			pt = FindPoint(dw_Angle, i_Radius, innerR, x_dot, y_dot,src);//找轉換後的座標

			dstData[j*step + i * 3 + 0] = data1[pt.y*step1+pt.x*3+0];//重新賦值
			dstData[j*step + i * 3 + 1] = data1[pt.y*step1 + pt.x *3+ 1];
			dstData[j*step + i * 3 + 2] = data1[pt.y*step1 + pt.x *3+ 2];
		}
	}

	cvShowImage("透視", dst);
	cvSaveImage("dst.jpg", dst);
}

main函數調用

int main()
{
	Mat Src = imread("img.png");

	IplImage* src;
	src = cvLoadImage("img.png");

	cylinderOn(Src);
	perspectOn(src);

	waitKey();
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章