We plan to create photographs of small particles using a microscope. These small elements tend to be convex and they are not supposed to touch each other. The background can be a little bit noisy, but there should be a large contrast between the background and the elements (we aim to create pictures like the example). I would like to extract all the individual elements, crop them and save as individual pictures. I need a tool, that is able to solve this problem also in cases where the border between an element and the background is less defined.

After I extracted all the photographs I run a Matlab script on each picture.

Operating system: preferably Linux, but Windows is also ok.

I prefer the selection to be as automatic as possible (to be able to run the algorithm on hundreds of pictures).

Sample image:

  • You will select manually? Or should the tool somehow guess what you consider an "individual element"? What operating system?
    – Nicolas Raoul
    May 30, 2017 at 8:49
  • 1
    Will there always be a plain, monchrome background? Can the objects overlap? Maybe if you tell us how you plan to use it? There is very little informaton here on which to base help
    – Mawg
    Jun 2, 2017 at 7:52
  • This problem starts easy but gets very hard as complexity is added (such as busy backgrounds), and no preexisting shrink-wrapped solution likely exists even for the easy instances. However, you could probably get pretty far with a simple-minded algorithm implemented in 30 or so lines of Python. Jun 3, 2017 at 3:19

1 Answer 1


Here's a Python 3 program that distinguishes object pixels from non-object pixels by simply thresholding the red channel at 128.

import sys
from PIL import Image

orig = Image.open(sys.argv[1])
width = orig.width
height = orig.height

# The workspace will be a list of lists of bools, where True
# means an object pixel.
workspace = list(orig.getdata())
workspace = list(zip(*(workspace[i * width : (i + 1) * width] for i in range(height))))
workspace = [[r < 128 for r, _, _ in col] for col in workspace]

n_objects = 0

for x in range(width):
    for y in range(height):
        if workspace[x][y]:
            n_objects += 1
            # Start a bounding box at this pixel.
            x1, x2, y1, y2 = x, x, y, y
            # Try growing the bounding box in each direction,
            # one pixel at a time, until none of the four
            # directions will get us another object pixel.
            while True:
                if   x1 > 0          and any(workspace[x1 - 1][yt    ] for yt in range(y1, y2 + 1)):  x1 -= 1
                elif x2 < width  - 1 and any(workspace[x2 + 1][yt    ] for yt in range(y1, y2 + 1)):  x2 += 1
                elif y1 > 0          and any(workspace[xt    ][y1 - 1] for xt in range(x1, x2 + 1)):  y1 -= 1
                elif y2 < height - 1 and any(workspace[xt    ][y2 + 1] for xt in range(x1, x2 + 1)):  y2 += 1
                else: break
            # Save everything in the bounding box as a new image.
            orig.crop((x1, y1, x2, y2)).save("out/obj-{:04d}-{:04d}.png".format(x1, y1))
            # Clear the workspace here so we don't re-process this object.
            for xt in range(x1, x2 + 1):
                for yt in range(y1, y2 + 1):
                    workspace[xt][yt] = False

print("Extracted", n_objects, "objects.")

Here's what the resulting files look like (not shown at equal scales):

The objects as separate images

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.