【python】OpenCV—Fourier Transform

embedded/2024/10/17 13:10:10/

在这里插入图片描述

文章目录

  • 1、傅里叶变换
    • 1.1、振幅信息——abs
    • 1.2、相位信息——angle
  • 2、傅里叶逆变换
    • 2.1、仅包含振幅或者仅包含相位 vs 振幅相位均包含
    • 2.2、两张图片振幅和相位互换
  • 3、滤波
    • 3.1、高通滤波
    • 3.2、低通滤波
    • 3.3、带通滤波
  • 4、涉及到的库函数
    • 4.1、numpy.fft.fft2
    • 4.2、numpy.fft.fftshift
  • 5、参考

比如实现图像的轮廓提取,在空间域滤波中我们使用一个拉普拉斯模板就可以提取,而在频域内,我们使用一个高通滤波模板(因为轮廓在频域内属于高频信号),可以实现轮廓的提取,后面也会把拉普拉斯模板频域化,会发现拉普拉斯其实在频域来讲就是一个高通滤波器

对图像进行傅里叶变换后其实是可以得到图像的振幅图与相位图的,而想把图像从频域空间恢复到时域空间,必须要同时有图像的振幅图与相位图才可以,缺少一个就恢复的不完整

1、傅里叶变换

import cv2
import numpy as np
import matplotlib.pyplot as pltimg = cv2.imread('1.jpg', 0)  # 直接读为灰度图像f = np.fft.fft2(img)  # 傅里叶变换fshift = np.fft.fftshift(f)  # 移位变换系数,使得直流分量在中间# 取绝对值:将复数变化成实数
# 取对数的目的为了将数据变化到较小的范围(比如0-255)
s1 = np.log(np.abs(f))
s2 = np.log(np.abs(fshift))plt.subplot(131), plt.imshow(img, 'gray'), plt.axis("off"), plt.title('img')
plt.subplot(132), plt.imshow(s1, 'gray'), plt.axis("off"), plt.title('original')
plt.subplot(133), plt.imshow(s2,'gray'), plt.axis("off"), plt.title('center')plt.show()

f 与 fshift 都是复数

输入图片

在这里插入图片描述

输出结果

在这里插入图片描述

1.1、振幅信息——abs

import cv2
import numpy as np
import matplotlib.pyplot as pltimg = cv2.imread('1.jpg', 0)  # 直接读为灰度图像
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)ph_f = np.abs(f)
ph_fshift = np.abs(fshift)plt.subplot(131), plt.imshow(img, 'gray'), plt.axis("off"), plt.title('Original')
plt.subplot(132), plt.imshow(ph_f, 'gray'), plt.axis("off"), plt.title('Amplitude')
plt.subplot(133), plt.imshow(ph_fshift, 'gray'), plt.axis("off"), plt.title('Center Amplitude')
plt.show()

在这里插入图片描述

1.2、相位信息——angle

numpy包中自带一个 angle 函数可以直接根据复数的实部与虚部求出角度(默认出来的角度是弧度)。

import cv2
import numpy as np
import matplotlib.pyplot as pltimg = cv2.imread('1.jpg', 0)  # 直接读为灰度图像
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)ph_f = np.angle(f)
ph_fshift = np.angle(fshift)plt.subplot(121), plt.imshow(ph_f, 'gray'), plt.axis("off"), plt.title('original')
plt.subplot(122), plt.imshow(ph_fshift, 'gray'), plt.axis("off"), plt.title('center')
plt.show()

在这里插入图片描述

2、傅里叶逆变换

恢复一个频域图像需要图像的振幅以及相位,而一个复数也正好包含这些,振幅就是实部虚部的平方和开方,相位就是 a r c t a n 实部 虚部 arctan \frac{实部}{虚部} arctan虚部实部

import cv2
import numpy as np
import matplotlib.pyplot as pltdef calc_snr(img, axis=0, ddof=0): # 计算信噪比a = np.asanyarray(img)m = a.mean(axis)sd = a.std(axis=axis, ddof=ddof)return np.where(sd == 0, 0, m / sd)img = cv2.imread('1.jpg', 0) # 直接读为灰度图像
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)snr = calc_snr(img, axis=None)
print('SNR for the original image is ' + str(snr))# 取绝对值:将复数变化成实数
# 取对数的目的为了将数据变化到0-255
s1 = np.log(np.abs(fshift))
plt.subplot(131), plt.imshow(img, 'gray'), plt.axis("off"), plt.title('original')
plt.subplot(132), plt.imshow(s1, 'gray'), plt.axis("off"), plt.title('center')# 逆变换
f1shift = np.fft.ifftshift(fshift)
img_back = np.fft.ifft2(f1shift)# 出来的是复数,无法显示
img_back = np.abs(img_back)
plt.subplot(133), plt.imshow(img_back,'gray'), plt.axis("off"), plt.title('img back')
plt.show()snr = calc_snr(img_back, axis=None)
print('SNR for the image obtained after fft reconstruction is ' + str(snr))

