雾看OpenCV(13)——图像变换之傅里叶变换

前言

本小节我们将要学习:
• 使用 OpenCV 对图像进行傅里叶变换
• 使用 Numpy 中 FFT(快速傅里叶变换)函数
• 傅里叶变换的一些用处
• 我们将要学习的函数有:cv2.dft(),cv2.idft() 等

正文

傅里叶变换

离散傅里叶变换(Discrete Fourier Transform,缩写为DFT),是傅里叶变换在时域和频域上都呈离散的形式,将信号的时域采样变换为其DTFT的频域采样。

一维傅里叶变化公式
在这里插入图片描述
流行的一幅图像
在这里插入图片描述
在这里插入图片描述
从上图就很容易看出,其实任何一个连续信号都是由若干个正弦信号或余弦信号叠加而成,你从时域方向看过去,确实就是一个连续的信号,但俗话说,横看成岭侧成峰,你从侧面看过去,不就是一个一个信号,它的高度,就是它的频率了,这应该不难理解。
接下来,分析一下这三个量

  1. 那么 w1,w2,…w1,w2,…可以看成是频率的变化(一般认为就是从1,2,…n定死了),所有的A就是对应频率下的振幅,所有的 ϕϕ就是对应频率下的相位,那么对于任一个信号,如果都认为频率w是从1,2,3…一直增加的话,那么每个信号就只由一组振幅与一组 ϕϕ来决定,他们的不同决定了最终信号的不同。
  1. 再说一下振幅,这里的振幅实际上也就是这个正弦信号或余弦信号所能达到的最大值,那意味着这个振幅越大,对整个原始信号的影响越大。
  1. 频率:对应频率,肯定是当w =1的时候,对总的信号影响最大,也就是说,当w越大,其实整个信号越高频,那么振幅就会逐渐减少,从而他们对应原始信号的印象也就越小,所以,有时我们去掉高频信号,并不会对整体的原始信号产生多大的影响。
  1. 来理解下什么是相位,相位表示其实表面对应频率下的正弦分量偏离原点的程度。 也就是本来当相位为0时,你每个正弦图像的凹槽或凸槽其实都是落在pai的整数倍的,但因为这个数据的影响,会导致原来图像发生偏移。

二维傅里叶变换公式
在这里插入图片描述

用处

  1. DFT:可用来分析图像的频域特性。
  2. 傅里叶变换的目的是可将时域(即时间域)上的信号转变为频域(即频率域)上的信号,随着域的不同, 对同一个事物的认知角度也随之改变,因此在时域中某些不好处理的地方,在频域就可以较为简单地处理。这就可以大量减少处理信号存储量。

特征

  1. 对于一个正弦信号,如果它的幅度变化非常快,我们可以说他是高频信号,如果变化非常慢,我们称之为低频信号。而恰好,边界和噪声是图像中的高频分量,注意,我们这里强调的是变换快,而不是变换次数多,这是两个概念。

傅里叶变换公式
在这里插入图片描述

Numpy中的傅里叶变换

效果图
在这里插入图片描述
流程图
在这里插入图片描述
code

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

img = cv.imread("../../images/lena.jpg")
src = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
print(src)
f = np.fft.fft2(src)#做二维傅里叶变换
print(f)
fshift = np.fft.fftshift(f)#这里的作用是将低频部分(幅值为0的部分移动N/2)
magnitude_spectrum = 20*np.log(np.abs(fshift))#根据该数据作幅度图

plt.subplot(121) ,plt.imshow(src,cmap='gray')
plt.title("input image"),plt.xticks([]),plt.yticks([])

plt.subplot(122),plt.imshow(magnitude_spectrum,cmap='gray')
plt.title("fshift"),plt.xticks([]),plt.yticks([])
plt.show()
# cv.namedWindow("input image",cv.WINDOW_AUTOSIZE)
# cv.imshow('input image', src)
cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口

cv.destroyAllWindows()

