《OpenCv视觉之眼》Python图像处理六 :Opencv图像傅里叶变换和傅里叶逆变换原理及实现

本专栏主要介绍如果通过OpenCv-Python进行图像处理,通过原理理解OpenCv-Python的函数处理原型,在具体情况中,针对不同的图像进行不同等级的、不同方法的处理,以达到对图像进行去噪、锐化等一系列的操作。同时,希望观看本专栏的小伙伴可以理解到OpenCv进行图像处理的强大哦,如有转载,请注明出处(原文链接和作者署名),感谢各位小伙伴啦!

前文参考:
《OpenCv视觉之眼》Python图像处理一 :Opencv-python的简介及Python环境搭建
《OpenCv视觉之眼》Python图像处理二 :Opencv图像读取、显示、保存基本函数原型及使用
《OpenCv视觉之眼》Python图像处理三 :Opencv图像属性、ROI区域获取及通道处理
《OpenCv视觉之眼》Python图像处理四 :Opencv图像灰度处理的四种方法及原理
《OpenCv视觉之眼》Python图像处理五 :Opencv图像去噪处理之均值滤波、方框滤波、中值滤波和高斯滤波

上次博客,我们讲解了OpenCV图像去噪的几种基本方法,包括均值滤波、中值滤波、高斯滤波等,也就是在图像时域上进行去噪的方法,其中,高斯滤波和中值滤波是需要重点掌握的图像去噪方法,因为对图像的高斯噪声和椒盐噪声去除的效果比较好;除了以上时域上的去噪方法之外,OpenCV也提供了对图像频域上的去噪的函数方法,例如高通滤波和低通滤波,通常用作图像除噪、图像增强和锐化等,因此,高通滤波和低通滤波也是我们学习OpenCV需要掌握的两种图像去噪的方法,但由于高通和低通滤波的实现原理是通过傅里叶变换后进行处理,然后通过傅里叶逆变换进行实现的,所以首先我们需要了解图像傅里叶变换和傅里叶逆变换的原理。

本次博客,林君学长将带大家了解OpenCV图像的傅里叶变换和逆变换原理,通过原理编写傅里叶变换和逆变换的功能函数,并同时介绍OpenCV傅里叶变换和逆变换的库函数使用方法,一起学习吧!

在进行傅里叶变换学习时,我们必须要明白为什么要在频率域研究图像增强?

  • 可以利用频率成分和图像外表之间的对应关系。一些在空间域表述困难的增强任务,在频率域中变得非常普通
  • 滤波在频率域更为直观,它可以解释空间域滤波的某些性质
  • 可以在频率域指定滤波器,做反变换,然后在空间域使用结果滤波器作为空间域滤波器的指导
  • 一旦通过频率域试验选择了空间滤波,通常实施都在空间域进行

提示:傅里叶变换是为后面低通滤波或者说是高通滤波做准备哦,一般来说,处理的是灰度图像,通过上面的介绍我们也可以看出,但下面的原理我们通过处理彩色图像编写的,而OpenCV库函数是在处理灰度图像基础上实现的

一、傅里叶变换

  • 傅里叶变换(Fourier Transform,FT)后,对同一事物的观看角度随之改变,可以从频域里发现一些从时域里不易察觉的特征。某些在时域内不好处理的地方,在频域内可以容易地处理。

1、傅里叶变换原理

