Come Glimt riconosce tuo figlio, sull'iPhone

Peter Theill · 4 novembre 2026

Glimt trasforma un secondo di ogni giorno in un film. La parte interessante è che non devi filmare quel secondo: Glimt lo trova nei video che hai già girato e sceglie il secondo in cui c'è tuo figlio, anche in un video di compleanno affollato con altri cinque bambini. Tutto gira sull'iPhone: niente server, niente account, niente upload. Ecco come funziona, con i modelli, la matematica e tempi reali.

La pipeline

La pipeline di Glimt, dal rullino al secondo inquadrato, in otto passaggi1Il tuo rullinoPhotoKit: video e Live Photo del giorno, prima i video, al massimo 162Campiona i framecirca 2 al secondo, max 16 per video (6 per Live Photo), 1024 px3Trova i voltiVision: riquadri dei volti + qualità, ignorati i volti sotto i 36 px4Allinea ogni voltolandmark di Vision: occhi, naso, angoli della bocca → 112 × 112, dritto5EmbeddingSFace (MobileFaceNet, Core ML): 128 numeri per volto, circa 2 ms6Confrontasimilarità del coseno con ogni foto di riferimento, conta la migliore7Punteggio e sceltabambino 60 %, estetica + nitidezza + volto 40 %, miglior finestra di 1 s8Inquadrasegue il volto del bambino, zooma quando è piccolo
I passaggi 4–6 sono il riconoscimento facciale; tutto il resto serve a scegliere un buon secondo e a inquadrarlo.

Perché non usare semplicemente Vision di Apple?

Vision qui fa molto: trova i volti (VNDetectFaceCaptureQualityRequest, che valuta anche quanto ogni volto è nitido e frontale), i loro landmark (VNDetectFaceLandmarksRequest) e, su iOS 18, quanto è bello un frame (CalculateImageAestheticsScoresRequest, che segnala anche scontrini e screenshot). Quello che non offre è di chi è un volto. L'album Persone di Photos lo sa, ma quell'indice non è aperto alle app.

La prima versione di Glimt usava VNGenerateImageFeaturePrintRequest su un ritaglio del volto. È un'impronta d'immagine generica: confronta capelli, luce, colori e sfondo tanto quanto il volto stesso. In pratica scambiava tranquillamente fratelli e sorelle, e qualsiasi bambino fotografato nella stessa stanza alla stessa ora del giorno. Era lo strumento sbagliato.

Il modello di riconoscimento facciale

Ora Glimt ha il suo: SFace (Zhong et al., 2021, «Sigmoid-Constrained Hypersphere Loss for Robust Face Recognition»), con un backbone MobileFaceNet, pubblicato nell'OpenCV Zoo con licenza Apache 2.0. Dichiara il 99,40 % su LFW. Prende un volto di 112 × 112 e restituisce 128 numeri, un embedding; due foto della stessa persona danno embedding che puntano nella stessa direzione.

La conversione è stata ONNX → PyTorch (onnx2torch) → Core ML (coremltools, ML Program, float16), poi una quantizzazione lineare dei pesi a 8 bit. Il risultato pesa 9,8 MB, si carica in circa 0,5 s la prima volta e il suo output corrisponde al modello ONNX originale con un coseno di 0,998 su un input di test. La normalizzazione dell'input è dentro il grafo, quindi l'app gli passa semplici pixel RGB da 0 a 255.

L'allineamento conta più di quanto pensi

SFace è stato addestrato su volti allineati a un template fisso: occhi, punta del naso e angoli della bocca in posizioni note nel quadrato di 112 × 112. Un volto inclinato o decentrato dà un embedding sensibilmente peggiore. I landmark di Vision forniscono i cinque punti (le pupille, il centro della regione del naso, i punti più esterni delle labbra); Glimt trova poi la rotazione, la scala uniforme e la traslazione che li portano meglio sul template. In due dimensioni quel problema ai minimi quadrati ha una soluzione in forma chiusa, se tratti i punti come numeri complessi:

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 applica (una sola trasformazione affine, renderer software, un output di 112 pixel) e il ritaglio va al modello. Le foto di riferimento vengono elaborate due volte, così come sono e specchiate, e si fa la media delle due: un riferimento un po' più stabile, gratis.

È lo stesso bambino? Calibrare la soglia

L'app confronta l'embedding di ogni volto con le foto di riferimento tramite similarità del coseno e tiene la corrispondenza migliore. Dove tracciare la linea è stato misurato, non indovinato, su ritratti della Casa Bianca di pubblico dominio (ritratti ufficiali a distanza di anni e un ritratto di famiglia con entrambi i genitori e le due figlie):

