尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗La webcam detecta los post-its en vivo y el proyector muestra esos mismos
contornos, sus colores y la pelota. Hay un post-it de inicio, uno de llegada
y rebotes opcionales. Las esquinas también se muestran si están presentes,
pero no son necesarias para jugar. Por defecto, el inicio es amarillo, la
llegada naranja, los rebotes verdes y las esquinas fucsia o violetas. Desde
la vista de cámara podés elegir otros colores. J abre la ventana /projector.
No hay pasos manuales de calibración: para que cada contorno caiga justo
alrededor de su papel, el panel alinea cámara y proyector solo, con unos
patrones de puntos que proyecta durante unos 8 segundos (A lo repite).
También hay una versión que corre entera en el navegador, sin instalar nada: ver Versión web (Vercel).
Con Python 3.12 y uv instalados, ejecutá desde esta carpeta:
uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python -r requirements.txt
./.venv/bin/python web.py
Se abrirá Chrome en http://127.0.0.1:8765/. Elegí la Logitech C920 en el
selector de cámaras y permití el acceso a la cámara para esa página si Chrome
lo solicita. El navegador muestra la imagen en la pantalla principal. El
servidor Python nunca abre la cámara, así que no necesita permiso de cámara
de macOS. Recibe fotos JPEG del navegador en dos casos: al escanear y, unas
cuatro veces por segundo, para la detección en vivo.
Pulsá J o el botón Abrir proyector. Mové la nueva ventana a la pantalla
del proyector y activá su pantalla completa desde el botón de esa ventana.
Podés cambiar su tamaño o activar pantalla completa durante la partida: el
encuadre se adapta conservando las proporciones. Los controles permanecen en la
ventana principal. Si Chrome bloquea la ventana emergente, permití las
ventanas emergentes para 127.0.0.1 y volvé a pulsar J. Con la ventana
del proyector en pantalla completa, el panel alinea cámara y proyector (ver
Alinear cámara y proyector).
Debajo de "Preparar la pared", el panel muestra el siguiente paso. Cuando la cámara ve un inicio y una llegada válidos, la pelota aparece automáticamente.
Con la cámara y la proyección listas:
Espacio. No se apunta: la pelota aparece quieta
justo debajo del post-it de inicio y cae por gravedad. Para llevarla a la
llegada, mové los post-its: rebota en los de rebote perdiendo algo de
fuerza y se desliza por los inclinados. Si sale del campo por cualquier
borde, también por abajo, pierde; si se queda quieta 1,5 s (apoyada o
trabada en una V), también.R inicia otro intento con el tablero detectado más
reciente. S permite releerlo expresamente sin apagar la proyección.Si se cierra la ventana del proyector durante un intento, la pelota queda en
pausa. Reabrila con J y pulsá Continuar intento (o Espacio). Con el
intento en pausa también podés releer los post-its o empezar otro
intento con R.
La detección y sus contornos siguen en directo durante la partida. La física
usa el tablero con el que se lanzó la pelota: mover un papel cambia el
contorno en vivo, pero su rebote se actualiza al iniciar el siguiente intento.
Sin imágenes nuevas durante 1,5 segundos, desaparecen los contornos viejos.
El proyector muestra el fondo elegido, la luz de cada papel detectado y la
pelota; el video de la cámara queda en el panel. Sin alinear, encaja la
imagen de la cámara en la ventana (con bandas si hace falta), dibuja un
borde neutro alrededor de cada papel y avisa Sin alinear · pulsá A en el panel: los bordes no caen sobre los papeles. Alineado, cada papel se
ilumina por dentro con su color hasta 3 píxeles del proyector de su borde,
con un borde más fuerte del mismo color, y la pelota se mueve en
coordenadas del proyector.
La cámara y el proyector miran la pared desde lugares distintos. Para que el
color caiga justo sobre su papel, el panel mide cómo se corresponden
sus píxeles (una homografía; el detalle está en
docs/alignment-and-color.md).
Cuándo. Sola, la primera vez que la ventana del proyector está en
pantalla completa con la cámara funcionando, y de nuevo si cambia el tamaño
de la proyección, la cámara o su resolución. A o Alinear proyector la
repite cuando quieras. Si movés la cámara o el proyector, pulsá A: ese
movimiento el panel no lo nota solo.
Qué se ve. El proyector muestra 14 patrones de puntos blancos sobre negro durante unos 8 segundos (más con una cámara en automático, como el iPhone). No muevas los equipos mientras tanto. Si la toma sale imprecisa o incoherente, el panel la repite una vez más despacio.
Resultado. Si sale bien, el panel dice cuánto se corren los contornos,
por ejemplo "Proyector alineado: los contornos caen a 0.2 px de cada papel
(60 puntos medidos)". Menos de 1.5 px no se nota a la vista. Si falla, dice
qué vio la cámara y qué hacer, y A la repite:
| Mensaje empieza con | Qué pasó | Qué hacer |
|---|---|---|
| "La cámara encontró solo N de los M puntos" | la cámara no ve toda la imagen del proyector, o hay mucha luz encima | encuadrá la pared entera, proyector encendido, bajá la luz |
| "La cámara vio puntos que no encajan entre sí" | un reflejo o algo brillante se confundió con un punto | sacá o tapá lo que brille |
| "La alineación salió poco precisa" | los puntos se vieron borrosos o débiles: casi siempre luz del cuarto sobre la pared, o se movió un equipo | bajá la luz, no toques cámara ni proyector |
| "La cámara ve solo una parte chica" | la proyección ocupa poco del video | acercá o mové la cámara |
| "La alineación se cortó antes de terminar" | la ventana del proyector perdió el foco o se cerró | no cambies de ventana mientras alinea |
Encuadre. La cámara tiene que ver toda la proyección. Si el panel dice
pocos puntos (por ejemplo 32 de 60) o poca cobertura, la alineación vale
solo para la parte que vio: encuadrá la pared entera y pulsá A.
Chequeo de color. Después de alinear, el panel proyecta un cuadrado de cada color sobre pared vacía y mira si la cámara toma alguno por un post-it. Es solo un aviso: el color se proyecta igual. Si alguno se confunde, el panel avisa que al sacar un papel su luz puede tardar unos segundos en apagarse.
Alineado, el proyector pone color solo por dentro de cada papel, a 3 px de
su borde, y nada sobre la pared vacía, así su propia luz no crea post-its
falsos. Medido en el simulador de lazo cerrado (services/simulation), en
sala normal, oscura e iluminada: el contorno queda a 1.3-3.9 px de donde
debe, el color nunca llega a la pared y un papel quitado se apaga en 0-2
cuadros. Con el diseño anterior, en
la pared real la detección cambió de estado 730 veces en 26 minutos
(runs/web-events.jsonl, 19/10), y en el simulador los contornos crecían
hasta 12 veces el papel.
https://postit-wall-game.vercel.app: la misma interfaz corre entera en el
navegador, sin Python ni instalación.
El backend es un port a JavaScript de los servicios de Python (visión,
alineación, física y estado del juego) sobre OpenCV.js 4.12, la misma versión
de OpenCV que usa Python. Las pruebas de paridad exigen que el port dé las
mismas respuestas que Python en 2116 casos (services/browser/README.md).
postitEvents() en la consola lista los eventos.Para construir y probar el sitio estático en la Mac:
npm ci
npm run build # dist/
node scripts/serve-static.mjs # http://localhost:4870/
vercel deploy --prod lo publica: vercel.json define el build y
.vercelignore sube solo los 21 archivos que el build necesita. El proyecto
de Vercel está conectado al repositorio de GitHub: cada push genera un
deploy de vista previa. Para probar en Chrome el sitio publicado:
node tests/closed-loop-browser.mjs https://postit-wall-game.vercel.app.
Un iPhone cerca de la Mac aparece como cámara (Cámara de continuidad), por ejemplo "Cámara de iPhone de Tobias". Elegilo en el selector Cámara debajo del video. El panel recuerda esa elección al recargar. Sin elección guardada prefiere la C920, después el iPhone y recién después la cámara FaceTime de la Mac, que mira al operador y no a la pared. El iPhone suele aparecer unos segundos después de abrir la página. Si para entonces el panel ya arrancó con la FaceTime, pasa solo al iPhone, siempre que todavía no hayas elegido otra cámara a mano ni preparado un tablero. El tablero queda asociado al nombre de la cámara: recargar la página con la misma cámara lo conserva mientras llegan imágenes recientes, y elegir otra lo descarta porque cambia el encuadre. Si la cámara no entrega imágenes en 8 segundos, el panel lo dice y muestra Reintentar cámara, que vuelve a la cámara recordada.
Arriba a la derecha, junto a Abrir proyección, está Fondo: Oscuro /
Claro. También se cambia con la tecla F, desde el panel o desde la
ventana del proyector. El cambio se ve al instante en la ventana del
proyector y queda guardado en el navegador.
#f3f2ec) y pelota oscura.F, u Oscuro/Claro) vuelve al fondo del tema. Si el color elegido cae
dentro del color de algún post-it (por ejemplo un amarillo como el del
inicio), el panel lo rechaza: la cámara vería toda la pared como ese
post-it.La pelota no se ve hasta que la soltás (Espacio): aparece debajo del
inicio en el momento en que empieza a caer, con un destello y una onda del
color del inicio, y deja una estela al caer. Si toca la llegada, la llegada
explota en luces de colores. Mientras la pelota está en el aire y hasta 2,5 s
después de que termina, la cámara no lee la pared: los efectos pueden tener
cualquier color sin que se confundan con post-its. Después de eso (o con
R) vuelve a leer, y ahí podés mover los post-its.
Hay sonidos: un "whoosh" al soltar, un golpe en cada rebote, una fanfarria al llegar y un tono que baja si sale del campo. Suenan en la computadora del panel (Chrome solo deja sonar después de tocar algo en esa ventana, así que alcanza con apretar Espacio o hacer clic). El botón Sonido arriba los apaga o prende, y se recuerda.
Alineado, cada post-it brilla en su color con el borde del papel en blanco (antes ese borde quedaba oscuro). Con la sala a oscuras ese blanco confunde a la cámara: dejá alguna luz encendida.
Sin alinear, en los dos fondos el proyector dibuja bordes neutros (blancos en el oscuro, grafito en el claro). Alineado, cada papel lleva su color por dentro. Nunca pone color sobre la pared vacía.
Al usar el gotero o buscar el ajuste de cámara, la ventana muestra solo su fondo, sin bordes ni pelota. Así la lectura de colores y el ajuste de cámara se hacen con la misma luz que habrá durante el juego, también con fondo claro.
Debajo del video hay una fila por tipo de post-it: inicio, llegada, rebotes y esquinas. Cada una tiene:
4/4.Esc cancela.Sobre la imagen, cada post-it detectado se ilumina con un halo de su color.
Así se ve qué papel exacto cuenta como cada tipo. Colores originales
vuelve a los colores por defecto. Los colores quedan guardados en
runs/palette.json y se mantienen al reiniciar el servidor.
Límites:
R prepara una pelota nueva con la distribución actual.El balance de blancos, la exposición y el foco automáticos cambian los colores cuando cambia la luz del proyector. Por eso el panel los fija:
HD Pro Webcam C920 (046d:082d)), lo aplica en manual
enseguida.La búsqueda prueba:
Cada prueba la puntúa el backend con el mismo detector del escaneo, en este orden de prioridad:
El foco elige la imagen más nítida.
En la barra de la cámara se ve el resultado, por ejemplo
Cámara fija: 4000 K · 20 ms · foco 35.
Por dónde fija la cámara. Primero se lo pide a Chrome. Si Chrome no ofrece
modo manual, como pasa con la C920 en macOS, el servidor le habla a la cámara
directamente por USB (ver services/uvc/README.md), y el estado dice
Cámara fija por USB: …. En ese caso, cada 20 segundos el panel comprueba que
nada haya vuelto a automático (por ejemplo, si se desenchufó la cámara o la Mac
se durmió). Si pasó, vuelve a aplicar el ajuste y lo registra como reapplied.
Qué hace el panel según lo que se puede controlar de cada ajuste:
En cualquier caso, pasá el mouse sobre el estado para ver qué informó Chrome de cada control. Si Chrome ofrece el manual pero un control no lo toma, el estado lo marca ("no tomó el manual: foco").
Los ajustes quedan en runs/camera-presets.json. En runs/web-events.jsonl
quedan como camera_report:
Si cambiás la luz de la sala, buscá otra vez. Si cambia el color de la imagen, volvé a tomar los colores con el gotero.
La aplicación original de Pygame sigue disponible. Desde esta carpeta:
./.venv/bin/python main.py
Por defecto se abre en la segunda pantalla (índice 1) si existe y usa
la webcam 0. En este Mac, AVFoundation enumera la cámara FaceTime como
0 y la Logitech HD Pro Webcam C920 como 1. Para usar la C920, abrí
Terminal y ejecutá:
cd /Users/tobiasarraiza/Documents/Codex/2026-10-02/bien/outputs/postit-wall-game
./.venv/bin/python main.py --camera 1
Cuando macOS solicite acceso a la cámara para Terminal, elegí Permitir. Si no aparece el aviso, revisá Ajustes del Sistema → Privacidad y seguridad → Cámara y activá Terminal si figura allí. El permiso corresponde a la aplicación que inicia Python.
El modo clásico usa siempre los colores por defecto: inicio amarillo, llegada naranja, rebotes verdes y esquinas fucsia. Los colores elegidos en la interfaz web no se aplican a este modo.
A calibra automáticamente: el proyector muestra puntos blancos de
coordenadas conocidas, la webcam los localiza y se calcula una homografía.
Otros cuatro puntos verifican el error. Si la webcam no ve bastantes puntos,
el programa ofrece calibración manual.M calibra manualmente: usá las flechas para mover una cruz hasta el centro
de cada post-it fucsia, en el orden indicado. Shift reduce la velocidad y
Enter confirma cada cruz.Enter acepta; A o M repite
la calibración.← y → cambian la dirección de lanzamiento. Espacio lanza la pelota.
La pelota rebota en los verdes, gana al tocar el naranja y pierde al salir
del campo. R reinicia con el mismo tablero.S. La
proyección queda negra brevemente, se vuelve a leer el tablero y aparece una
pelota nueva en el amarillo. C recalibra si se movió el equipo. Esc
cierra el juego.La calibración automática requiere que los puntos blancos proyectados sean
visibles para la webcam. Con luz muy intensa, sombras o exposición automática
variable, usá M. La calibración supone pared plana; una lente con mucha
distorsión puede necesitar corrección óptica antes de mapear puntos.
Las pruebas son sintéticas y se ejecutan sin hardware. Requieren Node.js además de Python para validar la interfaz del navegador:
./scripts/verify.sh
El script ejecuta estas pruebas y evaluaciones (unos 2 minutos):
./.venv/bin/python -m unittest discover -s . -p 'test_*.py' -q
./.venv/bin/python -m services.calibration.evals.synthetic
./.venv/bin/python -m services.calibration.evals.coded
./.venv/bin/python -m services.vision.eval
./.venv/bin/python -m services.physics.evals.scenarios
./.venv/bin/python -m services.physics.evals.gravity
./.venv/bin/python -m services.runtime.eval
./.venv/bin/python -m services.web.backend.eval
./.venv/bin/python -m tests.eval_web_flow
./.venv/bin/python -m services.web.contract.camera_trials --check
./.venv/bin/python -m services.uvc.smoke
./.venv/bin/python -m services.browser.parity.export
node --test services/browser/tests/*.test.mjs
node services/browser/evals/parity.mjs
node --test services/web/tests/*.test.mjs
node services/web/evals/flows.mjs
node services/web/evals/live-projection.mjs
./.venv/bin/python -m services.web.evals.eyedropper
./.venv/bin/python -m services.simulation.feedback_eval --jobs 4
Con POSTIT_BROWSER=1 ./scripts/verify.sh corre además en Chrome real
tests/live-browser.mjs y tests/closed-loop-browser.mjs (unos 4 minutos;
necesitan Chrome y Playwright).
services/calibration/evals/coded.py decodifica la alineación en 14
condiciones (exposición automática, reflejos, desenfoque, cuadros
mezclados): 140/140 sin ids equivocados, error contra la verdad p95 ≤ 0,11 px.
services/simulation/feedback_eval.py es el lazo cerrado: el backend real,
un proyector y una cámara simulados, y 18 escenarios (sin alinear, alineado
exacto o con 2 px de error, con y sin relleno, sala oscura, iluminada,
exposición automática y el sistema completo con su alineación y su chequeo
de color). Exige 0 parpadeos, 0 post-its fantasma, el contorno a ≤ 4 px de
donde debe y el color nunca sobre la pared. La línea base con el proyector anterior
reproduce la falla medida en la pared real.
services/browser/parity exporta las respuestas de Python y los tests de
services/browser exigen que el port de JavaScript dé las mismas en 2116
casos: detección, refinamiento subpíxel, alineación con sus cinco motivos de
falla, sondas y guiones completos del controlador.
tests/closed-loop-browser.mjs abre el panel y el proyector reales en Chrome
con una cámara simulada que filma la pared iluminada por la ventana del
proyector, con los dos backends: la alineación sale a 0,03-0,08 px de la
verdad, los contornos a 0,5 px de los papeles, cada papel con su color y
ninguna luz del proyector sobre la pared alrededor.
services/web/evals/eyedropper.py mide el gotero: hace 588 clicks sobre
fotos de la pared en siete condiciones de luz y foco, y exige que todo click
adentro del post-it funcione y que ningún click fallido pase sin aviso.
services/web/evals/flows.mjs comprueba que cada botón del panel coincida
con lo que el backend acepta en cada fase del juego. Si cambiás reglas del
backend o la forma de /api/state, regenerá la matriz que usa:
./.venv/bin/python -m services.web.contract.matrix. Una prueba falla
mientras la matriz guardada no coincida con el backend.
services/web/evals/live-projection.mjs compara los contornos de todas las
muestras reales en pantallas horizontales, verticales y con distintas
proporciones. Exige 100 % de coincidencia de colores, geometría y pelota.
tests/live-browser.mjs verifica las dos ventanas con Chrome y una cámara
sintética. Ver services/web/README.md para ejecutarlo.
Lo que ninguna prueba sin hardware cubre: la lente, el enfoque y la
exposición automática de la cámara real, y el brillo del proyector real. La
alineación los mide en cada sesión y dice su error. Los eventos de la interfaz
web quedan en runs/web-events.jsonl; el modo clásico usa runs/events.jsonl.
contracts/: datos y coordenadas compartidos.services/vision/: segmentación de los post-its por color.services/calibration/: puntos proyectados y homografía del modo clásico.services/physics/: pelota y colisiones en coordenadas del tablero.services/runtime/: captura de cámara, pantalla, controles y registro.services/web/backend/: servidor local, estado del juego y rutas HTTP.services/web/ui/: panel de control y ventana de proyección del navegador.services/web/contract/: matriz de estados del backend que leen las
pruebas y la evaluación del navegador.services/browser/: port a JavaScript del backend para la versión web.services/simulation/: cámara, proyector y pared simulados; evaluación del
lazo cerrado.docs/alignment-and-color.md: contrato de la alineación y de lo que
proyecta cada modo.services/uvc/: control de la cámara por USB (balance, exposición y foco
manuales) cuando Chrome no lo ofrece.main.py: entrada del programa.web.py: entrada de la interfaz web.Cada servicio tiene sus propias pruebas, evaluación y explicación. El hook de
Git ejecuta las pruebas locales antes de cada commit; si clonás el proyecto,
activá el hook con git config core.hooksPath .githooks.
El hook divide la suite completa en grupos independientes que corren en
paralelo; mantiene la misma cobertura y limita los hilos de OpenCV.