SSD目标检测(1):如何制作自己的数据集(详细说明附源码)

前言:因为要依赖上一章SSD多目标定位检测识别框架来训练预测自己的数据集,所以建立自己的数据集是一个复杂且避不开的步骤,以下给出了制作自己的数据集所要经过的简单步骤,而后也有更详细的说明奉上。

  1. VOC2007数据集简介;
  2. 规定文件夹名称,图片名称、格式;如何生成txt文件;
  3. 如何使用labelImg工具给图片上标签,并生成.xml文件;
  4. 最后如何将.xml文件生成SSD所需要的.tfrecords文件;

以下过程详细,如有不明请留言提醒,详细过程如下:
—-—-—-—-—-—-—-—-—-—-—-—–—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-
—-—-—-—-—-—-—-—-—-—-—-—–—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-

1、VOC2007数据集简介

知己知彼,方百战不殆。想制作自己的数据集当然要先了解SSD使用的数据集VOC2007长啥样。VOC2007下载链接 ,密码是:m5io。(VOC2007完整下载有3个压缩包+1个PDF,上面链接里只包含其中一个压缩包VOCtrainval_06-Nov-2007)。打开压缩包就如下图:
这里写图片描述
VOC2007详细介绍在这里,提供给大家有兴趣作了解。而制作自己的数据集只需用到前三个文件夹,所以请事先建好这三个文件夹放入同一文件夹内,同时ImageSets文件夹内包含Main文件夹

  • JPEGImages:用于存放训练、测试的图片(图片格式最好为.jpg)
  • Annatations:用于存放.xml格式的文件,也就是图片对应的标签,每个.xml文件都对应于JPEGImages文件夹的一张图片
  • ImageSets:内含Main文件夹,在../ImageSets/Main文件夹下包含test.txt、train.txt、val.txt、trainval.txt四个文件,生成的方式第二步有详细说明

—-—-—-—-—-—-—-—-—-—-—-—–—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-

2、制作自己的数据集

第一步:下载图片,存入JPEGImages文件夹——你可以直接从各种渠道下载得到所需要的图片集,存入到JPEGImages文件夹下,命名格式统一为“00xxxx.jpg”,如下图:
这里写图片描述

第二步:生成Main文件夹下的.txt文件——在主目录下运行以下代码既可生成test.txt、train.txt、val.txt、trainval.txt四个文件,请注意每一个path地址是否正确(其实这四个txt文件在后续并没有什么用处)

# -*- coding:utf-8 -*-
# -*- author:zzZ_CMing  CSDN address:https://blog.csdn.net/zzZ_CMing
# -*- 2018/07/18; 15:19
# -*- python3.5
import os  
import random  

trainval_percent = 0.7  
train_percent = 0.8
xmlfilepath = 'Annotations'
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(txtsavepath+'/trainval.txt', 'w')  
ftest = open(txtsavepath+'/test.txt', 'w')  
ftrain = open(txtsavepath+'/train.txt', 'w')  
fval = open(txtsavepath+'/val.txt', 'w')  

for i in list:  
    name = total_xml[i][:-4]+'\n'  
    if i in trainval:  
        ftrainval.write(name)  
        if i in train:  
            ftrain.write(name)  
        else:  
            fval.write(name)  
    else:  
        ftest.write(name)  

ftrainval.close()  
ftrain.close()  
fval.close()  
ftest .close()
print('Well Done!!!')

运行完成,得到如下文件:可以打开看一看,内容就是各个图片的索引,意味着哪些图片用做训练,哪些用做测试。
这里写图片描述

