6

I am currently doing circle detection on images look like this one, but some of the drops merge and form some irregular shapes(red marks in the original image). I am using houghcircle function in opencv to detect circles. For those irregular shapes, the function can only detect them as several small circles, but I really want the program to consider irregular shape as an entire big shape and get a big circle like I draw in my output image.

Original image

Output image

My code will detect all the circles and get diameters of them.

Here is my code:

def circles(filename, p1, p2, minR, maxR):
# print(filename)
img  = cv2.imread(filename, 0)
img = img[0:1000, 0:1360]
l = len(img)
w = len(img[1])

cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)

circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 25,
                            param1 = int(p1) ,param2 = int(p2), minRadius = int(minR), maxRadius = int(maxR))

diameter = open(filename[:-4] + "_diamater.txt", "w")
diameter.write("Diameters(um)\n")
for i in circles[0,:]:
    diameter.write(str(i[2] * 1.29 * 2) + "\n")

count = 0
d = []
area = []
for i in circles[0,:]:
    cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
    cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
    count += 1
    d += [i[2]*2]
    area += [i[2]*i[2]*pi*1.286*1.286]

f = filename.split("/")[-1]
cv2.imwrite(filename[:-4] + "_circle.jpg", cimg)

# cv2.imwrite("test3/edge.jpg", edges)
print "Number of Circles is %d" % count

diaM = []
for i in d:
    diaM += [i*1.286]

bWidth = range(int(min(diaM)) - 10, int(max(diaM)) + 10, 2)

txt = '''
Sample name: %s 
Average diameter(um): %f     std: %f
Drop counts: %d
Average coverage per drop(um^2): %f     std: %f
''' % (f, np.mean(diaM), np.std(diaM), count, np.mean(area), np.std(area))

fig = plt.figure()
fig.suptitle('Histogram of Diameters', fontsize=14, fontweight='bold')
ax1 = fig.add_axes((.1,.4,.8,.5))
ax1.hist(diaM, bins = bWidth)
ax1.set_xlabel('Diameter(um)')
ax1.set_ylabel('Frequency')
fig.text(.1,.1,txt)
plt.savefig(filename[:-4] + '_histogram.jpg')
plt.clf()

print "Total area is %d" % (w*l)
print "Total covered area is %d" % (np.sum(area))

rt = "Number of Circles is " + str(count) + "\n" + "Coverage percent is " + str(np.divide(np.sum(area), (w*l))) + "\n"
return rt

3 Answers 3

2

You can use minEnclosingCircle for this. Find the contours of your image, then apply the function to detect the shapes as circles.

Below is a simple example with a c++ code. In your case, I feel you should use a combination of Hough-circles and minEnclosingCircle, as some circles in your image are very close to each other, there's a chance that they may be detected as a single contour.

input image:

input

circles:

output

Mat im = imread("circ.jpg");
Mat gr;
cvtColor(im, gr, CV_BGR2GRAY);
Mat bw;
threshold(gr, bw, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);

vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(bw, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
for(int idx = 0; idx >= 0; idx = hierarchy[idx][0])
{
    Point2f center;
    float radius;
    minEnclosingCircle(contours[idx], center, radius);

    circle(im, Point(center.x, center.y), radius, Scalar(0, 255, 255), 2);
}
Sign up to request clarification or add additional context in comments.

Comments

1

If you still want to use the HoughCircles function, you could just see if two circles overlap and make a new circle out of them.

1 Comment

I have tried that way, but there are many other circles right next to each other. So it does not work very well. Thanks anyway.
0

When you have such beautiful well separated and contrasted patterns, the easiest way would be to use shape indexes. See this paper or this poster. In both cases you have a list of shape indexes.

Thanks to shape indexes, you can what follow:

  • binaries the image
  • connected components labeling in order to separate each pattern
  • compute shape indexes (most of them use basic measures)
  • classify the pattern according to the shape indexes values.

As in your specific case the round shapes are perfectly round, I would use the following shape indexes:

  • Circularity = >using just radii, so easiest to compute and perfect in your case.
  • Extension/elongation/stretching by radii => Perfect in your case but the minium ball computation is not available in all the libraries.
  • Iso-perimetric deficit => really easy to compute, but a little bit less stable than the circularity because of the perimeter.

Also work in your case:

  • Gap inscribed disk
  • Spreading of Morton
  • Deficit
  • Extension by diameters

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.