PROMPT — App de retrato hablado (composición facial pericial)
Reutilizable: pega este prompt en cualquier sesión nueva (IA o humana) para continuar o reconstruir el proyecto sin re-litigar lo ya decidido. Actualizado 2026-08-09 con todo lo aprendido en el desarrollo y el benchmark de alternativas.
1. Rol y contexto
Eres ingeniera/o de software construyendo una herramienta de composición
facial (retrato hablado) para uso pericial en México. NO es un sistema
de reconocimiento facial. Responde en español. Escopa hacia abajo: entrega
la versión más pequeña que funcione de verdad antes de pulir. Persiste
toda decisión en ~/nef/decisions/<fecha>-<tema>.md.
Stack aprobado: Python CLI (numpy, scipy, cryptography), sin dependencias externas en runtime (sin red, sin SaaS).
2. Prohibiciones duras (no negociables)
- Sin embeddings faciales, matching, ni conexión a padrones o bases de
identidad. (Incluye: no usar
face_recognition/dlib encodings, InsightFace, ni inversión latente de GANs.) - Sin telemetría ni salida de datos del dispositivo. (Test que escanea imports: socket, http, urllib, requests, etc. prohibidos.)
- Datos biométricos = datos sensibles (LFPDPPP art. 3 VI): cifrado en reposo y retención explícita con fecha de purga.
- Prohibido JPEG en cualquier punto de la cadena; solo PNG lossless.
- Prohibidos assets vectoriales o de catálogo por rasgo: el retrato sale íntegro del modelo estadístico.
3. Representación (modelo)
- Forma: PCA sobre 70 landmarks (alineación Procrustes).
- Textura: PCA sobre parche 512×512 gris, warpado a la forma media y normalizado fotométricamente.
- Eigenvectores en
.binfloat32, cargados localmente, versionados inmutable (manifest.json con SHA-256 por archivo; hash del modelo = hash del manifest; cualquier alteración = rechazo al cargar). - Regresión lineal semántica → coeficientes (para guiar la generación por categorías del sondeo).
4. Render
- Warp afín por triángulo sobre triangulación de Delaunay (topología fija sobre la forma media, guardada en el modelo).
- Byte-determinista: mismos coeficientes + σ + modelo ⇒ mismos bytes. Esto hace que el PNG sea estrictamente derivado de composite.json + modelo.
- Salida 1024×1024.
- Modo primario: baja frecuencia (gaussiano σ configurable, por defecto 8 px a 1024 = equivalente a 2 px a 256). Secundario: nítido.
- El σ aplicado se registra (
sigma_aplicado) en composite.json y en la ficha.
5. Cruce evolutivo
- Interpolación SIEMPRE en espacio de coeficientes PCA (forma + textura), nunca promediando bitmaps (verificado por test: el hijo ≠ promedio de renders; el render del hijo = render de los coeficientes mezclados).
- Generación: 6 candidatas alrededor del objetivo semántico + mutación gaussiana; iteraciones posteriores: cruce de las seleccionadas (mezcla convexa 0.3-0.7 + mutación) con el objetivo.
- Optimización validada: render nítido una vez, derivar el blur con gaussian_filter (mitad de renders).
6. Flujo obligatorio (bloqueante, en este orden)
- Recuerdo libre en texto, sin estímulo visual. No avanza hasta
cerrarse (
.cerrar). - Sondeo por categorías (forma de cara, edad percibida, complexión, boca) todavía sin imágenes.
- Generación holística: 6 caras candidatas (blur primario), el testigo selecciona por impresión global, iteración evolutiva.
- Ajuste fino paramétrico, sólo al final.
Sin telemetría; imágenes solo aparecen desde la fase 3.
7. Artefactos e integridad
composite.json= fuente de verdad: {sesión_id, timestamp, purge_at, parámetros, recuerdo, sondeo, coeficientes, linaje_de_iteraciones, confianza_declarada, sigma_aplicado, hashes{json_sha256, model_sha256}}.render.svgparamétrico (malla de Delaunay + landmarks + hashes), sin assets binarios. El PNG raster es derivado.ficha.pdfcon el retrato embebido (lossless) y los hashes SHA-256 del JSON y del modelo.retrato verificarre-deriva y compara; PNG reproducible desde JSON + modelo.- Sesión cifrada en reposo (Fernet, clave local 0600) con
purge_at(por defecto 90 días) y comando de purga.
8. Trampas técnicas ya encontradas (no repetir)
- Procrustes: usar solo traslación + rotación (sin rescale, para conservar tamaño como información) y canonicalizar la forma media al marco del render (altura 96, mentón en y=100). Sin esto el render sale fuera del canvas.
- Matriz afín del warp: aplicar la inversa correcta
(
pts_src = pts_dst @ inv(M).T). La transpuesta equivocada produce la identidad sobre los vértices y el warp queda vacío. - Dimensiones de los .bin: guardar
dimsen el manifest yreshapeal cargar (los .bin son float32 planos). - EOF de entrada: el flujo interactivo debe manejar EOF como interrupción limpia, no traceback.
- σ escala con la resolución: si cambias
out_px, recalibra el σ por defecto y documenta que σ está en píxeles de salida.
9. Benchmark de alternativas (concluido, no re-abrir sin motivo)
| Alternativa | Resultado | Motivo |
|---|---|---|
scikit-image PiecewiseAffineTransform | NO adoptar | ~10× más lento, no byte-idéntico (rompe regeneración de PNG), API en deprecación |
| StyleGAN2-ADA | Descartado | Licencia NVIDIA no comercial; pesos FFHQ entrenados con fotos reales; projector.py = inversión latente (riesgo R1); GPU ≥12 GB |
| MediaPipe Tasks | Solo data prep tras auditoría | El Privacy Notice oficial declara telemetría a Google |
| dlib shape predictor (68) | Aprobado para data prep | Local, determinista; prohibido usar dlib.face_recognition (embeddings) |
| Menpo/menpofit | Referente académico | Mismo paradigma AAM; re-evaluar su fitting (SDM) con dataset real |
| OpenCV EigenFace | Validación conceptual | Confirma el enfoque PCA; nuestro parche warpado es superior |
| InsightFace / face_recognition | Prohibidos | Embeddings + matching (R1) |
Conclusión: el motor estadístico propio se mantiene. El cuello de botella de calidad es el dataset, no el motor.
10. Datos y formato
- Dataset sintético para validación:
landmarks.npy(N,70,2) float32,textures.npy(N,512,512,1) uint8,semantic.npy(N,9) float32. - El dataset real entra con el mismo formato (landmarks alineados obtenibles con dlib 68 + mapeo a los 70 del modelo).
- Los datos sintéticos se marcan explícitamente como NO biométricos.
11. Pendientes (estado actual)
- Dataset real de 70 landmarks alineados (formato listo).
- Validación del flujo completo con un perito o usuaria real.
- Decidir UI: el flujo corre hoy en terminal; un asistente visual (sin red) podría ser la siguiente capa.
- Regresión semántica → coeficientes afinada con datos reales.
12. Definición de hecho
- Tests que fijan los contratos duros y pasan (hoy: 11).
- Una sesión end-to-end real: synthetic → build → sesión → verificar → finalizar → purge, con artefactos generados y revisados por una persona.