python opencv入门 傅里叶变换(25)

内容来自OpenCV-Python Tutorials 自己翻译整理

傅里叶变换原理的文章

目标
用opencv对图像进行傅里叶变换
numpy进行快速傅里叶变换
傅里叶变换的用处
学习函数函数cv2.dft(),cv2.idft()

原理
傅里叶变换常用来分析各种滤波器的特性。可以是用2D离散傅里叶变换分析图像的频域特性。

(个人理解,在图像问题当中,频域是指图像的灰度变化,也就是灰度图像的梯度值,这个和轮廓的原理差不多,灰度值变化剧烈的叫做高频分量,例如边界和噪声。灰度值变化缓慢的称谓低频分量)

实现2D离散傅里叶变换(DFT)的的算法叫做快速傅里叶变换(FFT)。

对图像进行X方向和Y方向的傅里叶变换,会得到图像的频域表示图。

直观理解,一个正弦信号,如果幅度变换很快,可以称之为高频信号,如果变换慢,可以称之为低频信号。在图像中,灰度值变化快的位置,可以称之为高频分量(只变化快而不是次数多),灰度值变化慢的称之为低频分量。

图像使用使用二维离散傅里叶变换后得到一个复数矩阵,叫做图像的频谱图。
矩阵中的每个值对应F(u,v),由于是复数,所以一般在数学计算上都用极坐标表示。
F(u)=|F(u)|ejϕ(u)

其中
|F(u)|=[R2(u)+I2(u)]12 叫做变换的振幅或者频率谱,实数和复数分别表示振幅和相位

其中
ϕ(u)=arctan[I(u)R(u)] 叫做相角或者相位谱

傅里叶变换值的平方,叫做普功率

numpy中的傅里叶变换
np.fft.fft2()函数可对信号进行频率转换,输出一个复数数组(有人给翻译成复杂数组,明显错误!)。
第一个参数要求为灰度图像,第二个参数为输出数组大小(可选),输出数组大小和输入图像大小一样。若输出结果比输入大,输入图像要在变换前补0,输出结果比输入小,输入图像被切割。

公式
这里写图片描述

代码
频率为0 的部分(直流分量)在输出图像的左上角。(2D傅里叶变换F(x,y)的F(0,0)位置在图像的左上角,F(0,0)表示的是图像灰度的均值)

如果想让直流分量在输出图像的重心,那么需要将结果沿着两个方向平移分别平移N/2和M/2(原文是两个方向平移N/2,我感觉写错了-_-),其中M和N代表图像的长宽。
使用np.fft.fftshift()函数来实现平移,参数输入使用2维傅里叶变换得到的复杂数组即可。

import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('3.jpg',0)
f = np.fft.fft2(img)
print(f)
fshift = np.fft.fftshift(f)#得到结果为复数

magnitude_spectrum = 20*np.log(np.abs(fshift))#先取绝对值,表示取模。取对数,将数据范围变小

plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

python opencv入门 傅里叶变换(25)_第1张图片

对频域图像进行操作,例如使用高通滤波和重建图像(DFT的逆变换)。
(高通滤波的意思就是增强边缘,非边缘部分被过滤,同理,低通滤波就是将图像边的平滑)

使用一个60×60的矩形窗口进行蒙板操作,去除低频分量,使用函数np.fft.ifftshift将图像中心平移回左上角,然后使用函数 np.ifft2()进行FFT逆变换,将得到的复数结果取绝对值。

import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('3.jpg',0)
f = np.fft.fft2(img)#得到结果为复数矩阵
fshift = np.fft.fftshift(f)#直接取中心

rows, cols = img.shape
crow,ccol = rows/2 , cols/2


fshift[int(crow-30):int(crow+30), int(ccol-30):int(ccol+30)] = 0#蒙板大小60×60
f_ishift = np.fft.ifftshift(fshift)#回到左上角
img_back = np.fft.ifft2(f_ishift)#使用FFT逆变换,此时结果仍然是复数
img_back = np.abs(img_back)#还原成JET图像,变成可以看的图像
plt.subplot(131),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132),plt.imshow(img_back, cmap = 'gray')
plt.title('Image after HPF'), plt.xticks([]), plt.yticks([])
plt.subplot(133),plt.imshow(img_back)
plt.title('Result in JET'), plt.xticks([]), plt.yticks([])
plt.show()

python opencv入门 傅里叶变换(25)_第2张图片

上面例子可以看到高通滤波和边缘检测差不多,同时可以看到上面图片当中除了裂痕的部分,都被过滤过去了,说明非裂痕部分是低频区域,都被过滤了

我选的图片是人工画出来的图片,没有明暗效果,而且相同颜色的灰度值部分相同。如果是照的图片,使用傅里叶变换会有振铃效应,因为是用矩形窗口进行蒙板操作,蒙板被转换成正弦形状就会有上面的问题,最好是用高斯窗口。

OpenCV 中的傅里叶变换

函数为 cv2.dft() 和 cv2.idft(),分别为傅里叶变换和逆变换
输出结果和上面一样,但是,分别是双通道,第一个通道是实数部分,第二个通道是虚数
输入图像要转换成小数

import numpy as np
import cv2
from matplotlib import pyplot as plt


img = cv2.imread('33.jpg',0)#读图

dft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT)#傅里叶变换

dft_shift = np.fft.fftshift(dft)#平移到中心

magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))#频谱图

plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

使用函数 cv2.cartToPolar()会同时得到幅度和相位,此函数也是直角坐标转换为极坐标的函数。

之前做了一个高通滤波器,现在实现一个低通滤波器,将高频部分去除。
首先实现一个蒙板,低频去对应的部分设置为1,高频设置为0。

import numpy as np
import cv2
from matplotlib import pyplot as plt


img = cv2.imread('3.jpg',0)#读图

dft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)

rows, cols = img.shape
crow,ccol = rows/2 , cols/2
# create a mask first, center square is 1, remaining all zeros
mask = np.zeros((rows,cols,2),np.uint8)
mask[int(crow)-30:int(crow)+30, int(ccol)-30:int(ccol)+30] = 1
# apply mask and inverse DFT
fshift = dft_shift*mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1])
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img_back, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

python opencv入门 傅里叶变换(25)_第3张图片

opencv中的函数比numpy的速度要快,但是numpy的操作更加友好

DFT性能优化

数组大小是2的指数DFT效率最高,数组大小是2,3,5的倍数时,性能也挺高。所以可以通过修改输入图像的大小(补0)来提高效率。opencv需要手动补0,numpy自动。

在opencv中有一个函数,cv2.getOptimalDFTSize(),可以被cv2.dft和np.fff.fff2使用。

import numpy as np
import cv2
from matplotlib import pyplot as plt


img = cv2.imread('33.jpg',0)#读图

rows,cols = img.shape

print("%s %s"%(rows,cols))

nrows = cv2.getOptimalDFTSize(rows)
ncols = cv2.getOptimalDFTSize(cols)

print("%s %s"%(nrows,ncols))

输出结果
67 72
72 72

把数据拷贝过去或者使用函数cv2.copyMakeBoder()

nimg = np.zeros((nrows,ncols))
nimg[:rows,:cols] = img

或者

right = ncols - cols
bottom = nrows - rows
#just to avoid line breakup in PDF file
bordertype = cv2.BORDER_CONSTANT
nimg = cv2.copyMakeBorder(img,0,bottom,0,right,bordertype, value = 0)

你可能感兴趣的:(python opencv入门 傅里叶变换(25))