Como o Glimt reconhece seu filho, no iPhone

Peter Theill · 4 de novembro de 2026

O Glimt transforma um segundo de cada dia em um filme. A parte interessante é que você não precisa filmar esse segundo: o Glimt o encontra nos vídeos que você já gravou e escolhe o segundo em que o seu filho aparece, mesmo num vídeo de aniversário lotado com outras cinco crianças. Tudo roda no iPhone: sem servidor, sem conta, sem upload. É assim que funciona, com os modelos, a matemática e tempos reais.

O pipeline

O pipeline do Glimt, do rolo da câmera ao segundo enquadrado, em oito etapas1Seu rolo da câmeraPhotoKit: vídeos e Live Photos do dia, vídeos primeiro, no máximo 162Amostrar quadroscerca de 2 por segundo, máx. 16 por vídeo (6 por Live Photo), 1024 px3Encontrar rostosVision: caixas dos rostos + qualidade, rostos abaixo de 36 px ignorados4Alinhar cada rostolandmarks do Vision: olhos, nariz, cantos da boca → 112 × 112, reto5EmbeddingSFace (MobileFaceNet, Core ML): 128 números por rosto, cerca de 2 ms6Compararsimilaridade de cosseno com cada foto de referência, vale a melhor7Pontuar e escolhercriança 60 %, estética + nitidez + rosto 40 %, melhor janela de 1 s8Enquadrarseguir o rosto da criança, dar zoom quando ele é pequeno
As etapas 4 a 6 são o reconhecimento facial; todo o resto serve para escolher um bom segundo e enquadrá-lo.

Por que não usar simplesmente o Vision da Apple?

O Vision faz muita coisa aqui: encontra os rostos (VNDetectFaceCaptureQualityRequest, que também avalia o quanto cada rosto está nítido e de frente), seus landmarks (VNDetectFaceLandmarksRequest) e, no iOS 18, o quanto um quadro é bonito (CalculateImageAestheticsScoresRequest, que também sinaliza recibos e capturas de tela). O que ele não oferece é de quem é um rosto. O álbum Pessoas do Photos sabe, mas esse índice não é aberto para apps.

A primeira versão do Glimt usava VNGenerateImageFeaturePrintRequest num recorte do rosto. É uma impressão digital genérica de imagem: compara cabelo, luz, cores e fundo tanto quanto o próprio rosto. Na prática, confundia tranquilamente irmãos, e qualquer criança fotografada no mesmo cômodo no mesmo horário do dia. Era a ferramenta errada.

O modelo de reconhecimento facial

Agora o Glimt traz o seu próprio: SFace (Zhong et al., 2021, “Sigmoid-Constrained Hypersphere Loss for Robust Face Recognition”), com um backbone MobileFaceNet, publicado no OpenCV Zoo sob a licença Apache 2.0. Ele reporta 99,40 % no LFW. Recebe um rosto de 112 × 112 e devolve 128 números, um embedding; duas fotos da mesma pessoa geram embeddings que apontam na mesma direção.

A conversão foi ONNX → PyTorch (onnx2torch) → Core ML (coremltools, ML Program, float16), seguida de quantização linear dos pesos em 8 bits. O resultado tem 9,8 MB, carrega em cerca de 0,5 s na primeira vez e sua saída bate com o modelo ONNX original com cosseno de 0,998 numa entrada de teste. A normalização da entrada está dentro do grafo, então o app passa simples pixels RGB de 0 a 255.

O alinhamento importa mais do que você imagina

O SFace foi treinado com rostos alinhados a um template fixo: olhos, ponta do nariz e cantos da boca em posições conhecidas no quadrado de 112 × 112. Um rosto inclinado ou fora do centro gera um embedding visivelmente pior. Os landmarks do Vision fornecem os cinco pontos (as pupilas, o centro da região do nariz, os pontos mais externos dos lábios); o Glimt então encontra a rotação, a escala uniforme e o deslocamento que melhor os levam ao template. Em duas dimensões, esse problema de mínimos quadrados tem solução fechada se você tratar os pontos como números complexos:

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

O Core Image a aplica (uma única transformação afim, renderizador por software, saída de 112 pixels) e o recorte vai para o modelo. As fotos de referência passam pelo modelo duas vezes, como estão e espelhadas, e as duas são combinadas pela média: uma referência um pouco mais estável, de graça.

É a mesma criança? Calibrando o limiar

O app compara o embedding de cada rosto com as fotos de referência por similaridade de cosseno e fica com a melhor correspondência. Onde traçar a linha foi medido, não chutado, com retratos da Casa Branca em domínio público (retratos oficiais com anos de diferença e um retrato de família com pai, mãe e as duas filhas):