在这里插入图片描述

SNR for the original image is 1.3192715781603694
SNR for the image obtained after fft reconstruction is 1.3192715781603694

可以看到傅里叶逆变换恢复出来的图像和原始图像一模一样,统计出来的信噪比也一模一样

2.1、仅包含振幅或者仅包含相位 vs 振幅相位均包含

import cv2
import numpy as np
import matplotlib.pyplot as pltdef calc_snr(img, axis=0, ddof=0): # 计算信噪比a = np.asanyarray(img)m = a.mean(axis)sd = a.std(axis=axis, ddof=ddof)return np.where(sd == 0, 0, m / sd)img = cv2.imread('1.jpg',0) #直接读为灰度图像snr = calc_snr(img, axis=None)
print('SNR for the image obtained after fft reconstruction is ' + str(snr))f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)plt.subplot(221), plt.imshow(img, 'gray'), plt.title('original')
plt.xticks([]), plt.yticks([])#---------------------------------------------
# 逆变换--取绝对值就是振幅 abs
#---------------------------------------------f1shift = np.fft.ifftshift(np.abs(fshift))
img_back = np.fft.ifft2(f1shift)# 出来的是复数,无法显示
img_back = np.abs(img_back)# 调整大小范围便于显示
img_back = (img_back-np.amin(img_back))/(np.amax(img_back)-np.amin(img_back))
plt.subplot(222), plt.imshow(img_back, 'gray'), plt.title('only Amplitude')
plt.xticks([]), plt.yticks([])#---------------------------------------------
# 逆变换--取相位 angle
#---------------------------------------------f2shift = np.fft.ifftshift(np.angle(fshift))
img_back = np.fft.ifft2(f2shift)# 出来的是复数,无法显示
img_back = np.abs(img_back)# 调整大小范围便于显示
img_back = (img_back-np.amin(img_back))/(np.amax(img_back)-np.amin(img_back))
plt.subplot(223), plt.imshow(img_back, 'gray'), plt.title('only phase')
plt.xticks([]), plt.yticks([])#---------------------------------------------
# 逆变换--将两者合成看看
#---------------------------------------------
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)  # 重新赋值给s2dd
s2.imag = np.array(s1_imag)f2shift = np.fft.ifftshift(s2)  # 对新的进行逆变换
img_back = np.fft.ifft2(f2shift)# 出来的是复数,无法显示
img_back = np.abs(img_back)# 调整大小范围便于显示
img_back = (img_back-np.amin(img_back))/(np.amax(img_back)-np.amin(img_back))
plt.subplot(224), plt.imshow(img_back, 'gray'), plt.title('another way')
plt.xticks([]), plt.yticks([])
plt.show()snr = calc_snr(img_back, axis=None)
print('SNR for the image obtained after fft reconstruction is ' + str(snr))

在这里插入图片描述

SNR for the image obtained after fft reconstruction is 1.3192715781603694
SNR for the image obtained after fft reconstruction is 1.3192715781603694

仅仅振幅的恢复图啥也不是,仅仅的相位图还有那么点意思。最后是把振幅与相位分别作为频域内复数的实部和虚部,得到的恢复图才与原来的一样。

2.2、两张图片振幅和相位互换

