Skip to content

Isoler les objets de segmentation

Après avoir effectué la tâche de segmentation, il est parfois souhaitable d'extraire les objets isolés des résultats de l'inférence. Ce guide fournit une recette générique sur la façon d'accomplir cette tâche en utilisant lemode prédictifde Ultralytics .

Exemple de segmentation d'objets isolés

Visite guidée de la recette

  1. Commence par les importations nécessaires

    from pathlib import Path
    
    import cv2
    import numpy as np
    from ultralytics import YOLO
    
    Ultralytics Installer

    Voir la section Ultralytics Quickstart Installation pour une démonstration rapide de l'installation des bibliothèques requises.


  2. Charge un modèle et exécute-le predict() sur une source.

    from ultralytics import YOLO
    
    # Load a model
    model = YOLO('yolov8n-seg.pt')
    
    # Run inference
    results = model.predict()
    

    Pas d'arguments de prédiction ?

    Sans spécifier de source, les images d'exemple de la bibliothèque seront utilisées :

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

    Ceci est utile pour effectuer des tests rapides avec le predict() méthode.

    Pour plus d'informations sur les modèles de segmentation, consulte le site Tâche du segment page. Pour en savoir plus sur predict() méthode, voir Mode prédiction de la documentation.


  3. Itère maintenant sur les résultats et les contours. Pour les flux de travail qui veulent enregistrer une image dans un fichier, l'image source base-name et la détection class-label sont récupérés pour une utilisation ultérieure (facultatif).

    # (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. Pour en savoir plus sur la façon de travailler avec les résultats de la détection, voir la section des boîtes pour le mode prédiction.
    2. Pour en savoir plus predict() voir les résultats Travailler avec les résultats du mode prédictif
    Pour la boucle

    Une image unique n'itère la première boucle qu'une seule fois. Une image unique ne comportant qu'une seule détection n'effectuera qu' une seule fois l'itération de chaque boucle.


  4. Commence par générer un masque binaire à partir de l'image source, puis dessine un contour rempli sur le masque. Cela permettra d'isoler l'objet des autres parties de l'image. Un exemple de bus.jpg pour l'une des personnes détectées person Les objets de la classe sont illustrés à droite.

    Image de masque binaire

    # 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. Pour plus d'informations sur c.masks.xy voir Section des masques du mode prédictif.

    2. Ici, les valeurs sont coulées dans np.int32 pour la compatibilité avec drawContours() d'OpenCV.

    3. L'OpenCV drawContours() La fonction s'attend à ce que les contours aient une forme de [N, 1, 2] élargis la section ci-dessous pour plus de détails.

    Étends-toi pour comprendre ce qui se passe lors de la définition du contour variable.

    • c.masks.xy : : Fournit les coordonnées des points du contour du masque au format. (x, y). Pour plus de détails, reporte-toi à la rubrique Section des masques du mode prédictif.

    • .pop() : : Comme masks.xy est une liste contenant un seul élément, cet élément est extrait à l'aide de la fonction pop() méthode.

    • .astype(np.int32) : : Utilisation masks.xy sera retourné avec un type de données de float32mais cela ne sera pas compatible avec le logiciel OpenCV drawContours() et cela changera le type de données en int32 pour la compatibilité.

    • .reshape(-1, 1, 2) : : Reformate les données dans la forme requise de [N, 1, 2]N est le nombre de points de contour, chaque point étant représenté par une seule entrée. 1et l'entrée est composée de 2 valeurs. Les -1 indique que le nombre de valeurs sur cette dimension est flexible.

    Développe pour une explication de la drawContours() configuration.

    • Encapsuler le contour entre crochets, [contour]a permis de générer efficacement le masque de contour souhaité au cours des tests.

    • La valeur -1 spécifiée pour le drawContours() Le paramètre indique à la fonction de dessiner tous les contours présents dans l'image.

    • Le tuple (255, 255, 255) représente la couleur blanche, qui est la couleur souhaitée pour dessiner le contour dans ce masque binaire.

    • L'ajout de cv2.FILLED colorera de la même façon tous les pixels entourés par la limite du contour, dans ce cas, tous les pixels entourés seront blancs.

    • Voir Documentation OpenCV sur drawContours() pour plus d'informations.


  5. Ensuite, il y a deux options pour la suite de l'image à partir de ce point et une option ultérieure pour chacune d'entre elles.

    Options d'isolation des objets

    # Create 3-channel mask
    mask3ch = cv2.cvtColor(b_mask, cv2.COLOR_GRAY2BGR)
    
    # Isolate object with binary mask
    isolated = cv2.bitwise_and(mask3ch, img)
    
    Comment cela fonctionne-t-il ?
    • Tout d'abord, le masque binaire est converti d'une image à un seul canal en une image à trois canaux. Cette conversion est nécessaire pour l'étape suivante où le masque et l'image originale sont combinés. Les deux images doivent avoir le même nombre de canaux pour être compatibles avec l'opération de mélange.

    • L'image originale et le masque binaire à trois canaux sont fusionnés à l'aide de la fonction OpenCV bitwise_and(). Cette opération permet de conserver seulement les valeurs des pixels qui sont supérieures à zéro (> 0) des deux images. Puisque les pixels du masque sont supérieurs à zéro (> 0) seulement dans la région du contour, les pixels restants de l'image originale sont ceux qui se superposent au contour.

    Isoler avec des pixels noirs : Sous-options

    Image en taille réelle

    Il n'y a pas d'étapes supplémentaires à suivre pour conserver l'image en taille réelle.

    Exemple Taille réelle Image d'un objet isolé Fond noir
    Exemple de sortie en taille réelle

    Image de l'objet recadré

    Des étapes supplémentaires sont nécessaires pour recadrer l'image afin de n'inclure que la région de l'objet.

    Exemple de recadrage d'une image d'objet isolé Fond noir

    # (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. Pour plus d'informations sur les résultats des boîtes englobantes, voir la section Boîtes du mode Prédire.
    Que fait ce code ?
    • Le c.boxes.xyxy.cpu().numpy() récupère les boîtes de délimitation sous la forme d'un tableau NumPy dans le fichier xyxyxmin, ymin, xmaxet ymax représentent les coordonnées du rectangle de la boîte de délimitation. Voir Section des boîtes du mode prédictif pour plus de détails.

    • Le squeeze() supprime toutes les dimensions inutiles du tableau NumPy, en veillant à ce qu'il ait la forme attendue.

    • Conversion des valeurs de coordonnées à l'aide de .astype(np.int32) change le type de données des coordonnées de la boîte de float32 à int32Ils sont donc compatibles avec le recadrage d'images à l'aide de tranches d'index.

    • Enfin, la région de la boîte englobante est découpée dans l'image à l'aide d'un découpage par index. Les limites sont définies par le [ymin:ymax, xmin:xmax] coordonnées de la boîte de délimitation de la détection.

    # Isolate object with transparent background (when saved as PNG)
    isolated = np.dstack([img, b_mask])
    
    Comment cela fonctionne-t-il ?
    • Utilisation du logiciel NumPy dstack() (empilement de tableaux le long de l'axe de profondeur) en conjonction avec le masque binaire généré, créera une image à quatre canaux. Cela permet à tous les pixels situés en dehors du contour de l'objet d'être transparents lors de l'enregistrement sous forme d'image. PNG fichier.

    Isoler avec des pixels transparents : Sous-options

    Image en taille réelle

    Il n'y a pas d'étapes supplémentaires à suivre pour conserver l'image en taille réelle.

    Exemple Taille réelle Image d'objet isolé Sans arrière-plan
    Exemple de sortie en taille réelle + arrière-plan transparent

    Image de l'objet recadré

    Des étapes supplémentaires sont nécessaires pour recadrer l'image afin de n'inclure que la région de l'objet.

    Exemple de recadrage d'une image d'objet isolé sans arrière-plan

    # (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. Pour plus d'informations sur les résultats des boîtes englobantes, voir la section Boîtes du mode Prédire.
    Que fait ce code ?
    • Lors de l'utilisation de c.boxes.xyxy.cpu().numpy()les boîtes de délimitation sont renvoyées sous la forme d'un tableau NumPy, à l'aide de la fonction xyxy format des coordonnées de la boîte, qui correspondent aux points xmin, ymin, xmax, ymax pour la boîte de délimitation (rectangle), voir Section des boîtes du mode prédictif pour plus d'informations.

    • Ajouter squeeze() s'assure que toutes les dimensions superflues sont supprimées du tableau NumPy.

    • Conversion des valeurs de coordonnées à l'aide de .astype(np.int32) change le type de données des coordonnées de la boîte de float32 à int32 qui sera compatible lors du recadrage de l'image à l'aide des tranches d'index.

    • Enfin, la région de l'image pour la boîte de délimitation est découpée à l'aide d'un découpage par index, où les limites sont définies à l'aide de la fonction [ymin:ymax, xmin:xmax] coordonnées de la boîte de délimitation de la détection.

    Et si je veux que l'objet recadré comprenne l' arrière-plan ?

    Il s'agit d'une fonctionnalité intégrée à la bibliothèque Ultralytics . Voir l'article save_crop argument pour Arguments d'inférence du mode prédictif pour plus de détails.


  6. Ce qu'il faut faire ensuite est entièrement laissé à ton appréciation en tant que développeur. Un exemple basique d'une étape suivante possible (enregistrer l'image dans un fichier pour une utilisation ultérieure) est présenté.

    • NOTE : cette étape est facultative et peut être ignorée si elle n'est pas nécessaire pour ton cas d'utilisation spécifique.
    Exemple Étape finale
    # Save isolated object to file
    _ = cv2.imwrite(f'{img_name}_{label}-{ci}.png', iso_crop)
    
    • Dans cet exemple, le img_name est le nom de base du fichier image source, label est le nom de la classe détectée, et ci est l'index de la détection de l'objet (en cas d'instances multiples portant le même nom de classe).

Exemple de code complet

Ici, toutes les étapes de la section précédente sont combinées en un seul bloc de code. Pour une utilisation répétée, il serait optimal de définir une fonction pour exécuter certaines ou toutes les commandes contenues dans la section for-Mais c'est un exercice que nous laissons au lecteur.

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. La ligne qui remplit contour est combinée en une seule ligne ici, alors qu'elle était divisée en plusieurs lignes ci-dessus.
  2. C'est à toi de décider ce que tu veux mettre ici !
  3. Voir Mode prédictif pour plus d'informations.
  4. Voir Tâche de segment pour plus d'informations.
  5. En savoir plus sur Travailler avec des résultats
  6. En savoir plus sur les résultats du masque de segmentation


Créé le 2023-11-27, Mis à jour le 2024-04-27
Auteurs : glenn-jocher (6), RizwanMunawar (1), Burhan-Q (1)

Commentaires