crtdrops
NOTA · ESPAÑOL ·

Fantasmas en la imagen: mis técnicas de visión de 2001 para un robot contra dos modelos locales

Un cuadro con el eco al 70 %: el disco y su fantasma 40 px a la derecha. El recuadro de Qwen encierra a los dos; el ojo de 2001 cae en el disco; Gemma contestó (320, 240), el centro de la imagen. Error mediano en 72 cuadros: 0.1 px en 37.5 ms para el ojo de 2001, 1.3 px en 146 s para Qwen (en los 37 donde dio el centro) y 177.8 px en 104 s para Gemma.

En 2001 estaba terminando Ingeniería Electrónica en la Universidad Iberoamericana, en Santa Fe. El proyecto final era ambicioso: un robot móvil que se guiara por lo que veía. Éramos tres, así que lo dividimos en tres, y la visión me tocó a mí: un DSP y una de las primeras cámaras USB, 640×480 en grises, un cuadro por segundo, lenta para responder y con cada cuadro por procesar. Funcionaba dentro de los límites de su paradigma: nada de inferencias. El fondo tenía que ser uniforme, hasta había que saber su color de antemano, y el objeto tenía que estar a menos de 50 cm. Los filtros de ventana, un umbral y un centroide hacían el resto.

Las imágenes de esa época traían fantasmas, aunque no los de esa cámara. La televisión analógica los tenía literalmente: la señal rebota en un edificio, llega unos microsegundos tarde y una copia tenue de todo queda a la derecha de la imagen. En ese tiempo también trabajé en un cancelador de eco que medía ese fantasma en una señal de referencia y lo quitaba.

El tema de esta semana es el fantasma en la máquina, en los días en que México recuerda a sus muertos. Así que traje de vuelta ese ojo, lo escribí de nuevo y embrujé la escena del robot con los fantasmas de la televisión analógica. Luego lo puse frente a dos modelos de visión de hoy, corriendo en local en una mini PC. Los mismos cuadros embrujados para todos. ¿A cuál engaña el fantasma?

Lo que construí

Tres ojos, y una escena para embrujarlos.

Cada cuadro es sintético y sale de una semilla fija, así que la posición real del disco se conoce con exactitud. Nadie tiene que juzgar a ojo quién atinó.

Demostración

La corrida entera, cuadro por cuadro, está en crtdrops.mx/fantasmas (en español y en inglés en la misma página). Escoge un fantasma, una intensidad y un cuadro, y la página dibuja dónde dijo cada ojo que estaba el disco, junto a dónde estaba de verdad, con lo que contestó cada modelo.

Código

github.com/israel0negrete-crtdrops/ghosts-in-the-frame, MIT. Es el código tal como corrió, con las bitácoras y todo lo que contestó cada ojo (de los modelos se guardaron los primeros 300 caracteres de cada respuesta; cuatro de Qwen se cortan ahí). Los nombres y comentarios están en español, como corrieron; las preguntas a los modelos, en inglés. comparar.py reconstruye la comparación a partir de esas respuestas, byte por byte.

Cómo lo hice

La escena (escena.py, sólo biblioteca estándar) escribe cada cuadro como PGM y las posiciones reales en JSON. Las 8 líneas de arriba llevan una señal de referencia: negro con un pulso blanco corto, como la televisión analógica llevaba señales de prueba y de referencia para cancelar fantasmas en su intervalo de borrado vertical. El eco deforma ese pulso igual que todo lo demás, y de eso se trata.

El cancelador de eco busca la copia del pulso en las líneas de referencia. Dónde empieza la copia da el retraso del fantasma; qué tan brillante es, su intensidad. Luego quita el fantasma línea por línea con un filtro recursivo, y[x] = g[x] − a·(y[x−d] − negro), la inversa exacta del rebote único e ideal que simula esta escena. Un fantasma real tiene más rebotes y nunca es tan limpio. El cancelador no adivina a partir de la imagen: mide el fantasma en una señal cuya forma ya conoce. Es una ventaja que los modelos no tienen. Ven las mismas líneas, y su pregunta sólo les dice que las ignoren.

Los filtros y el centroide. Una ventana de 3×3 recorre el cuadro. La mediana tira los valores aislados, que es justo lo que es el ruido impulsivo. Luego cuenta cada píxel que pase de un umbral fijo, y el centroide de esos píxeles es la respuesta. Cuatro filtros más ninguno, con y sin cancelador: diez variantes.

Los modelos reciben cada cuadro como PNG por la API de Ollama, con temperatura 0, semilla 42 y un contexto de 4,096 tokens. A Gemma se le pide el centro del disco como {"x", "y"}. A Qwen, su recuadro, que es como está entrenado para ubicar objetos. El programa que los corre sólo trabaja de 7:00 a 21:30, para no competir con el turno de noche del laboratorio, y se puede detener y reanudar.

Lo hice en pareja con Claude Code: la escena y las técnicas salen de mi anteproyecto, Claude escribió la mayor parte del código y lo corrimos juntos en los equipos del laboratorio. El ojo de 2001 corrió también en una Raspberry Pi 5 y dio exactamente los mismos puntos que en el N100.

