Reputation: 351
In Matlab, we can perform a watershed transform on the distance transform to separate two touching objects:
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?
Upvotes: 3
Views: 5092
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
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