当前位置: 首页 > news >正文

傅里叶变换和其图像处理中的应用

以下部分文字资料整合于网络,本文仅供自己学习用!

一、为什么要在频域进行图像处理?

一些在空间域表述困难的增强任务,在频率域中变得非常普通

滤波在频率域更为直观,你想想嘛,所谓滤波,就是把二维图像信号种某个频率信号给去掉。如果定义域是频率,值域是该频率信号的强度(这个坐标体系其实就是频域),直接把对应频率的强度弄为0不就行了,省的还去做卷积(联想空间域的滤波器!是不是得沿着图像走一遭。频域直接啪的一下把这个频率对应的图像信号置为0 ,多简单!)

二、理论知识:傅里叶变换

2.1:先从欧拉公式说起

🍊请记住,我们下面在做的目的都是在理解的基础上,说明下面这个,也就是欧拉公式,表示的是以以频率为f的在复平面上的一种圆周旋转,这对我们后面利用这点理解傅里叶变换至关重要

e i w t / e i 2 Π f e^{iwt}/e^{i2Πf} eiwt/eif

2.1.1:e的起源——复利问题

💸问题背景:
“李华有1单位的本金,年利率为r,银行一年结算n次,则一年后李华得到:”
在这里插入图片描述

我们发现,当r=1,即年利率为100%时,这个极限刚好是e。关键在于,当n趋于无穷时,每期的收益1/n也在减小,最后二者抵消,恰好收敛到了一个确定的值——e,
在这里插入图片描述
当原本要变换r时,我们把这个变换细分成n份,即每一次都变化r的1/n,那么最后得到:

在这里插入图片描述

2.1.2:复数乘法的意义

在继续深入之前,我们必须要理解复数乘法的意义。

🍊复数一共有四种表示形式,这里我们讲的集合意义是从极坐标形式出发的,而推出复数乘法的几何意义,又是从代数形式和三角形式推出的:
在这里插入图片描述
💐推导过程:
在这里插入图片描述

🍊复数的几何意义:
在这里插入图片描述
首先复数本身的极坐标的几何意义如上,通过推导过程可知,两个复数相乘,相当于:模长相乘,幅角相加

在这里插入图片描述

2.1.3:欧拉公式

在回到这个公式,再思考一下它的意义:初始值为1(n=0),每次变化(在原有基础上)r/n,总的变换是r,这里可能不是很哈理解,需要结合复利来理解
在这里插入图片描述
当我们把r换做,如下图
在这里插入图片描述
右边变成了什么?对,复数的乘积!这里带了个n,可能不是很好理解,我们先暂时把n具体花为4,来帮助我们理解:
在这里插入图片描述
上图可以看到,两个规律:1、每次变换的是角度的累加和模长相乘,角度总变化是Π。2、当n趋向于∞,此时左侧不断收拢。如下图所示
在这里插入图片描述
在这里插入图片描述
至此,我们达到了我们的目的:对下面公式的理解为:一个在复平面以w为角速度或f为角速度,模长为1旋转的矢量!这对于我们从旋转矢量的角度进行傅里叶变换的理解至关重要的。
在这里插入图片描述
同时也不要忘记它的三角形式
在这里插入图片描述
⭐还有一点要记住的是,e前面的系数,表示的是这个旋转矢量的模长,对应到三角形式,也就是幅值!(这也是为什么后来求解赋值时,需要对复数形式的信号求绝对值!)

2.2:一维傅里叶变换

这里我们先做好公式中定义,方面后面对公式理解的讲解:

  • f(x):表示在时域上的原信号,定义域是时间x,值域是信号强度
  • F(u):频域表示,u表示的是频率,当u取不同值时,它是一个复数!

接下来的2.2.12.2.2将对一维的傅里叶公式进行理解和分析!(这些小节中对函数符号用法可能不一致,但是不要紧,清楚具体含义即可!)
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

傅里叶变换的本质就是将时域的非周期信号,转换为在频域的非周期信号

