← Retour au blogue

Capturer le flou sur un smartphone

Les smartphones sont conçus pour embellir chaque photo. On appuie sur le déclencheur et une brigade d'algorithmes entre en action : réduction du bruit, accentuation des contours, stabilisation optique, fusion HDR. Pour la photographie du quotidien, c'est parfait. Pour une application de simulation argentique comme Apperture, c'est un vrai problème.

L'objectif

Apperture vous permet de prévisualiser le rendu d'une scène sur pellicule avant de déclencher. La pellicule est impitoyable : chaque pose coûte de l'argent, et on ne découvre le résultat qu'après le développement. Ce que les photographes argentiques débutants sous-estiment systématiquement, c'est la quantité de flou qu'une vitesse d'obturation lente produit. À 1/30s, une personne qui traverse le cadre peut être totalement méconnaissable. Apperture doit montrer cela avec précision, avant que le déclencheur ne clique. On pourrait aussi dire qu'on veut forcer les téléphones à prendre de mauvaises photos.

Le processeur d'image s'y oppose

Le processeur d'image (ISP) d'un smartphone est conçu pour faire disparaître le flou. Il interprète le flou de mouvement de la même façon qu'il interprète le tremblement de l'appareil : comme un défaut à corriger. Sur la plupart des appareils Android, l'API Camera2 permet de désactiver certaines étapes de traitement :

  • CONTROL_MODE_OFF pour désactiver l'exposition et la mise au point automatiques
  • EDGE_MODE_OFF pour désactiver l'accentuation des contours
  • NOISE_REDUCTION_MODE_OFF pour désactiver la réduction du bruit
  • LENS_OPTICAL_STABILIZATION_MODE_OFF pour désactiver la stabilisation optique

En théorie, cela devrait donner un signal brut du capteur. En pratique, sur des appareils comme le Pixel 8, la couche HAL de Google applique une accentuation et une stabilisation non documentées, sous la surface de l'API Camera2. Aucun paramètre logiciel n'atteint ces routines. Le flou disparaît quand même.

Simulation HP5 par smartphone Ilford HP5 sur pellicule

La photo de droite a été prise sur Ilford HP5 à ISO 400, f/8, 1 seconde. Celle de gauche est la tentative d'un smartphone de simuler la même pellicule aux mêmes réglages. On remarque que l'ISP a lissé le flou de mouvement que la pellicule a fidèlement capturé, produisant un résultat qu'aucun vrai appareil à cette vitesse d'obturation n'aurait jamais livré.

Sur iOS, le problème est différent. Le framework AVFoundation d'Apple n'expose pas les mêmes contrôles granulaires. Quand on capture via AVCapturePhotoOutput, la pile de photographie computationnelle d'Apple s'exécute automatiquement : Deep Fusion, Smart HDR, et tout ce que l'OS juge bon d'appliquer. Il n'existe aucun moyen de désactiver cela photo par photo. Réduire la priorité de qualité à .speed diminue la quantité de traitement, mais ne l'élimine pas. Les images résultantes restaient systématiquement plus nettes qu'un vrai appareil au même temps d'exposition ne l'aurait produit.

L'essai du RAW

L'étape suivante logique était la capture en RAW. Les fichiers DNG contiennent les données brutes du capteur, avant traitement par l'ISP. Sur Android on peut demander une capture RAW_SENSOR si l'appareil le supporte, puis traiter le DNG soi-même. Cela fonctionnait, mais posait deux problèmes : tous les appareils ne supportent pas la sortie RAW (le Motorola G Power, un Android d'entrée de gamme courant, ne le supporte pas), et les fichiers DNG pèsent entre 25 et 50 Mo chacun, nécessitant une étape de décodage supplémentaire avant que le pipeline LUT puisse les traiter.

Le RAW résolvait le problème de l'ISP, mais créait à sa place un problème de fragmentation matérielle.

L'accumulation de trames

La solution s'est avérée plus simple que prévu. Plutôt que d'affronter l'ISP sur une seule capture, on le contourne en empilant des trames.

Quand on appuie sur le déclencheur dans Apperture, la couche native commence à collecter des trames YUV depuis la sortie vidéo continue de l'appareil photo, pendant toute la durée de la vitesse d'obturation demandée. Chaque trame est capturée à la vitesse d'obturation rapide native du téléphone, ce qui ne laisse rien à corriger à l'ISP. On fusionne ensuite toutes ces trames en une moyenne glissante :

accumulé = accumulé x (n-1)/n + nouvelleTrame x 1/n

Un sujet en mouvement apparaît à une position légèrement différente dans chaque trame. Après la moyenne, ces positions se fondent les unes dans les autres en un flou temporel naturel. L'ISP ne voit jamais d'image floue, parce que nous assemblons le flou nous-mêmes, après coup, à partir de trames qu'il avait déjà approuvées.

Sur iOS, la clé a été d'abandonner complètement AVCapturePhotoOutput. En lisant les trames depuis AVCaptureVideoDataOutput à la place, elles contournent entièrement la pile de photographie computationnelle d'Apple. Les trames vidéo ne passent pas par Deep Fusion ni par Smart HDR. L'accumulation s'exécute sur le GPU via des shaders Metal : chaque trame entrante est fusionnée dans une texture accumulatrice en virgule flottante 32 bits, puis un passage de normalisation divise par le nombre de trames à l'expiration du temps d'obturation.

Cette approche fonctionne sur tous les appareils. Aucun support RAW requis.

Ce qu'on obtient

Le résultat est un flou de mouvement que les algorithmes du téléphone n'ont jamais touché. Les longues poses produisent des traînées lumineuses. Une main agitée devant l'objectif pendant une demi-seconde laisse une vraie trainée. Le flou varie avec la vitesse d'obturation exactement comme sur pellicule. Maintenant, si un photographe argentique utilisant Apperture tente de photographier une foule en basse lumière, il verra à quel point les visages seront flous, avant de commettre ce choix stylistique audacieux sur pellicule.

Ce n'est pas une simulation. C'est la vraie chose, assemblée à partir des indices que l'ISP cherchait à effacer.