一、函数:
opencv学习笔记24-opencv图像的傅里叶变换(幅值谱)
二、示例代码:
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/objdetect.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <iostream>
using namespace cv;
using namespace std;
int calcVisbalDft(cv::Mat srcMat, cv::Mat& magMat, cv::Mat& ph, double& normVal);
int main()
{
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
Mat srcMat = imread("C:\\Users\\86173\\Desktop\\TI\\Q版阿离.png", 0);
Mat ph_image;
Mat ma_image;
double normVal;
if (srcMat.empty()) {
cout << "failed to read image!:" << endl;
return -1;
}
calcVisbalDft(srcMat, ma_image, ph_image, normVal);
cout << "normVal:" << endl;
cout << normVal << endl;
imshow("原始图像", srcMat);//原始图像
imshow("幅度谱", ma_image);//幅度谱
imshow("相位谱", ph_image);//相位谱
waitKey(0);
return 0;
}
//输入图片,输出幅值谱,相位谱和还原归一化时的系数(最大值)
int calcVisbalDft(Mat srcMat, Mat& magMat, Mat& ph, double& normVal)
{
Mat dst;
srcMat = imread("C:\\Users\\86173\\Desktop\\TI\\Q版阿离.png", 0);
int m = getOptimalDFTSize(srcMat.rows); //2,3,5的倍数有更高效率的傅里叶变换
int n = getOptimalDFTSize(srcMat.cols);
Mat output_image;
//把灰度图像放在左上角,在右边和下边扩展图像,扩展部分填充为0;
copyMakeBorder(srcMat, output_image, 0, m - srcMat.rows, 0, n - srcMat.cols, BORDER_CONSTANT, Scalar::all(0));
//planes[0]为dft变换的实部,planes[1]为虚部,ph为相位, plane_true=mag为幅值
Mat planes[] = { Mat_<float>(output_image), Mat::zeros(output_image.size(), CV_32F) };
Mat planes_true = Mat_<float>(output_image);
//保存相位(Mat_代表确定了数据类型,访问元素时不需要再指定元素类型)
ph = Mat_<float>(output_image);
Mat complexImg;
//多通道complexImg既有实部又有虚部
merge(planes, 2, complexImg);
//对上边合成的mat进行傅里叶变换,***支持原地操作***,傅里叶变换结果为复数.通道1存的是实部,通道二存的是虚部
dft(complexImg, complexImg);
//把变换后的结果分割到两个mat,一个实部,一个虚部,方便后续操作
split(complexImg, planes);
//此部分目的为更好地显示幅值
magnitude(planes[0], planes[1], planes_true);//幅度谱mag
phase(planes[0], planes[1], ph);//相位谱ph
Mat A = planes[0];
Mat B = planes[1];
magMat = planes_true;
magMat += Scalar::all(1);//对幅值加1
//计算出的幅值一般很大,达到10^4,通常没有办法在图像中显示出来,需要对其进行log求解。
log(magMat, magMat);
//取矩阵中的最大值,便于后续还原时去归一化
minMaxLoc(magMat, 0, &normVal, 0, 0);
//修剪频谱,如果图像的行或者列是奇数的话,那其频谱是不对称的,因此要修剪
magMat = magMat(Rect(0, 0, magMat.cols & -2, magMat.rows & -2));
ph = ph(Rect(0, 0, magMat.cols & -2, magMat.rows & -2));
Mat _magI = magMat.clone();
//将幅度归一化到可显示范围。
normalize(_magI, _magI, 0, 1, CV_MINMAX);
//显示规则频谱图
int cx = magMat.cols / 2;
int cy = magMat.rows / 2;
//这里是以中心为标准,把mag图像分成四部分
Mat tmp;
Mat quadrant0(magMat, Rect(0, 0, cx, cy));
Mat quadrant1(magMat, Rect(cx, 0, cx, cy));
Mat quadrant2(magMat, Rect(0, cy, cx, cy));
Mat quadrant3(magMat, Rect(cx, cy, cx, cy));
quadrant0.copyTo(tmp);
quadrant3.copyTo(quadrant0);
tmp.copyTo(quadrant3);
quadrant1.copyTo(tmp);
quadrant2.copyTo(quadrant1);
tmp.copyTo(quadrant2);
normalize(magMat, magMat, 1, 0, CV_MINMAX);
return 0;
}
三、运行结果: