Precisa de imagens anotadas para treinar sua IA no Python? Use o COCO Dataset!

Um guia simples de como usar o pycocotools para selecionar imagens específicas do conjunto de imagens COCO (Common Objects in Context).

João Guilherme Araujo
Porto
6 min readOct 23, 2020

--

Duas imagens lado a lado, uma é a imagem original e a outra é a mesma imagem só que os objetos são delimitados por polígonos
Imagem do COCO Dataset com e sem anotação obtida na seção exploratória das imagens

O que é o COCO Dataset?

O COCO (Common Objects in Context) é um dos maiores conjuntos de imagens anotadas e foi criado para obter o estado da arte dos algoritmos de Reconhecimento de Objetos. Sabe aquelas fotos que qualquer criança tira quando pega uma câmera fotográfica? Por exemplo, a foto de um sofá na sala ou de um carro no estacionamento. Esses são exemplos dos 80 objetos comuns em seus contextos naturais disponibilizados no COCO.

O COCO teve e tem um papel importante nos avanços dos algoritmos de Visão Computacional, pois, em grande parte, esses algoritmos são baseados em técnicas de Aprendizado Profundo (Deep Learning), que são técnicas “gulosas”, ou seja, quanto mais dados, melhor. Sendo um dos maiores conjuntos, normalmente o COCO é utilizado em publicações de novas técnicas.

Uma anotação pode ser feita de várias formas:

  • uma tag que classifica a imagem como um todo (gato, carro, …);
  • uma caixa delimitadora (normalmente quadrado) selecionando o objeto;
  • um polígono que segmenta a imagem detectando os objetos;
  • descrição escrita da imagem (“gato deitado no sofá”).
Três imagens exemplificando os tipos de anotações que podem ser feitas em imagens: classificação, caixa e polígono
Exemplos de anotação: classificação “gata” (esquerda), caixa delimitadora (centro) e segmentação (direita). Anotação feita no CVAT. Foto da minha gata, Betina.

O COCO tem mais de 200 mil imagens anotadas e só quem fez anotação de segmentação sabe da complexidade e do consumo de tempo que essa tarefa tem. Usar o COCO é um grande atalho, observe o vídeo abaixo para um exemplo de anotação de segmentação:

Exemplo de ferramenta de anotação de imagens do tipo segmentação. Nesse caso é a notação de somente uma imagem. Dá para ver que não é rápido.

O COCO ocupa mais de 25GB em espaço de disco divididos entre conjunto de treinamento, validação e teste. Contudo, nem sempre precisamos de todas as suas imagens para fazer um estudo.

Nos passos a seguir, vamos apresentar o pacote pycocotools, que vai te auxiliar na manipulação desse conjunto de imagens no Python.

Instalação do “pycocotools”

O pycocotools é um pacote do Python que funciona perfeitamente em qualquer distribuição Linux. Esse pacote é uma API para ler e manipular as anotações do conjunto de imagens COCO. Para instalar esse pacote é simples:

pip install pycocotools

Contudo, você pode enfrentar problemas em sistemas Windows. Para instalar o pycocotools no Windows sem problemas, use o seguinte código:

pip install pycocotools-windows

Antes de irmos para os códigos…

O pycocotools não manipula as imagens propriamente ditas, ele lê e manipula o arquivo de anotações do conjunto COCO. Dessa forma, você pode fazer filtros e selecionar somente imagens de interesse.

Por exemplo, você pode escolher imagens de carro em que esses carros representem pelo menos 50% da área da imagem.

Assim, nesse momento, não há necessidade de baixar o conjunto de imagens como um todo, basta acessar as anotações. Você pode baixar as anotações na página oficial do COCO ou usar esse link para baixar os 241MB das anotações da versão de 2014 do COCO.

Sem esse arquivo das anotações não conseguimos seguir. É só baixar e descompactar. No caso, o arquivo com as anotações é o “instances_val2014.json”.

Comandos do terminal Linux para baixar e descompactar:

wget -nv --show-progress http://images.cocodataset.org/annotations/annotations_trainval2014.zipunzip annotations_trainval2014.zip

Agora podemos ir para a prática

Nessa seção vamos passar pelos códigos mais importantes do pacote pycocotools que manipulam essas anotações, vamos dividir em seções para cada comando.

Carregar a classe principal com arquivo de anotações:

Vamos criar um objeto chamado coco (em minúsculo) que contém as anotações.

from pycocotools.coco import COCO
coco = COCO('./annotations/instances_val2014.json')

Obter os id’s de objetos específicos:

Vamos usar o método getCatIds para filtrar os objetos, nesse caso, vamos filtrar os id’s de carro, bicicleta, gato e cachorro. A saída é uma lista de id’s.

cat_ids  = coco.getCatIds(catNms=['car', 'bicycle', 'cat', 'dog'])
print(cat_ids)
# Output:
# [2, 3, 17, 18]

Obter os id’s das anotações dos objetos filtrados:

Agora que temos os id’s dos objetos de interesse, vamos filtrar todas as anotações desses objetos com o getAnnIds. O retorno é uma lista de id’s.

Lembrando que um objeto pode aparecer mais de uma vez em uma imagem.

ann_ids = coco.getAnnIds(catIds=cat_ids)
print(ann_ids)
# Output:
# [1727, 1728, 1767, 1768, 1769, 1773, 1774, 2144, 2251, ...]

Obter as anotações a partir dos id’s das anotações:

Os id’s são só indicadores, para obter a anotação completa do objeto temos que usar o método loadAnns.

all_ann = coco.loadAnns(ann_ids)
print(all_ann[:2]) # exibe as duas primeiras
# Output:
# [
# {
# "segmentation": [[374.46, 310.42, ..., 310.86]],
# "area": 2243.7513000000004,
# "iscrowd": 0,
# "image_id": 495357,
# "bbox": [337.02, 244.46, 66.47, 66.75],
# "category_id": 18,
# "id": 1727
# },
# {
# "segmentation": [
# [214.59, 205.04, 218.39, ..., 205.13],
# [247.96, 237.39, 246.89,..., 238.2]
# ],
# "area": 1698.440800000001,
# "iscrowd": 0,
# "image_id": 116061,
# "bbox": [213.81, 192.39, 53.94, 70.28],
# "category_id": 18,
# "id": 1728
# }
# ]

Perceba que a segunda anotação tem mais de uma segmentação. Isso acontece quando um objeto tem alguma informação sobreposta e ele é dividido, conforme a imagem a seguir:

Uma imagem de uma pessoa andando de bicicleta, onde a bicicleta é marcada com três polígonos, pois um só não seria suficiente
Imagem que contém bicicleta no COCO dataset, perceba que o polígono da bicicleta é dividido em três partes.

Definição dos valores:

  • segmentation: polígono que define o objeto [[x1,y1,x2,y2,…]];
  • area: área definida por esse polígono;
  • iscrowd: se objeto tem ou não outras informações sobrepostas;
  • image_id: id da imagem ao qual esse objeto pertence;
  • bbox: pontos que definem a caixa delimitadora do objeto;
  • category_id: id do objeto;
  • id: id da anotação.

Obter as informações das imagens de cada anotação:

Como uma imagem pode ter mais de uma anotação e, até o momento, nós só temos as informações das anotações, temos que usar o método loadImgs para obter as informações das imagens a partir do id da imagem vinculada à anotação.

# obtem as informações da imagem da primeira anotação (0)
img_example = coco.loadImgs(all_ann[0]["image_id"])

Exemplo de saída e chaves importantes:

{
'license': 1,
'file_name': 'COCO_val2014_000000495357.jpg',
'coco_url': 'http://images.cocodataset.org/val2014/COCO_val2014_000000495357.jpg',
'height': 479,
'width': 640,
'date_captured': '2013-11-15 12:47:11',
'flickr_url': 'http://farm5.staticflickr.com/4053/4432398298_d729bfc4e3_z.jpg',
'id': 495357
}

Definição dos valores:

  • file_name: nome do arquivo, importante para automatizar leitura;
  • coco_url: URL para baixar a imagem;
  • height e width: dimensões da imagem.

Converter as anotações em pandas.DataFrame:

import pandas as pd# Um loop em todas as anotaçõesdf_rows = []
for cur_ann in all_ann:
cbbox = cur_ann["bbox"]
area_obj = cur_ann["area"]
cimg_info = coco.loadImgs(cur_ann["image_id"])
filename = cimg_info[0]["file_name"]
cur_class = coco.loadCats(cur_ann['category_id'])[0]['name']
width, height = cimg_info[0]["width"], cimg_info[0]["height"]
area_img = width * height
df_rows = df_rows + [[filename, cur_class, area_img, area_obj]]# Transforma em um pandas.DataFrame
df = pd.DataFrame(df_rows, columns=["filename", "class", "area_img", "area_obj"])

Segue um exemplo da saída desse pandas.DataFrame:

Exemplo de saída do código mencionado anteriormente.

Exemplo de utilização prática

Quem usa o conjunto de imagens COCO sabe que muitos dos objetos podem ocupar áreas insignificantes da tela. A imagem a seguir é um exemplo disso:

Um controle. Um objeto quase que insignificante na cena.

Para filtrar somente imagens relevantes, podemos usar o pandas.DataFrame que construímos na seção anterior e selecionar somente as anotações de objetos que ocupam mais de 50% da imagem.

fltr = df['area_obj']/df['area_img'] > 0.50
df = df.iloc[fltr, :]

Como baixar e acessar essas imagens?

Usando a URL das informações da imagem ou baixando todo o conjunto de imagens COCO e acessando o nome do arquivo.

Fácil, né? O que achou?

Você pretende testar esse material?
Você já usou o COCO Dataset em algum estudo ou prática?
Já fez uso de algum outro conjunto de imagens? Qual?

Escreve a resposta lá nos comentários, vai ser um prazer interagir com vocês.

Agradecimentos especiais:

Renan Butkeraites (Colaboração, Revisão)
Adriano Moala (Revisão)
Catarina Pröglhöf (Revisão)
Fernanda Ribeiro (Revisão)
Comunicação Institucional Porto Seguro (Revisão)

--

--

João Guilherme Araujo
Porto
Writer for

Araujo here! I am a math/computer/statistics lover and I'm always looking for theory applications. In the free time, eventually, I'll put some stuff here.