Open-vocabulary detection

Open-vocabulary detection replaces a checkpoint's fixed class list with words you choose at call time. In LibreYOLO it is not a separate task: it is the detect task served by a separate model tier, loaded through the LibreOpenVocab factory instead of LibreYOLO.

Definition

Open-vocabulary detection returns ordinary detection Results: boxes, confidences and class indices, with result.names mapping those indices back to the strings you asked for. What changes is where the class list comes from. A conventional detector is trained against a fixed set of categories and can never emit a category outside it. These models take the vocabulary as text at inference time, so set_classes(["forklift", "safety cone"]) is enough to make those the classes.

LibreYOLO has no open-vocabulary task key. These models declare SUPPORTED_TASKS = ("detect",) like any other detector. What separates them is the loading path: they are Hugging Face snapshots rather than LibreYOLO state-dict checkpoints, so they stay out of the LibreYOLO() factory and are constructed through LibreOpenVocab() instead. That factory is a sibling of LibreSAM() and LibreVLM(), not a replacement for LibreYOLO().

Scores are real detection scores, not a generated caption parsed after the fact. Each family scores image regions against the text embedding of every prompt.

Models

Four families make up the tier, all of them predict only. Load any of them by alias through LibreOpenVocab.

Grounding DINO, from IDEA Research, in t and b sizes. It is the tier default, and the only family that accepts text_threshold, a second cutoff on the decoded phrase's token score.

OWLv2, from Google Research, in b16 and l14 sizes. It scores image regions against text embeddings from a CLIP-style encoder.

OMDet-Turbo, from Om AI Lab, in one t size. It decouples class embeddings from a language task prompt, and is the one family here that suppresses overlapping boxes inside its own post-processing, so iou= is honored.

OV-DEIM, in s, m and l sizes, a DETR-style detector that matches decoder queries to text embeddings from a bundled MobileCLIP text tower. It is one-to-one matching with top-K selection, so no NMS runs anywhere.

OV-DEIM's weights are the restricted case in this tier. The detector weights are CC BY-NC 4.0, non-commercial. The bundled text tower is under Apple's Machine Learning Research Model license, research use only. The l checkpoint adds a DINOv3-S backbone fine-tune under Meta's DINOv3 License. All three license texts ship inside the weight repository, and the library logs the same summary when it resolves the weights, before the model is built. Read OV-DEIM before deploying it.

The tier needs one extra:

bash
pip install "libreyolo[openvocab]"

That covers transformers and timm for the three wrapped families, and the huggingface_hub, safetensors, regex and ftfy packages OV-DEIM needs as a native port.

A second tier also takes a text vocabulary: LibreVLM() loads generative vision-language models, such as Qwen3-VL and Florence-2, and turns their output into the same Results. It shares the set_classes() surface. The difference is what produces the boxes: the families on this page are discriminative detectors that emit scores directly, while the VLM tier generates them.

Predict

Python
from libreyolo import LibreOpenVocab, SAMPLE_IMAGE model = LibreOpenVocab("grounding-dino-t")model.set_classes(["person", "dog", "skateboard"]) result = model.predict(SAMPLE_IMAGE, conf=0.25)print(result.names)for box in result.boxes:    print(box.cls, box.conf, box.xyxy)
Swap the vocabulary
from libreyolo import LibreOpenVocab, SAMPLE_IMAGE model = LibreOpenVocab("owlv2-b16") # set_classes is sticky: it holds until the next call to it.# Labels must be unique once lowercased and stripped of articles.model.set_classes(["a red backpack", "traffic cone"])result = model.predict(SAMPLE_IMAGE) model.set_classes(["bicycle wheel"])result = model.predict(SAMPLE_IMAGE)
Grounding DINO text threshold
from libreyolo import LibreOpenVocab, SAMPLE_IMAGE model = LibreOpenVocab("grounding-dino-b")model.set_classes(["remote control", "school bus"]) # conf filters by box score, text_threshold by the decoded phrase's# token score. Both default to 0.25 when left unset. Only Grounding# DINO accepts text_threshold; the others raise.result = model.predict(SAMPLE_IMAGE, conf=0.25, text_threshold=0.3)

set_classes() takes a non-empty list of label strings and holds until it is called again. Labels must be unique once lowercased and stripped of leading articles, so "a bus" and "bus" cannot coexist in one vocabulary. Multi-word phrases are labels like any other, and each family turns the list into its own text input before tokenizing, so "traffic cone" is a different query from "cone".

Three prediction arguments behave differently here than on a native detector. imgsz= is rejected, because the processor owns resizing for these families. augment=True is rejected, since test-time augmentation is out of scope for the tier. iou= applies only to the family whose processor runs its own suppression; where nothing is suppressed, passing it warns and is ignored.

Left unset, conf takes the loaded family's own default rather than predict()'s usual 0.25, and that default is not the same across the tier. Set it explicitly when comparing two families on the same image.

track() raises across the tier. Run predict() per frame instead. See prediction for sources, streaming and result handling.

Train

No family in this tier trains inside LibreYOLO. train() raises: fine-tune upstream and load the resulting weights. The vocabulary passed to set_classes() is the only setting that changes what a loaded model detects.

Validate

There is no validator for this tier, and val() raises. Open-vocabulary validation needs a dedicated one, because the standard detection validator feeds image tensors straight to the model, while these families require text-conditioned inputs built alongside them.

Export

Export is out of scope for the tier and export() raises. These models run through predict() in PyTorch.

Verified against LibreYOLO v1.5.0.