傅里叶逆变换
效果图
在这里插入图片描述
关于这个操作,是将原来的高频噪声进行去除,将那部分数据进行赋值为0。然后,用剩下的fshift的部分来进行傅里叶变换的逆变换,也就是将低频噪声进行变换,从而得到大部分的轮廓。
code

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


img = cv.imread("../../images/lena.jpg")
src = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
f = np.fft.fft2(src)
fshift = np.fft.fftshift(f)
magnitude_spectrum = 20*np.log(np.abs(fshift))#绘制幅值图

rows,cols = src.shape#注意获取的要是灰度图像的两路信号
crow,ccol = int(rows/2),int(cols/2)#获得图像宽高的一半
fshift[crow-30:crow+30,ccol-30:ccol+30] = 0#将中间那部分的频率设置为
f_ishift = np.fft.ifftshift(fshift)#numpy.fft模块中的fftshift函数可以将FFT输出中的直流分量移动到频谱的中央。ifftshift函数则是其逆操作。
img_back = np.fft.ifft2(f_ishift)#傅里叶的逆变换
#取绝对值
img_back = np.abs(img_back)

plt.subplot(131) ,plt.imshow(src,cmap='gray')
plt.title("input image"),plt.xticks([]),plt.yticks([])

plt.subplot(132),plt.imshow(magnitude_spectrum,cmap='gray')
plt.title("fshift"),plt.xticks([]),plt.yticks([])

plt.subplot(133),plt.imshow(img_back,cmap='gray')
plt.title("fshift"),plt.xticks([]),plt.yticks([])


plt.show()
# cv.namedWindow("input image",cv.WINDOW_AUTOSIZE)
# cv.imshow('input image', src)
cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口

cv.destroyAllWindows()

OpenCV的傅里叶变换

流程图
在这里插入图片描述
效果图

在这里插入图片描述

code

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
src = cv.imread("../../images/lena.jpg",0)#读入灰度图片是写0
dft = cv.dft(np.float32(src), flags=cv.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
magnitude_spectrum = 20 * np.log(cv.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))

