1. 配套使用: tf.train.Examples将数据转换为二进制,提升IO效率和方便管理
对于int类型 : tf.train.Examples(features=tf.train.Features(feature=tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))))
对于bytes类型: tf.train.Examples(features=tf.train.Features(feature=tf.train.Feature(int64_list=tf.train.BytesList(value=[value]))))
2. tf.image.decode_jpeg(image, channel=3) # 将图片进行解码操作,通道数是3
参数说明:image表示图片,channel表示通道数
3. tf.image.encode_jpeg(image, format='rgb', quality=100) # 将解码后的图片,转码成jpeg
参数说明:image表示图片,format表示rgb的类型,quality表示图片的质量
4. tf.train.Coordinator() # 构建多线程通道,使用coord.join(threads)将线程列表添加
5.t = threading.Thread(target=, args=) # 建立单线程
参数说明:target表示对应的函数,args表示传入的参数
6. tf.python_io.TFRecordWriter(output_path) 构造TFR的写入器writer,使用writer.write将打包的参数写入
参数说明:output_path表示保存TFR文件的路径
7.np.linspace(0, len(3), 3) # 表示将数据进行切分,
参数说明:0表示第一个数,len(3)表示第二个数,3表示切分成3个数,
8. sys.stdout.flush() 进行数据的刷新
数据说明:在flower文件中,存在5个文件,每个文件的文件名分别对应的是标签名,在flower_label.txt保存的5个标签名
代码说明:这个采用了多线程的方式,即如果要生成n个shards,有2个thread, 那么每个thread就处理n/2个shards的生成
参数说明:使用tf.app.flags.DEFINE_string 进行参数的设置,使用FLAGS = tf.app.flags.FLAGS将参数并入FLAGS中
代码主要分为第一部分和第二部分,使用tf.app.run()执行代码,定义model(),在model中分为两部分
第一部分:用于进行文件名的读取filenames,标签的制作labels,以及存入标签名texts
第二部分:建立多个线程,在每个线程中,调用函数,建立TFR的写入writer,循环一个线程的shards数,将标准化后的example进行写入,关闭writer
第一部分:
第一步:构建find_image_files, 传入参数为文件名,标签名
第二步:使用[l.strip() for l in TF.gfile.FastGFile(f).readlines()] 来获得标签值
第三步: 循环标签值,使用os.path.join(‘%s/%s/*’%())标签值和文件路径进行拼接,获得每一个文件夹的名字
第四步: 使用tf.gfile.Glob(文件名) 获得文件名中所有的图片名称,如果报错,打印文件名,continue
第五步:定义filenames, labels, texts = []
第六步:使用.extend() 将图片列表添加到filenames, 使用[label_index] * len(matchfile)制作标签,以及[text] * len(matchfile) 制作标签名,使用.extend() 进行添加
第七步: 构造shuffed_index = np.arange(len(filenames)), 使用random.shuffle(shuffed_index) 获得混乱的标签索引值
第八步:使用filenames = [filename[i] for i in shuffed_index] 构造打乱的filenams,labels,和texts, 返回结果
第二部分:将生成的filenams,labels和texts, train_shards传入到process_image_files函数
第一步:使用assert len(filenames) == len(labels),即filenams的大小是否和labeles以及texts的大小相同
第二步:使用np.linspace(0, len(filenames), train_shards+1) 根据train_shards大小进行切分,
第三步:循环切分的列表,将两个之间的索引值添加到ranges中, ranges.append([split_index[i], split_index[i+1]]), 使用sys.stdout.flush() 刷新操作
第四步:实例化coder = ImageCode()
第一步:构造png转jpg的初始化函数,使用tf.placeholder初始化,使用tf,image.decode_png()解码png,使用tf.image.encode_jpeg()编码jpeg
第二步:构造jpg的解码的初始化函数, 使用tf.placeholder初始化,使用tf.image.decode_jpeg()解码jpg
第三步:构造函数png_to_jpeg(self, image_data), sess.run(self._png_to_jpg, feed_dict={self.png_image:image_data}),进行实际转换
第四步:构造函数decode_jpeg(self, image_data):, sess.run(self._decode_jpg) 进行实际转换
第五步:使用coord = tf.train.Coordinator() 建立线程的通道
第六步:循环num_thread, 建立args参数,使用threading.Thread(target=_precess_image_files_batch, args) 构建单线程,使用t.start()启动单线程,threads.append(t), 然后再将coord.join(threads)构建出了多线程
函数target = _precess_image_files_batch 说明:主要是用于构建Tfrecord数据集
第一步:将train_shards / num_threads相除,获得每个线程所需要建立的shards的个数
第二步:使用np.linspace(range[thread_index][0], range[thread_index][1],num_shards_per_batch + 1) # 获得每一个shards的数据索引
第三步:使用ranges[thread_index][1] - ranges[thread_index][0] 获得当前线程所执行的样本数
第四步:循环num_shard_per_batch,即每个线程所需要建立的shards个数
第五步:使用shard = thread_index * num_per_batch + s,获得当前的shards的索引
第六步:使用name,shard, trian_shard建立当前的路径
第七步:使用os.path.join() 将文件路径与当前路径进行拼接,获得保存路径
第八步:使用writer = tf.python_io.TFRecordWriter(output_path) 构造一个TFR写入的writer函数
第九步:循环每一个shards的数据索引值
第十步:使用_image_process获得图片的image_buff, 如果是png格式使用coder转换为jpg格式
第十一步:使用tf.train.Example(features=tf.train.Features(feature={
})) # 将参数进行打包,返回example
第十二步:writer.write(example.SerializeToString()) 将数据写入到writer中
第十三步:如果执行1000次,打印当前时间,第几个线程,第几张图片, 该线程的图片数,使用sys.stdout.flush() 刷新
第十四步:一个shards结束后,writer.close() ,打印,并进行刷新
第十五步:整个循环结束后,打印,并刷新
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function from datetime import datetime
import os
import random
import sys import threading
import numpy as np
import tensorflow as tf tf.app.flags.DEFINE_string('train_directory', './flower_photos/',
'Training data directory')
tf.app.flags.DEFINE_string('validation_directory', './flower_photos',
'Validation data directory') tf.app.flags.DEFINE_string('output_directory', './data/',
'Output data directory') tf.app.flags.DEFINE_integer('train_shards', 2, 'Number of shards in training TFRecord files.') tf.app.flags.DEFINE_integer('validation_shards', 0, 'Number of shards in validation TFRecord files.') tf.app.flags.DEFINE_integer('num_threads', 2, 'Number of thread to preprocess the images') tf.app.flags.DEFINE_string('labels_file', './flower_label.txt', 'labels file') FLAGS = tf.app.flags.FLAGS def _int64_feature(value): if not isinstance(value, list):
value = [value]
# 将数据转换为int的列表类型
return tf.train.Feature(int64_list=tf.train.Int64List(value=value)) def _bytes_feature(value):
# 将数据转换为bytes的列表类型
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) def _convert_to_example(filename, image_buff, label, text, height, width): colorspace= 'RGB'
channels = 3
image_format = 'JPEG'
# 使用tf.train.Example和tf.train.Features构造打包数据
example = tf.train.Example(features=tf.train.Features(feature={
'image/height': _int64_feature(height),
'image/width': _int64_feature(width),
'image/colorspace': _bytes_feature(tf.compat.as_bytes(colorspace)),
'image/channels': _int64_feature(channels),
'image/class/label': _int64_feature(label),
'image/class/text': _bytes_feature(tf.compat.as_bytes(text)),
'image/format': _bytes_feature(tf.compat.as_bytes(image_format)),
'image/filename': _bytes_feature(tf.compat.as_bytes(os.path.basename(filename))),
'image/encoded': _bytes_feature(tf.compat.as_bytes(image_buff))
}))
return example def is_png(filename): return '.png' in filename def _process_image(filename, coder): # 打开文件夹,使用bytes数据类型
with tf.gfile.FastGFile(filename, 'rb') as f:
# 进行数据的读取
image_data = f.read()
# 如果数据是png的格式的话就转换为jpeg类型
if is_png(filename):
image_data = coder.png_to_jpeg(image_data)
# 将iamge_data进行解码
image = coder.decode_jpeg(image_data)
# 判断图片的维度是否是3
assert len(image.shape) == 3
# 图片的高度
height = image.shape[0]
# 图片的宽度
width = image.shape[1] return image_data, height, width def _process_image_files_batch(coder, thread_index, ranges, name, filenames, texts, labels, train_shards): # 获得线程数
num_threads = len(ranges)
# 保证线程和train_shards可以整除
assert not train_shards % num_threads
# 每个线程所需要执行的shards数
num_shards_per_batch = int(train_shards / num_threads)
# 根据每个线程的shars数,获得每个线程切分后的数据索引值
shard_ranges = np.linspace(ranges[thread_index][0],
ranges[thread_index][1], num_shards_per_batch + 1)
# 获得线程的样本数目
num_files_in_thread = ranges[thread_index][1] - ranges[thread_index][0]
# 用于图片的统计
counter = 0
# 循环线程
for s in range(num_shards_per_batch):
# 获得当前是第几个shards
shard = thread_index * num_shards_per_batch + s
# 构造TFR的路径
output_filename = '%s-%.5d-of-%.5d.tfrecord'%(name, shard, train_shards)
# 拼接获得最终的路径
output_file = os.path.join(FLAGS.output_directory, output_filename)
# 输入路径,构造TFR的读入器
writer = tf.python_io.TFRecordWriter(output_file)
#
shard_counter = 0
# 获得数据索引值的列表
file_in_shard = np.arange(shard_ranges[s], shard_ranges[s+1]).astype('int')
# 循环列表
for i in file_in_shard:
# 获得文件名
filename = filenames[i]
# 获得标签值
label = labels[i]
# 获得标签名
text = texts[i]
# 使用tf.gfile.FastGFile(filename, 'rb'): 打开文件
image_buff, height, width = _process_image(filename, coder)
# 将各个参数进行打包
example = _convert_to_example(filename, image_buff, label, text,
height, width)
# 将一张图片打包好的参数写入到writer里面
writer.write(example.SerializeToString())
shard_counter += 1
counter += 1
# 如果读取了1000张图片,打印并进行刷新操作
if not counter % 1000:
print('%s [thread %d]:Processed %d of %d images in thread batch.'%(
datetime.now(), thread_index, counter, num_files_in_thread
))
sys.stdout.flush()
# 循环了一个shards就关闭writer,打印和刷新操作
writer.close()
print('%s [thread %d]:Processed %d of %d images in thread batch.' % (
datetime.now(), thread_index, counter, num_files_in_thread
))
sys.stdout.flush()
# 循环结束,打印和刷新操作
print('%s [thread %d]:Processed %d of %d images in thread batch.' % (
datetime.now(), thread_index, counter, num_files_in_thread
))
sys.stdout.flush() class ImageCoder(object): def __init__(self):
# 构造sess函数
self._sess = tf.Session()
# 初始化png的输入数据
self.png_img = tf.placeholder(dtype=tf.string)
# 将png进行解码操作,使用tf.image.decode_png
image = tf.image.decode_png(self.png_img, channels=3)
# 对解码的图片,使用tf.image.encode_jpeg进行加码操作,format表示rgb, quality表示图片的质量
self._png_to_jpeg = tf.image.encode_jpeg(image, format='rgb', quality=100)
# 初始化输入jpeg_image的图片
self.jpeg_image = tf.placeholder(dtype=tf.string)
# 使用tf.image.decode_jpeg对图片进行解码操作
self._decode_jpeg = tf.image.decode_jpeg(self.jpeg_image, channels=3) def png_to_jpeg(self, image_data):
# 输入数据,获得实际的png_to_jpeg的转换
return self._sess.run(self._png_to_jpeg, feed_dict={self.png_img:image_data}) def decode_jpeg(self, image_data):
# 输入数据,进行实际的decode_jpeg的解码操作
image = self._sess.run(self._decode_jpeg, feed_dict={self.jpeg_image:image_data}) return image def _process_image_files(name, filenames, labels, texts, train_shards):
# 第一步:确认维度是否相等
assert len(filenames) == len(texts)
assert len(filenames) == len(labels) # 第二步:使用np.linspace()将len(filenames)根据train_shards的个数进行切片,获得每一份的索引值
ranges = []
split_index = np.linspace(0, len(filenames), train_shards+1)
# 第三步:根据索引值,构造range列表,即每一个列表,对于前后的索引值
for i in range(len(split_index) - 1):
ranges.append([split_index[i], split_index[i+1]]) # 刷新操作
print('Launching %d threads for spacings: %s'%(FLAGS.num_threads, ranges))
sys.stdout.flush()
# 第四步:实例化ImageCoder() 进行png_to_jpeg,或者对jpeg进行解码操作
coder = ImageCoder() # 第五步:使用tf.train.Coordinator获得线程的通道
coord = tf.train.Coordinator()
# 构造线程的列表
threads = []
# 第六步:循环线程的个数,构造多线程
for thread_index in range(len(ranges)):
# 构造线程参数,coder表示解码器,thread_index当前是第几个线程,ranges表示线程的范围,name表示名字,filenames表示文件的图片名
args = (coder, thread_index, ranges, name, filenames, texts, labels, train_shards)
# 构建线程
t = threading.Thread(target=_process_image_files_batch, args=args)
# 开始线程
t.start()
# 线程添加到线程列表中
threads.append(t)
# 将线程添加到通道中
coord.join(threads) def _find_image_files(data_path, labels_file): # 使用tf.gfile.FastGFile读取标签文件,获得标签的列表
upload_labels = [l.strip() for l in tf.gfile.FastGFile(labels_file).readlines()] # 第一种类别的标签值
label_index = 1
# 文件图片的名字列表
filenames = []
# 标签列表
labels = []
# 图片标签名列表
texts = []
# 循环标签文本
for text in upload_labels:
# 构造相对路径
jpeg_path_join = '%s/%s/*' % (data_path, text)
try:
# 使用tf.gfile.Glob遍历相对路径下所有的图片,获得图片的路径
matchfile = tf.gfile.Glob(jpeg_path_join)
except:
print(jpeg_path_join)
continue # 将获得的路径列表添加到filenames中
filenames.extend(matchfile)
# 产生[1] * len(filenams)的标签,并添加到标签列表中
labels.extend([label_index] * len(matchfile))
# 产生[test] * len(filenames)的标签名,并添加到标签名列表中
texts.extend([text] * len(matchfile))
# 标签值加1,作为下一个类比的标签值
label_index += 1
# 获得len(filenames)的索引值
shuffled_index = np.arange(len(filenames))
# 将索引值进行打乱
random.seed(1234)
random.shuffle(shuffled_index)
# 根据打乱的索引值重新构造filenames,labels,和texts
filenames = [filenames[i] for i in shuffled_index]
labels = [labels[i] for i in shuffled_index]
texts = [texts[i] for i in shuffled_index]
# 返回filenames,labels和texts
return filenames, labels, texts def _process_dataset(name, data_path, train_shards, labels_file): # 第一部分:找出文件名,构造标签列表以及文件名列表,并使用random.shuffle获得乱序的索引
filenames, labels, texts = _find_image_files(data_path, labels_file)
# 第二部分:将文件图片的名字,标签值,标签名,以及生产shards个数传入,用于生成tfrecord
_process_image_files(name, filenames, labels, texts, train_shards) def main(unused_argv):
# 确认train_shards是够能被线程整除
assert not FLAGS.train_shards % FLAGS.num_threads, ('在训练过程中线程的数目应该与文件的数目相对应') assert not FLAGS.validation_shards % FLAGS.num_threads, ('在验证集制作中线程的数目应该与文件的数目相对应')
# 构建process_dataset执行函数,传入的参数有name, 训练数据文件路径,训练集的num,以及标签的文件名
_process_dataset('train', FLAGS.train_directory,
FLAGS.train_shards,
FLAGS.labels_file) if __name__ == '__main__':
tf.app.run()