The
Is there a general approach that would work for this image as well. without custom fine-tuning and changing parameters?
CodePudding user response:
- Apply high contrast (maximum possible value)
- convert to black & white image using high threshold (I've used 250)
- min filter (value=8)
- max filter (value=8)
CodePudding user response:
One approach uses the fact that the background changes color only very slowly. Here I apply the gradient magnitude to each of the channels and compute the norm of the result, giving me an image highlighting the quicker changes in color. The watershed of this (with sufficient tolerance) should have one or more regions covering the background and touching the image edge. After identifying those regions, and doing a bit of cleanup we get these results (red line is the edge of the mask, overlaid on the input image):
I did have to adjust the tolerance, with a lower tolerance in the first case, more of the shadow is seen as object. I think it should be possible to find a way to set the tolerance based on the statistics of the gradient image, I have not tried.
There are no other parameters to tweak here, the minimum object area, 300, is quite safe; an alternative would be to keep only the one largest object.
This is the code, using DIPlib (disclaimer: I'm an author). out
is the mask image, not the outline as displayed above.
import diplib as dip
import numpy as np
img = dip.ImageRead('Pa9DO.png')
img = img[362:915, 45:877]
img = img(slice(0,2))
tol = 7
gm = dip.Norm(dip.GradientMagnitude(img))
lab = dip.Watershed(gm, connectivity=1, maxDepth=tol, flags={'correct','labels'})
ll = np.unique(np.concatenate((
np.unique(lab[:,0]),
np.unique(lab[:,-1]),
np.unique(lab[0,:]),
np.unique(lab[-1,:]))))
out = dip.Image(lab.Sizes(), dt='BIN')
out.Fill(1)
for l in ll:
if l != 0:
out = out - (lab == l)
out = dip.Opening(out, dip.SE(3, 'rectangular'))
out = dip.AreaOpening(out, filterSize=300)
dip.Overlay(img, dip.Dilation(out, 3) - out).Show()