这篇文章写的不错:
http://johnhany.net/2013/11/dft-based-text-rotation-correction/
进行一些简单处理:
(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;
}
跑一下:
#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变换并输出,测试:
测试矩阵信息(行,列,值):
这个scanf读取方法是顺序读取,每次scanf后,下一次打开的时候自动记录上次读到的位置,忽略空格和换行符。
得到结果:
结果中,第一个矩阵是原始数据的复数矩阵,第二个是填充矩阵(为了便于进行傅里叶变换,需要将行列填充为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进行测试,得到结果:
问题:
1.只有尺寸正方形才可以等效变换
2.小尺寸挺快,越大越慢
3.fftw库可以替换更快:http://www.fftw.org/index.html