以下部分文字资料整合于网络,本文仅供自己学习用!
一些在空间域表述困难的增强任务,在频率域中变得非常普通
滤波在频率域更为直观,你想想嘛,所谓滤波,就是把二维图像信号种某个频率信号给去掉。如果定义域是频率,值域是该频率信号的强度(这个坐标体系其实就是频域),直接把对应频率的强度弄为0不就行了,省的还去做卷积(联想空间域的滤波器!是不是得沿着图像走一遭。频域直接啪的一下把这个频率对应的图像信号置为0 ,多简单!)
请记住,我们下面在做的目的都是在理解的基础上,说明下面这个,也就是欧拉公式,表示的是以以频率为f
的在复平面上的一种圆周旋转,这对我们后面利用这点理解傅里叶变换至关重要
e i w t / e i 2 Π f e^{iwt}/e^{i2Πf} eiwt/ei2Πf
问题背景:
“李华有1
单位的本金,年利率为r
,银行一年结算n
次,则一年后李华得到:”
我们发现,当r=1
,即年利率为100%时,这个极限刚好是e。关键在于,当n趋于无穷时,每期的收益1/n
也在减小,最后二者抵消,恰好收敛到了一个确定的值——e
,
当原本要变换r
时,我们把这个变换细分成n
份,即每一次都变化r的1/n
,那么最后得到:
在继续深入之前,我们必须要理解复数乘法的意义。
复数一共有四种表示形式,这里我们讲的集合意义是从极坐标形式出发的,而推出复数乘法的几何意义,又是从代数形式和三角形式推出的:
推导过程:
复数的几何意义:
首先复数本身的极坐标的几何意义如上,通过推导过程可知,两个复数相乘,相当于:模长相乘,幅角相加
在回到这个公式,再思考一下它的意义:初始值为1(n=0),每次变化(在原有基础上)r/n
,总的变换是r
,这里可能不是很哈理解,需要结合复利来理解
当我们把r换做iΠ
,如下图
右边变成了什么?对,复数的乘积!这里带了个n
,可能不是很好理解,我们先暂时把n具体花为4,来帮助我们理解:
上图可以看到,两个规律:1、每次变换的是角度的累加和模长相乘,角度总变化是Π
。2、当n趋向于∞,此时左侧不断收拢。如下图所示
至此,我们达到了我们的目的:对下面公式的理解为:一个在复平面以w为角速度或f为角速度,模长为1旋转的矢量!这对于我们从旋转矢量的角度进行傅里叶变换的理解至关重要的。
同时也不要忘记它的三角形式
⭐还有一点要记住的是,e前面的系数,表示的是这个旋转矢量的模长,对应到三角形式,也就是幅值!(这也是为什么后来求解赋值时,需要对复数形式的信号求绝对值!)
这里我们先做好公式中定义,方面后面对公式理解的讲解:
f(x)
:表示在时域上的原信号,定义域是时间x
,值域是信号强度F(u)
:频域表示,u
表示的是频率,当u
取不同值时,它是一个复数!接下来的2.2.1
和2.2.2
将对一维的傅里叶公式进行理解和分析!(这些小节中对函数符号用法可能不一致,但是不要紧,清楚具体含义即可!)
从几何意义理解公式的本质(它要表达什么?):
首先,通过对指数e表示的复数那一节,我们对复数可以表示矢量进行周期旋转有了很好的理解。
当我们把原信号乘以一个这样的旋转向量时,我们就可以看作把原信号按照频率f
进行缠绕(时间是无穷到无穷,但是缠绕的频率是就是旋转向量旋转的频率f
是不变的!)
在这个旋转频率f
下,我们会得到这个缠绕图像的质心如下图所示:
正如上图所言,整个表达式的结果只是表示按照f
为频率进行缠绕(旋转)时,对应的质心,它是一个复数,由实部x和虚部y组成。这其实就是傅里叶频域!简单来说,它就是一个频率对应一个质心(复数)! 就是g(f)
这个函数
这个时候你可能就想,欸对,这个式子我理解了,表示这么个东西,然后呢?然后呢?是不是感觉明白了又没明白。我们上面只是从几何意义上理解了,下面我们将从傅里叶变换的作用出发,为什么这个表达式能分离出不同的频率的信号呢?
从作用出发,对公式进行分析(为什么它能分离出不同频率的信号):
大家注意到没,上面所讲,其实涉及到两个频率
⭐当复合原信号进行傅里叶变换时(就是把原信号进行上面那样的进行旋转缠绕,不同旋转缠绕f得到不同的质心坐标),假设这个复合原信号由a、b、c三个频率的正弦波复合而成,那么当旋转频率为a、b、c时,这时的质心复数的模会比其他任何旋转频率下对应的质心的模都要大!
由于这种规律的存在,我们只需要在频域图像中找到质心的模或者实部或者虚部处于峰值对应的那个频率f,是不是就找到了这个复合信号有哪些频率的信号组成!
傅里叶变换公式原理总结
旋转的矢量:一个复数,表示模长为1的矢量以频率为f进行逆时针旋转
e ( i ∗ 2 ∗ π ∗ f ∗ t ) e^{(i*2 * \pi *f * t)} e(i∗2∗π∗f∗t)
由于傅里叶变换表示的旋转矢量是从顺时针进行旋转,于是我们加个负号
e − ( 2 ∗ π ∗ i ∗ t ∗ f ) e^{-(2 * \pi * i * t * f)} e−(2∗π∗i∗t∗f)
为了表示我们的原始时域复合信号g(t)
以这样的频率进行旋转,我们将其相乘
g ( t ) × e ( 2 ∗ π ∗ i ∗ t ∗ f ) g(t)×e^{(2∗π∗i∗t∗f)} g(t)×e(2∗π∗i∗t∗f)
如果某频率信号的持续时间很长,那么旋转矢量的模长就会被放大
下面讲解一下为什么这样做能分解出频率信号,从正交性的角度分析,先看下面三个正余弦相乘的公式:
联想到之前我们说复数的三角形式:
不难理解这个公式:
g ( t ) × e ( − 2 ∗ π ∗ i ∗ t ∗ f ) g(t)×e^{(-2∗π∗i∗t∗f)} g(t)×e(−2∗π∗i∗t∗f)
因为只有当g(t)中含有f的信号时,基于正交性,才能得到非0值,这也是为什么旋转频率f选择对了,质心距离远点(其实就是表达式的值)会大一些!
将其理解为一个信号叠加的过程F(u)
是质心,把不同频率u
的信号进行累加,得到x对应的原信号
二维傅里叶变换是在一维基础之上的,我们一般使用二维傅里叶变换进行图像处理(图像信息就是二维离散信号),实现空间域到频域的转换,在频域进行一些操作从而对图像进行处理
一维信号&二维信号类比
下面对比的都是在转换成频域之前的那一套
由于下面这个图文讲的实在通俗易懂,我就不必重复造轮子,相信大家看完一定对二维傅里叶变换及其频谱图有一个很好的大体上理解
看完上面的描述,有一个大致理解,但是这之中的变换过程具体又是如何进行的,有点说不清道不明的感觉,但是能确定的是,频谱图的含义是什么
频域图中的一个(X,Y)点表示一张表示正弦波的灰度图像,该点亮度表示该灰度图像的正弦波的幅值,到中心的距离(Len=sqrt(XX+YY))表该灰度图像中该正弦波的频率,到中心点的方向(Dir=y/x)表示灰度图像中该正弦波的方向(注意是垂直正交的)。相位图中每一个点表示对应灰度图像中正弦波的相位。然后该频域图(相位图)所有(X,Y)点表示的正弦波灰度图像的叠加就得到了我们看到的灰度图像。
我们基于上面的讲解以及一维傅里叶变换,进行再深入分析和理解
为什么要进行频谱中心化?
以参考一维傅里叶变换。中心化之前的频谱从左到右是从0到采样频率Fs(Fs = ),假如N个点,则第n个点的频率f=(n-1)(n-1)/N。
由于Fs能采样的最大信号频率为Fs/2,由此右半部分和左半部分是对称的。一般频谱图只画出左半部分,称之为单边带。
由于左半部分和右半部分对称,因此可采集的最高信号频率为Fs/2,左边从0增加到Fs/2,右边从Fs/2下降到0。
傅立叶变换的相应操作
包括了:变换与逆变换,变换后得到频域上的图像的幅值、相位。
# 傅立叶变换 相应操作
# 得到频域上的图像,其幅值、相位
# 变换再逆变换得到原图
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('test_img/lena.jpg', 0) # 直接读为灰度图像
f = np.fft.fft2(img) # 将二维的离散数字图像进行傅里叶变换,变换后的频域图像存储在变量f中
fshift = np.fft.fftshift(f) # 对评语图像进行中心化操作,将频域远点移动到图像中心,方便后续处理
# 取绝对值.:将复数变化成实数
# 取对数的目的为了将数据变化到较小的范围(比如0-255)
c1 = np.log(np.abs(f)) # 频域后图像的振幅信息
c2 = np.log(np.abs(fshift)) # 中心化操作
ph_f = np.angle(f) # 图像上每个像素点对应的相位图
ph_fshift = np.angle(fshift) # 中心化操作
# 逆变换,获得原始图像
f0shift = np.fft.ifftshift(fshift) # 曲线频谱中心化,获得原始频谱
img_back = np.fft.ifft2(f0shift) # 基于二维图像的逆傅里叶变换
img_back = np.abs(img_back) # 出来的是复数,无法显示,于是取绝对值
# 逆变换方法2--取绝对值就是振幅
f1shift = np.fft.ifftshift(np.abs(fshift))
img_back1 = np.fft.ifft2(f1shift)
img_back1 = np.abs(img_back1)
img_back1 = (img_back1-np.amin(img_back1))/(np.amax(img_back1)-np.amin(img_back1))
# 逆变换方法3--取相位
f2shift = np.fft.ifftshift(np.angle(fshift))
img_back2 = np.fft.ifft2(f2shift)
# 出来的是复数,无法显示
img_back2 = np.abs(img_back2)
# 调整大小范围便于显示
img_back2 = (img_back2-np.amin(img_back2))/(np.amax(img_back2)-np.amin(img_back2))
# 逆变换方法4--两者合成
s1 = np.abs(fshift) # 取振幅
s1_angle = np.angle(fshift) # 取相位
s1_real = s1*np.cos(s1_angle) # 取实部
s1_imag = s1*np.sin(s1_angle) # 取虚部
s2 = np.zeros(img.shape, dtype=complex)
s2.real = np.array(s1_real) # 重新赋值s1给s2
s2.imag = np.array(s1_imag)
f3shift = np.fft.ifftshift(s2) # 对新的进行逆变换
img_back3 = np.fft.ifft2(f3shift)
# 出来的是复数,无法显示
img_back3 = np.abs(img_back3)
# 调整大小范围便于显示
img_back3 = (img_back3-np.amin(img_back3))/(np.amax(img_back3)-np.amin(img_back3))
plt.subplot(421), plt.imshow(img, 'gray'), plt.title('original')
plt.subplot(422), plt.imshow(c1, 'gray'), plt.title('Amplitude')
plt.subplot(423), plt.imshow(c2, 'gray'), plt.title('center')
plt.subplot(424), plt.imshow(ph_f, 'gray'), plt.title('phases')
plt.subplot(425), plt.imshow(img_back, 'gray'), plt.title('img_back')
plt.show()
频域上的低通滤波相当于平滑滤波处理。
这段代码主要实现了两种低通滤波器(ideal lowpass filter和gaussian lowpass filter)的图像处理。
下面给出了三种在频域上的低通滤波方法
lowPassFilter(image, d)
# ideal lowpass filter
# 定义ideal lowpass filter函数,接受图像image和截止频率d作为参数
def lowPassFilter(image, d):
f = np.fft.fft2(image) # 首先,对输入图像进行二维傅里叶变换
fshift = np.fft.fftshift(f) # 频谱图像中心化
"""
内部函数make_transform_matrix:操作在频率域上的低通滤波器
原理:创建一个变换矩阵,通过计算每个频率点与中心点的距离(这个距离就是频率),
将距离(频率)小于或等于频率阈值d的点置为1,其他点置为0
"""
def make_transform_matrix(d):
transfor_matrix = np.zeros(image.shape)
center_point = tuple(map(lambda x: (x - 1) / 2, image.shape))
for i in range(transfor_matrix.shape[0]):
for j in range(transfor_matrix.shape[1]):
def cal_distance(pa, pb):
from math import sqrt
dis = sqrt((pa[0] - pb[0]) ** 2 + (pa[1] - pb[1]) ** 2)
return dis
dis = cal_distance(center_point, (i, j))
if dis <= d:
transfor_matrix[i, j] = 1
else:
transfor_matrix[i, j] = 0
return transfor_matrix
# 在lowPassFilter内部调用make_transform_matrix函数,得到变换矩阵d_matrix
d_matrix = make_transform_matrix(d)
# 将频率域中心重新移动到原来位置,并且将其与傅里叶变换相乘(低通滤波),然后进行逆变换,获取低通滤波后的图像
new_img = np.abs(np.fft.ifft2(np.fft.ifftshift(fshift * d_matrix)))
return new_img
GaussianLowFilter(image, d)
# gaussian lowpass filter
def GaussianLowFilter(image, d):
f = np.fft.fft2(image)
fshift = np.fft.fftshift(f)
"""
内部函数make_transform_matrix:操作在频率域上的低通滤波器
原理:创建一个变换矩阵(使用高斯函数来表示),通过计算每个频率点与中心点的距离(这个距离就是频率),
将距离(频率)小于或等于频率阈值d的点置为1,其他点置为0
"""
def make_transform_matrix(d):
transfor_matrix = np.zeros(image.shape)
center_point = tuple(map(lambda x: (x-1)/2, image.shape))
for i in range(transfor_matrix.shape[0]):
for j in range(transfor_matrix.shape[1]):
def cal_distance(pa, pb):
from math import sqrt
dis = sqrt((pa[0]-pb[0])**2+(pa[1]-pb[1])**2)
return dis
dis = cal_distance(center_point, (i, j))
transfor_matrix[i, j] = np.exp(-(dis**2)/(2*(d**2)))
return transfor_matrix
d_matrix = make_transform_matrix(d)
new_img = np.abs(np.fft.ifft2(np.fft.ifftshift(fshift*d_matrix)))
return new_img
img_man = cv2.imread('test_img/lena.jpg', 0) # 直接读为灰度图像
plt.subplot(121), plt.imshow(img_man, 'gray'), plt.title('origial')
plt.xticks([]), plt.yticks([])
# --------------------------------
"""
计算图像的行数和列数,并创建一个与图像大小相同的全0矩阵mask
将mask中心位置周围范围像素值设置为1,以创建一个掩膜
"""
rows, cols = img_man.shape
mask = np.zeros(img_man.shape, np.uint8)
mask[int(rows/2-20):int(rows/2+20), int(cols/2-20):int(cols/2+20)] = 1
# --------------------------------
f1 = np.fft.fft2(img_man) # 对原始图像进行二维傅里叶变换
f1shift = np.fft.fftshift(f1) # 中心化
f1shift = f1shift*mask # !将中心化结构和掩膜mask相乘,实现低通滤波
f2shift = np.fft.ifftshift(f1shift) # 对新的进行逆变换
img_new = np.fft.ifft2(f2shift)
# 出来的是复数,无法显示
img_new = np.abs(img_new)
# 调整大小范围便于显示
img_new = (img_new-np.amin(img_new))/(np.amax(img_new)-np.amin(img_new))
plt.subplot(122), plt.imshow(img_new, 'gray'), plt.title('Lowpass')
plt.xticks([]), plt.yticks([])
plt.show()
频域上的高通滤波相当于边缘检测算子,提取边缘。
# 高通滤波
# 有利于提取图像的轮廓
import cv2
import numpy as np
import matplotlib.pyplot as plt
# ideal highpass filter
def highPassFilter(image, d): # D为阈值
f = np.fft.fft2(image)
fshift = np.fft.fftshift(f)
def make_transform_matrix(d):
transfor_matrix = np.zeros(image.shape)
center_point = tuple(map(lambda x: (x-1)/2, image.shape))
# 对频域的每个频率点进行遍历
for i in range(transfor_matrix.shape[0]):
for j in range(transfor_matrix.shape[1]):
def cal_distance(pa,pb):
from math import sqrt
dis = sqrt((pa[0]-pb[0])**2+(pa[1]-pb[1])**2)
return dis
dis = cal_distance(center_point, (i, j))
if dis <= d: # 距离小于d的频率成分置为1(高频)
transfor_matrix[i, j] = 0
else:
transfor_matrix[i, j] = 1
return transfor_matrix
d_matrix = make_transform_matrix(d)
# 将变换矩阵应用于频域结果,再进行逆傅里叶变换
new_img = np.abs(np.fft.ifft2(np.fft.ifftshift(fshift*d_matrix)))
return new_img
# gaussian highpass
def GaussianHighFilter(image, d):
f = np.fft.fft2(image)
fshift = np.fft.fftshift(f)
def make_transform_matrix(d):
transfor_matrix = np.zeros(image.shape)
center_point = tuple(map(lambda x: (x-1)/2, image.shape))
for i in range(transfor_matrix.shape[0]):
for j in range(transfor_matrix.shape[1]):
def cal_distance(pa, pb):
from math import sqrt
dis = sqrt((pa[0]-pb[0])**2+(pa[1]-pb[1])**2)
return dis
dis = cal_distance(center_point, (i, j))
# 在高斯滤波器种,距中心点的距离越大,变换矩阵的值越接近于1;距离越小,变换矩阵的值越接近于0
transfor_matrix[i, j] = 1-np.exp(-(dis**2)/(2*(d**2)))
return transfor_matrix
d_matrix = make_transform_matrix(d)
new_img = np.abs(np.fft.ifft2(np.fft.ifftshift(fshift*d_matrix)))
return new_img
img_man = cv2.imread('test_img/lena.jpg', 0) # 直接读为灰度图像
plt.subplot(221), plt.imshow(img_man, 'gray'), plt.title('origial')
plt.xticks([]), plt.yticks([])
# --------------------------------
# 生成一个和图像大小相同的掩膜
rows, cols = img_man.shape
mask = np.ones(img_man.shape, np.uint8)
# 掩膜将图像中心的一个正方形区域置为0,其余部分置为1
mask[int(rows/2-30):int(rows/2+30), int(cols/2-30):int(cols/2+30)] = 0
# --------------------------------
f1 = np.fft.fft2(img_man)
f1shift = np.fft.fftshift(f1)
# 将频域图与掩膜相乘
f1shift = f1shift*mask
f2shift = np.fft.ifftshift(f1shift) # 对新的进行逆变换
img_new = np.fft.ifft2(f2shift)
# 出来的是复数,无法显示
img_new = np.abs(img_new)
# 调整大小范围便于显示
img_new = (img_new-np.amin(img_new))/(np.amax(img_new)-np.amin(img_new))
out = GaussianHighFilter(img_man, 60)
out2 = highPassFilter(img_man, 60)
plt.subplot(222), plt.imshow(out2, 'gray'), plt.title('highPassFilter')
plt.subplot(223), plt.imshow(out, 'gray'), plt.title('GaussianHighFilter')
plt.subplot(224), plt.imshow(img_new, 'gray'), plt.title('mask_method')
plt.show()
带通滤波处于高通和低通之间,可指定频率的范围。
# 带通滤波器
import cv2
import numpy as np
import matplotlib.pyplot as plt
img_man = cv2.imread('test_img/lena.jpg', 0) # 直接读为灰度图像
plt.subplot(121), plt.imshow(img_man, 'gray'), plt.title('origial')
plt.xticks([]), plt.yticks([])
# --------------------------------
"""
定义两个掩膜矩阵mask1和mask2(和图像大小一致),用于在频域图上对图像进行带通滤波
掩膜的作用是通过将图像中心的一个区域设置为0或者1来选择保留频率成分
mask1:与原始图像具有相同形状的矩阵,初始值为1,将中心区域(16x16)置为0,抑制中心低频成分
mask2:将中心(160x160)置为0,其余保持为1,选择一定范围的高频
最后将mask1和mask2相乘,得到最终的带通掩膜
"""
rows, cols = img_man.shape
mask1 = np.ones(img_man.shape, np.uint8)
mask1[int(rows/2-8):int(rows/2+8), int(cols/2-8):int(cols/2+8)] = 0
mask2 = np.zeros(img_man.shape, np.uint8)
mask2[int(rows/2-80):int(rows/2+80), int(cols/2-80):int(cols/2+80)] = 1
mask = mask1*mask2
# --------------------------------
f1 = np.fft.fft2(img_man)
f1shift = np.fft.fftshift(f1)
f1shift = f1shift*mask
f2shift = np.fft.ifftshift(f1shift) # 对新的进行逆变换
img_new = np.fft.ifft2(f2shift)
# 出来的是复数,无法显示
img_new = np.abs(img_new)
# 调整大小范围便于显示
img_new = (img_new-np.amin(img_new))/(np.amax(img_new)-np.amin(img_new))
plt.subplot(122), plt.imshow(img_new, 'gray'), plt.title('result')
plt.xticks([]), plt.yticks([])
plt.show()
一种常见并应用广泛的频域上的滤波器。
在巴特沃斯滤波器中,阈值(d
)和阶数(n
)对滤波效果有重要影响。
阈值(d
):
阶数(n
):
因此,选择适当的阈值和阶数可以控制滤波器的频率选择性和抑制效果。较小的阈值和较小的阶数通常用于保留图像的整体特征和平滑图像,而较大的阈值和较大的阶数则用于强调图像的边缘和细节。具体的选择需要根据应用场景和期望的滤波效果进行调整。
# 巴特沃斯滤波器
import cv2
import numpy as np
import matplotlib.pyplot as plt
def butterworthPassFilter(image, d, n):
f = np.fft.fft2(image)
fshift = np.fft.fftshift(f)
def make_transform_matrix(d):
transfor_matrix = np.zeros(image.shape)
center_point = tuple(map(lambda x: (x - 1) / 2, image.shape))
for i in range(transfor_matrix.shape[0]):
for j in range(transfor_matrix.shape[1]):
def cal_distance(pa, pb):
from math import sqrt
dis = sqrt((pa[0] - pb[0]) ** 2 + (pa[1] - pb[1]) ** 2)
return dis
dis = cal_distance(center_point, (i, j))
# 使用巴特沃斯滤波器的公式
transfor_matrix[i, j] = 1 / ((1 + (d / dis)) ** n)
return transfor_matrix
d_matrix = make_transform_matrix(d)
new_img = np.abs(np.fft.ifft2(np.fft.ifftshift(fshift * d_matrix)))
return new_img
img = cv2.imread('test_img/lena.jpg', 0)
plt.subplot(231)
butter_100_1 = butterworthPassFilter(img, 100, 1)
plt.imshow(butter_100_1, cmap="gray")
plt.title("d=100,n=1")
plt.axis("off")
plt.subplot(232)
butter_100_2 = butterworthPassFilter(img, 100, 2)
plt.imshow(butter_100_2, cmap="gray")
plt.title("d=100,n=2")
plt.axis("off")
plt.subplot(233)
butter_100_3 = butterworthPassFilter(img, 100, 3)
plt.imshow(butter_100_3, cmap="gray")
plt.title("d=100,n=3")
plt.axis("off")
plt.subplot(234)
butter_100_1 = butterworthPassFilter(img, 30, 1)
plt.imshow(butter_100_1, cmap="gray")
plt.title("d=30,n=1")
plt.axis("off")
plt.subplot(235)
butter_100_2 = butterworthPassFilter(img, 30, 2)
plt.imshow(butter_100_2, cmap="gray")
plt.title("d=30,n=2")
plt.axis("off")
plt.subplot(236)
butter_100_3 = butterworthPassFilter(img, 30, 3)
plt.imshow(butter_100_3, cmap="gray")
plt.title("d=30,n=3")
plt.axis("off")
plt.show()
参考: