tf.data 加载图片

代码


# -*- coding: utf-8 -*-
"""
Created on  2020/11/20 14:24
@Author: CY
@email: 5844104706@qq.com
"""
import tensorflow as tf

AUTOTUNE = tf.data.experimental.AUTOTUNE
print("# 检索图片")
import pathlib

print('下载了 218 MB 花卉照片 ...')
data_root_orig = tf.keras.utils.get_file(
    origin='https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
    fname='flower_photos', untar=True)
data_root = pathlib.Path(data_root_orig)
print('花卉图片数据所在路径:', data_root)
for item in data_root.iterdir():
    print('数据子路径:', item)

import random

all_image_paths = list(data_root.glob('*/*'))
all_image_paths = [str(path) for path in all_image_paths]
random.shuffle(all_image_paths)

image_count = len(all_image_paths)
print('文件数:', image_count)
print('前10图片路径:', all_image_paths[:10])

print("#检查图片")
import os

attributions = (data_root / "LICENSE.txt").open(encoding='utf-8').readlines()[4:]
attributions = [line.split(' CC-BY') for line in attributions]
attributions = dict(attributions)

import IPython.display as display


def caption_image(image_path):
    image_rel = pathlib.Path(image_path).relative_to(data_root)
    return "Image (CC BY 2.0) " + ' - '.join(attributions[str(image_rel).replace("\\", "/")].split('-')[:-1])


for n in range(3):
    image_path = random.choice(all_image_paths)
    display.display(display.Image(image_path))
    print(caption_image(image_path))
    print()

print('# 确定每张图片的标签')
label_names = sorted(item.name for item in data_root.glob('*/') if item.is_dir())
print(label_names)
print('# 确定每张图片的标签索引')
label_to_index = dict((name, index) for index, name in enumerate(label_names))
print(label_to_index)
# 创建一个列表,包含每个文件的标签索引
all_image_labels = [label_to_index[pathlib.Path(path).parent.name]
                    for path in all_image_paths]

print("前10 First 10 labels indices: ", all_image_labels[:10])

print("# 加载和格式化图片:")
img_path = all_image_paths[0]
print(img_path)
img_raw = tf.io.read_file(img_path)
print('第一张图片前100 数据:', repr(img_raw)[:100] + "...")

print('将解码为图像 tensor(张量)')
img_tensor = tf.image.decode_image(img_raw)
print('img_tensor:', img_tensor.shape, 'img_tensor.dtype', img_tensor.dtype)

print("# 根据你的模型调整其大小:")
img_final = tf.image.resize(img_tensor, [192, 192])
img_final = img_final / 255.0
print(img_final.shape)
print(img_final.numpy().min())
print(img_final.numpy().max())


def preprocess_image(image):
    image = tf.image.decode_jpeg(image, channels=3)
    image = tf.image.resize(image, [192, 192])
    image /= 255.0  # normalize to [0,1] range

    return image


def load_and_preprocess_image(path):
    image = tf.io.read_file(path)
    return preprocess_image(image)

import matplotlib.pyplot as plt

image_path = all_image_paths[0]
label = all_image_labels[0]

plt.imshow(load_and_preprocess_image(img_path))
plt.grid(False)
plt.xlabel(caption_image(img_path))
plt.title(label_names[label].title())
print()

print('构建一个 图片数据集tf.data.Dataset')
path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)
#创建一个新的数据集,通过在路径数据集上映射 preprocess_image 来动态加载和格式化图片
image_ds = path_ds.map(load_and_preprocess_image, num_parallel_calls=AUTOTUNE)

import matplotlib.pyplot as plt

plt.figure(figsize=(8,8))
for n, image in enumerate(image_ds.take(4)):
    plt.subplot(2,2,n+1)
    plt.imshow(image)
    plt.grid(False)
    plt.xticks([])
    plt.yticks([])
    plt.xlabel(caption_image(all_image_paths[n]))
    plt.show()

# 图片, 标签)对数据集
label_ds = tf.data.Dataset.from_tensor_slices(tf.cast(all_image_labels, tf.int64))
for label in label_ds.take(10):
    print(label_names[label.numpy()])

#这些数据集顺序相同,你可以将他们打包在一起得到一个(图片, 标签)对数据集
#当你拥有形似 all_image_labels 和 all_image_paths 的数组,
# tf.data.dataset.Dataset.zip 的替代方法是将这对数组切片
image_label_ds = tf.data.Dataset.zip((image_ds, label_ds))
print(image_label_ds)

