开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第19天,点击查看活动详情
前言
在本节中,我们将学习如何使用离散傅里叶变换( Discrete Fourier Transform, DFT )执行上采样并增加图像分辨率。上采样通常在空域中进行,通过使用最近邻、双线性或双立方插值来推测新增的像素值。但在本节中,我们将尝试使用 DFT 实现上采样,并在频域中应用低通滤波器( Low Pass Filter, LPF)。
离散傅里叶变换与低通滤波器
为了充分理解相关方法,我们首先了解如何在空域和频域中使用卷积实现 LPF。我们已经知道,滤波是指改变像素强度值以发现某些图像特征,例如平滑或锐化等。
LPF 仅允许通过来自图像频域表示的低频(使用 DFT 获得)部分,并过滤所有超出截止值的高频部分。可以在空间域中使用合适的核(例如高斯核)执行卷积实现 LPF,起始时核窗口位于(灰度)图像左上角,核的尺寸大小不能超过图像尺寸。输出图像中的像素值是通过在输入图像中滑动核窗口进行计算的:
接下来,我们将在频域中实现 LPF,根据卷积定理,我们只需要进行元素乘法操作,因此算法运算速度很快:
根据卷积定理,我们可以将空间卷积转换为频域逐元素乘法操作。
使用离散傅里叶变换和低通滤波器对图像执行上采样
接下来,我们将使用尺寸为 400x600 的输入图像,并计算得到两倍尺寸的图像 (800x1200)。
(1) 首先,读取图像,将其转换为灰度图像,并在每个交替的行/列上填充零,从而令图像的尺寸大小扩大两倍:
import numpy as np
import numpy.fft as fp
from skimage.io import imread
from skimage.color import rgb2gray
import matplotlib.pyplot as plt
im = 255*rgb2gray(imread('1.png'))
im1 = np.zeros((2*im.shape[0], 2*im.shape[1]))
print(im.shape, im1.shape)
for i in range(im.shape[0]):
for j in range(im.shape[1]):
im1[2*i,2*j] = im[i,j]
def plot_image(im, title):
plt.imshow(im, cmap='gray')
plt.axis('off')
plt.title(title, size=10)
def plot_freq_spectrum(F, title, cmap=plt.cm.gray, show_axis=True, colorbar=False):
plt.imshow((20*np.log10(0.1 + fp.fftshift(F))).real.astype(int), cmap=cmap)
if not show_axis:
plt.axis('off')
if colorbar:
plt.colorbar()
plt.title(title, size=10)
(2) 我们将使用以下 LPF 核,可以看出,它的中心权重最高,而远离中心的权重较小:
kernel = [[0.25, 0.5, 0.25], [0.5, 1, 0.5], [0.25, 0.5, 0.25]]
(3) 要在频域中实现滤波器,我们需要将图像乘以核,为此,核的形状需要完全等于图像的形状。为了使用 NumPy 库的函数 pad(),我们首先定义填充函数 pad_with_zeros() 用值零填充核:
def pad_with_zeros(vector, pad_width, iaxis, kwargs):
vector[:pad_width[0]] = 0
vector[-pad_width[1]:] = 0
return vector
kernel = np.pad(kernel, (((im1.shape[0]-3)//2,(im1.shape[0]-3)//2+1), ((im1.shape[1]-3)//2,(im1.shape[1]-3)//2+1)), pad_with_zeros)
(4) 接下来,计算输入图像和扩展核的功率谱。由于核已经居中,因此,在应用 DFT 之前,我们需要应用 fftshift() 的逆函数 ifftshift():
freq = fp.fft2(im1)
freq_kernel = fp.fft2(fp.ifftshift(kernel))
(5) 通过频域中的逐元素乘法计算 LPF:
freq_LPF = freq*freq_kernel # 卷积理论
(6) 最后,使用逆 DFT 获得输出图像,我们需要提取复数输出中的实数部分:
im2 = fp.ifft2(freq_LPF).real
(7) 绘制输入、核和输出图像以及它们的功率谱:
plt.figure(figsize=(15,10))
plt.gray()
cmap = 'nipy_spectral'
plt.subplot(231), plot_image(im, 'Original Input Image')
plt.subplot(232), plot_image(im1, 'Padded Input Image')
plt.subplot(233), plot_freq_spectrum(freq, 'Original Image Spectrum', cmap=cmap)
plt.subplot(234), plot_freq_spectrum(freq_kernel, 'Image Spectrum of the LPF', cmap=cmap)
plt.subplot(235), plot_freq_spectrum(fp.fft2(im2), 'Image Spectrum after LPF', cmap=cmap)
plt.subplot(236), plot_image(im2.astype(np.uint8), 'Output Image')
plt.show()