自定义数据集、模型,用QT、python脚本模式调用tensorflow编译好的pb模型的图像分类项目
第一步:本文
第二步:
文章目录
flower_photos数据集
flower数据集下载
flower_photos文件夹包含了5个文件夹,每一个子文件夹的名称为一种花的名称,代表了不同的类别(daisy雏菊:633张,dandelion蒲公英:898张,roses玫瑰:641张,sunflowers向日葵:699张,tulips郁金香:799张)。平均每一种花都有734张图片,每一张图片都是RGB色彩模式的,大小也不相同。这里用flower数据集来模拟实际项目。
保存为tfrecord文件
虽然一个TFRecord文件中可以存储多个训练样例,但是当训练数据较大时,可以将数据分成多个TFRecord文件来提高效率。TensorFlow提供了tf.train.match_filenames_once函数来获取符合一个正则表达式的所有文件,得到的文件列表可以通过tf.train.string_input_producer函数进行有效的管理。
导入模块如下:
import os
import tensorflow as tf
import numpy as np
from PIL import Image
1.获取训练集和验证集的图像列表和标签列表
os.walk
获取当前目录下的所有子目录(包含当前目录),返回一个3元组dirpath, dirnames, filenames
如图,test目录下有两个目录,一个txt文件,一个JPEG图片。
测试代码,可以得到
import os
x0 = [x[0] for x in os.walk('test')]
x1 = [x[1] for x in os.walk('test')]
x2 = [x[2] for x in os.walk('test')]
print('x0:')
print(x0)
print('x1')
print(x1)
print('x2:')
print(x2)
os.listdir
获取目录下的所有文件。还是测试test文件夹,测试代码和结果如下
import os
file_dirs = os.listdir('test')
print(file_dirs)
完整功能函数大概思路:获取根目录下的所有子目录(每个子目录放的都是同一类别的图片),依次获得每个子目录下的所有文件名,按比例加入训练集和验证集。
以训练图片列表和训练标签列表为例子,假设训练图片列表和训练标签列表的元素数的n,则获取一2行n列的数组,第一行是图像列表,第二行时标签列表(显然图像列表和标签列表是对应的)。
将数组转置后变成n行2列,第一列为图像列表,第二列为标签列表。
转置后打乱,shuffle对多维数组进行打乱排列时,默认是对第一个维度也就是列维度进行随机打乱。
获取数组的第一列为图像列表,第二列为标签列表。
def get_fileslist(root_dir, ratio = 0.8):
"""得到训练集和验证集的图像列表和标签列表,默认划分比例为0.8"""
tra_image_list = []
tra_label_list = []
val_image_list = []
val_label_list = []
print("读取所有的子目录")
# 获取当前目录下所有的子目录。
# os.walk是一个目录树生成器。对于根在顶部(包括顶部自身,但不包括.和..)的
# 目录树中的每个目录本身,生成一个3元组dirpath, dirnames, filenames
file_dirs = [x[0] for x in os.walk(root_dir)]
# 得到的第一个目录是当前目录,不需要考虑。
del file_dirs[0]
for label in range(len(file_dirs)):
file_dir = file_dirs[label]# 获得标签label对应的文件目录路径
file_list = [] # 存储某个文件下的所有图片文件路径
label_list = []# 存储某个文件下的标签列表
for file_name in os.listdir(file_dir):#listdir会遍历目录下的每个文件
# 文件路径等于目录路径加文件名
file_path = os.path.join(file_dir, file_name)
file_list.append(file_path)
label_list.append(label)
# 对多维数组进行打乱排列时,默认是对第一个维度也就是列维度进行随机打乱
np.random.shuffle(file_list)
# 按比例划分训练集和验证集
cut = np.int(len(file_list) * ratio)
tra_image_list.extend(file_list[:cut])
tra_label_list.extend(label_list[:cut])
val_image_list.extend(file_list[cut:])
val_label_list.extend(label_list[cut:])
print("There are %d tra_image_list \nThere are %d tra_label_list \n"
"There are %d val_image_list \nThere are %d val_label_list \n"
%(len(tra_image_list),len(tra_label_list),len(val_image_list),
len(val_label_list)))
#2行m(2934)列,第一行是图像列表,第二行时标签列表
tra_temp = np.array([tra_image_list, tra_label_list])
#2行n(736)列,第一行是图像列表,第二行时标签列表
val_temp = np.array([val_image_list, val_label_list])
# 对于二维 ndarray,transpose在不指定参数是默认是矩阵转置。对于一维的shape,转置是不起作用的.
tra_temp = tra_temp.transpose()# 转置后变成m(2934)行2列,第一列为图像列表,第二列为标签列表
val_temp = val_temp.transpose()# 转置后变成n(736)行2列,第一列为图像列表,第二列为标签列表
# 对多维数组进行打乱排列时,默认是对第一个维度也就是列维度进行随机打乱
np.random.shuffle(tra_temp)#随机排列,注意调试时不用
np.random.shuffle(val_temp)
tra_image_list = list(tra_temp[:,0])#第一列为图像列表
tra_label_list = list(tra_temp[:,1])#第二列为标签列表
tra_label_list = [int(i) for i in tra_label_list]
val_image_list = list(val_temp[:,0])#第一列为图像列表
val_label_list = list(val_temp[:,1])#第二列为标签列表
val_label_list = [int(i) for i in val_label_list]
# 注意,image_list里面其实存的图片文件的路径
return tra_image_list, tra_label_list, val_image_list, val_label_list
创建tfrecord文件的帮助函数
# 生成字符串型的属性
def _bytes_feature(value):
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))
# 生成整数型的属性
def _int64_feature(value):
return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))
2.将数据写入一个tfrecord文件
思路:创建一个writer来写TFRecord文件,filename是输出TFRecord文件的地址。迭代获取每张图片,缩放为固定尺寸,将样本转成Example Protocol Buffer,并将所有的信息写入这个数据结构,序列化,写入TFRecord文件,全部写入后关闭。
def image2tfrecord(image_list, label_list, filename, size):
"""将图像数据写入TFRecord文件"""
len2 = len(image_list)
print("len=",len2)
# 创建一个writer来写TFRecord文件,filename是输出TFRecord文件的地址
writer = tf.python_io.TFRecordWriter(filename)
for i in range(len2):
# 读取图片并解码
image = Image.open(image_list[i])
image = image.resize((size,size))
# 转化为原始字节(tostring()已经被移除,用tobytes()替代)
image_bytes = image.tobytes()
# 创建字典
features = {}
# 用bytes来存储image
features['image_raw'] = _bytes_feature(image_bytes)
# 用int64来表达label
features['label'] = _int64_feature(label_list[i])
# 将所有的feature合成features
tf_features = tf.train.Features(feature=features)
# 将样本转成Example Protocol Buffer,并将所有的信息写入这个数据结构
tf_example = tf.train.Example(features=tf_features)
# 序列化样本
tf_serialized = tf_example.SerializeToString()
# 将序列化的样本写入trfrecord
writer.write(tf_serialized)
writer.close()
3.将数据写入一个多个tfrecord文件
思路:与写入单个文件类似。这里是每多少个图片数据写入一个tfrecord文件,有规则的命名,可以方便后边表达式获取文件列表。
per_nums表示每个tfrecord文件存储的数据个数
num_shards定义总共写入多少文件
如定义per_nums=1000,图像列表元素个数为2934,则应有3个文件
第1个文件存储image_list[0]~image_list[999]的1000个数据,生成文件flower_tra100_00001–01000.tfrecords
第2个文件存储image_list[1000]-image_list[1999]1000个数据,生成文件flower_tra100_01001–02000.tfrecords
第3个文件存储image_list[2000]-image_list[2933]934个文件,生成flower_tra100_02001–02934.tfrecords
def imagetomultfrecord(image_list, label_list, prename, size):
"""数据量较大时,将图像数据写入多个TFRecord文件"""
length = len(image_list)
print("len=",length)
per_nums = 1000# per_nums表示每个tfrecord文件存储的数据个数
# num_shards定义总共写入多少文件
if length % per_nums == 0:
num_shards = int(length / per_nums)
else:
num_shards = int(length / per_nums) + 1
for i in range(num_shards):
left = i * per_nums
if i == num_shards - 1:
right = length
else:
right = (i + 1) * per_nums
filename = './' + prename + ('_%.5d--%.5d.tfrecords' % (left + 1, right))
print(filename)
# 创建一个writer来写TFRecord文件,filename是输出TFRecord文件的地址
writer = tf.python_io.TFRecordWriter(filename)
for j in range(left, right):
# 读取图片并解码
image = Image.open(image_list[j])
image = image.resize((size,size))
# 转化为原始字节(tostring()已经被移除,用tobytes()替代)
image_bytes = image.tobytes()
# 创建字典
features = {}
# 用bytes来存储image
features['image_raw'] = _bytes_feature(image_bytes)
# 用int64来表达label
features['label'] = _int64_feature(label_list[j])
# 将所有的feature合成features
tf_features = tf.train.Features(feature=features)
# 将样本转成Example Protocol Buffer,并将所有的信息写入这个数据结构
tf_example = tf.train.Example(features=tf_features)
# 序列化样本
tf_serialized = tf_example.SerializeToString()
# 将序列化的样本写入trfrecord
writer.write(tf_serialized)
writer.close()
主函数如下:
if __name__=="__main__":
path="flower_photos"
tra_img_list,tra_label_list,val_image_list,val_label_list=get_fileslist(path)
image2tfrecord(tra_img_list, tra_label_list, "flower_tra100.tfrecord", 100)
image2tfrecord(val_image_list, val_label_list, "flower_val100.tfrecord", 100)
imagetomultfrecord(tra_img_list, tra_label_list, "flower_tra100", 100)
imagetomultfrecord(val_image_list, val_label_list, "flower_val100", 100)
运行后得到文件如下
可以看到29359+29359+27421=86139kB,保存为多个tfrecord文件与保存一个tfrecord的总大小基本一致。
读取并显示tfrecord文件中的数据
可以读取并显示Tfrecord文件的数据,看看是否正确。这里我们读取一个文件看看,flower_tra100_01001–02000.tfrecords。
filepath: 要读取的tfrecord文件的路径
size: 还原尺寸,如序列化前图像尺寸为100x100,则应还原为100x100的图像
num: 显示前几张图片
代码如下:
"""读取TFRecord文件中的数据显示为图片"""
import tensorflow as tf
import matplotlib.pyplot as plt
filepath = "flower_tra100_01001--02000.tfrecords"#文件路径
size = 100#还原尺寸
num = 5#查看图片数量
# tf.train.string_input_producer函数会使用初始化时提供的文件列表创建一个输入队列
# 输入队列中原始的元素为文件列表中的所有文件,可以设置shuffle参数。
filename_queue = tf.train.string_input_producer([filepath])
# 创建一个reader来读取TFRecord文件中的样例
reader = tf.TFRecordReader()
# 从文件中读出一个样例。也可以使用read_up_to函数一次性读取多个案例
_, serialized_example = reader.read(filename_queue) #返回文件名和文件
# 解析读入的一个样例。如果需要解析多个样例,可以用parse_example函数
features = tf.parse_single_example(
serialized_example,
features={
# tf.FixedLenFeature解析的结果为一个tensor
'label': tf.FixedLenFeature([], tf.int64),
'image_raw' : tf.FixedLenFeature([], tf.string),
}) #取出包含image和label的feature对象
# tf.decode_raw可以将字符串解析成图像对应的像素数组
image = tf.decode_raw(features['image_raw'], tf.uint8)
# 根据图像尺寸,还原图像
image = tf.reshape(image, [size , size, 3])
# 将image的数据格式转换成实数型,并进行归一化处理
# image = image.astype('float32');image /= 255
image = tf.cast(image, tf.float32) * 1.0/255
label = tf.cast(features['label'], tf.int32)
# 图像标准化是将数据通过去均值实现中心化的处理,更容易取得训练之后的泛化效果
# 线性缩放image以具有零均值和单位范数。操作计算(x - mean) / adjusted_stddev
# image = tf.image.per_image_standardization(image)
with tf.Session() as sess: #开始一个会话
init_op = tf.global_variables_initializer()
sess.run(init_op)
# 使用tf.train.Coordinator()来协同启动的线程。
coord=tf.train.Coordinator() #创建一个协调器,主要用于协同多个线程一起停止
threads= tf.train.start_queue_runners(coord=coord)#启动QueueRunner, 此时文件名队列已经进队
for i in range(num):
plt.imshow(image.eval())
plt.show()
# 调用request_stop()函数后,should_stop函数的返回值将被设置为True
# 这样其他的线程就可以同时停止了
coord.request_stop()
# 等待所有线程退出
coord.join(threads)
运行结果如下: