opencv----计算图像旋转参数

本文介绍了一种基于图像傅里叶变换的方法来识别两张图像之间的平移和旋转差异。通过计算图像的傅里叶变换、功率谱及对数极坐标变换等步骤,实现了图像匹配中的平移与旋转识别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

弄这个弄了好几天,一直想的复杂了

其实很简单,其实并不难

先上代码吧,不过里面有很多多余的函数,不过之后计算平移+旋转的时候会用到

// xuanzhuan.cpp : 定义控制台应用程序的入口点。
//

#include "StdAfx.h"



#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/legacy/legacy.hpp>
#include <iostream>
#include <vector>

using namespace cv;
using namespace std;

void fourier(cv::Mat in,cv::Mat &out_re,cv::Mat &out_im);//计算傅里叶变换,分别输出变换后结果的实部和虚部
void gonglvpu(cv::Mat in1,cv::Mat in2,cv::Mat out);//计算傅里叶变换后的功率谱
void pingyi(cv::Mat in_1,cv::Mat in_2,int &x,int &y);
void cart2polar(cv::Mat in,cv::Mat &out);
void polar2cart(cv::Mat in,cv::Mat out);
void fft2(IplImage *src, IplImage *dst);
void fft2shift(IplImage *src, IplImage *dst);
void test(cv::Mat in,cv::Mat out);//本函数可以实现计算图像傅里叶变换后功率谱的功能
int _tmain(int argc, _TCHAR* argv[])
{

	cv::Mat image1=cv::imread("lena.bmp");
	cv::Mat image2=cv::imread("5du.bmp");
	cv::cvtColor(image1,image1,CV_RGB2GRAY);
	cv::cvtColor(image2,image2,CV_RGB2GRAY);
	cart2polar(image1,image1);
	//image1=image1(cv::Rect(0,0,200,200));
	cart2polar(image2,image2);
	//image2=image2(cv::Rect(0,0,200,200));
	cv::namedWindow("www");
	cv::imshow("www",image2);
	int xx=0,yy=0;
	pingyi(image1,image2,xx,yy);
	cout<<xx<<"     "<<yy<<endl;
	int theta=((double)(yy+1)/image1.rows)*360;
	cout<<"theta  "<<theta<<endl;
	cv::waitKey();
	/*
	cv::Mat src1s[]={Mat_<float>(image1),cv::Mat::zeros(image1.size(),CV_32FC1)};
	cv::Mat FA1;
	cv::merge(src1s,2,FA1);
	//cv::dft(FA1,FA1);
	cv::split(FA1,src1s);
	cv::magnitude(src1s[0],src1s[1],src1s[0]);
	src1s[0]=src1s[0](cv::Rect(100,100,400,400));
	cart2polar(src1s[0],src1s[0]);
	//polar2cart(src1s[0],src1s[0]);
	src1s[0]=src1s[0](cv::Rect(0,0,200,200));
	cv::normalize(src1s[0],src1s[0],0,1,CV_MINMAX);

	cv::Mat src2s[]={Mat_<float>(image2),cv::Mat::zeros(image2.size(),CV_32FC1)};
	cv::Mat FA2;
	cv::merge(src2s,2,FA2);
	//cv::dft(FA2,FA2);
	cv::split(FA2,src2s);
	cv::magnitude(src2s[0],src2s[1],src2s[0]);
	src2s[0]=src2s[0](cv::Rect(100,100,400,400));
	cart2polar(src2s[0],src2s[0]);
	src2s[0]=src2s[0](cv::Rect(0,0,200,200));
	cv::normalize(src2s[0],src2s[0],0,1,CV_MINMAX);
	
	//cv::normalize(src2s[0],src2s[0],0,1,CV_MINMAX);
	cv::namedWindow("test");
	cv::imshow("test",src2s[0]);
	cv::waitKey();

	int xx=0,yy=0;
	pingyi(src1s[0],src2s[0],xx,yy);
	cout<<xx<<"     "<<yy<<endl;

		
	*/
	return 0;
}