第三步:使用labelImg工具给图片打标签——这是最重要的一步。如果你的python已经pip install lxml下载了lxml,就可以直接在我网盘下载labelImg工具windows版使用,密码:gyf3。
通过以上网盘下载得到工具文件后,打开../data/predefined_classes.txt文件,可以发现这里都是图片标签——把你将要用到的标签都事先存入在这里,注意标签不能有中文。每次使用都把.exedata这两个文件拖到桌面上(如果直接在文件夹内运行.exe会报错不能运行),打开labelImg.exe文件,运行界面如下:就可以开始给图片打标签了
这里写图片描述
labelImg工具简单的使用步骤就是:

  1. 打开单个文件,或者打开一个图片文件夹
  2. 给目标物体建立box边框
  3. 对box边框内的物体贴上标签
  4. 把一张图片内所有目标物都打上各自标签后,再保存生成.xml文件,注意存入Annatations文件夹,文件名也要与当前图片保存一致
  5. 然后next下一张图片继续打标签,直到所有图片内物体都打上了标签,最后exit

—-—-—-—-—-—-—-—-—-—-—-—–—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-

3、用.xml标签,生成.tfrecord文件

说明:SSD框架所用到的标签文件并不直接是.xml格式文件,而是.tfrecord文件,因为这一部分比较重要,代码先贴上——只为想研究如何生成.tfrecord文件的同学准备,想要了解 SSD目标检测(2):使用自己的数据集做识别(详细说明附源码),请继续点击,详细过程讲解+源码即刻奉上

特别注意:要在主目录提前建好tfrecords_文件夹,不然会报错找不到目标文件夹
特别注意:要在主目录提前建好tfrecords_文件夹,不然会报错找不到目标文件夹

# -*- coding:utf-8 -*-
# -*- author:zzZ_CMing  CSDN address:https://blog.csdn.net/zzZ_CMing
# -*- 2018/07/17; 13:18
# -*- python3.5
"""
特别注意: path地址是否正确、要在主目录下提前创建“tfrecords_”文件夹
"""

import os
import sys
import random
import numpy as np
import tensorflow as tf
import xml.etree.ElementTree as ET  # 操作xml文件

# 我的标签定义只有两类,要根据自己的图片而定
VOC_LABELS = {
    'none': (0, 'Background'),
    'aiaitie': (1, 'Product')
}

# 图片和标签存放的文件夹.
DIRECTORY_ANNOTATIONS = 'Annotations/'
DIRECTORY_IMAGES = 'JPEGImages/'

# 随机种子.
RANDOM_SEED = 4242
SAMPLES_PER_FILES = 3  # 每个.tfrecords文件包含几个.xml样本


# 生成整数型,浮点型和字符串型的属性
def int64_feature(value):
    if not isinstance(value, list):
        value = [value]
    return tf.train.Feature(int64_list=tf.train.Int64List(value=value))

def float_feature(value):
    if not isinstance(value, list):
        value = [value]
    return tf.train.Feature(float_list=tf.train.FloatList(value=value))

def bytes_feature(value):
    if not isinstance(value, list):
        value = [value]
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=value))

# 图片处理
def _process_image(directory, name):
    # Read the image file.
    filename = directory + DIRECTORY_IMAGES + name + '.jpg'
    image_data = tf.gfile.FastGFile(filename, 'rb').read()

    # Read the XML annotation file.
    filename = os.path.join(directory, DIRECTORY_ANNOTATIONS, name + '.xml')
    tree = ET.parse(filename)
    root = tree.getroot()

    # Image shape.
    size = root.find('size')
    shape = [int(size.find('height').text),
             int(size.find('width').text),
             int(size.find('depth').text)]
    # Find annotations.
    bboxes = []
    labels = []
    labels_text = []
    difficult = []
    truncated = []
    for obj in root.findall('object'):
        label = obj.find('name').text
        labels.append(int(VOC_LABELS[label][0]))
        labels_text.append(label.encode('ascii'))  # 变为ascii格式

        if obj.find('difficult'):
            difficult.append(int(obj.find('difficult').text))
        else:
            difficult.append(0)
        if obj.find('truncated'):
            truncated.append(int(obj.find('truncated').text))
        else:
            truncated.append(0)

        bbox = obj.find('bndbox')
        a = float(bbox.find('ymin').text) / shape[0]
        b = float(bbox.find('xmin').text) / shape[1]
        a1 = float(bbox.find('ymax').text) / shape[0]
        b1 = float(bbox.find('xmax').text) / shape[1]
        a_e = a1 - a
        b_e = b1 - b
        if abs(a_e) < 1 and abs(b_e) < 1:
            bboxes.append((a, b, a1, b1))

    return image_data, shape, bboxes, labels, labels_text, difficult, truncated

