C++实现二维离散傅里叶变换(DFT)

        根据二维离散傅里叶变换公式(DFT),可以将图片从空间域转换到频率域内,对其进行一些处理,再通过离散傅里叶反变换(IDFT),转换回原空间域,达到一些特殊处理效果。

处理如下

原图

经过二维离散傅里叶变换(DFT),得到下图

再经过反变换,得到下图

这边我用C++重新实现下这个DFT和IDFT这两个算法。

根据定义

 照着着两个公式编写程序即可,注意的是,e可用下面公式展开

 由于安照此方式编写的程序套了4层循环,运行过于缓慢,我对原图进行了缩放,之前627*627分辨率的图,我缩放成250*250,进行处理。也可自行将程序中的width height改小,已节省时间看效果。

原图

上代码,为了加快for循环的速度 我这里使用了openMP,拉满CPU

#include <iostream>
#include<opencv2/opencv.hpp>

#define _USE_MATH_DEFINES
#include <math.h>
#include<omp.h>


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<uchar>(0);
	Vec3b *pImg = img.ptr<Vec3b>(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<uchar>(0);
	uchar *p2 = img.ptr<uchar>(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<uchar>(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<uchar>(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<double>(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<double>(0);
	uchar *pOut = out.ptr<uchar>(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

  • 2
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值