I want to use only one rectangle to cover the circle in this image:
And get this result with cv2.minAreaRect(cnt)
:
This image seems to be divided into multiple parts. Maybe it's because there are some breakpoint on the edge of this image. can you tell me how to use only one rectangle to cover this circle of my image? Thank you very much!
This is my code:
def draw_min_rect_circle(img, cnts): # conts = contours
img = np.copy(img)
for cnt in cnts:
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2) # blue
min_rect = cv2.minAreaRect(cnt) # min_area_rectangle
min_rect = np.int0(cv2.boxPoints(min_rect))
cv2.drawContours(img, [min_rect], 0, (0, 255, 0), 2) # green
(x, y), radius = cv2.minEnclosingCircle(cnt)
center, radius = (int(x), int(y)), int(radius) # center and radius of minimum enclosing circle
img = cv2.circle(img, center, radius, (0, 0, 255), 2) # red
return img
You probably searched for contours with the cv2.findContours()
and iterated through them to draw the rectangle on the image. The problem is that your image doesn't have the circle made out of one connected line but many broken lines.
Contours are curves joining all the continuous points (along the boundary), having same color or intensity (OpenCV documentation).
So to get a better result you should first prepare your image before you search for contours. You can use various tools for preprocessing the image (you can search the OpenCV documentation). In this case I would try to perform the procedure called "closing" with a small kernel. Closing is dilation followed by erosion of pixels. It can help connect your small contours to a one big contour (circle). Then you can select the biggest one and draw a bounding rectangle.
Example:
Input image:
import cv2
import numpy as np
img = cv2.imread('test.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
kernel = np.ones((3,3), dtype=np.uint8)
closing = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
_, contours, hierarchy = cv2.findContours(closing, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x,y), (x+w, y+h), (255,255,0), 1)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
Image after performing the closing operation:
Hope it helps. Cheers!
It is possible to join all the contours into a single contour, as they are just numpy arrays of point coordinates describing the contour. You can use np.concatenate(contours)
and it seems the cv2.minAreaRect
function doesn't care that the points in the new array are not continuous. In my case this worked better than using the closing function, as I have more complex objects. If you want you can give it a try, it's simple. This is how your function should look like:
def draw_min_rect_circle(img, cnts): # conts = contours
img = np.copy(img)
join_cnts = np.concatenate(cnts)
x, y, w, h = cv2.boundingRect(join_cnts)
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2) # blue
min_rect = cv2.minAreaRect(join_cnts) # min_area_rectangle
min_rect = np.int0(cv2.boxPoints(min_rect))
cv2.drawContours(img, [min_rect], 0, (0, 255, 0), 2) # green
(x, y), radius = cv2.minEnclosingCircle(join_cnts)
center, radius = (int(x), int(y)), int(radius) # center and radius of minimum enclosing circle
img = cv2.circle(img, center, radius, (0, 0, 255), 2) # red
return img
What you need to do is that you need to somehow obtain only 1 contour from the image by merging the contours, that is a bit difficult to do, so if you only want an enclosing rect around all contours, you can do something as such
def draw_min_rect_circle(img, cnts): # conts = contours
img = np.copy(img)
x1,y1 = np.inf
x2,y2 = 0
for cnt in cnts:
x, y, w, h = cv2.boundingRect(cnt)
if x > x1:
x1=x
if y > y1:
y1=y
if x2 < x+w
x2 = x+w
if y2 < y+h
y2 = y+h
w = x2 - x1
h = y2 - y1
r = math.sqrt((w*w) + (h*h)) / 2
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
cv2.circle(img, (x1+w/2,y1+h/2), r, (0, 0, 255), 2)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.