本篇主要讲的是如何将现存的数据变成Tensorflow
记录格式,然后我们就可以用这些数据来进行微调模型,以解决我们关心的问题了。
什么是TFRecord格式
一般使用TF读取数据有四种方式:
- 预先把所有数据加载进内存
- 在每轮训练中使用原生Python代码读取一部分数据,然后使用feed_dict输入到计算图
- 利用Threading和Queues从TFRecord中分批次读取数据
- 使用Dataset API
后面两种都用的是TFRecord
格式。
Tensorflow物体检测API需要训练数据格式是TFRecord
格式。
TFRecord
格式会将所有的标记,比如边界框以及原始图像打包成一个文件,创建TFRecord格式的文件会有一些麻烦,但是一旦建立好以后,使用起来非常方便。
创建单个TFRecord文件
TF给了我们一个样例程序:
def create_tf_example(label_and_data_info):
height = None
width = None
filename = None
encoded_image_data = None # encoded image bytes
image_format = None # b'jpeg' or b'png'
# 存储标准化以后的数据,一个框一个值
xmins = []
xmaxs = []
ymins = []
ymaxs = []
classes_text = [] # 边界框的类别名
classes = [] # 边界框的数字编号
# TODO
tf_label_and_data = tf.train.Example(features=tf.train.Features(feature={
'image/height':dataset_util_int64_feature(height),
'image/width': dataset_util_int64_feature(width),
'image/filename': dataset_util.bytes_feature(filename),
'image/source_id': dataset_util.bytes_feature(filename),
'image/encoded': dataset_util.bytes_feature(encoded_image_data),
'image/format': dataset_util.bytes_feature(image_format),
'image/object/bbox/xmin': dataset_util.float_list_feature(xmins),
'image/object/bbox/xmax': dataset_util.float_list_feature(xmaxs),
'image/object/bbox/ymin': dataset_util.float_list_feature(ymins),
'image/object/bbox/ymax': dataset_util.float_list_feature(ymaxs),
'image/object/class/text': dataset_util.bytes_list_feature(classes_text),
'image/object/class/label': dataset_util.int64_list_feature(classes),
}))
return tf_label_and_data
这个函数就是从单张图片中提取信息,注意到在方框信息和类别信息之外,我们还需要提供编码的图片数据,这个可以使用tf.gfile.GFile()
函数来完成。
创建完整的TFRecord文件
import tensorflow as tf
from object_detection.utils import dataset_util
flags = tf.app.flags
# 设定一个输入命令行参数
flags.DEFINE_string('output_path', '', 'Path to output TFRecord')
FLAGS = flags.FLAGS
def create_tf_example(data_and_label_info):
...
return tf_data_and_label
def main():
writer = tf.python_io.TFRecordWriter(FLAGS.output_path)
# TODO
file_loc = None
all_data_and_label_info = LOAD(file_loc)
for data_and_label_info in all_data_and_label_info:
tf_example = create_tf_example(data_and_label_info)
writer.write(tf_example_SerializeToString())
writer.close()
if __name__ == '__main__':
tf.app.run()
更具体的,针对Bosch Small Traffic Lights Dataset数据集,看这个老哥的实现,代码我拷贝如下:
import tensorflow as tf
import yaml
import os
from object_detection.utils import dataset_util
flags = tf.app.flags
flags.DEFINE_string('output_path', '', 'Path to output TFRecord')
FLAGS = flags.FLAGS
LABEL_DICT = {
"Green" : 1,
"Red" : 2,
"GreenLeft" : 3,
"GreenRight" : 4,
"RedLeft" : 5,
"RedRight" : 6,
"Yellow" : 7,
"off" : 8,
"RedStraight" : 9,
"GreenStraight" : 10,
"GreenStraightLeft" : 11,
"GreenStraightRight" : 12,
"RedStraightLeft" : 13,
"RedStraightRight" : 14
}
def create_tf_example(example):
# Bosch
height = 720 # Image height
width = 1280 # Image width
filename = example['path'] # Filename of the image. Empty if image is not from file
filename = filename.encode()
with tf.gfile.GFile(example['path'], 'rb') as fid:
encoded_image = fid.read()
image_format = 'png'.encode()
xmins = [] # List of normalized left x coordinates in bounding box (1 per box)
xmaxs = [] # List of normalized right x coordinates in bounding box
# (1 per box)
ymins = [] # List of normalized top y coordinates in bounding box (1 per box)
ymaxs = [] # List of normalized bottom y coordinates in bounding box
# (1 per box)
classes_text = [] # List of string class name of bounding box (1 per box)
classes = [] # List of integer class id of bounding box (1 per box)
for box in example['boxes']:
#if box['occluded'] is False:
#print("adding box")
xmins.append(float(box['x_min'] / width))
xmaxs.append(float(box['x_max'] / width))
ymins.append(float(box['y_min'] / height))
ymaxs.append(float(box['y_max'] / height))
classes_text.append(box['label'].encode())
classes.append(int(LABEL_DICT[box['label']]))
tf_example = tf.train.Example(features=tf.train.Features(feature={
'image/height': dataset_util.int64_feature(height),
'image/width': dataset_util.int64_feature(width),
'image/filename': dataset_util.bytes_feature(filename),
'image/source_id': dataset_util.bytes_feature(filename),
'image/encoded': dataset_util.bytes_feature(encoded_image),
'image/format': dataset_util.bytes_feature(image_format),
'image/object/bbox/xmin': dataset_util.float_list_feature(xmins),
'image/object/bbox/xmax': dataset_util.float_list_feature(xmaxs),
'image/object/bbox/ymin': dataset_util.float_list_feature(ymins),
'image/object/bbox/ymax': dataset_util.float_list_feature(ymaxs),
'image/object/class/text': dataset_util.bytes_list_feature(classes_text),
'image/object/class/label': dataset_util.int64_list_feature(classes),
}))
return tf_example
def main(_):
writer = tf.python_io.TFRecordWriter(FLAGS.output_path)
# BOSCH
INPUT_YAML = "data/test-bosch/dataset_test_rgb/test.yaml"
examples = yaml.load(open(INPUT_YAML, 'rb').read())
#examples = examples[:10] # for testing
len_examples = len(examples)
print("Loaded ", len(examples), "examples")
for i in range(len(examples)):
examples[i]['path'] = os.path.abspath(os.path.join(os.path.dirname(INPUT_YAML), examples[i]['path']))
counter = 0
for example in examples:
tf_example = create_tf_example(example)
writer.write(tf_example.SerializeToString())
if counter % 10 == 0:
print("Percent done", (counter/len_examples)*100)
counter += 1
writer.close()
if __name__ == '__main__':
tf.app.run()
其中,yaml
文件样式是:
- boxes:
- {label: Green, occluded: false, x_max: 582.3417892052, x_min: 573.3726437481,
y_max: 276.6271175345, y_min: 256.3114627642}
- {label: Green, occluded: false, x_max: 517.6267821724, x_min: 510.0276868266,
y_max: 273.164089267, y_min: 256.4279864221}
path: ./rgb/train/2015-10-05-16-02-30_bag/720654.png
- boxes: []
path: ./rgb/train/2015-10-05-16-02-30_bag/720932.png
写完执行的方式是:
python tf_record.py --output_path training.record
在上面代码中出现的命令行参数使用,下面展开细说一下。
tf.app.flags使用
- tf.app.flags.DEFINE_string函数
- tf.app.flags.FLAGS变量
flags
其实就是对ArgumentParser
的一层浅浅的封装。
使用tf.app.flags.DEFINE_xxx()
就是用来添加命令行的可选参数,使用tf.app.flags.FLAGS
就是用来从对应的命令行参数取出参数。
其中,xxx
可以是:
- string
- float
- int
- bool
等等。
DEFINE_XXX(flag_name, default_value, doctstring)
这个函数调用的是_define_helper函数
。
def _define_helper(flag_name, default_value, docstring, flagtype):
_global_parser.add_argument('--' + flag_name,
default=default_value,
help=docstring,
type=flagtype)
使用示例:
import tensorflow as tf
FLAGS = tf.app.flags.FLAGS # 用于提取命令行参数
tf.app.flags.DEFINE_float('FLOAT', 0.01, 'Input a float number')
tf.app.flags.DEFINE_integer('INT',100, 'Input an integer number')
tf.app.flags.DEFINE_boolean('BOOL', True, 'Input a boolean number')
# 直接通过FLAGS对象提取命令行参数
print(FLAGS.FLOAT)
print(FLAGS.INT)
print(FLAGS.BOOL)
END.
参考:
https://blog.csdn.net/m0_37041325/article/details/77448971
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/platform/flags.py