# Automatically find small objects in picture and extract them as smaller images

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? Commented May 30, 2017 at 8:49
• 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
Commented 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. Commented Jun 3, 2017 at 3:19

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):