Reputation: 21
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
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
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.
Upvotes: 2