Para preparar uma base de dados de imagens para treinar uma rede neural no reconhecimento de objectos, é necessário reconhecer as imagens da base de dados. Isto significa atribuir-lhes uma etiqueta e uma zona de reconhecimento.
Este tutorial dá seguimento ao artigo sobre a criação de um banco de imagens.
Objetivo da preparação
O objetivo é criar conjuntos de dados que facilitem o treino com as ferramentas TensorFlow, Yolo ou Keras.
Há duas formas de o fazer:
- Utilizar labelImg
- Criar uma arquitetura de pastas e utilizar um script (formação apenas com Keras)
Preparar um banco de imagens com labelImg
Pode descarregar e instalar o labelImg
- Linux
python3 -m pip install labelImg labelImg
- Janelas
Siga as instruções de compilação no github. Também pode encontrar um executável labelImg.exe
Adicionar uma caixa e uma etiqueta
Inicie o labelImg e seleccione a pasta utilizando o botão “Open Dir”.
Para cada imagem, rodeará o objeto a detetar e atribuir-lhe-á um nome (etiqueta) utilizando o botão “Criar RectBox”.
Nota: evite ultrapassar a imagem ao desenhar a caixa. Isso pode causar problemas durante o treino.
Converter para o formato PascalVOC
Converter para o formato YOLO
Nota: Pode guardar os dois formatos em simultâneo ou guardar em VOC e converter para YOLO utilizando o script convert_voc_to_yolo.py.
Preparar um banco de imagens com uma arquitetura de pastas
A ideia é colocar as imagens em subpastas com o nome da classe. Para o treino, o banco de imagens deve conter entre 1 e 3 pastas: treino, teste, validação (as pastas de teste e validação são opcionais porque podem ser criadas a partir da primeira pasta).
N.B.: este método requer um único objeto por imagem
- images
- train
- gatos
- cães
- validation
- gatos
- cães
- train
Para criar os ficheiros que contêm o nome e as informações da zona de deteção a partir das pastas de imagens, pode utilizar o script gerar_arquivos_voc.py:
- os caminhos de acesso às várias pastas (pastas[‘train’
Os nomes das classes serão definidos pelos nomes das pastas e a zona de deteção pelo tamanho da imagem.
gerar_arquivos_voc.py
import glob import os import pickle import cv2 import xml.etree.ElementTree as ET import xml.dom.minidom from os import listdir, getcwd from os.path import join dirs = ['train', 'test'] classes = ['mug'] def getImagesInDir(dir_path): image_list = [] for filename in glob.glob(dir_path + '/**/*.png', recursive=True): image_list.append(filename) return image_list def generate_voc(image_path): #get image data dirname=os.path.dirname(image_path) foldername=dirname.split('\\')[-1] basename = os.path.basename(image_path) basename_no_ext = os.path.splitext(basename)[0] im = cv2.imread(image_path) h,w,c=im.shape root = ET.Element('annotation') folder = ET.SubElement(root, 'folder') folder.text=foldername filename = ET.SubElement(root, 'filename') filename.text=basename path = ET.SubElement(root, 'path') path.text=image_path source = ET.SubElement(root, 'source') database = ET.SubElement(source, 'database') database.text = 'Unknown' size = ET.SubElement(root, 'size') width = ET.SubElement(size, 'width') width.text='{}'.format(w) height = ET.SubElement(size, 'height') height.text='{}'.format(h) depth = ET.SubElement(size, 'depth') depth.text='{}'.format(c) segmented = ET.SubElement(root, 'segmented') segmented.text = '0' objec = ET.SubElement(root, 'object') name = ET.SubElement(objec, 'name') name.text=foldername pose = ET.SubElement(objec, 'pose') pose.text='Unspecified' truncated = ET.SubElement(objec, 'truncated') truncated.text='0' difficult = ET.SubElement(objec, 'difficult') difficult.text='0' bndbox = ET.SubElement(objec, 'bndbox') xmin = ET.SubElement(bndbox, 'xmin') xmin.text='{}'.format(0+5) ymin = ET.SubElement(bndbox, 'ymin') ymin.text='{}'.format(0+5) xmax = ET.SubElement(bndbox, 'xmax') xmax.text='{}'.format(w-5) ymax = ET.SubElement(bndbox, 'ymax') ymax.text='{}'.format(h-5) tree = ET.ElementTree(root) outxml=join(dirname,basename_no_ext+'.xml') tree.write(outxml) return outxml def convert(size, box): dw = 1./(size[0]) dh = 1./(size[1]) x = (box[0] + box[1])/2.0 - 1 y = (box[2] + box[3])/2.0 - 1 w = box[1] - box[0] h = box[3] - box[2] x = x*dw w = w*dw y = y*dh h = h*dh return (x,y,w,h) def convert_annotation(in_file): dirname=os.path.dirname(in_file) basename = os.path.basename(in_file) basename_no_ext = os.path.splitext(basename)[0] out_file = open(join(dirname, basename_no_ext + '.txt'), 'w') tree = ET.parse(in_file) root = tree.getroot() size = root.find('size') w = int(size.find('width').text) h = int(size.find('height').text) for obj in root.iter('object'): difficult = obj.find('difficult').text cls = obj.find('name').text if cls not in classes or int(difficult)==1: continue cls_id = classes.index(cls) xmlbox = obj.find('bndbox') b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text)) bb = convert((w,h), b) out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n') cwd = getcwd() for dir_path in dirs: full_dir_path = join(cwd,dir_path) image_paths = getImagesInDir(full_dir_path) for image_path in image_paths: xml_path=generate_voc(image_path) #generate voc file convert_annotation(xml_path) #convert to yolo file print("Finished processing: " + dir_path)
Este método produz rapidamente uma base de dados que pode ser utilizada para a formação (TensorFlow e Yolo), mesmo que a zona de reconhecimento seja aproximada.
N.B.: Uma vez criados os ficheiros XML e TXT, pode abrir o lableImg para aperfeiçoar as etiquetas e a zona de deteção.