【问题标题】:How to read (decode) .tfrecords file, see the images inside and do augmentation?如何读取(解码).tfrecords 文件,查看里面的图像并进行扩充?
【发布时间】:2020-11-25 14:57:34
【问题描述】:

我有一个.tfrecords 文件,我想提取、查看文件中的图像并对其进行扩充。 我正在使用https://colab.research.google.com TensorFlow 版本:2.3.0

对于下面的代码

raw_dataset = tf.data.TFRecordDataset("*path.tfrecords")

for raw_record in raw_dataset.take(1):
    example = tf.train.Example()
    example.ParseFromString(raw_record.numpy())
    print(example)

我面临以下输出:

features {
  feature {
    key: "depth"
    value {
      int64_list {
        value: 3
      }
    }
  }
  feature {
    key: "height"
    value {
      int64_list {
        value: 333
      }
    }
  }
  feature {
    key: "image_raw"
    value {
      bytes_list {
        value:
      }
    }
  }
  feature {
    key: "label"
    value {
      int64_list {
        value: 16
      }
    }
  }
  feature {
    key: "width"
    value {
      int64_list {
        value: 500
      }
    }
  }
}

【问题讨论】:

  • 您想从 .tfrecords 中查看图像吗?
  • 没错,我想从文件中打印一两张图片! @AkashDesai
  • @lko-SOF 您的 .tfrecord 文件是保密的吗?你能以某种方式分享它的一小部分,还是全部?这样我们就可以尝试编写一个从中提取图像的脚本。
  • 您好@Arty,请在以下链接中找到上传的数据集drive.google.com/file/d/1TnXFf-fodf9Z33g4pheSD4-Wq1Z87pzd/…

标签: python tensorflow decode tfrecord data-augmentation


【解决方案1】:

这是一个简单的代码,可以将您的 .tfrecord 图像提取为 .png 格式。

要运行下一个代码,您需要通过pip install tensorflow tensorflow_addons pillow numpy matplotlib 安装一次 pip 模块。

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 
import tensorflow as tf, PIL.Image, numpy as np

raw_dataset = tf.data.TFRecordDataset('max_32_set.tfrecords')

for i, raw_record in enumerate(raw_dataset.take(3)):
    example = tf.train.Example()
    example.ParseFromString(raw_record.numpy())
    info = {}
    for k, v in example.features.feature.items():
        if k == 'image_raw':
            info[k] = v.bytes_list.value[0]
        elif k in ['depth', 'height', 'width']:
            info[k] = v.int64_list.value[0]
    img_arr = np.frombuffer(info['image_raw'], dtype = np.uint8).reshape(
        info['height'], info['width'], info['depth']
    )
    # You can use img_arr numpy array above to directly augment/preprocess
    # your image without saving it to .png.
    img = PIL.Image.fromarray(img_arr)
    img.save(f'max_32_set.tfrecords.{str(i).zfill(5)}.png')

数据集中的第一张图片:

以下是为每个标签绘制图像数量的代码。 max_32_set.tfrecords 文件中的标签表示为整数(不是字符串名称),标签名称可能位于单独的小文件中,其中包含有关数据集的元信息。

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 
import tensorflow as tf, numpy as np, matplotlib.pyplot as plt

raw_dataset = tf.data.TFRecordDataset('max_32_set.tfrecords')

labels_cnts = {}
for i, raw_record in enumerate(raw_dataset.as_numpy_iterator()):
    example = tf.train.Example()
    example.ParseFromString(raw_record)
    info = {}
    for k, v in example.features.feature.items():
        if k == 'label':
            info[k] = v.int64_list.value[0]
    labels_cnts[info['label']] = labels_cnts.get(info['label'], 0) + 1

x, y = zip(*sorted(labels_cnts.items(), key = lambda e: e[0]))
plt.xlabel('label')
plt.ylabel('num images')
plt.plot(x, y)
plt.xticks(x)
plt.show()

max_32_set.tfrecords 的绘图:

接下来的代码使用高斯噪声和高斯模糊进行增强,增强的 tfrecord 数据集保存到max_32_set.augmented.tfrecords 文件:

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 
import tensorflow as tf, tensorflow_addons as tfa, PIL.Image, numpy as np, math

