为啥cv2和skimage之间的高斯滤波器不同?

Posted

技术标签:

【中文标题】为啥cv2和skimage之间的高斯滤波器不同?【英文标题】:Why is Gaussian Filter different between cv2 and skimage?为什么cv2和skimage之间的高斯滤波器不同? 【发布时间】:2016-07-15 19:04:32 【问题描述】:

我有一张图像,我使用cv2.GaussianBlurskimage.gaussian_filter 库应用了高斯模糊,但我得到了明显不同的结果。我很好奇为什么,以及如何使skimage 看起来更像cv2。我知道skimage.gaussian_filterscipy.scipy.ndimage.filters.gaussian_filter 的包装。为了清楚地说明问题,为什么这两个功能不同,可以做些什么来使它们更相似?

这是我的测试图片:

这是cv2 版本(看起来更模糊):

这是skimage/scipy 版本(看起来更清晰):

详情:

skimage_response = skimage.filters.gaussian_filter(im, 2, multichannel=True, mode='reflect')

cv2_response = cv2.GaussianBlur(im, (33, 33), 2)

所以 sigma=2 并且过滤器的大小足够大,它不应该产生影响。 Imagemagick covnert -gaussian-blur 0x2 在视觉上与 cv2 一致。

版本:cv2=2.4.10、skimage=0.11.3、scipy=0.13.3

【问题讨论】:

【参考方案1】:

对于 GaussianBlur,您使用的是相当大的内核(大小 = 33),这会导致大量平滑。平滑将在很大程度上取决于您的内核大小。使用您的参数,每个新像素值在 33*33 像素“窗口”中“平均”。

cv2.GaussianBlur 的定义可以在这里找到 http://docs.opencv.org/3.1.0/d4/d13/tutorial_py_filtering.html#gsc.tab=0

相比之下,skimage.filters.gaussian 似乎适用于较小的内核。在 skimage 中,“大小”由与内核大小相关的 sigma 定义,如下所述:https://en.wikipedia.org/wiki/Gaussian_filter

定义见:http://scikit-image.org/docs/dev/api/skimage.filters.html#skimage.filters.gaussian

为了获得相应的结果,您必须为 OpenCV 使用更小的内核。

此外,对于这两个库,我强烈建议使用最新的库版本。

【讨论】:

平滑量由 sigma 控制,而不是大小。像素不是直接平均的,它们是由高斯核加权平均的。 Size 只是截断计算,而 skimage 计算的大小为 4*sigma。版本应该不是问题。这是旧的基本功能。 据我在这里阅读,您使用的是已弃用的功能 gaussian_filter scikit-image.org/docs/dev/api/… ,但您认为这是基本功能是正确的。您也正确地认为 sigma 与内核大小相关。但是,我不明白为什么您在 scikit 中使用的 siga 为 2 应该相当于 OpenCV 中的内核大小为 33。 我不是数学家,但我阅读en.wikipedia.org/wiki/Gaussian_filter 的方式,[quote]“高斯内核需要 6\sigma-1 值,例如它需要 3 个 \sigma长度为 17" 的内核。这意味着您的 sima=2 等效于大小为 6*2-1=11 的内核。抱歉,我不是这方面的专家,但您可能需要检查您的尺寸假设。 在数学上,高斯核的大小是无限的,只是远离中心的值很小,可以忽略不计。功能 gaussian_filter 已弃用,但我怀疑这只是名称更改,因为它们都只是包装了 scipy 过滤器。 Scipy 使大小为 8 * sigma + 1(或 4 * sigma * 2 个边 + 1 个中心),而 opencv 做了类似的事情,但通过更大的尺寸增加精度不应使其在视觉上或多或少变得模糊。 是的,这个答案不正确。您可以拥有任意大小的内核,但 sigma 控制内核中的权重。 1.0 的 sigma 值和 99 的内核大小应该与 sigma=1.0 和 9 的内核大小给出相同的结果。只是较大的内核将包含大部分零。【参考方案2】:

如果有人对如何使 skimage.gaussian_filter() 与 Matlab 的等效 imgaussfilt() 匹配(我发现这个问题的原因)感到好奇,请将参数 'truncate=2' 传递给 skimage.gaussian_filter()。 skimage 和 Matlab 都将内核大小计算为 sigma 的函数。 Matlab 的默认值为 2。Skimage 的默认值为 4,默认情况下会产生明显更大的内核。

【讨论】:

很高兴知道,但这并不是真正的答案。有时我会滥用网站发布评论作为答案,因为文本不适合评论,或者因为评论包含代码并且评论系统不能很好地处理更长的 sn-ps 代码 - 但我猜你的文本适合评论。 【参考方案3】:

这两个是相等的:

gau_img = cv2.GaussianBlur(img, (5,5), 10.0) # 5*5 kernal, 2 on each side. 2 = 1/5 * 10 = 1/5 * sigma
gau_img = skimage.filters.gaussian(img, sigma=10, truncate=1/5)

整个高斯核仅由 sigma 定义。但是你使用高斯内核的哪一部分来模糊图像是由truncate(在skimage中)或ksize(在opencv中)定义的。

【讨论】:

如果我使用上面的,如果 truncate 是 4.0 而 sigma 是 5.0,这可能行不通,对吧?那么看起来内核大小应该是(0.25,0.25)?但是opencv要求ksize为正且奇数。 @dhamechaSpeaks ksize 必须为正且奇数,因为内核必须是对称的。如果 ksize=5,那么每边有 2 个像素。在这种情况下,skimage中的truncate参数应该设置为(ksize-1)/2/sigma = 2/sigma【参考方案4】:

根据[Scipy0.15.1 API][1]

scipy.ndimage.filters(img, sigma=sigma, truncate = 4.0)

它使用 truncate * sigma 中的内核大小设置高斯滤波器。在这种理解下,以下两个函数在灰度图像上会给出相同的结果:

trunc_val = 3
sigma_val = 3
k_size = int(sigma_val * trunc_val)
gau_img1 = cv2.GaussianBlur(img, (k_size,k_size), sigma_val)
gau_img2 = gaussian_filter(img, sigma = sigma_val, truncate = trunc_val) 

cv2.imshow("cv2 res", gau_img1)
cv2.imshow("scipy res", gau_img2)
cv2.waitKey(-1)

一些测试结果: 截断值 = 3; sigma_val = 3

trunc_val = 3; sigma_val = 1

trunc_val = 3; sigma_val = 9

【讨论】:

以上是关于为啥cv2和skimage之间的高斯滤波器不同?的主要内容,如果未能解决你的问题,请参考以下文章

为啥安装skimage后里面没有.util.montage模块

python库skimage 实现canny边缘探测

python库skimage 应用canny边缘探测算法

非对称高斯滤波器 - 水平和垂直滤波器的不同尺寸和 STD

OpenCV滤波器 龙门石窟篇Python-Open_CV系列(均值滤波器中值滤波器高斯滤波器双边滤波器)

使用cv2.Sobel()cv2.Scharr()cv2.Laplacian()寻找图像的梯度边缘