Detectron: O formato RLE ou polígono de "segmentação" para estender ao conjunto de dados coco

Criado em 2 fev. 2018  ·  38Comentários  ·  Fonte: facebookresearch/Detectron

Oi Detectron,

Recentemente, tentei adicionar meus dados de coco personalizados para executar o Detectron e encontrei os seguintes problemas.
(1) "segmentação" nos dados de coco como abaixo ,

{"segmentação": [[ 499,71, 397,28, ...... 342,71, 172,31 ]] , "área": ​​43466.12825, "iscrowd": 0, "id_imagem": 182155, "bbox": [338,89, 51,69, 205,82, 367,61], "id_categoria": 1, "id": 1248258},

{"segmentação": {"contagens": [66916, 6, 587, ..... 1, 114303], "tamanho": [594, 640] }, "área": ​​6197, "estácrowd": 1, "id_imagem": 284445, "bbox": [112, 322, 335, 94], "id_categoria": 1, "id": 9.001002844e + 11},
O primeiro formato de "segmentação" é o polígono e o segundo é necessário codificar / decodificar para o formato RLE.

Os formatos acima podem ser executados no Detectron.

(2) Eu adicionei uma nova categoria e gerei um novo formato RLE para o campo "segmentação" por meio da máscara coco api encode () / decode ().
Eu gerei dados assim.

"segmentação": [{ "conta ": "mng = 1fb02O1O1O001N2O001O1O0O2O1O1O001N2O001O1O0O2O1O001O1O1O010000O01000O010000O01000O01000O01000O01N2N2M2O2N2N1O2N2O001O10O B000O10O1O001 ^ OQ ^ O9Pb0EQ ^ O; Wb0OO01O1O1O001O1N2N`jT3?"," tamanho": [600,1000]}]

Descobri que os caracteres em negrito são diferentes do formato json de "segmentação" coco original, embora possa ser executado na implementação do MatterPort para Mask-RCNN.

Além disso, tentei modificar alguns códigos do Detectron para atender aos meus requisitos, mas foi muito difícil para mim porque muitos códigos precisam ser alterados.

Você poderia me dar algumas sugestões para executar meus dados personalizados?

Obrigado.

community help wanted

Comentários muito úteis

@topcomma
Talvez você possa tentar converter a máscara em polis.

labels_info = []
for mask in mask_list:
    # opencv 3.2
    mask_new, contours, hierarchy = cv2.findContours((mask).astype(np.uint8), cv2.RETR_TREE,
                                                        cv2.CHAIN_APPROX_SIMPLE)
    # before opencv 3.2
    # contours, hierarchy = cv2.findContours((mask).astype(np.uint8), cv2.RETR_TREE,
    #                                                    cv2.CHAIN_APPROX_SIMPLE)
    segmentation = []

    for contour in contours:
        contour = contour.flatten().tolist()
        # segmentation.append(contour)
        if len(contour) > 4:
            segmentation.append(contour)
    if len(segmentation) == 0:
        continue
    # get area, bbox, category_id and so on
    labels_info.append(
        {
            "segmentation": segmentation,  # poly
            "area": area,  # segmentation area
            "iscrowd": 0,
            "image_id": index,
            "bbox": [x1, y1, bbox_w, bbox_h],
            "category_id": category_id,
            "id": label_id
        },
    )

Todos 38 comentários

Eu tive um problema semelhante: algumas das funções em lib / utils / segms.py esperam que as segmentações estejam no formato “poli” e quebram quando são fornecidas em RLE.
É inconveniente, mas parece estar de acordo com as especificações para regiões não aglomeradas (iscrowd = 0):

O formato de segmentação depende se a instância representa um único objeto (iscrowd = 0 em que polígonos são usados) ou uma coleção de objetos (iscrowd = 1 em que RLE é usado).

[1] http://cocodataset.org/#download , seção "4.1. Anotações de instância de objeto”

A solução alternativa para mim foi transformar para o formato “poli”, que é essencialmente uma lista de vértices (x, y).

-Lesha.

Em 2 de fevereiro de 2018, às 06:41, Hu Xingui [email protected] escreveu:

