Простые утилиты

YOLO model code with 3D perspective visualization

Пакет ultralytics предоставляет множество утилит для поддержки, улучшения и ускорения твоих рабочих процессов. Хотя их гораздо больше, в этом руководстве освещены наиболее полезные для разработчиков инструменты, которые служат практическим справочником при программировании с использованием продуктов Ultralytics.



Watch: Ultralytics Utilities | Auto Annotation, Explorer API and Dataset Conversion

Данные

Автоматическая разметка / Аннотации

Аннотирование набора данных — это ресурсоемкий и трудоемкий процесс. Если у тебя есть модель обнаружения объектов Ultralytics YOLO, обученная на достаточном объеме данных, ты можешь использовать ее вместе с SAM для автоматической разметки дополнительных данных в формате сегментации.

from ultralytics.data.annotator import auto_annotate

auto_annotate(
    data="path/to/new/data",
    det_model="yolo26n.pt",
    sam_model="mobile_sam.pt",
    device="cuda",
    output_dir="path/to/save_labels",
)

Эта функция не возвращает никаких значений. Дополнительные детали:

Визуализация аннотаций набора данных

Эта функция визуализирует аннотации YOLO на изображении перед обучением, помогая выявить и исправить неверные аннотации, которые могут привести к ошибочным результатам обнаружения. Она рисует ограничивающие рамки, подписывает объекты именами классов и регулирует цвет текста в зависимости от яркости фона для лучшей читаемости.

from ultralytics.data.utils import visualize_image_annotations

label_map = {  # Define the label map with all annotated class labels.
    0: "person",
    1: "car",
}

# Visualize
visualize_image_annotations(
    "path/to/image.jpg",  # Input image path.
    "path/to/annotations.txt",  # Annotation file path for the image.
    label_map,
)

Преобразование масок сегментации в формат YOLO

Маски сегментации в формат YOLO

Используй это для преобразования набора данных с масками сегментации в формат сегментации Ultralytics YOLO. Эта функция берет директорию, содержащую маски в бинарном формате, и преобразует их в формат сегментации YOLO.

Преобразованные маски будут сохранены в указанной выходной директории.

from ultralytics.data.converter import convert_segment_masks_to_yolo_seg

# The classes here is the total classes in the dataset.
# for COCO dataset we have 80 classes.
convert_segment_masks_to_yolo_seg(masks_dir="path/to/masks_dir", output_dir="path/to/output_dir", classes=80)

Преобразование COCO в формат YOLO

Используй это для преобразования JSON-аннотаций COCO в формат YOLO. Для наборов данных обнаружения объектов (ограничивающие рамки) установи use_segments и use_keypoints в значение False.

from ultralytics.data.converter import convert_coco

convert_coco(
    "coco/annotations/",
    use_segments=False,
    use_keypoints=False,
    cls91to80=True,
)

Дополнительную информацию о функции convert_coco можно найти на странице справочника.

Получение размеров ограничивающих рамок

import cv2

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

model = YOLO("yolo26n.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(f"Bounding Box Width {width.item()}, Height {height.item()}, Area {area.item()}")

Преобразование ограничивающих рамок в сегменты

Имея данные ограничивающих рамок в формате x y w h, преобразуй их в сегменты с помощью функции yolo_bbox2segment. Организуй файлы изображений и аннотаций следующим образом:

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

yolo_bbox2segment(
    im_dir="path/to/images",
    save_dir=None,  # saved to "labels-segment" in images directory
    sam_model="sam_b.pt",
)

Посети справочную страницу yolo_bbox2segment для получения дополнительной информации о функции.

Преобразование сегментов в ограничивающие рамки

Если у тебя есть набор данных, который использует формат набора данных сегментации, ты можешь легко преобразовать их в выровненные (или горизонтальные) ограничивающие рамки (формат x y w h) с помощью этой функции.

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

Чтобы понять, как работает эта функция, посети справочную страницу.

Утилиты

Сжатие изображений

Сжимай отдельные файлы изображений до уменьшенного размера, сохраняя при этом их соотношение сторон и качество. Если входное изображение меньше заданного максимального размера, оно не будет изменено.

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)

Автоматическое разделение набора данных

Автоматически разделяй набор данных на выборки train/val/test и сохраняй полученные результаты в файлы autosplit_*.txt. Эта функция использует случайную выборку, которая исключается при использовании аргумента fraction для обучения.

from ultralytics.data.split import autosplit

autosplit(
    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
)

Смотри справочную страницу для получения дополнительных сведений об этой функции.

Преобразование сегмента-полигона в бинарную маску

Преобразуй один полигон (в виде списка) в бинарную маску заданного размера изображения. Полигон должен быть в форме [N, 2], где N — количество точек (x, y), определяющих контур полигона.

Предупреждение

N всегда должно быть четным.

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,
)

Ограничивающие рамки

Экземпляры горизонтальных ограничивающих рамок (BBox)