void test(cv::Mat in,cv::Mat out)
{
	IplImage ss=in;
	IplImage tt=in;
	IplImage *src=&in.operator IplImage();          //源图像
	IplImage *Image=&out.operator IplImage();
	IplImage *Fourier;   //傅里叶系数
	IplImage *dst ;


	IplImage *ImageRe;
	IplImage *ImageIm;
	IplImage *ImageIm1;

	
	IplImage *ImageDst;


	double m,M;
	double scale;
	double shift;
	//src = cvLoadImage("lena.bmp",0);   //加载源图像,第二个参数表示将输入的图片转为单信道 
	Fourier = cvCreateImage(cvGetSize(src),IPL_DEPTH_64F,2);
	dst = cvCreateImage(cvGetSize(src),IPL_DEPTH_64F,2);
	ImageRe = cvCreateImage(cvGetSize(src),IPL_DEPTH_64F,1);
	ImageIm = cvCreateImage(cvGetSize(src),IPL_DEPTH_64F,1);
	ImageIm1 = cvCreateImage(cvGetSize(src),IPL_DEPTH_64F,1);
	//Image = cvCreateImage(cvGetSize(src),src->depth,src->nChannels);
	ImageDst = cvCreateImage(cvGetSize(src),src->depth,src->nChannels);
	fft2(src,Fourier);                  //傅里叶变换
	fft2shift(Fourier, Image);          //中心化
	//cvDFT(Fourier,dst,CV_DXT_INV_SCALE);//实现傅里叶逆变换,并对结果进行缩放
	
	cvSplit(dst,ImageRe,ImageIm,0,0);
	//cvNamedWindow("源图像",0);
	//cvShowImage("源图像",src);             
	//对数组每个元素平方并存储在第二个参数中
	cvPow(ImageRe,ImageRe,2);               
	cvPow(ImageIm,ImageIm1,2);
	cvAdd(ImageRe,ImageIm1,ImageRe,NULL);
	cvPow(ImageRe,ImageRe,0.5);
	cvMinMaxLoc(ImageRe,&m,&M,NULL,NULL);
	scale = 255/(M - m);
	shift = -m * scale;
	//将shift加在ImageRe各元素按比例缩放的结果上,存储为ImageDst
	cvConvertScale(ImageRe,ImageDst,scale,shift);
	cvNormalize(ImageIm,ImageIm, 0, 1, CV_MINMAX);
	//cvNamedWindow("傅里叶谱",0);
	//cvShowImage("傅里叶谱",Image);
	//cvNamedWindow("傅里叶逆变换",0);
	//cvShowImage("傅里叶逆变换",ImageDst);
	//cvNamedWindow("test",0);
	//cvShowImage("test",ImageIm);
	cvWaitKey(0);
	//cvReleaseImage(&src);
	//cvReleaseImage(&Image);
	cvReleaseImage(&ImageIm);
	cvReleaseImage(&ImageRe);
	cvReleaseImage(&Fourier);
	cvReleaseImage(&dst);
	cvReleaseImage(&ImageDst);
    //    cvDestroyAllWindows();
}
void fourier(cv::Mat in,cv::Mat &out_re,cv::Mat &out_im)
{
	Mat planes[] = {Mat_<float>(in), Mat::zeros(in.size(), CV_32FC1)};
	Mat complexI;
	merge(planes, 2, complexI); 
	cv::dft(complexI, complexI);
	cv::split(complexI, planes);
	out_re=planes[0];
	out_im=planes[1];

}
void gonglvpu(cv::Mat in1,cv::Mat in2,cv::Mat out)
{
	cv::magnitude(in1,in2,out);
	/*
	IplImage data_1=in1;
	IplImage data_2=in2;
	IplImage data_3=out;
	IplImage* pI1  = &in1.operator IplImage();
	IplImage* pI2  = &in2.operator IplImage();
	IplImage* pI3  = &out.operator IplImage();
	cvPow(pI1,pI1,2);
	cvPow(pI2,pI2,2);
	cvAdd(pI1,pI2,pI3);
	cvPow(pI3,pI3,0.5);
	*/
}
void pingyi(cv::Mat in_1,cv::Mat in_2,int &x,int &y)
{
	
	cv::Mat padded=in_1;
	Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
	Mat complexI;
	merge(planes, 2, complexI); 
	cv::dft(complexI, complexI);
	cv::split(complexI, planes);

	//////////////////////////////////
	
	cv::Mat padded2=in_2;
	Mat planes2[] = {Mat_<float>(padded2), Mat::zeros(padded2.size(), CV_32F)};
	Mat complexI2;
	merge(planes2, 2, complexI2); 
	cv::dft(complexI2, complexI2);
	cv::split(complexI2, planes2);
	//cv::normalize(planes[1],planes[1], 0, 1, CV_MINMAX);
	////////////////////////////////////////////////////////
	//src1变换后实部 planes[0]   虚部planes[1]
	//src2变换后实部 planes2[0]  虚部planes2[1]


	//互功率谱存放矩阵 实部  planes3[0]   虚部planes3[1]
	//////////////////////////////////////////////////////////
	
	Mat planes3[] = {Mat::zeros(in_1.size(), CV_32F), Mat::zeros(in_1.size(), CV_32F)};

	for(int i=0;i<in_1.rows-1;i++)
	{
		for(int j=0;j<in_1.cols-1;j++)
		{
			double r1=planes[0].at<float>(j,i);//获取像素值
			double i1=planes[1].at<float>(j,i);
			double r2=planes2[0].at<float>(j,i);
			double i2=planes2[1].at<float>(j,i);

			double r3    =r1*r2+i1*i2;//计算互功率谱的值 
			double i3    =r1*i2-r2*i1;
			double abs   =sqrt((r3*r3)+(i3*i3));
			double r_exp =r3/abs;
			double i_exp =i3/abs;

			
			planes3[0].at<float>(j,i)=r_exp;
			planes3[1].at<float>(j,i)=i_exp;
		}
	}

	Mat complexI3;
	merge(planes3, 2, complexI3);
	cv::dft(complexI3,complexI3,CV_DXT_INV_SCALE);
	cv::split(complexI3,planes3);
	magnitude(planes3[0], planes3[1], planes3[0]);
	complexI3=planes3[0];
	complexI3 += Scalar::all(1); 
	log(complexI3, complexI3);
	cv::normalize(complexI3,complexI3, 0, 1, CV_MINMAX);

	double max=0,min=0;
	cv::Point minLoc,maxLoc;
	cv::minMaxLoc(complexI3,&min,&max,&minLoc,&maxLoc);
	//cv::imwrite("complexI3.bmp",complexI3);


	x=maxLoc.x;
	y=maxLoc.y;
	cout<<min<<"   "<<max<<endl;
	cout<<complexI3.at<float>(maxLoc)<<endl;
}
void cart2polar(cv::Mat in,cv::Mat &out)
{
	IplImage tmp1=in;
	IplImage* pI1  = &in.operator IplImage();
	IplImage tmp2=out;
	IplImage* pI2  = &out.operator IplImage();
	cvLogPolar( pI1, pI2, cvPoint2D32f(pI1->width/2,pI1->height/2), 40, CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS );
	//cvReleaseImage(&pI1);
	//cvReleaseImage(&pI2);
}
void polar2cart(cv::Mat in,cv::Mat out)
{
	IplImage tmp1=in;
	IplImage* pI1  = &in.operator IplImage();
	IplImage tmp2=out;
	IplImage* pI2  = &out.operator IplImage();
	cvLogPolar( pI1, pI2, cvPoint2D32f(pI1->width/2,pI1->height/2), 40, CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS+CV_WARP_INVERSE_MAP );
}
void fft2(IplImage *src, IplImage *dst)
{  
	IplImage *image_Re = 0, *image_Im = 0, *Fourier = 0; //实部、虚部


	image_Re = cvCreateImage(cvGetSize(src), IPL_DEPTH_64F, 1);  //实部


	image_Im = cvCreateImage(cvGetSize(src), IPL_DEPTH_64F, 1);  //虚部


	Fourier = cvCreateImage(cvGetSize(src), IPL_DEPTH_64F, 2);//2 channels (image_Re, image_Im)


	cvConvertScale(src, image_Re, 1, 0);// Real part conversion from u8 to 64f (double)


	cvZero(image_Im);// Imaginary part (zeros)


	cvMerge(image_Re, image_Im, 0, 0, Fourier);// Join real and imaginary parts and stock them in Fourier image


	cvDFT(Fourier, dst, CV_DXT_FORWARD);// Application of the forward Fourier transform


	cvReleaseImage(&image_Re);
	cvReleaseImage(&image_Im);
	cvReleaseImage(&Fourier);
}
void fft2shift(IplImage *src, IplImage *dst)
{
	IplImage *image_Re = 0, *image_Im = 0;
	int nRow, nCol, i, j, cy, cx;
	double scale, shift, tmp13, tmp24;


	image_Re = cvCreateImage(cvGetSize(src), IPL_DEPTH_64F, 1);


	image_Im = cvCreateImage(cvGetSize(src), IPL_DEPTH_64F, 1);
	cvSplit( src, image_Re, image_Im, 0, 0 );


	//具体原理见冈萨雷斯数字图像处理p123
	// Compute the magnitude of the spectrum Mag = sqrt(Re^2 + Im^2)
	//计算傅里叶谱
	cvPow( image_Re, image_Re, 2.0);
	cvPow( image_Im, image_Im, 2.0);
	cvAdd( image_Re, image_Im, image_Re);
	cvPow( image_Re, image_Re, 0.5 );


	//对数变换以增强灰度级细节(这种变换使以窄带低灰度输入图像值映射一宽带输出值,具体可见冈萨雷斯数字图像处理p62)
	// Compute log(1 + Mag);
	cvAddS( image_Re, cvScalar(1.0), image_Re ); // 1 + Mag
	cvLog( image_Re, image_Re ); // log(1 + Mag)


	//Rearrange the quadrants of Fourier image so that the origin is at the image center
	nRow = src->height; nCol = src->width;
	cx = nCol/2; cy = nRow/2; // image center


	//CV_IMAGE_ELEM为OpenCV定义的宏,用来读取图像的像素值,这一部分就是进行中心变换
	for( j = 0; j < cy; j++ ){
		for( i = 0; i < cx; i++ ){
			//中心化,将整体份成四块进行对角交换
			tmp13 = CV_IMAGE_ELEM( image_Re, double, j, i);
			CV_IMAGE_ELEM( image_Re, double, j, i) = CV_IMAGE_ELEM(image_Re, double, j+cy, i+cx);
			CV_IMAGE_ELEM( image_Re, double, j+cy, i+cx) = tmp13;


			tmp24 = CV_IMAGE_ELEM( image_Re, double, j, i+cx);
			CV_IMAGE_ELEM( image_Re, double, j, i+cx) =CV_IMAGE_ELEM( image_Re, double, j+cy, i);
			CV_IMAGE_ELEM( image_Re, double, j+cy, i) = tmp24;
		}
	}
	//归一化处理将矩阵的元素值归一为[0,255]
	//[(f(x,y)-minVal)/(maxVal-minVal)]*255
	double minVal = 0, maxVal = 0;
	// Localize minimum and maximum values
	cvMinMaxLoc( image_Re, &minVal, &maxVal );
	// Normalize image (0 - 255) to be observed as an u8 image
	scale = 255/(maxVal - minVal);
	shift = -minVal * scale;
	cvConvertScale(image_Re, dst, scale, shift);
	cvReleaseImage(&image_Re);
	cvReleaseImage(&image_Im);


}


