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

【python】OpenCV—Fourier Transform

在这里插入图片描述

文章目录

  • 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 plt

img = 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 plt

img = 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 plt

img = 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 plt

def 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 plt

def 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 plt

img1 = 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 plt

img1 = 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 plt

img1 = 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 plt

img1 = 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)。以下是该函数的中文文档:

一、函数签名

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。

五、示例

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的结果移动到复数平面上,使得直流分量(即零频率分量)位于中心

二、函数签名

numpy.fft.fftshift(x, axes=None)

三、参数

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

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

三、返回值

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

四、示例

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、参考

  • Python下opencv使用笔记(十)(图像频域滤波与傅里叶变换)

  • Python图像处理笔记——傅里叶变换


http://www.kler.cn/news/357462.html

相关文章:

  • 十一、SQL 优化:提升数据库性能的关键技巧与实例讲解
  • MongoDB 的优点和缺点
  • 探索YOLO v11:3D人工智能的RGB-D视觉革命
  • 深度解析 Redis 存储结构及其高效性背后的机制
  • 放眼全球:在竞争激烈的当下,海外媒体发稿何以备受关注?
  • UDP协议和TCP协议
  • 常见网络钓鱼类型
  • 【深度学习|地学应用】Aerosol——宏观层面分析地震气溶胶异常——采用 HYSPLIT-4模型模拟地震AOD异常的水平和垂直后向轨迹
  • IDEA中的快捷键大全--超详细
  • UltraISO(软碟通)制作U盘制作Ubuntu20.04启动盘
  • SQLI LABS | Less-2 GET-Error based-Intiger based
  • 24算法刷题 | Day46 | 动态规划 XIII | 子序列问题 IV | LeetCode 647. 回文子串,516. 最长回文子序列
  • 面试手撕代码-二十三种设计模式之享元模式
  • 计算机组成原理(笔记7高速缓冲存储器Cache,计算机组成原理的重难点全、直接、组相连)
  • 探索 Python 中的 XML 转换利器:xml2dict
  • 量子门电路开销——T门、clifford门、toffoli门、fredkin门
  • AutoFixture:.NET 的假数据生成工具
  • 道路垃圾识别数据集 含pt模型界面 18类 共7542张图片,xml和txt标签都有;
  • 安全光幕的工作原理及应用场景
  • 域7:安全运营 第18章(DRP)和第19章 (Investigation and Ethics)