Reputation: 1881
I've got an image that I apply a Gaussian Blur to using both cv2.GaussianBlur
and skimage.gaussian_filter
libraries, but I get significantly different results. I'm curious as to why, and what can be done to make skimage
look more like cv2
. I know skimage.gaussian_filter
is a wrapper around scipy.scipy.ndimage.filters.gaussian_filter
. To clearly state the question, why are the two functions different and what can be done to make them more similar?
Here is my test image:
Here is the cv2
version (appears blurrier):
Here is the skimage
/scipy
version (appears sharper):
Details:
skimage_response = skimage.filters.gaussian_filter(im, 2, multichannel=True, mode='reflect')
cv2_response = cv2.GaussianBlur(im, (33, 33), 2)
So sigma=2 and the size of the filter is big enough that it shouldn't make a difference. Imagemagick covnert -gaussian-blur 0x2
visually agrees with cv2
.
Versions: cv2
=2.4.10, skimage
=0.11.3, scipy
=0.13.3
Upvotes: 10
Views: 19220
Reputation: 30679
Both opencv
and scipy
allow specifying sigma
which has identical meaning in both libraries. The kernel size is determined differently:
scipy
it's derived from the truncate
parameter as int(truncate * sigma + 0.5)
opencv
it can be specified independently of sigma
(if omitted, it's calculated differently from sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8
.So to get identical results you need to explicitly specify both the kernel size and sigma:
import cv2
from skimage.filters import gaussian
import matplotlib.pyplot as plt
img = cv2.imread('bg4dZ.png', cv2.IMREAD_GRAYSCALE)
truncate = 4
sigma = 2
radius = int(truncate * sigma + 0.5)
ksize = 2 * radius + 1
opencv = cv2.GaussianBlur(img, (ksize, ksize), sigma, borderType=cv2.BORDER_REFLECT)
scipy = gaussian(img, sigma, truncate=truncate, preserve_range=True, mode='reflect')
fig, axs = plt.subplots(ncols=4, layout='constrained', figsize=(16, 4))
axs[0].imshow(img, cmap='gray')
axs[1].imshow(opencv, cmap='gray')
axs[2].imshow(scipy, cmap='gray')
diff = opencv - scipy
diff = axs[3].imshow(diff, cmap='seismic', vmin=diff.min(), vmax=-diff.min())
fig.colorbar(diff, shrink=.95)
for ax in axs:
ax.set_axis_off()
The remaining differences (see 4th plot) are caused by floating point calcuation and different resulting datatype (uint8
vs float64
).
Upvotes: 3
Reputation: 153
According to [Scipy0.15.1 API][1]
:
scipy.ndimage.filters(img, sigma=sigma, truncate = 4.0)
It setsthe Gauss filter with the kernel size in truncate * sigma. In this understanding, the following two fuctionc will give the same results on gray scale image:
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)
Some Test results:
trunc_val = 3; sigma_val = 3
Upvotes: 0
Reputation: 409
These two are equal:
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)
The whole Gaussian kernel is defined by sigma only. But which part of gaussian kernel do you use to blur the image is defined by truncate
(in skimage) or ksize
(in opencv).
Upvotes: 4
Reputation: 167
If anyone is curious about how to make skimage.gaussian_filter() match Matlab's equivalent imgaussfilt() (the reason I found this question), pass the parameter 'truncate=2' to skimage.gaussian_filter(). Both skimage and Matlab calculate the kernel size as a function of sigma. Matlab's default is 2. Skimage's default is 4, resulting in a significantly larger kernel by default.
Upvotes: 7
Reputation: 6259
For GaussianBlur, you are using a rather large kernel (size=33), which causes a lot of smoothing. Smoothing will depend drastically on you kernel size. With your parameters each new pixel value is "averaged" in a 33*33 pixel "window".
A definition of cv2.GaussianBlur can be found here http://docs.opencv.org/3.1.0/d4/d13/tutorial_py_filtering.html#gsc.tab=0
In contrast, skimage.filters.gaussian seems to work on a smaller kernel. In skimage, the "size" is defined by sigma which is related to kernel size as described here: https://en.wikipedia.org/wiki/Gaussian_filter
Definition can be found here: http://scikit-image.org/docs/dev/api/skimage.filters.html#skimage.filters.gaussian
In order to get corresponding results, you'd have to work with a smaller kernel for OpenCV.
Furthermore, for both libraries, I'd strongly recommend to use up to date library versions.
Upvotes: 3