clip-box — ¿dónde está en esta imagen?
Esta es la puerta de entrada "dónde está X". La implementación reside en
decision-model: el mismo cliente responde bool, choice, score y box,
y clip-box es un punto de entrada ligero que mantiene este vocabulario enrutable
y puede apuntar a un shim independiente cuando otro modelo ocupa la ranura de decisión.
clip-box --image scene.png --target "el cuadrado rojo" # slot shim en :8083
clip-box --image scene.png --target "el cuadrado rojo" --port 8761 # shim independiente
decision-model box --image scene.png --target "el cuadrado rojo" # lo mismo
h2o-lightning es un modelo de decisión de 4B: responde preguntas tipadas con
probabilidades y nunca genera prosa. Su pregunta box devuelve un rectángulo
en lugar de una etiqueta y tres números que importan más que el rectángulo:
| campo | significado |
|---|---|
box | left, top, right, bottom como fracciones de la imagen, desde la esquina superior izquierda |
confidence | P(IoU ≥ 0.5): una lectura de decisión sobre la imagen con el cuadro dibujado, no un número que el modelo narre |
present | P(el objetivo es visible en absoluto): una lectura separada sobre la imagen intacta |
found | si se produjo algún cuadro; un fallo devuelve la imagen completa con confianza 0 |
Filtra por present, no por found. Si se pide una bicicleta azul en una escena
que no la tiene, el modelo devuelve un cuadro de aspecto confiado alrededor de otra cosa con
found: true y present: 0.111. present es lo que dice que no.
Uso
# iniciar la API (las preguntas box están activas; el puerto frontal del slot es :8083)
h2o-serve --port 8083 # CPU, Q8_0
h2o-serve --backend vulkan # GPUs AMD: construye el backend Vulkan anclado una vez
h2o-serve --status
h2o-serve --stop
# preguntar
clip-box --image scene.png --target "el cuadrado rojo"
# ACCEPT box=(0.247, 0.247) -> (0.753, 0.753) confidence=0.651 present=0.885
clip-box --image scene.png --target "la bicicleta azul" # ausente -> sale con código 2
clip-box --image receipt.png --target "el total" # confidence=0.163 < 0.30 -> rechazado
clip-box --image scene.png --target "el cuadrado rojo" --json --crop /tmp/cuadrado.png
--json devuelve accepted, reason, box, pixels (calculados a partir del tamaño
real de la imagen), confidence, present, found y el model que respondió.
El código de salida 0 acepta, 2 rechaza, 1 es un error de solicitud.
--min-present (por defecto 0.5) y --min-confidence (por defecto 0.30) establecen los
umbrales; ambos también son variables de entorno (BOX_MIN_PRESENT,
BOX_MIN_CONFIDENCE). --crop escribe el cuadro aceptado; necesita Pillow.
Capacidad, no suposición
box es una característica shim, no una característica de protocolo: el propio
/v1/systemone de llama.cpp solo acepta choice, score y noul. La ranura de decisión
rota entre cinco candidatos, así que:
decision-model caps
# model /models/h2o-lightning-4b-Q4_K_M.gguf
# slot :8083 (box en :8083)
# primitives bool, choice, score, box
Si el ocupante no es h2o, caps no lista box y box se niega con el
nombre del modelo servido — en lugar de publicar una pregunta que ese endpoint no puede responder.
Para qué es realmente bueno
Medido en esta estación de trabajo (Q8_0, backend CPU), escenas sintéticas con verdad conocida:
| objetivo | IoU | confidence | present |
|---|---|---|---|
| cuadrado rojo, 512×512 centrado | 0.976 | 0.651 | 0.885 |
| rectángulo rojo, 512×1024 alto | 0.877 | 0.669 | 0.897 |
| rectángulo rojo, 1024×512 ancho | 0.848 | 0.722 | 0.895 |
| cuadrado rojo pequeño en una escena desordenada | 0.603 | 0.359 | 0.896 |
| "la bicicleta azul" (ausente) | — | 0.188 | 0.111 |
| "el total" en un recibo | fallo (cuadro debajo del total) | 0.144 | 0.899 |
Objetos salientes, iconos, formas, imágenes individuales: bueno, la relación de aspecto se maneja
correctamente. Texto pequeño denso en un recibo: falla, y su baja confianza lo dice.
Para un valor en un recibo, recorta con el vecindario del total o usa la pregunta
choice de la API de decisión ("cuál es el TOTAL? subtotal/total/impuesto"), que
puntuó correctamente en la misma imagen.
La trampa que esta habilidad existe para absorber
Pillow. El shim dibuja el cuadro antes de la lectura de precisión y devuelve
None en caso de ImportError, por lo que un Python sin Pillow responde cada cuadro con
confidence: 0.0. h2o-serve construye su propio venv y verifica la importación.
Archivos
| ruta | rol |
|---|---|
scripts/clip_box.py | punto de entrada — eleva --port/--host, delega a decision-model box |
../hardware/decision-model/scripts/decision_model.py | el cliente: una pregunta box, dos filtros, recorte, códigos de salida |
../hardware/decision-model/scripts/h2o_serve.py | el lanzador: bootstrap anclado por hash, venv seguro para Pillow, backend CPU/CUDA/Vulkan, --status, --stop |
El estado reside en ~/.cache/h2o_lightning_gguf (pesos + backend anclado) y
~/.local/share/h2o-lightning/venv. No se toca nada fuera de esas dos rutas.
Notas de protocolo
- El
box.modedel shim esgenerateaquí: una generación restringida en la convenciónbbox_2dde Qwen (0–1000, x primero), analizada y recortada.edges/hybridnecesitan un modelo entrenado en lecturas de bordes; mantengeneratecon esta versión. confidencees una probabilidad con su propia temperatura,1.0hasta ajustada, por lo que es conservadora: un cuadro casi perfecto en un cuadrado grande se lee 0.65, no 0.95.- Una imagen por solicitud por defecto; el shim acepta hasta 8 con
images:y el backend se inicia con--image-max-tokens 1024. - El lado de decisión del mismo servidor responde
choice/noul/scoreen texto e imágenes en/v1/systemone; para decisiones calibradas en texto, prefierepplx-decider(Índice de Decisión 60.06 vs 51.38 aquí). Usa este modelo para geometría, no para enrutamiento.