- 实现了一维和二维 FFT 和 IFFT 函数- 添加了图像填充函数,以支持非 2 的整数次幂大小的图像 - 编写了主程序,演示了 FFT 和 IFFT 的使用方法 - 使用 Matplotlib 可视化了原始图像、频谱图和恢复后的图像
87 lines
3.6 KiB
Python
87 lines
3.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
import numpy as np # 导入NumPy库,用于数值计算
|
||
import math # 导入math库,用于数学运算
|
||
import cv2 # 导入OpenCV库,用于图像处理
|
||
import matplotlib.pyplot as plt # 导入matplotlib库,用于绘图
|
||
|
||
# 一维FFT实现(递归 Cooley-Tukey)
|
||
def fft_1d(x):
|
||
N = len(x) # 获取输入序列的长度
|
||
if N <= 1: # 如果序列长度小于等于1,直接返回序列
|
||
return x
|
||
even = fft_1d(x[::2]) # 递归计算偶数索引位置的FFT
|
||
odd = fft_1d(x[1::2]) # 递归计算奇数索引位置的FFT
|
||
T = [np.exp(-2j * np.pi * k / N) * odd[k] for k in range(N // 2)] # 计算旋转因子
|
||
return [even[k] + T[k] for k in range(N // 2)] + \
|
||
[even[k] - T[k] for k in range(N // 2)] # 合并结果
|
||
|
||
# 一维IFFT实现
|
||
def ifft_1d(x):
|
||
N = len(x) # 获取输入序列的长度
|
||
x_conj = [np.conj(xi) for xi in x] # 对输入序列取共轭
|
||
y = fft_1d(x_conj) # 对共轭序列进行FFT
|
||
return [np.conj(yi) / N for yi in y] # 对结果取共轭并除以N,得到IFFT结果
|
||
|
||
# 二维FFT
|
||
def fft_2d(image):
|
||
image = image.astype(float) # 将图像转换为浮点型
|
||
rows, cols = image.shape # 获取图像的行数和列数
|
||
# 对每行进行FFT
|
||
fft_rows = [fft_1d(list(image[i, :])) for i in range(rows)]
|
||
# 对每列进行FFT
|
||
fft_cols = [fft_1d([fft_rows[i][j] for i in range(rows)]) for j in range(cols)]
|
||
return np.array(fft_cols).T # 返回转置后的结果
|
||
|
||
# 二维IFFT
|
||
def ifft_2d(freq_domain):
|
||
rows, cols = freq_domain.shape # 获取频域图像的行数和列数
|
||
# 对每列进行IFFT
|
||
ifft_cols = [ifft_1d([freq_domain[i][j] for i in range(rows)]) for j in range(cols)]
|
||
# 对每行进行IFFT
|
||
ifft_rows = [ifft_1d([ifft_cols[j][i] for j in range(cols)]) for i in range(rows)]
|
||
return np.array(ifft_rows) # 返回IFFT结果
|
||
|
||
# 填充图像为2的整数次幂大小
|
||
def pad_image(image):
|
||
h, w = image.shape # 获取图像的高度和宽度
|
||
h2 = 1 << (h - 1).bit_length() # 计算最接近且大于等于h的2的整数次幂
|
||
w2 = 1 << (w - 1).bit_length() # 计算最接近且大于等于w的2的整数次幂
|
||
padded = np.zeros((h2, w2)) # 创建填充后的零矩阵
|
||
padded[:h, :w] = image # 将原图像放入填充矩阵的左上角
|
||
return padded # 返回填充后的图像
|
||
|
||
# 主程序
|
||
def main():
|
||
# 读取图像
|
||
image = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE) # 以灰度模式读取图像
|
||
if image is None: # 如果图像读取失败,输出错误信息并返回
|
||
print("图像读取失败,请确认 image.jpg 文件存在于当前目录。")
|
||
return
|
||
|
||
# 填充图像
|
||
padded_image = pad_image(image) # 对图像进行填充
|
||
|
||
# 执行FFT和IFFT
|
||
freq = fft_2d(padded_image) # 对填充后的图像进行二维FFT
|
||
restored = ifft_2d(freq).real # 对频域图像进行二维IFFT,并取实部
|
||
|
||
# 可视化结果
|
||
plt.figure(figsize=(12, 4)) # 创建绘图窗口
|
||
|
||
plt.subplot(1, 3, 1) # 创建第一个子图
|
||
plt.title("Original") # 设置子图标题
|
||
plt.imshow(image, cmap="gray") # 显示原始图像
|
||
|
||
plt.subplot(1, 3, 2) # 创建第二个子图
|
||
plt.title("FFT Spectrum") # 设置子图标题
|
||
plt.imshow(np.log(np.abs(freq) + 1), cmap="gray") # 显示FFT频谱
|
||
|
||
plt.subplot(1, 3, 3) # 创建第三个子图
|
||
plt.title("Restored") # 设置子图标题
|
||
plt.imshow(restored, cmap="gray") # 显示恢复后的图像
|
||
|
||
plt.tight_layout() # 调整子图布局
|
||
plt.show() # 显示绘图窗口
|
||
|
||
if __name__ == "__main__":
|
||
main() # 运行主程序 |