JIAHAO HUANG
JIAHAO HUANG

Reputation: 189

Detection of irregular shapes using houghcircle function opencv python

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

Upvotes: 6

Views: 4253

Answers (3)

FiReTiTi
FiReTiTi

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:

  • 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

Upvotes: 0

dhanushka
dhanushka

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:

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);
}

Upvotes: 2

Chris
Chris

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

Related Questions