Mehrnoush
Mehrnoush

Reputation: 351

Watershed Transform of Distance Image with OpenCV

In Matlab, we can perform a watershed transform on the distance transform to separate two touching objects:

enter image description here enter image description here

The first image above is the image with touching objects that we wish to separate. The second image is its distance transform.

So, if the black and white image is called img, in Matlab we can do:

D = -bwdist(~img); 
L = watershed(D);

Now to do the same thing with openCV: OpenCV has a marker based watershed segmentation function. It appears that to perform the same task of separating two touching objects with openCV, one would need to provide markers for both objects and for the background.

img = np.zeros((400, 400), np.uint8)
cv2.circle(img, (150, 150), 100, 255, -1)
cv2.circle(img, (250, 250), 100, 255, -1)

dist = cv2.distanceTransform(img, cv2.cv.CV_DIST_L2, cv2.cv.CV_DIST_MASK_PRECISE)
dist3 = np.zeros((dist.shape[0], dist.shape[1], 3), dtype = np.uint8)
dist3[:, :, 0] = dist
dist3[:, :, 1] = dist
dist3[:, :, 2] = dist

markers = np.zeros(img.shape, np.int32)
markers[150,150] = 1 # seed for circle one
markers[250, 250] = 2 # seed for circle two
markers[50,50] =  3 # seeds for background

cv2.watershed(dist3, markers)

In the following image, you see the markers image after watershed was performed. The original black and white img is superimposed on it in red. The problem is that the object boundaries in the resulting markers image are not the same as the original image. How can I ensure that object boundaries stay the same?

enter image description here

Upvotes: 3

Views: 5092

Answers (2)

Dani
Dani

Reputation: 99

I am having the same issue but it could be solved by inverting the distance image with bitwise_not. Remember that "high intensity denotes peaks and hills while low intensity denotes valleys". Why your original distance map produces these forms at all is a mystery to me though. I guess the water flows downhill from your "hills" until it meats the background instead of filling a basin.

Upvotes: 0

Bear
Bear

Reputation: 31

You'd berrer get to know what readlly happen in the function of watershed. it starts flooding with its seeds and put the coordinate and the gradient of their neighbours in a priority queue.

As you know, when you apply distanceTransform on img, the gradient of circles becomes 0 or 1, but the background always be 0;

So, now you have 3 seeds, and flooding starts working: background(seed3), neighbours(seed1), neighbours(seed2), they can work in turn until the seed1 or seed2 meet their gradient 1; then just seed3 can carry on working.

When the seed3 meet the boundaries of circles, its gradient becomes 1, now they can work in turn again.

So if you want to ensure that object boundaries stay the same, you'd better increase the gradient when seed3 meet the boundaries of circles.

just like:

dist = cv2.distanceTransform(img, cv2.cv.CV_DIST_L2, cv2.cv.CV_DIST_MASK_PRECISE)
dist[dist > 0] += 2.0

here is the result

...There are some problems in it(when all the gradient in the queue is 1, which one will pop first, which one will pop second)

Upvotes: 3

Related Questions