Salta para o conte√ļdo

Isolamento de objetos de segmentação

Depois de executar a Tarefa de Segmento, às vezes é desejável extrair os objetos isolados dos resultados da inferência. Este guia fornece uma receita genérica sobre como fazer isso usando o Ultralytics Predict Mode.

Exemplo de segmentação de objectos isolados

Passeio pelas receitas

  1. Come√ßa com as importa√ß√Ķes necess√°rias

    from pathlib import Path
    
    import cv2 as cv
    import numpy as np
    from ultralytics import YOLO
    
    Ultralytics Instala

    Consulta a secção Ultralytics Quickstart Installation para obteres um passo-a-passo rápido sobre a instalação das bibliotecas necessárias.


  2. Carrega um modelo e executa predict() numa fonte.

    from ultralytics import YOLO
    
    # Load a model
    model = YOLO('yolov8n-seg.pt')
    
    # Run inference
    result = model.predict()
    
    N√£o tens argumentos de previs√£o?

    Sem especificar uma fonte, ser√£o utilizadas as imagens de exemplo da biblioteca:

    'ultralytics/assets/bus.jpg'
    'ultralytics/assets/zidane.jpg'
    

    Isto √© √ļtil para testes r√°pidos com o predict() m√©todo.

    Para obter informa√ß√Ķes adicionais sobre os modelos de segmenta√ß√£o, visita a p√°gina Tarefa do segmento p√°gina. Para saberes mais sobre predict() m√©todo, v√™ Modo de previs√£o sec√ß√£o da Documenta√ß√£o.


  3. Agora repete os resultados e os contornos. Para fluxos de trabalho que pretendam guardar uma imagem num ficheiro, a imagem de origem base-name e a deteção class-label são recuperados para utilização posterior (opcional).

    # (2) Iterate detection results (helpful for multiple images)
    for r in res:
        img = np.copy(r.orig_img)
        img_name = Path(r.path).stem # source image base-name
    
        # Iterate each object contour (multiple detections)
        for ci,c in enumerate(r):
            # (1) Get detection class name
            label = c.names[c.boxes.cls.tolist().pop()]
    
    1. Para saberes mais sobre como trabalhar com resultados de deteção, consulta a secção Caixas para o Modo de previsão.
    2. Para saberes mais sobre predict() resultados ver Trabalho com resultados do modo de previs√£o
    For-Loop

    Uma √ļnica imagem itera o primeiro ciclo apenas uma vez. Uma √ļnica imagem com uma √ļnica dete√ß√£o ir√° iterar cada ciclo apenas uma vez.


  4. Começa por gerar uma máscara binária a partir da imagem de origem e depois desenha um contorno preenchido na máscara. Isto permitirá que o objeto seja isolado das outras partes da imagem. Um exemplo de bus.jpg para um dos detectados person é mostrado à direita.

    Imagem de m√°scara bin√°ria

    # Create binary mask
    b_mask = np.zeros(img.shape[:2], np.uint8)
    
    # (1) Extract contour result
    contour = c.masks.xy.pop()
    # (2) Changing the type
    contour = contour.astype(np.int32)
    # (3) Reshaping
    contour = contour.reshape(-1, 1, 2)
    
    
    # Draw contour onto mask
    _ = cv.drawContours(b_mask,
                        [contour],
                        -1,
                        (255, 255, 255),
                        cv.FILLED)
    
    1. Para mais informa√ß√Ķes sobre c.masks.xy v√™ Sec√ß√£o de m√°scaras do modo de previs√£o.

    2. Aqui, os valores s√£o convertidos em np.int32 para compatibilidade com drawContours() do OpenCV.

    3. O OpenCV drawContours() espera que os contornos tenham uma forma de [N, 1, 2] expande a sec√ß√£o abaixo para obteres mais informa√ß√Ķes.

    Expande para compreender o que est√° a acontecer ao definir o contour vari√°vel.

    • c.masks.xy :: Fornece as coordenadas dos pontos de contorno da m√°scara no formato (x, y). Para mais pormenores, consulta a Sec√ß√£o de m√°scaras do modo de previs√£o.

    • .pop() :: Como masks.xy √© uma lista que cont√©m um √ļnico elemento, este elemento √© extra√≠do utilizando o pop() m√©todo.

    • .astype(np.int32) :: Usando masks.xy retornar√° com um tipo de dados de float32mas isso n√£o ser√° compat√≠vel com o OpenCV drawContours() por isso, altera o tipo de dados para int32 para compatibilidade.

    • .reshape(-1, 1, 2) :: Reformata os dados na forma requerida de [N, 1, 2] onde N √© o n√ļmero de pontos de contorno, sendo cada ponto representado por uma √ļnica entrada 1e a entrada √© composta por 2 valores. Os -1 indica que o n√ļmero de valores ao longo desta dimens√£o √© flex√≠vel.

    Expande para uma explicação do drawContours() configuração.

    • Encapsulando o contour entre par√™nteses rectos, [contour], foi considerado eficaz para gerar a m√°scara de contorno desejada durante os testes.

    • O valor -1 especificado para o drawContours() d√° instru√ß√Ķes √† fun√ß√£o para desenhar todos os contornos presentes na imagem.

    • O tuple (255, 255, 255) representa a cor branca, que √© a cor desejada para desenhar o contorno nesta m√°scara bin√°ria.

    • A adi√ß√£o de cv.FILLED Colore todos os pix√©is delimitados pelo contorno com a mesma cor, neste caso, todos os pix√©is delimitados ser√£o brancos.

    • V√™ Documenta√ß√£o do OpenCV em drawContours() para mais informa√ß√Ķes.


  5. A seguir, h√° duas op√ß√Ķes para avan√ßar com a imagem a partir deste ponto e uma op√ß√£o subsequente para cada uma delas.

    Op√ß√Ķes de isolamento de objectos

    # Create 3-channel mask
    mask3ch = cv.cvtColor(b_mask, cv.COLOR_GRAY2BGR)
    
    # Isolate object with binary mask
    isolated = cv.bitwise_and(mask3ch, img)
    
    Como é que isto funciona?
    • Primeiro, a m√°scara bin√°ria √© convertida de uma imagem de um canal para uma imagem de tr√™s canais. Esta convers√£o √© necess√°ria para o passo seguinte, em que a m√°scara e a imagem original s√£o combinadas. Ambas as imagens devem ter o mesmo n√ļmero de canais para serem compat√≠veis com a opera√ß√£o de combina√ß√£o.

    • A imagem original e a m√°scara bin√°ria de tr√™s canais s√£o fundidas utilizando a fun√ß√£o OpenCV bitwise_and(). Esta opera√ß√£o mant√©m apenas valores de pix√©is superiores a zero (> 0) de ambas as imagens. Uma vez que os pix√©is da m√°scara s√£o maiores do que zero (> 0) apenas dentro da regi√£o de contorno, os pixels restantes da imagem original s√£o aqueles que se sobrep√Ķem ao contorno.

    Isolar com p√≠xeis pretos: Sub-op√ß√Ķes

    Imagem em tamanho real

    N√£o s√£o necess√°rios passos adicionais para manter a imagem em tamanho real.

    Exemplo de imagem de objeto isolado em tamanho real com fundo preto
    Exemplo de saída em tamanho real

    Imagem do objeto recortada

    Passos adicionais necess√°rios para cortar a imagem para incluir apenas a regi√£o do objeto.

    Exemplo de recorte de imagem de objeto isolado com fundo preto

    # (1) Bounding box coordinates
    x1, y1, x2, y2 = c.boxes.xyxy.cpu().numpy().squeeze().astype(np.int32)
    # Crop image to object region
    iso_crop = isolated[y1:y2, x1:x2]
    

    1. Para mais informa√ß√Ķes sobre os resultados da caixa delimitadora, consulta a sec√ß√£o Caixas do modo Prever
    O que é que este código faz?
    • O c.boxes.xyxy.cpu().numpy() recupera as caixas delimitadoras como uma matriz NumPy no xyxy formato, em que xmin, ymin, xmaxe ymax representa as coordenadas do ret√Ęngulo da caixa delimitadora. V√™ Sec√ß√£o de caixas do modo de previs√£o para mais pormenores.

    • O squeeze() remove quaisquer dimens√Ķes desnecess√°rias da matriz NumPy, assegurando que tem a forma esperada.

    • Converte os valores das coordenadas utilizando .astype(np.int32) altera o tipo de dados das coordenadas da caixa de float32 para int32tornando-os compat√≠veis com o corte de imagens utilizando cortes de √≠ndice.

    • Finalmente, a regi√£o da caixa delimitadora √© cortada da imagem utilizando o corte de √≠ndice. Os limites s√£o definidos pelo par√Ęmetro [ymin:ymax, xmin:xmax] coordenadas da caixa delimitadora da dete√ß√£o.

    # Isolate object with transparent background (when saved as PNG)
    isolated = np.dstack([img, b_mask])
    
    Como é que isto funciona?
    • Usando o NumPy dstack() (empilhamento de matriz ao longo do eixo de profundidade) em conjunto com a m√°scara bin√°ria gerada, criar√° uma imagem com quatro canais. Isto permite que todos os pixels fora do contorno do objeto sejam transparentes quando guardados como uma PNG arquiva.

    Isolar com pixels transparentes: Sub-op√ß√Ķes

    Imagem em tamanho real

    N√£o s√£o necess√°rios passos adicionais para manter a imagem em tamanho real.

    Exemplo de imagem de objeto isolado em tamanho real sem fundo
    Exemplo de saída em tamanho real + fundo transparente

    Imagem do objeto recortada

    Passos adicionais necess√°rios para cortar a imagem para incluir apenas a regi√£o do objeto.

    Exemplo Cortar imagem de objeto isolado sem fundo

    # (1) Bounding box coordinates
    x1, y1, x2, y2 = c.boxes.xyxy.cpu().numpy().squeeze().astype(np.int32)
    # Crop image to object region
    iso_crop = isolated[y1:y2, x1:x2]
    

    1. Para mais informa√ß√Ķes sobre os resultados da caixa delimitadora, consulta a sec√ß√£o Caixas do modo Prever
    O que é que este código faz?
    • Quando utilizas c.boxes.xyxy.cpu().numpy()as caixas delimitadoras s√£o devolvidas como uma matriz NumPy, utilizando a fun√ß√£o xyxy formato das coordenadas da caixa, que correspondem aos pontos xmin, ymin, xmax, ymax para a caixa delimitadora (ret√Ęngulo), consulta Sec√ß√£o de caixas do modo de previs√£o para mais informa√ß√Ķes.

    • Acrescentar squeeze() assegura que quaisquer dimens√Ķes estranhas s√£o removidas da matriz NumPy.

    • Converte os valores das coordenadas utilizando .astype(np.int32) altera o tipo de dados das coordenadas da caixa de float32 para int32 que ser√° compat√≠vel quando recortares a imagem utilizando cortes de √≠ndice.

    • Finalmente, a regi√£o da imagem para a caixa delimitadora √© cortada utilizando o corte de √≠ndice, em que os limites s√£o definidos utilizando a fun√ß√£o [ymin:ymax, xmin:xmax] coordenadas da caixa delimitadora da dete√ß√£o.

    E se eu quiser o objeto cortado incluindo o fundo?

    Esta é uma funcionalidade incorporada na biblioteca Ultralytics . Vê a página save_crop argumento para Argumentos de inferência do modo de previsão para mais pormenores.


  6. O que fazer a seguir √© inteiramente deixado ao teu crit√©rio como programador. √Č apresentado um exemplo b√°sico de um passo seguinte poss√≠vel (guardar a imagem num ficheiro para utiliza√ß√£o futura).

    • NOTA: este passo √© opcional e pode ser ignorado se n√£o for necess√°rio para o teu caso de utiliza√ß√£o espec√≠fico.
    Exemplo Etapa final
    # Save isolated object to file
    _ = cv.imwrite(f'{img_name}_{label}-{ci}.png', iso_crop)
    
    • Neste exemplo, o img_name √© o nome base do ficheiro de imagem de origem, label √© o nome da classe detectada, e ci √© o √≠ndice da dete√ß√£o do objeto (no caso de m√ļltiplas inst√Ęncias com o mesmo nome de classe).