ds = tf.data.Dataset.from_tensor_slices((all_image_paths, all_image_labels))

# 元组被解压缩到映射函数的位置参数中
def load_and_preprocess_from_path_label(path, label):
    return load_and_preprocess_image(path), label

image_label_ds = ds.map(load_and_preprocess_from_path_label)
print(image_label_ds)

#训练的基本方法
# 要使用此数据集训练模型,你将会想要数据
### 被充分打乱。
### 被分割为 batch。
### 永远重复。
### 尽快提供 batch。
BATCH_SIZE = 32

# 设置一个和数据集大小一致的 shuffle buffer size(随机缓冲区大小)以保证数据
# 被充分打乱。
ds = image_label_ds.shuffle(buffer_size=image_count)
ds = ds.repeat()
ds = ds.batch(BATCH_SIZE)
# 当模型在训练的时候,`prefetch` 使数据集在后台取得 batch。
ds = ds.prefetch(buffer_size=AUTOTUNE)
print(ds)
#顺序很重要。
## 在 .repeat 之后 .shuffle,会在 epoch 之间打乱数据(当有些数据出现两次的时候,其他数据还没有出现过)。
## 在 .batch 之后 .shuffle,会打乱 batch 的顺序,但是不会在 batch 之间打乱数据。
#你在完全打乱中使用和数据集大小一样的 buffer_size(缓冲区大小)。较大的缓冲区大小提供更好的随机化,但使用更多的内存,直到超过数据集大小。
#在从随机缓冲区中拉取任何元素前,要先填满它。所以当你的 Dataset(数据集)启动的时候一个大的 buffer_size(缓冲区大小)可能会引起延迟。
#在随机缓冲区完全为空之前,被打乱的数据集不会报告数据集的结尾。Dataset(数据集)由 .repeat 重新启动,导致需要再次等待随机缓冲区被填满。
#最后一点可以通过使用 tf.data.Dataset.apply 方法和融合过的 tf.data.experimental.shuffle_and_repeat 函数来解决:
ds = image_label_ds.apply(
    tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))
ds = ds.batch(BATCH_SIZE)
ds = ds.prefetch(buffer_size=AUTOTUNE)
print(ds)

print("传递数据集至模型")
mobile_net = tf.keras.applications.MobileNetV2(input_shape=(192, 192, 3), include_top=False)
mobile_net.trainable=False
# 该模型期望它的输出被标准化至 [-1,1] 范围内
#help(keras_applications.mobilenet_v2.preprocess_input)
def change_range(image,label):
    return 2*image-1, label

keras_ds = ds.map(change_range)
# 数据集可能需要几秒来启动,因为要填满其随机缓冲区。
image_batch, label_batch = next(iter(keras_ds))
feature_map_batch = mobile_net(image_batch)
print(feature_map_batch.shape)

# 构建一个包装了 MobileNet 的模型并在 tf.keras.layers.Dense
# 输出层之前使用 tf.keras.layers.GlobalAveragePooling2D 来平均那些空间向量:
model = tf.keras.Sequential([
    mobile_net,
    tf.keras.layers.GlobalAveragePooling2D(),
    tf.keras.layers.Dense(len(label_names), activation = 'softmax')])
# 现在它产出符合预期 shape(维数)的输出:
logit_batch = model(image_batch).numpy()

print("min logit:", logit_batch.min())
print("max logit:", logit_batch.max())
print()

print("Shape:", logit_batch.shape)
model.compile(optimizer=tf.keras.optimizers.Adam(),
              loss='sparse_categorical_crossentropy',
              metrics=["accuracy"])
len(model.trainable_variables)
model.summary()

steps_per_epoch=tf.math.ceil(len(all_image_paths)/BATCH_SIZE).numpy()
print(steps_per_epoch)
model.fit(ds, epochs=1, steps_per_epoch=3)

print("性能")
import time
default_timeit_steps = 2*steps_per_epoch+1