Similarità del coseno tra embedding dei volti: stessa persona da 0,70 a 0,85, familiari da 0,20 a 0,43, estranei da 0,01 a 0,25. Un frame conta come il bambino da 0,45.0,45 = il bambinoStessa persona0,830,850,790,70Famiglia, stessa foto0,430,410,370,270,20Famiglia, altra foto0,300,26Estranei0,250,180,150,140,120,080,060,030,0100,250,50,751
Ogni punto è una coppia di volti; «stessa persona» significa la stessa persona in un'altra foto. La fascia ombreggiata è dove la «somiglianza» di Glimt sale da 0 (0,30) a 1 (0,60); un frame conta come il bambino da 0,45.

Lo stesso uomo in ritratti scattati a quattro anni di distanza ottiene 0,83; le stesse persone estratte dalla foto di famiglia 0,70–0,85. Gli estranei restano sotto 0,25. Il caso difficile è la famiglia: due sorelle nello stesso scatto ottengono 0,43 tra loro, madre e figlie fino a 0,41 (stessa luce e stessa posa rendono i parenti più simili), mentre la madre confrontata con le figlie in una foto diversa scende a 0,26–0,30. Quindi la linea sta a 0,45: sopra le sorelle, ben sotto la stessa persona. Cinque foto sono un controllo di buon senso, non una valutazione, e la soglia verrà regolata su veri video di famiglia.

Altre due regole tengono fuori i sosia: in un video un solo frame fortunato non basta (ne servono due a 0,45, o uno nettamente sopra, a 0,57), e quando in un frame ci sono più volti viene considerato solo quello che corrisponde meglio.

Più foto di riferimento

Una foto di riferimento è un'angolazione, una luce e un'età. I bambini cambiano in fretta. Glimt ti permette di aggiungere fino a cinque foto, e il volto in un frame deve somigliare solo a una di esse, così anche un profilo o il viso dello scorso inverno possono essere riconosciuti. Quando in una foto ci sono più persone, tocchi il volto giusto; Glimt non tira mai a indovinare «il più grande».

Scegliere il secondo e inquadrarlo

Ogni frame riceve un punteggio: 35 % per quanto è bello, 20 % per la nitidezza (varianza di un laplaciano su una copia in scala di grigi di 96 × 96: nei frame sfocati i valori cambiano poco da un pixel all'altro), 45 % per la presenza di un volto chiaro. Con le foto di riferimento, tutto questo punteggio vale il 40 % e «è il bambino?» il 60 %: un frame qualsiasi di tuo figlio batte un bellissimo frame di qualcun altro. Il secondo scelto è la finestra con la media migliore, così un buon frame accanto a uno sfocato non vince.

L'inquadratura per un film verticale ha avuto un suo bug che vale la pena raccontare. Ogni frame indicava il volto più chiaro e l'app faceva la media delle posizioni nel secondo. Con due bambini, il volto più chiaro si alternava tra loro e la media finiva sul muro in mezzo. Ora ogni frame indica il bambino (o il volto più in evidenza: dimensione × qualità, più stabile), e l'app prende il gruppo più grande di posizioni vicine tra loro, non la media. Quando il volto è piccolo, il ritaglio zooma finché occupa circa un quinto dell'altezza del frame, al massimo 1,6× per i video.

Quanto è veloce?

Millisecondi mediani per passaggio su un iPhone 15 Pro: decodificare il frame richiede 30 ms, riconoscere un volto 2 msDecodifica frame (1024 px)30 msPunteggio estetico (Vision)7,2 msVolti + qualità di cattura5,4 msNitidezza (laplaciano)2,1 msRiconosci un volto (SFace)2 msLandmark del volto1,5 ms
Mediana per frame, iPhone 15 Pro, iOS 27.2, 12 video, 167 frame. Un frame completo richiede 47 ms; un video intero (16 frame più il caricamento) 0,9 s.

La sorpresa: riconoscere un volto è la parte economica, circa 2 ms sul Neural Engine. Decodificare il frame del video costa quindici volte di più. In pratica Glimt passa in rassegna una giornata piena di video in pochi secondi, e un mese in pochi minuti.

In background

Riempire un anno di giorni mancanti richiede un po' di tempo, quindi deve continuare quando esci dall'app. BGContinuedProcessingTask di iOS 26 è fatto apposta: iOS mostra l'avanzamento nella Dynamic Island e nella schermata di blocco. Tre cose che abbiamo imparato a nostre spese, tutte da un piccolo log sul dispositivo:

Privacy

Glimt non ha codice di rete. Volti, embedding e scelte restano nello spazio di archiviazione dell'app sull'iPhone. L'unica cosa che può toccare la rete è Photos stessa, quando scarica i tuoi video dal tuo iCloud Photos perché non sono sul telefono.

Crediti: SFace di Yaoyao Zhong e colleghi; conversione ONNX di Chengrui Wang; OpenCV Zoo (Apache 2.0). Domande o idee: glimt@theill.com.