Benchmarking : Smil vs scikit-image

Morphological Image Libraries

Watershed segmentation

This page shows how watershed segmentation is benchmarked. Both for binary and gray level images.

The goal here isn't to obtain a perfect segmentation but to evaluate performance of the whatershed segmentation in a simple setup.

As they're a little different, we compared a simple segmentation setup : as we do with Smil and as it's suggested at scikit-image documentation. See below.

Qualitative results may not be exactly the same but are quite near good. As in the real life, some parameters may be fine tunned to give optimal results, we've just made a gross tunning, using the recommended methode we've found at scikit-image web site.

For the presentation below, the images were post-processed in order to ease readability.

Segmentation of gray level images - qualitative results

Segmentation results

Original image Smil scikit-image
Image : astronaut.png
Image : lena.png
Image : tools.png
Image : bubbles.png
Image : hubble_EDF.png

Segmentation method

Basically, both methods are similar : denoising, gross contour detection by gradient following a watershed.

There were a little tunning on parameters h and sz for Smil and szg and sgo for scikit-image.

The scikit-image result shows the basins instead of contours. In order to ease comparison with Smil, a post processing was done only for visualisation purposes in this page. The elapsed time to do this transformation wasn't take into account into results.

Source code

Smil scikit-image
import smilPython as sp

se = sp.HexSE()

imOpen = sp.Image(imIn)
sp.open(imIn, imOpen, sp.HexSE(sz))

imGrad = sp.Image(imIn)
sp.gradient(imOpen, imGrad, se)

imMin  = sp.Image(imIn)
sp.hMinima(imGrad, h, imMin, se)

imLabel = sp.Image(imOpen, 'UINT16')
sp.label(imMin, imLabel)

sp.watershed(imGrad, imLabel, imOut, se)


  
import skimage.io as io
import skimage.morphology as skm
from scipy import ndimage as ndi
from skimage.segmentation import watershed
from skimage.filters import rank
import numpy as np

imIn = imIn.astype('uint8')
# denoise image
denoised = rank.median(imIn, skm.disk(szo))
# find continuous region (low gradient -
# where less than 10 for this image) --> markers
# disk(5) is used here to get a more smooth image
markers = rank.gradient(denoised, skm.disk(szg)) < 10
markers = ndi.label(markers)[0]
# local gradient (disk(2) is used to keep edges thin)
gradient = rank.gradient(denoised, skm.disk(2))
# process the watershed
labels = watershed(gradient, markers)
  
See : scikit-image plot_marked_watershed

Segmentation of binary images

Segmentation of binary images is needed when one may want to semarate overlapping regions.

From the test images, even if all images were benchmarked, only the two bellow meet this need.

Segmentation results

Original image Smil scikit-image
Image : cells.png
Image : coffee.png

Segmentation method

The watershed algorithm may be used to segment only gray level images. To use it on binary images, the trick is to use some distance transform to create a gray level image from the binary one, invert it and use the max of the distance image as markers.

For memory, the distance transform on a binary image replaces the value of each pixel by the distance to the nearest background pixel.

While the algorithm proposed by scikit-image uses the Euclidean distance, Smil uses the morphological distance evaluated with an hexagonal structuring element.

Source code

Smil scikit-image
import smilPython as sp

se = sp.HexSE()
imDist = sp.Image(imIn)
sp.distance(imIn, imDist)
sp.inv(imDist, imDist)
sp.watershed(imDist, imOut, se(4))
sp.inv(imOut, imOut)
sp.inf(imIn, imOut, imOut)






  
import skimage.morphology as skm
from scipy           import ndimage as ndi
from skimage.feature import peak_local_max
from skimage.segmentation import watershed
import numpy as np

dist = ndi.distance_transform_edt(imIn)
se = skm.selem.diamond(3)
coords = peak_local_max(dist,
                        footprint=se,
                        labels=imIn)
mask = np.zeros(dist.shape, dtype=bool)
mask[tuple(coords.T)] = True
markers, _ = ndi.label(mask)
labels = watershed(-dist, markers, mask=imIn)
  
See : scikit-image plot_watershed