Glimt gör en sekund från varje dag till en film. Det intressanta är att du inte behöver filma den sekunden: Glimt hittar den i videorna du redan har tagit och väljer sekunden där ditt barn är med i bild, även i en fullsatt kalasvideo med fem andra barn. Allt körs på iPhonen: ingen server, inget konto, ingen uppladdning. Så här fungerar det, med modellerna, matematiken och riktiga tidsmätningar.
Pipelinen
Varför inte bara använda Apples Vision?
Vision gör mycket här: det hittar ansikten (VNDetectFaceCaptureQualityRequest, som också bedömer hur skarpt
och hur rakt framifrån varje ansikte är), deras landmärken (VNDetectFaceLandmarksRequest) och, på iOS 18, hur bra en bildruta
ser ut (CalculateImageAestheticsScoresRequest, som också flaggar kvitton och skärmavbilder). Det som saknas
är vem ett ansikte är. People-albumet i Photos vet det, men det indexet är inte öppet för appar.
Den första versionen av Glimt använde VNGenerateImageFeaturePrintRequest på ett utsnitt av ansiktet. Det är ett
generellt bildfingeravtryck: det jämför hår, ljus, färger och bakgrund lika mycket som själva ansiktet. I praktiken
matchade det gärna syskon, och vilket barn som helst som fotograferats i samma rum vid samma tid på dagen. Det var fel verktyg.
Modellen för ansiktsigenkänning
Nu har Glimt en egen: SFace (Zhong et al., 2021, ”Sigmoid-Constrained Hypersphere Loss for Robust Face Recognition”), med en MobileFaceNet-backbone, publicerad i OpenCV Zoo under licensen Apache 2.0. Den når 99,40 % på LFW. Den tar ett ansikte på 112 × 112 och returnerar 128 tal, en embedding; två foton av samma person ger embeddings som pekar åt samma håll.
Konverteringen gick ONNX → PyTorch (onnx2torch) → Core ML (coremltools, ML Program, float16),
och sedan linjär 8-bitarskvantisering av vikterna. Resultatet är 9,8 MB, laddas på ungefär 0,5 s första gången, och utdata
stämmer med den ursprungliga ONNX-modellen med cosinus 0,998 på en testindata. Normaliseringen av indata ligger inuti grafen, så
appen skickar in vanliga RGB-pixlar på 0–255.
Justeringen spelar större roll än man tror
SFace tränades på ansikten som justerats mot en fast mall: ögon, nästipp och mungipor på kända platser i kvadraten på 112 × 112. Ett ansikte som lutar eller sitter snett ger en märkbart sämre embedding. Visions landmärken ger de fem punkterna (pupillerna, mitten av näspartiet, läpparnas yttersta punkter); Glimt hittar sedan den rotation, likformiga skalning och förskjutning som bäst flyttar dem till mallen. I två dimensioner har det minstakvadratproblemet en sluten lösning om man behandlar punkterna som komplexa tal:
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 tillämpar den (en affin transform, mjukvarurenderare, 112 pixlar ut), och utsnittet går till modellen. Referensfoton bäddas in två gånger, som de är och spegelvända, och de två medelvärdesbildas: en lite stabilare referens på köpet.
Är det samma barn? Att kalibrera tröskeln
Appen jämför varje ansiktes embedding med referensfotona med cosinuslikhet och behåller den bästa matchningen. Var gränsen skulle gå mättes i stället för att gissas, på porträtt från Vita huset som är allmän egendom (officiella porträtt med flera års mellanrum och ett familjeporträtt med båda föräldrarna och två döttrar):
Samma man på porträtt tagna med fyra års mellanrum får 0,83; samma personer utplockade ur familjefotot 0,70–0,85. Främlingar stannar under 0,25. Det svåra fallet är familj: två systrar i samma bild får 0,43 mot varandra, mamma och döttrar upp till 0,41 (samma ljus och pose får släktingar att likna varandra mer), medan mamman mot sina döttrar på ett annat foto sjunker till 0,26–0,30. Så gränsen ligger på 0,45: över systrarna, en bra bit under samma person. Fem foton är en rimlighetskontroll, inte en utvärdering, och gränsen kommer att finjusteras på riktiga familjevideor.
Två regler till håller dubbelgångare ute: i en video räcker inte en lyckosam bildruta (det krävs två bildrutor på 0,45, eller en klart över på 0,57), och när flera ansikten finns i en bildruta räknas bara det som matchar bäst.
Flera referensfoton
Ett referensfoto är en vinkel, ett ljus och en ålder. Barn förändras snabbt. I Glimt kan du lägga till upp till fem foton, och ansiktet i en bildruta behöver bara likna ett av dem, så en profilbild eller förra vinterns ansikte kan ändå kännas igen. När ett foto har flera personer trycker du på rätt ansikte; Glimt gissar aldrig på ”det största”.
Att välja sekunden och beskära den
Varje bildruta får en poäng: 35 % hur bra den ser ut, 20 % skärpa (variansen av en Laplace-operator på en grå kopia på 96 × 96: suddiga bildrutor ändras långsamt från pixel till pixel), 45 % om det finns ett tydligt ansikte. Med referensfoton väger hela den poängen 40 % och ”är det barnet” 60 %: en vanlig bildruta av ditt barn slår en fin bildruta av någon annan. Den valda sekunden är fönstret med bäst medelvärde, så en bra bildruta bredvid en suddig vinner inte.
Beskärningen för en film i porträttformat hade en egen bugg som är värd att dela. Varje bildruta pekade ut det tydligaste ansiktet, och appen tog medelvärdet av positionerna över sekunden. Med två barn växlade det tydligaste ansiktet mellan dem, och medelvärdet hamnade på väggen mittemellan. Nu pekar varje bildruta ut barnet (eller det mest framträdande ansiktet: storlek × kvalitet, vilket är stabilare), och appen tar den största gruppen positioner som ligger nära varandra, inte medelvärdet. När ansiktet är litet zoomar utsnittet in tills det är ungefär en femtedel av bildrutans höjd, högst 1,6× för video.
Hur snabbt går det?
Överraskningen: att känna igen ett ansikte är den billiga delen, ungefär 2 ms på Neural Engine. Att avkoda videobildrutan kostar femton gånger mer. I praktiken tar sig Glimt igenom en fullspäckad dag med videor på några sekunder, och en månad på några minuter.
Körning i bakgrunden
Att fylla ett år av saknade dagar tar en stund, så det bör fortsätta när du lämnar appen. I iOS 26 är
BGContinuedProcessingTask gjord för just det: iOS visar förloppet i Dynamic Island och på
låsskärmen. Tre saker vi lärde oss den hårda vägen, alla från en liten logg på enheten:
- Ingen GPU i bakgrunden. Vision och Core ML väljer själva GPU:n, misslyckas tyst när appen inte ligger i förgrunden, och varje bildruta ser tom ut. GPU-åtkomst kräver ett särskilt entitlement och hårdvara som stöds; iPhone 15 Pro rapporterade inget.
- Men allt kan inte falla tillbaka på CPU:n. Att tvinga Vision till CPU:n förstörde ansiktsdetekteringen: dess nätverk för ansiktskvalitet levereras utan CPU-vikter på iPhone (”Missing weights path … nonane”). Glimt ber nu om Neural Engine först, sedan CPU:n, och faller annars tillbaka på enkla ansiktsrutor.
- Förloppet måste röra sig. En dag med en lång video från iCloud stod still i en halv minut, och iOS avslutade uppgiften. Glimt rapporterar nu förlopp efter varje analyserad bildruta och varje nedladdningssteg, och ber Photos om en version av varje video i mellankvalitet för analysen (filmerna använder fortfarande originalet).
Integritet
Glimt har ingen nätverkskod. Ansikten, embeddings och val stannar i appens egen lagring på iPhonen. Det enda som kan röra nätverket är Photos självt, när det hämtar dina egna videor från din egen iCloud Photos om de inte finns på telefonen.
Tack till: SFace av Yaoyao Zhong med kollegor; ONNX-konvertering av Chengrui Wang; OpenCV Zoo (Apache 2.0). Frågor eller idéer: glimt@theill.com.