def timeit(ds, steps=default_timeit_steps):
    overall_start = time.time()
    # 在开始计时之前
    # 取得单个 batch 来填充 pipeline(管道)(填充随机缓冲区)
    it = iter(ds.take(steps+1))
    next(it)

    start = time.time()
    for i,(images,labels) in enumerate(it):
        if i%10 == 0:
            print('.',end='')
    print()
    end = time.time()

    duration = end-start
    print("{} batches: {} s".format(steps, duration))
    print("{:0.5f} Images/s".format(BATCH_SIZE*steps/duration))
    print("Total time: {}s".format(end-overall_start))

ds = image_label_ds.apply(
    tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))
ds = ds.batch(BATCH_SIZE).prefetch(buffer_size=AUTOTUNE)
timeit(ds)

print("缓存")
ds = image_label_ds.cache()
ds = ds.apply(
    tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))
ds = ds.batch(BATCH_SIZE).prefetch(buffer_size=AUTOTUNE)
timeit(ds)
print("如果内存不够容纳数据,使用一个缓存文件")
ds = image_label_ds.cache(filename='./tmp/cache.tf-data')
ds = ds.apply(
    tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))
ds = ds.batch(BATCH_SIZE).prefetch(1)
timeit(ds)

print("TFRecord 文件")
#TFRecord 文件是一种用来存储一串二进制 blob 的简单格式。通过将多个示例打包进同一个文件内,
# TensorFlow 能够一次性读取多个示例,当使用一个远程存储服务,如 GCS 时,这对性能来说尤其重要
#原始图片数据中构建出一个 TFRecord 文件
print("原始图片数据构建TFRecord 文件")
image_ds = tf.data.Dataset.from_tensor_slices(all_image_paths).map(tf.io.read_file)
tfrec = tf.data.experimental.TFRecordWriter('images.tfrec')
tfrec.write(image_ds)
# TFRecord 文件读取的数据集,并使用你之前定义的 preprocess_image 函数对图像进行解码/重新格式化:
print("从TFRecord 文件读取的数据集")
image_ds = tf.data.TFRecordDataset('images.tfrec').map(preprocess_image)

#压缩该数据集和你之前定义的标签数据集以得到期望的 (图片,标签) 对
print("压缩该数据集和期望的 (图片,标签) 对")
ds = tf.data.Dataset.zip((image_ds, label_ds))
ds = ds.apply(
    tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))
ds=ds.batch(BATCH_SIZE).prefetch(AUTOTUNE)
timeit(ds)

print("序列化的 Tensor(张量)")
paths_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)
image_ds = paths_ds.map(load_and_preprocess_image)
# 有一个 tensor(张量)数据集,而不是一个 .jpeg 字符串数据集
# 将此序列化至一个 TFRecord 文件你首先将该 tensor(张量)数据集转化为一个字符串数据集
ds = image_ds.map(tf.io.serialize_tensor)
tfrec = tf.data.experimental.TFRecordWriter('images.tfrec')
tfrec.write(ds)
#有了被缓存的预处理,就能从 TFrecord 文件高效地加载数据——只需记得在使用它之前反序列化
ds = tf.data.TFRecordDataset('images.tfrec')

def parse(x):
    result = tf.io.parse_tensor(x, out_type=tf.float32)
    result = tf.reshape(result, [192, 192, 3])
    return result

ds = ds.map(parse, num_parallel_calls=AUTOTUNE)
ds = tf.data.Dataset.zip((ds, label_ds))
ds = ds.apply(
    tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))
ds=ds.batch(BATCH_SIZE).prefetch(AUTOTUNE)
timeit(ds)


运行结果(删除了警告)

