Tensorflow实现性别年龄检测项目前奏——数据预处理(转换数据集为 TFRecords 格式)

Adience 数据集简介:

实例中的数据集为Adience数据集,Adience数据集包含26580张图片,总共含有2284个类,涉及的年龄范围有8个区间(0~2, 4~6, 8~13, 15~20, 25~32, 38~43, 48~53, 60~),并且这个数据集含有噪声、姿势、光照等变化,尽可能真实地去模拟现实生活的情景。

Adience数据集的地址为:链接:https://pan.baidu.com/s/12uiTKUueWpOPHkknz_mvPg 
                                              提取码:x8wf 
文件目录与FLAGS设置:

Tensorflow实现性别年龄检测项目前奏——数据预处理(转换数据集为 TFRecords 格式)_第1张图片

FLAGS = tf.app.flags.FLAGS设置:
tf.app.flags.DEFINE_string('fold_dir', '../Folds/train_val_txt_files_per_fold/test_fold_is_0', '数据索引所在的文件夹')
tf.app.flags.DEFINE_string('data_dir', './aligned', '数据集所在的文件夹')
tf.app.flags.DEFINE_string('output_dir', '../Folds/Process_TFRecords/tesst_fold_is_0', '转换后 TFRecord 文件所在的位置')
tf.app.flags.DEFINE_string('train_list', 'age_train.txt', '训练数据集文件名列表')
tf.app.flags.DEFINE_string('valid_list', 'age_val.txt', '交叉验证数据集文件名列表')
tf.app.flags.DEFINE_integer('train_shards', 10, '训练集需要划分的TFRecords文件数目')
tf.app.flags.DEFINE_integer('valid_shards', 2, '验证集需要划分的TFRecords文件数目')
tf.app.flags.DEFINE_integer('num_threads', 2, '处理数据所需要的线程数量')

FLAGS = tf.app.flags.FLAGS

TFRecords转换基本函数:

def _int64_feature(value):
    if not isinstance(value, list):
        value = [value]
    return tf.train.Feature(int64_list=tf.train.Int64List(value=value))


def _bytes_feature(value):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))


def convert_to_example(filename, image, label, hight, width):
    example = tf.train.Example(features=tf.train.Features(feature={
        'image/class/label':_int64_feature(label),
        'image/filename':_bytes_feature(str.encode(os.path.basename(filename))),
        'image/encoded':_bytes_feature(image),
        'image/hight':_int64_feature(hight),
        'image/width':_int64_feature(width)
    }))
    return example

数据预处理(变换为 Tensorflow 专用格式 TFRecords )完整代码:

# -*- coding: utf-8 -*-
# @Time    : 2019/3/16 8:50
# @Author  : Chaucer_Gxm
# @Email   : [email protected]
# @File    : Study_Data_Process.py
# @GitHub  : https://github.com/Chaucergit/Code-and-Algorithm
# @blog    : https://blog.csdn.net/qq_24819773
# @Software: PyCharm
import os
import random
import sys
import threading
import numpy as np
import tensorflow as tf
import json
from six.moves import xrange
from datetime import datetime

tf.app.flags.DEFINE_string('fold_dir', '../Folds/train_val_txt_files_per_fold/test_fold_is_0', '数据索引所在的文件夹')
tf.app.flags.DEFINE_string('data_dir', './aligned', '数据集所在的文件夹')
tf.app.flags.DEFINE_string('output_dir', '../Folds/Process_TFRecords/tesst_fold_is_0', '转换后 TFRecord 文件所在的位置')
tf.app.flags.DEFINE_string('train_list', 'age_train.txt', '训练数据集文件名列表')
tf.app.flags.DEFINE_string('valid_list', 'age_val.txt', '交叉验证数据集文件名列表')
tf.app.flags.DEFINE_integer('train_shards', 10, '训练集需要划分的TFRecords文件数目')
tf.app.flags.DEFINE_integer('valid_shards', 2, '验证集需要划分的TFRecords文件数目')
tf.app.flags.DEFINE_integer('num_threads', 2, '处理数据所需要的线程数量')

FLAGS = tf.app.flags.FLAGS
resize_hight = 256
resize_width = 256


class ImageCoder(object):

    def __init__(self):
        self._sess = tf.Session()
        self._png_data = tf.placeholder(dtype=tf.string)
        image = tf.image.decode_png(self._png_data, channels=3)
        self._png_to_jpeg = tf.image.encode_jpeg(image, format='rgb', quality=100)

        self._decode_jpeg_data = tf.placeholder(dtype=tf.string)
        self._decode_jpeg = tf.image.decode_jpeg(self._decode_jpeg_data, channels=3)
        cropped = tf.image.resize_images(self._decode_jpeg, [resize_hight, resize_width])
        cropped = tf.cast(cropped, tf.uint8)
        self._recoded = tf.image.encode_jpeg(cropped, format='rgb', quality=100)

    def png_to_jpeg(self, image_data):
        return self._sess.run(self._png_to_jpeg, feed_dict={self._png_data:image_data})

    def resample_jpeg(self, image_data):
        image = self._sess.run(self._recoded, feed_dict={self._decode_jpeg_data: image_data})
        # image = self._sess.run(self._recoded, feed_dict={self._decode_jpeg_data: image_data})
        return image


