Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to obtain a dynamic threshold for contour detection in OpenCV

In my image database, there is a need to 1) detect if there is a flake (a very black contour) or not in an image and also 2) find a minimum closing circle to measure the radius of the flake.

However, the images come with slightly different illuminations.

Here are some examples:

This one is very easy to either detect and measure:

enter image description here

But these ones are more difficult:

enter image description here enter image description here enter image description here

My initial thought is to use a threshold related to the average value of pixels of images.

Is there any other way of computing such a dynamic threshold in OpenCV?

like image 278
John Avatar asked Sep 16 '19 09:09

John


People also ask

How do you find the contour of an image in OpenCV?

Contours can be explained simply as a curve joining all the continuous points having the same color or intensity. The contours are a useful tool for shape analysis and object detection and recognition. Step 1: Import OpenCV. Step 2: Import matplotlib. Step 3: Read the image. Step 4: Convert the image from bgr2rgb.

What is the right threshold value for contours?

Also you probably need to find a way to remove the veins. Otherwise it sees them all as contours. First off your thresholding values are well out, try values 180 to 255. Also, why do you think the contour you are after is in contours [0]?

How do contour-retrieval algorithms affect contour detection?

You will see how each contour-retrieval mode affects contour detection in images, and produces hierarchical results. Objects detected by contour-detection algorithms in an image could be: In most cases, where a shape contains more shapes, we can safely conclude that the outer shape is a parent of the inner shape.

How to find contours in a single channel grayscale image?

Converting the image to a single channel grayscale image is important for thresholding, which in turn is necessary for the contour detection algorithm to work properly. While finding contours, first always apply binary thresholding or Canny edge detection to the grayscale image.


Video Answer


2 Answers

I think what you're looking for is cv2.adaptiveThreshold() or Otsu's thresholding. To satisfy your requirements for #1, we can use a minimum threshold area to determine if the flake exists. For #2, once we detect the contour, we can use moments to determine the radius. Here's a simple approach

  • Convert image to grayscale and median blur
  • Adaptive threshold
  • Morph close to smooth image
  • Dilate to enhance contour
  • Find contours and sort using contour area

The main idea is to use a large median blur to remove the noise then adaptive threshold. Here's the results for each for your four pictures. For some of your pictures, the black spot was not actually a circle, it was more of a oval shape. You can decide what you want to do with that situation.

enter image description here enter image description here enter image description here enter image description here

import cv2

image = cv2.imread('4.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 
blur = cv2.medianBlur(gray, 25)
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,27,6)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=1)
dilate = cv2.dilate(close, kernel, iterations=2)

cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:10]

minimum_area = 500
for c in cnts:
    area = cv2.contourArea(c)
    if area > minimum_area:
        # Find centroid
        M = cv2.moments(c)
        cX = int(M["m10"] / M["m00"])
        cY = int(M["m01"] / M["m00"])
        cv2.circle(image, (cX, cY), 20, (36, 255, 12), 2) 
        x,y,w,h = cv2.boundingRect(c)
        cv2.putText(image, 'Radius: {}'.format(w/2), (10,20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (36,255,12), 2)
        break

cv2.imshow('thresh', thresh)
cv2.imshow('close', close)
cv2.imshow('image', image)
cv2.waitKey()
like image 168
nathancy Avatar answered Sep 17 '22 03:09

nathancy


You must start with a thresholding

Here you have some thresholding, you can choose the one you want, with good parameters, most of the noise will go.

Then you can do en edge detection

Finally, hough transform seems to best the best approach to detect circles (noise will be remove by the parameters of the hough circle transform).

You can set a minimal and a maximal radius, so if you have an idea of the average radius, you can adjust it this way.

like image 38
Sylvain Lejamble Avatar answered Sep 17 '22 03:09

Sylvain Lejamble