GazeFocus cambia automaticamente el foco de ventana entre monitores segun la orientacion de la cabeza (head pose, yaw): un unico proceso sincrono en Windows 11.
Un solo proceso que lee la pose de la cabeza desde la webcam, decide a que monitor estas mirando (yaw) y trae al primer plano una ventana de ese monitor, todo de forma automatica y sincrona en Windows 11.
- Windows 11.
- Una webcam.
- Conda (Miniconda o Anaconda).
El stack completo se define en environment.yml (Python 3.11, OpenCV, MediaPipe,
NumPy, pywin32). No hay dependencias fuera de environment.yml.
Crea el entorno conda a partir de environment.yml y activalo:
conda env create -f environment.yml
conda activate gazefocusTodos los comandos de las secciones siguientes se ejecutan dentro de
conda activate gazefocus.
Antes del modo normal hay que calibrar una vez. La calibracion te pide mirar a
cada monitor de izquierda a derecha y pulsar Enter; con eso aprende el yaw asociado a
cada frontera y escribe config/calibration.json.
python src/main.py --calibrateOpcionalmente puedes indicar la webcam y los FPS de captura:
python src/main.py --calibrate --cam-index 0 --fps 15Sigue los prompts en consola (mira cada monitor de izquierda a derecha y pulsa Enter).
Al terminar, el programa imprime la ruta del archivo escrito (config/calibration.json)
y la instruccion para arrancar el modo normal. La calibracion debe completarse antes
de ejecutar el modo normal.
Webcams espejadas: si tu camara entrega la imagen en espejo (lo que invierte el
signo del yaw), la calibracion lo detecta y corrige automaticamente —persiste un
flag mirror en config/calibration.json y emite un aviso por log—; no tienes que
configurar nada. En modo normal, M6 lee ese flag y lo aplica a la estimacion en vivo,
de modo que el angulo en runtime y las anclas calibradas quedan en el mismo marco. Una
secuencia de angulos genuinamente inconsistente (ni creciente ni decreciente) si
aborta con un error claro y no escribe el archivo.
python src/main.pyFlags opcionales:
python src/main.py --cam-index 0 --fps 15 --debounce-ms 500.0 --dead-band 0.1 --log-level INFO| Flag | Tipo | Default | Que hace |
|---|---|---|---|
--cam-index |
int | 0 |
Indice de la webcam (M1). |
--fps |
int | 15 |
FPS de procesamiento y cadencia del lazo. |
--debounce-ms |
float | 500.0 |
Ventana de debounce del decisor (M4) en ms. |
--dead-band |
float | (sin valor) | Banda muerta que sobreescribe la del mapa; si se omite, se usa la dead_band por frontera de la calibracion. |
--calibrate |
flag | False |
Ejecuta la calibracion y termina (no entra al lazo). |
--log-level |
{DEBUG,INFO,WARNING,ERROR} |
INFO |
Nivel de logging. |
El modo normal requiere una calibracion previa. Si config/calibration.json falta
o es invalido, el programa imprime un mensaje claro instando a ejecutar
python src/main.py --calibrate y sale con un codigo distinto de cero (sin abrir la
camara).
Pulsa Ctrl+C para un cierre limpio: el proceso libera la camara antes de salir.
Tambien Ctrl+Break (SIGBREAK) y SIGTERM convergen en el mismo cierre limpio. Nota: en Windows 11 la entrega de SIGTERM es best-effort; Ctrl+C y Ctrl+Break son las rutas fiables en consola.
Se registran:
- El arranque (modo,
cam_index,fps,debounce_ms,dead_band,log_level). - Cada evento de foco (
FocusEventcon sutarget_orderyts) y el resultado booleano de la actuacion de foco:True(el foco fue efectivo) oFalse. - El cierre solicitado.
El nivel es INFO por defecto y se configura con --log-level
(DEBUG/INFO/WARNING/ERROR). Un resultado False significa que el foco no
pego (la ventana no quedo al frente, p.ej. monitor sin ventana elegible o monitor
desconectado): es la degradacion con gracia del punto fragil de M5, y se registra a
nivel WARNING.
El cambio de foco real en Windows 11 no se puede automatizar (depende de la
mitigacion fragil de M5 y del estado de la sesion de escritorio), por lo que se valida
manualmente y no en CI. El procedimiento y el registro estan en
docs/manual_acceptance.md.
La prueba se lanza a traves del entry point de M6 (python src/main.py), que
declara Per-Monitor-V2 DPI awareness al arrancar; no invocando el actuador en un
proceso crudo sin esa declaracion. Escenarios resumidos:
- Base (1 monitor): dos ventanas reales;
focus_monitor(0)activa la esperada y devuelveTrue. - Multi-monitor: una ventana por monitor;
focus_monitor(order)activa la del monitor correcto en cada caso (valida la coincidencia del espacio de coordenadas M2 <-> M5). - Fallo controlado: monitor sin ventana elegible (o
orderinexistente);focus_monitordevuelveFalsesin excepcion y sin cambio de foco.
El cambio de foco real es la unica parte validada manualmente; el resto se cubre en CI.
- Todo se ejecuta dentro de
conda activate gazefocus. - El cambio de foco efectivo en background es el unico punto fragil del sistema (mitigacion de M5) y por eso se valida manualmente (seccion 8).
- El proceso declara Per-Monitor-V2 DPI awareness al arrancar para que las coordenadas de monitores y ventanas compartan el mismo espacio fisico.