Oi Detectron,

Recentemente, tentei adicionar meus dados de coco personalizados para executar o Detectron e encontrei os seguintes problemas.
(1) "segmentação" nos dados de coco como abaixo ,

{"segmentação": [[499,71, 397,28, ...... 342,71, 172,31]], "área": ​​43466.12825, "iscrowd": 0, "id_imagem": 182155, "bbox": [338,89, 51,69, 205,82, 367,61], "id_categoria": 1, "id": 1248258},

{"segmentação": {"contagens": [66916, 6, 587, ..... 1, 114303], "tamanho": [594, 640]}, "área": ​​6197, "estácrowd": 1, "id_imagem": 284445, "bbox": [112, 322, 335, 94], "id_categoria": 1, "id": 9.001002844e + 11},
O primeiro formato de "segmentação" é o polígono e o segundo é necessário codificar / decodificar para o formato RLE.

Os formatos acima podem ser executados no Detectron.

(2) Eu adicionei uma nova categoria e gerei um novo formato RLE para o campo "segmentação" por meio da máscara coco api encode () / decode ().
Eu gerei dados assim.

"Segmentação": [{ "conta": "mng = 1fb02O1O1O001N2O001O1O0O2O1O1O001N2O001O1O0O2O1O001O1O1O010000O01000O010000O01000O01000O01000O01N2N2M2O2N2N1O2N2O001O10O B000O10O1O001 ^ OQ ^ O9Pb0EQ ^ O; Wb0OO01O1O1O001O1N2N`jT3?", "Tamanho": [600,1000]}]

Descobri que os caracteres em negrito são diferentes do formato json de "segmentação" coco original, embora possa ser executado na implementação do MatterPort para Mask-RCNN.

Além disso, tentei modificar alguns códigos do Detectron para atender aos meus requisitos, mas foi muito difícil para mim porque muitos códigos precisam ser alterados.

Você poderia me dar algumas sugestões para executar meus dados personalizados?

Obrigado.

-
Você está recebendo isto porque está inscrito neste tópico.
Responda a este e-mail diretamente, visualize-o no GitHub ou ignore a conversa.

@amokeev , Como converter o formato "RLE" em "poly" para a sua solução alternativa?

@amokeev , Tem certeza de que o número na segmentação é (x, y) vértices? Por exemplo, "66916", um grande número! Outro, embora eu tenha definido "iscrowd" como "1" para o formato RLE, ainda não foi possível executar no Detectron.

Eu interpreto poli como uma lista de polígonos, definidos pelos vértices, como [[x1, y1, x2, y2 ... xN, yN], ... [x1, y1, x2, y2 ... xN, yN]], onde as coordenadas são de a mesma escala da imagem.
As máscaras, codificadas desta forma, são mostradas corretamente pelo CocoAPI [1]

Mas você pode querer uma resposta “oficial”.

[1] https://github.com/cocodataset/cocoapi https://github.com/cocodataset/cocoapi

Em 2 de fevereiro de 2018, às 09:18, Hu Xingui [email protected] escreveu:

@amokeev https://github.com/amokeev , Tem certeza de que o número na segmentação é (x, y) vértices? Por exemplo, "66916", um grande número! Outro, embora eu tenha definido "iscrowd" como "1" para o formato RLE, ainda não foi possível executar no Detectron.

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub https://github.com/facebookresearch/Detectron/issues/100#issuecomment-362516928 ou ignore o tópico https://github.com/notifications/unsubscribe-auth/AFlh63ObnXg- DcaDKwIi3pB4Ppig464Hks5tQsTkgaJpZM4R2tN3 .

@topcomma
Talvez você possa tentar converter a máscara em polis.