import cv2
import numpy as np
import matplotlib.pyplot as pltimg1 = cv2.imread('1.jpg', 0)  # 直接读为灰度图像img2 = cv2.imread('2.jpg', 0)  # 直接读为灰度图像plt.subplot(221), plt.imshow(img1, 'gray'), plt.title('origial1'), plt.xticks([]), plt.yticks([])
plt.subplot(222), plt.imshow(img2, 'gray'), plt.title('origial_2'), plt.xticks([]), plt.yticks([])# --------------------------------f1 = np.fft.fft2(img1)
f1shift = np.fft.fftshift(f1)
f1_A = np.abs(f1shift)  # 取振幅
f1_P = np.angle(f1shift)  # 取相位# --------------------------------
f2 = np.fft.fft2(img2)
f2shift = np.fft.fftshift(f2)
f2_A = np.abs(f2shift)  # 取振幅
f2_P = np.angle(f2shift)  # 取相位# ---图1的振幅--图2的相位--------------------
img_new1_f = np.zeros(img1.shape, dtype=complex)
img1_real = f1_A * np.cos(f2_P)  # 取实部
img1_imag = f1_A * np.sin(f2_P)  # 取虚部
img_new1_f.real = np.array(img1_real)
img_new1_f.imag = np.array(img1_imag)
f3shift = np.fft.ifftshift(img_new1_f)  # 对新的进行逆变换
img_new1 = np.fft.ifft2(f3shift)# 出来的是复数,无法显示
img_new1 = np.abs(img_new1)# 调整大小范围便于显示
img_new1 = (img_new1 - np.amin(img_new1)) / (np.amax(img_new1) - np.amin(img_new1))
plt.subplot(223), plt.imshow(img_new1, 'gray'), plt.title('abs + angle'), plt.xticks([]), plt.yticks([])# ---图2的振幅--图1的相位--------------------
img_new2_f = np.zeros(img1.shape, dtype=complex)
img2_real = f2_A * np.cos(f1_P)  # 取实部
img2_imag = f2_A * np.sin(f1_P)  # 取虚部
img_new2_f.real = np.array(img2_real)
img_new2_f.imag = np.array(img2_imag)
f4shift = np.fft.ifftshift(img_new2_f)  # 对新的进行逆变换
img_new2 = np.fft.ifft2(f4shift)# 出来的是复数,无法显示
img_new2 = np.abs(img_new2)# 调整大小范围便于显示
img_new2 = (img_new2 - np.amin(img_new2)) / (np.amax(img_new2) - np.amin(img_new2))
plt.subplot(224), plt.imshow(img_new2, 'gray'), plt.title('angle + abs'), plt.xticks([]), plt.yticks([])
plt.show()

输入的另外一张图片

在这里插入图片描述

在这里插入图片描述

第三张图是第一张图的振幅和第二张图的相位融合

第四张图是第一张的相位和第二张图的振幅融合

可以看到相位才是灵魂

可以理解振幅不过描述图像灰度的亮度,占用谁的振幅不过使得结果哪些部分偏亮或者暗而已,而图像是个什么样子是由它的相位决定的。相位描述的是一个方向,方向正确了,那么最终的结果离你的目的就不远了

3、滤波

3.1、高通滤波

