Por qué dos cámaras no me alcanzaron
Sol pleno, sombra del árbol, las dos cámaras coincidiendo en lo equivocado. Cuando dos testigos no son dos testigos: la trampa de los…
Por qué dos cámaras no me alcanzaron
Sol pleno, sombra del árbol, las dos cámaras coincidiendo en lo equivocado. Cuando dos testigos no son dos testigos: la trampa de los errores correlacionados, y por qué tuve que romper la idea de que más cámaras eran la respuesta.
Es la una de la tarde. Sol pleno, esos días raros en que la terraza queda iluminada como un set de fotografía. Sira está durmiendo adentro, en la cama del living, lo más lejos del sillón cocina que puede estar sin salir de la casa.
La alarma suena. Mi voz grabada le grita a la nada.
Cuando voy a mirar los frames, las dos cámaras coinciden: “hay un perro sobre el sillón”. Las dos. Cada una con su modelo, cada una con su ángulo. Mismas zonas detectadas, casi el mismo segundo. No es una cámara equivocándose mientras la otra acierta. Las dos equivocándose, juntas, con confianza.

Vista de una cámara — y, atrás a la derecha, la otra. La red detecta lo obvio (perro, sofá, silla, mesa); el problema empieza cuando las dos vistas tienen que coincidir sobre lo mismo.
Lo que había sobre el sillón era la sombra del árbol del fondo moviéndose con el viento. La sombra entraba por debajo de la pérgola, caía justo sobre el cojín, y cada vez que una ráfaga la movía, el pixel-diff — o el modelo, según el día — veía un “objeto sospechoso” ahí.
La regla “ambas cámaras detectan”, que en el post anterior celebré como la salida elegante al problema 2D, acababa de caerse.
El error que no esperaba
Mi razonamiento original era el de los tribunales: dos testigos independientes que coinciden tienen muchísima más fuerza que uno solo. Si una cámara se equivoca por una pelusa pasando frente al lente, la otra — desde el ángulo opuesto — no va a tener esa misma pelusa. Eran dos perspectivas, dos modelos, dos oportunidades de errar de forma distinta. El acuerdo entre las dos parecía sólido.
Lo que no había considerado es que algunos errores no son independientes.
La sombra del árbol existe en el mundo real. No es un artefacto de una cámara. Las dos la ven, las dos la registran como cambio de píxeles sobre la zona del sillón, las dos lo interpretan como “algo pasa ahí”. El AND no protege contra eso. El AND solo protege contra errores aleatorios, que se cancelan entre sí porque cada cámara se equivoca por motivos distintos. Cuando las dos cámaras tienen el mismo motivo para equivocarse, el AND multiplica el problema, no lo divide.
Esto fue una lección dura. Las técnicas estadísticas habituales asumen errores independientes casi siempre, y cuando los errores están correlacionados, las garantías matemáticas se derrumban. Yo había estado tratando dos cámaras como dos testigos, sin preguntarme si los testigos miraban un mismo fenómeno o dos fenómenos distintos.
Las dos cámaras miraban el mismo fenómeno. La sombra. La pelusa cruzando ambos campos de visión. El reflejo del sol moviéndose por el cojín nuevo. Eran un solo testigo doblemente confirmado.

Las dos cámaras dicen “perro”. Lo que ven es la sombra del árbol. Sira, mientras tanto, duerme en el pasto.
La idea: no más cámaras, otra fuente
La salida no fue agregar una tercera cámara. Agregar más testigos del mismo tipo — dos ojos, tres ojos, cuatro ojos viendo lo mismo — no iba a romper la correlación. La salida fue agregar una fuente que no fuera una cámara apoyada en un modelo neuronal.
Hasta ese punto, todo el sistema dependía de una sola idea técnica: mostrarle una imagen a un detector de objetos y preguntarle “¿hay un perro acá?”. Si esa idea fallaba — porque el modelo confundía sombra con perro, porque clasificaba a Sira acostada como gato, porque algo en la imagen lo confundía — todo el sistema fallaba. Una sola idea técnica era una sola línea de defensa.
Lo que terminó funcionando fue partir el sistema en dos fuentes ortogonales:
Fuente A: el detector neuronal. Mira un frame, dice “perro acá, con score 0.78”. Es buena para clasificar siluetas. Es mala con poses raras, con sombras grandes, con cualquier patrón visual ambiguo.
Fuente B: un detector basado en diferencia de píxeles. No usa modelo de IA. Compara dos frames consecutivos, mide cuántos píxeles cambiaron dentro de la zona del sillón, y exige que ese movimiento sea sostenido durante varios segundos. No le importa qué objeto produce el movimiento. Solo le importa que algo se está moviendo encima del sillón durante el tiempo suficiente para que no sea una pelusa pasando.
Las dos fuentes pueden equivocarse. Pero no se equivocan por las mismas razones. Una sombra que confunde al modelo no necesariamente produce un patrón de pixel-diff sostenido (la sombra se mueve, pero el cojín debajo no cambia entre frames). Un cojín moviéndose por el viento produce pixel-diff pero el modelo no lo clasifica como perro.
Cuando las dos coinciden en que hay algo sobre el sillón — el modelo dice “perro” y el pixel-diff dice “movimiento sostenido en la zona” — , la chance de que sea Sira de verdad sube mucho. No porque cada fuente sea más precisa que antes, sino porque las dos formas distintas de equivocarse rara vez coinciden.

