问题
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