plt.subplot(121), plt.imshow(src, 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()


# cv.namedWindow("input image",cv.WINDOW_AUTOSIZE)
# cv.imshow('input image', src)
cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口

cv.destroyAllWindows()

OpenCV的逆傅里叶变换

HPF(高通滤波)

高通滤波(high-pass filter) 是一种过滤方式,规则为高频信号能正常通过,而低于设定临界值的低频信号则被阻隔、减弱。但是阻隔、减弱的幅度则会依据不同的频率以及不同的滤波程序(目的)而改变。它有的时候也被叫做低频去除过滤(low-cut filter)。高通滤波是低通滤波的对立。

LPF(低通滤波)

低通滤波(Low-pass filter) 是一种过滤方式,规则为低频信号能正常通过,而超过设定临界值的高频信号则被阻隔、减弱。但是阻隔、减弱的幅度则会依据不同的频率以及不同的滤波程序(目的)而改变。它有的时候也被叫做高频去除过滤(high-cut filter)或者最高去除过滤(treble-cut filter)。低通过滤是高通过滤的对立。
效果图
在这里插入图片描述
效果图
在这里插入图片描述
code

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

src = cv.imread("../../images/lena.jpg",0)#读入灰度图片是写0
##### 进行傅里叶变换操作
dft = cv.dft(np.float32(src), flags=cv.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
magnitude_spectrum = 20 * np.log(cv.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))


#####进行傅里叶逆变换操作
row,col = src.shape
rows,cols = int(row/2),int(col/2)
mask = np.zeros((row,col,2),np.uint8)
mask[rows - 30:rows + 30, cols - 30:cols + 30] = 1
new_img = dft_shift*mask#获取感兴趣区域
idft_shift = np.fft.ifftshift(new_img)#将低频区域回归原位,再次往四周移
idft = cv.idft(idft_shift)#拿到要拿的应有的shift图了,就可以进行逆傅里叶变换了
imagitude = cv.magnitude(idft[:,:,0],idft[:,:,1])

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

# cv.namedWindow("input image",cv.WINDOW_AUTOSIZE)
# cv.imshow('input image', src)
cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口

cv.destroyAllWindows()

DFT的性能优化

当数组的大小为某些值时 DFT 的性能会更好。当数组的大小是 2 的指数 时 DFT 效率最高。当数组的大小是 2,3,5 的倍数时效率也会很高。所以 如果你想提高代码的运行效率时,你可以修改输入图像的大小(补 0)。对于 OpenCV 你必须自己手动补 0。但是 Numpy,你只需要指定 FFT 运算的大 小,它会自动补 0。

函数

cv.magnitude()

void magnitude(InputArray x,InputArray y,OutputArray magnitude)
第一个参数:InputArray类型的x,表示矢量的浮点型X坐标值,也就是实部
第二个参数:InputArray类型的y,表示矢量的浮点型Y坐标值,也就是虚部
第三个参数:OutputArray类型的magnitude,输出的幅值,它和第一个参数X有着同样的尺寸和类型

Magnitude()函数的原理:
在这里插入图片描述

cv.getOptimalDFTSize()

DFT最优尺寸大小:getOptimalDFTSize()函数
getOptimalDFTSize函数返回给定向量尺寸的傅里叶最优尺寸大小。
int getOptimalDFTSize(int vecsize)
此函数的唯一一个参数为int类型的vecsize,向量尺寸,即图片的rows、cols

cv2.copyMakeBoder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REPLICATE)

  1. 参数说明: img表示需要补零的图片, top_size, bottom_size, left_size, right_size表示需要补零的尺寸, cv2.BORDER_REPLICATE表示补零的方式,这个是复制
  1. 补零的方式说明
    cv2.BORDER_REPLICATE: 进行复制的补零操作, 只对边缘的点进行复制,然后该列上的点都是这些
    cv2.BORDER_REFLECT: 进行翻转的补零操作,举例只对当前对应的边缘 gfedcba|abcdefgh|hgfedcb
    cv2.BORDER_REFLECT_101: 进行翻转的补零操作, gfedcb|abcdefgh|gfedcb
    cv2.BORDER_WRAP: 进行上下边缘调换的外包复制操作 bcdegh|abcdefgh|abcdefg

对一些主要功能函数的解释

Numpy实现
fft = np.fft.fft2(img)
将空间域转化为频率域

OpenCV实现
dft = cv2.dft(np.float32(img),flag=cv2.DFT_COMPLEX_OUTPUT)
这个函数与np.fft.fft2(img)实现相同的功能,但要注意先将img转化为float32的格式,flag传入cv2.DFT_COMPLEX_OUTPUT表示输出的是一个复数。

shift = np.fft.fftshift(fft)
将低频部分移动到图像中心

ishift = np.fft.ifftshift(shift)
将低频部分从中心移动回到左上角

ifft = np.fft.ifft2(ishift)
将频率域转化回空间域

idft = cv2.idft(ishift)
这个函数与np.fft.ifft2(shift)实现相同的功能,注意这两个函数输出都是一个复数,都需要用到下面这个函数转化回图像。

img = cv2.magnitude(real,imag)
real是虚数的实部,imag是虚数的虚部
需要注意的是,np.fft.ifft2()返回的是一个虚数数组,需要用np.real(ifft)求得实部,用np.imag(ifft)求得虚部;
而cv2.idft()返回的是一个双通道图像,用idft[:,:,0]求得实部,用idft[:,:,1]求得虚部。

结果得到的并不是一个归一化的图像,我们可以使用cv2.normalize()进行归一化或者使用20*np.log(np.abs(img))进行归一化。

参考

  1. OpenCV-Python-Tutorial-中文版20160814.pdf
  2. 图像傅立叶变换
  3. Python下opencv使用笔记(十)(图像频域滤波与傅里叶变换)
  4. 傅里叶分析和图像的傅里叶频谱解析
  5. Opencv学习----矩阵操作-基本操作
  6. 分别用OpenCV-Python和Numpy实现傅里叶变换和逆傅里叶变换
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值