waldol1
waldol1

Reputation: 1881

Why is Gaussian Filter different between cv2 and skimage?

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:

Original Image

Here is the cv2 version (appears blurrier):

cv2 image

Here is the skimage/scipy version (appears sharper):

skimage version

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

Answers (5)

Stef
Stef

Reputation: 30679

Both opencv and scipy allow specifying sigma which has identical meaning in both libraries. The kernel size is determined differently:

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()

enter image description here

The remaining differences (see 4th plot) are caused by floating point calcuation and different resulting datatype (uint8 vs float64).

Upvotes: 3

Zimeng Zhao
Zimeng Zhao

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 enter image description here

trunc_val = 3; sigma_val = 1 enter image description here

trunc_val = 3; sigma_val = 9 enter image description here

Upvotes: 0

Haotao Wang
Haotao Wang

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

user2348114
user2348114

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

tfv
tfv

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

Related Questions