图像分类项目新手升级版(一):制作多个TFRecord数据集

自定义数据集、模型,用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)

运行结果如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值