4

I am trying to figure out the kernel being used in skimage.filters's laplace function. I know that a Laplacian filter is based on matrix convolution, but I just can't seem to make sense of the values produced by skimage.filters's laplace function.

This is an example:

>>> import numpy as np
>>> import skimage
>>> from skimage.filters import laplace
>>> x = [[2,3,2],[5,3,6],[3,7,3]]
>>> x = np.array(x)
>>> x
array([[2, 3, 2],
       [5, 3, 6],
       [3, 7, 3]])
>>> laplace(x, ksize=3)
array([[ 0.00000000e+00,  0.00000000e+00,  0.00000000e+00],
       [ 0.00000000e+00, -9.75781955e-19,  0.00000000e+00],
       [ 0.00000000e+00,  0.00000000e+00,  0.00000000e+00]])

If skimage.filters's laplace function uses a kernel/operator of

[[ 0,  1, 0],
 [ 1, -4, 1],
 [ 0,  1, 0]]

then according to matrix convolution, it should have produced the output of

[[ 0,  5, -1],
 [12, -9, 16],
 [ 0, 19, -1]]

instead of

[[ 0.00000000e+00,  0.00000000e+00,  0.00000000e+00],
 [ 0.00000000e+00, -9.75781955e-19,  0.00000000e+00],
 [ 0.00000000e+00,  0.00000000e+00,  0.00000000e+00]]

I am very confused on what kernel/operator skimage.filters's laplace function is using to have almost every output value so close to zero, such as -9.75781955e-19. I honestly don't think any reasonable kernel/operator could produce this output, so maybe I am just not understanding how Python's skimage.filters's laplace function is working...

Any help/comments/suggestions/insights to this question would be greatly appreciated. Thank you.

Ethan Yim
  • 85
  • 1
  • 6

1 Answers1

3

welcome to the scikit-image thread of Stack Overflow! The reason for this strange behaviour is that the dtype of x is int64, and the scikit-image laplace function calls img_as_float in order to do the computation in float numbers, but when casting the dtype it also divides the array by the maximum value of the origin dtype (here 2^63 - 1), hence the very small values. If you want to avoid this problem you can convert the image to float before passing it to laplace:

>>> x = x.astype(np.float)                                                
>>> filters.laplace(x)                                                    
array([[-4.,  2., -5.],
       [ 7., -9., 10.],
       [-6., 12., -7.]])

(the function uses the default boundary-condition mode of scipy.ndimage.convolve which is 'reflect').

Note that this behaviour (dividing by the max value of the dtype) is likely to change with scikit-image 1.0, precisely because we've noticed that it can be confusing for users as in your case.

  • Ah I see, thank you! That explains a lot. Just as a follow-up, is there a way to use the boundary-condition mode of `constant` instead of `reflect`? I know that `scipy.ndimage.laplace` allows me to do this, but I'm not sure how I can do this with `skimage.filters.laplace`. Also as a broader question, what are some pros and cons of using `skimage.filters.laplace` over `scipy.ndimage.laplace` ? – Ethan Yim Jun 21 '20 at 22:17
  • 1
    You unfortunately can't currently change the boundary mode, but we would accept a pull request to add this feature! We have a contributor guide here: https://scikit-image.org/docs/stable/contribute.html Alternatively, please create an issue at https://github.com/scikit-image/scikit-image/issues with the feature request! =) – Juan Jun 22 '20 at 04:39
  • I don't expect there'll be much difference between skimage's laplace and ndimage's. – Juan Jun 22 '20 at 04:45