Cómo Glimt reconoce a tu hijo, en el iPhone

Peter Theill · 4 de noviembre de 2026

Glimt convierte un segundo de cada día en una película. Lo interesante es que no tienes que grabar ese segundo: Glimt lo encuentra en los vídeos que ya hiciste y elige el segundo en el que sale tu hijo, incluso en un vídeo de cumpleaños abarrotado con otros cinco niños. Todo se ejecuta en el iPhone: sin servidor, sin cuenta, sin subir nada. Así es como funciona, con los modelos, las matemáticas y tiempos reales.

El pipeline

El pipeline de Glimt, del carrete al segundo encuadrado, en ocho pasos1Tu carretePhotoKit: vídeos y Live Photos del día, vídeos primero, 16 como máximo2Muestrear fotogramasunos 2 por segundo, máx. 16 por vídeo (6 por Live Photo), 1024 px3Detectar carasVision: recuadros de caras + calidad; se omiten caras de menos de 36 px4Alinear cada caralandmarks de Vision: ojos, nariz, comisuras → 112 × 112, enderezada5EmbeddingSFace (MobileFaceNet, Core ML): 128 números por cara, unos 2 ms6Compararsimilitud coseno con cada foto de referencia, cuenta la mejor7Puntuar y elegirniño 60 %, estética + nitidez + cara 40 %, mejor ventana de 1 s8Encuadrarseguir la cara del niño, hacer zoom cuando es pequeña
Los pasos 4 a 6 son el reconocimiento facial; todo lo demás consiste en elegir un buen segundo y encuadrarlo.

¿Por qué no usar simplemente Vision de Apple?

Vision hace mucho aquí: encuentra las caras (VNDetectFaceCaptureQualityRequest, que además puntúa lo nítida y frontal que es cada cara), sus landmarks (VNDetectFaceLandmarksRequest) y, en iOS 18, lo bien que se ve un fotograma (CalculateImageAestheticsScoresRequest, que también marca tiques y capturas de pantalla). Lo que no ofrece es de quién es una cara. El álbum Personas de Photos lo sabe, pero ese índice no está abierto a las apps.

La primera versión de Glimt usaba VNGenerateImageFeaturePrintRequest sobre un recorte de la cara. Es una huella de imagen genérica: compara el pelo, la luz, los colores y el fondo tanto como la propia cara. En la práctica confundía alegremente a hermanos, y a cualquier niño fotografiado en la misma habitación a la misma hora del día. Era la herramienta equivocada.

El modelo de reconocimiento facial

Ahora Glimt lleva el suyo propio: SFace (Zhong et al., 2021, «Sigmoid-Constrained Hypersphere Loss for Robust Face Recognition»), con un backbone MobileFaceNet, publicado en el OpenCV Zoo bajo licencia Apache 2.0. Declara un 99,40 % en LFW. Recibe una cara de 112 × 112 y devuelve 128 números, un embedding; dos fotos de la misma persona dan embeddings que apuntan en la misma dirección.

La conversión fue ONNX → PyTorch (onnx2torch) → Core ML (coremltools, ML Program, float16), seguida de una cuantización lineal de pesos a 8 bits. El resultado ocupa 9,8 MB, carga en unos 0,5 s la primera vez y su salida coincide con el modelo ONNX original con un coseno de 0,998 en una entrada de prueba. La normalización de la entrada está dentro del grafo, así que la app le pasa píxeles RGB de 0 a 255 sin más.

La alineación importa más de lo que crees

SFace se entrenó con caras alineadas a una plantilla fija: ojos, punta de la nariz y comisuras de la boca en posiciones conocidas dentro del cuadrado de 112 × 112. Una cara inclinada o descentrada da un embedding claramente peor. Los landmarks de Vision proporcionan los cinco puntos (las pupilas, el centro de la región de la nariz, los puntos extremos de los labios); después Glimt busca la rotación, la escala uniforme y el desplazamiento que mejor los llevan a la plantilla. En dos dimensiones, ese problema de mínimos cuadrados tiene solución cerrada si tratas los puntos como números complejos:

a = Σ conj(zᵢ − z̄) · (wᵢ − w̄) / Σ |zᵢ − z̄|²      // rotation + scale, one complex number
w = a · z + (w̄ − a · z̄)                          // the transform for any point z

Core Image la aplica (una sola transformación afín, renderizador por software, una salida de 112 píxeles) y el recorte va al modelo. Las fotos de referencia se procesan dos veces, tal cual y en espejo, y se promedian ambas: una referencia algo más estable sin coste extra.

¿Es el mismo niño? Calibrar el umbral

La app compara el embedding de cada cara con las fotos de referencia mediante similitud coseno y se queda con la mejor coincidencia. Dónde poner el límite se midió, no se adivinó, con retratos de la Casa Blanca de dominio público (retratos oficiales separados por años y un retrato familiar con ambos padres y sus dos hijas):

