Search code examples
pythonnumpymatplotlibobject-detectionimread

How to detect the coordinates of each 30 cells with pixels? (without using opencv, ideally without using any library)


I have an image of 30 cells. I would like to detect the 30 cells in this image by tracking specifically their nuclei (blue). I think the idea is to either group a certain number of blue pixels and consider it as one nucleus (30 total), or only count the bluest pixels (again, 30 total).

The following code gets the coordinates of all of the blue pixels.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

control = mpimg.imread('jpeg.jpg')
ys = control.shape[0]
xs = control.shape[1]
blue = np.nonzero(control[:,:,2])


print(blue[0], len(blue[0]))
print(blue[1], len(blue[1]))

plt.imshow(control,cmap = plt.get_cmap('gray'),interpolation='none')
plt.show()

This code returns:

[  0   0   0 ... 447 447 447] 19031
[112 113 114 ... 381 382 383] 19031

Clearly, 19031 is too big. I only want 30.

This is the image [1]: https://i.sstatic.net/VhX5o.jpg


Solution

  • What you're looking for are 30 blobs, rather than pixels. Using Hough Circles is nice and easy.

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    # load image
    img = plt.imread('VhX5o.jpg')
    
    # convert image to gray
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # median blur
    gray = cv2.medianBlur(gray, 5)
    
    # Detect circles
    circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 10,
              param1=200,
              param2=30,
              minRadius=0,
              maxRadius=0)
    
    print(len(circles))  # 30
    circles = np.uint16(np.around(circles))
    
    # filtered image returns just the (blue) nucleus blobs
    filt_img = np.zeros(img.shape)
    for i in circles[0,:]:
        cv2.circle(filt_img,(i[0],i[1]),2,(0,0,255), 3)
        cv2.circle(img,(i[0],i[1]),2,(0,0,255), 3)
    
    # plot filt_img
    plt.figure()
    plt.imshow(filt_img)
    
    # plot with circles drawn over original image
    plt.imshow(img)
    

    You can use the circle positions as the centroids for each of the nuclei.

    Hope that helps!

    For future cases, I'd also recommend scipy.ndimage.measurements.label() for detecting blobs.

    Blobs detected are overlayed onto original image If anyone could recommend me how to upload both images as part of this post that would be great!