Для управления данными ограничивающих рамок класс Bboxes помогает конвертировать форматы координат, масштабировать размеры рамок, вычислять площади, учитывать смещения и многое другое.

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]]
# )

Смотри справочный раздел Bboxes для ознакомления с другими атрибутами и методами.

Совет

Многие из следующих функций (и другие) доступны через класс Bboxes, но если ты предпочитаешь работать с функциями напрямую, смотри следующие подразделы о том, как импортировать их независимо.

Масштабирование рамок

При увеличении или уменьшении изображения ты можешь соответствующим образом масштабировать координаты ограничивающих рамок, используя 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)
# >>> 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]]
# )

Преобразования форматов ограничивающих рамок

XYXY → XYWH

Преобразуй координаты ограничивающей рамки из формата (x1, y1, x2, y2) в формат (x, y, ширина, высота), где (x1, y1) — верхний левый угол, а (x2, y2) — нижний правый угол.

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]]
# )

Все преобразования ограничивающих рамок

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

See the docstring for each function or visit the ultralytics.utils.ops reference page to read more.

Построение графиков (Plotting)

Утилиты для аннотирования

Ultralytics включает класс Annotator для аннотирования различных типов данных. Лучше всего использовать его с ограничивающими рамками обнаружения объектов, ключевыми точками позы и ориентированными ограничивающими рамками.

Аннотирование рамок

Примеры на Python с использованием Ultralytics YOLO 🚀
import cv2 as cv
import numpy as np

from ultralytics.utils.plotting import Annotator, colors

names = {
    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()

Имена можно брать из model.names при работе с результатами обнаружения. Также смотри справочную страницу Annotator для получения дополнительных сведений.

Аннотирование Ultralytics Sweep

Аннотирование методом Sweep с использованием утилит Ultralytics
import cv2
import numpy as np

from ultralytics import YOLO
from ultralytics.solutions.solutions import SolutionAnnotator
from ultralytics.utils.plotting import colors

# User defined video path and model file
cap = cv2.VideoCapture("path/to/video.mp4")
model = YOLO(model="yolo26s-seg.pt")  # Model file, e.g., yolo26s.pt or yolo26m-seg.pt

if not cap.isOpened():
    print("Error: Could not open video.")
    exit()

# Initialize the video writer object.
w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS))
video_writer = cv2.VideoWriter("ultralytics.avi", cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h))

masks = None  # Initialize variable to store masks data
f = 0  # Initialize frame count variable for enabling mouse event.
line_x = w  # Store width of line.
dragging = False  # Initialize bool variable for line dragging.
classes = model.names  # Store model classes names for plotting.
window_name = "Ultralytics Sweep Annotator"

def drag_line(event, x, _, flags, param):
    """Mouse callback function to enable dragging a vertical sweep line across the video frame."""
    global line_x, dragging
    if event == cv2.EVENT_LBUTTONDOWN or (flags & cv2.EVENT_FLAG_LBUTTON):
        line_x = max(0, min(x, w))
        dragging = True

