团购手机网站怎么做,大学生一个人做网站,推广手机卡返佣平台,28岁女生学前端开发难吗接触到傅里叶-梅林算法#xff0c;需要用到傅里叶变换#xff0c;于是去查了一下OpenCV中的实现方法#xff0c;没想到习以为常的傅里叶变换之中的门道还不少。
//傅里叶变换https://blog.csdn.net/keith_bb/article/details/53389819Mat I imread(Lena.jpg, I…接触到傅里叶-梅林算法需要用到傅里叶变换于是去查了一下OpenCV中的实现方法没想到习以为常的傅里叶变换之中的门道还不少。
//傅里叶变换https://blog.csdn.net/keith_bb/article/details/53389819Mat I imread(Lena.jpg, IMREAD_GRAYSCALE); //读入图像灰度图//判断图像是否加载成功if (I.empty()){cout 图像加载失败! endl;return -1;}elsecout 图像加载成功! endl endl;Mat padded; //以0填充输入图像矩阵int m getOptimalDFTSize(I.rows);//为了使dft()运算更高效改变尺寸,注意参数是行/列int n getOptimalDFTSize(I.cols);//一个数组尺寸是2、3、5的倍数例如300 5*5*3*2*2同样有很高的处理效率//填充输入图像I输入矩阵为padded上方和左方不做填充处理copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));//中间四个参数是int型代表四个方向填充的长度填充之后得到paddedMat planes[] { Mat_float(padded), Mat::zeros(padded.size(), CV_32F) };//Mat_float对应的是CV_32F 这是创建了一个Mat类型的数组两个元素大小都是padded类型都是float为延扩后的图像增添一个初始化为0的通道Mat complexI;merge(planes, 2, complexI); //将planes融合合并成一个多通道数组complexI//这个函数将单通道的数组融合起来第一个参数是指向mat的指针/*imshow(complexI, complexI);waitKey();*///会溢出因为complex是mat数组用于存放实部和虚部dft(complexI, complexI); //进行傅里叶变换 src不能是padded//注意这里的输入图像不是原图也不是padded而是融合得到的complexI//支持图像原地计算 (输入输出为同一图像)//计算幅值转换到对数尺度(logarithmic scale)// log(1 sqrt(Re(DFT(I))^2 Im(DFT(I))^2))split(complexI, planes); //planes[0] Re(DFT(I),planes[1] Im(DFT(I))//即planes[0]为实部,planes[1]为虚部magnitude(planes[0], planes[1], planes[0]); //将求得的幅度放在planes[0],planes[0] magnitudeMat magI planes[0];magI Scalar::all(1);//加1(为了取对数之后全为整数吧0之后才取对数为了将灰度值跨度缩小增强可视化效果log(magI, magI); //线性尺度转换到对数尺度(logarithmic scale)//剪切和象限变换//如果有奇数行或列则对频谱进行裁剪magI magI(Rect(0, 0, magI.cols-2, magI.rows-2));//这个按位与什么意思//x-2代表x与-2按位相与,-2的二进制形式为1111 1110在x与-2按位相与后不管x是奇数还是偶数最后x都会变成一个偶数https://blog.csdn.net/autocyz/article/details/48803859//重新排列傅里叶图像中的象限使得原点位于图像中心int cx magI.cols / 2;int cy magI.rows / 2;Mat q0(magI, Rect(0, 0, cx, cy)); //左上角图像划定ROI区域Mat q1(magI, Rect(cx, 0, cx, cy)); //右上角图像Mat q2(magI, Rect(0, cy, cx, cy)); //左下角图像Mat q3(magI, Rect(cx, cy, cx, cy)); //右下角图像//变换左上角和右下角象限Mat tmp;q0.copyTo(tmp);q3.copyTo(q0);tmp.copyTo(q3);//变换右上角和左下角象限q1.copyTo(tmp);q2.copyTo(q1);tmp.copyTo(q2);//归一化处理用0-1之间的浮点数将矩阵变换为可视的图像格式Mat magI01;normalize(magI, magI01, 0, 1, CV_MINMAX);//0和1分别是下限和上限范围归一化而不是数值归一化imshow(输入图像, I);imshow(频谱图, magI01);//imshow对float数据会扩大255倍这也是归一化到0~1的原因imwrite(Fouier0101.jpg, magI01);傅里叶变换后求得的幅值没有经过对数尺度变换而直接范围归一化在0~1没有对数尺度变换也没有范围归一化 可以看到直接求得的幅值都很大即频率成分都很高。
对数尺度变换后但没有范围归一化看不出来什么差别不过中心点黑点变得不明显了。
对数尺度变换后归一化在0~1没有对数尺度变换直接归一化在0~255对数尺度变换后归一化在0~255对数尺度变换后归一化在0~2Normalize之后imshow问题
将图像范围归一化在0~1图像会是近似成全黑的imwrite保存到本地后查看图像也验证了确实如此但为什么opencv打开的频谱图效果良好呢OpenCV的工作路径是项目下的不是解决方案
咨询了一下师兄师兄认为是imshow的问题因为matlab中的imshow就可选对不同数据进行不同的展示方法。猜测imshow对normalize之后是0~1之间的浮点数有一个特别的操作而不是简单的将0~1之间的浮点数作为最终展示的图像的灰度值。查了一下资料果然如此如果载入的图像是8位无符号类型8-bitunsigned就显示图像本来的样子。如果图像是16位无符号类型16-bitunsigned或32位整型32-bit integer便用像素值除以256。也就是说值的范围是[0,255 x 256]映射到[0,255]。
如果图像是32位浮点型32-bitfloating-point像素值便要乘以255。也就是说该值的范围是[0,1]映射到[0,255]。有个术语称之为亮度图像。但为什么归一化到0~1再乘255和直接归一化在0~255的效果不同呢这是因为取对数后的频谱依然存在大量大于255的幅值如果直接归一化在0~255这些大于255的数值依然是255.归一化在0~1再乘255相当于进行了缩放先缩小范围在0~1尽管大量的值依然集中在1附近然后按比例放大到图像灰度值的范围。
有的资料甚至说OpenCV的imshow不允许处理除了uchar8-bitunsigned以外的数据类型。如果保存成jpeg格式每个像素用24bit真彩表示JPG是有损压缩。JPG是有损压缩PNG是无损压缩bmp没有经过压缩。
DFT函数问题
dft(complexI,complexI);DFT要分别计算实部和虚部把要处理的图像作为输入的实部、一个全零的图像作为输入的虚部。dft()输入和输出应该分别为单张图像所以要先用merge()把实虚部图像合并分别处于图像comImg的两个通道内。计算得到的实虚部仍然保存在comImg的两个通道内。
傅里叶变换的最优尺寸问题
getOptimalDFTSize函数得到最优的行列数然后使用copyMakeBorder进行填充。数组的大小是2的整数次幂的情况dft变换的速度是最快的。当然大部分情况下数组的大小不会是2的次幂但是如果其大小可以分解为2、3、5的累成也是能够提高dft的效率的。
附dft在python中的实现
#!/usr/bin/env.python
#!/usr/bin/env.python
import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltimg cv.imread(Joseph_Fourier_250.jpg, 0)
f np.fft.fft2(img) # 快速傅里叶变换算法得到频率分布
fshift np.fft.fftshift(f) # 默认结果中心点位置是在左上角转移到中间位置fimg np.log(np.abs(fshift)1) # fft 结果是复数求绝对值结果才是振幅(化为实数
cv.imwrite(dft.bmp,fimg);
fimg0np.log(np.abs(f)1) #取对数可以将数据变化范围缩小# 展示结果
plt.subplot(131), plt.imshow(img, gray), plt.title(Original Fourier)
plt.subplot(132), plt.imshow(fimg0, gray), plt.title(Fourier Fourier)
plt.subplot(133), plt.imshow(fimg, gray), plt.title(Fourier Fourier-shift)
plt.show()
Python代码没有图像填充等操作直接对图像进行dft变换中心平移后取对数。但保存频谱图再查看会发现保存的频谱图的灰度值依然在0~1区间。Reference:
1. 代码opencv\sources\samples\cpp\dft.cpp
2. 乘以255https://blog.csdn.net/honey1992/article/details/50055615
3. imshow不允许除uchar以外https://blog.csdn.net/kelvin_yan/article/details/50033981
4. JPG、PNGhttps://www.cnblogs.com/skyfsm/p/7136709.html
5. http://johnhany.net/2013/11/dft-based-text-rotation-correction/
6. 补码按位与偶数https://blog.csdn.net/autocyz/article/details/48803859