Longo demais; não vou ler: Este artigo questiona práticas correntes na anotação de imagens para visão de computador e propõe armazenar anotações como metadados da imagem, dispensando o arquivo de texto pareado.
Não sei se a prática teve origem na tentativa de contornar limitações tecnológicas dos primórdios. Trabalhando com visão de computador sempre achei um tanto desconcertante que seja preciso criar um arquivo de texto [sidecar file] – contendo as coordenadas das regiões de interesse e das caixas delimitadoras [bounding boxes] – para trabalhar em ‘tandem’ com o arquivo da imagem, quando o próprio arquivo de imagem possui um ambiente ideal, até mesmo Turing-completo [como o JPEG XL], para armazenar esses dados de uma forma estruturada. Isso parece muito ineficiente.
A proposta aqui é tentar simplificar o sistema de arquivos do dataset, eliminando os arquivos de texto secundários, e verificar se há algum ganho importante que justifique mudanças no atual paradigma do processo de treinamento, pelo menos para pequenos conjuntos de dados e tarefas de ajuste fino.
Os arquivos secundários, por definição, armazenam dados (geralmente metadados) que não são suportados pelo formato de um arquivo de origem. Obviamente, isso não é verdade com os arquivos de imagem digital modernos.
Procuro também entender os problemas técnicos e conceituais – e porque não dizer, éticos – relacionados à inserção/escrita/leitura de dados nessas estruturas/ambientes, bem como verificar se há algo a ganhar no processo de treinamento, pelo menos para pequenos datasets, datasets proprietários e/ou tarefas de ajuste fino.
Criar uma tag EXIF personalizada
De acordo com a Wikipedia [Inglês] “O padrão XMP foi projetado para ser extensível, permitindo aos usuários adicionar seus próprios tipos personalizados de metadados”.
Em um mundo perfeito, essa tag personalizada teria seu próprio tipo de dados. Para este exercício usaremos a vocação natural que as tags XMP têm para lidar com strings.
Uma imagem digital, além da informação ótica contida nos pixels, têm um amplo setor dedicado a armazenar informações gerais sobre outros detalhes. Esse setor é dividido em um grande número de campos de metadados, chamados ‘tags’, que armazenam informações específicas sobre o arquivo, como velocidade do obturador, exposição, condições ambientais, localização do dispositivo e inúmeros outros bits. Uma tabela, em outras palavras.
Há um grande número de tags XMP [link em inglês por falta de um em português – lusófonos, precisamos despertar para a tecnologia]. Os diversos dispositivos disponíveis no mercado adotam diversos formatos, sendo EXIF um entre eles.
Tags definidas pelo usuário
Precisamos então criar uma tag para conter nosso rótulo; nossa própria tag EXIF.
No mundo perfeito deste exercício, uma tag ‘Label’ para imagens estaria incluída na especificação XMP e populá-la seria responsabilidade das ferramentas de anotação.
As anotações são parte integrante da aprendizagem de máquina supervisionada. Em uma sessão de trabalho, a ferramenta de anotação normalmente cria, no momento em que um anotador seleciona a região da imagem que contém o item a ser rotulado, um arquivo de texto contendo as coordenadas da anotação da imagem, estruturado em um determinado formato – json, xml, CSV, etc.
São essas coordenadas que permitem ao sistema de AI sobrepor as “caixas delimitadoras” [‘bounding boxes’] – aqueles quadrados já familiares que delimitam os itens-alvo para detecção, como na imagem que ilustra este post. Esse arquivo de texto vive em um casamento indissolúvel com o arquivo de imagem e, para fins de visão de computador computacional, são sempre referenciados juntos.
Um argumento comum é que este esquema de separação imagem/texto permite maior flexibilidade nas anotações, atomização do dataset, etc. Mas contraponho o argumento de que nada é muito diferente quando todos são metadados.
Em uma etiqueta adequadadamente formatada, os dados de texto permanecem compartimentados e manipulá-los não será mais difícil do que manipular um arquivo de texto. Ainda é perfeitamente possível manter o conteúdo das tags sincronizado com arquivos de texto mantidos fora do dataset. O dataset não precisa mais de um sistema de arquivos [FileSystem]. Além disso, “grandes datasets de arquivos pareados têm custos consideráveis, bem como preocupações com a baixa qualidade” (Jia et al., 2021)[0].
Simplificar o dataset
Vamos então nos livrar do arquivo de texto e armazenar nossas anotações como uma tag EXIF do arquivo de imagem. Existem muitos módulos disponíveis em Python para esta tarefa, mas pouca diversidade. Muitos estão desatualizados. Uma pesquisa nos canais Anaconda (conda, conda-forge) e PyPi (pip) retorna módulos como pyexiv2; piexif e PyExifTool. Este último é um ‘wrapper’ Python para o ExifTool, que é uma aplicação escrita em Pearl. É meu preferido no momento. Não detalharei aqui as peculiaridades de cada um.
Com exiftool é possível executar manipulações avançadas em tags. Vamos usá-lo para criar uma nova tag chamada ‘Label’:
O processo envolve editar o arquivo exif.config contendo as tags que queremos definir, conforme estipulado na documentação do módulo:
%Image::ExifTool::UserDefined = (
# Todas as tags EXIF tags são adicionadas à tabela principal ‘Main table’
'Image::ExifTool::Exif::Main' => {
# Example 1. EXIF:NewEXIFTag
0xd000 => {
Name => 'Label',
Writable => 'int16u',
WriteGroup => 'IFD0',
},
# definir mais tags abaixo...
}
O espaço de tags EXIF é domínio dos fabricantes de hardware. Programas comuns de edição de imagens como Gimp, Photoshop e outros oferecem maneiras de acessar e editar tags EXIF em seus ambientes.
Intervenções programáticas via Python, C++ requerem abordagens mais técnicas e usuários experientes.
Alternativamente, para uma versão mais simples do experimento, podemos pular a construção de tags personalizadas e usar – após renomeá-las adequadamente – uma ou duas das tags predefinidas na especificação EXIF e disponíveis na maioria dos dispositivos. Exemplos dessas são as tags UserComments, MakerNotes, etc.
Neste experimento, a anotação do rótulo da imagem será serializada para uma tag personalizada chamada ‘Label’ [1] na tabela XMP/EXIF da imagem – assumindo que a tag tenha sido criada ou renomeada no exif.config
def writeToEXIFtag (dadosAnotados)
#pseudocódigo por enquanto
Imagem.Exif.Label = dadosAnotados
em vez do arquivo de texto emparelhado [json, xml, csv, etc.]
def writeToJSONFile(path, fileName, data):
fileName = fileName.split(".")[0]
filePathNameWExt = path + '/' + fileName + '.json'
with open(filePathNameWExt, 'w') as fp:
json.dump(data,fp)
como no processo usual.
Neste projeto, para maior praticidade [integração com outros módulos, etc.], o melhor caminho parece ser utilizar ambientes virtuais, como virtualenv e conda. Dificilmente é possível reunir exatamente os mesmos pacotes em ambas as plataformas. No momento estou utilizando ambientes que configurei com módulos que montei através da prática não muito limpa de misturar conda+pip. Ainda tenho coisas para descobrir – não tenho muita experiência com Pearl e estou tendo dificuldade em fazer com que tudo [exiftool + pyexiftool] funcione junto.
A favor
- Processamento mais eficiente [a verificar].
- Os arquivos de imagem do conjunto de dados podem ser renomeados e usados em qualquer outro dataset sem trabalho adicional.
- Sem problemas com formatos diferentes. Esses Xlabels [rótulos EXIF] podem coexistir com os arquivos de anotação pareados.
- A simplicidade traz ganhos pedagógicos; uma curva de aprendizagem [humana] menos acentuada.
- Câmeras podem pré-anotar imagens automaticamente – pelo menos categorias universais, como COCO [isso é um ‘Pró’?].
Contra
- Esse esquema reduz em muito a flexibilidade dos dados [a verificar]
- Aumento do tamanho do conjunto de dados [a verificar]
- Menos controle sobre conjuntos de dados e anotações [a verificar]
- Os problemas habituais da economia de vigilância [câmeras detectando, identificando, classificando…]
Epílogo
Não tenho conhecimento de ideias semelhantes e gostaria de saber se existem. Eu também gostaria de saber se na verdade estou chegando atrasado a uma solução já rejeitada. Ainda estou nos estágios iniciais e receber feedback é parte fundamental do processo.
Estarei relatando os progressos [ou falta de]. Tenho o esqueleto do repositório no GitHub[2], e vou estar lapidando e finalizando a versão inicial nos próximos dias. É um projeto modesto – praticamente todo o README está neste post, porque a ideia é muito simples, como eu creio que todos podem ver.
[0] Scaling Up Visual and Vision-Language Representation Learning With Noisy Text Supervision https://arxiv.org/pdf/2102.05918.pdf
[1] A questão de se criar novas tags, ou renomear alguma existente, [ex. UserComments → Label], ou ambos, ou ainda outra opção com outro tipo de dados, está aberta, assim como a questão de se usar tags simples ou combinadas – por exemplo, para atomizar as coordenadas das caixas delimitadoras e outras informações
[2] https://github.com/VoxLeone/XLabel
APÊNDICE
Para criar uma tag personalizada usando o pyexiftool
, você precisa ter a biblioteca pyexiftool
instalada em seu ambiente Python. Como mencionado no artigo, pyexiftool
é um ‘wrapper’ Python para exiftool
, que é uma aplicação escrita nativamente em Pearl. Aqui está um exemplo de função que cria uma tag personalizada em uma imagem usando o pyexiftool
:
import exiftool
def create_custom_tag(image_path, tag_name, tag_value):
with exiftool.ExifTool() as et:
et.execute(f'-{tag_name}={tag_value}', image_path)
# Exemplo de uso:
image_path = "caminho/para/imagem.jpg"
tag_name = "XMP:CustomTag"
tag_value = "Valor da tag personalizada"
create_custom_tag(image_path, tag_name, tag_value)
Substitua ‘caminho/para/imagem.jpg
‘ pelo caminho real para a imagem em que você deseja criar a tag. Defina tag_name
como o nome desejado para sua tag e tag_value
como o valor que você deseja atribuir a ela.
Esta função vai então usar pyexiftool
para executar a ferramenta exiftool
em seu sistema e definir a tag personalizada na imagem especificada.
É preciso ter o exiftool
instalado em seu sistema para que esta biblioteca funcione corretamente.