Lo que salió

Los mismos 72 cuadros (18 casos, cuatro cuadros de cada uno), en el N100:

Ojo Error mediano Tiempo por cuadro
2001 · filtro de mediana + cancelador de eco 0.1 px (en el fantasma más difícil, gaussiano σ 50: 7.8 px) 37.5 ms
Qwen2.5-VL 3B 1.3 px en los 37 cuadros donde dio el recuadro completo 146 s
Gemma 3 4B 177.8 px 104 s

Qwen no siempre contestó igual. En 37 cuadros dio el recuadro completo, y se mide el centro. En 32 dio sólo la esquina superior izquierda, a 2.8 px, con un ancho y un alto inventados (de 10 a 500 px, para un disco de unos 71 px en su escala), así que no hay centro que medir. En 3 siguió anidando la misma estructura hasta llenar el contexto de 4,096 tokens, sin una sola coordenada.

El ojo de 2001 queda más cerca, y es unas 3,900 veces más rápido que Qwen. Los dos en la misma CPU, con una diferencia que conviene decir: el tiempo del C cubre el procesamiento de un cuadro que ya está en memoria, mientras que el de los modelos incluye la llamada HTTP, decodificar y redimensionar la imagen y escribir la respuesta.

Lo que hicieron los fantasmas

Qwen sí ve el disco, pero contesta en otra escala. Los primeros números decían que su error mediano era de 366 px, y parecía ceguera. No lo era. Ollama reportó 1,122 tokens de entrada en cada cuadro, y ese número lo delató. Quítale los unos 86 tokens de la pregunta y quedan 1,036: 37×28 tokens, uno por cada bloque de 28×28 px, o sea que Ollama había agrandado el cuadro de 640×480 a 1,036×784 antes de que Qwen lo viera. Qwen contestó en esa imagen, aunque la pregunta decía 640×480. Reescalados, los cuatro cuadros limpios quedan a 1.4 px o menos. No ajusté la escala hasta que cuadrara; sale de la cuenta de tokens.

El eco engaña primero a Qwen. En 10 de los 11 cuadros con eco donde Qwen dio el recuadro completo, el recuadro abarca el disco y su fantasma juntos: unos 86 px de ancho, que son los 44 px del disco más los 40 px de retraso, así que su centro se corre unos 20 px a la derecha. Pasa desde el eco más débil, el del 20 %, aunque su pregunta dice que ignore cualquier copia más tenue del disco. El ojo de 2001 sin su cancelador aguanta los ecos del 20 y del 35 %, pero sólo porque un fantasma tan tenue queda debajo de su umbral fijo; con los del 50 y el 70 % el fantasma lo pasa y arrastra el centroide los mismos 20 px. Con el cancelador se queda a menos de un píxel en los cuatro.

Gemma contestó (320, 240) en los 72 cuadros, también en los limpios: el centro exacto de la imagen. Con esta pregunta no localiza el disco. Sólo probé una pregunta; con otra quizá le iría mejor.

No todas las técnicas de 2001 sirven. Con el Laplaciano o la prueba de borde de mínima distancia y un umbral fijo, el ojo pierde el disco en cuanto hay cualquier ruido: gaussiano σ 10, o 1 % de ruido impulsivo. Escogí la variante de mediana con cancelador después de ver los resultados, por ser la de peor caso más bajo. Las diez variantes están en el repositorio, también las que fallan.

¿Por qué importa que sea abierto?

Para ser precisos con “abierto”: los pesos de Gemma están bajo los Gemma Terms of Use, y los de Qwen2.5-VL 3B bajo la Qwen Research License, que no permite uso comercial, aunque la ficha de Ollama diga Apache 2.0. Ninguna es una licencia OSI. Lo que importa aquí es que los pesos están en mi disco y puedo correrlos, revisarlos y fijarlos. Ollama es MIT, y mi código también.

Lo que 2001 todavía sabía

Nada de esto dice que los modelos sean malos. Un modelo de 3B que nunca había visto mi escena, con una pregunta corta como única instrucción, encuentra un disco de 44 px a un píxel o dos cuando da el recuadro completo y no hay eco. El ojo de 2001 sabe exactamente una cosa, y se hizo para eso.

Pero esa es la lección que me quedo: cuando conoces la forma de tu fantasma, puedes medirlo y quitarlo, y unas cuantas líneas de aritmética le ganan a un modelo que tiene que adivinar. Sentí orgullo al verlo. Las matemáticas rigen el universo, y aun con IA seguirán siendo la base de todo. Nostalgia, sí. Pero el avance tecnológico se basa en eso, en mejorar todo lo aprendido, y nada es imposible.

Israel Negrete Lepe es ingeniero en electrónica. Veinticinco años haciendo que sistemas distintos funcionen juntos en condiciones reales: del sensor, el vehículo o la transacción al backend, los datos, la seguridad y la operación. Hoy, cada vez más, IA en el borde. Acepto proyectos por hora o por entrega: israel0negrete@gmail.com.