Convertir máscara binaria a polígono/RLE (formato de segmento de coco)

La segmentación de la anotación del conjunto de datos de coco no es una máscara binaria, sino un formato de polígono,
consulte una anotación.

{
    
    
	"segmentation": [[510.66,423.01,511.72,420.03,510.45......]], #两两组成(x,y)坐标,polygon格式
	"area": 702.1057499999998, #面积
	"iscrowd": 0,  #是不是一群物体,为0是seg是polygon格式,否则是RLE格式
	"image_id": 289343,  #对应的image id
	"bbox": [473.07,395.93,38.65,28.67], #(x,y,w,h)
	"category_id": 18,  #分类label
	"id": 1768  #当前annotation的id,每一个图像有不止一个对象,所以要对每一个对象编号(每个对象的ID是唯一的)
},

La segmentación es en realidad un punto de contorno de una máscara binaria.
Si desea convertir la máscara binaria a este formato, debe extraer el contorno.

1. Formato de polígono

Encontré una imagen de una máscara binaria en Internet.

Por favor agregue una descripción de la imagen

extraer su contorno

mask_img = cv2.imread("mask.png",cv2.IMREAD_GRAYSCALE)
contours, _ = cv2.findContours(mask_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
polygons = []
for object in contours:
    coords = []

    for point in object:
        coords.append(int(point[0][0]))
        coords.append(int(point[0][1]))

    polygons.append(coords)
    print(polygons)
    #[[131, 48, 130, 49, 129, 50, 128, 51, ...]]

¿Se puede usar este polígono? ¿Es lo mismo que el formato de anotación de coco?
Vamos a verificarlo.
Presente la biblioteca coco que viene con python, use annToMasklas funciones que contiene, convierta el polígono en una máscara
y luego muéstrelo para ver si es igual a la imagen original.

Veamos annToMaskprimero la función, primero necesita convertir el formato de polígono al formato RLE.

    def annToMask(self, ann):
        """
        Convert annotation which can be polygons, uncompressed RLE, or RLE to binary mask.
        :return: binary mask (numpy 2D array)
        """
        rle = self.annToRLE(ann)
        m = maskUtils.decode(rle)
        return m
        
    def annToRLE(self, ann):
        """
        Convert annotation which can be polygons, uncompressed RLE to RLE.
        :return: binary mask (numpy 2D array)
        """
        t = self.imgs[ann['image_id']]
        h, w = t['height'], t['width']
        segm = ann['segmentation']
        if type(segm) == list:
            # polygon -- a single object might consist of multiple parts
            # we merge all parts into one mask rle code
            rles = maskUtils.frPyObjects(segm, h, w)
            rle = maskUtils.merge(rles)
        elif type(segm['counts']) == list:
            # uncompressed RLE
            rle = maskUtils.frPyObjects(segm, h, w)
        else:
            # rle
            rle = ann['segmentation']
        return rle

En el proceso de conversión a RLE, se necesita el image_id y la segmentación en la anotación.
Ahora solo tenemos una segmentación, y no hay image_id,
y el image_id solo se usa para obtener el w, h de la imagen,
así que importe el coco instance_val2017.jsony encuentre un image_id para usar una vez.

from pycocotools.coco import COCO
coco_api = COCO('coco/annotations/instances_val2017.json')
ann = dict()
ann['image_id'] = 37777
ann['segmentation'] = polygons
mask = coco_api.annToMask(ann)
mask = np.clip(mask*255,0,255)

Muestra que se puede usar el contorno encontrado (el image_id aleatorio hará que h, w sean inconsistentes con la imagen original, pero no afectará la máscara). (Habrá algunos errores
en la continuidad de la curva).

Por favor agregue una descripción de la imagen

Todavía se prefiere el formato RLE a continuación.

2. Formato RLE

Si se usa en el segmento de coco, esta es la primera opción, habrá errores en el contorno superior y el formato de polígono en la función coco tiene muchas restricciones.

El código para convertir la máscara a RLE proviene de las utilidades del segmento cualquier cosa ,
donde la máscara es una máscara binaria en forma de tensor y el valor es int.

    def mask_to_rle(tensor: torch.Tensor) -> List[Dict[str, Any]]:
        """
        Encodes masks to an uncompressed RLE, in the format expected by
        pycoco tools.
        """
        # Put in fortran order and flatten h,w
        h, w, b = tensor.shape  #需要根据tensor的shape修改
        tensor = tensor.permute(2, 1, 0).flatten(1)  #需要根据tensor的shape修改

        # Compute change indices
        diff = tensor[:, 1:] ^ tensor[:, :-1] #要求值是int型
        change_indices = diff.nonzero()

        # Encode run length
        out = []
        for i in range(b):
            cur_idxs = change_indices[change_indices[:, 0] == i, 1]
            cur_idxs = torch.cat(
                [
                    torch.tensor([0], dtype=cur_idxs.dtype, device=cur_idxs.device),
                    cur_idxs + 1,
                    torch.tensor([h * w], dtype=cur_idxs.dtype, device=cur_idxs.device),
                ]
            )
            btw_idxs = cur_idxs[1:] - cur_idxs[:-1]
            counts = [] if tensor[i, 0] == 0 else [0]
            counts.extend(btw_idxs.detach().cpu().tolist())
            out.append({
    
    "size": [h, w], "counts": counts})
        return out

Supongo que te gusta

Origin blog.csdn.net/level_code/article/details/129837837
Recomendado
Clasificación