8

I'm new to python and stuck..

I want to make a python script that allows me to separate adjacent particles on an image like this:

BEFORE

into separate regions like this:

AFTER

I was suggested to use the watershed method, which as far as I understand it would give me a something like this:

Watershed in 3D

EDIT Actually found out that this is distance transform and not watershed

Where I then could use a threshold to separate them.. Followed this openCV watershed guide but it only worked to cut out the particles. Was not able to "transform" the code to do what I want.

I then took another approach. Tried to use the openCV contours which gave me good contours of the particles. I have then been looking intensively for an easy way to perform polygon offset in order to shrink the edge like this:

Edge offset

Using the center from the offset contours (polygon) should give me the number of particles.. But I just haven been able to find a simple way to do edge offset / polygon shrinking with python.

Community
  • 1
  • 1
Norfeldt
  • 8,272
  • 23
  • 96
  • 152
  • 2
    You might be interested by : http://stackoverflow.com/questions/10313602/reshaping-noisy-coin-into-a-circle-form/10324759#10324759 Distance transform might be useful in your case. As far as I understand it, polygon shrinking could be equivalent to thresholding the distanceMap, no ? – Quentin Geissmann Oct 07 '12 at 19:57
  • +1 for Quentin link, distanceTransform is what you are looking for and the example is very similar to your problem – remi Oct 08 '12 at 13:01
  • The distance transform acutually got me very far, but then @Nicolas Barbey posted a really nice and easy solution. – Norfeldt Oct 08 '12 at 13:50

1 Answers1

7

Here is a script using numpy, scipy and the scikit-image (aka skimage). It makes use of local maxima extraction and watershading plus labeling (ie connected components extraction).

import numpy as np
import scipy.misc
import scipy.ndimage
import skimage.feature
import skimage.morphology

# parameters
THRESHOLD = 128

# read image
im = scipy.misc.imread("JPh65.png")
# convert to gray image
im = im.mean(axis=-1)
# find peaks
peak = skimage.feature.peak_local_max(im, threshold_rel=0.9, min_distance=10)
# make an image with peaks at 1
peak_im = np.zeros_like(im)
for p in peak:
    peak_im[p[0], p[1]] = 1
# label peaks
peak_label, _ = scipy.ndimage.label(peak_im)
# propagate peak labels with watershed
labels = skimage.morphology.watershed(255 - im, peak_label)
# limit watershed labels to area where the image is intense enough
result = labels * (im > THRESHOLD)
Nicolas Barbey
  • 6,639
  • 4
  • 28
  • 34
  • Thank you very much for this answer - would you mind telling me what scipy.ndimage.label does and how the peak_label is used in watershed? – Norfeldt Oct 08 '12 at 14:26
  • 1
    scipy.ndimage.label performs connected component labeling (it assigns a number to a cluster of 1 pixels surounded by zeros). peak_label is used to set seeds for the watershed (afaik regions are grown from seeds by the watershed algorithm). – Nicolas Barbey Oct 08 '12 at 15:52
  • Thank you once again @Nicolas for clearing that out. -just a silly question: is it possible to label with random numbers (seems like it is incrementing)? – Norfeldt Oct 08 '12 at 16:42
  • 1
    You can change label number afterwards doing something like result[result == 3] = 5 for instance. Looping through label indexes and np.random.rand will give you a random number. – Nicolas Barbey Oct 08 '12 at 19:00
  • 1
    If you feel my answer is useful and working for you, don't hesitate to vote for it ;) – Nicolas Barbey Oct 08 '12 at 19:01
  • Checked your answer as the solution.. I just forgot to vote - sorry about that – Norfeldt Oct 08 '12 at 20:34