Stupide raison pour laquelle votre application de vision industrielle astucieuse ne fonctionne pas: orientation dans EXIF

J'ai beaucoup écrit sur les projets de vision par ordinateur et d'apprentissage automatique, tels que les systèmes de reconnaissance d'objets et les projets de reconnaissance faciale . J'ai également une bibliothèque open source de reconnaissance faciale Python qui en fait en quelque sorte dans le top 10 des bibliothèques d'apprentissage automatique les plus populaires sur Github . Tout cela a conduit les nouveaux venus en Python et en vision industrielle à me poser beaucoup de questions.



Par expérience, il existe un problème technique spécifique qui embrouille le plus souvent les gens. Non, ce n'est pas une question théorique difficile ou un problème avec des GPU coûteux. Le fait est que presque tout le monde charge des images dans la mémoire en rotation, sans même le savoir. Et les ordinateurs ne détectent pas très bien les objets ou ne reconnaissent pas les visages dans les images tournées.

Comment les appareils photo numériques font pivoter automatiquement les images


Lorsque vous prenez une photo, l'appareil photo enregistre la position du téléphone, de sorte que dans un autre programme, l'image sera affichée dans le bon sens:



Mais l'appareil photo ne fait pas réellement pivoter les données de pixels à l'intérieur du fichier. Étant donné que les capteurs d'image des appareils photo numériques sont lus ligne par ligne comme un flux continu d'informations sur les pixels, il est plus facile pour l'appareil photo de toujours stocker les données de pixels dans le même ordre, quelle que soit la position réelle du téléphone.



C'est la préoccupation du programme pour la visualisation - faites pivoter correctement l'image avant de l'afficher sur l'écran. Avec les données de l'image elle-même, l'appareil photo enregistre également les métadonnées - paramètres de l'objectif, données de localisation et, bien sûr, l'angle de rotation de l'appareil photo. L'utilisateur doit utiliser ces informations pour s'afficher correctement.

Le format de métadonnées d'image le plus courant est appelé EXIF (abréviation de Exchangeable Image File Format). Les métadonnées EXIF ​​sont intégrées dans chaque fichier jpeg. Vous ne pouvez pas les voir à l'écran, mais ils sont lus par n'importe quel programme qui sait où chercher.

Voici les métadonnées EXIF ​​à l'intérieur de l'image JPEG de notre oie de l'outil exiftool :



Voir l'élément «Orientation»? Il dit au spectateur qu'avant d'afficher sur l'écran, l'image doit être tournée de 90 degrés dans le sens des aiguilles d'une montre. Si le programme oublie de le faire, l'image sera de son côté!



Pourquoi cela brise-t-il autant d'applications de vision industrielle en Python?


Les métadonnées EXIF ​​ne faisaient pas partie à l'origine du format JPEG. Ils ont été introduits beaucoup plus tard, empruntant l'idée au format TIFF. Pour des raisons de compatibilité descendante, ces métadonnées sont facultatives et certains programmes ne prennent pas la peine de les analyser.

La plupart des bibliothèques d'images Python, telles que numpy, scipy, TensorFlow, Keras, etc., se considèrent comme des outils scientifiques pour les personnes sérieuses qui travaillent avec des ensembles de données partagés. Ils ne se soucient pas des problèmes au niveau des consommateurs , tels que la rotation automatique de l'image, bien que cela soit nécessaire pour presque toutes les photographies du monde prises avec des appareils photo modernes.

Cela signifie que lors du traitement d'une image avec presque n'importe quelle bibliothèque Python, vous obtenez les données d'image d'origine sans rotation. Et devinez ce qui se passe lorsque vous essayez de télécharger une photo de votre côté ou à l'envers dans le modèle de détection de visage ou d'objet? Le détecteur ne se déclenche pas car vous lui avez donné de mauvaises données.

Vous pensez peut-être que les problèmes ne surviennent que dans les programmes pour débutants et étudiants, mais ce n'est pas le cas! Même la version de démonstration de l'API Vision phare de Google ne gère pas correctement l'orientation EXIF:


Démo Google Vision API ne sait pas comment faire pivoter une image orientée portrait prise à partir d'un téléphone mobile standard