1)、什么是傅里叶变换?
能将满足一定条件的某个函数表示成三角函数(正弦和/或余弦函数)或者它们的积分的线性组合。在不同的研究领域,傅立叶变换具有多种不同的变体形式,如连续傅立叶变换和离散傅立叶变换。傅里叶变换是一种分析信号的方法,它可分析信号的成分,也可用这些成分合成信号。许多波形可作为信号的成分,比如正弦波、方波、锯齿波等,傅里叶变换用正弦波作为信号的成分。
傅里叶变换的实质是将一个信号分离为无穷多多正弦/复指数信号的加成,也就是说,把信号变成正弦信号相加的形式——既然是无穷多个信号相加,那对于非周期信号来说,每个信号的加权应该都是零——但有密度上的差别,你可以对比概率论中的概率密度来思考一下——落到每一个点的概率都是无限小,但这些无限小是有差别的所以,傅里叶变换之后,横坐标即为分离出的正弦信号的频率,纵坐标对应的是加权密度。
在这里插入图片描述
2)、傅里叶变换作用?
傅里叶变换可以将一个时域信号转换成在不同频率下对应的振幅及相位,其频谱就是时域信号在频域下的表现,而反傅里叶变换可以将频谱再转换回时域的信号。最简单最直接的应用就是时频域转换。将图像时域处理上面复杂的特征转为频域实现简单化,对简单化的特征进行处理,然后再通过傅里叶逆变换回到原来的图像,如下所示:
在这里插入图片描述
3)、图像进行傅里叶变换原理
我们知道,灰度图像是由二维的离散的点构成的。二维离散傅里叶变换(Two-Dimensional Discrete Fourier Transform)常用于图像处理中,对图像进行傅里叶变换后得到其频谱图。频谱图中频率高低表征图像中灰度变化的剧烈程度。图像中边缘和噪声往往是高频信号,而图像背景往往是低频信号。我们在频率域内可以很方便地对图像的高频或低频信息进行操作,完成图像去噪,图像增强,图像边缘提取等操作,因此,对图像进行傅里叶变换的原理如下公式:
F ( u , v ) = x = 0 H 1 y = 0 W 1 f ( x , y ) e 2 j π ( u x / H + v y / W ) H W F ( u , v ) f ( x , y ) u [ 0 , H 1 ] v [ 0 , W 1 ] F(u,v)=\sum_{x=0}^{H-1}\sum_{y=0}^{W-1}f(x,y)e^{-2j\pi(ux/H+vy/W)} \\\\ 图像高H,宽W。F(u,v)表示频域图像,f(x,y)表示时域图像。u的范围为[0,H-1],v的范围为[0,W-1] ↑
那么怎么将上面的二维公式转为我们可以理解的二维图像呢?其实很简单, f ( x , y ) f(x,y) 表示在(x,y)点上的图像的像素值,该像素值乘以后面的在(u,v)点的 e 2 j π ( u x / H + v y / W ) e^{-2j\pi(ux/H+vy/W)} 处理出来的一个数,然后对图像所有像素进行遍历,最后求和,就得到在频域上该(u,v)点对应的值,通过该原理,我们就可以自己写出傅里叶变换的功能函数了,来看吧

2、自定义傅里叶变换功能函数

1)、自定义傅里叶变换函数

import cv2
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] #显示中文
#自定义傅里叶变换功能函数
def dft(img):
    #获取图像属性
    H,W,channel=img.shape
    #定义频域图,从公式可以看出为求出结果为复数,因此,需要定义为复数矩阵
    F = np.zeros((H, W,channel), dtype=np.complex)
    # 准备与原始图像位置相对应的处理索引
    x = np.tile(np.arange(W), (H, 1))
    y = np.arange(H).repeat(W).reshape(H, -1)
    #通过公式遍历
    for c in range(channel):#对彩色的3通道数进行遍历
        for u in range(H):
            for v in range(W):
                F[u, v, c] = np.sum(img[..., c] * np.exp(-2j * np.pi * (x * u / W + y * v / H))) / np.sqrt(H * W)
    return F

2)、读取图像,进行傅里叶变化并显示变换后的结果

#读取图像
img=cv2.imread("my.jpg")
#进行图像裁剪,加快傅里叶运算速率。将原始尺寸缩放为100*100的尺寸
img = cv2.resize(img, (100, 100), interpolation=cv2.INTER_CUBIC)
#BGR转换为RGB显示格式,方便通过matplotlib进行图像显示
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
#调用傅里叶变换函数
result =dft(img)
#将傅里叶频谱图从左上角移动到中心位置
fshift = np.fft.fftshift(result)
#将复数转为浮点数进行傅里叶频谱图显示
fimg = np.log(np.abs(fshift))
#图像显示
plt.subplot(121), plt.imshow(img), plt.title('原图像')
plt.axis('off')
plt.subplot(122), plt.imshow(fimg), plt.title('傅里叶变换')
plt.axis('off')
plt.show()

在这里插入图片描述
上图是将原图进行缩放之后进行傅里叶变换的,只为了降低函数运行时间,尽快的得出结果;当然,OpenCV中也提供了图像傅里叶变换的库函数,接下,我们介绍OpenCV中傅里叶库函数的使用!

3、OpenCV库函数实现傅里叶变换

OpenCV 中相应的函数是cv2.dft()和用Numpy输出的结果一样,但是是双通道的。第一个通道是结果的实数部分,第二个通道是结果的虚数部分,并且输入图像要首先转换成 np.float32 格式。
1)、函数原型:img=cv2.dft(src, flags=None, nonzeroRows=None)

  • src表示输入图像,需要通过np.float32转换格式
  • flags表示转换标记,其中DFT _INVERSE执行反向一维或二维转换,而不是默认的正向转换;DFT _SCALE表示缩放结果,由阵列元素的数量除以它;DFT _ROWS执行正向或反向变换输入矩阵的每个单独的行,该标志可以同时转换多个矢量,并可用于减少开销以执行3D和更高维度的转换等;DFT _COMPLEX_OUTPUT执行1D或2D实数组的正向转换,这是最快的选择,默认功能;DFT _REAL_OUTPUT执行一维或二维复数阵列的逆变换,结果通常是相同大小的复数数组,但如果输入数组具有共轭复数对称性,则输出为真实数组
  • nonzeroRows表示当参数不为零时,函数假定只有nonzeroRows输入数组的第一行(未设置)或者只有输出数组的第一个(设置)包含非零,因此函数可以处理其余的行更有效率,并节省一些时间;这种技术对计算阵列互相关或使用DFT卷积非常有用

