以下这个代码是我以前在一篇博客上照着敲的,但是时间久远我实在不知道是在哪篇博客上看得,所以没能给出地址链接,侵删。
我按照自己的理解对代码进行了注释。
#include
#include
using namespace cv;
using namespace std;
int main()
{
Mat input = imread("C:\\Users\\dushuang\\Desktop\\lotsfiles\\bhs\\1.jpg",0);
imshow("input", input);
//FFT快速算法首先将二维傅里叶变换转换成两个一维傅里叶变换的乘积,然后在对一维傅里叶变换
//进行快速算法,根据傅里叶变换的性质可以将n个元素的一维离散傅里叶转换为两个n/2个元素的一维离散傅里叶变换
//同理递归可以转换成m个一维的离散傅里叶变换,为了方便计算,所以要求图像的尺寸为2阶指数(2,4,8,16,32…)的数组计算速度最快,
//一个数组尺寸是2、3、5的倍数(例如:300 = 5*5*3*2*2)同样有很高的处理效率。
int w = getOptimalDFTSize(input.cols);
int h = getOptimalDFTSize(input.rows);
Mat padded;
copyMakeBorder(input, padded, 0, h - input.rows, 0, w - input.cols, BORDER_CONSTANT, Scalar::all(0));
padded.convertTo(padded, CV_32FC1);//这样可以将三通道转换为一通道
imshow("padded", padded);//这句代码其实是错误的,因为inshow只能显示8U的图像,32F的图像显示不出来
//对时域乘以pow(-1,i+j)后在进行傅里叶变换结果是对频域的中心化,当然也可以转换到频域后进行剪切
for (int i = 0; i < padded.rows; i++)
{
float *ptr = padded.ptr<float>(i);
for (int j = 0; j < padded.cols; j++)
ptr[j] *= pow(-1, i + j);
}
Mat plane[] = { padded, Mat::zeros(padded.size(), CV_32F) };//创建一个Mat型数组来装转换后的数据,因为Fm一般为复数所以需要两个Mat空间进行装填
Mat complexImg;
merge(plane, 2, complexImg);//将Mat数组转换成一个二通道的图,第一个通道为待转换图像。
dft(complexImg, complexImg);//输入图像为二通道,支持原地转换。
/*****************gaussian*******************/
Mat gaussianBlur(padded.size(), CV_32FC2);
Mat gaussianSharpen(padded.size(), CV_32FC2);
float D0 = 2 * 10 * 10;
for (int i = 0; i < padded.rows; i++)//这里够将两个高斯图像
{
float*p = gaussianBlur.ptr<float>(i);
float*q = gaussianSharpen.ptr<float>(i);
for (int j = 0; j < padded.cols; j++)
{
float d = pow(i - padded.rows / 2, 2) + pow(j - padded.cols / 2, 2);//这里d表示该点离图像中心的距离
p[2 * j] = expf(-d / D0);//gaussianBlur的第零通道,expf return float exp.
p[2 * j + 1] = expf(-d / D0);//gaussianBlur的第一通道
q[2 * j] = 1 - expf(-d / D0);//gaossianShapen的第零通道
q[2 * j + 1] = 1 - expf(-d / D0);//gaussianShapen的第一通道
}
}
multiply(complexImg, gaussianBlur, gaussianBlur);//矩阵元素对应相乘,注意,和矩阵相乘区分
multiply(complexImg, gaussianSharpen, gaussianSharpen);//对两个平面都做高斯乘法(与先求模再做高斯乘法效果一样)
/***********************将频域显示出来**************************/
split(complexImg, plane);
magnitude(plane[0], plane[1], plane[0]);//求模
plane[0] += Scalar::all(1);//防止有零,避免不能进行log运算
log(plane[0], plane[0]);//将坐标压缩,以便观察。因为1000与100相差太大不便观察差异,变成2和3之后容易观察。
normalize(plane[0], plane[0], 1, 0, CV_MINMAX);
imshow("dft", plane[0]);
/*********************将处理之后的频域显示出来************************/
split(gaussianBlur, plane);
magnitude(plane[0], plane[1], plane[0]);
plane[0] += Scalar::all(1);
log(plane[0], plane[0]);
normalize(plane[0], plane[0], 1, 0, CV_MINMAX);
imshow("gaussianBlur", plane[0]);
split(gaussianSharpen, plane);
magnitude(plane[0], plane[1], plane[0]);
plane[0] += Scalar::all(1);
log(plane[0], plane[0]);
normalize(plane[0], plane[0], 1, 0, CV_MINMAX);
imshow("gaussianShapen", plane[0]);
/************************idft*****************************/
idft(gaussianBlur, gaussianBlur);//输入图像为二通道
idft(gaussianSharpen, gaussianSharpen);
split(gaussianBlur, plane);
magnitude(plane[0], plane[1], plane[0]);//反变换也是求magnitude
normalize(plane[0], plane[0], 1, 0, CV_MINMAX);
imshow("idft-gaussianBlur", plane[0]);
split(gaussianSharpen, plane);
magnitude(plane[0], plane[1], plane[0]);
normalize(plane[0], plane[0], 1, 0, CV_MINMAX);
imshow("idft_gaussianSharpen", plane[0]);
waitKey();
return 0;
}
结合前三篇文章,这段代码应该非常好理解。
这里我要提一下:当我们对图像四周进行填0操作后,图像的频域尺寸也会相应变大,但由于我们填的是0,而频域像素值是以我们的空域值为系数的,所以系数不会变化,但是我们基底的M和N值会变换,但是变化微小,造成的影响不是很大。