【OpenCV 完整例程】77. OpenCV 实现快速傅里叶变换
傅里叶变换在理论上需要 O ( M N ) 2 O(MN)^2 O(MN)2 次运算,非常耗时;快速傅里叶变换只需要 O ( M N l o g ( M N ) ) O(MN log (MN)) O(MNlog(MN)) 次运算就可以完成。
OpenCV 中的傅里叶变换函数 cv.dft() 对于行数和列数都可以分解为 2 p ∗ 3 q ∗ 5 r 2^p * 3^q * 5^r 2p∗3q∗5r 的矩阵的计算性能最好。为了提高运算性能,可以对原矩阵的右侧和下方补 0,以满足该分解条件。OpenCV 中的
cv.getOptimalDFTSize() 函数可以实现图像的最优 DFT 尺寸扩充,适用于 cv.dft() 和 np.fft.fft2()。
函数说明:
cv.getOptimalDFTSize(versize) → retval
参数说明:
- versize:数组大小
- retval:DFT 扩充的最优数组大小
使用 OpenCV 中的 cv.dft() 函数也可以实现图像的傅里叶变换,cv.idft() 函数实现图像傅里叶逆变换。
函数说明:
cv.dft(src[, dst[, flags[, nonzeroRows]]]) → dst
cv.idft(src[, dst[, flags[, nonzeroRows]]]) → dst
参数说明:
- src:输入图像,单通道灰度图像,使用 np.float32 格式
- dst:输出图像,图像大小与 src 相同,数据类型由 flag 决定
- flag:转换标识符
- cv.DFT_INVERSE:用一维或二维逆变换取代默认的正向变换
- cv.DFT_SCALE:缩放比例标识,根据元素数量求出缩放结果,常与DFT_INVERSE搭配使用
- cv.DFT_ROWS: 对输入矩阵的每行进行正向或反向的傅里叶变换,常用于三维或高维变换等复杂操作
- cv.DFT_COMPLEX_OUTPUT:对一维或二维实数数组进行正向变换,默认方法,结果是由 2个通道表示的复数阵列,第一通道是实数部分,第二通道是虚数部分
- cv.DFT_REAL_OUTPUT:对一维或二维复数数组进行逆变换,结果通常是一个尺寸相同的复数矩阵
注意事项:
- 输入图像 src 是 np.float32 格式,如图像使用 np.uint8 格式则必须先转换 np.float32 格式。
- 默认方法 cv.DFT_COMPLEX_OUTPUT 时,输入 src 是 np.float32 格式的单通道二维数组,输出 dst 是 2个通道的二维数组,第一通道 dft[:,:,0] 是实数部分,第二通道 dft[:,:,1] 是虚数部分。
- 不能直接用于显示图像。可以使用 cv.magnitude() 函数将傅里叶变换的结果转换到灰度 [0,255]。
- idft(src, dst, flags) 等价于 dft(src, dst, flags=DFT_INVERSE)。
- OpenCV 实现傅里叶变换,计算速度比 Numpy 更快。
转换标识符为 cv.DFT_COMPLEX_OUTPUT 时,cv.dft() 函数的输出是 2个通道的二维数组,使用 cv.magnitude() 函数可以实现计算二维矢量的幅值 。
例程 8.12:OpenCV 快速傅里叶变换
# 8.12:OpenCV 快速傅里叶变换
imgGray = cv2.imread("../images/Fig0429a.tif", flags=0) # flags=0 读取为灰度图像
rows,cols = imgGray.shape[:2] # 图像的行(高度)/列(宽度)
# 快速傅里叶变换(要对原始图像进行矩阵扩充)
rPad = cv2.getOptimalDFTSize(rows) # 最优 DFT 扩充尺寸
cPad = cv2.getOptimalDFTSize(cols) # 用于快速傅里叶变换
imgEx = np.zeros((rPad,cPad,2),np.float32) # 对原始图像进行边缘扩充
imgEx[:rows,:cols,0] = imgGray # 边缘扩充,下侧和右侧补0
dftImgEx = cv2.dft(imgEx, cv2.DFT_COMPLEX_OUTPUT) # 快速傅里叶变换
# 傅里叶逆变换
idftImg = cv2.idft(dftImgEx) # 逆傅里叶变换
idftMag = cv2.magnitude(idftImg[:,:,0], idftImg[:,:,1]) # 逆傅里叶变换幅值
# 矩阵裁剪,得到恢复图像
idftMagNorm = np.uint8(cv2.normalize(idftMag, None, 0, 255, cv2.NORM_MINMAX)) # 归一化为 [0,255]
imgRebuild = np.copy(idftMagNorm[:rows, :cols])
print("max(imgGray-imgRebuild) = ", np.max(imgGray-imgRebuild))
print("imgGray:{}, dftImg:{}, idftImg:{}, imgRebuild:{}".
format(imgGray.shape, dftImgEx.shape, idftImg.shape, imgRebuild.shape))
plt.figure(figsize=(9, 6))
plt.subplot(131), plt.title("Original image"), plt.axis('off')
plt.imshow(imgGray, cmap='gray')
plt.subplot(132), plt.title("Log-trans of DFT amp"), plt.axis('off')
dftAmp = cv2.magnitude(dftImgEx[:,:,0], dftImgEx[:,:,1]) # 幅度谱,中心化
dftAmpLog = np.log(1 + dftAmp) # 幅度谱对数变换,以便于显示
plt.imshow(cv2.normalize(dftAmpLog, None, 0, 255, cv2.NORM_MINMAX), cmap='gray')
plt.subplot(133), plt.title("Rebuild image with IDFT"), plt.axis('off')
plt.imshow(imgRebuild, cmap='gray')
plt.tight_layout()
plt.show()
(本节完)
版权声明:
youcans@xupt 原创作品,转载必须标注原文链接
Copyright 2021 youcans, XUPT
Crated:2022-1-20