import cv2
import numpy as np
import matplotlib.pyplot as pltimg1 = cv2.imread('1.jpg',0) #直接读为灰度图像
plt.subplot(131),plt.imshow(img1,'gray'),plt.title('origial'), plt.xticks([]),plt.yticks([])#--------------------------------
rows, cols = img1.shape
mask = np.ones(img1.shape,np.uint8)
mask[rows//2-30:rows//2+30, cols//2-30:cols//2+30] = 0
plt.subplot(132), plt.imshow(mask,'gray'), plt.title('mask'), plt.xticks([]), plt.yticks([])#--------------------------------
f1 = np.fft.fft2(img1)
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(133),plt.imshow(img_new,'gray'),plt.title('Highpass'), plt.xticks([]),plt.yticks([])
plt.show()

在这里插入图片描述

3.2、低通滤波

import cv2
import numpy as np
import matplotlib.pyplot as pltimg1 = cv2.imread('1.jpg',0) #直接读为灰度图像
plt.subplot(131),plt.imshow(img1,'gray'),plt.title('origial'), plt.xticks([]),plt.yticks([])
#--------------------------------
rows,cols = img1.shape
mask = np.zeros(img1.shape,np.uint8)
mask[rows//2-20:rows//2+20,cols//2-20:cols//2+20] = 1
plt.subplot(132),plt.imshow(mask,'gray'),plt.title('Mask'), plt.xticks([]),plt.yticks([])
#--------------------------------
f1 = np.fft.fft2(img1)
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(133),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 pltimg1 = cv2.imread('1.jpg',0) #直接读为灰度图像
plt.subplot(131),plt.imshow(img1,'gray'),plt.title('origial'), plt.xticks([]),plt.yticks([])
#--------------------------------
rows,cols = img1.shape
mask1 = np.ones(img1.shape,np.uint8)
mask1[rows//2-8:rows//2+8,cols//2-8:cols//2+8] = 0
mask2 = np.zeros(img1.shape,np.uint8)
mask2[rows//2-80:rows//2+80,cols//2-80:cols//2+80] = 1
mask = mask1*mask2
plt.subplot(132),plt.imshow(mask,'gray'),plt.title('Mask'), plt.xticks([]),plt.yticks([])
#--------------------------------
f1 = np.fft.fft2(img1)
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(133),plt.imshow(img_new,'gray'),plt.title('Lowpass'), plt.xticks([]),plt.yticks([])
plt.show()

在这里插入图片描述

4、涉及到的库函数

4.1、numpy.fft.fft2

numpy.fft.fft2 是 NumPy 库中的一个函数,用于计算二维离散傅里叶变换(2D Discrete Fourier Transform, DFT)。以下是该函数的中文文档:

一、函数签名

python">numpy.fft.fft2(a, s=None, axes=(-2, -1), norm=None)

二、参数

  • a:array_like 类型,输入数组。这应该是一个具有复数或实数的二维数组。
  • s:可选参数,整数或整数元组。指定输出形状(每个转换轴的长度)。如果给定一个整数,则输入数组的每个维度都会被扩展到这个尺寸。如果给定一个2-tuple,则输入数组的每个维度都会被缩放到这个尺寸。
    • s[0] 指第一个轴(索引为 0 的轴),s[1] 指第二个轴(索引为 1 的轴)等。
    • 如果给定的形状小于输入的形状,则将剪切输入。
    • 如果它更大,输入将用零填充。
    • 如果未给定,则使用输入沿指定轴的形状 axes。
  • axes:可选参数,整数或整数元组。指定进行傅里叶变换的轴。
    • 如果未给出,则使用最后两个轴(即默认进行二维FFT)。
    • 重复索引 axes 表示在该轴上执行多次变换。
    • 一个元素序列意味着执行一维FFT。
  • norm:可选参数,字符串或 None。指定在傅里叶变换中使用的归一化方式。
    • ‘backward’:向后变换(缩放与逆变换一致)。
    • ‘ortho’:正交归一化(使变换具有单位范数)。
    • ‘forward’:向前变换(未缩放)。
    • 如果为 None,则不进行归一化(默认为 ‘backward’)。

三、返回值

  • 返回一个复数数组,表示输入数组的二维离散傅里叶变换。输出数组沿所指示的轴转换,具有与输入数组 a 相同的数据类型。

四、注意事项

  • 如果 s 和 axes 的长度不同,或者 axes 未给定但 len(s) != 2,则会引发 ValueError。

  • 如果 axes 中的元素大于输入数组 a 的轴数,则会引发 IndexError。

五、示例

python">import numpy as np  # 示例 1:简单的二维数组  
a = np.array([[5, 4], [6, 3]])  
fft2_result = np.fft.fft2(a)  
print(fft2_result)  # 示例 2:具有复数元素的二维数组  
a_complex = np.array([[5+1j, 4-2j], [6+3j, 3-1j]])  
fft2_complex_result = np.fft.fft2(a_complex)  
print(fft2_complex_result)

六、拓展信息

  • numpy.fft.ifft2:这是 np.fft.fft2 的逆操作,用于计算二维逆离散傅里叶变换

  • numpy.fft.fftn:这个函数用于计算n维实数组的傅里叶变换,其中n是数组的维度数。

  • numpy.fft.fftshift:这个函数将零频率项(即直流分量)移到数组的中心,通常用于可视化傅里叶变换的结果

4.2、numpy.fft.fftshift

一、描述

numpy.fft.fftshift 是一个用于对离散傅里叶变换(DFT)结果进行移位的函数。它可以将实数DFT的结果移动到复数平面上,使得直流分量(即零频率分量)位于中心

二、函数签名

python">numpy.fft.fftshift(x, axes=None)

三、参数

  • x:
    类型:array_like
    描述:输入数组,通常为实数DFT的结果。

  • axes:
    类型:int 或 shape tuple,可选
    描述:指定要移位的轴。默认为 None,表示对所有轴进行移位。可以传入一个整数或一个元组,表示要移位的轴。例如,对于2D数组,可以传入 1 或 (0, 1) 来分别对行和列进行移位。

三、返回值

  • 类型:与输入数组 x 相同类型的数组
  • 描述:返回移位后的数组。

四、示例

python">import numpy as np  # 创建一个实数DFT的结果数组  
x = np.array([1, 2, 3, 4])  # 对数组进行移位  
y = np.fft.fftshift(x)  # 注意:由于输入数组是一维的,并且长度是偶数,所以结果可能不符合直观预期(因为示例中的输出并不是从原始文档得到的)  
# 在实际使用中,fftshift通常用于二维或更高维度的数组,并且与fft2等函数结合使用  print(y)  # 输出可能是一个经过移位的数组

五、注意事项

  • numpy.fft.fftshift 并不直接计算DFT,而是对已经计算出的DFT结果进行移位。

  • 当输入数组 x 的长度是偶数时,y[0] 并不一定是奈奎斯特分量(Nyquist frequency component),而是经过移位后的数组的第一个元素。

  • numpy.fft.fftshift 常常与 numpy.fft.fft、numpy.fft.fft2 等函数结合使用,以在可视化DFT结果时,将直流分量置于频谱的中心。

六、拓展信息

  • 如果你在处理图像或其他二维数据时,numpy.fft.fftshift 可以帮助你更容易地解释和理解傅里叶变换的结果,因为直流分量(即低频信息)通常位于中心位置。

  • 与 numpy.fft.fftshift 相反的操作是 numpy.fft.ifftshift,它将经过 fftshift 处理的数组还原回原始位置。

5、参考


http://www.ppmy.cn/embedded/128172.html

相关文章

使用verilog设计实现FPGA实现的图像直方图均衡化及其仿真

以下是一个使用Verilog实现图像直方图均衡化的基本框架。 ## 一、图像直方图均衡化原理 1. 首先计算图像的直方图,即统计每个灰度值出现的频率。 2. 然后根据直方图计算累积分布函数(CDF)。 3. 最后根据CDF对每个像素的灰度值进行重新映射,以实现直方图均衡化,增强图像对…

Vulnhub靶场案例渗透[7]- DC7

文章目录 1. 靶场搭建2. 信息收集2.1 确定靶机ip2.2 服务信息收集2.3 社工信息收集 3. 提权 1. 靶场搭建 靶场源地址 检验下载文件的检验码&#xff0c;对比没问题使用vmware打开 # windwos 命令 Get-FileHash <filePath> -Algorithm MD5 # linux md5sum filepath2. 信…

MySQL-06.DDL-表结构操作-创建

一.DDL(表操作) create database db01;use db01;create table tb_user(id int comment ID&#xff0c;唯一标识,username varchar(20) comment 用户名,name varchar(10) comment 姓名,age int comment 年龄,gender char(1) comment 性别 ) comment 用户表; 此时并没有限制ID为…

Ac423 采药

代码 #include <bits/stdc.h> #define int long long using namespace std;const int N 200010, mod 1e9 7;int n, m, k, x, y, z, ans, t; int w[N], f[N];void solve() {cin >> m >> n;for (int i 1; i < n; i ){cin >> x >> y;for (…

2024了,传统行业转行AI,可不可行?

大家好&#xff0c;我是刚刚毕业于一所985学校的交叉学科硕士&#xff0c;现在做AI交叉 本科&#xff1f;呵呵&#xff0c;当然是传统行业 当时的一腔热血&#xff0c;也不得不对现实低头 经历考研的沉沦&#xff0c;二战的破釜&#xff0c;终于收获985大学offer 然而还是本…

方波信号发生器(完整SCL源代码)

正弦和余弦信号发生器请参考下面文章链接: 1、博途PLC平台 PLC信号发生器(博途SCL)_博图软件波形发生器怎么用-CSDN博客文章浏览阅读1.1k次。本文介绍了如何使用博途SCL编程实现不同周期和幅值的信号发生器,包括余弦和正弦信号。通过信号发生器,可以用于验证PLC的滤波器效…

物联网智能项目综述

物联网智能项目综述 引言 你是否想象过一个没有开关的家&#xff0c;或是一个在你到家前就知道你需要什么的城市&#xff1f;物联网&#xff08;IoT&#xff09;正悄然改变着我们的生活&#xff0c;赋予设备更智能的能力。想象一下&#xff0c;所有设备不仅仅是孤立的工具&am…

二分查找法的细节

第一个&#xff0c;最基本的二分查找算法&#xff1a; 1 因为我们初始化 right nums.length - 1 2 所以决定了我们的「搜索区间」是 [left, right] 3 所以决定了 while (left < right) 4 同时也决定了 left mid1 和 right mid-1 5 6 因为我们只需找到一个 target 的索引…