Glimt gør ét sekund fra hver dag til en film. Det interessante er, at du ikke behøver at filme det sekund: Glimt finder det i de videoer, du allerede har taget, og vælger det sekund, hvor dit barn er med i billedet – også i en fyldt fødselsdagsvideo med fem andre børn. Det hele kører på iPhonen: ingen server, ingen konto, ingen upload. Her er, hvordan det virker, med modellerne, matematikken og rigtige målinger.
Pipelinen
Hvorfor ikke bare bruge Apples Vision?
Vision klarer meget her: det finder ansigter (VNDetectFaceCaptureQualityRequest, som også vurderer, hvor skarpt
og hvor forfra hvert ansigt er), deres landmarks (VNDetectFaceLandmarksRequest) og, på iOS 18, hvor godt et billede
ser ud (CalculateImageAestheticsScoresRequest, som også fanger kvitteringer og skærmbilleder). Hvad det ikke
tilbyder, er hvem et ansigt er. People-albummet i Photos ved det, men det indeks er ikke åbent for apps.
Den første version af Glimt brugte VNGenerateImageFeaturePrintRequest på et udsnit af ansigtet. Det er et
generelt billedfingeraftryk: det sammenligner hår, lys, farver og baggrund lige så meget som selve ansigtet. I praksis
matchede det glad og gerne søskende og ethvert barn, der var fotograferet i samme rum på samme tid af dagen. Det var det forkerte værktøj.
Ansigtsgenkendelsesmodellen
Nu har Glimt sin egen med: SFace (Zhong et al., 2021, »Sigmoid-Constrained Hypersphere Loss for Robust Face Recognition«), en MobileFaceNet-backbone, udgivet i OpenCV Zoo under licensen Apache 2.0. Den opnår 99,40 % på LFW. Den tager et ansigt på 112 × 112 og returnerer 128 tal, en embedding; to fotos af samme person giver embeddings, der peger i samme retning.
Konverteringen gik ONNX → PyTorch (onnx2torch) → Core ML (coremltools, ML Program, float16),
efterfulgt af lineær 8-bit-kvantisering af vægtene. Resultatet fylder 9,8 MB, indlæses på ca. 0,5 s første gang, og dets output
matcher den oprindelige ONNX-model med en cosinus på 0,998 på et testinput. Normaliseringen af input ligger inde i grafen, så
appen giver den helt almindelige RGB-pixels fra 0–255.
Justering betyder mere, end man skulle tro
SFace er trænet på ansigter, der er justeret til en fast skabelon: øjne, næsetip og mundvige på kendte steder i kvadratet på 112 × 112. Et ansigt, der hælder eller sidder skævt, giver en mærkbart dårligere embedding. Visions landmarks giver de fem punkter (pupillerne, midten af næseområdet, læbernes yderste punkter); Glimt finder så den rotation, ensartede skalering og forskydning, der bedst flytter dem over på skabelonen. I to dimensioner har dette mindste kvadraters-problem en lukket løsning, hvis man behandler punkterne som komplekse 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 udfører den (én affin transformation, software-renderer, et output på 112 pixels), og udsnittet går videre til modellen. Referencefotos får lavet embedding to gange, som de er og spejlvendt, og de to midles: en lidt mere stabil reference helt gratis.
Er det det samme barn? Kalibrering af tærsklen
Appen sammenligner hvert ansigts embedding med referencefotoene via cosinus-lighed og beholder det bedste match. Hvor grænsen skulle ligge, blev målt, ikke gættet, på portrætter fra Det Hvide Hus i public domain (officielle portrætter med flere års mellemrum og et familieportræt med begge forældre og to døtre):
Den samme mand på portrætter taget med fire års mellemrum får 0,83; de samme personer fundet på familiefotoet 0,70–0,85. Fremmede holder sig under 0,25. Det svære tilfælde er familie: to søstre i samme billede får 0,43 mod hinanden, mor og døtre op til 0,41 (samme lys og positur får slægtninge til at ligne hinanden mere), mens mor mod sine døtre på et andet foto falder til 0,26–0,30. Så grænsen ligger på 0,45: over søstrene, et godt stykke under samme person. Fem fotos er et fornuftstjek, ikke en evaluering, og grænsen bliver finjusteret på rigtige familievideoer.
To regler mere holder dobbeltgængere ude: i en video er ét heldigt billede ikke nok (det kræver to billeder på 0,45 eller ét klart over på 0,57), og når der er flere ansigter i et billede, tæller kun det, der matcher bedst.
Flere referencefotos
Ét referencefoto er én vinkel, ét lys og én alder. Børn forandrer sig hurtigt. I Glimt kan du tilføje op til fem fotos, og ansigtet i et billede skal kun ligne ét af dem, så et profilbillede eller ansigtet fra sidste vinter stadig kan genkendes. Når der er flere personer på et foto, trykker du på det rigtige ansigt; Glimt gætter aldrig på »det største«.
At vælge sekundet og finde udsnittet
Hvert billede får en score: 35 % for hvor godt det ser ud, 20 % for skarphed (variansen af en Laplacian på en grå kopi på 96 × 96: slørede billeder ændrer sig langsomt fra pixel til pixel), 45 % for om der er et tydeligt ansigt. Med referencefotos tæller hele den score 40 % og »er det barnet« 60 %: et helt almindeligt billede af dit barn slår et smukt billede af en anden. Det valgte sekund er vinduet med det bedste gennemsnit, så ét godt billede ved siden af et sløret ikke vinder.
Udsnittet til en film i portrætformat havde sin egen fejl, som er værd at dele. Hvert billede pegede på det tydeligste ansigt, og appen tog gennemsnittet af positionerne over sekundet. Med to børn skiftede det tydeligste ansigt mellem dem, og gennemsnittet landede på væggen midt imellem. Nu peger hvert billede på barnet (eller det mest fremtrædende ansigt: størrelse × kvalitet, som er mere stabilt), og appen tager den største gruppe af positioner, der ligger tæt sammen, ikke middelværdien. Når ansigtet er lille, zoomer udsnittet ind, til det fylder omkring en femtedel af billedets højde, højst 1,6× for video.
Hvor hurtigt er det?
Overraskelsen: at genkende et ansigt er den billige del, ca. 2 ms på Neural Engine. At afkode videobilledet koster femten gange mere. I praksis kommer Glimt igennem en travl dag med videoer på få sekunder og en måned på få minutter.
Kørsel i baggrunden
Det tager et stykke tid at udfylde et år med manglende dage, så arbejdet skal fortsætte, når du forlader appen. iOS 26's
BGContinuedProcessingTask er lavet til netop det: iOS viser fremskridtet i Dynamic Island og på
låseskærmen. Tre ting, vi lærte på den hårde måde, alle fra en lille log på enheden:
- Ingen GPU i baggrunden. Vision og Core ML vælger selv GPU'en, fejler stille, når appen ikke er i forgrunden, og hvert billede ser tomt ud. GPU-adgang kræver et særligt entitlement og understøttet hardware; iPhone 15 Pro meldte ingen.
- Men ikke alt kan falde tilbage til CPU'en. At tvinge Vision over på CPU'en ødelagde ansigtsdetektionen: dens netværk til ansigtskvalitet leveres uden CPU-vægte på iPhonen (»Missing weights path … nonane«). Glimt beder nu om Neural Engine først, så CPU'en, og falder ellers tilbage til almindelige ansigtsbokse.
- Fremskridtet skal blive ved med at bevæge sig. En dag med en lang video fra iCloud stod stille i et halvt minut, og iOS afsluttede opgaven. Glimt melder nu fremskridt efter hvert analyseret billede og hvert download-trin og beder Photos om en version i mellemkvalitet af hver video til analysen (filmene bruger stadig originalen).
Privatliv
Glimt har ingen netværkskode. Ansigter, embeddings og valg bliver i appens eget lager på iPhonen. Det eneste, der kan røre netværket, er Photos selv, når den henter dine egne videoer fra dit eget iCloud Photos, hvis de ikke ligger på telefonen.
Tak til: SFace af Yaoyao Zhong og kolleger; ONNX-konvertering af Chengrui Wang; OpenCV Zoo (Apache 2.0). Spørgsmål eller idéer: glimt@theill.com.