Тексто-управляемые модели детекции: OmDet‑Turbo vs Grounding DINO

В последние годы модели открытого словаря (open‑vocabulary) произвели революцию в компьютерном зрении, позволив детектировать объекты, не входящие в обучающую выборку. Два ярких представителя этого класса — OmDet‑Turbo (OM‑AI Lab) и Grounding DINO (IDEA Research). Обе модели принимают на вход произвольный текстовый запрос и изображение, возвращая ограничивающие рамки для указанных объектов. Рассмотрим данные модели по той причине, что обе они распространяются под лицензией Apache 2.0, в отличие от модели распространения YOLO.

 Точность моделей

2026-08-21_16-02-59

Наиболее интересна возможность использования в условиях, близких к реальному времени, поэтому рассматриваем tiny модели.  Это модели:

  • omdet-turbo-swin-tiny-hf - модель жестко привязана к размеру 640×640
  • grounding-dino-tiny - позволяет использовать разные размеры

 Тестирование

import time

import torch

import requests

import matplotlib.pyplot as plt

import matplotlib.patches as patches

from PIL import Image

from transformers import AutoProcessor, OmDetTurboForObjectDetection

 

# Загрузка процессора и модели (пока на CPU, потом перенесём)

processor = AutoProcessor.from_pretrained("omlab/omdet-turbo-swin-tiny-hf")

model = OmDetTurboForObjectDetection.from_pretrained(

    "omlab/omdet-turbo-swin-tiny-hf",

    low_cpu_mem_usage=False  # для совместимости с timm

)

model.eval()

 

# Загрузка тестового изображения

url = "http://images.cocodataset.org/val2017/000000039769.jpg"

image = Image.open(requests.get(url, stream=True).raw).convert("RGB")

classes = ["cat", "remote"]

 

def measure_inference_time(model, processor, image, classes, device, num_runs=20, warmup=5):

    """Замеряет среднее время инференса на указанном устройстве."""

    model = model.to(device)

    # Подготовка данных (процессор сам приведёт к 640×640)

    inputs = processor(images=image, text=classes, return_tensors="pt").to(device)

 

    # Прогрев

    with torch.no_grad():

        for _ in range(warmup):

            _ = model(**inputs)

 

    # Синхронизация для GPU

    if device.type == 'cuda':

        torch.cuda.synchronize()

 

    start = time.perf_counter()

    for _ in range(num_runs):

        with torch.no_grad():

            _ = model(**inputs)

    if device.type == 'cuda':

        torch.cuda.synchronize()

    end = time.perf_counter()

 

    return (end - start) / num_runs

 

# Сравнение

if torch.cuda.is_available():

    print("Сравнение CPU и GPU")

    print("-" * 40)

 

    # CPU

    cpu_time = measure_inference_time(model, processor, image, classes, torch.device('cpu'))

    print(f"CPU: {cpu_time:.4f} сек/кадр")

 

    # GPU

    gpu_time = measure_inference_time(model, processor, image, classes, torch.device('cuda'))

    print(f"GPU: {gpu_time:.4f} сек/кадр")

    print(f"Ускорение GPU: {cpu_time / gpu_time:.2f}x")

else:

    print("CUDA недоступна, выполняем только на CPU")

    cpu_time = measure_inference_time(model, processor, image, classes, torch.device('cpu'))

    print(f"CPU: {cpu_time:.4f} сек/кадр")

 

# Визуализация детекций (на GPU, если доступен)

def visualize_detections(image, processor, model, classes, threshold=0.3):

    device = next(model.parameters()).device

    inputs = processor(images=image, text=classes, return_tensors="pt").to(device)

    with torch.no_grad():

        outputs = model(**inputs)

 

    results = processor.post_process_grounded_object_detection(

        outputs,

        target_sizes=[(image.height, image.width)],

        text_labels=classes,

        threshold=threshold,

        nms_threshold=0.3,

    )

    result = results[0]

    boxes, scores, text_labels = result["boxes"], result["scores"], result["text_labels"]

 

    fig, ax = plt.subplots(1, figsize=(10, 10))

    ax.imshow(image)

    for box, score, text_label in zip(boxes, scores, text_labels):

        x1, y1, x2, y2 = box.tolist()

        rect = patches.Rectangle(

            (x1, y1), x2 - x1, y2 - y1,

            linewidth=2, edgecolor='red', facecolor='none'

        )

        ax.add_patch(rect)

        ax.text(x1, y1 - 5, f"{text_label} {score:.2f}",

                color='red', fontsize=12, backgroundcolor='white')

    ax.axis('off')

    plt.show()

 

# Покажем результат на GPU, если он есть, иначе на CPU

model.to(torch.device('cuda' if torch.cuda.is_available() else 'cpu'))

visualize_detections(image, processor, model, classes)

 

 Результат на i7-6700K и NVIDIA RTX 2080 Super:

CPU: 1.2375 сек/кадр

GPU: 0.0442 сек/кадр

Ускорение GPU: 28.02x

a111

Для grounding-dino-tiny проводилось 2 теста. Для изображения 640x640:

GPU: среднее = 0.1971 сек
CPU: среднее = 6.0049 сек
Ускорение GPU: 27.28x

Для изображения 224x224:

GPU: среднее = 0.0933 сек
CPU: среднее = 0.8232 сек
Ускорение GPU: 8.82x

 

 Результаты

Даже если брать маленькое изображение, то grounding-dino-tiny на GPU намного медленнее:

0.0933 сек против 0.0442 сек

При этом на CPU быстрее:

0.8232 сек против 1.2375 сек

При снижении входного размера до 224×224 мы наблюдаем незначительное падение качества детекции: объекты мелкого размера (например, пульт на стандартном изображении) становятся менее уверенными, но крупные (кошка) по‑прежнему распознаются хорошо. Однако модели при этом на CPU ненамного быстрее, что не позволяет ее использовать в около-реальном времени.

Поэтому для коммерческого продукта, близкого к работе в реальном времени остается лишь использовать OmDet‑Turbo на GPU. Yolo может работать быстрее, но лицензия там не позволяет включить ее в закрытый монолитный продукт.