根据二维离散傅里叶变换公式(DFT),可以将图片从空间域转换到频率域内,对其进行一些处理,再通过离散傅里叶反变换(IDFT),转换回原空间域,达到一些特殊处理效果。
处理如下
原图
经过二维离散傅里叶变换(DFT),得到下图
再经过反变换,得到下图
这边我用C++重新实现下这个DFT和IDFT这两个算法。
根据定义
照着着两个公式编写程序即可,注意的是,e可用下面公式展开
由于安照此方式编写的程序套了4层循环,运行过于缓慢,我对原图进行了缩放,之前627*627分辨率的图,我缩放成250*250,进行处理。也可自行将程序中的width height改小,已节省时间看效果。
原图
上代码,为了加快for循环的速度 我这里使用了openMP,拉满CPU
#include
#include
#define _USE_MATH_DEFINES
#include
#include
using namespace cv;
const int height = 250, width = 250;
struct ComplexNum {
double real;//实部
double imagin;//虚部
};
Mat BGR2GRAY(Mat img)
{
int w = img.cols;
int h = img.rows;
Mat grayImg(h, w, CV_8UC1);
uchar *p = grayImg.ptr(0);
Vec3b *pImg = img.ptr(0);
for (int i = 0; i < w*h; ++i)
{
p[i] = 0.2126*pImg[i][2] + 0.7152*pImg[i][1] + 0.0722*pImg[i][0];
}
return grayImg;
}
Mat Resize(Mat img)
{
int w = img.cols;
int h = img.rows;
Mat out(height, width, CV_8UC1);
uchar *p = out.ptr(0);
uchar *p2 = img.ptr(0);
int x_before, y_before;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
{
x_before = (int)x*w*1.0 / width;
y_before = (int)y*h*1.0 / height;
p[y * width + x] = p2[y_before * w + x_before];
}
}
return out;
}
//2维傅里叶变换函数
int DFT2D(Mat img, ComplexNum (*dst)[width],int size_w,int size_h)
{
#pragma omp parallel for
for (int u = 0; u < size_h; u++) {
for (int v = 0; v < size_w; v++) {
double real = 0.0;
double imagin = 0.0;
for (int i = 0; i < size_h; i++) {
for (int j = 0; j < size_w; j++) {
double I = (double)img.at(j,i);
double x = M_PI * 2 * ((double)i*u / (double)size_w + (double)j*v / (double)size_h);
real += cos(x)*I;
imagin += -sin(x)*I;
}
}
dst[u][v].real = real;
dst[u][v].imagin = imagin;
}
}
return 0;
}
//2维逆傅里叶变换函数
int IDFT2D(ComplexNum (*src)[width], Mat out, int size_w, int size_h) {
#pragma omp parallel for
for (int i = 0; i < size_h; i++) {
for (int j = 0; j < size_w; j++) {
double real = 0.0;
double imagin = 0.0;
for (int u = 0; u < size_h; u++) {
for (int v = 0; v < size_w; v++) {
double R = src[u][v].real;
double I = src[u][v].imagin;
double x = M_PI * 2 * ((double)i*u / (double)size_w + (double)j*v / (double)size_h);
real += R * cos(x) - I * sin(x);
imagin += I * cos(x) + R * sin(x);
}
}
double g = sqrt(real*real + imagin * imagin)*1.0 /(size_w*size_h);
out.at(j, i) = (uchar)g;
}
}
return 0;
}
//计算幅度
int Complex2Mat(ComplexNum(*src)[width], Mat out, int size_w, int size_h)
{
for (int u = 0; u < size_h; u++) {
for (int v = 0; v < size_w; v++) {
double R = src[u][v].real;
double I = src[u][v].imagin;
double g = sqrt(R*R + I*I);
g = log(g + 1); //转换为对数尺度
out.at(u, v) = g;
}
}
return 0;
}
//自动缩放到0-255范围内,并变换象限,将低频移至中间
int AutoScale(Mat src,Mat out)
{
int w = src.cols;
int h = src.rows;
double *p = src.ptr(0);
uchar *pOut = out.ptr(0);
double max = p[0];
double min = p[0];
for (int i = 0; i < w*h; i++)
{
if (p[i] > max) max = p[i];
if (p[i] < min) min = p[i];
}
double scale = 255.0 / (max - min);
for (int i = 0; i < w*h; i++)
{
int j = i+w*h/2+w/2;
if (j > w*h) j = j - w * h; //低频移至中间
pOut[i] = (uchar)((p[j] - min)*scale);
}
return 0;
}
int main()
{
Mat img = imread("d:\\1.jpg");
imshow("img", img);
Mat gray = BGR2GRAY(img);
imshow("gray", gray);
imwrite("gray.jpg", gray);
Mat imgRez = Resize(gray);
imshow("imgRez", imgRez);
imwrite("imgRez.jpg", imgRez);
//std::cout << imgRez;
ComplexNum (*dst)[width]=new ComplexNum[height][width];
DFT2D(imgRez, dst, width, height);
Mat out = Mat::zeros(height, width, CV_64F);
Complex2Mat(dst, out, width, height);
//imshow("out", out);
Mat out2 = Mat::zeros(height, width, CV_8UC1);
AutoScale(out, out2);
imshow("out2", out2);
imwrite("out2.jpg", out2);
Mat out3 = Mat::zeros(height, width, CV_8UC1);
IDFT2D(dst, out3, width, height);
imshow("out3", out3);
imwrite("out3.jpg", out3);
waitKey(0);
}
OK