# 转化样例
def _convert_to_example(image_data, labels, labels_text, bboxes, shape,
                        difficult, truncated):
    xmin = []
    ymin = []
    xmax = []
    ymax = []
    for b in bboxes:
        assert len(b) == 4
        # pylint: disable=expression-not-assigned
        [l.append(point) for l, point in zip([ymin, xmin, ymax, xmax], b)]
        # pylint: enable=expression-not-assigned

    image_format = b'JPEG'
    example = tf.train.Example(features=tf.train.Features(feature={
        'image/height': int64_feature(shape[0]),
        'image/width': int64_feature(shape[1]),
        'image/channels': int64_feature(shape[2]),
        'image/shape': int64_feature(shape),
        'image/object/bbox/xmin': float_feature(xmin),
        'image/object/bbox/xmax': float_feature(xmax),
        'image/object/bbox/ymin': float_feature(ymin),
        'image/object/bbox/ymax': float_feature(ymax),
        'image/object/bbox/label': int64_feature(labels),
        'image/object/bbox/label_text': bytes_feature(labels_text),
        'image/object/bbox/difficult': int64_feature(difficult),
        'image/object/bbox/truncated': int64_feature(truncated),
        'image/format': bytes_feature(image_format),
        'image/encoded': bytes_feature(image_data)}))
    return example

# 增加到tfrecord
def _add_to_tfrecord(dataset_dir, name, tfrecord_writer):
    image_data, shape, bboxes, labels, labels_text, difficult, truncated = \
        _process_image(dataset_dir, name)
    example = _convert_to_example(image_data, labels, labels_text,
                                  bboxes, shape, difficult, truncated)
    tfrecord_writer.write(example.SerializeToString())


# name为转化文件的前缀
def _get_output_filename(output_dir, name, idx):
    return '%s/%s_%03d.tfrecord' % (output_dir, name, idx)


def run(dataset_dir, output_dir, name='voc_train', shuffling=False):
    if not tf.gfile.Exists(dataset_dir):
        tf.gfile.MakeDirs(dataset_dir)

    path = os.path.join(dataset_dir, DIRECTORY_ANNOTATIONS)
    filenames = sorted(os.listdir(path))  # 排序
    if shuffling:
        random.seed(RANDOM_SEED)
        random.shuffle(filenames)

    i = 0
    fidx = 0
    while i < len(filenames):
        # Open new TFRecord file.
        tf_filename = _get_output_filename(output_dir, name, fidx)
        with tf.python_io.TFRecordWriter(tf_filename) as tfrecord_writer:
            j = 0
            while i < len(filenames) and j < SAMPLES_PER_FILES:
                sys.stdout.write(' Converting image %d/%d \n' % (i + 1, len(filenames)))  # 终端打印,类似print
                sys.stdout.flush()  # 缓冲

                filename = filenames[i]
                img_name = filename[:-4]
                _add_to_tfrecord(dataset_dir, img_name, tfrecord_writer)
                i += 1
                j += 1
            fidx += 1

    print('\nFinished converting the Pascal VOC dataset!')


# 原数据集路径,输出路径以及输出文件名,要根据自己实际做改动
dataset_dir = "C:/Users/Admin/Desktop/"
output_dir = "./tfrecords_"
name = "voc_train"

def main(_):
    run(dataset_dir, output_dir, name)

if __name__ == '__main__':
    tf.app.run()

得到的.tfrecords文件如下:
这里写图片描述
到这里,用于SSD的自己的数据集就建立完成了,主要需要的就是.tfrecords文件。下一章 SSD目标检测(2):使用自己的数据集做识别(详细说明附源码)介绍如何用自己的数据集开展训练预测

猜你喜欢

转载自blog.csdn.net/zzz_cming/article/details/81131101