def find_images_files(list_file, data_dir):
    print('从路径 %s 寻找数据集索引所指的具体文件名与标签'% list_file)
    files_labels = [x.strip().split(' ') for x in tf.gfile.FastGFile(list_file, 'r').readlines()]
    labels = []
    filenames = []
    for path, label in files_labels:
        pic_file_patf = '%s/%s' %(data_dir, path)
        if os.path.exists(pic_file_patf):
            filenames.append(pic_file_patf)
            labels.append(label)
    # unique_labels = set(labels)
    shuffled_index = list(range(len(filenames)))
    # 定义随机种子,固定每次生成的随机数一样,进而打乱filenames的顺序
    random.seed(12345)
    random.shuffle(shuffled_index)
    filenames = [filenames[i] for i in shuffled_index]
    labels = [labels[i] for i in shuffled_index]
    return filenames, labels


def _int64_feature(value):
    if not isinstance(value, list):
        value = [value]
    return tf.train.Feature(int64_list=tf.train.Int64List(value=value))


def _bytes_feature(value):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))


def convert_to_example(filename, image, label, hight, width):
    example = tf.train.Example(features=tf.train.Features(feature={
        'image/class/label':_int64_feature(label),
        'image/filename':_bytes_feature(str.encode(os.path.basename(filename))),
        'image/encoded':_bytes_feature(image),
        'image/hight':_int64_feature(hight),
        'image/width':_int64_feature(width)
    }))
    return example


def is_png(filename):
    return '.png' in filename


def process_image(filename, coder):
    with tf.gfile.FastGFile(filename, 'rb') as f:
        image_data = f.read()
    if is_png(filename):
        print('把PNG格式文件%s转换为JPEG格式' % filename)
        image_data = coder.png_to_jpeg(image_data)
    image = coder.resample_jpeg(image_data)
    return image, resize_hight, resize_width


def process_image_files_batch(coder, thread_index, ranges, name, filenames, labels, num_shards):
    num_threads = len(ranges)
    num_shards_per_batch = int(num_shards / num_threads)
    print('此线程分担 %d 个Shards。' % num_shards_per_batch)
    shard_ranges = np.linspace(ranges[thread_index][0], ranges[thread_index][1], num_shards_per_batch + 1).astype(int)
    print('Shard的范围为:', shard_ranges)
    num_files_in_thread = ranges[thread_index][1] - ranges[thread_index][0]
    print('线程中处理图片的数量为:', num_files_in_thread)
    counter = 0
    for s in xrange(num_shards_per_batch):
        shard = thread_index * num_shards_per_batch + s
        print('%s线程%d正在生成第%d个文件' % (name, thread_index, shard))
        output_filename = '%s-%.5d-of-%.5d' % (name, shard, num_shards)
        output_file = os.path.join(FLAGS.output_dir, output_filename)
        # 定义书写 TFRecords 文件的方法
        writer = tf.python_io.TFRecordWriter(output_file)

        shard_counter = 0
        files_in_shard = np.arange(shard_ranges[s], shard_ranges[s+1], dtype=int)
        print('在Shard中的图片的索引范围为:', files_in_shard)
        for i in files_in_shard:
            # 获取单张图片的name和label
            filename = filenames[i]
            label = int(labels[i])
            # 处理单张图片
            image, hight, width = process_image(filename, coder)
            example = convert_to_example(filename, image, label, hight, width)
            writer.write(example.SerializeToString())
            shard_counter += 1
            counter += 1
        writer.close()
        print('%s 线程 %d 的第 %d 步已经写入 %d 张图片在 Batch 中' % (datetime.now(), thread_index, counter, num_files_in_thread))
        sys.stdout.flush()
        shard_counter = 0


def threading_process_images(name, filenames, labels, num_shards):
    # 首先判断文件名的数量与标签的数量是否一致
    assert len(filenames) == len(labels)

    # 生成线程锁需要运行的节点
    spacing = np.linspace(0, len(filenames), FLAGS.num_threads + 1).astype(np.int)
    ranges = []
    threads = []
    for i in xrange(len(spacing) - 1):
        ranges.append([spacing[i], spacing[i+1]])
    print('在文件序列%s使用%d个线程' % (ranges, FLAGS.num_threads))
    print('在此强制刷新缓存区')
    sys.stdout.flush()

    # 调用tf.train.Coordinator() 来创建一个线程协调器,管理启动的线程。
    coord = tf.train.Coordinator()

    # 声明一个用于处理图像基本操作的类
    coder = ImageCoder()
    threads = []

    for thread_index in xrange(len(ranges)):
        print('启动线程----%d' % thread_index)
        args = (coder, thread_index, ranges, name, filenames, labels, num_shards)
        t = threading.Thread(target=process_image_files_batch, args=args)
        t.start()
        threads.append(t)
    coord.join(threads)
    print('在%s, 完成了 %d 张图片的转换' % (datetime.now(), len(filenames)))
    print('处理 %s 完毕' % name)
    sys.stdout.flush()


