Skip to content

Utilitaires simples

code avec perspective

Le ultralytics est livré avec une myriade d'utilitaires qui peuvent aider, améliorer et accélérer tes processus de travail. Il en existe beaucoup d'autres, mais en voici quelques-uns qui seront utiles à la plupart des développeurs. Ils constituent également un excellent point de référence à utiliser lors de l'apprentissage de la programmation.



Regarde : Ultralytics Utilitaires | Annotation automatique, API d'exploration et conversion de jeux de données

Données

YOLO Explorateur de données

YOLO Explorateur a été ajouté dans le 8.1.0 est un outil puissant que tu peux utiliser pour mieux comprendre ton ensemble de données. L'une des principales fonctions de YOLO Explorer est la possibilité d'utiliser des requêtes textuelles pour trouver des instances d'objets dans ton jeu de données.

Étiquetage automatique / Annotations

L'annotation des ensembles de données est un processus très gourmand en ressources et en temps. Si tu disposes d'un modèle de détection d'objets YOLO entraîné sur une quantité raisonnable de données, tu peux l'utiliser pour auto-annoter des données supplémentaires (format de segmentation). SAM pour annoter automatiquement des données supplémentaires (format de segmentation).

from ultralytics.data.annotator import auto_annotate

auto_annotate(  # (1)!
    data="path/to/new/data",
    det_model="yolov8n.pt",
    sam_model="mobile_sam.pt",
    device="cuda",
    output_dir="path/to/save_labels",
)
  1. Cette fonction ne renvoie rien

  2. Voir la section de référence pour annotator.auto_annotate pour en savoir plus sur le fonctionnement de cette fonction.

  3. A utiliser en combinaison avec le fonction segments2boxes pour générer des boîtes de délimitation pour la détection d'objets

Convertir COCO au format YOLO

Sert à convertir les annotations COCO JSON dans le format approprié YOLO . Pour les ensembles de données de détection d'objets (boîte englobante), use_segments et use_keypoints devraient tous deux être False

from ultralytics.data.converter import convert_coco

convert_coco(  # (1)!
    "../datasets/coco/annotations/",
    use_segments=False,
    use_keypoints=False,
    cls91to80=True,
)
  1. Cette fonction ne renvoie rien

Pour plus d'informations sur le convert_coco fonction, visite la page de référence

Obtenir les dimensions du cadre de délimitation

from ultralytics.utils.plotting import Annotator
from ultralytics import YOLO
import cv2

model = YOLO('yolov8n.pt')  # Load pretrain or fine-tune model

# Process the image
source = cv2.imread('path/to/image.jpg')
results = model(source)

# Extract results
annotator = Annotator(source, example=model.names)

for box in results[0].boxes.xyxy.cpu():
    width, height, area = annotator.get_bbox_dimension(box)
    print("Bounding Box Width {}, Height {}, Area {}".format(
        width.item(), height.item(), area.item()))

Convertir les cadres de délimitation en segments

Avec les x y w h les données de la boîte de délimitation, les convertir en segments à l'aide de la fonction yolo_bbox2segment fonction. Les fichiers d'images et d'annotations doivent être organisés de cette façon :

data
|__ images
    ├─ 001.jpg
    ├─ 002.jpg
    ├─ ..
    └─ NNN.jpg
|__ labels
    ├─ 001.txt
    ├─ 002.txt
    ├─ ..
    └─ NNN.txt
from ultralytics.data.converter import yolo_bbox2segment

yolo_bbox2segment(  # (1)!
    im_dir="path/to/images",
    save_dir=None,  # saved to "labels-segment" in images directory
    sam_model="sam_b.pt",
)
  1. Cette fonction ne renvoie rien

Visite le yolo_bbox2segment page de référence pour plus d'informations sur cette fonction.

Convertir les segments en boîtes englobantes

Si tu as un jeu de données qui utilise la fonction format du jeu de données de segmentation tu peux facilement les convertir en boîtes de délimitation haut-droite (ou horizontale) (x y w h ) avec cette fonction.

import numpy as np

from ultralytics.utils.ops import segments2boxes

segments = np.array(
    [
        [805, 392, 797, 400, ..., 808, 714, 808, 392],
        [115, 398, 113, 400, ..., 150, 400, 149, 298],
        [267, 412, 265, 413, ..., 300, 413, 299, 412],
    ]
)

segments2boxes([s.reshape(-1, 2) for s in segments])
# >>> array([[ 741.66, 631.12, 133.31, 479.25],
#           [ 146.81, 649.69, 185.62, 502.88],
#           [ 281.81, 636.19, 118.12, 448.88]],
#           dtype=float32) # xywh bounding boxes