Modelo neural y pixel-diff mirando el mismo frame. Cada uno “ve” la escena con su lenguaje propio. La alarma se dispara solo cuando los dos coinciden.
Cómo se combinan, en concreto
La regla quedó: alarma solo si A y B detectan, en la misma zona física, dentro de la misma ventana de tiempo del evento. No basta que A diga “perro en el sillón cocina” y B diga “movimiento sostenido en la mesa”. Tienen que apuntar al mismo mueble.
Los falsos positivos bajaron mucho. La sombra del árbol seguía existiendo, pero ya no disparaba la alarma porque pixel-diff exigía movimiento sostenido y la sombra no movía suficiente píxel debajo del cojín. La pelusa cruzando el lente seguía existiendo, pero ya no sumaba con B porque era instantánea, no sostenida.
Cuando Sira realmente se subía, las dos fuentes coincidían fácilmente: el modelo la clasificaba como perro con score alto, pixel-diff registraba cambio sostenido en la zona porque ella se movía sobre el cojín. La regla funcionaba como esperaba.
Por unos días.
Y tampoco alcanzó
Lo que descubrí después es que dos fuentes ortogonales también tienen su talón de Aquiles, y mío específicamente fue este: cuando el modelo daba score bajo (0.30, 0.25) pero igual marcaba algo como “posible perro”, y pixel-diff saltaba por una sombra grande sostenida — un día con nubes pasando, viento moderado — , el AND se cumplía. A: “posible perro, no muy seguro”. B: “se está moviendo algo, sostenido, acá”. Ambos sobre el sillón cocina. Alarma.
Esos casos eran pocos — dos o tres por día — pero suficientes para que Sira siguiera dudando del altavoz, que es exactamente lo que quería evitar. Volvíamos al problema original: cualquier falso positivo rompe el condicionamiento. Cualquiera.
La solución fue tan poco elegante como honesta: subir el umbral de la fuente A a 0.40. Si el modelo no está al menos un 40% seguro de ver un perro, no cuenta su voto. Eso eliminó los casos de “A débil + B disparado por sombra”. El costo fue perder algunas alarmas reales donde el modelo veía a Sira con score 0.35, pero ya está: prefería perder esa alarma a generar un falso positivo.
Y agregué una segunda regla, más vergonzosa de admitir: si hay una persona en la terraza durante los últimos 30 segundos, suprimir la alarma. Porque mi propio cuerpo cruzando frente al sillón disparaba las dos fuentes a la vez, y mi propia voz me corregía a mí como si fuera Sira. Una vez aprendido eso, lo arreglé con una línea: “si la cámara ve un humano, callate por medio minuto.”
Cada arreglo era un parche. Ninguno era una solución limpia. Pero juntos, el sistema dejó de equivocarse de forma costosa.
Una tercera fuente que mira con palabras: el VLM
Aun con A + B + threshold + supresión por persona, me quedaba un grupo chico pero molesto de eventos ambiguos. La regla los dejaba pasar, yo los miraba al rato y pensaba “está claro que esto no era un perro sobre el sillón”. Explicar por qué no lo era requería contexto que ni el modelo neural ni el pixel-diff podían dar: “Sira pasó por delante”, “la sombra del árbol cubre justo esa zona a las cinco de la tarde”, “el cojín está doblado al medio y desde ese ángulo parece un perro”. Era información del mundo, no de píxeles.
Ahí entró una tercera fuente, distinta en otro nivel: un VLM (vision-language model). Un VLM es un modelo que recibe una imagen y devuelve una descripción en lenguaje natural. La frase típica que devuelve para un crop de Sira parada en el piso, frente al chaise: “the dog is on the floor, near a bench, but not on it”. Para un crop de Sira realmente sobre el cojín: “a small dog sitting on a wooden chaise lounge”.
Esa diferencia — near a bench vs on a chaise lounge — es exactamente la información que rompía la trampa 2D del post 1. El VLM no está contando píxeles ni midiendo cambios entre frames; está describiendo la escena en términos de objetos y relaciones espaciales. Es la primera fuente del sistema que entiende la diferencia entre “al lado” y “encima” — los detectores nativos no entienden eso, solo saben de bounding boxes.
Operacionalmente el VLM es lento: tarda varios segundos por crop. No sirve para alarmar en tiempo real. Lo uso como verificador post-hoc: cuando un evento queda en zona ambigua (score A medio, B disparado), abro el bundle, le paso los crops, y leo la frase. Si dice “on furniture” con confianza alta, confirmo la alarma; si dice “near furniture” o “on the floor”, la suprimo. Es lo más cercano a un par de ojos humanos que tiene el sistema, encapsulado en una sola frase por frame.
Y es probablemente la fuente más importante de cara a lo que viene: es la única que aporta contexto semántico — la diferencia entre “hay un perro en el frame” y “el perro está donde no debería estar” — , y por eso pesa distinto que las otras cuando se la suma a las features del bundle.