Bien que Google Vision reconnaisse certains animaux de son côté, il les marque avec le label commun «animal», car les modèles de vision industrielle sont beaucoup plus difficiles à reconnaître une oie de son côté qu'une oie verticale. Voici le résultat, si vous faites pivoter l'image correctement avant de la soumettre au modèle:



Avec la bonne orientation, Google détecte les oiseaux avec une marque d'oie plus spécifique et un indicateur de confiance plus élevé. Bien mieux!

C'est un problème super évident lorsque vous voyez clairement que l'image est de son côté , comme dans cette démo. Mais c'est là que tout devient insidieux - généralement vous ne le voyez pas! Tous les programmes normaux de votre ordinateur afficheront l'image dans le bon sens, et non la façon dont elle est réellement stockée sur le disque. Par conséquent, lorsque vous essayez d'afficher une image pour voir pourquoi votre modèle ne fonctionne pas, elle s'affichera correctement et vous ne comprendrez pas pourquoi le modèle ne fonctionne pas!


Finder sur Mac affiche toujours les photos pivotées correctement depuis EXIF. Il n'y a aucun moyen de voir que l'image est effectivement stockée sur le côté

Cela conduit inévitablement à de nombreux tickets ouverts sur Github: les gens se plaignent que les projets open source sont cassés et que les modèles ne sont pas très précis. Mais le problème est beaucoup plus simple - ils alimentent juste des photos tournées ou inversées à l'entrée!

Correction


La solution est que chaque fois que vous chargez des images dans des programmes Python, vous devez vérifier les métadonnées d'orientation EXIF ​​et faire pivoter les images si nécessaire. C'est assez facile à faire, mais sur Internet, il est étonnamment difficile de trouver des exemples de code qui conviennent à toutes les orientations.

Voici le code pour charger n'importe quelle image dans un tableau numpy avec la bonne orientation:

import PIL.Image import PIL.ImageOps import numpy as np def exif_transpose(img): if not img: return img exif_orientation_tag = 274 # Check for EXIF data (only present on some files) if hasattr(img, "_getexif") and isinstance(img._getexif(), dict) and exif_orientation_tag in img._getexif(): exif_data = img._getexif() orientation = exif_data[exif_orientation_tag] # Handle EXIF Orientation if orientation == 1: # Normal image - nothing to do! pass elif orientation == 2: # Mirrored left to right img = img.transpose(PIL.Image.FLIP_LEFT_RIGHT) elif orientation == 3: # Rotated 180 degrees img = img.rotate(180) elif orientation == 4: # Mirrored top to bottom img = img.rotate(180).transpose(PIL.Image.FLIP_LEFT_RIGHT) elif orientation == 5: # Mirrored along top-left diagonal img = img.rotate(-90, expand=True).transpose(PIL.Image.FLIP_LEFT_RIGHT) elif orientation == 6: # Rotated 90 degrees img = img.rotate(-90, expand=True) elif orientation == 7: # Mirrored along top-right diagonal img = img.rotate(90, expand=True).transpose(PIL.Image.FLIP_LEFT_RIGHT) elif orientation == 8: # Rotated 270 degrees img = img.rotate(90, expand=True) return img def load_image_file(file, mode='RGB'): # Load the image with PIL img = PIL.Image.open(file) if hasattr(PIL.ImageOps, 'exif_transpose'): # Very recent versions of PIL can do exit transpose internally img = PIL.ImageOps.exif_transpose(img) else: # Otherwise, do the exif transpose ourselves img = exif_transpose(img) img = img.convert(mode) return np.array(img) 

À partir d'ici, vous pouvez transférer un tableau de données d'image vers n'importe quelle bibliothèque de vision industrielle Python standard qui attend un tableau d'entrée: par exemple, Keras ou TensorFlow.

Puisque le problème est omniprésent, j'ai publié cette fonction en tant que bibliothèque pip appelée image_to_numpy . Vous pouvez l'installer comme suit:

  pip3 installer image_to_numpy 

Il fonctionne avec n'importe quel programme Python, fixant le chargement d'image, par exemple:

 import matplotlib.pyplot as plt import image_to_numpy # Load your image file img = image_to_numpy.load_image_file("my_file.jpg") # Show it on the screen (or whatever you want to do) plt.imshow(img) plt.show() 

Voir le fichier readme pour plus de détails.

Profitez-en!

Source: https://habr.com/ru/post/fr471112/


All Articles