Pour comprendre le fonctionnement de cette fonction, visite la page de référence.

Utilitaires

Compression d'images

Compresse un fichier image unique à une taille réduite tout en préservant son rapport d'aspect et sa qualité. Si l'image d'entrée est plus petite que la dimension maximale, elle ne sera pas redimensionnée.

from pathlib import Path

from ultralytics.data.utils import compress_one_image

for f in Path("path/to/dataset").rglob("*.jpg"):
    compress_one_image(f)  # (1)!
  1. Cette fonction ne renvoie rien

Séparation automatique de l'ensemble des données

Divise automatiquement un ensemble de données en train/val/test et enregistre les fractionnements résultants dans autosplit_*.txt fichiers. Cette fonction utilisera un échantillonnage aléatoire, qui n'est pas inclus dans l'utilisation de fraction argument en faveur de la formation.

from ultralytics.data.utils import autosplit

autosplit(  # (1)!
    path="path/to/images",
    weights=(0.9, 0.1, 0.0),  # (train, validation, test) fractional splits
    annotated_only=False,  # split only images with annotation file when True
)
  1. Cette fonction ne renvoie rien

Voir la page de référence pour plus de détails sur cette fonction.

Segment-polygone vers masque binaire

Convertit un polygone unique (sous forme de liste) en un masque binaire de la taille d'image spécifiée. Polygone sous forme de [N, 2] avec N comme le nombre de (x, y) points définissant le contour du polygone.

Avertissement

N doit toujours ĂŞtre Ă©gaux.

import numpy as np

from ultralytics.data.utils import polygon2mask

imgsz = (1080, 810)
polygon = np.array([805, 392, 797, 400, ..., 808, 714, 808, 392])  # (238, 2)

mask = polygon2mask(
    imgsz,  # tuple
    [polygon],  # input as list
    color=255,  # 8-bit binary
    downsample_ratio=1,
)

Boîtes de délimitation

Boîte de délimitation (horizontale) Instances

Pour gérer les données de la boîte de délimitation, la fonction Bboxes Le cours t'aidera à convertir le formatage des coordonnées des boîtes, à mettre à l'échelle les dimensions des boîtes, à calculer les superficies, à inclure les décalages, et plus encore !

import numpy as np

from ultralytics.utils.instance import Bboxes

boxes = Bboxes(
    bboxes=np.array(
        [
            [22.878, 231.27, 804.98, 756.83],
            [48.552, 398.56, 245.35, 902.71],
            [669.47, 392.19, 809.72, 877.04],
            [221.52, 405.8, 344.98, 857.54],
            [0, 550.53, 63.01, 873.44],
            [0.0584, 254.46, 32.561, 324.87],
        ]
    ),
    format="xyxy",
)

boxes.areas()
# >>> array([ 4.1104e+05,       99216,       68000,       55772,       20347,      2288.5])

boxes.convert("xywh")
print(boxes.bboxes)
# >>> array(
#     [[ 413.93, 494.05,  782.1, 525.56],
#      [ 146.95, 650.63,  196.8, 504.15],
#      [  739.6, 634.62, 140.25, 484.85],
#      [ 283.25, 631.67, 123.46, 451.74],
#      [ 31.505, 711.99,  63.01, 322.91],
#      [  16.31, 289.67, 32.503,  70.41]]
# )

Voir le Bboxes section de référence pour plus d'attributs et de méthodes disponibles.

Astuce