labels_info = []
for mask in mask_list:
    # opencv 3.2
    mask_new, contours, hierarchy = cv2.findContours((mask).astype(np.uint8), cv2.RETR_TREE,
                                                        cv2.CHAIN_APPROX_SIMPLE)
    # before opencv 3.2
    # contours, hierarchy = cv2.findContours((mask).astype(np.uint8), cv2.RETR_TREE,
    #                                                    cv2.CHAIN_APPROX_SIMPLE)
    segmentation = []

    for contour in contours:
        contour = contour.flatten().tolist()
        # segmentation.append(contour)
        if len(contour) > 4:
            segmentation.append(contour)
    if len(segmentation) == 0:
        continue
    # get area, bbox, category_id and so on
    labels_info.append(
        {
            "segmentation": segmentation,  # poly
            "area": area,  # segmentation area
            "iscrowd": 0,
            "image_id": index,
            "bbox": [x1, y1, bbox_w, bbox_h],
            "category_id": category_id,
            "id": label_id
        },
    )

@amokeev ,
@Sundrops

Obrigado por suas sugestões.
Vou tentar.

@Sundrops , como seu método de conversão, pode obter o resultado da lista "poly". Muito obrigado! Mas ainda não sei por que a coordenada no arquivo COCO json é um número grande / pequeno como "66916" ou "1"?

As anotações

  1. polígono (instância do objeto) [[499,71, 397,28, ...... 342,71, 172,31]] vértices
  2. RLE descompactado (multidão). "segmentação": {"contagens": [66916, 6, 587, ..... 1, 114303], "tamanho": [594, 640]}, 66916 representa o número do rótulo 0.

