Reputation: 189
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.
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
Upvotes: 6
Views: 4253
Reputation: 5898
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:
As in your specific case the round shapes are perfectly round, I would use the following shape indexes:
Also work in your case:
Upvotes: 0
Reputation: 10682
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:
circles:
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);
}
Upvotes: 2
Reputation: 36
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.
Upvotes: 1