一、数据格式介绍
数据文件夹命名为VOC2012,内部有5个子文件夹,如下,
我们的检测任务中使用JPEGImages文件夹和Annotations文件夹。
JPEGImages文件夹中包含了PASCAL VOC所提供的所有的图片信息,包括了训练图片和测试图片。
这些图像都是以“年份_编号.jpg”格式命名的。
图片的像素尺寸大小不一,但是横向图的尺寸大约在500*375左右,纵向图的尺寸大约在375*500左右,基本不会偏差超过100。(在之后的训练中,第一步就是将这些图片都resize到300*300或是500*500,所有原始图片不能离这个标准过远。)
这些图像就是用来进行训练和测试验证的图像数据。
Annotations文件夹中存放的是xml格式的标签文件,每一个xml文件都对应于JPEGImages文件夹中的一张图片。
xml文件的具体格式如下:(对于2007_000392.jpg)
<annotation> <folder>VOC2012</folder> <filename>2007_000392.jpg</filename> //文件名 <source> //图像来源(不重要) <database>The VOC2007 Database</database> <annotation>PASCAL VOC2007</annotation> <image>flickr</image> </source> <size> //图像尺寸(长宽以及通道数) <width>500</width> <height>332</height> <depth>3</depth> </size> <segmented>1</segmented> //是否用于分割(在图像物体识别中01无所谓) <object> //检测目标 <name>horse</name> //物体类别 <pose>Right</pose> //拍摄角度 <truncated>0</truncated> //是否被截断(0表示完整) <difficult>0</difficult> //目标是否难以识别(0表示容易识别) <bndbox> //bounding-box(包含左上角和右下角xy坐标) <xmin>100</xmin> <ymin>96</ymin> <xmax>355</xmax> <ymax>324</ymax> </bndbox> </object> <object> //多检测目标 <name>person</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <bndbox> <xmin>198</xmin> <ymin>58</ymin> <xmax>286</xmax> <ymax>197</ymax> </bndbox> </object> </annotation>
二、数据预处理流程
为了加快数据的读取,框架将数据及标签预先读取并写入tfrecord中,这一部分独立于网络或者说训练结构之外,我们单独介绍这一部分。
启动命令如下,注意需要提前建好OUTPUT_DIR文件夹否则会报错,
DATASET_DIR=./VOC2012/ OUTPUT_DIR=./tfrecords python tf_convert_data.py \ --dataset_name=pascalvoc \ # 数据集名称,实际作者就实现了这一个数据集的预处理方法 --dataset_dir=${DATASET_DIR} \ --output_name=voc2012_tfr # tfr文件名 --output_dir=${OUTPUT_DIR}
脚本tf_convert_data.py
这个脚本主要用于和命令行交互,核心功能就一句调用命令:
# './VOC2012/' './tfrecords' 'voc2012_tfr' pascalvoc_to_tfrecords.run(FLAGS.dataset_dir, FLAGS.output_dir, FLAGS.output_name)
脚本datasets.pascalvoc_to_tfrecords.py
run函数是tfr书写的核心函数,在这个函数中,我们确定具体的每一个tfr文件名,循环的读取图片和标签数据名称,按照指定的容量取书写到每一个tfr文件。
def run(dataset_dir, output_dir, name='voc_train', shuffling=False): """Runs the conversion operation. Args: dataset_dir: The dataset directory where the dataset is stored. output_dir: Output directory. """ if not tf.gfile.Exists(dataset_dir): tf.gfile.MakeDirs(dataset_dir) # Dataset filenames, and shuffling. # './VOC2012/' 'Annotations/' path = os.path.join(dataset_dir, DIRECTORY_ANNOTATIONS) filenames = sorted(os.listdir(path)) # 无路径文件名 if shuffling: random.seed(RANDOM_SEED) random.shuffle(filenames) # Process dataset files. 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: # 一个文件200张图 sys.stdout.write('\r>> Converting image %d/%d' % (i+1, len(filenames))) sys.stdout.flush() # 这两句的输出不会生成多行报告,而是在同一行不断更新数字 filename = filenames[i] img_name = filename[:-4] # 图片名称,去掉字符'.jpg' _add_to_tfrecord(dataset_dir, img_name, tfrecord_writer) # 获取数据并书写 i += 1 j += 1 fidx += 1 # Finally, write the labels file: # labels_to_class_names = dict(zip(range(len(_CLASS_NAMES)), _CLASS_NAMES)) # dataset_utils.write_label_file(labels_to_class_names, dataset_dir) print('\nFinished converting the Pascal VOC dataset!')
这其中,确定具体的每一个tfr文件名函数_get_output_filename很简单,而由文件名读取数据并书写进tfr函数也就分为读文件和写文件两步骤,都很直观,
def _add_to_tfrecord(dataset_dir, name, tfrecord_writer): """Loads data from image and annotations files and add them to a TFRecord. Args: dataset_dir: Dataset directory; name: Image name to add to the TFRecord; tfrecord_writer: The TFRecord writer to use for writing. """ 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) # 书写tfr tfrecord_writer.write(example.SerializeToString()) def _get_output_filename(output_dir, name, idx): return '%s/%s_%03d.tfrecord' % (output_dir, name, idx)