# 检索图片
下载了 218 MB 花卉照片 ...
花卉图片数据所在路径: C:\Users\Administrator\.keras\datasets\flower_photos
数据子路径: C:\Users\Administrator\.keras\datasets\flower_photos\daisy
数据子路径: C:\Users\Administrator\.keras\datasets\flower_photos\dandelion
数据子路径: C:\Users\Administrator\.keras\datasets\flower_photos\LICENSE.txt
数据子路径: C:\Users\Administrator\.keras\datasets\flower_photos\roses
数据子路径: C:\Users\Administrator\.keras\datasets\flower_photos\sunflowers
数据子路径: C:\Users\Administrator\.keras\datasets\flower_photos\tulips
文件数: 3670
前10图片路径: ['C:\\Users\\Administrator\\.keras\\datasets\\flower_photos\\daisy\\721595842_bacd80a6ac.jpg', 'C:\\Users\\Administrator\\.keras\\datasets\\flower_photos\\daisy\\2590291468_2635d3e4e0_n.jpg', 'C:\\Users\\Administrator\\.keras\\datasets\\flower_photos\\tulips\\8523133474_d2c0845b54.jpg', 'C:\\Users\\Administrator\\.keras\\datasets\\flower_photos\\roses\\298670754_f25edda891.jpg', 'C:\\Users\\Administrator\\.keras\\datasets\\flower_photos\\tulips\\9019694597_2d3bbedb17.jpg', 'C:\\Users\\Administrator\\.keras\\datasets\\flower_photos\\daisy\\4694730335_2553e77aa5_z.jpg', 'C:\\Users\\Administrator\\.keras\\datasets\\flower_photos\\roses\\14145188939_b4de638bd3_n.jpg', 'C:\\Users\\Administrator\\.keras\\datasets\\flower_photos\\sunflowers\\13648603305_1268eda8b7_n.jpg', 'C:\\Users\\Administrator\\.keras\\datasets\\flower_photos\\sunflowers\\877083343_e3338c4125.jpg', 'C:\\Users\\Administrator\\.keras\\datasets\\flower_photos\\roses\\15186434972_e353da940a.jpg']
#检查图片
<IPython.core.display.Image object>
Image (CC BY 2.0)  by tcfairyqueen 

<IPython.core.display.Image object>
Image (CC BY 2.0)  by ellhoisa 

<IPython.core.display.Image object>
Image (CC BY 2.0)  by Holly Victoria Norval 

# 确定每张图片的标签
['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']
# 确定每张图片的标签索引
{'daisy': 0, 'dandelion': 1, 'roses': 2, 'sunflowers': 3, 'tulips': 4}
前10 First 10 labels indices:  [0, 0, 4, 2, 4, 0, 2, 3, 3, 2]
# 加载和格式化图片:
C:\Users\Administrator\.keras\datasets\flower_photos\daisy\721595842_bacd80a6ac.jpg
 
第一张图片前100 数据: <tf.Tensor: shape=(), dtype=string, numpy=b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x01,\x01,\x...
将解码为图像 tensor(张量)
img_tensor: (335, 500, 3) img_tensor.dtype <dtype: 'uint8'>
# 根据你的模型调整其大小:
(192, 192, 3)
0.0
0.82736516

构建一个 图片数据集tf.data.Dataset
daisy
daisy
tulips
roses
tulips
daisy
roses
sunflowers
sunflowers
roses
<ZipDataset shapes: ((192, 192, 3), ()), types: (tf.float32, tf.int64)>
<MapDataset shapes: ((192, 192, 3), ()), types: (tf.float32, tf.int32)>
<PrefetchDataset shapes: ((None, 192, 192, 3), (None,)), types: (tf.float32, tf.int32)>
<PrefetchDataset shapes: ((None, 192, 192, 3), (None,)), types: (tf.float32, tf.int32)>
传递数据集至模型
 
(32, 6, 6, 1280)
min logit: 0.0049569164
max logit: 0.8101183

Shape: (32, 5)
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
mobilenetv2_1.00_192 (Functi (None, 6, 6, 1280)        2257984   
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280)              0         
_________________________________________________________________
dense (Dense)                (None, 5)                 6405      
=================================================================
Total params: 2,264,389
Trainable params: 6,405
Non-trainable params: 2,257,984
_________________________________________________________________
115.0
3/3 [==============================] - 1s 411ms/step - loss: 2.1935 - accuracy: 0.1667
性能
........................
231.0 batches: 17.713555097579956 s
417.30753 Images/s
Total time: 25.449723482131958s
缓存
........................
231.0 batches: 1.6241695880889893 s
4551.24887 Images/s
Total time: 9.2717866897583s
如果内存不够容纳数据,使用一个缓存文件
........................
231.0 batches: 9.51319932937622 s
777.02566 Images/s
Total time: 16.94539761543274s
TFRecord 文件
原始图片数据构建TFRecord 文件
从TFRecord 文件读取的数据集
压缩该数据集和期望的 (图片,标签) 对
........................
231.0 batches: 14.919986486434937 s
495.44281 Images/s
Total time: 21.482720613479614s
序列化的 Tensor(张量)
........................
231.0 batches: 6.6847569942474365 s
1105.79936 Images/s
Total time: 9.348265171051025s
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ynchyong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值