TP4 - Photographie algorithmique

Ce travail vise la génération de mosaiques (ou panoramas) à partir d'une série d'images. Pour ce faire, des points d'intérêt sont détectés sur chacune des images et regroupés en paires. Une transformation homographique (ou projective) est calculée à partir de quatre paires et est appliquée à l'une des images.

Ce laboratoire a été réalisé avec Python 3.5.1, Numpy 1.10.4, Scipy 0.17.0 et Scikit-Image 0.12.3. Les classes et fonctions créées dans ce laboratoire ont été fait en concordance avec celles de Scikit-Image de façon à pouvoir facilement substituer des composantes de ce travail par celles présentent dans les libraires utilisées.

Partie 0: Réchauffement

Avant toute chose il est nécessaire de programmer les fonctions permettant d'appliquer une transformation.

Appliquer une homographie en tant que tel est simple. Il suffit de faire une multiplication de matrice. La difficulté vient du fait que numpy représente les images avec des coordonnées matricielles alors que scikit-image représente les transformations en coordonnées cartésiennes pour être en accord avec la litérature scientifique. Il faut donc faire attention pour ne pas mélanger les deux systèmes. \[ \begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} x \\ y \end{bmatrix} \begin{bmatrix} a & b & c \\ d & e & f \\ g & h & i \end{bmatrix} \]

Une autre difficulté vient du fait que de transformer une image, l'a fait généralement sortir de son cadre. Il faut donc précalculer la taille finale du résultat en transformant les quatre coins de l'image. Finalement, dans sa forme actuelle, la déformation de l'image se fait en ittérant sur chaque pixel. Les boucles Python étant très lente, la routine de déformation est des centaines de fois plus lente que celle de Scikit-Image programmée en C.

Partie 1: Appariement manuel

L'objectif ici est de déformer une image vers une autre à partir de correspondances déjà établies.

La première étape est de calculer l'homographie amenant le jeu de points source vers le jeu de points de points de destination. Le problème consiste en majorité à trouver les vecteurs propres de la matrice: \[ \begin{bmatrix} x_1 & y_1 & 1 & 0 & 0 & 0 & -x_1*X_1 & -y_1*X_1 & X_1 \\ 0 & 0 & 0 & x_1 & y_1 & 1 & -x_1*Y_1 & -y_1*Y_1 & Y_1 \\ \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots \\ x_n & y_n & n & 0 & 0 & 0 & -x_n*X_n & -y_n*X_n & X_n \\ 0 & 0 & 0 & x_n & y_n & n & -x_n*Y_n & -y_n*Y_n & Y_n \end{bmatrix} \] Un minimum de quatre paires de points est nécessaire, mais idéalement davantages peuvent être utilisées.

Une fois la transformation calculée, il suffit de l'appliquer sur l'image source et de répéter le processus pour chaque paire d'image de la mosaique. Contrairement à ce qui est spécifié dans l'énoncé du laboratoire, un meilleur résultat a été obtenue non pas en faisant la moyenne des images là où elles se chevauchent, mais en ne conservant que l'une des deux à cet endroit. Le résultat est ainsi plus net.

Pour les images présentées, la première série a été obtenue à partir des points fournis. Les deux autres images ont été générées à partir de points provenant d'un détecteur ORB et de la procédure décrire à la partie 2. Il était en effet plus simple d'attendre le code de la partie 2 plutôt que de sélectionner manuellement des points.

Partie 2: Appariement automatique

Pour obtenir des bons descripteurs, ceux-ci sont calculés sur des endroits où les gradients sont très forts, soient des coins. Pour cela le détecteur Harris venant avec Scikit-Image est utilisé sur l'image source et cible. Les points très proches les uns des autres sont ensuite supprimés.

Pour que ces points soient associables entre les images, il faut les identifier avec un descripteur, dans ce cas "Multi-Image Matching using Multi-Scale Oriented Patches". Celui-ci est très simple. Il suffit d'échantillonner une fenêtre de 8x8 sur l'image flouée autour du coin avec un espace entre chacun des points. Par la suite, il faut normaliser chacun de ces vecteurs pour les rendre invariants aux transformations affines en intensité. Finalement, le papier suggère d'appliquer une transformation en ondelette sur le vecteur de descripteur. Cette dernière étape a été implémentée, mais n'affecte pas énormément la performance en précision du descripteur. Elle pourrait par contre être utile pour rechercher plus rapidement les appariements.

La prochaine étape est d'apparier les descripteurs. Pour cela, une approche force brute a été programmée. La somme des différences au carré est prises entre chacun des descripteurs des deux images et la paire choisie correspond à celle ayant la plus faible erreur. Pour rendre le résultat encore plus robuste, la fonction prend en paramètre un seuil de maximum de SDC. Un appariement ayant une erreur supérieur à ce seuil est ignoré.

Beaucoup de résultats erronés sont générés par l'étape précédante, il est donc nécessaire de d'abord filtrer les résultats. Pour cela, l'algorithme RANSAC est utilisé. Les points ainsi filtrés peuvent être traités comme expliqué à la partie 1.

Vous pourrez voir que certains résultats possèdent des erreurs. Cela est du au fait que lorsque la banque d'image est grande, l'algorithme ne fusionne pas les photos dans le bon ordre et calcule ainsi des transformations inadéquates.

La première image représente un exemple des appariements détectés automatiquement.

Partie 3: Vos images

Dans cette section, l'algorithme est testé sur mes propres images. Pour obtenir le meilleur résultat, le téléphone ayant pris les photos a été mis en mode manuel avec un iso, temps d'obturation et balance des blancs constants entre les prises. Cela permet de réduire les changements spontanés dans la mosaique.

La première image représente un exemple des appariements détectés automatiquement.

Crédits supplémentaires

Détection de panorama

Une détection simple de panorama a été programmée. Celle-ci prend une liste désordonnée de photos faisant partie de la mosaique et détermine comment les placer. L'algorithme est celui-ci:

  1. Retirer une image de la liste d'image
  2. Calculer la transformation de cette image vers toutes les autres
  3. Retirer de la liste l'image de destination ayant la transformation la plus faible (celle qui s'éloigne le moins de la matrice identité)
  4. Appliquer la transformation entre ces images
  5. Insérer le résultat dans la liste d'image
  6. Faire une récurrence

L'algorithme fonctionne, mais possède plusieurs défauts.



Jérôme de Courval