为啥cv2和skimage之间的高斯滤波器不同?
Posted
技术标签:
【中文标题】为啥cv2和skimage之间的高斯滤波器不同?【英文标题】:Why is Gaussian Filter different between cv2 and skimage?为什么cv2和skimage之间的高斯滤波器不同? 【发布时间】:2016-07-15 19:04:32 【问题描述】:我有一张图像,我使用cv2.GaussianBlur
和skimage.gaussian_filter
库应用了高斯模糊,但我得到了明显不同的结果。我很好奇为什么,以及如何使skimage
看起来更像cv2
。我知道skimage.gaussian_filter
是scipy.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模块