La plupart des fonctions suivantes (et bien d'autres) sont accessibles à l'aide de la fonction Bboxes classe mais si tu préfères travailler avec les fonctions directement, vois dans les sous-sections suivantes comment les importer indépendamment.

Boîtes de mise à l'échelle

Lors de la mise à l'échelle d'une image vers le haut ou vers le bas, les coordonnées de la boîte englobante correspondante peuvent être mises à l'échelle de façon appropriée à l'aide de la fonction ultralytics.utils.ops.scale_boxes.

import cv2 as cv
import numpy as np

from ultralytics.utils.ops import scale_boxes

image = cv.imread("ultralytics/assets/bus.jpg")
h, w, c = image.shape
resized = cv.resize(image, None, (), fx=1.2, fy=1.2)
new_h, new_w, _ = resized.shape

xyxy_boxes = np.array(
    [
        [22.878, 231.27, 804.98, 756.83],
        [48.552, 398.56, 245.35, 902.71],
        [669.47, 392.19, 809.72, 877.04],
        [221.52, 405.8, 344.98, 857.54],
        [0, 550.53, 63.01, 873.44],
        [0.0584, 254.46, 32.561, 324.87],
    ]
)

new_boxes = scale_boxes(
    img1_shape=(h, w),  # original image dimensions
    boxes=xyxy_boxes,  # boxes from original image
    img0_shape=(new_h, new_w),  # resized image dimensions (scale to)
    ratio_pad=None,
    padding=False,
    xywh=False,
)

print(new_boxes)  # (1)!
# >>> array(
#     [[  27.454,  277.52,  965.98,   908.2],
#     [   58.262,  478.27,  294.42,  1083.3],
#     [   803.36,  470.63,  971.66,  1052.4],
#     [   265.82,  486.96,  413.98,    1029],
#     [        0,  660.64,  75.612,  1048.1],
#     [   0.0701,  305.35,  39.073,  389.84]]
# )
  1. Boîtes de délimitation mises à l'échelle pour la nouvelle taille de l'image

Conversions de formats de boîtes englobantes

XYXY → XYWH

Convertit les coordonnées de la boîte de délimitation du format (x1, y1, x2, y2) au format (x, y, largeur, hauteur) où (x1, y1) est le coin en haut à gauche et (x2, y2) est le coin en bas à droite.

import numpy as np

from ultralytics.utils.ops import xyxy2xywh

xyxy_boxes = np.array(
    [
        [22.878, 231.27, 804.98, 756.83],
        [48.552, 398.56, 245.35, 902.71],
        [669.47, 392.19, 809.72, 877.04],
        [221.52, 405.8, 344.98, 857.54],
        [0, 550.53, 63.01, 873.44],
        [0.0584, 254.46, 32.561, 324.87],
    ]
)
xywh = xyxy2xywh(xyxy_boxes)

print(xywh)
# >>> array(
#     [[ 413.93,  494.05,   782.1, 525.56],
#     [  146.95,  650.63,   196.8, 504.15],
#     [   739.6,  634.62,  140.25, 484.85],
#     [  283.25,  631.67,  123.46, 451.74],
#     [  31.505,  711.99,   63.01, 322.91],
#     [   16.31,  289.67,  32.503,  70.41]]
# )

Toutes les conversions de la boîte de délimitation

from ultralytics.utils.ops import (
    ltwh2xywh,
    ltwh2xyxy,
    xywh2ltwh,  # xywh → top-left corner, w, h
    xywh2xyxy,
    xywhn2xyxy,  # normalized → pixel
    xyxy2ltwh,  # xyxy → top-left corner, w, h
    xyxy2xywhn,  # pixel → normalized
)

for func in (ltwh2xywh, ltwh2xyxy, xywh2ltwh, xywh2xyxy, xywhn2xyxy, xyxy2ltwh, xyxy2xywhn):
    print(help(func))  # print function docstrings

Voir la docstring pour chaque fonction ou visiter le site ultralytics.utils.ops page de référence pour en savoir plus sur chaque fonction.

Tracé

Dessiner des annotations

Ultralytics comprend une classe Annotator qui peut être utilisée pour annoter n'importe quel type de données. Il est plus facile de l'utiliser avec les boîtes de délimitation de détection d'objet, les points clés de pose et les boîtes de délimitation orientées.

Boîtes de délimitation horizontales

import cv2 as cv
import numpy as np

from ultralytics.utils.plotting import Annotator, colors

names = {  # (1)!
    0: "person",
    5: "bus",
    11: "stop sign",
}

image = cv.imread("ultralytics/assets/bus.jpg")
ann = Annotator(
    image,
    line_width=None,  # default auto-size
    font_size=None,  # default auto-size
    font="Arial.ttf",  # must be ImageFont compatible
    pil=False,  # use PIL, otherwise uses OpenCV
)

xyxy_boxes = np.array(
    [
        [5, 22.878, 231.27, 804.98, 756.83],  # class-idx x1 y1 x2 y2
        [0, 48.552, 398.56, 245.35, 902.71],
        [0, 669.47, 392.19, 809.72, 877.04],
        [0, 221.52, 405.8, 344.98, 857.54],
        [0, 0, 550.53, 63.01, 873.44],
        [11, 0.0584, 254.46, 32.561, 324.87],
    ]
)

for nb, box in enumerate(xyxy_boxes):
    c_idx, *box = box
    label = f"{str(nb).zfill(2)}:{names.get(int(c_idx))}"
    ann.box_label(box, label, color=colors(c_idx, bgr=True))

image_with_bboxes = ann.result()
  1. Les noms peuvent être utilisés à partir de model.names quand travailler avec les résultats de la détection

Boîtes de délimitation orientées (OBB)

import cv2 as cv
import numpy as np

from ultralytics.utils.plotting import Annotator, colors

obb_names = {10: "small vehicle"}
obb_image = cv.imread("datasets/dota8/images/train/P1142__1024__0___824.jpg")
obb_boxes = np.array(
    [
        [0, 635, 560, 919, 719, 1087, 420, 803, 261],  # class-idx x1 y1 x2 y2 x3 y2 x4 y4
        [0, 331, 19, 493, 260, 776, 70, 613, -171],
        [9, 869, 161, 886, 147, 851, 101, 833, 115],
    ]
)
ann = Annotator(
    obb_image,
    line_width=None,  # default auto-size
    font_size=None,  # default auto-size
    font="Arial.ttf",  # must be ImageFont compatible
    pil=False,  # use PIL, otherwise uses OpenCV
)
for obb in obb_boxes:
    c_idx, *obb = obb
    obb = np.array(obb).reshape(-1, 4, 2).squeeze()
    label = f"{obb_names.get(int(c_idx))}"
    ann.box_label(
        obb,
        label,
        color=colors(c_idx, True),
        rotated=True,
    )

image_with_obb = ann.result()

Bounding Boxes Circle Annotation (Circle Label)

import cv2

from ultralytics import YOLO
from ultralytics.utils.plotting import Annotator, colors

model = YOLO("yolov8s.pt")
cap = cv2.VideoCapture("path/to/video/file.mp4")

w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS))
writer = cv2.VideoWriter("Ultralytics circle annotation.avi", cv2.VideoWriter_fourcc(*"MJPG"), fps, (w, h))

