How to find the location of several objects in an image with Python


Assuming that i have an image like

in which objects do not overlap and there is space between them, i run these lines of codes:

    import matplotlib.pyplot as plt
    import numpy as np
    from skimage.color import rgb2gray
    from skimage import io, img_as_ubyte
    from skimage.filters import threshold_otsu

    image = img_as_ubyte(rgb2gray(io.imread("sc.png")))
    threshold = threshold_otsu(image)
    thresh_img = image < threshold

The result is this image

I would like to know if there is any way to find those x1, y1 (assuming this is the point of far left on the top) and x2, y2 (assuming this is the point of far right on bottom) to separate each object based on those values to get a small images like this

I tried some lines on my own like this:

     ind = np.nonzero(image.any(axis=0))[0] # indices of non empty columns 
     width = ind[-1] - ind[0] + 1
     ind = np.nonzero(image.any(axis=1))[0] # indices of non empty rows
     height = ind[-1] - ind[0] + 1

and this one with scipy:

    from scipy import ndimage

    # Label objects
    labeled_image, num_features = ndimage.label(image)
    # Find the location of all objects
    objs = ndimage.find_objects(labeled_image)
    # Get the height and width
    measurements = []
    for ob in objs:
        measurements.append((int(ob[0].stop - ob[0].start), int(ob[1].stop - ob[1].start)))

But i am not getting anywhere. Can someone help?

From your description it appears that your objects never overlap across the whole x axis. In that case, you can compress that dimension, like so:

row_img = np.any(thresh_img, axis=0)

Then, it’s only a matter of finding where in that axis does the signal changes:

change = np.flatnonzero(np.diff(row))

You can then use that list to crop the larger image (take all rows, only crop by columns):

subimages = [image[:,xi+1:xf] for xi, xf in zip(change[0::2], change[1::2])]
1 Like

Using skimage.measure.regionprops you can also directly obtain the cropped labelled objects and/or intensity objects via the image and/or intensity_image properties. So no need to do it manually. Alternatively you can also recover the bounding boxes for all objects via the bbox property.