Я хочу использовать только один прямоугольник, чтобы покрыть круг на этом изображении:
И получите этот результат с помощью cv2.minAreaRect(cnt)
:
Кажется, что это изображение разделено на несколько частей. Может быть, это потому, что на краю этого изображения есть точка останова. можете ли вы сказать мне, как использовать только один прямоугольник, чтобы покрыть этот круг моего изображения? Большое спасибо!
Это мой код:
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
Что вам нужно сделать, так это то, что вам нужно каким-то образом получить только 1 контур из изображения путем слияния контуров, что немного сложно сделать, поэтому, если вам нужен только охватывающий прямоугольник вокруг всех контуров, вы можете сделать что-то как таковое
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)
Вы, вероятно, искали контуры с помощью cv2.findContours()
и повторяли их, чтобы нарисовать прямоугольник на изображении. Проблема в том, что на вашем изображении круг состоит не из одной соединенной линии, а из множества ломаных линий.
Контуры — это кривые, соединяющие все непрерывные точки (вдоль границы), имеющие одинаковый цвет или интенсивность (документация OpenCV).
Поэтому, чтобы получить лучший результат, вы должны сначала подготовить изображение, прежде чем искать контуры. Вы можете использовать различные инструменты для предварительной обработки изображения (вы можете поискать в документации OpenCV). В этом случае я бы попробовал выполнить процедуру под названием «закрытие» с небольшим ядром. Закрытие — это расширение с последующей эрозией пикселей. Это может помочь соединить ваши маленькие контуры в один большой контур (круг). Затем вы можете выбрать самый большой и нарисовать ограничивающий прямоугольник.
Пример:
Входное изображение:
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()
Результат:
Изображение после выполнения операции закрытия:
Надеюсь, поможет. Ваше здоровье!
Это отличное решение. Метод удаления шума путем замыкания и повторения контуров для нахождения максимума прост, но очень эффективен.
Простые решения, если они работают, по моему опыту, являются лучшими решениями. Рад, что это помогло.
@kavko Спасибо за помощь! Ваше решение работает. Это действительно отличное решение!
Можно объединить все контуры в один контур, так как они представляют собой просто массивы координат точек, описывающих контур. Вы можете использовать np.concatenate(contours), и кажется, что функция cv2.minAreaRect не заботится о том, чтобы точки в новом массиве не были непрерывными. В моем случае это сработало лучше, чем использование функции закрытия, так как у меня более сложные объекты. Если хотите, можете попробовать, это просто. Вот как должна выглядеть ваша функция:
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
Конечно, можно было бы найти решение на предоставленном двоичном изображении, но я думаю, было бы лучше, если бы вы предоставили исходное входное изображение, так как может быть лучшее общее решение для вашей проблемы.