How to detect ellipse and remove outliers in image using opencv Python

2.6k views Asked by At

I'm trying to extract the dots that form an ellipse then draw it . But because of some points that can be considered as outliers, I got an invalid mask of ellipse. Like this:

This is the image that I want to extract the ellipse from Here is what I got when I tried to link between the points to draw an ellipse Here is the final mask which is invalid form

Here is the code that I'm executing, but it always selects the outlier

`cv2.rectangle(cleanedpartiallyimage, (0, 0), (1200, 10), (0, 0, 0), -1)

cv2.rectangle(cleanedpartiallyimage, (0, 0), (47, 1200), (0, 0, 0), -1)

image = cv2.cvtColor(cleanedpartiallyimage, cv2.COLOR_BGR2HSV) lower = np.array([85, 0, 20], dtype="uint8")
 upper = np.array([95, 255, 255], dtype="uint8") mygray = cv2.inRange(image, lower, upper)

#--- Gaussian and Canny filters to make it easy to get the contours

blurred = cv2.GaussianBlur(mygray, (5, 5), 0) imageCanny = cv2.Canny(blurred, 0, 100, 0)

ret,th = cv2.threshold(imageCanny,127,255, 0)

#--- Find all the contours in the binary image --- 
contours,hierarchy = cv2.findContours(th,3,1) 
cnt = contours big_contour = [] max = 0 for i in cnt:

area = cv2.contourArea(i) #--- find the contour having biggest area --- 
if(area > max): max = area big_contour = i

final = cv2.drawContours(imageCanny, big_contour, -1, (0,255,0), 3)

actualcontours, hierarchy = cv2.findContours(final, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

#---Removing side contour points

actualcontours = getactualcontours(actualcontours, 60)

empty = np.zeros((image.shape[0], image.shape[1], 3), np.uint8)

#---Removes linear contour points

ConvexHullPoints = contoursConvexHull(actualcontours)

#---Converts the points to Ellipse using fitEllipse

test41 = cv2.polylines(empty, [ConvexHullPoints], True, (255, 255, 255), 3) 
imageCannyee = cv2.Canny(test41, 0, 100, 0) 
contours, hierarchy = cv2.findContours(imageCannyee, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) 
for cont in contours: 
   if len(cont) < 20: 
    break 
   elps = cv2.fitEllipse(cont) 
anotherempty = np.zeros((image.shape[0], image.shape[1], 3), np.uint8)
#---Drawing the ellipse into the empty mask
cv2.ellipse(anotherempty, elps, (255, 255, 255), 2) plt.imshow(anotherempty)
1

There are 1 answers

1
nathancy On

Here's a simple approach:

  1. Obtain binary image. We load the image, convert to grayscale, Gaussian blur, then Otsu's threshold to obtain a binary image.

  2. Dilate to form single contour. Next we create an elliptical shaped kernel using cv2.getStructuringElement with the cv2.MORPH_ELLIPSE parameter and dilate to combine small individual contours into a single large contour.

  3. Identify ellipse. Next we find contours, filter using contour area and then detect the ellipse with cv2.fitEllipse().


enter image description here

import cv2

# Load image, grayscale, Gaussian blur, Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Dilate with elliptical shaped kernel
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
dilate = cv2.dilate(thresh, kernel, iterations=2)

# Find contours, filter using contour threshold area, draw ellipse
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    if area > 5000:
        ellipse = cv2.fitEllipse(c)
        cv2.ellipse(image, ellipse, (36,255,12), 2)

cv2.imshow('thresh', thresh)
cv2.imshow('dilate', dilate)
cv2.imshow('image', image)
cv2.waitKey()