0
点赞
收藏
分享

微信扫一扫

【OpenCV 完整例程】77. OpenCV 实现快速傅里叶变换

【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 2p3q5r 的矩阵的计算性能最好。为了提高运算性能,可以对原矩阵的右侧和下方补 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:对一维或二维复数数组进行逆变换,结果通常是一个尺寸相同的复数矩阵

注意事项:

  1. 输入图像 src 是 np.float32 格式,如图像使用 np.uint8 格式则必须先转换 np.float32 格式。
  2. 默认方法 cv.DFT_COMPLEX_OUTPUT 时,输入 src 是 np.float32 格式的单通道二维数组,输出 dst 是 2个通道的二维数组,第一通道 dft[:,:,0] 是实数部分,第二通道 dft[:,:,1] 是虚数部分。
  3. 不能直接用于显示图像。可以使用 cv.magnitude() 函数将傅里叶变换的结果转换到灰度 [0,255]。
  4. idft(src, dst, flags) 等价于 dft(src, dst, flags=DFT_INVERSE)。
  5. 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


举报

相关推荐

0 条评论