c_inp_fname = 'max_32_set.tfrecords'
c_out_fname = 'max_32_set.augmented.tfrecords'
c_augment_types = ('noise', 'blur', 'noise_blur', 'noise_blur_mirror')
c_res_class_size = None # If None then auto configured to maximal class size

def calc_labels():
    raw_dataset = tf.data.TFRecordDataset(c_inp_fname)
    cnts, labels = {}, []
    for i, raw_record in enumerate(raw_dataset):
        example = tf.train.Example()
        example.ParseFromString(raw_record.numpy())
        label = example.features.feature['label'].int64_list.value[0]
        cnts[label] = cnts.get(label, 0) + 1
        labels.append(label)
    return cnts, labels

def img_gen():
    raw_dataset = tf.data.TFRecordDataset(c_inp_fname)
    for i, raw_record in enumerate(raw_dataset):
        example = tf.train.Example()
        example.ParseFromString(raw_record.numpy())
        info = {}
        for k, v in example.features.feature.items():
            if k == 'image_raw':
                info[k] = v.bytes_list.value[0]
            elif k in ['depth', 'height', 'width']:
                info[k] = v.int64_list.value[0]
        img_arr = np.frombuffer(info['image_raw'], dtype = np.uint8).reshape(
            info['height'], info['width'], info['depth']
        )
        yield example, img_arr
        
def gaussian_noise(inp, stddev):
    noise = tf.random.normal(shape = tf.shape(inp), mean = 0.0, stddev = stddev, dtype = inp.dtype)
    return inp + noise
        
def augment(a, cnt):
    min_noise_stddev, max_noise_stddev = 5., 20.
    blur_kern, min_blur_stddev, max_blur_stddev = 3, 1., 5.
    
    assert cnt >= 1
    pad_a = lambda x: np.pad(x, (
        (0, 2 ** math.ceil(math.log(x.shape[0]) / math.log(2)) - x.shape[0]),
        (0, 2 ** math.ceil(math.log(x.shape[1]) / math.log(2)) - x.shape[1]),
        (0, 0)), constant_values = 0)
    post_a = lambda x: np.clip(x[:a.shape[0], :a.shape[1]], 0, 255).astype(np.uint8)
    yield 'orig', a
    cnt -= 1
    res = []
    fcnt = math.ceil(cnt / len(c_augment_types))
    linsp = lambda l, r, c: [(l + (i + 1) * (r - l) / (c + 1)) for i in range(c)]
    for noise_stddev, blur_stddev in zip(linsp(min_noise_stddev, max_noise_stddev, fcnt), linsp(min_blur_stddev, max_blur_stddev, fcnt)):
        if 'noise' in c_augment_types:
            #yield 'noise', post_a(tf.keras.layers.GaussianNoise(stddev = noise_stddev)(prep_a, training = True).numpy())
            res.append(('noise', post_a(gaussian_noise(a.astype(np.float32), stddev = noise_stddev).numpy())))
        if 'blur' in c_augment_types:
            res.append(('blur', post_a(tfa.image.gaussian_filter2d(pad_a(a).astype(np.float32), filter_shape = blur_kern, sigma = blur_stddev).numpy())))
        if 'noise_blur' in c_augment_types or 'noise_blur_mirror' in c_augment_types:
            nbr = post_a(tfa.image.gaussian_filter2d(
                pad_a(gaussian_noise(a.astype(np.float32), stddev = noise_stddev).numpy()),
                filter_shape = blur_kern, sigma = blur_stddev).numpy())
            if 'noise_blur' in c_augment_types:
                res.append(('noise_blur', nbr))
            if 'noise_blur_mirror' in c_augment_types:
                res.append(('noise_blur_mirror', tf.image.flip_left_right(nbr).numpy().astype(np.uint8)))
    assert cnt <= len(res) <= cnt + len(c_augment_types), (cnt, len(res), len(c_augment_types))
    yield from res[:cnt]

