Anthony
Anthony

Reputation: 667

How to accurately detect the positions of hollow circles (rings) with OpenCV Python?

I would like to determine the center positions of the tips of the syringes in this (video still) image. The tips are nominally round and of known size and quantity.

I am currently putting red ink on the tips to make them easier to detect. It would be nice to not to have to do this but I think without it, detection would be very difficult. Anyone like a challenge?

I started off trying SimpleBlobDetector as it has some nice filtering. One thing I couldn't figure out was how to get SimpleBlobDetector to detect the hollow circles (rings)?

I then tried canny + hough but the circle detection was too unstable, the positions jumped around.

I am currently using findContours + minEnclosingCircle which works OK but still quite unstable. The mask looks like this. The result. You can see the accuracy is not great:

Gif

I briefly looked at RANSAC but I couldn't find a Python example that would detect multiple circles plus the edge detection is tricky.

My current code:

# https://stackoverflow.com/questions/32522989/opencv-better-detection-of-red-color
frame_inv = ~frame0
# Convert BGR to HSV
hsv = cv2.cvtColor(frame_inv, cv2.COLOR_BGR2HSV)
blur = cv2.GaussianBlur(hsv, (5, 5), 0)
# define range of color in HSV
lower_red = np.array([90 - 10, 70, 50])
upper_red = np.array([90 + 10, 255, 255])
# Threshold the HSV image to get only red colors
mask = cv2.inRange(hsv, lower_red, upper_red)
# cv2.imshow('Mask', mask)
kernel = np.ones((5, 5), np.uint8)
dilate = cv2.dilate(mask, kernel)
# cv2.imshow('Dilate', dilate)
contours = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]

tipXY = []
for c in contours:
    area = cv2.contourArea(c)
    if area > 200:
        (x, y), r = cv2.minEnclosingCircle(c)
        center = (int(x), int(y))
        r = int(r)
        shift = 2
        factor = 2 ** shift
        cv2.circle(frame0, (int(round((x) * factor)), int(round((y) * factor))),
                       int(round(10 * factor)), (0, 255, 0), 2, shift=shift)
        tipXY.append(center)

Any suggestions to improve the position detection accuracy/stability?

Upvotes: 5

Views: 1367

Answers (1)

Jeru Luke
Jeru Luke

Reputation: 21243

Here is a better way to segment red color using the second image as input.

Idea:

Since the red color is prominent, I tried converting to other known color spaces (LAB and YCrCb) and viewed their individual channels. The Cr from YCrCb expressed the red color more prominently. According to this link, Cr channel represents the difference between red and luminance, enabling the color to stand out.

Code:

img = cv2.imread('stacked_rings.jpg')

ycc = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
cr_channel = ycc[:,:,1]

enter image description here

Though the hollow rings can be seen, the pixel intensity range is limited to the range [109 - 194]. Let's stretch the range:

dst = cv2.normalize(cr_channel, dst=None, alpha=0, beta=255,norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)

enter image description here

The circles a more prominent. Hope this pre-processing step helps you.

Upvotes: 1

Related Questions