Similaridade de cosseno entre embeddings faciais: mesma pessoa 0,70 a 0,85, familiares 0,20 a 0,43, estranhos 0,01 a 0,25. Um quadro conta como a criança a partir de 0,45.0,45 = a criançaMesma pessoa0,830,850,790,70Família, mesma foto0,430,410,370,270,20Família, outra foto0,300,26Estranhos0,250,180,150,140,120,080,060,030,0100,250,50,751
Cada ponto é um par de rostos; “mesma pessoa” significa a mesma pessoa em outra foto. A faixa sombreada é onde a “semelhança” do Glimt sobe de 0 (0,30) para 1 (0,60); um quadro conta como a criança a partir de 0,45.

O mesmo homem em retratos tirados com quatro anos de diferença marca 0,83; as mesmas pessoas recortadas da foto de família, 0,70–0,85. Estranhos ficam abaixo de 0,25. O caso difícil é a família: duas irmãs na mesma foto marcam 0,43 entre si, mãe e filhas até 0,41 (mesma luz e mesma pose deixam parentes mais parecidos), enquanto a mãe comparada às filhas numa foto diferente cai para 0,26–0,30. Por isso a linha fica em 0,45: acima das irmãs, bem abaixo da mesma pessoa. Cinco fotos são um teste de sanidade, não uma avaliação, e a linha será ajustada com vídeos reais de família.

Mais duas regras deixam os sósias de fora: num vídeo, um único quadro de sorte não basta (são precisos dois quadros a 0,45, ou um claramente acima, a 0,57), e quando há vários rostos num quadro, só o que melhor corresponde é considerado.

Várias fotos de referência

Uma foto de referência é um ângulo, uma luz e uma idade. Crianças mudam rápido. O Glimt permite adicionar até cinco fotos, e o rosto num quadro só precisa se parecer com uma delas, então uma foto de perfil ou o rosto do inverno passado ainda podem ser reconhecidos. Quando uma foto tem várias pessoas, você toca no rosto certo; o Glimt nunca adivinha “o maior”.

Escolhendo o segundo e enquadrando

Cada quadro recebe uma pontuação: 35 % pela estética, 20 % pela nitidez (variância de um laplaciano numa cópia em tons de cinza de 96 × 96: em quadros borrados os valores mudam pouco de um pixel para outro) e 45 % por haver um rosto bem visível. Com fotos de referência, essa pontuação inteira vale 40 % e “é a criança?” vale 60 %: um quadro comum do seu filho ganha de um quadro lindo de outra pessoa. O segundo escolhido é a janela com a melhor média, para que um bom quadro ao lado de um borrado não vença.

O enquadramento para um filme na vertical teve um bug próprio que vale a pena contar. Cada quadro apontava o rosto mais nítido e o app tirava a média das posições ao longo do segundo. Com duas crianças, o rosto mais nítido alternava entre elas, e a média caía na parede no meio. Agora cada quadro aponta a criança (ou o rosto mais proeminente: tamanho × qualidade, que é mais estável), e o app pega o maior grupo de posições próximas umas das outras, não a média. Quando o rosto é pequeno, o recorte dá zoom até ele ocupar cerca de um quinto da altura do quadro, no máximo 1,6× para vídeo.

Quão rápido é?

Milissegundos (mediana) por etapa em um iPhone 15 Pro: decodificar o quadro leva 30 ms, reconhecer um rosto 2 msDecodificar quadro (1024 px)30 msPontuação estética (Vision)7,2 msDetectar rostos + qualidade5,4 msNitidez (laplaciano)2,1 msReconhecer um rosto (SFace)2 msLandmarks do rosto1,5 ms
Mediana por quadro, iPhone 15 Pro, iOS 27.2, 12 vídeos, 167 quadros. Um quadro completo leva 47 ms; um vídeo inteiro (16 quadros mais o carregamento), 0,9 s.

A surpresa: reconhecer um rosto é a parte barata, cerca de 2 ms no Neural Engine. Decodificar o quadro do vídeo custa quinze vezes mais. Na prática, o Glimt passa por um dia cheio de vídeos em poucos segundos, e por um mês em poucos minutos.

Rodando em segundo plano

Preencher um ano de dias faltando leva um tempo, então o processo precisa continuar quando você sai do app. O BGContinuedProcessingTask do iOS 26 foi feito para isso: o iOS mostra o progresso na Dynamic Island e na Tela Bloqueada. Três coisas que aprendemos do jeito difícil, todas a partir de um pequeno log no aparelho:

Privacidade

O Glimt não tem código de rede. Rostos, embeddings e escolhas ficam no armazenamento do próprio app no iPhone. A única coisa que pode tocar a rede é o próprio Photos, buscando os seus vídeos no seu iCloud Photos quando eles não estão no telefone.

Créditos: SFace por Yaoyao Zhong e colegas; conversão ONNX por Chengrui Wang; OpenCV Zoo (Apache 2.0). Dúvidas ou ideias: glimt@theill.com.