平常处理图像都是在空间域上进行的,在空间域的操作都是对图像像素直接处理,而频率域的操作是在傅里叶变换上执行的。对于傅里叶变换,本人现在也还不是特别理解,简单的理解应该是一幅图像在频率中可以看作极限接近于多个正弦和余弦函数的累加。想要理解的小伙伴推荐看傅里叶掐死教程。
以下附上代码(参考别的作者,为了避免参考代码被删,这边附上实现代码)
int openCVTest::myFFT(cv::Mat& inputImg)
{
//得到DFT的最佳尺寸(2的指数),以加速计算
cv::Mat paddedImg;
int m = cv::getOptimalDFTSize(inputImg.rows);
int n = cv::getOptimalDFTSize(inputImg.cols);
std::cout << "图片原始尺寸为:" << inputImg.cols << "*" << inputImg.rows << std::endl;
std::cout << "DFT最佳尺寸为:" << n << "*" << m << std::endl;
//填充图像的下端和右端
cv::copyMakeBorder(inputImg, paddedImg, 0, m - inputImg.rows,
0, n - inputImg.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
//将填充的图像组成一个复数的二维数组(两个通道的Mat),用于DFT
cv::Mat matArray[] = {cv::Mat_<float>(paddedImg), cv::Mat::zeros(paddedImg.size(), CV_32F)};
cv::Mat complexInput, complexOutput;
merge(matArray, 2, complexInput);
cv::dft(complexInput, complexOutput);
//计算幅度谱(傅里叶谱)
cv::split(complexOutput, matArray);
cv::Mat magImg;
cv::magnitude(matArray[0], matArray[1], magImg);
//转换到对数坐标
magImg += cv::Scalar::all(1);
cv::log(magImg, magImg);
//将频谱图像裁剪成偶数并将低频部分放到图像中心(以下原文中没被注释,但是我测试时有问题,应该是图像长宽越界了,所以直接注释掉)
// int width = (magImg.cols / 2)*2;
// int height = (magImg.cols / 2)*2;
// magImg = magImg(cv::Rect(0, 0, width, height));
int colToCut = magImg.cols/2;
int rowToCut = magImg.rows/2;
//获取图像,分别为左上右上左下右下
//注意这种方式得到的是magImg的ROI的引用
//对下面四幅图像进行修改就是直接对magImg进行了修改
cv::Mat topLeftImg(magImg, cv::Rect(0, 0, colToCut, rowToCut));
cv::Mat topRightImg(magImg, cv::Rect(colToCut, 0, colToCut, rowToCut));
cv::Mat bottomLeftImg(magImg, cv::Rect(0, rowToCut, colToCut, rowToCut));
cv::Mat bottomRightImg(magImg, cv::Rect(colToCut, rowToCut, colToCut, rowToCut));
//第二象限和第四象限进行交换
//cv::Mat tmpImg1 = topLeftImg.clone();
cv::Mat tmpImg;
topLeftImg.copyTo(tmpImg);
bottomRightImg.copyTo(topLeftImg);
tmpImg.copyTo(bottomRightImg);
//第一象限和第三象限进行交换
//cv::Mat tmpImg2 = bottomLeftImg.clone();
bottomLeftImg.copyTo(tmpImg);
topRightImg.copyTo(bottomLeftImg);
tmpImg.copyTo(topRightImg);
//归一化图像
cv::normalize(magImg, magImg, 0, 1, CV_MINMAX);
//傅里叶反变换
cv::Mat complexIDFT, IDFTImg;
cv::idft(complexOutput, complexIDFT);
cv::split(complexIDFT, matArray);
cv::magnitude(matArray[0], matArray[1], IDFTImg);
cv::normalize(IDFTImg, IDFTImg, 0, 1, CV_MINMAX);
cv::namedWindow("输入图像", cv::WINDOW_NORMAL);
cv::imshow("输入图像", inputImg);
cv::namedWindow("频谱图像", cv::WINDOW_NORMAL);
cv::imshow("频谱图像", magImg);
cv::namedWindow("反变换图像", cv::WINDOW_NORMAL);
cv::imshow("反变换图像", IDFTImg);
/***********************频率域滤波部分*************************/
//高斯低通滤波函数(中间高两边低)
cv::Mat gaussianBlur(paddedImg.size(),CV_32FC2);
//高斯高通滤波函数(中间低两边高)
cv::Mat gaussianSharpen(paddedImg.size(),CV_32FC2);
double D0=2*10*10*10;
for(int i=0;i<paddedImg.rows;i++)
{
float*p=gaussianBlur.ptr<float>(i);
float*q=gaussianSharpen.ptr<float>(i);
for(int j=0;j<paddedImg.cols;j++)
{
double d=pow(i-paddedImg.rows/2,2)+pow(j-paddedImg.cols/2,2);
p[2*j]=expf(-d/D0);
p[2*j+1]=expf(-d/D0);
q[2*j]=1-expf(-d/D0);
q[2*j+1]=1-expf(-d/D0);
}
}
//将实部和虚部按照频谱图的方式换位
//低频在图像中心,用于滤波
cv::split(complexOutput, matArray);
//matArray[0]表示DFT的实部
cv::Mat q1(matArray[0], cv::Rect(0, 0, colToCut, rowToCut));
cv::Mat q2(matArray[0], cv::Rect(colToCut, 0, colToCut, rowToCut));
cv::Mat q3(matArray[0], cv::Rect(0, rowToCut, colToCut, rowToCut));
cv::Mat q4(matArray[0], cv::Rect(colToCut, rowToCut, colToCut, rowToCut));
//第二象限和第四象限进行交换
q1.copyTo(tmpImg);
q4.copyTo(q1);
tmpImg.copyTo(q4);
//第一象限和第三象限进行交换
q2.copyTo(tmpImg);
q3.copyTo(q2);
tmpImg.copyTo(q3);
//matArray[1]表示DFT的虚部
cv::Mat qq1(matArray[1], cv::Rect(0, 0, colToCut, rowToCut));
cv::Mat qq2(matArray[1], cv::Rect(colToCut, 0, colToCut, rowToCut));
cv::Mat qq3(matArray[1], cv::Rect(0, rowToCut, colToCut, rowToCut));
cv::Mat qq4(matArray[1], cv::Rect(colToCut, rowToCut, colToCut, rowToCut));
//第二象限和第四象限进行交换
qq1.copyTo(tmpImg);
qq4.copyTo(qq1);
tmpImg.copyTo(qq4);
//第一象限和第三象限进行交换
qq2.copyTo(tmpImg);
qq3.copyTo(qq2);
tmpImg.copyTo(qq3);
cv::merge(matArray, 2, complexOutput);
//滤波器和DFT结果相乘(矩阵内积)
cv::multiply(complexOutput,gaussianBlur,gaussianBlur);
cv::multiply(complexOutput,gaussianSharpen,gaussianSharpen);
//计算频谱
cv::split(gaussianBlur,matArray);
cv::magnitude(matArray[0],matArray[1],magImg);
magImg+= cv::Scalar::all(1);
cv::log(magImg,magImg);
cv::normalize(magImg,magImg,1,0,CV_MINMAX);
cv::namedWindow("高斯低通滤波频谱", cv::WINDOW_NORMAL);
cv::imshow("高斯低通滤波频谱",magImg);
cv::split(gaussianSharpen,matArray);
cv::magnitude(matArray[0],matArray[1],magImg);
magImg += cv::Scalar::all(1);
cv::log(magImg,magImg);
cv::normalize(magImg,magImg,1,0,CV_MINMAX);
cv::namedWindow("高斯高通滤波频谱", cv::WINDOW_NORMAL);
cv::imshow("高斯高通滤波频谱",magImg);
//IDFT得到滤波结果
cv::Mat gaussianBlurImg;
cv::idft(gaussianBlur, complexIDFT);
cv::split(complexIDFT, matArray);
cv::magnitude(matArray[0], matArray[1], gaussianBlurImg);
cv::normalize(gaussianBlurImg, gaussianBlurImg, 0, 1, CV_MINMAX);
cv::Mat gaussianSharpenImg;
cv::idft(gaussianSharpen, complexIDFT);
cv::split(complexIDFT, matArray);
cv::magnitude(matArray[0], matArray[1], gaussianSharpenImg);
cv::normalize(gaussianSharpenImg, gaussianSharpenImg, 0, 1, CV_MINMAX);
cv::namedWindow("高斯低通滤波", cv::WINDOW_NORMAL);
cv::imshow("高斯低通滤波", gaussianBlurImg);
cv::namedWindow("高斯高通滤波", cv::WINDOW_NORMAL);
cv::imshow("高斯高通滤波", gaussianSharpenImg);
/******************************************************/
cv::waitKey(0);
return 0;
}
参考文章:https://www.jianshu.com/p/286050594d9e