Código de exemplo completo

Aqui, todos os passos da sec√ß√£o anterior s√£o combinados num √ļnico bloco de c√≥digo. Para uma utiliza√ß√£o repetida, seria √≥timo definir uma fun√ß√£o para executar alguns ou todos os comandos contidos no bloco for-loops, mas isso √© um exerc√≠cio deixado ao leitor.

from pathlib import Path

import cv2 as cv
import numpy as np
from ultralytics import YOLO

m = YOLO('yolov8n-seg.pt')#(4)!
res = m.predict()#(3)!

# iterate detection results (5)
for r in res:
    img = np.copy(r.orig_img)
    img_name = Path(r.path).stem

    # iterate each object contour (6)
    for ci,c in enumerate(r):
        label = c.names[c.boxes.cls.tolist().pop()]

        b_mask = np.zeros(img.shape[:2], np.uint8)

        # Create contour mask (1)
        contour = c.masks.xy.pop().astype(np.int32).reshape(-1, 1, 2)
        _ = cv.drawContours(b_mask, [contour], -1, (255, 255, 255), cv.FILLED)

        # Choose one:

        # OPTION-1: Isolate object with black background
        mask3ch = cv.cvtColor(b_mask, cv.COLOR_GRAY2BGR)
        isolated = cv.bitwise_and(mask3ch, img)

        # OPTION-2: Isolate object with transparent background (when saved as PNG)
        isolated = np.dstack([img, b_mask])

        # OPTIONAL: detection crop (from either OPT1 or OPT2)
        x1, y1, x2, y2 = c.boxes.xyxy.cpu().numpy().squeeze().astype(np.int32)
        iso_crop = isolated[y1:y2, x1:x2]

        # TODO your actions go here (2)
  1. A linha que preenche o contour √© combinada numa √ļnica linha aqui, onde foi dividida em v√°rias acima.
  2. O que vai aqui é contigo!
  3. Consulta o Modo de previs√£o para obteres mais informa√ß√Ķes.
  4. Para mais informa√ß√Ķes, consulta Tarefa de segmento.
  5. Sabe mais sobre Trabalhar com resultados
  6. Sabe mais sobre os resultados da máscara de segmentação


Criado em 2023-11-27, Atualizado em 2024-01-14
Autores: glenn-jocher (4), RizwanMunawar (1), Burhan-Q (1)

Coment√°rios