Similitud coseno entre embeddings faciales: misma persona 0,70 a 0,85, familiares 0,20 a 0,43, desconocidos 0,01 a 0,25. Un fotograma cuenta como el niño a partir de 0,45.0,45 = el niñoMisma persona0,830,850,790,70Familia, misma foto0,430,410,370,270,20Familia, otra foto0,300,26Desconocidos0,250,180,150,140,120,080,060,030,0100,250,50,751
Cada punto es un par de caras; «misma persona» significa la misma persona en otra foto. La franja sombreada es donde la «semejanza» de Glimt sube de 0 (0,30) a 1 (0,60); un fotograma cuenta como el niño a partir de 0,45.

El mismo hombre en retratos tomados con cuatro años de diferencia obtiene 0,83; las mismas personas extraídas de la foto familiar, 0,70–0,85. Los desconocidos se quedan por debajo de 0,25. El caso difícil es la familia: dos hermanas en la misma toma obtienen 0,43 entre ellas, la madre y las hijas hasta 0,41 (la misma luz y la misma pose hacen que los parientes se parezcan más), mientras que la madre frente a sus hijas en una foto distinta baja a 0,26–0,30. Así que el límite está en 0,45: por encima de las hermanas y muy por debajo de la misma persona. Cinco fotos son una comprobación de cordura, no una evaluación, y el límite se ajustará con vídeos familiares reales.

Dos reglas más dejan fuera a los parecidos: en un vídeo, un solo fotograma afortunado no basta (hacen falta dos a 0,45, o uno claramente por encima, a 0,57), y cuando hay varias caras en un fotograma solo se tiene en cuenta la que mejor coincide.

Varias fotos de referencia

Una foto de referencia es un ángulo, una luz y una edad. Y los niños cambian rápido. Glimt te deja añadir hasta cinco fotos, y la cara de un fotograma solo tiene que parecerse a una de ellas, así que una foto de perfil o la cara del invierno pasado todavía pueden reconocerse. Cuando una foto tiene varias personas, tocas la cara correcta; Glimt nunca adivina «la más grande».

Elegir el segundo y encuadrarlo

Cada fotograma recibe una puntuación: un 35 % por lo bien que se ve, un 20 % por la nitidez (varianza de un laplaciano sobre una copia en grises de 96 × 96: en los fotogramas borrosos los valores cambian poco de un píxel a otro) y un 45 % por si hay una cara clara. Con fotos de referencia, toda esa puntuación pesa un 40 % y «¿es el niño?» un 60 %: un fotograma normal de tu hijo gana a uno precioso de otra persona. El segundo elegido es la ventana con la mejor media, para que un buen fotograma junto a uno borroso no gane.

El encuadre para una película en vertical tuvo su propio bug, que vale la pena contar. Cada fotograma señalaba la cara más clara y la app promediaba las posiciones a lo largo del segundo. Con dos niños, la cara más clara alternaba entre ellos y la media acababa en la pared del medio. Ahora cada fotograma señala al niño (o la cara más destacada: tamaño × calidad, que es más estable), y la app toma el mayor grupo de posiciones cercanas entre sí, no la media. Cuando la cara es pequeña, el recorte hace zoom hasta que ocupa alrededor de una quinta parte de la altura del fotograma, como mucho 1,6× en vídeo.

¿Qué tan rápido es?

Milisegundos (mediana) por paso en un iPhone 15 Pro: decodificar el fotograma lleva 30 ms, reconocer una cara 2 msDecodificar imagen (1024 px)30 msPuntuación estética (Vision)7,2 msCaras + calidad de captura5,4 msNitidez (laplaciano)2,1 msReconocer una cara (SFace)2 msLandmarks faciales1,5 ms
Mediana por fotograma, iPhone 15 Pro, iOS 27.2, 12 vídeos, 167 fotogramas. Un fotograma completo lleva 47 ms; un vídeo entero (16 fotogramas más cargarlo), 0,9 s.

La sorpresa: reconocer una cara es la parte barata, unos 2 ms en el Neural Engine. Decodificar el fotograma del vídeo cuesta quince veces más. En la práctica, Glimt repasa un día con muchos vídeos en unos segundos, y un mes en unos minutos.

Ejecución en segundo plano

Rellenar un año de días que faltan lleva un rato, así que debería seguir cuando sales de la app. BGContinuedProcessingTask, de iOS 26, está hecho para eso: iOS muestra el progreso en la Dynamic Island y en la pantalla bloqueada. Tres cosas que aprendimos por las malas, todas gracias a un pequeño log en el dispositivo:

Privacidad

Glimt no tiene código de red. Las caras, los embeddings y las elecciones se quedan en el almacenamiento propio de la app en el iPhone. Lo único que puede tocar la red es Photos, cuando descarga tus propios vídeos de tu propio iCloud Photos si no están en el teléfono.

Créditos: SFace de Yaoyao Zhong y colaboradores; conversión a ONNX de Chengrui Wang; OpenCV Zoo (Apache 2.0). Preguntas o ideas: glimt@theill.com.