注意,由于输出的频谱结果是一个复数,需要调用cv2.magnitude()函数将傅里叶变换的双通道结果转换为0到255的范围
2)、cv2.magnitude()函数原型:cv2.magnitude(x, y)

  • x表示浮点型X坐标值,即实部
  • y表示浮点型Y坐标值,即虚部

最终输出结果为幅值,即:

i m g ( I ) = x ( I ) 2 + y ( I ) 2 img(I)=\sqrt{x(I)^2+y(I)^2}
3)、傅里叶库函数使用

#傅里叶库函数使用
import cv2
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] #显示中文
#读取图像
img=cv2.imread("my.jpg",0)
#进行图像裁剪,加快傅里叶运算速率。将原始尺寸缩放为100*100的尺寸
#OpneCV傅里叶变换函数
result =cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT)#需要将图像进行一次float转换
#将频谱低频从左上角移动至中心位置
dft_shift = np.fft.fftshift(result)
#频谱图像双通道复数转换为0-255区间
result1 = 20*np.log(cv2.magnitude(dft_shift[:,:,0], dft_shift[:,:,1]))
#图像显示
plt.subplot(121), plt.imshow(img,'gray'), plt.title('原图像')
plt.axis('off')
plt.subplot(122), plt.imshow(result1,'gray'), plt.title('傅里叶变换')
plt.axis('off')
plt.show()

在这里插入图片描述
使用OpenCV函数需要先将图像转为灰度图,然后才能进行处理,这里林君学长再介绍一个Numpy库的傅里叶变换处理,给出相关代码,小伙伴们自己理解哈,不做说明哦!
4)、扩展:Numpy库的图像傅里叶变换

#Numpy傅里叶变换
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] #显示中文
#读取图像灰度图像
img = cv.imread('my.jpg',0)
#快速傅里叶变换算法得到频率分布
f = np.fft.fft2(img)
#默认结果中心点位置是在左上角,
#调用fftshift()函数转移到中间位置
fshift = np.fft.fftshift(f)       
#fft结果是复数, 其绝对值结果是振幅
fimg = np.log(np.abs(fshift))
#展示结果
plt.subplot(121), plt.imshow(img,'gray'), plt.title('原图像')
plt.axis('off')
plt.subplot(122), plt.imshow(fimg,'gray'), plt.title('傅里叶变换')
plt.axis('off')
plt.show()

在这里插入图片描述

二、傅里叶逆变换

1、傅里叶逆变换原理

1)、傅里叶反变换原理就是将图像频域还原为图像时域,具体原理公式如下所示:
f ( x , y ) = u = 0 H 1 v = 0 W 1 F ( u , v ) e 2 j π ( u x / H + v y / W ) H W f ( x , y ) , F ( u , v ) x [ 0 , H 1 ] y [ 0 , W 1 ] f(x,y)=\sum_{u=0}^{H-1}\sum_{v=0}^{W-1} F(u,v)e^{2j\pi(ux/H+vy/W)} \\\\ 图像高H,宽W。f(x,y)表示时域图像,F(u,v)表示频域图像。x的范围为[0,H-1],y的范围为[0,W-1] ↑
对于公式的理解,和上面傅里叶变换公式理解一致

2、自定义傅里叶逆变换功能函数

1)、自定义傅里叶逆变换功能函数

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] #显示中文
#傅里叶反变换
def idft(G):
    H, W, channel = G.shape
    #定义空白时域图像
    out = np.zeros((H, W, channel), dtype=np.float32)
    # 准备与原始图像位置相对应的处理索引
    x = np.tile(np.arange(W), (H, 1))
    y = np.arange(H).repeat(W).reshape(H, -1)
    #通过公式遍历
    for c in range(channel):
        for u in range(H):
            for v in range(W):
                out[u, v, c] = np.abs(np.sum(G[..., c] * np.exp(2j * np.pi * (x * u / W + y * v / H)))) / np.sqrt(W * H)
    # 剪裁
    out = np.clip(out, 0, 255)
    out = out.astype(np.uint8)
    return out

2)、调用函数,先进行傅里叶变换,然后进行傅里叶逆变换并显示结果

