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:
But these ones are more difficult:
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?
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.
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]?
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.
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.
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
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.
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()
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With