C++实现图像二维DFT离散傅里叶(FFT)代码——等效于opencv

这篇文章写的不错:

http://johnhany.net/2013/11/dft-based-text-rotation-correction/

 

首先是一个opencv的dft运行代码:

进行一些简单处理:

(1)去掉using namespace std(会和dft函数实现过程中的自定义complex复数结构体冲突,导致结构体不明确,验证发现 后面并不需要std命名空间,若需要自己加std:: ,并且#include)

(2)将复杂的图片导入提示直接改成预期的图片路径简化输入

(3)解释一下:

a. FFT是逐通道处理,所以用的灰度图,彩色图像也是逐通道FFT的

b. opencv的dft(img,img),输入是二通道的mat类,输出也是。两通道分别存储数据的实部和虚部.故而经常可以类似看到如下操作:

 if (img.channels() == 1)
    {
        cv::Mat planes[] = {cv::Mat_ (img), cv::Mat_::zeros(img.size())};
        //cv::Mat planes[] = {cv::Mat_ (img), cv::Mat_::zeros(img.size())};
        cv::merge(planes, 2, img);
    }

将输入数据的虚部填充为0,再进行合并得到二通道Mat矩阵再送入dft() .

 

代码如下:

#include "opencv2/core/core.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"

#include 

using namespace cv;

int main(int argc, const char ** argv)
{
	
	Mat img = imread("D:\\application\\visual studio\\GaussianFilter\\mean_filter\\x64\\Debug\\dlll.jpg", CV_LOAD_IMAGE_GRAYSCALE);   //以灰度图像读入
	int M = getOptimalDFTSize(img.rows);                               // 获得最佳DFT尺寸,为2的次方
	int N = getOptimalDFTSize(img.cols);                                 //同上
	Mat padded;
	copyMakeBorder(img, padded, 0, M - img.rows, 0, N - img.cols, BORDER_CONSTANT, Scalar::all(0));   // opencv中的边界扩展函数,提供多种方式扩展

	Mat planes[] = { Mat_(padded), Mat::zeros(padded.size(), CV_32F) };          // Mat 数组,第一个为扩展后的图像,一个为空图像,
	Mat complexImg;
	merge(planes, 2, complexImg);                                                                              // 合并成一个Mat

	dft(complexImg, complexImg);                                                                              // FFT变换, dft需要一个2通道的Mat

																											  // compute log(1 + sqrt(Re(DFT(img))**2 + Im(DFT(img))**2))
	split(complexImg, planes);                                                                                     //分离通道, planes[0] 为实数部分,planes[1]为虚数部分
	magnitude(planes[0], planes[1], planes[0]);                                                          // 求模
	Mat mag = planes[0];
	mag += Scalar::all(1);
	log(mag, mag);                                                                                                      // 模的对数

																														// crop the spectrum, if it has an odd number of rows or columns
	mag = mag(Rect(0, 0, mag.cols & -2, mag.rows & -2));                                        //保证偶数的边长

	int cx = mag.cols / 2;
	int cy = mag.rows / 2;

	// rearrange the quadrants of Fourier image                                                        //对傅立叶变换的图像进行重排,4个区块,从左到右,从上到下 分别为q0, q1, q2, q3
	// so that the origin is at the image center                                                          //  对调q0和q3, q1和q2
	Mat tmp;
	Mat q0(mag, Rect(0, 0, cx, cy));
	Mat q1(mag, Rect(cx, 0, cx, cy));
	Mat q2(mag, Rect(0, cy, cx, cy));
	Mat q3(mag, Rect(cx, cy, cx, cy));

	q0.copyTo(tmp);
	q3.copyTo(q0);
	tmp.copyTo(q3);

	q1.copyTo(tmp);
	q2.copyTo(q1);
	tmp.copyTo(q2);

	normalize(mag, mag, 0, 1, CV_MINMAX);                                                           // 规范化值到 0~1 显示图片的需要

	imshow("spectrum magnitude", mag);
	waitKey();
	return 0;
}

跑一下:

 

 

然后是实现二维FFT的C语言代码:

#include 
#include 
#include 

#define _CRT_SECURE_NO_WARNINGS
#define intsize sizeof(int)
#define complexsize sizeof(complex)
#define PI 3.1415926


int *a, *b;
int nLen, init_nLen, mLen, init_mLen, N, M;
FILE *dataFile;


typedef struct {
	float real;
	float image;
}complex;


complex *A, *A_In, *W;


complex Add(complex, complex);
complex Sub(complex, complex);
complex Mul(complex, complex);
int calculate_M(int);
void reverse(int, int);
void readData();
void fft(int, int);
void printResult();




int main()
{
	int i, j;


	readData();


	A = (complex *)malloc(complexsize*nLen);
	reverse(nLen, N);
	for (i = 0; i

该代码段读取对应路径下的txt文件中的矩阵信息,进行FFT变换并输出,测试:

测试矩阵信息(行,列,值):

 C++实现图像二维DFT离散傅里叶(FFT)代码——等效于opencv_第1张图片

这个scanf读取方法是顺序读取,每次scanf后,下一次打开的时候自动记录上次读到的位置,忽略空格和换行符。

得到结果:

C++实现图像二维DFT离散傅里叶(FFT)代码——等效于opencv_第2张图片

结果中,第一个矩阵是原始数据的复数矩阵,第二个是填充矩阵(为了便于进行傅里叶变换,需要将行列填充为2的幂次,这里是8),第三个为输出的FFT后数组。

 

将两者结合,通过后一个FFT的C语言函数替换opencv的dft进行整合代码如下:

整合,调试能够出结果,记录一下:


#include 
#include 
#include 
#include "opencv2/core/core.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include  
#include
#include
#include

#define _CRT_SECURE_NO_WARNINGS
#define intsize sizeof(int)
#define complexsize sizeof(complex)
#define PI 3.1415926

using namespace cv;

int *a, *b;
int nLen, init_nLen, mLen, init_mLen, N, M;
FILE *dataFile;


typedef struct {
	float real;
	float image;
}complex;


complex *A, *A_In, *W;


complex Add(complex, complex);
complex Sub(complex, complex);
complex Mul(complex, complex);
int calculate_M(int);
void reverse(int, int);
void fft(int, int);

#define ch 2









void fft(int fft_nLen, int fft_M)
{
	int i;
	int lev, dist, p, t;
	complex B;


	W = (complex *)malloc(complexsize*fft_nLen / 2);


	for (lev = 1; lev <= fft_M; lev++)
	{
		dist = (int)pow(2, lev - 1);
		for (t = 0; t(i);
			for (int j = 0; j < init_nLen; j++)
			{
				//A_In[i*nLen + j].real = (int)data[j];
				A_In[i*nLen + j].real = (int)img.at(i, j);
				//std::cout << A_In[i*nLen + j].real<< std::endl;
				A_In[i*nLen + j].image = 0.0;
			}
		}


		/*下面两个for循环,是初始化: 将扩展矩阵的非元素部分填充0 */
		for (int i = 0; i < mLen; i++)
		{
			for (int j = init_nLen; j < nLen; j++)
			{
				A_In[i*nLen + j].real = 0.0;
				A_In[i*nLen + j].image = 0.0;
			}
		}


		for (int i = init_mLen; i < mLen; i++)
		{
			for (int j = 0; j < init_nLen; j++)
			{
				A_In[i*nLen + j].real = 0.0;
				A_In[i*nLen + j].image = 0.0;
			}
		}

		/*end_readdata*/

		/*两次一维DFT*/
		A = (complex *)malloc(complexsize*nLen);
		reverse(nLen, N);
		for (int i = 0; i < mLen; i++)
		{
			for (int j = 0; j < nLen; j++)
			{
				A[j].real = A_In[i*nLen + b[j]].real;
				A[j].image = A_In[i*nLen + b[j]].image;
			}

			fft(nLen, N);
			for (int j = 0; j < nLen; j++)
			{
				A_In[i*nLen + j].real = A[j].real;
				A_In[i*nLen + j].image = A[j].image;
			}
		}
		free(A);

		A = (complex *)malloc(complexsize*mLen);
		reverse(mLen, M);
		for (int i = 0; i < nLen; i++)
		{
			for (int j = 0; j < mLen; j++)
			{
				A[j].real = A_In[b[j] * nLen + i].real;
				A[j].image = A_In[b[j] * nLen + i].image;
			}


			fft(mLen, M);
			for (int j = 0; j < mLen; j++)
			{
				A_In[j*nLen + i].real = A[j].real;
				A_In[j*nLen + i].image = A[j].image;
			}
		}
		free(A);


		/*————————output——————————*/
		/*
		注意:
		fft变换后,由于进行了矩阵的填充,因此输出的矩阵尺寸放大了,此处提取左上方的init_mLen*init_nLen元素即可
		opencv算法的out.rows和init_mlen不一样,后者是图幅原始尺寸,前者是扩展过的
		为了适应这个dft我给改了让两者一样
		*/

		//printf("Output results:\n");


		/*处理后取左上角*/
		for (int i = 0; i < img.rows; i++)
		{
			for (int j = 0; j < img.cols; j++)
			{

				out.at(i, j)[0] = (float)A_In[i*nLen + j].real;
				//std::cout << A_In[i*nLen + j].real << std::endl;
				out.at(i, j)[1] = (float)A_In[i*nLen + j].image;
			}

		}



		free(A_In);



		/*——————————end_out——————————*/
	}

	//——————————————————————————————————————————————————

	return out;
}







int main(int argc, const char ** argv)
{

	Mat img = imread("D:\\application\\visual studio\\GaussianFilter\\mean_filter\\x64\\Debug\\lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);   //以灰度图像读入
	//resize(img, img, Size(200,200), 0, 0, INTER_LINEAR);
	//imshow("paoisd", img);																																//imshow("src", img);

	//int M_ = getOptimalDFTSize(img.rows);                               // 获得最佳DFT尺寸,为2的次方
	//int N_ = getOptimalDFTSize(img.cols);                                 //同上
	//Mat padded;
	//copyMakeBorder(img, padded, 0, M_ - img.rows, 0, N_ - img.cols, BORDER_CONSTANT, Scalar::all(0));   // opencv中的边界扩展函数,提供多种方式扩展
	////opencv的边界扩展到2 3 5 倍数,便于FFT https://blog.csdn.net/jiangzhaopu/article/details/8441205
	//Mat planes[] = { Mat_(padded), Mat::zeros(padded.size(), CV_32F) };          // Mat 数组,第一个为扩展后的图像,一个为空图像,
	//Mat complexImg;
	//merge(planes, 2, complexImg);                                                                              // 合并成一个Mat

																																		 //opencv的边界扩展到2 3 5 倍数,便于FFT https://blog.csdn.net/jiangzhaopu/article/details/8441205
	Mat planes[] = { Mat_(img), Mat::zeros(img.size(), CV_32F) };          // Mat 数组,第一个为扩展后的图像,一个为空图像,
	Mat complexImg;
	merge(planes, 2, complexImg);                                                                              // 合并成一个Mat

	DFT_trans(img,complexImg);
	//DWORD start_time = GetTickCount();
	//std::cout <<"raw:"<< img.rows << " " << img.cols ;
	//if (ch == 1)
	//{
	//	dft(complexImg, complexImg);                                                                              // FFT变换, dft需要一个2通道的Mat
	//}
	//else
	//{
	//	//——————————————————REPLACE DFT()————————————————————————————————
	//	/*read data*/
	//	init_mLen = img.rows;
	//	init_nLen = img.cols;
	//	/*下面步骤是将矩阵的行列扩展到最近的2的幂次,行列可不同,FFT的预处理*/
	//	M = calculate_M(init_mLen);
	//	N = calculate_M(init_nLen);
	//	mLen = (int)pow(2, M);
	//	nLen = (int)pow(2, N);
	//	//mLen = M_;
	//	//nLen = N_;


	//	A_In = (complex *)malloc(complexsize*nLen*mLen);

	//	/*图像矩阵元素存储到complex结构体中*/
	//	for (int i = 0; i < init_mLen; i++)
	//	{
	//		uchar *data = img.ptr(i);
	//		for (int j = 0; j < init_nLen; j++)
	//		{
	//			A_In[i*nLen + j].real = (int)data[j];
	//			//std::cout << A_In[i*nLen + j].real<< std::endl;
	//			A_In[i*nLen + j].image = 0.0;
	//		}
	//	}


	//	/*下面两个for循环,是初始化: 将扩展矩阵的非元素部分填充0 */
	//	for (int i = 0; i < mLen; i++)
	//	{
	//		for (int j = init_nLen; j < nLen; j++)
	//		{
	//			A_In[i*nLen + j].real = 0.0;
	//			A_In[i*nLen + j].image = 0.0;
	//		}
	//	}


	//	for (int i = init_mLen; i < mLen; i++)
	//	{
	//		for (int j = 0; j < init_nLen; j++)
	//		{
	//			A_In[i*nLen + j].real = 0.0;
	//			A_In[i*nLen + j].image = 0.0;
	//		}
	//	}

	//	/*end_readdata*/

	//	/*两次一维DFT*/
	//	A = (complex *)malloc(complexsize*nLen);
	//	reverse(nLen, N);
	//	for (int i = 0; i < mLen; i++)
	//	{
	//		for (int j = 0; j < nLen; j++)
	//		{
	//			A[j].real = A_In[i*nLen + b[j]].real;
	//			A[j].image = A_In[i*nLen + b[j]].image;
	//		}

	//		fft(nLen, N);
	//		for (int j = 0; j < nLen; j++)
	//		{
	//			A_In[i*nLen + j].real = A[j].real;
	//			A_In[i*nLen + j].image = A[j].image;
	//		}
	//	}
	//	free(A);

	//	A = (complex *)malloc(complexsize*mLen);
	//	reverse(mLen, M);
	//	for (int i = 0; i < nLen; i++)
	//	{
	//		for (int j = 0; j < mLen; j++)
	//		{
	//			A[j].real = A_In[b[j] * nLen + i].real;
	//			A[j].image = A_In[b[j] * nLen + i].image;
	//		}


	//		fft(mLen, M);
	//		for (int j = 0; j < mLen; j++)
	//		{
	//			A_In[j*nLen + i].real = A[j].real;
	//			A_In[j*nLen + i].image = A[j].image;
	//		}
	//	}
	//	free(A);


	//	/*————————output——————————*/
	//	/*
	//	注意:
	//	fft变换后,由于进行了矩阵的填充,因此输出的矩阵尺寸放大了,此处提取左上方的init_mLen*init_nLen元素即可
	//	opencv算法的complexImg.rows和init_mlen不一样,后者是图幅原始尺寸,前者是扩展过的
	//	为了适应这个dft我给改了让两者一样
	//	*/

	//	//printf("Output results:\n");


	//	/*处理后取左上角*/
	//	for (int i = 0; i < complexImg.rows; i++)
	//	{
	//		for (int j = 0; j < complexImg.cols; j++)
	//		{

	//			complexImg.at(i, j)[0] = (float)A_In[i*nLen + j].real;
	//			//std::cout << A_In[i*nLen + j].real << std::endl;
	//			complexImg.at(i, j)[1] = (float)A_In[i*nLen + j].image;
	//		}

	//	}



	//	free(A_In);



	//	/*——————————end_out——————————*/
	//}

	////——————————————————————————————————————————————————
	//DWORD end_time = GetTickCount();
	//std::cout << std::endl << "running time: " << (end_time - start_time) / 1000.0 << " s" << std::endl;

	std::cout << "complex" << complexImg.rows << " " << complexImg.cols << std::endl;																										  // compute log(1 + sqrt(Re(DFT(img))**2 + Im(DFT(img))**2))
	split(complexImg, planes);                                                                                     //分离通道, planes[0] 为实数部分,planes[1]为虚数部分
	magnitude(planes[0], planes[1], planes[0]);                                                 // 求模
	Mat mag = planes[0];
	mag += Scalar::all(1);
	log(mag, mag);                        // 模的对数
										  // crop the spectrum, if it has an odd number of rows or columns
	mag = mag(Rect(0, 0, mag.cols & (-2), mag.rows & (-2)));                //保证偶数的边长

	int cx = mag.cols / 2;
	int cy = mag.rows / 2;
	//std::cout << mag.rows << " " << mag.cols<

对lena进行测试,得到结果:

C++实现图像二维DFT离散傅里叶(FFT)代码——等效于opencv_第3张图片

问题:

1.只有尺寸正方形才可以等效变换

2.小尺寸挺快,越大越慢

3.fftw库可以替换更快:http://www.fftw.org/index.html

 

 

你可能感兴趣的:(运行记录)