Как сгруппировать части маски в изображении с помощью Python?

Мне нужно разделить маску таким образом, чтобы в случае несоответствия внутри маски она была разделена. Например, если я рисую маску кошки, я хочу, чтобы широкая часть (тело) была одной маской, а узкая часть (хвост) — другой.

Сейчас у меня есть сплошная маска, включающая в себя как тело кошки, так и ее хвост. Я хочу разделить это на две отдельные маски. Как я могу добиться этого с помощью Python?

оригинальная маска Как сгруппировать части маски в изображении с помощью Python?

желаемая маска

Как сгруппировать части маски в изображении с помощью Python?

Я рассмотрел использование методов, описанных в этом, в которых основное внимание уделяется разбиению многоугольников и разделению контуров на несколько треугольников. Однако этот подход не соответствует моим потребностям, поскольку я хочу разделить маску по размеру и форме, а не создавать несколько треугольных разделов.

🤔 А знаете ли вы, что...
С Python можно создавать ботов для социальных сетей и мессенджеров.


50
1

Ответ:

Решено

Вы можете использовать Дефекты выпуклости, чтобы определить точки, между которыми нужно «разрезать».

Это делается так здесь например.

import cv2
import matplotlib.pyplot as plt
import numpy as np

def split_mask(mask):
    _, thresh = cv2.threshold(mask , 120,255, cv2.THRESH_BINARY)
    contours, _ = cv2.findContours(thresh, 2, 1)

    for contour in contours:
        if  cv2.contourArea(contour) > 20:
            hull = cv2.convexHull(contour, returnPoints = False)
            defects = cv2.convexityDefects(contour, hull)
            if defects is None:
                continue
            
            # Gather all defect points to filter them.
            dd = [e[0][3]/256 for e in defects]
            points = []
            for i in range(len(dd)):
                _,_,f,_ = defects[i,0]
                if dd[i] > 1.0 and dd[i]/np.max(dd) > 0.2:
                    points.append(f)

            # If there is defect points, erase the mask closest points.
            if len(points) >= 2:
                for i, f1 in enumerate(points):
                    p1 = tuple(contour[f1][0])
                    nearest = min((tuple(contour[f2][0]) for j, f2 in enumerate(points) if i != j),
                                  key=lambda p2: (p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)
                    cv2.line(thresh, p1, nearest, 0, 20)
    return thresh    

if __name__= = "__main__":
    mask = cv2.imread("<path-to-your-image>", cv2.IMREAD_GRAYSCALE)
    mask_splitted = split_mask(mask)
    plt.imshow(mask_splitted)
    plt.show()

Это даст следующее на вашем изображении: