Это пятый день моего участия в августовском испытании обновлений, подробности о мероприятии:Испытание августовского обновления
окрестности
- виндовс 10 64 бит
- python 3.7
- labelImg
Аннотация к изображению
Здесь используются инструменты с открытым исходным кодомlabelImgАннотируйте изображение, и формат экспортируемого набора данныхPASCAL VOC
, после завершения маркировки данных вы можете видеть, что папка выглядит так, маркируйте файлxml
смешанный с файлами изображений
Самодельный набор данных VOC
Во-первых, согласноVOC2007
Требования к формату набора данных, создавать папки отдельноVOCdevkit
,VOC2007
,Annotations
,ImageSets
,Main
иJPEGImages
, их иерархия следующая
└─VOCdevkit
└─VOC2007
├─Annotations
├─ImageSets
│ └─Main
└─JPEGImages
в,Annotations
используется для храненияxml
файл аннотации,JPEGImages
используется для хранения файлов изображений иImageSets/Main
хранить несколькоtxt
Текстовый файл.Содержимое файла - это имена изображений в тренировочном наборе, проверочном наборе и тестовом наборе (уберите расширение).Эти текстовые файлы необходимо сгенерировать самим, о чем мы поговорим позже.
Далее будетimages
Скопируйте файлы изображений из папки вJPEGImages
папка,images
в файлеxml
Скопируйте файл аннотации вAnnotations
в папке
Затем создайте новый скрипт и поместите его вVOCdevkit/VOC2007
папка, названнаяtest.py
─VOCdevkit
└─VOC2007
│ test.py
│
├─Annotations
├─ImageSets
│ └─Main
└─JPEGImages
Содержание скрипта следующее
import os
import random
# 训练集和验证集的比例分配
trainval_percent = 0.1
train_percent = 0.9
# 标注文件的路径
xmlfilepath = 'Annotations'
# 生成的txt文件存放路径
txtsavepath = 'ImageSets\Main'
total_xml = os.listdir(xmlfilepath)
num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)
ftrainval = open('ImageSets/Main/trainval.txt', 'w')
ftest = open('ImageSets/Main/test.txt', 'w')
ftrain = open('ImageSets/Main/train.txt', 'w')
fval = open('ImageSets/Main/val.txt', 'w')
for i in list:
name = total_xml[i][:-4] + '\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftest.write(name)
else:
fval.write(name)
else:
ftrain.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest.close()
Затем перейдите в каталогVOCdevkit/VOC2007
, выполнить скрипт, после завершения, вImageSets/Main
сгенерировано 4txt
документ
├─ImageSets
│ └─Main
│ test.txt
│ train.txt
│ trainval.txt
│ val.txt
│
└─JPEGImages
Формат этих четырех файлов одинаков, а содержимое файла — это соответствующее имя изображения без расширения (сxml
Удалить файл аннотации.xml
последовательные) результаты
OK
, с вышеуказанной подготовкой данных, и, наконец, мы используемYOLO
серединаv3/v4
версию в качестве примера, чтобы увидеть, как сочетаются набор данных и профиль обучения?
Здесь мы загружаем файл изyolo
официальный файл скриптаСемья P Eddie.com/Media/files…,Пучокurl
Подать заявку в браузере для скачивания
Код относительно прост, то есть абсолютный путь изображения потребуется обучить, проверить и записать соответствующую картинку.txt
в файле
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
# 原始脚本中包含了VOC2012,这里,我们把它删除
# sets=[('2012', 'train'), ('2012', 'val'), ('2007', 'train'), ('2007', 'val'), ('2007', 'test')]
sets=[('2007', 'train'), ('2007', 'val'), ('2007', 'test')]
# classes也需要根据自己的实际情况修改
# classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]
classes = ["hat"]
def convert(size, box):
dw = 1./size[0]
dh = 1./size[1]
x = (box[0] + box[1])/2.0
y = (box[2] + box[3])/2.0
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(year, image_id):
in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id))
out_file = open('VOCdevkit/VOC%s/labels/%s.txt'%(year, image_id), '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')
wd = getcwd()
for year, image_set in sets:
if not os.path.exists('VOCdevkit/VOC%s/labels/'%(year)):
os.makedirs('VOCdevkit/VOC%s/labels/'%(year))
image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split()
list_file = open('%s_%s.txt'%(year, image_set), 'w')
for image_id in image_ids:
list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id))
convert_annotation(year, image_id)
list_file.close()
После выполнения вышеуказанного скрипта вVOCdevkit
Будет создан каталог того же уровня2007_train.txt
,2007_val.txt
,2007_test.txt
.
вот и все, домашнееVOC2007
Набор данных готов. соответствуетdarknet
файл конфигурации вcfg/voc.data
просто пиши
classes= 1
train = 2007_train.txt
valid = 2007_val.txt
names = data/voc.names
backup = backup/
Преобразование в формат данных YOLO
Во-первых, позвольте мне объяснить, что ранее упомянутые инструменты аннотацииlabelImg
можно экспортироватьYOLO
Формат данных. Но если вы понимаете, формат этикеткиxml
Данные, его необходимо преобразовать. Возьми пример, который мы отмечали выше
хранить все изображения вimages
папка,xml
Файлы аннотаций помещаются вAnnotations
папку, затем создайте папкуlabels
├─Annotations
├─images
└─labels
Подготовьте скрипт конвертации нижеvoc2yolo.py
, некоторые комментарии прописаны в коде
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
# 根据自己情况修改
classes = ["hat"]
def convert(size, box):
dw = 1. / size[0]
dh = 1. / size[1]
x = (box[0] + box[1]) / 2.0
y = (box[2] + box[3]) / 2.0
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(image_id):
if not os.path.exists('Annotations/%s.xml' % (image_id)):
return
in_file = open('annotations/%s.xml' % (image_id))
out_file = open('labels/%s.txt' % (image_id), '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'):
cls = obj.find('name').text
if cls not in classes:
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')
for image in os.listdir('images'):
# 这里需要根据你的图片情况进行对应修改。比如图片名称是123.456.jpg,这里就会出错了。一般来讲,如果图片格式固定,如全都是jpg,那就image_id=image[:-4]处理就好了。总之,情况比较多,自己看着办,哈哈!
image_id = image.split('.')[0]
convert_annotation(image_id)
После выполнения вышеуказанного скриптаlabels
папка будет созданаtxt
форматированный файл аннотации
все знают,yolov5
Структура набора данных, используемая во время обучения, выглядит следующим образом.
├─test
│ ├─images
│ └─labels
├─train
│ ├─images
│ └─labels
└─valid
├─images
└─labels
Поэтому нам также нужно объединить файл изображения с соответствующимtxt
Файл этикетки снова разделяется, и сначала создается внешний слой.train
,valid
,test
папку, а затем создать отдельную папку под каждой папкойimages
иlabels
папка
Затем вы можете использовать следующий скрипт, чтобы пропорционально разделить файлы изображений и меток.
import os
import shutil
import random
# 训练集、验证集和测试集的比例分配
test_percent = 0.1
valid_percent = 0.2
train_percent = 0.7
# 标注文件的路径
image_path = 'images'
label_path = 'labels'
images_files_list = os.listdir(image_path)
labels_files_list = os.listdir(label_path)
print('images files: {}'.format(images_files_list))
print('labels files: {}'.format(labels_files_list))
total_num = len(images_files_list)
print('total_num: {}'.format(total_num))
test_num = int(total_num * test_percent)
valid_num = int(total_num * valid_percent)
train_num = int(total_num * train_percent)
# 对应文件的索引
test_image_index = random.sample(range(total_num), test_num)
valid_image_index = random.sample(range(total_num), valid_num)
train_image_index = random.sample(range(total_num), train_num)
for i in range(total_num):
print('src image: {}, i={}'.format(images_files_list[i], i))
if i in test_image_index:
# 将图片和标签文件拷贝到对应文件夹下
shutil.copyfile('images/{}'.format(images_files_list[i]), 'test/images/{}'.format(images_files_list[i]))
shutil.copyfile('labels/{}'.format(labels_files_list[i]), 'test/labels/{}'.format(labels_files_list[i]))
elif i in valid_image_index:
shutil.copyfile('images/{}'.format(images_files_list[i]), 'valid/images/{}'.format(images_files_list[i]))
shutil.copyfile('labels/{}'.format(labels_files_list[i]), 'valid/labels/{}'.format(labels_files_list[i]))
else:
shutil.copyfile('images/{}'.format(images_files_list[i]), 'train/images/{}'.format(images_files_list[i]))
shutil.copyfile('labels/{}'.format(labels_files_list[i]), 'train/labels/{}'.format(labels_files_list[i]))
После выполнения кода вы можете увидеть аналогичную файловую иерархию
─test
│ ├─images
│ │ 1234565343231.jpg
│ │ 1559035146628.jpg
│ │ 2019032210151.jpg
│ │
│ └─labels
│ 1234565343231.txt
│ 1559035146628.txt
│ 2019032210151.txt
│
├─train
│ ├─images
│ │ 1213211.jpg
│ │ 12i4u33112.jpg
│ │ 1559092537114.jpg
│ │
│ └─labels
│ 1213211.txt
│ 12i4u33112.txt
│ 1559092537114.txt
│
└─valid
├─images
│ 120131247621.jpg
│ 124iuy311.jpg
│ 1559093141383.jpg
│
└─labels
120131247621.txt
124iuy311.txt
1559093141383.txt
На данный момент набор данных действительно готов.
YOLO в VOC
если вы получитеtxt
, но нужно использоватьVOC
, также необходимо преобразовать. Посмотрите на скрипт ниже, комментарии прописаны в коде
import os
import xml.etree.ElementTree as ET
from PIL import Image
import numpy as np
# 图片文件夹,后面的/不能省
img_path = 'images/'
# txt文件夹,后面的/不能省
labels_path = 'labels/'
# xml存放的文件夹,后面的/不能省
annotations_path = 'Annotations/'
labels = os.listdir(labels_path)
# 类别
classes = ["hat"]
# 图片的高度、宽度、深度
sh = sw = sd = 0
def write_xml(imgname, sw, sh, sd, filepath, labeldicts):
'''
imgname: 没有扩展名的图片名称
'''
# 创建Annotation根节点
root = ET.Element('Annotation')
# 创建filename子节点,无扩展名
ET.SubElement(root, 'filename').text = str(imgname)
# 创建size子节点
sizes = ET.SubElement(root,'size')
ET.SubElement(sizes, 'width').text = str(sw)
ET.SubElement(sizes, 'height').text = str(sh)
ET.SubElement(sizes, 'depth').text = str(sd)
for labeldict in labeldicts:
objects = ET.SubElement(root, 'object')
ET.SubElement(objects, 'name').text = labeldict['name']
ET.SubElement(objects, 'pose').text = 'Unspecified'
ET.SubElement(objects, 'truncated').text = '0'
ET.SubElement(objects, 'difficult').text = '0'
bndbox = ET.SubElement(objects,'bndbox')
ET.SubElement(bndbox, 'xmin').text = str(int(labeldict['xmin']))
ET.SubElement(bndbox, 'ymin').text = str(int(labeldict['ymin']))
ET.SubElement(bndbox, 'xmax').text = str(int(labeldict['xmax']))
ET.SubElement(bndbox, 'ymax').text = str(int(labeldict['ymax']))
tree = ET.ElementTree(root)
tree.write(filepath, encoding='utf-8')
for label in labels:
with open(labels_path + label, 'r') as f:
img_id = os.path.splitext(label)[0]
contents = f.readlines()
labeldicts = []
for content in contents:
# 这里要看你的图片格式了,我这里是jpg,注意修改
img = np.array(Image.open(img_path + label.strip('.txt') + '.jpg'))
# 图片的高度和宽度
sh, sw, sd = img.shape[0], img.shape[1], img.shape[2]
content = content.strip('\n').split()
x = float(content[1])*sw
y = float(content[2])*sh
w = float(content[3])*sw
h = float(content[4])*sh
# 坐标的转换,x_center y_center width height -> xmin ymin xmax ymax
new_dict = {'name': classes[int(content[0])],
'difficult': '0',
'xmin': x+1-w/2,
'ymin': y+1-h/2,
'xmax': x+1+w/2,
'ymax': y+1+h/2
}
labeldicts.append(new_dict)
write_xml(img_id, sw, sh, sd, annotations_path + label.strip('.txt') + '.xml', labeldicts)
Выполните приведенный выше скрипт, вы можетеAnnotations
увидеть преобразованныйxml
файл. НазадVOC
Операции с наборами данных см. во второй части текста.