图像处理一般分为空间域处理和频率域处理。空间域处理是直接对图像内部的像素进行处理,其主要划分为灰度变换和空间滤波两种形式。灰度变换是对图像内单个像素进行处理,比如调节对比度和处理阈值等。空间滤波涉及图像质量的改变,例如图像平滑处理。空间域处理的计算简单方便,运算速度快。频率域处理是先将图像变换到频率域,然后在频率域对图像进行处理,最后再通过反变换将图像变换回空间域。傅里叶变换是应用最广的一种频域变换,它能够将图像从空间域变换到频率域,同样,逆傅里叶变换则可以将频率域的信息变换到空间域内。下面将对相关程序进行简单介绍。
1 Numpy实现傅里叶变换
Numpy的功能模块中的 fft2() 函数可以实现傅里叶变换,下面来介绍一下如何使用Numpy模块实现图像的傅里叶变换,以及在频率域内过滤图像的低频信息保留高频信息,从而实现高通滤波。
1.1 傅里叶变换
1.1.1 函数介绍
在Numpy中实现方框滤波的函数为numpy.fft.fft2(),其语法格式如下:
a = numpy.fft.fft2(src)
式中:
(1). a为返回值,是一个复数数组(complex ndarray)
(2). src为原始图像,是灰度图像
经过该函数的处理,可以得到图像的频谱信息。此时,图像频谱中零频率分量位于频谱图像(频域图像)的的左上角,不过为了便于观察,常使用numpy.fft.fftshift()将零频率成分移动到频域图像的中心位置,如下图所示
numpy.fft.fftshift()的语法格式如下:
b = numpy.fft.fftshift(a)
式中:
(1). b为返回值,和参数a相同,为调整过的复数数组(频谱)
(2). a为原始频谱,即numpy.fft.fft2()的返回值
由于图像频谱中的零频率分量被移动到了频域图像的中心位置,故而对于观察频谱中的零频分量十分有效。
关于返回值的复数数组,为了显示图像,需要将它们的值调整到[0, 255]的灰度空间内,使用公式为:
dst = 20 * numpy.log(numpy.abs(b))
式中:
(1). dst为返回值,是新的像素图像
(2). b为频谱值,即numpy.fft.fftshift()的返回值(调整过的复数数组)
numpyp.abs(x) 效果为返回数组x每个元素的绝对值
numpy.log(x) 效果为取对数,在不标明底数的情况下其底数默认为自然对数e (相关函数的简单介绍)
1.1.2 程序实例
用Numpy实现傅里叶变换,并观察得到的频谱图像
import numpy as np
from cv2 import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r"D:\anaconda\vscode-python\pic\jqm.jpg",0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
magnitude_spectrum = 20 * np.log(np.abs(fshift))
plt.subplot(121)
plt.imshow(img,cmap='gray')
plt.title('original')
plt.axis('off')
plt.subplot(122)
plt.imshow(magnitude_spectrum,cmap='gray')
plt.title('result')
plt.axis('off')
plt.show()
效果:
此处不用 cv2.imshow() 的原因是对于图像通道的定义不一样且还存在图像深度等一系列问题
参考:
cv2.imshow()和plt.imshow()显示的色差问题
解决“cv2.error:Unsupported depth of input image”问题
1.2 逆傅里叶变换
1.2.1 函数介绍
基于前文为了方便观察使用 numpy.fft.shift() 将零频分量移动到原来的位置,再进行逆傅里叶变换
函数 numpy.fft.ifftshift() 是 numpy.fft.fftshift() 的逆函数,其语法格式如下:
b = numpy.fft.ifftshift(a)
式中:
(1). b为返回值,调整后(调整回来)的频谱
(2). a为原始频谱,即零频移动到中心的频谱
而函数 numpy.fft.ifft2() 则可以实现逆傅里叶变换,其语法格式如下:
dst = numpy.fft.ifft2(b)
式中:
(1). dst为返回值,为一空间域复数数组
(2). b为频域数据(即前文将零频位置调整回去的频谱)
逆傅里叶变换得到的空间域信息为一复数数组,需要将该信息调整到[0, 255]的灰度空间内,使用公式为
res = numpy.abs(dst)
式中:
(1). res为最终得到的灰度图像
(2). dst逆傅里叶变换结果
1.2.2 程序实例
用Numpy实现傅里叶变换、逆傅里叶变换
import numpy as np
from cv2 import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r"D:\anaconda\vscode-python\pic\jqm.jpg",0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
ishift = np.fft.ifftshift(fshift)
res1 = np.fft.ifft2(ishift)
res2 = np.abs(res1)
plt.subplot(121)
plt.imshow(img,cmap='gray')
plt.title('img')
plt.axis('off')
plt.subplot(122)
plt.imshow(res2,cmap='gray')
plt.title('res')
plt.axis('off')
plt.show()
效果:
1.3 高通滤波示例
1.3.1 介绍
在一幅图像内同时存在着高频信号和低频信号,低频信号 对应着图像内变化缓慢的灰度分量(比如漫无边际的沙漠)。高频信号 对应图像内变化速度越来越快的灰度分量,是由灰度的尖锐过渡造成的(比如无尽的沙漠中出现了一片绿洲,那么绿洲的边缘信息就对应着高频信号)。
傅里叶变换可以将图像的高频新号和低频信号分离。例如可以像前文提到的那样,通过函数 fftshift() 将低频信号放到图像的中心位置再进行处理。通过在频域对高频信号和低频信号分别进行处理后在将其一起经逆傅里叶变换返回空间域就完成了对图像的频域处理。通过对图像进行频域处理,可以实现图像增强、图像去噪、边缘检测、特征提取、压缩与加密等操作。
可以通过: 傅里叶正变换 -> 频域(图像) -> 将低频分量置零 -> 傅里叶正变换 -> 图像(空间域)
这样就屏蔽了低频信号保留了大部分高频信号,即高通滤波,大概效果如下图所示
要将上图中右图中间的像素值置零需要先计算其中心位置的坐标,然后选取以该坐标为中心,上下左右各30像素大小的区域,并将该区域内的像素值置零。该滤波器的实现方法如下:
rows,cols = img.shape
crow,ccol = int(rows/2),int(cols/2)
fshift[crow-30:crow+30,ccol-30:ccol+30] = 0
1.3.2 程序实例
使用Numpy对图像进行高通滤波观察其差异
import numpy as np
from cv2 import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r"D:\anaconda\vscode-python\pic\jqm.jpg",0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
rows,cols = img.shape
crow,ccol = int(rows/2),int(cols/2)
fshift[crow-30:crow+30,ccol-30:ccol+30] = 0
ishift = np.fft.ifftshift(fshift)
res1 = np.fft.ifft2(ishift)
res2 = np.abs(res1)
plt.subplot(121)
plt.imshow(img,cmap='gray')
plt.title('img')
plt.axis('off')
plt.subplot(122)
plt.imshow(res2,cmap='gray')
plt.title('res')
plt.axis('off')
plt.show()
效果(边缘检测既视感):
2 OpenCV实现傅里叶变换
2.1 傅里叶变换
2.1.1 函数介绍
在OpenCV中实现傅里叶变换的函数为cv2.dft(),其语法格式如下:
a = cv2.dft(img,flags)
式中:
(1). a为返回值,是原始图像的频谱信息(与使用Numpy进行傅里叶变换所得的值类似)
(2). img为原始图像,应用时需要先使用 np.float32() 改变下图像格式,不然或许会出现这样的错误。
(3). flags为转换标识,其值通常为 [cv2.DFT_COMPLEX_OUTPUT] ,用来输出一个复数阵列。
函数 cv2.dft() 的输出结果与使用Numpy进行傅里叶变换得到的结果一致,但其返回的值是双通道的(第一个通道为结果的实数部分,第二个通道为结果的虚数部分)
同样为了处理方便,我们可以在 cv2.dft() 处理后使用 np.fft.fftshift() 将零频率分量移至频谱图像中心位置。
在经过上述处理后,频谱图像还是一个由实部和虚部构成的值,要将其显现出来还需要进一步处理。
函数 cv2.magnitude() 可以计算频谱信息的幅度,其语法格式如下:
c = cv2.magnitude(a,b)
式中:
(1). c为返回值,是a和b的平方和取二次方根后的结果
(2). a为浮点型x坐标值,也就是实部
(3). b为浮点型y坐标值,也就是虚部(需与参数a具有相同的size大小)
对频谱信息的幅度进行调整后还需要对其进一步转换,以便将频谱信息以图像的形式展现出来(将幅度值映射到灰度空间[0, 255]中)
此处使用公式:
res = 20 * np.log(cv2.magnitude(a,b))
2.1.2 程序实例
使用OpenCV对图像进行傅里叶变换并观察其现象
import numpy as np
from cv2 import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r"D:\anaconda\vscode-python\pic\jqm.jpg",0)
dft = cv2.dft(np.float32(img),flags=cv2.DFT_COMPLEX_OUTPUT)
dftshift = np.fft.fftshift(dft)
result = 20 * np.log(cv2.magnitude(dftshift[:,:,0],dftshift[:,:,1]))
plt.subplot(121)
plt.imshow(img,cmap='gray')
plt.title('img')
plt.axis('off')
plt.subplot(122)
plt.imshow(result,cmap='gray')
plt.title('res')
plt.axis('off')
plt.show()
效果:
左图为原始图像(灰度),右图为频谱图像,是使用函数 np.fft.fftshift() 将零频率分量移至频谱图像中心位置的结果。
2.2 逆傅里叶变换
2.2.1 函数介绍
在OpenCV中实现逆傅里叶变换的函数为cv2.idft(),其语法格式如下:
a = cv2.idft(img)
式中:
(1). a为返回值,依旧是复数,故而需要函数 cv2.magnitude() 计算其幅度从而映射回灰度空间。
(2). img为原始图像,应用时需要先使用 np.float32() 改变下图像格式,不然或许会出现这样的错误。
2.2.2 程序实例
使用OpenCV对图像进行傅里叶变换和逆傅里叶变换,并展示原始图像经逆傅里叶变换后得到的图像。
import numpy as np
from cv2 import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r"D:\anaconda\vscode-python\pic\jqm.jpg",0)
dft = cv2.dft(np.float32(img),flags=cv2.DFT_COMPLEX_OUTPUT)
# dftshift = np.fft.fftshift(dft)
# result = 20 * np.log(cv2.magnitude(dftshift[:,:,0],dftshift[:,:,1]))
res1 = cv2.idft(dft)
res2 = cv2.magnitude(res1[:,:,0],res1[:,:,1])
plt.subplot(121)
plt.imshow(img,cmap='gray')
plt.title('img')
plt.axis('off')
plt.subplot(122)
plt.imshow(res2,cmap='gray')
plt.title('res')
plt.axis('off')
plt.show()
效果:
2.3 低通滤波器示例
2.3.1 介绍
前面提到过,高频信号对应图像内变化速度越来越快的灰度分量,去掉其后会影响边缘信息的显示,简言之图像进行低通滤波后会变模糊。
该滤波器的制作方法与前文中的高通滤波器类似,只不过这次被置零的是高频信号(如下图)
对于上图中第三幅图的这种构造可以使用掩膜来实现具体程序如下:
rows,cols = img.shape
crow,ccol = int(rows/2),int(cols/2)
mask = np.zeros((rows,cols,2),np.uint8)
mask[crow-30:crow+30,ccol-30:ccol+30] = 1
再将掩膜与频谱图像进行与运算即可实现低通滤波
2.3.2 程序实例
使用OpenCV对图像进行低通滤波观察其差异
import numpy as np
from cv2 import cv2
import matplotlib.pyplot as plt
img = cv2.imread(r"D:\anaconda\vscode-python\pic\jqm.jpg",0)
dft = cv2.dft(np.float32(img),flags=cv2.DFT_COMPLEX_OUTPUT)
dftshift = np.fft.fftshift(dft)
rows,cols = img.shape
crow,ccol = int(rows/2),int(cols/2)
mask = np.zeros((rows,cols,2),np.uint8)
mask[crow-30:crow+30,ccol-30:ccol+30] = 1
fshift = dftshift * mask
ishift = np.fft.ifftshift(fshift)
res1 = cv2.idft(ishift)
res2 = cv2.magnitude(res1[:,:,0],res1[:,:,1])
plt.subplot(121)
plt.imshow(img,cmap='gray')
plt.title('img')
plt.axis('off')
plt.subplot(122)
plt.imshow(res2,cmap='gray')
plt.title('res')
plt.axis('off')
plt.show()
效果:
明显可见,经过低通滤波后,图像的边缘信息被明显削弱