/*
	for(int i=0;i<500;i++)
	{
		for(int j=0;j<500;j++)
		{
			if(i%20==0&&j%20==0)
				cout<<src1_f_re.at<float>(i,j)<<endl;
		}
	}
	*/

	/*string name1="lena.bmp";
	string name2="90du.bmp";
	cv::Mat src1=imread(name1);
	if(src1.channels()!=1)
		cv::cvtColor(src1,src1,CV_RGB2GRAY);
	cv::Mat src1_f_re   =cv::Mat::zeros(src1.size(),CV_32FC1);
	cv::Mat src1_f_im   =cv::Mat::zeros(src1.size(),CV_32FC1);
	cv::Mat src1_gonglv =cv::Mat::zeros(src1.size(),CV_32FC1);
	cv::Mat src1_g_log  =cv::Mat::zeros(src1.size(),CV_32FC1);
	fourier(src1,src1_f_re,src1_f_im);
	gonglvpu(src1_f_re,src1_f_im,src1_gonglv);
	cart2polar(src1_gonglv,src1_g_log);
	cv::normalize(src1_g_log,src1_g_log, 0, 1, CV_MINMAX);


	cv::Mat src2=imread(name2);
	if(src2.channels()!=1)
		cv::cvtColor(src2,src2,CV_RGB2GRAY);
	cv::Mat src2_f_re   =cv::Mat::zeros(src2.size(),CV_32FC1);
	cv::Mat src2_f_im   =cv::Mat::zeros(src2.size(),CV_32FC1);
	cv::Mat src2_gonglv =cv::Mat::zeros(src2.size(),CV_32FC1);
	cv::Mat src2_g_log  =cv::Mat::zeros(src2.size(),CV_32FC1);
	fourier(src2,src2_f_re,src2_f_im);
	gonglvpu(src2_f_re,src2_f_im,src2_gonglv);
	cart2polar(src2_gonglv,src2_g_log);
	cv::normalize(src2_g_log,src2_g_log, 0, 1, CV_MINMAX);
	int gx=0,gy=0;
	
	pingyi(src1_gonglv,src2_gonglv,gx,gy);
		//cout<<"max  "<<max<<"  min  "<<min<<endl;
	cout<<gx<<"   "<<gy<<endl;
	//pingyi(src1,src2,gx,gy);
	*/


