pythonpython-3.xopencvimage-processingopencv3.0

How to count small seeds in an image with a noisy background


I have an image of tomato seeds, The number of seeds in the image are 32. I am trying to count those seeds using OpenCV. The image has some noise which is causing wrong counts.

here

My approach is to count the centers of the circles. The general idea is to threshold and then flood fill from a background pixel to fill in the background (flood fill works like the paint bucket tool in photo editing apps), that way you only see the centers.

Here is my code:

img = cv2.imread('tomato.jpg', 0)
dst = cv2.fastNlMeansDenoisingColored(img,None,10,10,7,21)
img = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)
seed_pt = (25, 25)
fill_color = 0
mask = np.zeros_like(img)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
for th in range(60, 120):
    prev_mask = mask.copy()
    mask = cv2.threshold(img, th, 255, cv2.THRESH_BINARY)[1]
    mask = cv2.floodFill(mask, None, seed_pt, fill_color)[1]
    mask = cv2.bitwise_or(mask, prev_mask)
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)

n_centers = cv2.connectedComponents(mask)[0] - 1
print('There are %d cells in the image.'%n_centers)

The output is:

There are 34 cells in the image.

the original count is 32.

How can I fix this?


Solution

  • My solution is to combine RGB mask, with HSV mask using inRange, there still some points to be improved, but the concept is to do masking in more than one color-space:

    output: There are 32 cells in the image.

    seeds

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    def HSV_mask(img, hsv_range):
        """
        Function: check_HSV, to get the ratio of the marked pixels of specific color in the frame,
          using HSV space.
        ---
        Parameters:
        @param: img, ndarray, image frame of shape [height, width, 3(BGR)].
        @param: hsv_range, tuple of 6, defines the hsv ranges low high for Hue, Saturation, and Value.
        ---
        @return: mask, ndarray, the masked image for the given color.
        """
        hsv_img = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
    
        mask=cv2.inRange(hsv_img,hsv_range[0::2],hsv_range[1::2])
    
        return mask
    
    
    img = cv2.imread('001.jpg')
    
    # RGB Mask
    rgb_mask = img.copy()
    rgb_mask[rgb_mask[...,0]>140]=(0, 0, 0)
    rgb_mask[rgb_mask[...,1]>140]=(0, 0, 0)
    rgb_mask[rgb_mask[...,2]<70]=(0, 0, 0)
    rgb_mask[np.sum(rgb_mask, axis=2)>0]=(1, 1, 1)
    
    masked = img*rgb_mask
    
    
    # HSV Mask
    H1 = (1, 8)
    H2 = (175, 180)
    S = (40, 255)
    V = (100, 150)
    
    myRange1 = (H1 + S + V)
    myRange2 = (H2 + S + V)
    hsv_mask1 = HSV_mask(masked, myRange1)
    hsv_mask2 = HSV_mask(masked, myRange2)
    hsv_mask = hsv_mask1#cv2.bitwise_or(hsv_mask1, hsv_mask2)
    
    # Morphology
    k = 7
    # Get the structuring element:
    maxKernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, ksize=(k, k))
    # Perform closing:
    mask = cv2.morphologyEx(hsv_mask, cv2.MORPH_CLOSE, maxKernel, None, None, 1, cv2.BORDER_CONSTANT)
    
    
    # Median Filter
    filteredMask = cv2.medianBlur(mask,5)
    
    plt.figure(num='Tomato Seeds')
    
    plt.subplot(221)
    plt.imshow(img[...,[2, 1, 0]])
    plt.title('Original')
    
    plt.subplot(222)
    plt.imshow(masked[...,[2,1,0]])
    plt.title('rgb masked')
    
    plt.subplot(223)
    plt.imshow(hsv_mask)
    plt.title('HSV mask')
    
    plt.subplot(224)
    plt.imshow(filteredMask)
    plt.title('Median Filter')
    
    plt.show()
    
    n_centers = cv2.connectedComponents(filteredMask)[0] - 1
    print('There are %d cells in the image.'%n_centers)