python opencv - blob detection or circle detection

cmplx96 picture cmplx96 · Feb 13, 2017 · Viewed 24k times · Source

I am having problems detecting circle areas. I tried it with the HoughCircles function from opencv. However even though the images are pretty similar the parameters for the funtion have to be different in order to detect the cirles.

Another approach I tried was to iterate over every pixel and check if the current pixel is white. If this is the case then check if there is a blob object in the area (distance to blob center smaller than a threshold). If there is, append the pixel to the blob, if not then create a new blob. This also didn't work properly.

Has anyone a idea how I can make this work (90% detection) ? I attached a example image and another image where I marked the cirles. Thanks!

example

example with arrows

UPDATE: Thank you for the help so far! This is the code where I acquire the contours and filter them by area:

im = cv2.imread('extract_blue.jpg')
imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
im_gauss = cv2.GaussianBlur(imgray, (5, 5), 0)
ret, thresh = cv2.threshold(im_gauss, 127, 255, 0)
# get contours
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

contours_area = []
# calculate area and filter into new array
for con in contours:
    area = cv2.contourArea(con)
    if 1000 < area < 10000:
        contours_area.append(con)

This works pretty neat. I drew them on the image: contours_filtered_area

This is the part where I filtered by circularity, it goes straight below the code where I filter by area:

contours_cirles = []

# check if contour is of circular shape
for con in contours_area:
    perimeter = cv2.arcLength(con, True)
    area = cv2.contourArea(con)
    if perimeter == 0:
        break
    circularity = 4*math.pi*(area/perimeter*perimeter)
    print circularity
    if 0.8 < circularity < 1.2:
        contours_cirles.append(con)

However, the new list 'contours_cirles' is empty. I printed 'circularity' in the loop and the values are all between 10 000 and 100 000.

UPDATE #2: After correcting the missing brackets it is working now!

contours_cirles = []

# check if contour is of circular shape
for con in contours_area:
    perimeter = cv2.arcLength(con, True)
    area = cv2.contourArea(con)
    if perimeter == 0:
        break
    circularity = 4*math.pi*(area/(perimeter*perimeter))
    print circularity
    if 0.7 < circularity < 1.2:
        contours_cirles.append(con)

Thanks a lot guys! :)

example_done

Answer

ZdaR picture ZdaR · Feb 13, 2017

As a starting point you may start with:

  • Find all the contours in the given image using cv2.findContours()
  • Iterate over each contour:
    • calculate the area, if the area of contour is in a given range say 70 < area < 150. This will filter out some extremely smaller and large contours.
    • After filtering the contours with the area threshold, you need to check the number of edges of contour, which can be done using: cv2.approxPolyDP(), for a circle len(approx) must be > 8 but < 23. Or you may apply some more sophisticated operations to detect circles here.

You should try to implement this approach and update the question with the code that you will write henceforth.

EDIT: As suggested by @Miki, there is a better and cleaner way of detecting if a geometrical shape is of circular shape using circularity = 4pi(area/perimeter^2), and decide a threshold such as 0.9, to check if the shape is circular. For perfect circle circularity == 1. You may fine tune this threshold as per your needs.

You may consult arcLength to find the perimeter of the contour and contourArea to get the area of the contour which are required to calculate the circularity.