def process():
    labels_cnts, labels = calc_labels()
    max_class_size = max(labels_cnts.values())
    if c_res_class_size is not None:
        assert max_class_size <= c_res_class_size, f'Maximal class size is {max_class_size}, while requested res class size is smaller, {c_res_class_size}!'
        class_size = c_res_class_size
    else:
        class_size = max_class_size
    cur_labels_cnts = {}
    for iimg, (proto, imga) in enumerate(img_gen()):
        label = proto.features.feature['label'].int64_list.value[0]
        cur_labels_cnts[label] = cur_labels_cnts.get(label, 0) + 1
        need_cnt = class_size // labels_cnts[label] + int(cur_labels_cnts[label] <= class_size % labels_cnts[label])
        for iaug, (taug, aug) in enumerate(augment(imga, need_cnt)):
            #PIL.Image.fromarray(aug).save(f'max_32_set.tfrecords.aug.{str(iimg).zfill(5)}.{iaug}_{taug}.png')
            protoc = type(proto)()
            protoc.ParseFromString(proto.SerializeToString())
            protoc.features.feature['image_raw'].bytes_list.value[0] = aug.tobytes()
            yield protoc.SerializeToString()
        if (iimg % 10) == 0:
            print(iimg, ' ', sep = '', end = '', flush = True)
            
def main():
    assert tf.executing_eagerly()
    tf.data.experimental.TFRecordWriter(c_out_fname).write(
        tf.data.TFRecordDataset.from_generator(process, tf.string)
    )

main()

增强图像示例:

原文:

噪音:

模糊:

噪声模糊:

噪声模糊镜像:

增强后每个标签的图像数量(每个标签精确平衡 30 张图像):


与上述相同的扩充,但对于带有标签图像的输入和输出文件夹,而不是 TFRecordDataset,将 c_inp_dirc_out_dir 更改为您的文件夹路径:

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 
import tensorflow as tf, tensorflow_addons as tfa, PIL.Image, numpy as np, math, matplotlib.pyplot as plt

c_inp_dir = './images/'
c_out_dir = './images_out/'
c_augment_types = ('noise', 'blur', 'noise_blur', 'noise_blur_mirror')
c_res_class_size = None # If None then auto configured to maximal class size

def calc_labels(dirn = None):
    if dirn is None:
        dirn = c_inp_dir
    cnts, labels = {}, []
    for label in sorted(os.listdir(f'{dirn}')):
        label = int(label)
        labels.append(label)
        cnts[label] = len(os.listdir(f'{dirn}/{label}/'))
    return cnts, labels

def img_gen():
    cnts = {}
    for label in sorted(os.listdir(c_inp_dir)):
        label = int(label)
        for fname in sorted(os.listdir(f'{c_inp_dir}/{label}/')):
            img_arr = np.array(PIL.Image.open(f'{c_inp_dir}/{label}/{fname}'))
            yield label, img_arr, fname
        
def gaussian_noise(inp, stddev):
    noise = tf.random.normal(shape = tf.shape(inp), mean = 0.0, stddev = stddev, dtype = inp.dtype)
    return inp + noise
        
def augment(a, cnt):
    min_noise_stddev, max_noise_stddev = 5., 20.
    blur_kern, min_blur_stddev, max_blur_stddev = 3, 1., 5.
    
    assert cnt >= 1
    pad_a = lambda x: np.pad(x, (
        (0, 2 ** math.ceil(math.log(x.shape[0]) / math.log(2)) - x.shape[0]),
        (0, 2 ** math.ceil(math.log(x.shape[1]) / math.log(2)) - x.shape[1]),
        (0, 0)), constant_values = 0)
    post_a = lambda x: np.clip(x[:a.shape[0], :a.shape[1]], 0, 255).astype(np.uint8)
    yield 'orig', a
    cnt -= 1
    res = []
    fcnt = math.ceil(cnt / len(c_augment_types))
    linsp = lambda l, r, c: [(l + (i + 1) * (r - l) / (c + 1)) for i in range(c)]
    for noise_stddev, blur_stddev in zip(linsp(min_noise_stddev, max_noise_stddev, fcnt), linsp(min_blur_stddev, max_blur_stddev, fcnt)):
        if 'noise' in c_augment_types:
            #yield 'noise', post_a(tf.keras.layers.GaussianNoise(stddev = noise_stddev)(prep_a, training = True).numpy())
            res.append(('noise', post_a(gaussian_noise(a.astype(np.float32), stddev = noise_stddev).numpy())))
        if 'blur' in c_augment_types:
            res.append(('blur', post_a(tfa.image.gaussian_filter2d(pad_a(a).astype(np.float32), filter_shape = blur_kern, sigma = blur_stddev).numpy())))
        if 'noise_blur' in c_augment_types or 'noise_blur_mirror' in c_augment_types:
            nbr = post_a(tfa.image.gaussian_filter2d(
                pad_a(gaussian_noise(a.astype(np.float32), stddev = noise_stddev).numpy()),
                filter_shape = blur_kern, sigma = blur_stddev).numpy())
            if 'noise_blur' in c_augment_types:
                res.append(('noise_blur', nbr))
            if 'noise_blur_mirror' in c_augment_types:
                res.append(('noise_blur_mirror', tf.image.flip_left_right(nbr).numpy().astype(np.uint8)))
    assert cnt <= len(res) <= cnt + len(c_augment_types), (cnt, len(res), len(c_augment_types))
    yield from res[:cnt]

