1
$\begingroup$

I have the following image: input image

I need to get two ellipses which will 'describe' my 'ring'.

I have the following code at the moment:

import cv2
import numpy as np
import imutils

image = cv2.imread('front2.png', cv2.IMREAD_COLOR)

# Convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

_, gray = cv2.threshold(gray, 50, 255, cv2.THRESH_BINARY)

# Downsize image (by factor 4) to speed up morphological operations
gray = cv2.resize(gray, dsize=(0, 0), fx=0.25, fy=0.25)
cv2.imshow("Gray resized", gray)
cv2.imwrite('Gray_resized.png', gray)

# Morphological opening: Get rid of the stuff at the top of the ellipse
gray = cv2.morphologyEx(gray, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)))
cv2.imshow("Gray removed noise", gray)
cv2.imwrite('Gray_removed_noise.png', gray)

# Resize image to original size
gray = cv2.resize(gray, dsize=(image.shape[1], image.shape[0]))

# Find contours
cnts, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

# Draw found contours in input image
image = cv2.drawContours(image, cnts, -1, (0, 0, 255), 2)

if len(cnts) != 0:
    for cont in cnts:
        if cont.size < 10 or cv2.contourArea(cont) < 100:
            continue
        elps = cv2.fitEllipse(cont)
        cv2.ellipse(image,elps,(0,255,0),2)

# Downsize image
out_image = cv2.resize(image, dsize=(0, 0), fx=0.25, fy=0.25)
cv2.imshow("Output image", out_image)
cv2.imwrite('Output_image.png', out_image)

I've got images like:

Gray resized Gray removed noise Output image

In this way it detected one ellipse well enough but it hasn't detected the second one.

As i read it'd be more logical to use 'cv2.THRESH_BINARY_INV' in threshold but in this way it doesn't detect ellipse in the center:

Output for binary inverse thresh

Also if i try to lower the threshold my ring starts to fade:

Gray resized

The main questions: How to get rid of this noise at the top of my ring for detecting contours? And how to detect these ellipses?

I've looked at these answers: 1st 2nd 3rd but i haven't managed to achive my goal.

P.S. I can't use Hough-Circle method here. Because i have few similar images and radius for 'x' and 'y' axis may different and i need this precision(it means i need only ellipses and their radiuses for two axises).

$\endgroup$
3
$\begingroup$

I would recommend to use adaptive threshold rather than global threshold. Adaptive threshold deals better with uneven illumination. I have modified your code accordingly. You may want to experiment further with the value of block size and C value.

I have also used RETR_CCOMP and only show contours which either have a parent (index 3 != -1) or don't have any children (index 2 == -1).

I don't guarantee that this will work for other images but it may be a good starting point for your further experimentation.

import cv2
import numpy as np
import imutils

image = cv2.imread('front2.png', cv2.IMREAD_COLOR)

# Convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
h, w = gray.shape
s = int(w / 8)
gray = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, s, 7.0)

# Downsize image (by factor 4) to speed up morphological operations
gray = cv2.resize(gray, dsize=(0, 0), fx=0.25, fy=0.25)
cv2.imshow("Gray resized", gray)
cv2.imwrite('Gray_resized.png', gray)

# Morphological opening: Get rid of the stuff at the top of the ellipse
gray = cv2.morphologyEx(gray, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)))
cv2.imshow("Gray removed noise", gray)
cv2.imwrite('Gray_removed_noise.png', gray)

# Resize image to original size
gray = cv2.resize(gray, dsize=(image.shape[1], image.shape[0]))

# Find contours
cnts, hier = cv2.findContours(gray, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)

# Draw found contours in input image
image = cv2.drawContours(image, cnts, -1, (0, 0, 255), 2)

for i, cont in enumerate(cnts):
    h = hier[0, i, :]
    print(h)
    if h[3] != -1:
        elps = cv2.fitEllipse(cnts[i])
    elif h[2] == -1:
        elps = cv2.fitEllipse(cnts[i])
    cv2.ellipse(image, elps, (0, 255, 0), 2)

# Downsize image
out_image = cv2.resize(image, dsize=(0, 0), fx=0.25, fy=0.25)
cv2.imshow("Output image", out_image)
cv2.imwrite('Output_image.png', out_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

enter image description here

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.