Encuentre automáticamente objetos pequeños en la imagen y extráigalos como imágenes más pequeñas

Planeamos crear fotografías de partículas pequeñas usando un microscopio. Estos pequeños elementos tienden a ser convexos y se supone que no deben tocarse entre sí. El fondo puede ser un poco ruidoso, pero debe haber un gran contraste entre el fondo y los elementos (nuestro objetivo es crear imágenes como las del ejemplo). Me gustaría extraer todos los elementos individuales, recortarlos y guardarlos como imágenes individuales. Necesito una herramienta que pueda resolver este problema también en los casos en que el borde entre un elemento y el fondo está menos definido.

Después de extraer todas las fotografías, ejecuté un script de Matlab en cada imagen.

Sistema operativo: preferiblemente Linux, pero Windows también está bien.

Prefiero que la selección sea lo más automática posible (para poder ejecutar el algoritmo en cientos de imágenes).

Imagen de muestra:

¿Seleccionará manualmente? ¿O debería la herramienta adivinar de alguna manera lo que considera un "elemento individual"? ¿Qué sistema operativo?
¿Habrá siempre un fondo liso y monocromático? ¿Pueden los objetos superponerse? ¿Quizás si nos dices cómo planeas usarlo? Aquí hay muy poca información en la que basar la ayuda.
Este problema comienza fácil pero se vuelve muy difícil a medida que se agrega complejidad (como fondos ocupados), y es probable que no exista una solución retractilada preexistente incluso para las instancias fáciles. Sin embargo, probablemente podría llegar bastante lejos con un algoritmo simple implementado en unas 30 líneas de Python.

Respuestas (1)

Aquí hay un programa de Python 3 que distingue los píxeles de los objetos de los píxeles que no son objetos simplemente ajustando el umbral del canal rojo a 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.")

Así es como se ven los archivos resultantes (no se muestran a escalas iguales):

Los objetos como imágenes separadas