I have a picture of a bunch of sphere, and I want to calculate the centroid of each sphere on the plane and generate the Voronoi diagram for it. But

(1) In the picture the sphere are contacting each other, so they become one component when binarized. This is undesired.

(2) Some of the spheres are out of focus, thus darker. This makes (1) harder to deal with.

Is there an image transformation where I can break up two white regions that's "barely connected"? Thanks!

Some spheres

  • $\begingroup$ @Pickett Yeah I found that post and played with some parameters there, WatershedComponents tend to partition the background into multiple components probably because this image is packed. And still I can't find a proper threshold for Binarize to break up the spheres without losing some darker ones on the left. $\endgroup$
    – arax
    Oct 10, 2015 at 11:23
  • $\begingroup$ Sorry, I accidentally removed my comment because it didn't show me your comment and I wanted to add something to mine. This is the post I linked to. It can find the maximums, even if WatershedComponents doesn't work. This should be enough to generate the Voronoi diagram, is this enough for you? $\endgroup$
    – C. E.
    Oct 10, 2015 at 11:25
  • 1
    $\begingroup$ @Pickett Thanks for the information, I think the main problem is that I can't find a proper threshold for Binarize to break apart the joining spheres without losing some darker ones on the left. $\endgroup$
    – arax
    Oct 10, 2015 at 12:39
  • 1
    $\begingroup$ Here's a solution: ImageCorrelate[image, GaussianMatrix[3]] // MaxDetect // ComponentMeasurements[#, "Centroid"][[All, 2]] & // VoronoiMesh // HighlightMesh[#, {Style[1, Red], Style[2, Opacity[0]]}] & // Show[image, #] &. The sequence of operations, reading left to right along the processing pipeline, is smooth to reduce noise, detect peaks, find centroids of peaks, convert to Voronoi mesh, highlight the mesh itself but make its cells invisible, overlay the mesh on the original image. $\endgroup$ Oct 10, 2015 at 15:22

2 Answers 2


One very general way to solve this kind of problem is to use GeodesicDilation to find the peaks.

The intuition behind GeodesicDilation is that you dilate an image, then "clip" it using a mask image, i.e. for every pixel in the mask and the dilated image, you choose the darker of the two pixels. So the mask limits the growth of the dilation. This is repeated until convergence.

To find peaks, you start with "reduced" version of the source image, i.e. you subtract a constant from the image or multiply it by a constant, then dilate it using the original image as mask. In 1d, the result looks like this:

enter image description here

As you can see, the reconstruction (the green line) grows in every iteration, until it reaches the "borders" of the source image. Now, if you subtract this reconstruction from the source image:

enter image description here

...everything but the peaks will be zero.

In 2d:

img = ColorConvert[Import["https://i.stack.imgur.com/4gWnl.png"], "Grayscale"];    
γ = 0.5;
plateu = GeodesicDilation[ImageMultiply[img, γ], img];
bin = Binarize[ImageDifference[plateu, img], 0];
bin = Erosion[bin, 1]

enter image description here

I've used Erosion to remove small 1-pixel peaks and to separate close peaks, but you might want to play with different alternatives (e.g. the watershed-based algorithm @Pickett linked to - now that you have a marker image, that should work fine.)

Final step: Find centroids and draw the Voronoi cells:

centroids = ComponentMeasurements[bin, "Centroid"][[All, 2]];
v = VoronoiMesh[centroids];
   GraphicsComplex[MeshCoordinates[v], MeshCells[v, 1]]}]]

enter image description here


You can also separate the spheres using Erosion after the Binarize, and Watershed to visualize the regions about each sphere.

img = Erosion[
    "Grayscale"], 1]
water = Image[WatershedComponents[ColorNegate[img]]]
ColorCombine[{water, img, img}, "RGB"]

enter image description here


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.