while True:
    ret, im0 = cap.read()
    if not ret:
        break

    annotator = Annotator(im0, line_width=2)

    results = model.predict(im0)
    boxes = results[0].boxes.xyxy.cpu()
    clss = results[0].boxes.cls.cpu().tolist()

    for box, cls in zip(boxes, clss):
        x1, y1 = int((box[0] + box[2]) // 2), int((box[1] + box[3]) // 2)
        annotator.circle_label(box, label=model.names[int(cls)], color=colors(int(cls), True))

    writer.write(im0)
    cv2.imshow("Ultralytics circle annotation", im0)

    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

writer.release()
cap.release()
cv2.destroyAllWindows()

Bounding Boxes Text Annotation (Text Label)

import cv2

from ultralytics import YOLO
from ultralytics.utils.plotting import Annotator, colors

model = YOLO("yolov8s.pt")
cap = cv2.VideoCapture("path/to/video/file.mp4")

w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS))
writer = cv2.VideoWriter("Ultralytics text annotation.avi", cv2.VideoWriter_fourcc(*"MJPG"), fps, (w, h))

while True:
    ret, im0 = cap.read()
    if not ret:
        break

    annotator = Annotator(im0, line_width=2)

    results = model.predict(im0)
    boxes = results[0].boxes.xyxy.cpu()
    clss = results[0].boxes.cls.cpu().tolist()

    for box, cls in zip(boxes, clss):
        x1, y1 = int((box[0] + box[2]) // 2), int((box[1] + box[3]) // 2)
        annotator.text_label(box, label=model.names[int(cls)], color=colors(int(cls), True))

    writer.write(im0)
    cv2.imshow("Ultralytics text annotation", im0)

    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

writer.release()
cap.release()
cv2.destroyAllWindows()

Voir le Annotator Page de référence pour en savoir plus.

Divers

Profilage du code

Vérifie la durée d'exécution/de traitement du code à l'aide de with ou en tant que décorateur.

from ultralytics.utils.ops import Profile

with Profile(device="cuda:0") as dt:
    pass  # operation to measure

print(dt)
# >>> "Elapsed time is 9.5367431640625e-07 s"

Ultralytics Formats pris en charge

Tu veux ou tu dois utiliser les formats des types d'images ou de vidéos pris en charge par Ultralytics de façon programmatique ? Utilise ces constantes si tu en as besoin.

from ultralytics.data.utils import IMG_FORMATS, VID_FORMATS

print(IMG_FORMATS)
# {'tiff', 'pfm', 'bmp', 'mpo', 'dng', 'jpeg', 'png', 'webp', 'tif', 'jpg'}

print(VID_FORMATS)
# {'avi', 'mpg', 'wmv', 'mpeg', 'm4v', 'mov', 'mp4', 'asf', 'mkv', 'ts', 'gif', 'webm'}

Rendre divisible

Calcule le nombre entier le plus proche de x pour faire une division Ă©gale lorsqu'on la divise par y.

from ultralytics.utils.ops import make_divisible

make_divisible(7, 3)
# >>> 9
make_divisible(7, 2)
# >>> 8


Created 2024-02-20, Updated 2024-06-20
Authors: glenn-jocher (8), ambitious-octopus (1), IvorZhu331 (1), RizwanMunawar (1), Burhan-Q (2)

Commentaires