def process():
    labels_cnts, labels = calc_labels()
    max_class_size = max(labels_cnts.values())
    if c_res_class_size is not None:
        assert max_class_size <= c_res_class_size, f'Maximal class size is {max_class_size}, while requested res class size is smaller, {c_res_class_size}!'
        class_size = c_res_class_size
    else:
        class_size = max_class_size
    
    cur_labels_cnts = {}
    for iimg, (label, imga, fname) in enumerate(img_gen()):
        os.makedirs(f'{c_out_dir}/{label}/', exist_ok = True)
        cur_labels_cnts[label] = cur_labels_cnts.get(label, 0) + 1
        need_cnt = class_size // labels_cnts[label] + int(cur_labels_cnts[label] <= class_size % labels_cnts[label])
        for iaug, (taug, aug) in enumerate(augment(imga, need_cnt)):
            PIL.Image.fromarray(aug).save(f'{c_out_dir}/{label}/{fname}.{iaug}_{taug}.png')
        if (iimg % 10) == 0:
            print(iimg, ' ', sep = '', end = '', flush = True)
            
def plot_cnts(dirn):
    labels_cnts = calc_labels(dirn)[0]
    x, y = zip(*sorted(labels_cnts.items(), key = lambda e: e[0]))
    plt.xlabel('label')
    plt.ylabel('num images')
    plt.plot(x, y)
    plt.xticks(x)
    plt.show()
            
def main():
    process()
    plot_cnts(c_inp_dir)
    plot_cnts(c_out_dir)

main()

【讨论】:

  • 您好@Arty,您是否可以向您展示如何做同样的事情,但如果图像位于 5 个文件夹中。每个 i 文件夹都有 i 类图像。需要考虑的是,在这种情况下,图像是 .jpg
  • @Iko-SOF 如您所见,目前我使用tf.data.TFRecordDataset 作为输入和输出。对于您目前的情况,您想要什么?据我了解,您不再拥有 TFRecordDataset,而只是包含图像的文件夹。输出呢,你希望它是哪种格式?带有图像或输出的文件夹也应为 TFRecordDataset?
  • 感谢您的快速回复@Arty,输出将再次以 .jpg 格式(因此不再有 tfrecord 数据集),我认为最好在单独的问题中进行,无需打扰你这里的答案。能否请您在下面的链接中提出新问题?
  • @Iko-SOF 我想我会在这里扩展我当前的答案,并采用您的文件夹案例,因为大部分代码都是相同的,我认为 StackOverflow 不适合复制从一个答案到另一个答案的几乎整个代码都做了一些小的修改。 StackOverflow 倾向于提供一个丰富的答案并将其他问题标记为重复。
  • @Iko-SOF 刚刚更新了我上面的答案。在它的最后添加了一段代码,用于您的带有图像的文件夹的情况。您可以在代码的开头看到 c_inp_dirc_out_dir 变量,其中包含输入/输出文件夹的路径。该代码在开始时进行增强,然后绘制两个图,首先是未修改的图像计数,当您关闭窗口后,它会绘制第二个图,其中包含增强的图像计数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-20
相关资源
最近更新 更多