El panel del labeler: dos cámaras, modelo neural + pixel-diff, VLM al cierre, decisión final, y los crops del bundle. Acá se decide caso por caso si la alarma fue legítima.
El bichito
Empecé a notar que Sira había aprendido algo nuevo.
Durante el día casi no se subía al sillón. La alarma había sonado demasiadas veces; aunque la mayoría fueron falsas, ella ya desconfiaba. Pero de noche, cuando todos dormíamos, sí se subía. Lo descubrí mirando los bundles de la madrugada. Cuatro de la mañana, sin testigos, sin riesgo.

Las dos de la mañana. Sira en el sillón, sola, sabiendo perfectamente que ya nadie va a salir a corregirla.
La perra había aprendido a esquivar el sistema. No a no subirse, sino a no subirse cuando el sistema podía atraparla.
Lo encontré cómico y deprimente a la vez. Mi sistema se había convertido en una rutina que ella estaba aprendiendo a torear, no en una corrección de conducta. Sira no había dejado de subirse: solo había trasladado el horario.
Hay algo casi entrañable en esto. Sira no es un sistema técnico, pero acabó haciendo justo lo que harían dos sistemas técnicos adversarios: uno aprende a detectar, el otro aprende a evadir. La carrera armamentista a baja velocidad, dentro de mi propia casa, entre catorce kilos de pelos blancos con manchas y dos cámaras IP de treinta dólares.
Para la próxima
Sigo con el sistema “A + B en misma zona + umbral 0.40 + supresión por persona”. Funciona. No funciona perfecto, pero funciona.
Lo que viene es algo más ambicioso: en vez de seguir poniendo reglas hechas a mano, dejar que un modelo de clasificación aprenda solo qué pesar de cada fuente, según el contexto. ¿Es de noche con IR prendido? El pixel-diff se vuelve más ruidoso, hay que confiar menos. ¿Hay sombras moviéndose por el viento? El modelo neural debe pesar más. ¿La frase del VLM sobre los crops dice “dog standing on furniture” con confianza alta? Esa fuente vale más.
Hoy tengo el clasificador entrenando en una notebook con los ~250 bundles etiquetados a mano que llevo acumulados. Los números preliminares son honestos: con tan pocos casos donde Sira sí estaba sobre el sillón — veintiocho, hasta hoy — el modelo todavía está ciego. Cuando se le pide alta precisión (cero falsos positivos), su recall cae al 10%. Es decir, atrapa solo una de cada diez veces que ella sí se subió. Operativamente inútil.
Para la siguiente entrega voy a contar cómo se acumulan los datos de entrenamiento, qué pinta tienen las features que terminé extrayendo de cada bundle (incluyendo cosas tan poco glamorosas como el centroide vertical normalizado del bounding box), y por qué tirar todas las heurísticas a la basura para que el modelo decida suena bonito en papel y se rompe en la realidad sin suficientes datos.
Por ahora me quedo con dos ideas que también valen, creo, para cualquier sistema con múltiples sensores:
Una sola fuente — por más buena que sea — es una sola idea técnica defendiendo todo el sistema.
Y dos fuentes que se equivocan por las mismas razones son menos “dos fuentes” que “una fuente con micrófono doble”.
메타데이터
- post_id
- b41f5c8f1ceb
- slug
- por-qué-dos-cámaras-no-me-alcanzaron-b41f5c8f1ceb
- url
- https://medium.com/@dadeicoz/por-qu%C3%A9-dos-c%C3%A1maras-no-me-alcanzaron-b41f5c8f1ceb
- canonical_url
- https://medium.com/@dadeicoz/por-qu%C3%A9-dos-c%C3%A1maras-no-me-alcanzaron-b41f5c8f1ceb
- author_url
- https://medium.com/@dadeicoz
- status
- ok
- fetched_at
- 2026-06-18 00:10:23