O polígono e o RLE descompactado serão convertidos para o formato RLE compacto com o MaskApi.
O formato RLE compacto:
segmentação ": [{" conta ": "mng = 1fb02O1O1O001N2O001O1O0O2O1O1O001N2O001O1O0O2O1O001O1O1O010000O01000O010000O01000O01000O01000O01N2N2M2O2N2N1O2N2O001O10O B000O10O1O001 ^ OQ ^ O9Pb0EQ ^ O; Wb0OO01O1O1O001O1N2N`jT3"," tamanho ":? [600,1000]}]

@Sundrops ,
Obrigado pela sua grande ajuda.
Meus dados personalizados do tipo coco podem ser treinados no Detectron agora.

@topcomma ,
eu tenho o mesmo problema que você.
Como método do Sundrops, não consigo encontrar o arquivo para converter a máscara em polys.
Você poderia me dizer qual arquivo? Muito obrigado!

@ lg12170226
Você pode consultar o código python do coco stuff (https://github.com/nightrome/cocostuff) para implementá-lo você mesmo.
Na base de código não há arquivo da anotação relacionada.

@topcomma : Eu tenho uma imagem bruta e N imagens de rótulo. Cada etiqueta é armazenada em um único arquivo, então tenho N imagem para etiqueta. Quero treinar o Mask RCNN em meu próprio conjunto de dados, então primeiro preciso converter para o formato COCO. Você poderia compartilhar o código como convertê-lo para o estilo COCO? Obrigado

Gostaria de saber se existe uma maneira de converter RLEs compactados em polys / RLEs não compactados?

@realwecan depois de decodificar RLE com pycocotools.mask.decode, você pode verificar minha implementação para gerar polígonos com opencv:

conversor coco-json

@hazirbas : obrigado pelo seu código. Por que não usar o Davis 2017 que contém segmentação de instância? Podemos usar seu código para converter Davis 2017 para o formato coco para usar esta implementação de maskrcnn?

@ John1231983 você precisa modificar o script de acordo para ler arquivos divididos, bem como o arquivo db_info.yml. Para minha própria pesquisa, eu precisava disso para o DAVIS 2016.

Outra solução para gerar polígonos, mas usando skimage vez de opencv .

import json
import numpy as np
from pycocotools import mask
from skimage import measure

ground_truth_binary_mask = np.array([[  0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
                                     [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
                                     [  0,   0,   0,   0,   0,   1,   1,   1,   0,   0],
                                     [  0,   0,   0,   0,   0,   1,   1,   1,   0,   0],
                                     [  0,   0,   0,   0,   0,   1,   1,   1,   0,   0],
                                     [  0,   0,   0,   0,   0,   1,   1,   1,   0,   0],
                                     [  1,   0,   0,   0,   0,   0,   0,   0,   0,   0],
                                     [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
                                     [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0]], dtype=np.uint8)

fortran_ground_truth_binary_mask = np.asfortranarray(ground_truth_binary_mask)
encoded_ground_truth = mask.encode(fortran_ground_truth_binary_mask)
ground_truth_area = mask.area(encoded_ground_truth)
ground_truth_bounding_box = mask.toBbox(encoded_ground_truth)
contours = measure.find_contours(ground_truth_binary_mask, 0.5)

annotation = {
        "segmentation": [],
        "area": ground_truth_area.tolist(),
        "iscrowd": 0,
        "image_id": 123,
        "bbox": ground_truth_bounding_box.tolist(),
        "category_id": 1,
        "id": 1
    }

for contour in contours:
    contour = np.flip(contour, axis=1)
    segmentation = contour.ravel().tolist()
    annotation["segmentation"].append(segmentation)

print(json.dumps(annotation, indent=4))

como você converteria uma máscara binária ou RLE codificado em RLE descompactado para uso no campo "iscrowd: 1" "contagens"?

@waspinator As segmentações com seu código usando skimage são diferentes do código de @Sundrops usando cv2.
Esses dois resultados estão corretos e esses dois resultados podem ser usados ​​pelo Detectron? Por favor me dê alguns conselhos, obrigado. @waspinator @Sundrops

Resultados usando seu código com skimage:

{"segmentação": [[0,0, 252,00196078431372, 1,0, 252,00196078431372, 2,0, 252,00196078431372, 3,0, 252,00196078431372, 4,0, 252,00196078431372, 5,0, 252,00196078431372, 6,013781372601372,00192, 7,013,00192, 7,013,00192,036013,00192, 7,013,0019,0019 , 11,0, 252,00196078431372, 12,0, 252,00196078431372, 13,0, 252,00196078431372, 14,0, 252,00196078431372, 15,0, 252,00196078431372, 16,0, 252,00196078431372, 17,0, 252,00196078431372, 18,0, 252,00196078431372, 19,0, 252,00196078431372, 20,0, 252,00196078431372, 21,0, 252,00196078431372, 22,0, 252,00196078431372, 23,0 , 252,00196078431372, 24,0, 252,00196078431372, 25,0, 252,00196078431372, 26,0, 252,00196078431372, 27,0, 252,00196078431372, 28,0, 252,00196078431372, 29,0, 252,00196078431372, 30,0, 252,00196078431372, 31,0, 252,00196078431372, 32,0, 252,00196078431372, 33,0, 252,00196078431372, 34,0, 252,00196078431372, 35,0, 252,00196078431372 , 36,0, 252,00196078431372, 37,0, 252,00196078431372, 38. 0, 252,00196078431372, 39,0, 252,00196078431372, 40,0, 252,00196078431372, 41,0, 252,00196078431372, 42,0, 252,00196078431372, 43,0, 252,00196078431372, 44,0, 252,00196078431372, 45,0, 252,00196078431372, 46,0, 252,00196078431372, 47,0, 252,00196078431372, 48,0, 252,00196078431372, 49,0, 252,00196078431372, 50,0, 252,00196078431372, 51,0, 252,00196078431372, 52,0, 252,00196078431372, 53,0, 252,00196078431372, 54,0, 252,00196078431372, 55,0, 252,00196078431372, 56,0, 252,00196078431372, 57,0, 252,00196078431372, 58,0, 252,00196078431372, 59,0, 252,00196078431372, 60,0, 252,00196078431372, 61,0, 252,00196078431372, 62,0, 252,00196078431372, 63,0, 252,00196078431372, 64,0, 252,00196078431372, 65,0, 252,00196078431372, 66,0, 252,00196078431372, 67,0, 252,00196078431372, 68,0, 252,00196078431372, 69,0, 252,00196078431372, 70,0, 252,00196078431372, 71,0, 252,00196078431372, 72,0, 252,00196078431372, 73,0, 252,00196078431372, 74,0, 252,00196078431372, 75,0, 252,00196078431372, 76,0, 252,00196 078431372, 77,0, 252,00196078431372, 78,0, 252,00196078431372, 79,0, 252,00196078431372, 80,0, 252,00196078431372, 81,0, 252,00196078431372, 82,0, 252,00196078431372, 83,0, 252,00196078431372, 84,0, 252,00196078431372, 85,0, 252,00196078431372, 86,0, 252,00196078431372, 87,0, 252,00196078431372, 88,0, 252,00196078431372, 89,0, 252,00196078431372, 90,0, 252,00196078431372, 91,0, 252,00196078431372, 92,0, 252,00196078431372, 93,0, 252,00196078431372, 93,00196078431372, 252,0, 94,0, 251,00196078431372, 96,060, 251,00196078431372, 95,060, 251,0019

Resultados com código @Sundrops usando cv2:

[94, 252, 93, 253, 0, 253, 0, 286, 188, 286, 188, 269, 187, 268, 187, 252]

@Kongsea Eu não testei a implementação do @Sundrops cv2, mas a ideia básica deve ser a mesma. Eles produzirão resultados diferentes, pois há uma quantidade infinita de conjuntos de pontos que você pode usar para descrever uma forma. Mas, caso contrário, ambos devem funcionar. Eu simplesmente não tinha o cv2 instalado, então escrevi algo que não requer isso.

@Kongsea @waspinator Eu testei meu código. Funciona.

Obrigado @Sundrops @waspinator .
Vou tentar.

@waspinator Existe alguma maneira de converter o vértice do polígono de segmentação em RLE? Meu alvo é feito iscrowd = 1

@Sundrops Por que você comentou esta parte em seu código?

# if len(contour) > 4:
    #     segmentation.append(contour)
# if len(segmentation) == 0:
#     continue

De fato, precisamos lidar com esse caso, certo?

@ Yuliang-Zou Você deve descomentar esta parte quando os contornos forem usados ​​para Detectron. Beacase o Detectron irá tratá-lo como um retângulo quando len (contorno) == 4. Eu atualizei meu código anterior.

@Sundrops Obrigado. Mas ainda precisamos lidar com len(contour)==2 , certo?

@ Yuliang-Zou Sim, mas o código if len(contour) > 4: tem identificador len(contour)==2 e len(contour)==4 .

@Sundrops entendo, obrigado!

Escrevi uma biblioteca e um artigo para ajudar na criação de conjuntos de dados no estilo COCO.

https://patrickwasp.com/create-your-own-coco-style-dataset/

@Sundrops & @topcomma Tenho problemas para carregar os dados anotados em picocotols porque minha anotação inclui segmentação sem máscara. alguma ideia de como visualizar a anotação sem máscara nas picocotools?

@Sundrops
ann: {
"segmentação": [
[312,29, 562,89, 402,25, 511,49, 400,96, 425,38, 398,39, 372,69, 388,11, 332,85, 318,71, 325,14, 295,58, 305,86, 269,88, 314,86, 258,31, 337,99, 217,19, 321,29, 182,47, 348,49, 132,13 , 358,55, 159,36, 377,83, 116,95, 421,53, 167,07, 499,92, 232,61, 560,32, 300,72, 571,89]
],
"área": ​​54652,9556,
"iscrowd": 0,
"id_imagem": 480023,
"bbox": [116,95, 305,86, 285,3, 266,03],
"category_id": 58,
"id": 86
}
Como posso calcular a área com mask.py em coco-api? Obrigada.
Meu código é o seguinte, mas com erro:

segmentação = ann ['segmentação']
bimask = np.array (segmentação, dtype = np.uint8, pedido = 'F')
imprimir ("bimask:", bimask)
rleObjs = mask.encode (bimask)
print ("rleObjs:", rleObjs)
area = mask.area (rleObjs)
imprimir ("área:", área)

@manketon
Talvez você possa tentar o cv2, mas não tenho certeza se está certo. É apenas um exemplo da Remoção de contornos de uma imagem usando Python e OpenCV .

def is_contour_bad(c):
    # approximate the contour
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)
    # return True if it is not a rectangle
    return not len(approx) == 4
image = cv2.imread('xx.jpg')
contours = ann['segmentation']
mask = np.ones(image.shape[:2], dtype="uint8") * 255
# loop over the contours
for c in contours:
    # if the contour is not a rectangle, draw it on the mask
    if is_contour_bad(c):
        cv2.drawContours(mask, [c], -1, 0, -1)
area = (mask==0).sum()

@Sundrops @waspinator Tenho uma pergunta. Se minha máscara de objeto original tiver grandes orifícios, quando for convertida em polígonos, como devo convertê-la de volta corretamente? As funções de decodificação e mesclagem na API coco tratariam o furo como partes do objeto, portanto, quando convertido de volta, os furos se tornariam máscaras. Como devo fazer neste caso?

@ wangg12 Pelo que eu sei, o COCO não tem uma maneira nativa de codificar buracos.

for contour in contours:
        contour = contour.flatten().tolist()
        segmentation.append(contour)
        if len(contour) > 4:
            segmentation.append(contour)

Olá @Sundrops , sinceramente obrigado por seus códigos. Sou um iniciante no Detectron. Eu me sinto confuso por que o comprimento do contorno (que é uma lista) deveria ser maior que 4, ou seja, o que acontecerá com o modelo quando o comprimento for menor que 4. Uma de suas respostas disse que a Detectron vai tratá-lo como um retângulo. Na minha perspectiva, pode haver alguns objetos com a forma de retângulo, então acho que está tudo bem. Além disso, gostaria de saber se você anexa o contorno duas vezes em uma iteração. Acho que o código certo deve ser.

for contour in contours:
        contour = contour.flatten().tolist()
        if len(contour) > 4:
            segmentation.append(contour)

Será muito apreciado se você puder me dar algumas sugestões. Muito obrigado!

@BobZhangHT Sim, ele deve anexar o contorno uma vez em uma iteração.
Para sua primeira pergunta, se len (ann ['segmentation'] [0]) == 4, cocoapi assumirá que todos são retangulares.

# cocoapi/PythonAPI/pycocotools/coco.py
def annToRLE(self, ann):
    t = self.imgs[ann['image_id']]
    h, w = t['height'], t['width']
    segm = ann['segmentation']
    if type(segm) == list:
        rles = maskUtils.frPyObjects(segm, h, w) 
        rle = maskUtils.merge(rles)
   ......
# cocoapi/PythonAPI/pycocotools/_mask.pyx
def frPyObjects(pyobj, h, w):
    # encode rle from a list of python objects
    if type(pyobj) == np.ndarray:
        objs = frBbox(pyobj, h, w)
    elif type(pyobj) == list and len(pyobj[0]) == 4:
        objs = frBbox(pyobj, h, w)
    elif type(pyobj) == list and len(pyobj[0]) > 4:
        objs = frPoly(pyobj, h, w)
   ......

@Sundrops Obrigado pela sua resposta!

Eu interpreto poli como uma lista de polígonos, definidos pelos vértices, como [[x1, y1, x2, y2 ... xN, yN], ... [x1, y1, x2, y2 ... xN, yN]], onde as coordenadas são de a mesma escala da imagem. As máscaras, codificadas desta forma, são mostradas corretamente pelo CocoAPI [1] Mas você pode querer obter uma resposta “oficial”. [1] https://github.com/cocodataset/cocoapi https://github.com/cocodataset/cocoapi

Em 2 de fevereiro de 2018, às 09h18, Hu Xingui @ . * > escreveu: @amokeev https://github.com/amokeev , Tem certeza que o número na segmentação é (x, y) vértices? Por exemplo, "66916", um grande número! Outro, embora eu tenha definido "iscrowd" como "1" para o formato RLE, ainda não foi possível executar no Detectron. - Você está recebendo isso porque foi mencionado. Responda a este e-mail diretamente, visualize-o no GitHub < # 100 (comentário) > ou ignore o tópico https://github.com/notifications/unsubscribe-auth/AFlh63ObnXg-DcaDKwIi3pB4Ppig464Hks5tQsTkgaJpZM4R2tN3 .

Como posso transformar RLE em polígonos quando "iscrowd" é "1" porque o matterport / MaskRCNN funciona apenas com polígonos. @amokeev. Desejo usar a implementação de matterport de maskRCNN com um conjunto de dados coco criado usando pycococreator.

Esta página foi útil?
0 / 5 - 0 avaliações