在这里插入图片描述

2.2.1:从旋转向量的角度理解

这是下面讲解的定义:
在这里插入图片描述

🍊从几何意义理解公式的本质(它要表达什么?):

首先,通过对指数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(i2πft)
    由于傅里叶变换表示的旋转矢量是从顺时针进行旋转,于是我们加个负号
    e − ( 2 ∗ π ∗ i ∗ t ∗ f ) e^{-(2 * \pi * i * t * f)} e(2πitf)

  • 为了表示我们的原始时域复合信号g(t)以这样的频率进行旋转,我们将其相乘

g ( t ) × e ( 2 ∗ π ∗ i ∗ t ∗ f ) g(t)×e^{(2∗π∗i∗t∗f)} g(t)×e(2πitf)

  • 计算对应旋转频率f下的质心坐标,我们可以进行一个估计,在波形图上取n个点,进行坐标相加然后除n取平均,n越大,坐标越精确。基于这种思想,我们对时间进行积分
    在这里插入图片描述
    在这里插入图片描述

如果某频率信号的持续时间很长,那么旋转矢量的模长就会被放大

2.2.2:进一步从正交性的角度理解

下面讲解一下为什么这样做能分解出频率信号,从正交性的角度分析,先看下面三个正余弦相乘的公式:
在这里插入图片描述
联想到之前我们说复数的三角形式:
在这里插入图片描述
不难理解这个公式:

g ( t ) × e ( − 2 ∗ π ∗ i ∗ t ∗ f ) g(t)×e^{(-2∗π∗i∗t∗f)} g(t)×e(2πitf)

因为只有当g(t)中含有f的信号时,基于正交性,才能得到非0值,这也是为什么旋转频率f选择对了,质心距离远点(其实就是表达式的值)会大一些!

2.2.3:一维傅里叶反变换的理解

在这里插入图片描述
将其理解为一个信号叠加的过程F(u)是质心,把不同频率u的信号进行累加,得到x对应的原信号

2.3:二维傅里叶变换

二维傅里叶变换是在一维基础之上的,我们一般使用二维傅里叶变换进行图像处理(图像信息就是二维离散信号),实现空间域到频域的转换,在频域进行一些操作从而对图像进行处理

🌺一维信号&二维信号类比