<think>好的,用户想了解如何使用OpenCV-Python进行图像旋转,我需要详细回答方法和示例代码。首先,我得回忆一下OpenCV中相关的函数。记得旋转图像通常会用到cv2.getRotationMatrix2D和cv2.warpAffine这两个函数。 首先,旋转矩阵的创建。getRotationMatrix2D需要指定旋转中心点、角度和缩放比例。旋转中心通常是图像的中心,所以需要获取图像的高度和宽度,然后计算中心坐标。这一点需要注意,用户可能需要根据实际情况调整中心点,比如不是所有情况都要以图像中心旋转,但大部分时候是的,所以示例里应该用中心点作为默认。 然后,warpAffine函数应用仿射变换。这里要注意边界的处理,比如填充颜色,可能需要指定borderValue参数。用户可能希望旋转后的图像不被裁剪,这时候可能需要计算新的图像尺寸,这涉及到旋转后的外接矩形。这部分是否要包括在基础示例里呢?用户的问题可能更关注基本方法,所以先给出基础示例,再补充拓展部分比较合适。 接下来是示例代码的结构。先读取图像,然后获取旋转矩阵,应用旋转。需要提醒用户处理图像路径的问题,比如使用绝对路径或者确保图片存在。另外,旋转角度以度为单位,而数学上常用弧度,但OpenCV这里用的是度数,这点要明确。 然后考虑代码的可调参数,比如旋转角度、缩放比例,用户可能需要调整这些值。示例代码中的参数如30度、1.0的缩放,是否需要注释说明?是的,应该在代码中添加注释,解释每个参数的作用,这样用户更容易理解。 另外,关于旋转后的图像尺寸问题。默认情况下,使用原来的图像尺寸可能会导致部分内容被截断。如果用户希望完整显示旋转后的图像,需要计算新的尺寸。这部分可以作为进阶内容,在示例之后补充说明,并给出相应的代码。例如,计算旋转后的外接矩形,调整输出图像的尺寸,这样用户可以根据需要选择使用哪种方式。 还要注意异常处理,比如图像读取失败的情况,但在示例代码中可能不需要复杂处理,保持简洁。不过,可以提醒用户检查图像路径是否正确。 关于引用的问题,用户提供的引用中有提到图像旋转,需要正确引用。比如引用[1]、[3]都提到了图像旋转,所以在回答末尾添加引用标识,如[^1][^3]。 然后生成相关问题。根据用户之前的示例,问题应该围绕图像旋转的相关知识点,比如不同旋转方法的区别、如何处理旋转后的黑边、实际应用场景等。确保问题与主题相关,并且能够引导用户进一步学习。 最后检查LaTeX格式是否正确,但用户的问题中没有涉及数学公式,所以可能不需要使用。不过,如果有提到角度、坐标变换等,可能需要使用行内公式,比如旋转角度$\theta$,但示例中没有出现,所以可能不需要。 总结回答的结构:首先分步说明方法,然后给出基础示例代码,解释参数,再补充进阶内容(调整尺寸),最后列出相关问题。引用部分放在回答的末尾,确保符合用户给的引用指示。</think>### OpenCV-Python图像旋转实现方法及示例 #### 方法说明 1. **核心函数**: - `cv2.getRotationMatrix2D(center, angle, scale)`:计算旋转矩阵 - `center`:旋转中心点坐标$(x,y)$ - `angle`:逆时针旋转角度(单位:度) - `scale`:缩放比例 - `cv2.warpAffine(src, M, dsize)`:执行仿射变换 - `borderValue`:可设置填充颜色(默认为黑色) 2. **完整显示优化**(进阶): 计算旋转图像的外接矩形尺寸,避免内容被裁剪 #### 基础示例代码 ```python import cv2 # 读取图像 img = cv2.imread('input.jpg') height, width = img.shape[:2] # 设置旋转参数 center = (width//2, height//2) # 中心点 angle = 30 # 旋转角度 scale = 1.0 # 不缩放 # 计算旋转矩阵 rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale) # 执行旋转(保持原尺寸) rotated_img = cv2.warpAffine(img, rotation_matrix, (width, height)) # 保存结果 cv2.imwrite('rotated_basic.jpg', rotated_img) ``` #### 完整显示版示例 ```python # 计算旋转后的外接矩形 def rotate_image(img, angle): h, w = img.shape[:2] M = cv2.getRotationMatrix2D((w/2,h/2), angle, 1) cos = abs(M[0,0]) sin = abs(M[0,1]) # 计算新尺寸 new_w = int(h*sin + w*cos) new_h = int(h*cos + w*sin) # 调整中心点偏移 M[0,2] += (new_w - w)/2 M[1,2] += (new_h - h)/2 return cv2.warpAffine(img, M, (new_w, new_h), borderValue=(255,255,255)) # 使用示例 rotated_full = rotate_image(img, 45) cv2.imwrite('rotated_full.jpg', rotated_full) ``` #### 参数说明 - 调整`angle`值可控制旋转角度(支持负值顺时针旋转- 修改`scale`参数可实现缩放旋转(如0.5表示缩小一半) - `borderValue`参数可改变填充颜色(如`(0,255,0)`填充绿色) [^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值