Razão estúpida por que seu aplicativo de visão de máquina astuto não funciona: orientação em EXIF

Escrevi muito sobre projetos de visão computacional e aprendizado de máquina, como sistemas de reconhecimento de objetos e projetos de reconhecimento de faces . Eu também tenho uma biblioteca de reconhecimento facial de código-fonte Python que de alguma forma chegou às 10 bibliotecas de aprendizado de máquina mais populares no Github . Tudo isso levou os recém-chegados ao Python e à visão de máquina a me fazerem muitas perguntas.



Por experiência, há um problema técnico específico que muitas vezes confunde as pessoas. Não, essa não é uma questão teórica difícil ou um problema com GPUs caras. O fato é que quase todo mundo carrega imagens na memória rotacionada, mesmo sem saber. E os computadores não detectam objetos muito bem ou reconhecem rostos em imagens rotacionadas.

Como as câmeras digitais giram imagens automaticamente


Quando você tira uma foto, a câmera registra a posição do telefone, para que em outro programa a imagem seja exibida na orientação correta:



Mas a câmera não gira os dados de pixel dentro do arquivo. Como os sensores de imagem nas câmeras digitais são lidos linha a linha como um fluxo contínuo de informações em pixels, é mais fácil para a câmera armazenar sempre os dados em pixels na mesma ordem, independentemente da posição real do telefone.



Essa é a preocupação do programa para visualizar - gire a imagem corretamente antes de exibi-la na tela. Juntamente com os dados da própria imagem, a câmera também salva metadados - configurações da lente, dados de localização e, é claro, o ângulo de rotação da câmera. O visualizador deve usar essas informações para exibir corretamente.

O formato mais comum de metadados de imagem é chamado EXIF (abreviação de Exchangeable Image File Format). Os metadados EXIF ​​são incorporados em todos os arquivos jpeg. Você não pode vê-los na tela, mas eles são lidos por qualquer programa que sabe onde procurar.

Aqui estão os metadados EXIF ​​dentro da imagem JPEG do nosso ganso da ferramenta exiftool :



Veja o elemento 'Orientação'? Ele diz ao espectador que, antes de exibir na tela, a imagem deve ser girada 90 graus no sentido horário. Se o programa se esquecer de fazer isso, a imagem estará do seu lado!



Por que isso quebra tantos aplicativos de visão de máquina em Python?


Os metadados EXIF ​​não faziam parte originalmente do formato JPEG. Eles foram introduzidos muito mais tarde, emprestando a idéia do formato TIFF. Para compatibilidade com versões anteriores, esses metadados são opcionais e alguns programas não se preocupam em analisá-los.

A maioria das bibliotecas de imagens Python, como numpy, scipy, TensorFlow, Keras, etc., se consideram ferramentas científicas para pessoas sérias que trabalham com conjuntos de dados compartilhados. Eles não se importam com questões no nível do consumidor , como a rotação automática da imagem, embora isso seja necessário para quase todas as fotografias do mundo tiradas com câmeras modernas.

Isso significa que, ao processar uma imagem com quase qualquer biblioteca Python, você obtém os dados da imagem original sem rotação. E adivinhe o que acontece quando você tenta fazer upload de uma foto do seu lado ou de cabeça para baixo no modelo de detecção de rosto ou objeto? O detector não dispara porque você forneceu dados incorretos.

Você pode pensar que os problemas surgem apenas nos programas de iniciantes e estudantes, mas não é assim! Mesmo a versão demo da API principal da Google, Vision, não trata a orientação EXIF ​​corretamente:


Demonstração A API do Google Vision não sabe como girar imagens orientadas a retrato tiradas de um telefone celular padrão

Embora o Google Vision reconheça alguns animais ao seu lado, ele os marca com o rótulo comum "animal", porque os modelos de visão por computador são muito mais difíceis de reconhecer um ganso do que um ganso vertical. Aqui está o resultado, se você girar a imagem corretamente antes de enviá-la ao modelo:



Com a orientação correta, o Google detecta pássaros com uma marca de ganso mais específica e um indicador de confiança mais alto. Muito melhor!

Esse é um problema super óbvio quando você vê claramente que a imagem está do lado , como nesta demonstração. Mas é aqui que tudo se torna insidioso - geralmente você não vê! Todos os programas normais do seu computador exibirão a imagem na orientação correta, e não como ela é realmente armazenada no disco. Portanto, quando você tenta visualizar uma imagem para ver por que seu modelo não está funcionando, ela será exibida corretamente e você não entenderá por que o modelo não está funcionando!


O Finder no Mac sempre exibe fotos giradas corretamente a partir do EXIF. Não há como ver se a imagem está realmente armazenada de lado

Isso inevitavelmente leva a muitos tickets abertos no Github: as pessoas reclamam que os projetos de código aberto estão com problemas e os modelos não são muito precisos. Mas o problema é muito mais simples - eles apenas colocam fotos giradas ou invertidas na entrada!

Correção


A solução é que, sempre que você carregar imagens em programas Python, verifique os metadados da orientação EXIF ​​e gire as imagens, se necessário. É muito fácil de fazer, mas na Internet é surpreendentemente difícil encontrar exemplos de código que sejam adequados para todas as orientações.

Aqui está o código para carregar qualquer imagem em uma matriz numpy com a orientação correta:

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) 

A partir daqui, você pode transferir uma matriz de dados de imagem para qualquer biblioteca padrão de visão de máquina Python que espera uma matriz de entrada: por exemplo, Keras ou TensorFlow.

Como o problema é onipresente, publiquei essa função como uma biblioteca de pips chamada image_to_numpy . Você pode instalá-lo da seguinte maneira:

  instalação do pip3 image_to_numpy 

Funciona com qualquer programa Python, corrigindo o carregamento de imagens, por exemplo:

 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() 

Veja o arquivo leia - me para mais detalhes.

Aproveite!

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


All Articles