OpenCV: Fitting a single circle to an image (in Python)

China☆狼群 提交于 2021-02-07 19:46:25

问题


I have an image like this:

I need to fit an ellipse to the dark area (note: must be an ellipse, not a circle). What is the best way to do this in OpenCV? My first step so far has been to apply an adaptive (Otsu) threshold to it, which results in:

But I'm not sure where to go from there. I'm writing the app in Python, but it's more the algorithm design I'm looking for.

EDIT based on response/comment:

OK, so I have already tried the morphology. Based on the OpenCV documentation, I did a 3-iteration "close" operation on it (dilation, then erosion) to remove the small particles, which results in:

Then, to expand it back out to closerto the original shape, I did a 3-iteration "open" operation (erosion, then dilation), which results in:

From here, I did Canny edge detection, which resulted in:

Now, I used findContours on it, but ran into an issue. It found dozens of contours along the edge, each one a short segment along the circumference. Which means, even if I take the maximum size contour, it might only represent 10% of the circumference, which is insufficient to accurately fit an ellipse. This is why the other questions that @Demi-Lune suggested didn't work for me; they all have very clean, sharp edges and findContours finds a nice single contour that covers the entire perimiter of each shape, but that doesn't happen for my messier image. So, what's the best way to fit the ellipse from here?


回答1:


If the object has circle shape, then use cv2.minEnclosingCircle is good. Or else, you can use cv2.fitEllipse to find the most fitted ellipse around the object. Remember to do find contour with white object in black background.

import cv2
import numpy as np

img = cv2.imread("1.jpg")

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_,thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
thresh = cv2.bitwise_not(thresh)

element = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=(5, 5))

morph_img = thresh.copy()
cv2.morphologyEx(src=thresh, op=cv2.MORPH_CLOSE, kernel=element, dst=morph_img)

contours,_ = cv2.findContours(morph_img,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

areas = [cv2.contourArea(c) for c in contours]
sorted_areas = np.sort(areas)

#bounding box (red)
cnt=contours[areas.index(sorted_areas[-1])] #the biggest contour
r = cv2.boundingRect(cnt)
cv2.rectangle(img,(r[0],r[1]),(r[0]+r[2],r[1]+r[3]),(0,0,255),2)

#min circle (green)
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
cv2.circle(img,center,radius,(0,255,0),2)

#fit ellipse (blue)
ellipse = cv2.fitEllipse(cnt)
cv2.ellipse(img,ellipse,(255,0,0),2)


cv2.imshow("morph_img",morph_img)
cv2.imshow("img", img)
cv2.waitKey()




回答2:


why don't you do thing like "close" then "open" to clear all the mess.

Raw image :

Otsu :

Close + open ; both with 7x7 kernel ; The binary image is beauty and clean now.

Only one contour is detect :

The ellipse is as : (please not that your image is circle so ellipse should be in circle shape)



来源:https://stackoverflow.com/questions/55621959/opencv-fitting-a-single-circle-to-an-image-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!