pjmanator
pjmanator

Reputation: 21

Remove certain points from findContours to get better result from fitEllipse

I want to fit an ellipse to a partly damaged object in a picture. (The pictures here are only simplified examples for illustration!)

Image with damaged elliptical object

image

By doing this

def sort(n):
    return n.size

Image = cv2.imread('acA2500/1.jpg', cv2.IMREAD_GRAYSCALE)

#otsu binarization
_, binary = cv2.threshold(Image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

#invert binary image for opencv findContours
inverse_binary = cv2.bitwise_not(binary)

#find contours
contours, _ = cv2.findContours(inverse_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

#sort contours by length
largest_contours = sorted(contours, key=sort, reverse=True)

#fit ellipse
contour = largest_contours[0]
ellipse = cv2.fitEllipseDirect(contour)

I get this result, which is not very satisfying.

Result of cv2.findContours and cv2.fitEllipse

So, I've built this loop to get rid of the contour points which are not on the ellipse.

contour = largest_contours[0]
newcontour = np.zeros([1, 1, 2])
newcontour = newcontour.astype(int)
for coordinate in contour:
    if coordinate[0][0] < 600:
        newcontour = np.insert(newcontour, 0, coordinate, 0)
newellipse = cv2.fitEllipse(newcontour)

And get this result, which is good.

Result after trimming the contour points

The Problem is, that I have to do a lot of these fits in a short time period. So far this does not reach the desired speed.

Is there any better/faster/nicer way to trim the contour points? Since I don't have a lot of coding experience, I would be happy to find some help here :-)

Edit:

I edited the example pictures so that it is now clear that unfortunatly a cv2.minEnclosingCircle approach does not work.

Also the pictures now demonstrate why I sort the contours. In my real code I fit an ellipse to the three longest contours an than see which one I want to use trough a different routine.

If I don't trimm the contour and pick the contour for cv2.fitEllipse by hand, the code needs around 0.5s. With contour trimming and three times cv2.fitEllipse it takes around 2s. It may only take 1s

Upvotes: 2

Views: 1519

Answers (1)

Kinght 金
Kinght 金

Reputation: 18341

If the object is an circle, then you can use cv2.minEnclosingCircle on the contour to capture it.

#!/usr/bin/python3
# 2019/02/13 08:50 (CST)
# https://stackoverflow.com/a/54661012/3547485

import cv2
img = cv2.imread('test.jpg')

## Convert to grayscale and threshed it
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, threshed = cv2.threshold(gray, 100, 255, cv2.THRESH_OTSU|cv2.THRESH_BINARY_INV)

## Find the max outers contour
cnts = cv2.findContours(threshed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]
cv2.drawContours(img, cnts, -1, (255, 0, 0), 2, cv2.LINE_AA)

## Use minEnclosingCircle
(cx,cy),r = cv2.minEnclosingCircle(cnts[0])
cv2.circle(img, (int(cx), int(cy)), int(r), (0, 255, 0), 1, cv2.LINE_AA)

## This it
cv2.imwrite("dst.jpg", img)

Here is my result.

enter image description here

Upvotes: 2

Related Questions