#读取图像
img=cv2.imread("my.jpg")
#进行图像裁剪,加快傅里叶运算速率。将原始尺寸缩放为100*100的尺寸
img = cv2.resize(img, (100, 100), interpolation=cv2.INTER_CUBIC)
#BGR转换为RGB显示格式,方便通过matplotlib进行图像显示
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
#调用傅里叶变换函数,先将图像进行傅里叶变换,该函数上面我们通过原理写出了哦!
result =dft(img)
#将傅里叶频谱图从左上角移动到中心位置
fshift = np.fft.fftshift(result)
#将复数转为浮点数进行傅里叶频谱图显示
fimg = np.log(np.abs(fshift))
#调用傅里叶逆变换函数
result2=idft(result)
#图像显示
plt.subplot(131), plt.imshow(img), plt.title('原图像')
plt.axis('off')
plt.subplot(132), plt.imshow(fimg), plt.title('傅里叶变换')
plt.axis('off')
plt.subplot(133), plt.imshow(result2), plt.title('傅里叶逆变换')
plt.axis('off')
plt.show()

在这里插入图片描述
最后我们可以看出,我们通过原理书写的傅里叶变换和傅里叶逆变换是没有问题的,因此,而且傅里叶反变换的结果是和之前的图像一模一样的,几乎没有什么损失,后面我们将会讲到在傅里叶变换的基础上实现高通或者说是低通滤波,来对图像去噪,记得收看啦!

3、OpenCV库函数实现傅里叶逆变换

OpenCV中也提供了傅里叶逆变换的库函数,我们一起看一下函数原型吧
1)、函数原型img=cv2.idft(src,flags,nonzeroRows)

  • src表示输入图像,包括实数或复数
  • flags表示转换标记
  • nonzeroRows表示要处理的img行数,其余行的内容未定义

2)、OpenCV库函数实现傅里叶逆变换的完整代码如下所示:

#OpenCV实现傅里叶逆变换
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] #显示中文

#读取图像
img = cv2.imread('my.jpg', 0)
#傅里叶变换
dft = cv2.dft(np.float32(img), flags = cv2.DFT_COMPLEX_OUTPUT)#傅里叶变换库函数调用
dftshift = np.fft.fftshift(dft)#将傅里叶频域从左上角移动到中间
res1= 20*np.log(cv2.magnitude(dftshift[:,:,0], dftshift[:,:,1]))#双通道结果转换为0到255的范围用于图像显示
#傅里叶逆变换
ishift = np.fft.ifftshift(dftshift)#将频域从中间移动到左上角
iimg = cv2.idft(ishift)#傅里叶逆变换库函数调用
res2 = cv2.magnitude(iimg[:,:,0], iimg[:,:,1])#双通道结果转换为0到255的范围
#显示图像
plt.subplot(131), plt.imshow(img, 'gray'), plt.title('原图像')
plt.axis('off')
plt.subplot(132), plt.imshow(res1, 'gray'), plt.title('傅里叶变换')
plt.axis('off')
plt.subplot(133), plt.imshow(res2, 'gray'), plt.title('傅里叶逆变换')
plt.axis('off')
plt.show()

在这里插入图片描述
3)、扩展:Numpy库也提供图像了傅里叶变换,完整代码如下所示:

#Numpy库的傅里叶变换
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] #显示中文
#读取图像
img = cv.imread('my.jpg',0)
#傅里叶变换
f = np.fft.fft2(img) #Numpy的库函数傅里叶变换
fshift = np.fft.fftshift(f)
res = np.log(np.abs(fshift))
#傅里叶逆变换
ishift = np.fft.ifftshift(fshift)
iimg = np.fft.ifft2(ishift) #Numpy的库函数傅里叶逆变换
iimg = np.abs(iimg)#将复数变为实数
#展示结果
plt.subplot(131), plt.imshow(img,'gray'), plt.title('原图像')
plt.axis('off')
plt.subplot(132), plt.imshow(res,'gray'), plt.title('傅里叶变换')
plt.axis('off')
plt.subplot(133), plt.imshow(iimg,'gray'), plt.title('Inverse Fourier Image')
plt.axis('off')
plt.show()

在这里插入图片描述

以上就是本次博客的全部内容,遇到问题的小伙伴记得留言评论,学长看到会为大家进行解答的,这个学长不太冷!

任何的收获不是巧合,而是每天的努力与坚持得来的。人生因有梦想,而充满动力。
不怕你每天迈一小步,只怕你停滞不前;不怕你每天做一点事,只怕你无所事事。 坚持,是生命的一种毅力!执行,是努力的一种坚持!

陈一月的又一天编程岁月^ _ ^

猜你喜欢

转载自blog.csdn.net/qq_42451251/article/details/107832069