def main(unuse_argv):
    # 1.通过文件夹和txt文件获取文件名与标签值列表
    filename_valid = '%s/%s' % (FLAGS.fold_dir, FLAGS.valid_list)
    filename_train = '%s/%s' % (FLAGS.fold_dir, FLAGS.train_list)

    print(filename_valid)
    print('******************************************************')
    print(filename_train)
    # 2.定义读取文件夹中每个图像的函数,对数据集中的每个图像进行读取,返回每个图像的文件路径+名称,以及对应的标签

    filename_valid, labels_valid = find_images_files(filename_valid, FLAGS.data_dir)
    # 线程函数去执行转换格式
    threading_process_images('valid_data', filename_valid, labels_valid, FLAGS.valid_shards)

    filename_train, labels_train = find_images_files(filename_train, FLAGS.data_dir)
    # 线程函数去执行转换格式
    threading_process_images('train_data', filename_train, labels_train, FLAGS.train_shards)

    # 5.创建书写 json 文件的文件夹
    if os.path.exists(FLAGS.output_dir) is False:
        print('创建文件夹%s' % FLAGS.output_dir)
        os.makedirs(FLAGS.output_dir)

    # 4.获取书写 json 文件的参数
    len_labels_valid = len(labels_valid)
    unique_labels_valid = set(labels_valid)
    len_labels_train = len(labels_train)
    unique_labels_train = set(labels_train)
    out_file = os.path.join(FLAGS.output_dir, 'md.json')
    md = {'num_valid_shards': FLAGS.valid_shards,
          'num_train_shards':FLAGS.train_shards,
          'valid_counts':len_labels_valid,
          'train_counts':len_labels_train,
          'timestamp': str(datetime.now()),
          'nlabels':len(unique_labels_train)}
    with open(out_file, 'w') as f:
        json.dump(md, f)
    print('数据转换完成')

if __name__ == '__main__':
    tf.app.run()

运行过程与结果:

......
启动线程----0
此线程分担 5 个Shards。
启动线程----1
Shard的范围为: [   0 1182 2364 3546 4728 5911]
此线程分担 5 个Shards。
Shard的范围为: [ 5911  7093  8275  9458 10640 11823]
线程中处理图片的数量为: 5912
train_data线程1正在生成第5个文件
线程中处理图片的数量为: 5911
train_data线程0正在生成第0个文件
在Shard中的图片的索引范围为: [5911 5912 5913 ... 7090 7091 7092]
在Shard中的图片的索引范围为: [   0    1    2 ... 1179 1180 1181]
2019-03-16 13:14:11.869724 线程 0 的第 1182 步已经写入 5911 张图片在 Batch 中
train_data线程0正在生成第1个文件
在Shard中的图片的索引范围为: [1182 1183 1184 ... 2361 2362 2363]
2019-03-16 13:14:12.719736 线程 1 的第 1182 步已经写入 5912 张图片在 Batch 中
train_data线程1正在生成第6个文件
在Shard中的图片的索引范围为: [7093 7094 7095 ... 8272 8273 8274]
2019-03-16 13:14:47.542721 线程 0 的第 2364 步已经写入 5911 张图片在 Batch 中
train_data线程0正在生成第2个文件
在Shard中的图片的索引范围为: [2364 2365 2366 ... 3543 3544 3545]
2019-03-16 13:14:47.946224 线程 1 的第 2364 步已经写入 5912 张图片在 Batch 中
train_data线程1正在生成第7个文件
在Shard中的图片的索引范围为: [8275 8276 8277 ... 9455 9456 9457]
2019-03-16 13:15:23.292752 线程 0 的第 3546 步已经写入 5911 张图片在 Batch 中
train_data线程0正在生成第3个文件
在Shard中的图片的索引范围为: [3546 3547 3548 ... 4725 4726 4727]
2019-03-16 13:15:23.852874 线程 1 的第 3547 步已经写入 5912 张图片在 Batch 中
train_data线程1正在生成第8个文件
在Shard中的图片的索引范围为: [ 9458  9459  9460 ... 10637 10638 10639]
2019-03-16 13:15:58.858819 线程 0 的第 4728 步已经写入 5911 张图片在 Batch 中
train_data线程0正在生成第4个文件
在Shard中的图片的索引范围为: [4728 4729 4730 ... 5908 5909 5910]
2019-03-16 13:15:59.038819 线程 1 的第 4729 步已经写入 5912 张图片在 Batch 中
train_data线程1正在生成第9个文件
在Shard中的图片的索引范围为: [10640 10641 10642 ... 11820 11821 11822]
2019-03-16 13:16:34.308167 线程 0 的第 5911 步已经写入 5911 张图片在 Batch 中
2019-03-16 13:16:35.276127 线程 1 的第 5912 步已经写入 5912 张图片在 Batch 中
在2019-03-16 13:16:36.276222, 完成了 11823 张图片的转换
处理 train_data 完毕
数据转换完成

Tensorflow实现性别年龄检测项目前奏——数据预处理(转换数据集为 TFRecords 格式)_第2张图片

 

你可能感兴趣的:(深度学习,Tensorflow,数据预处理)