Получить область внутри контуров Opencv Python?

Я использовал метод адаптивного порога для создания изображения, подобного приведенному ниже:

enter image description here

Код, который я использовал, был:

image = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 45, 0)

Затем я использую этот код для получения контуров:

cnt = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]

Моя цель - создать маску, используя все пиксели внутри внешнего контура, поэтому я хочу заполнить все пиксели внутри объекта, который будет белым. Как я могу это сделать?

Я попытался создать код ниже, чтобы создать маску, но результирующая маска не отличается от изображения после применения адаптивного порога

mask = np.zeros(image.shape[:2], np.uint8)
cv2.drawContours(mask, cnt, -1, 255, -1)

Ответ 1

То, что у вас есть, почти правильно. Если вы посмотрите на свое пороговое изображение, причина, по которой он не работает, состоит в том, что ваш объект обуви имеет пробелы на изображении. В частности, то, что вам нужно, - это то, что вы ожидаете, что у ботинка будет свой периметр, чтобы он был all. Если это произойдет, то, если вы извлечете самый внешний контур (что делает ваш код), вы должны иметь только один контур, представляющий внешний периметр объекта. Как только вы заполните контур, ваша обувь должна быть полностью твердой.

Поскольку периметр вашей обуви не является полным и сломанным, это приводит к отключению белых областей. Если вы используете findContours, чтобы найти все контуры, он найдет только контуры каждой из белых фигур, а не самый внешний периметр. Таким образом, если вы попытаетесь использовать findContours, он даст вам тот же результат, что и исходное изображение, потому что вы просто находите периметр каждой белой области внутри изображения, а затем заполняете эти области findContours.


Что вам нужно сделать, так это убедиться, что изображение полностью закрыто. Я бы рекомендовал вам использовать morphology, чтобы закрыть все отключенные области вместе, а затем запустить вызов findContours на этом новом изображении, В частности, выполните двоичное морфологическое закрытие. Что это делает, так это то, что он отключает изолированные белые области, которые находятся близко друг к другу и обеспечивает их подключение. Используйте морфологическое закрытие и, возможно, используйте что-то вроде квадратного структурирующего элемента размером 7 x 7 для закрытия обуви. Этот структурирующий элемент, который вы можете представить как минимальное разделение между белыми областями, чтобы считать их связанными.

Как таковой, сделайте что-то вроде этого:

import numpy as np
import cv2 
image = cv2.imread('...') # Load your image in here
# Your code to threshold
image = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 45, 0)    

# Perform morphology
se = np.ones((7,7), dtype='uint8')
image_close = cv2.morphologyEx(image, cv2.MORPH_CLOSE, se)

# Your code now applied to the closed image
cnt = cv2.findContours(image_close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]
mask = np.zeros(image.shape[:2], np.uint8)
cv2.drawContours(mask, cnt, -1, 255, -1)

Этот код по существу принимает ваше пороговое изображение и применяет морфологическое закрытие к этому изображению. После этого мы находим внешние контуры этого изображения и заполняем их белым. FWIW, я скачал ваше пороговое изображение и попробовал это самостоятельно. Это то, что я получаю с вашим изображением:

enter image description here

Ответ 2

Простой подход состоит в том, чтобы закрыть отверстия на переднем плане для формирования единого контура с помощью cv2.morphologyEx() и cv2.MORPH_CLOSE

enter image description here

Теперь, когда внешний контур заполнен, мы можем найти внешний контур с помощью cv2.findContours() и использовать cv2.fillPoly(), чтобы заполнить все пиксели белым

.enter image description here

import cv2

# Load in image, convert to grayscale, and threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Close contour
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=1)

# Find outer contour and fill with white
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cv2.fillPoly(close, cnts, [255,255,255])

cv2.imshow('close', close)
cv2.waitKey()