[Image Segmentation] Traditional segmentation algorithm - watershed algorithm (including example display based on opencv)

1. Principle of Watershed Algorithm

The watershed algorithm regards the image as a terrain surface in geography, the high gray value area in the image is regarded as a mountain peak, and the low gray value area is regarded as a valley. Then image segmentation is realized.

If we pour water into the "valleys", the water level will gradually rise, and then the water from different valleys will come together. If we prevent the water from different valleys from converging, we need to build dams where the water flows may meet. We need to put The images are divided into two distinct sets: catchment basins and watershed lines. The dam we built is the watershed line, which is the segmentation of the original image.

But due to noise or any other irregularities in the image, this approach can give over-segmented results. So OpenCV implements a marker based watershed algorithm where you specify which are all valley points to merge and which are not. This is an interactive image segmentation. What we do is give different labels to objects we know. Mark the areas we are sure are foreground or objects with one color (or intensity), the areas we are sure are background or non-objects with another color, and finally the areas we are not sure about with 0. This is our mark. Then apply the watershed algorithm. Then our marker will be updated with the label we gave and the object's bounds will have a value of -1.



Second, the steps of using opencv to realize the watershed algorithm:

  1. To find an approximate estimate of the foreground of an image, one can use Otsu's binarization.
  2. The denoising of the original image O is realized by morphological opening operation.
  3. Get "determined background B" by dilation operation.
  4. Use the distance transformation function cv2.distanceTransform() to process the image, and perform threshold segmentation on the result to obtain "determined foreground F".
  5. Compute the unknown area UN. UN=O-B-F.
  6. Use cv.connectedComponents() to realize the labeling work of the original image O.
  7. Correct the annotation result of cv.connectedComponents().
  8. Use the watershed segmentation function cv.watershed() to complete the image segmentation.

3. Algorithm example

Let's take the coin image in contact with the following figure as an example to segment and obtain the range of coins.

1. Get an approximate estimate of the coin

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('coins.png')
assert img is not None, "file could not be read, check with os.path.exists()"
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(gray,0,255,cv.THRESH_BINARY_INV+cv.THRESH_OTSU)

2. Image denoising

# noise removal
kernel = np.ones((3,3),np.uint8)
opening = cv.morphologyEx(thresh,cv.MORPH_OPEN,kernel, iterations = 2)

3. Determine the background area

# sure background area
sure_bg = cv.dilate(opening,kernel,iterations=3)

4. Determine the foreground area

# Finding sure foreground area
dist_transform = cv.distanceTransform(opening,cv.DIST_L2,5)
ret, sure_fg = cv.threshold(dist_transform,0.7*dist_transform.max(),255,0)

5. Identify unknown areas

# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv.subtract(sure_bg,sure_fg)

6. Realize image annotation and repair processing

# Marker labelling
ret, markers = cv.connectedComponents(sure_fg)
# Add one to all labels so that sure background is not 0, but 1
markers = markers+1
# Now, mark the region of unknown with zero
markers[unknown==255] = 0

7. Apply the watershed algorithm to achieve segmentation

markers = cv.watershed(img,markers)
# The boundary region will be marked with -1.
img[markers == -1] = [255,0,0]

Note: In the above method, the determination of the unknown area by the distance function is for the case where the objects are in contact with each other. For the case where the objects are independent and not in contact with each other, the erosion operation can be performed after the expansion operation, and then the former is subtracted from the latter to obtain the unknown area. :

sure_bg = cv2.dilate(opening, kernel, iterations=3)  # sure background area
sure_fg = cv2.erode(opening, kernel, iterations=3)  # sure foreground area
unknown = cv2.subtract(sure_bg, sure_fg)  # unknown area

Tags: Algorithm OpenCV Computer Vision

Posted by Wuhtzu on Wed, 22 Mar 2023 01:42:50 +1030