二维卷积定理的验证(下,cv2.filter2D())

二维卷积定理的验证(下,cv2.filter2D())

前言 : 内容承接了二维卷积定理的验证(上)。主要解决该博文中最后提到的问题,也即cv2.filter2D()如何应用傅里叶变换来替代进行图像的卷积计算。

0. 基本问题

二维卷积定理表达式如下
f ( x , y ) ∗ g ( x , y ) = F − 1 [ F ( f ( x , y ) ) ⋅ F ( g ( x , y ) ) ] f(x,y)*g(x,y) = F^{-1}[F(f(x,y))·F(g(x,y))] f(x,y)g(x,y)=F1[F(f(x,y))F(g(x,y))]
如博文二维卷积定理的验证(上)中所述,在验证过程中发现,padding 的不同影响着最终结果的不同。

而等式左侧卷积操作和右侧傅里叶变换,都需要预先对图像进行 padding 操作,也即边界处理。

因此,在前述博文验证基础上,衍生出一个问题,即“是否存在着某种边界处理方式,使得卷积图像结果矩阵尺寸没有变化,且所有值都可以与傅里叶方式计算下等价。”

也即,cv2.filter2D()是如何进行边界处理的。

1. 思路与流程

问题以及验证流程简化下,可以描述成以下:

  1. 先使用 cv2.filter2D( img, -1, kenel)函数计算出一个结果。

    注意 kenel 要大于11*11,只有尺寸大时,该函数才是使用傅里叶变换来实现卷积操作。

  2. 使用 scipy.signal.convolve2d()函数完成正常卷积操作。

    注意:这里涉及到了边界处理和卷积类型选择

  3. 使用 np.fft.fft2()np.fft.ifft2() 函数完成傅里叶变换操作。

    注意:这里涉及到了边界处理

最终目的是,通过调节(2)中卷积时的边界处理和类型选择,以及,(3)傅里叶变换时的边界处理,使得(2)(3)结果与(1)中结果完全一致。

这样,(1)(2)结果相同,也就验证了卷积定理;(1)(3)结果相同,可以弄清cv2.filter2D()中傅里叶变换代替卷积过程。

2. 代码实践

按照上述思路,进行参数调试,因为比较繁琐,不呈现调试过程,只呈现最终代码。

完整代码

import cv2
import numpy as np
from scipy import signal

# ---------- 原始图像 f(x) 和 卷积核 g(x) -------------- #
img = cv2.imread("fp.jpg")[0:101,0:101]
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

kenel = np.ones((17,17))/17**2

# ---------------- cv2.filter2D() ---------------------#
# 数据类型
gray = gray.astype(np.float64)

Conv_img = cv2.filter2D(gray, -1, kenel)
np.savetxt('Conv_img64.txt', Conv_img, fmt='%0.8f')

# ------------------ signal.convolve2d() --------------#
# 边界类型选取
#img_pad = cv2.copyMakeBorder(gray, 8,8,8,8, cv2.BORDER_REFLECT)
img_pad = cv2.copyMakeBorder(gray, 8,8,8,8, cv2.BORDER_REFLECT_101)
#img_pad = cv2.copyMakeBorder(gray, 8,8,8,8, cv2.BORDER_REPLICATE)
#img_pad = cv2.copyMakeBorder(gray, 8,8,8,8, cv2.BORDER_CONSTANT, 0)
# 数据类型
img_pad = img_pad.astype(np.float64)
kenel = kenel.astype(np.float64)

Conv = signal.convolve2d(img_pad, kenel, mode='valid')
np.savetxt('Conv64.txt', Conv, fmt='%0.8f')

# ---------------- fft() & ifft() ---------------------#
# padding 边界选择
img_pad = cv2.copyMakeBorder(gray, 8,8,8,8, cv2.BORDER_REFLECT_101) #cv2.BORDER_REPLICATE
kenel_pad = cv2.copyMakeBorder(kenel, 50,50,50,50, cv2.BORDER_CONSTANT, 0)
# 数据类型
img_pad = img_pad.astype(np.float64)
kenel_pad = kenel_pad.astype(np.float64)

# F(f(x))
img_fft = np.fft.fftshift(np.fft.fft2(img_pad))
# F(g(x))
kenel_fft = np.fft.fftshift(np.fft.fft2(kenel_pad))

# ifft( F(f(x))·F(g(x)) )
FFT = np.fft.ifftshift(np.fft.ifft2(np.fft.fftshift(img_fft*kenel_fft)))
np.savetxt('FFT64.txt', np.abs(FFT)[8:-8, 8:-8], fmt='%0.8f')

细节说明

  • 三种方法下,数据类型要保持一致!!!不然结果的小数点后会有些许差异。
  • 傅里叶变换的结果图像尺寸会变大,需要剪切掉一部分,才能与前两者方法完全相等!!!

这两点,上述代码都有体现。

最终结果

  • cv2.filter2D() 结果

在这里插入图片描述

  • 卷积结果

在这里插入图片描述

  • 傅里叶变换结果

在这里插入图片描述

3. 结论与分析

  • 从上述结果中可以看出,三者结果是一致的,此条件下:

    • 卷积,图片 padding 使用 cv2.BORDER_REFLECT_101 边界。使用了 ‘same’ 卷积。

      说明:上述代码中虽然使用了 ‘valid’。但因为预先进行了padding,所以实际上是 ’same‘ 操作。

    • 傅里叶变换,图片 padding 使用 cv2.BORDER_REFLECT_101 边界,核 padding 使用补零操作。结果尺寸为 ( i m g _ H + k e n e l _ H − 1 ) × ( i m g _ W + k e n e l _ W − 1 ) (img\_H + kenel\_H-1)\times (img\_W + kenel\_W-1) (img_H+kenel_H1)×(img_W+kenel_W1) ,因此需要剪裁。

  • 三种函数计算前,数据的精度要保持一致!!!这样最终结果才会完全相同。

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值