9

I am trying to count the number of drops in this image and the coverage percentage of the area covered by those drops. I tried to convert this image into black and white, but the center color of those drops seems too similar to the background. So I only got something like the second picture. Is there any way to solve this problem or any better ideas? Thanks a lot.

source image

converted image

Will
  • 2,163
  • 1
  • 22
  • 22
JIAHAO HUANG
  • 189
  • 1
  • 2
  • 4
  • 1
    You can use [Hough Circles](http://docs.opencv.org/2.4/modules/imgproc/doc/feature_detection.html?highlight=houghcircles#houghcircles) for most of them – Pedro Jul 27 '16 at 17:21

4 Answers4

13

You can fill the holes of your binary image using scipy.ndimage.binary_fill_holes. I also recommend using an automatic thresholding method such as Otsu's (avaible in scikit-image).enter image description here

from skimage import io, filters
from scipy import ndimage
import matplotlib.pyplot as plt

im = io.imread('ba3g0.jpg', as_grey=True)
val = filters.threshold_otsu(im)
drops = ndimage.binary_fill_holes(im < val)
plt.imshow(drops, cmap='gray')
plt.show()

For the number of drops you can use another function of scikit-image

from skimage import measure
labels = measure.label(drops)
print(labels.max())

And for the coverage

print('coverage is %f' %(drops.mean()))
7

I used the following code to detect the number of contours in the image using OpenCV and python.

import cv2
import numpy as np
img = cv2.imread('ba3g0.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,127,255,1)
contours,h = cv2.findContours(thresh,1,2)
for cnt in contours:
    cv2.drawContours(img,[cnt],0,(0,0,255),1)

Result For further removing the contours inside another contour, you need to iterate over the entire list and compare and remove the internal contours. After that, the size of "contours" will give you the count

ma9
  • 158
  • 11
Saurav
  • 597
  • 10
  • 24
  • Hi I am getting error for line: contours,h = cv2.findContours(thresh,1,2) ValueError: too many values to unpack (expected 2)" – Ajay Sant Sep 11 '17 at 14:52
  • I think I am getting the abouve error because of Opencv version diffrence. Here is the new version : im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) – Ajay Sant Sep 11 '17 at 15:26
  • you might probably be using python 3.x with opencv 3. correct? – Saurav Sep 12 '17 at 05:18
2

The idea is to isolate the background form the inside of the drops that look like the background. Therefore i found the connected components for the background and the inside drops took the largest connected component and change its value to be like the foreground value which left me with an image which he inside drops as a different value than the background. Than i used this image to fill in the original threshold image. In the end using the filled image i calculated the relevant values

import cv2
import numpy as np
from matplotlib import pyplot as plt

# Read image
I = cv2.imread('drops.jpg',0);

# Threshold
IThresh = (I>=118).astype(np.uint8)*255

# Remove from the image the biggest conneced componnet

# Find the area of each connected component
connectedComponentProps = cv2.connectedComponentsWithStats(IThresh, 8, cv2.CV_32S)

IThreshOnlyInsideDrops = np.zeros_like(connectedComponentProps[1])
IThreshOnlyInsideDrops = connectedComponentProps[1]
stat = connectedComponentProps[2]
maxArea = 0
for label in range(connectedComponentProps[0]):
    cc = stat[label,:]
    if cc[cv2.CC_STAT_AREA] > maxArea:
        maxArea = cc[cv2.CC_STAT_AREA]
        maxIndex = label


# Convert the background value to the foreground value
for label in range(connectedComponentProps[0]):
    cc = stat[label,:]
    if cc[cv2.CC_STAT_AREA] == maxArea:
        IThreshOnlyInsideDrops[IThreshOnlyInsideDrops==label] = 0
    else:
        IThreshOnlyInsideDrops[IThreshOnlyInsideDrops == label] = 255

# Fill in all the IThreshOnlyInsideDrops as 0 in original IThresh
IThreshFill = IThresh
IThreshFill[IThreshOnlyInsideDrops==255] = 0
IThreshFill = np.logical_not(IThreshFill/255).astype(np.uint8)*255
plt.imshow(IThreshFill)

# Get numberof drops and cover precntage
connectedComponentPropsFinal = cv2.connectedComponentsWithStats(IThreshFill, 8, cv2.CV_32S)
NumberOfDrops = connectedComponentPropsFinal[0]
CoverPresntage = float(np.count_nonzero(IThreshFill==0)/float(IThreshFill.size))

# Print
print "Number of drops = " + str(NumberOfDrops)
print "Cover precntage = " + str(CoverPresntage)
Amitay Nachmani
  • 3,259
  • 1
  • 18
  • 21
  • What language is this? MATLAB? Some context around your answer would be helpful. Also keep in mind the question was tagged with Python. A solution in a different language may not be usable by the person who asked the question. – Lex Scarisbrick Jul 28 '16 at 22:01
  • always returns 2 even than there is much more than 2 object – Rocketq Apr 26 '19 at 17:53
2

Solution

image = cv2.imread('image path.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# (thresh, blackAndWhiteImage) = cv2.threshold(gray, 127, 255, 
cv2.THRESH_BINARY)
plt.imshow(gray, cmap='gray')
blur = cv2.GaussianBlur(gray, (11, 11), 0)
plt.imshow(blur, cmap='gray')
canny = cv2.Canny(blur, 30, 40, 3)
plt.imshow(canny, cmap='gray')
dilated = cv2.dilate(canny, (1, 1), iterations=0) 
plt.imshow(dilated, cmap='gray')
(cnt, hierarchy) = cv2.findContours(
dilated.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
cv2.drawContours(rgb, cnt, -1, (0, 255, 0), 2)
plt.imshow(rgb)
print("No of circles: ", len(cnt))

gray scale img

Edge detection draw contours

Ali Hassan
  • 188
  • 4
  • 8