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)

  1. 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.)
  2. Sin telemetría ni salida de datos del dispositivo. (Test que escanea imports: socket, http, urllib, requests, etc. prohibidos.)
  3. Datos biométricos = datos sensibles (LFPDPPP art. 3 VI): cifrado en reposo y retención explícita con fecha de purga.
  4. Prohibido JPEG en cualquier punto de la cadena; solo PNG lossless.
  5. 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 .bin float32, 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)

  1. Recuerdo libre en texto, sin estímulo visual. No avanza hasta cerrarse (.cerrar).
  2. Sondeo por categorías (forma de cara, edad percibida, complexión, boca) todavía sin imágenes.
  3. Generación holística: 6 caras candidatas (blur primario), el testigo selecciona por impresión global, iteración evolutiva.
  4. 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.svg paramétrico (malla de Delaunay + landmarks + hashes), sin assets binarios. El PNG raster es derivado.
  • ficha.pdf con el retrato embebido (lossless) y los hashes SHA-256 del JSON y del modelo.
  • retrato verificar re-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)

  1. 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.
  2. 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.
  3. Dimensiones de los .bin: guardar dims en el manifest y reshape al cargar (los .bin son float32 planos).
  4. EOF de entrada: el flujo interactivo debe manejar EOF como interrupción limpia, no traceback.
  5. σ 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)

AlternativaResultadoMotivo
scikit-image PiecewiseAffineTransformNO adoptar~10× más lento, no byte-idéntico (rompe regeneración de PNG), API en deprecación
StyleGAN2-ADADescartadoLicencia NVIDIA no comercial; pesos FFHQ entrenados con fotos reales; projector.py = inversión latente (riesgo R1); GPU ≥12 GB
MediaPipe TasksSolo data prep tras auditoríaEl Privacy Notice oficial declara telemetría a Google
dlib shape predictor (68)Aprobado para data prepLocal, determinista; prohibido usar dlib.face_recognition (embeddings)
Menpo/menpofitReferente académicoMismo paradigma AAM; re-evaluar su fitting (SDM) con dataset real
OpenCV EigenFaceValidación conceptualConfirma el enfoque PCA; nuestro parche warpado es superior
InsightFace / face_recognitionProhibidosEmbeddings + 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.