LibreVLM
Apunta un modelo de visión y lenguaje a una imagen, entrégale una lista de palabras y recibe cajas. LibreVLM convierte Qwen3-VL, Florence-2 y modelos afines en detectores de objetos de vocabulario abierto que usan exactamente la misma API Results que cualquier otro modelo de LibreYOLO.
Introducción
Un detector clásico incluye una lista fija de clases integrada en su cabeza. LibreVLM elimina esa restricción. Encapsula modelos modernos de visión y lenguaje ajustados mediante instrucciones, les pide que emitan bounding boxes, analiza el texto generado y devuelve el mismo objeto Results que ya usas para YOLO9 y RF-DETR. La lista de clases es solo una lista de palabras que proporcionas durante la ejecución, por lo que añadir una categoría nueva no cuesta nada y funciona en zero-shot.
- Vocabulario abierto. Detecta
"pink car","license plate"o"the small island"sin entrenar nunca una cabeza para esas clases. - Una factoría, un contrato.
LibreVLM(...)devuelve el objetoResultsestándar conboxes.xyxy,boxes.clsyboxes.conf, además de.plot()y.save(). - Backends intercambiables. Seis familias de modelos tras una sola cadena de alias, desde un Florence-2 de 230M hasta un Qwen3-VL de 8B.
- Una vía de escape sin procesar.
chat()permite hacer preguntas abiertas sobre imágenes cuando necesitas algo más que cajas.
Por qué hay un nivel independiente y una página aparte
LibreVLM se mantiene deliberadamente fuera de la factoría de vocabulario cerrado LibreYOLO(...) y de su registro .pt. Estos modelos se controlan mediante prompts, tienen vocabulario abierto y ofrecen una confianza sintética, por lo que cumplen un contrato diferente. Tratarlos como un nivel propio mantiene limpia la documentación principal de detección y deja claro qué se está midiendo.
En la rama dev
LibreVLM se encuentra actualmente en la rama dev y está previsto para la versión v1.3; no forma parte de v1.2.0. Es un nivel de inferencia exclusivo de Python: todavía no hay rutas de entrenamiento, validación, exportación ni CLI, y las puntuaciones de confianza son provisionales. Lee la sección Limitaciones antes de construir sobre él.
Instalación
LibreVLM se encuentra tras el extra opcional vlm. Este instala una versión reciente de transformers y las utilidades que necesitan algunos procesadores. Sin el extra, importar una familia VLM genera un ImportError que remite a esta página.
1 pip install 'libreyolo[vlm]'
Los pesos se descargan desde Hugging Face Hub la primera vez que se usan y se guardan en una carpeta local weights/. Algunas familias se distribuyen con licencias que no son OSI y muestran un aviso una sola vez antes de la descarga. Se recomienda una GPU para los backends más grandes, pero todos los modelos también se ejecutan en CPU con device="cpu".
Inicio rápido
Construye un modelo, declara las palabras que te interesan y realiza una predicción. El backend predeterminado es Qwen3-VL-4B, el detector más potente del nivel, con licencia Apache-2.0.
1 from libreyolo import LibreVLM 2 3 # Qwen3-VL-4B by default; weights autodownload on first use 4 model = LibreVLM() 5 6 # The vocabulary is just words. Any words. 7 model.set_classes(["pink car", "wheel"]) 8 9 result = model.predict("street.jpg") 10 11 print(result.boxes.xyxy) # pixel [x1, y1, x2, y2] 12 print(result.boxes.cls) # ids into ["pink car", "wheel"] 13 result.plot() # same drawing helpers as any LibreYOLO model 14 result.save("out.jpg")
Ese es todo el ciclo. Todo lo que viene después de predict() se comporta como un detector normal, por lo que el código existente de visualización, recorte y seguimiento sigue funcionando.
Modelos compatibles
Elige un backend mediante el alias que pasas a LibreVLM(...). Un nombre de familia sin más se resuelve al tamaño predeterminado. El backend predeterminado general es qwen3-vl-4b. En la práctica, los detectores más potentes son Qwen3-VL, LFM2-VL y Florence-2.
| Familia | Nombre alternativo | Tamaños (parámetros) | Licencia | Notas |
|---|---|---|---|---|
| Qwen3-VL | qwen3-vl-2b / -4b / -8b | 2B / 4B / 8B | Apache-2.0 | Predeterminado y más potente. Punto de partida recomendado. |
| LFM2-VL | lfm2-vl-450m / -1.6b | 450M / 1.6B | LFM Open License | Tamaño apto para edge, detector pequeño sorprendentemente potente. Requiere aceptar un aviso. |
| InternVL3 | internvl3-1b / -2b / -8b | 1B / 2B / 8B | Qwen License | Buena localización con 8B; los tamaños pequeños son débiles. Requiere aceptar un aviso. |
| Florence-2 | florence-2-base / -large | 0.23B / 0.77B | MIT | Modelo diseñado específicamente para localización. Cajas ajustadas, sin chat(). |
| SmolVLM2 | smolvlm2-500m / -2.2b | 500M / 2.2B | Apache-2.0 | Diminuto y rápido; detector más débil. Adecuado para pruebas rápidas. |
| Kosmos-2 | kosmos-2 | ~1.6B | MIT | Modelo de localización de 2023. Cajas menos precisas, sin chat(). |
Elegir un backend
- Mejor calidad:
qwen3-vl-8boqwen3-vl-4b(el predeterminado). - Cajas ajustadas, poco espacio:
florence-2-large. - Edge / CPU:
lfm2-vl-450mosmolvlm2-500m. - Licencia totalmente permisiva: cualquier tamaño de Qwen3-VL, SmolVLM2, Florence-2 o Kosmos-2.
Licencias
Qwen3-VL y SmolVLM2 tienen licencia Apache-2.0; Florence-2 y Kosmos-2 tienen licencia MIT. LFM2-VL e InternVL3 poseen licencias que no son OSI y muestran un aviso una sola vez antes de su primera descarga, para que puedas tomar una decisión informada sobre el uso comercial.
Configurar el vocabulario
El vocabulario es el núcleo de la detección de vocabulario abierto. Llama a set_classes() con una lista de cadenas de etiquetas. Es persistente: se conserva en todas las llamadas posteriores a predict() y track() hasta que vuelvas a configurarlo. Devuelve self, por lo que permite encadenar llamadas.
1 # Sticky and chainable 2 model = LibreVLM("qwen3-vl-2b").set_classes(["person", "dog", "cat"]) 3 4 # Set it once at construction instead 5 model = LibreVLM("lfm2-vl-450m", names=["boat"], device="cpu") 6 7 # Re-set any time to change what you are looking for 8 model.set_classes(["a red car", "a blue truck"])
Las etiquetas pueden ser cualquier frase. Deben ser únicas sin distinguir entre mayúsculas y minúsculas, y debes pasar una lista, no una cadena sola. Si nunca llamas a set_classes(), el modelo utiliza el vocabulario COCO-80 como alternativa para que un simple predict() siga haciendo algo razonable.
Predicción
predict() (y la llamada equivalente model(...)) acepta los mismos tipos de fuente que cualquier detector de LibreYOLO: una ruta, una imagen PIL, un array de numpy, una URL, una carpeta o un vídeo. stream=True y track() también funcionan.
1 result = model.predict( 2 source="image.jpg", # path | PIL | ndarray | URL | folder | video 3 conf=0.25, # see note below: scoring is synthetic 4 classes=[0], # optional: keep only these vocabulary ids 5 max_det=300, 6 )
Forma de la salida
Recibes el objeto Results estándar, idéntico al de un detector de vocabulario cerrado:
| Campo | Forma / tipo | Significado |
|---|---|---|
result.boxes.xyxy | N x 4 | Cajas en píxeles [x1, y1, x2, y2], escaladas a la imagen original. |
result.boxes.cls | N | Identificadores de clase que indexan el vocabulario de set_classes(). |
result.boxes.conf | N | Confianza sintética: 1.0 para cada caja (consulta Limitaciones). |
result.plot() / .save() | - | Las utilidades habituales para dibujar y guardar. |
Internamente, LibreVLM analiza de forma tolerante la salida del modelo (gestiona bloques de Markdown, texto suelto, cajas duplicadas y arrays truncados), vuelve a asignar las etiquetas de texto libre a tus identificadores de clase y descarta cualquier etiqueta que no esté en tu vocabulario. Este último paso hace que un generador de formato libre se comporte como un detector de conjunto cerrado.
Ejemplos
Detectar un objeto de un color específico
1 from libreyolo import LibreVLM 2 3 model = LibreVLM("qwen3-vl-4b") 4 model.set_classes(["red car"]) 5 6 result = model.predict("parking_lot.jpg") 7 print(f"Found {len(result.boxes.cls)} red car(s)") 8 result.save("red_cars.jpg")
Cajas ajustadas con Florence-2
1 # Florence-2 is a purpose-built grounder: very tight pixel boxes. 2 model = LibreVLM("florence-2-large") 3 model.set_classes(["a red car", "license plate"]) 4 5 result = model.predict("car.jpg") 6 result.plot()
Filtrar a una sola clase sobre la marcha
1 model = LibreVLM("qwen3-vl-2b").set_classes(["person", "dog", "cat"]) 2 3 # classes= filters the configured vocabulary by id 4 people_only = model.predict("street.jpg", classes=[0])
Ejecutar en CPU con una imagen de muestra integrada
1 from libreyolo import LibreVLM, SAMPLE_IMAGE 2 3 model = LibreVLM("lfm2-vl-450m", device="cpu") 4 # No set_classes() -> falls back to the COCO-80 vocabulary 5 result = model.predict(SAMPLE_IMAGE) 6 print(model.names[result.boxes.cls[0]]) # e.g. "person"
Batches, carpetas y vídeo
1 model = LibreVLM().set_classes(["forklift", "pallet"]) 2 3 # A whole folder 4 for result in model.predict("warehouse_frames/", stream=True): 5 result.save() 6 7 # A video file (frames are processed one at a time) 8 model.predict("warehouse.mp4", save=True)
Chat sin procesar
A veces quieres el modelo, no el detector. Las familias con plantilla de chat exponen chat(), que recibe una imagen y un prompt de formato libre y devuelve literalmente el texto decodificado. Úsalo para contar, generar descripciones o responder preguntas visuales rápidas.
1 model = LibreVLM("qwen3-vl-4b") 2 3 answer = model.chat("harbor.jpg", "How many boats are docked? Answer with a number.") 4 print(answer)
chat() está disponible en las familias con plantilla de chat (Qwen3-VL, LFM2-VL, SmolVLM2, InternVL3). Florence-2 y Kosmos-2 son modelos de localización con tokens de tarea y generan NotImplementedError; usa predict() con ellos.
Diferencias entre backends
Todas las familias devuelven el mismo objeto Results, pero llegan a él de formas distintas. Rara vez necesitas preocuparte por ello, aunque ayuda a entender por qué algunos backends se comportan como lo hacen. A las familias de chat se les solicita un array JSON de cajas; los modelos de localización usan tokens de tarea específicos.
| Familia | Uso de prompts | Espacio de coordenadas | chat() |
|---|---|---|---|
| Qwen3-VL | Prompt JSON de cajas | De 0 a 1000, reescalado | Sí |
| LFM2-VL | Prompt JSON de cajas | Normalizado de 0 a 1 | Sí |
| SmolVLM2 | Prompt JSON de cajas | Normalizado de 0 a 1 | Sí |
| InternVL3 | Prompt JSON de cajas | De 0 a 1000, reescalado | Sí |
| Florence-2 | Token de tarea | Píxeles nativos | No |
| Kosmos-2 | Prompt de localización | Normalizado, reescalado | No |
En las familias de chat puedes sustituir el prompt de detección mediante el argumento prompt= del constructor y limitar la longitud de generación con max_new_tokens=. El dispositivo y el dtype se resuelven automáticamente: bf16 o fp16 en CUDA, fp32 en CPU.
Limitaciones
LibreVLM es potente, pero joven. Conocer sus límites desde el principio evita sorpresas más adelante.
- Confianza sintética. Cada caja recibe una puntuación de 1.0. Por tanto, el filtro
conf=se comporta como un todo o nada en lugar de como un umbral real. - Sin mAP ni validación.
val()genera un error, porque las puntuaciones sintéticas harían engañoso el mAP de COCO. - Sin entrenamiento ni exportación.
train()yexport()generan un error. Haz fine-tuning del VLM en el proyecto original y carga los pesos resultantes. - Seguimiento degradado.
track()se ejecuta, pero las puntuaciones uniformes dejan inactiva la fase de recuperación de baja confianza del tracker. - Una imagen cada vez. La generación es secuencial en v1, por lo que los valores mayores de
batch=no aceleran el proceso. - Solo API de Python. La CLI de
libreyolotodavía no resuelve los alias de VLM.
Dónde destaca
Usa LibreVLM cuando el conjunto de clases sea abierto, cambie con frecuencia o resulte difícil de etiquetar de antemano: prototipado rápido, categorías de cola larga o poco frecuentes y flujos de trabajo del tipo "encuentra lo que describo con palabras". Cuando necesites confianza calibrada, rendimiento o un artefacto desplegable, entrena un YOLO9 o RF-DETR de vocabulario cerrado mediante la documentación principal.