while cap.isOpened():  # Loop over the video capture object.
    ret, im0 = cap.read()
    if not ret:
        break
    f = f + 1  # Increment frame count.
    count = 0  # Re-initialize count variable on every frame for precise counts.
    results = model.track(im0, persist=True)[0]

    if f == 1:
        cv2.namedWindow(window_name)
        cv2.setMouseCallback(window_name, drag_line)

    annotator = SolutionAnnotator(im0)

    if results.boxes.is_track:
        if results.masks is not None:
            masks = [np.array(m, dtype=np.int32) for m in results.masks.xy]

        boxes = results.boxes.xyxy.tolist()
        track_ids = results.boxes.id.int().cpu().tolist()
        clss = results.boxes.cls.cpu().tolist()

        for mask, box, cls, t_id in zip(masks or [None] * len(boxes), boxes, clss, track_ids):
            color = colors(t_id, True)  # Assign different color to each tracked object.
            label = f"{classes[cls]}:{t_id}"
            if mask is not None and mask.size > 0:
                if box[0] > line_x:
                    count += 1
                    cv2.polylines(im0, [mask], True, color, 2)
                    x, y = mask.min(axis=0)
                    (w_m, _), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)
                    cv2.rectangle(im0, (x, y - 20), (x + w_m, y), color, -1)
                    cv2.putText(im0, label, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
            else:
                if box[0] > line_x:
                    count += 1
                    annotator.box_label(box=box, color=color, label=label)

    # Generate draggable sweep line
    annotator.sweep_annotator(line_x=line_x, line_y=h, label=f"COUNT:{count}")

    cv2.imshow(window_name, im0)
    video_writer.write(im0)
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

# Release the resources
cap.release()
video_writer.release()
cv2.destroyAllWindows()

Находи дополнительные подробности о методе sweep_annotator в нашем справочном разделе здесь.

Адаптивное аннотирование меток

Предупреждение

Начиная с Ultralytics v8.3.167, circle_label и text_label были заменены на единую функцию adaptive_label. Теперь ты можешь указать тип аннотации с помощью аргумента shape:

  • Прямоугольник: annotator.adaptive_label(box, label=names[int(cls)], color=colors(cls, True), shape="rect")
  • Круг: annotator.adaptive_label(box, label=names[int(cls)], color=colors(cls, True), shape="circle")


Watch: In-Depth Guide to Text & Circle Annotations with Python Live Demos | Ultralytics Annotations 🚀
Адаптивное аннотирование меток с использованием утилит Ultralytics
import cv2

from ultralytics import YOLO
from ultralytics.solutions.solutions import SolutionAnnotator
from ultralytics.utils.plotting import colors

model = YOLO("yolo26s.pt")
names = model.names
cap = cv2.VideoCapture("path/to/video.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 = SolutionAnnotator(im0)
    results = model.predict(im0)[0]
    boxes = results.boxes.xyxy.cpu()
    clss = results.boxes.cls.cpu().tolist()

    for box, cls in zip(boxes, clss):
        annotator.adaptive_label(box, label=names[int(cls)], color=colors(cls, True), shape="circle")
    writer.write(im0)
    cv2.imshow("Ultralytics circle annotation", im0)

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

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

Смотри справочную страницу SolutionAnnotator для получения дополнительных сведений.

Прочее

Профилирование кода

Проверяй длительность выполнения/обработки кода, используя with или в качестве декоратора.

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

Нужно программно использовать поддерживаемые форматы изображений или видео в Ultralytics? Используй эти константы при необходимости:

from ultralytics.data.utils import IMG_FORMATS, VID_FORMATS

print(IMG_FORMATS)
# {'avif', 'bmp', 'dng', 'heic', 'heif', 'jp2', 'jpeg', 'jpeg2000', 'jpg', 'mpo', 'png', 'tif', 'tiff', 'webp'}

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

Делать делимым (Make Divisible)

Вычисляй ближайшее целое число к x, которое делится на y без остатка.

from ultralytics.utils.ops import make_divisible

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

Часто задаваемые вопросы (FAQ)

Какие утилиты включены в пакет Ultralytics для улучшения рабочих процессов машинного обучения?

Пакет Ultralytics включает утилиты, разработанные для упрощения и оптимизации рабочих процессов машинного обучения. Основные утилиты включают автоаннотирование для разметки наборов данных, преобразование COCO в формат YOLO с помощью convert_coco, сжатие изображений и автоматическое разделение наборов данных. Эти инструменты сокращают объем ручной работы, обеспечивают согласованность и повышают эффективность обработки данных.

Как я могу использовать Ultralytics для автоматической разметки моего набора данных?

Если у тебя есть предобученная модель обнаружения объектов Ultralytics YOLO, ты можешь использовать ее вместе с моделью SAM для автоматической аннотации твоего набора данных в формате сегментации. Вот пример:

from ultralytics.data.annotator import auto_annotate

auto_annotate(
    data="path/to/new/data",
    det_model="yolo26n.pt",
    sam_model="mobile_sam.pt",
    device="cuda",
    output_dir="path/to/save_labels",
)

Для получения подробной информации изучи справочный раздел auto_annotate или воспользуйся Ultralytics Platform в качестве облачной альтернативы без написания кода, предлагающей маскирование в один клик через SAM 2.1 или SAM 3, а также предсказания от предобученных и дообученных моделей YOLO для задач детекции, сегментации и OBB.

Как преобразовать аннотации набора данных COCO в формат YOLO в Ultralytics?

Для преобразования JSON-аннотаций COCO в формат YOLO для обнаружения объектов ты можешь использовать утилиту convert_coco. Вот пример фрагмента кода:

from ultralytics.data.converter import convert_coco

convert_coco(
    "coco/annotations/",
    use_segments=False,
    use_keypoints=False,
    cls91to80=True,
)

Для получения дополнительной информации посети справочную страницу convert_coco.

Как я могу проанализировать состав и распределение моего набора данных?

Ultralytics Platform предоставляет автоматическую аналитику наборов данных: вкладка Charts показывает распределение выборок, количество основных классов, гистограммы размеров изображений и 2D-тепловые карты расположения аннотаций, помогая выявить дисбаланс и выбросы перед началом обучения.

Как я могу преобразовать ограничивающие рамки в сегменты в Ultralytics?

Для преобразования существующих данных ограничивающих рамок (в формате x y w h) в сегменты ты можешь использовать функцию yolo_bbox2segment. Убедись, что твои файлы организованы с разделением директорий для изображений и меток.

from ultralytics.data.converter import yolo_bbox2segment

yolo_bbox2segment(
    im_dir="path/to/images",
    save_dir=None,  # saved to "labels-segment" in the images directory
    sam_model="sam_b.pt",
)

Для получения дополнительной информации посети справочную страницу yolo_bbox2segment.

Комментарии