1.离散傅里叶变换
任何信号都可以由一系列正弦信号叠加形
成,
一维领域信号是一维正弦波的叠加,二维领域是
二维平面波的增加.由于图像可以看作是二维信号
因此可以对图像进行傅里叶变换.但是
,由于
图像是离散信号,因此对图像的傅里叶变换应该是离散傅里叶变换.
离散傅里叶变换能得到图像的频域信息,频域信息可以从另一个方面理解图像。
图像中像素波
动较大的区域对应的
频域是高频区域,因此高频区域体现的是图像的细节、纹理信息,而低频信息
代表了图像的轮廓信息.通过频域分析也可以实现去除图像中某些特定的成分,例如光照信息主要
体现为低频信息
因此去除图像中的低频信息可以去除图像中的光照干扰.图像滤波中常将滤波器
分成高通滤波器
、低通滤波
器等
指的就是保留图像中频率较高或者较低的部分。
例如高斯滤波楼
就是低通滤波.
首先计算适合图像离散傅里叶变换的最优尺寸,之后利用
copy
MakeBorder
函数
扩展图像尺寸,然后进行离散傅里叶变换,最后计算变换结果的幅值.为了能够显示变换结果中的幅值,将结果进行归一化处理.根据式 (8-
7)可知变换后的原点位于四
个顶点,因此通过图像变换,将原点调整到图像中心.
同时,为了验证正变换和逆变换的可逆性,也给出了小型矩阵的正逆变换的结果。
实现:
void visionagin:: Mydft()
{
Mat src = imread("C:\\Users\\86176\\Downloads\\visionimage\\star.jfif");
resize(src, src, Size(502, 502));
Mat gry;
cvtColor(src, gry, COLOR_BGR2GRAY);
imshow("原图像", gry);
int Row = getOptimalDFTSize(gry.rows);//计算傅里叶变换最适合尺寸
int Col = getOptimalDFTSize(gry.cols);
Mat fitmat;
int t = ( Row - gry.rows) / 2;
int b = Row - gry.rows - t ;
int l = ( Col- gry.cols) / 2;
int r = Col-gry.cols - l ;
copyMakeBorder(gry, fitmat, t, b, l, r, BORDER_CONSTANT, Scalar(0));//边界填充
imshow("扩充后的图像", fitmat);
//构建傅里叶变换输入量
Mat flo[2], complex;
flo[0] = Mat_<float>(fitmat);//实数部分
flo[1] = Mat::zeros(fitmat.size(),CV_32F);//虚数部分cv_32F
merge(flo, 2, complex);//合并
Mat result;//傅里叶变换结果
dft(complex, result);//离散傅里叶变换
//将复数转化为幅值
Mat resultc[2];
split(result, resultc);//将结果分离成实数和虚数部分
Mat amplitude;//幅值
magnitude(resultc[0], resultc[1], amplitude);//计算幅值
//使用对数缩小m1=log(m+1),保证所有值为正
amplitude = amplitude + 1;
log(amplitude, amplitude);//求自然对数
//扩展后的图像与原图像对应的部分
amplitude = amplitude(Rect(t, l, gry.cols, gry.rows));
normalize(amplitude, amplitude, 0, 1, NORM_MINMAX);//归一化
imshow("傅里叶变换后的图像", amplitude);//得到的原点在四个角上
//调整显示图像 左上和右下互换 右上和左下互换
Mat leftup = amplitude(Rect(0, 0, amplitude.cols / 2, amplitude.rows / 2));
Mat rightup = amplitude(Rect(amplitude.cols / 2, 0, amplitude.cols / 2, amplitude.rows / 2));
Mat leftdown= amplitude(Rect(0,amplitude.rows / 2, amplitude.cols / 2, amplitude.rows / 2));
Mat rightdown = amplitude(Rect(amplitude.cols / 2, amplitude.rows / 2, amplitude.cols / 2, amplitude.rows / 2));
//交换
Mat temp1,temp2;
leftup.copyTo(temp1);
rightdown.copyTo(leftup);
temp1.copyTo(rightdown);
rightup.copyTo(temp2);
leftdown.copyTo(rightup);
temp2.copyTo(leftdown);
imshow("调整后的结果", amplitude);
}
2.傅里叶变换进行卷积
傅里叶变换可以将两个矩阵的卷积转换成两个矩阵傅里叶变换结果的乘积,通过这种方式可以
极大地提高卷积的计算速度.但是图像傅里叶变换结果都是具有复数共辄对称性的复数矩阵,两个
矩阵相乘需要计算对应位置的两个复数乘积,
OpenCV
提供了用于计算两个复数矩阵的乘积的
mulspectrums
函数
void visionagin:: MyMulspectrums()
{
Mat src = imread("C:\\Users\\86176\\Downloads\\visionimage\\star.jfif");
Mat imggry;
cvtColor(src, imggry, COLOR_BGR2GRAY);
imshow("原图", imggry);
Mat gry = Mat_<float>(imggry);//数据转化成float
Mat kernel = (Mat_<float>(5, 5) << 1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1);
Mat result;//构建输出图像
int rwidth = abs(gry.cols - kernel.cols) + 1;
int rheight = abs(gry.rows - kernel.rows) + 1;
result.create(rheight, rwidth, gry.type());
//计算傅里叶变换最优SIZE
int width = getOptimalDFTSize(gry.cols+kernel.cols-1);
int height = getOptimalDFTSize(gry.rows+kernel.rows-1);
Mat tempa;
//扩展输入图像尺寸
int a_t = 0;
int a_b = height- gry.rows ;
int a_l = 0;
int a_r = width- gry.cols ;
copyMakeBorder(gry, tempa, a_t, a_b, a_l, a_r, BORDER_CONSTANT);
//扩展卷积核尺寸
Mat tempk;
int k_t = 0;
int k_b = height - kernel.rows;
int k_l = 0;
int k_r = width - kernel.cols;
copyMakeBorder(kernel, tempk, k_t, k_b, k_l, k_r, BORDER_CONSTANT);
//分别进行傅里叶变换
dft(tempa, tempa, 0, gry.rows);
dft(tempk, tempk, 0, kernel.rows);
mulSpectrums(tempa, tempk, tempa, DFT_COMPLEX_OUTPUT);
//对相乘结果进行逆变换
idft(tempa, tempa, DFT_SCALE,result.rows);//DFT_SCALE
//结果归一化
normalize(tempa, tempa, 0, 1,NORM_MINMAX);
//截取部分图像显示
tempa(Rect(0, 0, result.cols, result.rows)).copyTo(result);
imshow("result", result);
}