pythonimage-processingcentroid

Find centroid of many bright circles


I'm using Python to find the centroid of each bright point in the following image, to calculate the average distance between neighbours. scipy.ndimage.find_objects and scipy.ndimage.center_of_mass seem promising, but I don't see how to put everything together. Where can I find some references? I've done the task "manually" with Tracker, but I'd like to automate the process. Thank you.

enter image description here


Solution

  • We can probably solve it using scipy.ndimage.find_objects, but I prefer using cv2.connectedComponentsWithStats:


    Here is a code sample:

    import numpy as np
    import cv2
    from scipy.spatial.distance import cdist
    
    img = cv2.imread('spots.png', cv2.IMREAD_GRAYSCALE)  # Load image in Grayscale format.
    
    # Convert to binary - use manual threshold
    #_, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_OTSU) # Try automatic threshold
    _, thresh = cv2.threshold(img, 10, 255, cv2.THRESH_BINARY) # Try manual threshold
    
    # Find connected components with statistics
    nlabel, labels, stats, centroids = cv2.connectedComponentsWithStats(thresh, connectivity=8)
    
    test_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
    
    coords = []  # List of filtered centroieds
    
    # Draw a cross at the coordinates of the centroieds, for testing
    for i in range(1, nlabel):
        if (stats[i, cv2.CC_STAT_AREA] > 4):    # Only if area is grater than 4 pixels
            coords.append(centroids[i])  # Create new list of centroids
            x, y = centroids[i]  # Get the coordinate of the centroied
            x, y = int(round(x)), int(round(y))  # Round and cast to int
            cv2.drawMarker(test_img, (x, y), (120, 157, 187), markerType=cv2.MARKER_CROSS, markerSize=5, thickness=1, line_type=cv2.LINE_8)  # Draw cross at the centroied
    
    
    # Compute average distance between neighbors
    ################################################################################
    # https://stackoverflow.com/questions/51305370/calculating-average-distance-of-nearest-neighbours-in-pandas-dataframe
    # Find euclidean distance from every centroied to every other centroied.
    dist = cdist(coords, coords)
    
    dist[dist == 0] = 1.0e9  # Exclude the distance from centroied to itself (replace with large number).
    
    dist = np.sort(dist.ravel())  # Sort the distances
    
    # Each distance is the sorted list is duplicated twice - for point A to point B and from point B to point A.
    n_min_dists = len(coords)*2
    
    # Average of centroid to its nearest neighbor.
    avg_min_dist = np.average(dist[0:n_min_dists])
    
    # Assume distance to all neighbors is no more than 1.2*"distance to nearest neighbor" (we also want to exclude diagonals)
    neighbors_dists = dist[dist < avg_min_dist*1.2]
    
    average_distance_between_neighbors = np.average(neighbors_dists)
    
    print('average_distance_between_neighbors = ' + str(average_distance_between_neighbors))
    ################################################################################
    
    # Show images for testing
    cv2.imshow('thresh', thresh)
    cv2.imshow('test_img', test_img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    Test image:
    enter image description here


    average_distance_between_neighbors = 21.858534029025055