自定义数据集、模型,用QT、c++调用tensorflow编译好的pb模型的图像分类项目
第一步:本文
第二步:读取自定义的TFRecord数据集,训练卷积网络并保存pb模型 第三步:opencv读取自定义的pb模型,自定义softmax,argmax函数输出图像分类结果
流程图
参考如下:
Windows下用c++来调用tensorflow训练好的模型
flower_photos数据集
flower数据集下载 flower_photos文件夹包含了5个文件夹,每一个子文件夹的名称为一种花的名称,代表了不同的类别(daisy雏菊:633张,dandelion蒲公英:898张,roses玫瑰:641张,sunflowers向日葵:699张,tulips郁金香:799张)。平均每一种花都有734张图片,每一张图片都是RGB色彩模式的,大小也不相同。这里用flower数据集可以模拟实际项目。
保存为tfrecord文件
注意flower_photos文件夹下的txt文件要删掉,不然会报错。
1.get_files函数得到的实际是图片的文件名列表和标签列表。如果不打乱排列的话,最后划分数据集和验证集的时候会发现是按顺序划分的。这里我们为了保证数据集和验证集的分布,开始时先按比例划分每个文件夹。比如5个文件夹,每个文件夹100张图,我们先每个文件夹取80张,5个80张合起来就是训练集,5个20张合起来就是验证集。
2.image2tfrecord中可以看到按文件名读取图像后,图像resize大小为100了。
3.最后保存的训练集和验证集的名称分别是flower_tra100.tfrecords和flower_val100.tfrecords。
我们后面可能会用不同的模型训练,输入的张量大小也不尽相同。例如有的模型要求输入图像是224x224x3的,那我们这里在image2tfrecord函数中图像resize大小改成224x224,生成的文件名改成flower_tra224.tfrecords和flower_val224.tfrecords。生成的文件可以重复使用,适应不同的需求。
"""
这里的图片是采用的花朵集的图片 ,先将图片转化成tfrecorder格式。
这里有5种花朵的图片,每种花朵都放在同一个文件夹下
"""
import os
import numpy as np
from PIL import Image
import tensorflow as tf
def get_files(file_dir, ratio = 0.8):
"""得到训练集和验证集的图像列表和标签列表,默认划分比例为0.8"""
daisy = []
label_daisy = []
dandelion = []
label_dandelion = []
roses = []
label_roses = []
sunflowers = []
label_sunflowers = []
tulips = []
label_tulips = []
for file in os.listdir(file_dir):
pp=os.path.join(file_dir,file)
for pic in os.listdir(pp):
pic_path=os.path.join(pp,pic)
if file=="daisy":
daisy.append(pic_path)#读取所在位置名称
label_daisy.append(0)#labels标签为0
elif file=="dandelion":
dandelion.append(pic_path)#读取所在位置名称
label_dandelion.append(1)#labels标签为1
elif file=="roses":
roses.append(pic_path)#读取所在位置名称
label_roses.append(2)#labels标签为2
elif file=="sunflowers":
sunflowers.append(pic_path)#读取所在位置名称
label_sunflowers.append(3)#labels标签为3
elif file=="tulips":
tulips.append(pic_path)#读取所在位置名称
label_tulips.append(4)#labels标签为4
print("There are %d daisy \nThere are %d dandelion "
"\nThere are %d roses \nThere are %d sunflowers "
"\nThere are %d tulips"
%(len(daisy),len(dandelion),len(roses),len(sunflowers),len(tulips)))
# 对多维数组进行打乱排列时,默认是对第一个维度也就是列维度进行随机打乱
np.random.shuffle(daisy)
np.random.shuffle(dandelion)
np.random.shuffle(roses)
np.random.shuffle(sunflowers)
np.random.shuffle(label_tulips)
# 按比例划分训练集和验证集
s1 = np.int(len(daisy) * ratio) # 633 * 0.8 = 506.4
s2 = np.int(len(dandelion) * ratio) # 898 * 0.8 = 718.4
s3 = np.int(len(roses) * ratio) # 641 * 0.8 = 512.8
s4 = np.int(len(sunflowers) * ratio) # 699 * 0.8 = 559.2
s5 = np.int(len(label_tulips) * ratio) # 799 * 0.8 = 639.2
# np.hstack():在水平方向上平铺;np.vstack():在竖直方向上堆叠
# 506 + 718 + 515 + 559 + 639 = 2934
# 633 + 898 + 641 + 699 + 799 - 736
tra_image_list = np.hstack(
(daisy[:s1],dandelion[:s2],roses[:s3],sunflowers[:s4],tulips[:s5]))#1行2934列
tra_label_list = np.hstack(
(label_daisy[:s1],label_dandelion[:s2],label_roses[:s3],
label_sunflowers[:s4],label_tulips[:s5]))#1行2934列
val_image_list = np.hstack(
(daisy[s1:],dandelion[s2:],roses[s3:],sunflowers[s4:],tulips[s5:]))#1行736列
val_label_list = np.hstack(
(label_daisy[s1:],label_dandelion[s2:],label_roses[s3:],
label_sunflowers[s4:],label_tulips[s5:]))#1行736列
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行2934列,第一行是图像列表,第二行时标签列表
tra_temp = np.array([tra_image_list, tra_label_list])
#2行736列,第一行是图像列表,第二行时标签列表
val_temp = np.array([val_image_list, val_label_list])
# 对于二维 ndarray,transpose在不指定参数是默认是矩阵转置。对于一维的shape,转置是不起作用的.
tra_temp = tra_temp.transpose()# 转置后变成2934行2列,第一列为图像列表,第二列为标签列表
val_temp = val_temp.transpose()# 转置后变成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
def image2tfrecord(image_list,label_list,filename):
# 生成字符串型的属性
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]))
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((100,100))
# 转化为原始字节(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()
if __name__=="__main__":
path="flower_photos"
tra_img_list,tra_label_list,val_image_list,val_label_list=get_files(path)
image2tfrecord(tra_img_list, tra_label_list, "flower_tra100.tfrecords")
image2tfrecord(val_image_list, val_label_list, "flower_val100.tfrecords")
我在anaconda下的spider运行,可以看到
1.可以知道,5种类型一共有633+898+641+699+799=3670张照片,训练集照片应为633x0.8+898x0.8+641x0.8+699x0.8+799x0.8=2934张,验证集照片应为3670-2934=736张,可以看到划分验证集成功。
2.可以看到训练集的前五张图片标签为20301,即玫瑰、雏菊、向日葵、雏菊、蒲公英。验证集的前五张图片标签为43401,即郁金香、向日葵、郁金香、雏菊、蒲公英。
3.分别点击Variable explorer里的train_img_list和val_img_list分别查看训练图像列表和验证图像列表,可以看到图像列表存储的是打乱后的图片的路径文件名。
这里我们先查看train_img_list的第1个元素(下标0),路径名flower_photos\roses\8502529435_c6e40d0df4.jpg,由上图的train_label_list可以看到第1个元素的标签为2,对应。根据路径找到图片。
再查看val_img_list的第1个元素(下标0),路径名为flower_photos\tulips\8762189906_8223cef62f.jpg,由前面图的val_label_list可以看到第1个元素的标签为4,对应。根据路径找到图片。
3.如果将get_file函数中的np.random.shuffle(temp)注释掉,可以看到完全是按顺序划分的训练集和验证集。GG
读取并显示tfrecord文件中的数据
"""读取TFRecord文件中的数据显示为图片"""
import tensorflow as tf
import matplotlib.pyplot as plt
# tf.train.string_input_producer函数会使用初始化时提供的文件列表创建一个输入队列
# 输入队列中原始的元素为文件列表中的所有文件,可以设置shuffle参数。
filename_queue = tf.train.string_input_producer(["flower_tra100.tfrecords"])
# 创建一个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, [100 , 100, 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(5):
plt.imshow(image.eval())
plt.show()
# 调用request_stop()函数后,should_stop函数的返回值将被设置为True
# 这样其他的线程就可以同时停止了
coord.request_stop()
# 等待所有线程退出
coord.join(threads)
1.先是显示训练集中的前5张照片,结果如下。可以看到第1张图和前面一样,且前五张种类依次是玫瑰、雏菊、向日葵、雏菊、蒲公英,与前面一一对应。注意显示的图片的大小是100x100。
2.然后读取并显示验证集中的前5张照片,结果如下。可以看到第1张图和前面一样,且前五张种类依次是郁金香、向日葵、郁金香、雏菊、蒲公英,与前面一一对应。注意显示的图片的大小是100x100
3.将图像标准化那一行代码取消注释,即image = tf.image.per_image_standardization(image),可以看到图像特征更加的泛化。
自定义数据集、模型,用QT、c++调用tensorflow编译好的pb模型的图像分类项目
第一步:本文
第二步:读取自定义的TFRecord数据集,训练卷积网络并保存pb模型 第三步:opencv读取自定义的pb模型,自定义softmax,argmax函数输出图像分类结果
流程图
参考如下:
Windows下用c++来调用tensorflow训练好的模型
flower_photos数据集
flower数据集下载 flower_photos文件夹包含了5个文件夹,每一个子文件夹的名称为一种花的名称,代表了不同的类别(daisy雏菊:633张,dandelion蒲公英:898张,roses玫瑰:641张,sunflowers向日葵:699张,tulips郁金香:799张)。平均每一种花都有734张图片,每一张图片都是RGB色彩模式的,大小也不相同。这里用flower数据集可以模拟实际项目。
保存为tfrecord文件
注意flower_photos文件夹下的txt文件要删掉,不然会报错。
1.get_files函数得到的实际是图片的文件名列表和标签列表。如果不打乱排列的话,最后划分数据集和验证集的时候会发现是按顺序划分的。这里我们为了保证数据集和验证集的分布,开始时先按比例划分每个文件夹。比如5个文件夹,每个文件夹100张图,我们先每个文件夹取80张,5个80张合起来就是训练集,5个20张合起来就是验证集。
2.image2tfrecord中可以看到按文件名读取图像后,图像resize大小为100了。
3.最后保存的训练集和验证集的名称分别是flower_tra100.tfrecords和flower_val100.tfrecords。
我们后面可能会用不同的模型训练,输入的张量大小也不尽相同。例如有的模型要求输入图像是224x224x3的,那我们这里在image2tfrecord函数中图像resize大小改成224x224,生成的文件名改成flower_tra224.tfrecords和flower_val224.tfrecords。生成的文件可以重复使用,适应不同的需求。