Segmentation of small particles using Watershed and Distance Transform

395 views Asked by At

I’m trying to separate the dust particles in a picture. To do so, I was thinking of using the watershed algorithm (it’s the first time for me using it). The picture I’m working with is the following: Original picture

Following the tutorial on the OpenCV documentation named “Image Segmentation with Distance Transform and Watershed Algorithm” (link), I wrote the following python code:

img = cv2.imread(img_path)

# sharpen the image to acute the edges of the foreground objects
kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]], dtype=np.float32)
imgLaplacian = cv2.filter2D(img, cv2.CV_32F, kernel)
sharp = np.float32(img)
imgResult = sharp - imgLaplacian

# Binarization
gray = cv2.cvtColor(imgResult, cv2.COLOR_BGR2GRAY)
_, img_bin = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
dist = cv2.distanceTransform(img_bin, cv2.DIST_L2, 5)
# Normalize the distance image for range = {0.0, 1.0}
# so we can visualize and threshold it
cv2.normalize(dist, dist, 0, 1.0, cv2.NORM_MINMAX)

# Threshold to obtain the peaks
# This will be the markers for the foreground objects
_, dist = cv2.threshold(dist, min_norm, max_norm, cv2.THRESH_BINARY)
# Dilate a bit the dist image
dist = cv2.dilate(dist, np.ones((3,3), dtype=np.uint8))

# Create the CV_8U version of the distance image
# It is needed for findContours()
dist_8u = dist.astype('uint8')
# Find total markers
contours, _ = cv2.findContours(dist_8u, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Create the marker image for the watershed algorithm
markers = np.zeros(dist.shape, dtype=np.int32)
# Draw the foreground markers
for i in range(len(contours)):
    cv2.drawContours(markers, contours, i, (i+1), -1)
cv2.watershed(imgResult, markers)
img[markers == -1] = [0,0,255] # Draw the contours

ImgResult is the visualized as following: enter image description here

As you can see in the picture, many of the bigger particles are still considered as just one particle, while some of the smaller particles are not even considered.

I’m sure there’s something I’m missing, but I can’t figure out what.

Let me know if something is not clear or if the pictures are not understandable. (I used another function to make the contours more visible, the code posted generates a red border as large as a pixel).

P.S.

I also tried to follow “Image Segmentation with Watershed Algorithm” (link) tutorial from OpenCV docs. The results are a little bit better for the bigger particles (they get separated), but it doesn’t identify many particles (I assume due to the morphological operations). The picture follows.

enter image description here

P.P.S.

In both cases, the algorithm generates a weird border around the whole picture.

Desired result

The desired output is something similar to this: enter image description here

Drawing function

To draw on the pictures I used:

for label in no.unique(markers):
    if label == 0:
         continue
    mask = np.zeros(image.shape[:2], dtype="uint8")
    mask[markers == label] = 255
    cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    c = max(cnts, key=cv2.contourArea)
    cv2.drawContours(image, [c], -1, (36,255,12), 2)
0

There are 0 answers