下面对比的都是在转换成频域之前的那一套

  • 定义域
    • 一维:时域(横坐标是时间)
    • 二维:空间域(二维坐标表示的是信号所在位置)
  • 值域(信号强度
    • 一维就是时域的那个纵坐标值,一般就是声音信号的强度。
    • 二维也就是灰度值呗

这里先抛出公式:在这里插入图片描述

2.3.1:大致上理解

由于下面这个图文讲的实在通俗易懂,我就不必重复造轮子,相信大家看完一定对二维傅里叶变换及其频谱图有一个很好的大体上理解
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3.2:深入分析:

看完上面的描述,有一个大致理解,但是这之中的变换过程具体又是如何进行的,有点说不清道不明的感觉,但是能确定的是,频谱图的含义是什么
频域图中的一个(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。

在这里插入图片描述

三、傅里叶变换在图像处理中的应用

3.1傅立叶变换基本操作

傅立叶变换的相应操作
包括了:变换与逆变换,变换后得到频域上的图像的幅值、相位。

# 傅立叶变换 相应操作
# 得到频域上的图像,其幅值、相位
# 变换再逆变换得到原图import cv2
import numpy as np
import matplotlib.pyplot as pltimg = 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()

在这里插入图片描述

3.2:低通滤波

频域上的低通滤波相当于平滑滤波处理。
这段代码主要实现了两种低通滤波器(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 sqrtdis = sqrt((pa[0] - pb[0]) ** 2 + (pa[1] - pb[1]) ** 2)return disdis = cal_distance(center_point, (i, j))if dis <= d:transfor_matrix[i, j] = 1else:transfor_matrix[i, j] = 0return transfor_matrix# 在lowPassFilter内部调用make_transform_matrix函数,得到变换矩阵d_matrixd_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 sqrtdis = sqrt((pa[0]-pb[0])**2+(pa[1]-pb[1])**2)return disdis = cal_distance(center_point, (i, j))transfor_matrix[i, j] = np.exp(-(dis**2)/(2*(d**2)))return transfor_matrixd_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()

在这里插入图片描述

3.3:高通滤波

频域上的高通滤波相当于边缘检测算子,提取边缘。

# 高通滤波
# 有利于提取图像的轮廓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 sqrtdis = sqrt((pa[0]-pb[0])**2+(pa[1]-pb[1])**2)return disdis = cal_distance(center_point, (i, j))if dis <= d: # 距离小于d的频率成分置为1(高频)transfor_matrix[i, j] = 0else:transfor_matrix[i, j] = 1return transfor_matrixd_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 sqrtdis = sqrt((pa[0]-pb[0])**2+(pa[1]-pb[1])**2)return disdis = cal_distance(center_point, (i, j))# 在高斯滤波器种,距中心点的距离越大,变换矩阵的值越接近于1;距离越小,变换矩阵的值越接近于0transfor_matrix[i, j] = 1-np.exp(-(dis**2)/(2*(d**2)))return transfor_matrixd_matrix = make_transform_matrix(d)new_img = np.abs(np.fft.ifft2(np.fft.ifftshift(fshift*d_matrix)))return new_imgimg_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()

在这里插入图片描述

3.4:带通滤波

带通滤波处于高通和低通之间,可指定频率的范围。

# 带通滤波器import cv2
import numpy as np
import matplotlib.pyplot as pltimg_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()

在这里插入图片描述

3.5:巴特沃兹滤波

一种常见并应用广泛的频域上的滤波器。
在巴特沃斯滤波器中,阈值(d)和阶数(n)对滤波效果有重要影响。

  1. 阈值(d):

    • 较小的阈值会保留更多低频成分,对应于图像中的平滑部分。因此,较小的阈值会导致图像较模糊的滤波效果。
    • 较大的阈值会抑制低频成分,对应于图像中的细节部分。因此,较大的阈值会导致图像较清晰但可能失去细节的滤波效果。
    • 不同的阈值选择会在频域上选择不同的频率范围,从而影响图像的频率特性。
  2. 阶数(n):

    • 较小的阶数会产生较为平缓的滤波曲线,对应于相对平滑的滤波效果。这意味着低频和高频成分的抑制相对均匀。
    • 较大的阶数会产生较为陡峭的滤波曲线,对应于较强的抑制效果。这意味着滤波器对低频和高频成分的抑制会更加明显。
    • 阶数越高,滤波曲线的变化越快,滤波器通过选择性地抑制频率成分来实现更精细的滤波效果。

因此,选择适当的阈值和阶数可以控制滤波器的频率选择性和抑制效果。较小的阈值和较小的阶数通常用于保留图像的整体特征和平滑图像,而较大的阈值和较大的阶数则用于强调图像的边缘和细节。具体的选择需要根据应用场景和期望的滤波效果进行调整。

# 巴特沃斯滤波器import cv2
import numpy as np
import matplotlib.pyplot as pltdef 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 sqrtdis = sqrt((pa[0] - pb[0]) ** 2 + (pa[1] - pb[1]) ** 2)return disdis = cal_distance(center_point, (i, j))# 使用巴特沃斯滤波器的公式transfor_matrix[i, j] = 1 / ((1 + (d / dis)) ** n)return transfor_matrixd_matrix = make_transform_matrix(d)new_img = np.abs(np.fft.ifft2(np.fft.ifftshift(fshift * d_matrix)))return new_imgimg = 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()

在这里插入图片描述


参考:

  • 傅里叶分析之掐死教程(完整版)
  • 图像预处理之傅里叶变换与频谱滤波
  • 图像处理(5)–图像的傅里叶变换
  • 通俗讲解:图像傅里叶变换

相关文章:

傅里叶变换和其图像处理中的应用

以下部分文字资料整合于网络&#xff0c;本文仅供自己学习用&#xff01; 一、为什么要在频域进行图像处理&#xff1f; 一些在空间域表述困难的增强任务&#xff0c;在频率域中变得非常普通 滤波在频率域更为直观&#xff0c;你想想嘛&#xff0c;所谓滤波&#xff0c;就是…...

风电厂VR情景式三维模拟实训系统让教育更加贴近实际

VR虚拟仿真实训室是一种利用虚拟现实技术和仿真技术&#xff0c;为教学和学生实训提供虚拟环境和实践机会的设施。它通过将学生置身于虚拟场景中&#xff0c;让他们可以身临其境地参与到实际场景中&#xff0c;从而提高实际操作能力和技能水平。 在教学方面&#xff0c;VR虚拟仿…...

竞赛 深度学习+python+opencv实现动物识别 - 图像识别

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 inception_v3网络5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…...

LeetCode 2562. 找出数组的串联值:模拟(双指针)

【LetMeFly】2562.找出数组的串联值&#xff1a;模拟&#xff08;双指针&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/find-the-array-concatenation-value/ 给你一个下标从 0 开始的整数数组 nums 。 现定义两个数字的 串联 是由这两个数值串联起来…...

【mac】常用命令01

1、如何像windows一样看磁盘&#xff1f; 不断的在上层文件夹显示&#xff0c;找到最上层&#xff0c;拖拽到左侧&#xff0c;方便之后找 2、Macintosh HD显示隐藏文件夹方法 终端窗口&#xff1a; defaults write com.apple.finder AppleShowAllFiles true killall Finder 或者…...

android 13.0 添加系统字体并且设置为默认字体

1.概述 在13.0系统定制化开发中,在产品定制中,有产品需求对于系统字体风格不太满意,所以想要更换系统的默认字体,对于系统字体的修改也是常有的功能,而系统默认也支持增加字体,所以就来添加楷体字体为系统字体,并替换为系统默认字体, 接下来就来分析下替换默认字体的方…...

平面设计师要会3D吗 优漫动游

所谓物以稀为贵&#xff0c;对于设计人才也一样。越是稀缺的能力&#xff0c;能够发挥的价值就越高。于是现在很多互联网名企在招聘的时候都加了一条&#xff1a;会3D设计者优先。 其实这些看起来很厉害的设计&#xff0c;归结起来无非就三种形式&#xff1a;插画、三维、合成…...

【LeetCode热题100】--105.从前序与中序遍历序列构造二叉树

105.从前序与中序遍历序列构造二叉树 二叉树前序遍历顺序&#xff1a;根左右 二叉树中序遍历顺序&#xff1a;左根右 只要我们在中序遍历中定位到根节点&#xff0c;那么我们就可以分别知道左子树和右子树中的节点数目。由于同一颗子树的前序遍历和中序遍历的长度显然是相同的…...

缓存设计的创新之旅:架构的灵魂之一

缓存在架构设计中占有重要地位。缓存在提升性能中也扮演重要的角色。常见的有对资源的缓存&#xff0c;比如数据库连接池、http连接池&#xff0c;还有对数据的缓存等。缓存的设计可复杂也可简单&#xff0c;但是需要考虑的点却很多。 缓存对象 设计缓存的时候一定要考虑的是&…...

Unnatural Instructions: Tuning Language Models with (Almost) No Human Labor

本文是LLM系列文章&#xff0c;针对《Unnatural Instructions: Tuning Language Models with (Almost) No Human Labor》的翻译。 TOC 摘要 指令调优使预训练的语言模型能够从推理时间的自然语言描述中执行新的任务。这些方法依赖于以众包数据集或用户交互形式进行的大量人工…...

uniapp中全局页面挂载组件(H5)

前言 我们已经学习了 uniapp中全局页面挂载组件&#xff08;小程序&#xff09; 有些小伙伴问在H5怎么做那让我们试一试 直接上代码 //引用组件 import dialog from ./index.vue; //我这里要把小程序的方法和h5方法写一起所以用了混入 import mixins from ./mixins.js //使用…...

设计模式(1)-设计模式前置基础知识

1&#xff0c;设计模式概述 1.1 软件设计模式的产生背景 "设计模式"最初并不是出现在软件设计中&#xff0c;而是被用于建筑领域的设计中。 1977年美国著名建筑大师、加利福尼亚大学伯克利分校环境结构中心主任克里斯托夫亚历山大&#xff08;Christopher Alexand…...

【05】基础知识:React组件实例三大核心属性 - props

一、props 了解 理解 1、每个组件对象都会有 props&#xff08;properties的简写&#xff09;属性 2、组件标签的所有属性都保存在 props 中 作用 通过标签属性从组件外向组件内传递变化的数据 注意 组件内部不要修改 props 数据 二、案例 需求&#xff1a;自定义用来…...

JOSEF约瑟 漏电继电器 JD1-200 工作电压:380V 孔径:45mm 50~500mA

JD1系列漏电继电器 系列型号 JD1-100漏电继电器 JD1-200漏电继电器 JD1-250漏电继电器 JD1系列漏电继电器原为分体式固定式安装&#xff0c;为适应现行安装场合需要&#xff0c;上海约瑟继电器厂在产品原JD1一体式漏电继电器基础上进行产品升级&#xff0c;开发出现在较为…...

[题] 差分矩阵 #差分

题目 差分矩阵 题解 只有一个操作&#xff1a; void insert(int x1, int y1, int x2, int y2, int c){b[x1][y1] c;b[x2 1][y1] - c;b[x1][y2 1] - c;b[x2 1][y2 1] c; }利用差分的思想&#xff0c;扩展到二维上。 insert函数作用是将矩阵之内的数全部加上c&#xff0c;…...

Studio One6.5最新版本新增了对Linux的支持

音乐制作人们&#xff0c;这是你们翘首以待的消息。数字音频工作站&#xff08;DAW&#xff09;已经成为音乐制作专业人士重要工具之一。 遗憾的是&#xff0c;对于 Linux 用户而言&#xff0c;选择十分有限。最受欢迎的选择通常是开源 DAW&#xff0c;如 Ardour、Audacity和闭…...

大模型引发“暴力计算”,巨头加速推进液冷“降温”

点击关注 文&#xff5c;姚悦 编&#xff5c;王一粟 一进入部署了液冷服务器的数据中心&#xff0c;不仅没有嘈杂的风扇声&#xff0c;甚至在不开空调的夏日也完全没有闷热感。 在大模型引发“暴力计算”的热潮下&#xff0c;数据中心的上下游&#xff0c;正在加紧推进液冷“…...

git log 美化配置

编辑 vim ~/.gitconfig 添加配置 [alias]lg log --graph --abbrev-commit --decorate --dateformat:%m-%d %H:%M:%S --formatformat:%C(bold blue)%h%C(reset) - %s %C(bold yellow)% d%C(reset) %n %C(dim white) (%ad) - %an%C(reset) --allgit lg 效果...

Spark 的主要组件及任务分工

Spark 是一个开源的分布式计算框架&#xff0c;旨在处理大规模数据集的快速计算和分析。下面是 Spark 的主要组件及其任务分工的详细介绍&#xff1a; Driver&#xff08;驱动器&#xff09;&#xff1a;【任务调度】 负责整个 Spark 应用程序的执行和协调。解析用户程序&#…...

Apache Spark 中的 RDD是什么

目录 RDD容错性 RDD进行迭代计算 RDD是Resilient Distributed Dataset的缩写&#xff0c;是Apache Spark中的一个关键概念。RDD是一种分布式的内存抽象&#xff0c;用于将数据划分为不同的片段以进行并行计算。RDD是一个只读的数据集&#xff0c;可以分布在集群的不同节点上&…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...