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
    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
    results = 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
    _ = cv2.drawContours(b_mask,
                        [contour],
                        -1,
                        (255, 255, 255),
                        cv2.FILLED)
    
    1. Para mais informações sobre c.masks.xySecçã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 cv2.FILLED Colore todos os pixéis delimitados pelo contorno com a mesma cor, neste caso, todos os pixéis delimitados serão brancos.

    • Documentação do OpenCV em drawContours() para mais informações.


  5. De seguida, 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 = cv2.cvtColor(b_mask, cv2.COLOR_GRAY2BGR)
    
    # Isolate object with binary mask
    isolated = cv2.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
    _ = cv2.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
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)
        _ = cv2.drawContours(b_mask, [contour], -1, (255, 255, 255), cv2.FILLED)

        # Choose one:

        # OPTION-1: Isolate object with black background
        mask3ch = cv2.cvtColor(b_mask, cv2.COLOR_GRAY2BGR)
        isolated = cv2.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-04-27
Autores: glenn-jocher (6), RizwanMunawar (1), Burhan-Q (1)

Comentários