Наборы данных Tensorflow: Обрезка/изменение размера изображений на пакет после набора данных.пакет()

#python #tensorflow #tensorflow-datasets

Вопрос:

Можно ли обрезать/изменять размер изображений за пакет ?

Я использую API набора данных Tensorflow, как показано ниже:

 dataset = dataset.shuffle().repeat().batch(batch_size, drop_remainder=True)
 

Я хочу, чтобы в пакете все изображения имели одинаковый размер. Однако в разных партиях он может иметь разные размеры.

Например, 1-я партия содержит все изображения формы (размер пакета, 300, 300, 3). Следующая партия может содержать изображения формы (размер пакета, 224, 224, 3). Другая партия может иметь изображения формы (размер пакета, 400, 400, 3).

В принципе, я хочу иметь пакеты в форме dyman, однако все изображения в пакете имеют статические формы.

Если мы поступим так, как следует:

 dataset = dataset.shuffle().repeat().batch(batch_size, drop_remainder=True).map(lambda x, y: map_fn(x, y))
 

Применяется ли вышеуказанная функция .map() к каждому пакету отдельно или ко всему набору данных ?

Если выше .map() не применяется к каждой партии отдельно, как мы можем это сделать ? Можем ли мы определить любой итератор после dataset.batch(), применить tf.image.crop_and_resize() к каждому изображению в пакете, а затем использовать dataset.concatenate() для объединения всех преобразованных пакетов ?

Я создаю набор данных, как показано ниже:

 # Dataset creation (read image data from files of COCO dataset)
dataset = tf.data.Dataset.list_files(self._file_pattern, shuffle=False)
dataset = dataset.shard(dataset_num_shards, dataset_shard_index)
dataset = dataset.shuffle(tf.cast(256 / dataset_num_shards, tf.int64))
dataset = dataset.interleave(map_func=tf.data.TFRecordDataset(filename).prefetch(1), cycle_length=32, block_length=1, num_parallel_calls=tf.data.experimental.AUTOTUNE)
dataset = dataset.map(tf_example_decoder.TfExampleDecoder().decode, num_parallel_calls=64)
dataset = dataset.shuffle(64).repeat()
# Parse each image for preprocessing
dataset = dataset.map(lambda data, _: _parse_example(data), num_parallel_calls=64)
dataset = dataset.batch(batch_size=batch_size, drop_remainder=True)

# Below code suggested by you to resize images to fixed shape in each batch
def resize_data(images, labels):
    tf.print('Original shape -->', tf.shape(images))
    SIZE = (300, 300)
    return tf.image.resize(images, SIZE), labels
dataset = dataset.map(resize_data)
dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)

tf.estimator.Estimator(...).train(
        input_fn=dataset,
        steps=steps,
        hooks=train_hooks)
 

Ответ №1:

В общем, вы можете попробовать что-то вроде этого:

 import tensorflow as tf
import numpy as np

dataset1 = tf.data.Dataset.from_tensor_slices(np.random.random((32, 300, 300, 3)))
dataset2 = tf.data.Dataset.from_tensor_slices(np.random.random((32, 224, 224, 3)))
dataset3 = tf.data.Dataset.from_tensor_slices(np.random.random((32, 400, 400, 3)))
dataset = dataset1.concatenate(dataset2.concatenate(dataset3))
dataset = dataset.shuffle(1).repeat().batch(32, drop_remainder=True)

def resize_data(images):
  tf.print('Original shape -->', tf.shape(images))
  SIZE = (180, 180)

  return tf.image.resize(images, SIZE)

dataset = dataset.map(resize_data)

for images in dataset.take(3):
  tf.print('New shape -->', tf.shape(images))
 
 Original shape --> [32 300 300 3]
New shape --> [32 180 180 3]
Original shape --> [32 224 224 3]
New shape --> [32 180 180 3]
Original shape --> [32 400 400 3]
New shape --> [32 180 180 3]
 

Вы также можете использовать tf.image.resize_with_crop_or_pad , если хотите:

 def resize_data(images):
  tf.print('Original shape -->', tf.shape(images))
  SIZE = (180, 180)
  return tf.image.resize_with_crop_or_pad(images, SIZE[0], SIZE[1])

dataset = dataset.map(resize_data)

for images in dataset.take(3):
  tf.print('New shape -->', tf.shape(images))
 

Обратите внимание, что использование repeat() создаст бесконечный набор данных.

Обновление 1

Если вам нужен случайный размер для каждой партии, попробуйте что-нибудь вроде этого:

 import tensorflow as tf
import numpy as np

dataset1 = tf.data.Dataset.from_tensor_slices(np.random.random((32, 300, 300, 3)))
dataset2 = tf.data.Dataset.from_tensor_slices(np.random.random((32, 224, 224, 3)))
dataset3 = tf.data.Dataset.from_tensor_slices(np.random.random((32, 400, 400, 3)))
dataset = dataset1.concatenate(dataset2.concatenate(dataset3))
dataset = dataset.batch(32, drop_remainder=True).shuffle(96)


def resize_data(images):
  batch_size = tf.shape(images)[0]
  images_resized = tf.TensorArray(dtype=tf.float32, size = 0, dynamic_size=True)
  SIZE = tf.random.uniform((2,), minval=300, maxval=500, dtype=tf.int32)
  for i in range(batch_size):
    images_resized = images_resized.write(images_resized.size(), tf.image.resize(images[i], SIZE))
  return images_resized.stack()

dataset = dataset.map(resize_data)

for images in dataset:
  tf.print('New shape -->', tf.shape(images))
 
 New shape --> [32 392 385 3]
New shape --> [32 468 459 3]
New shape --> [32 466 461 3]
 

Обновление 2

Очень гибкий вариант, который работает для любого размера пакета, будет выглядеть следующим образом:

 import tensorflow as tf
import numpy as np

dataset1 = tf.data.Dataset.from_tensor_slices(np.random.random((32, 300, 300, 3)))
dataset2 = tf.data.Dataset.from_tensor_slices(np.random.random((32, 224, 224, 3)))
dataset3 = tf.data.Dataset.from_tensor_slices(np.random.random((32, 400, 400, 3)))
dataset = dataset1.concatenate(dataset2.concatenate(dataset3))

def resize_and_batch(dataset, batch_size):
  final_dataset = None
  duration = len(dataset)//batch_size
  random_sizes = [tf.random.uniform((2,), minval=300, maxval=500, dtype=tf.int32) for _ in range(duration)]

  for i, size in zip(range(duration), random_sizes):
    idx = i * batch_size
    if i == 0:
      final_dataset = tf.data.Dataset.from_tensor_slices([tf.image.resize(x, size) for x in dataset.take(batch_size)])
    else:
      final_dataset = final_dataset.concatenate(tf.data.Dataset.from_tensor_slices([tf.image.resize(x, size) for x in dataset.skip(idx).take(batch_size)]))
  return final_dataset

batch_size = 10
ds = resize_and_batch(dataset, batch_size)
ds = ds.batch(batch_size).shuffle(len(ds))
for images in ds:
 tf.print('New shape -->', images.shape)
 
 New shape --> TensorShape([10, 399, 348, 3])
New shape --> TensorShape([10, 356, 329, 3])
New shape --> TensorShape([10, 473, 373, 3])
New shape --> TensorShape([10, 489, 489, 3])
New shape --> TensorShape([10, 421, 335, 3])
New shape --> TensorShape([10, 447, 455, 3])
New shape --> TensorShape([10, 355, 382, 3])
New shape --> TensorShape([10, 310, 396, 3])
New shape --> TensorShape([10, 345, 356, 3])
 

Комментарии:

1. Это выглядит хорошо. Однако у меня все еще ничего не получается. Когда я пытаюсь обучить модель, она выдает ошибку, как показано ниже: INVALID_ARGUMENT: Cannot add tensor to the batch: number of elements does not match. Shapes are: [tensor]: [640,426,3], [batch]: [480,640,3] несмотря на то, что я дал РАЗМЕР = (300, 300) в tf.image.resize(изображения, РАЗМЕР), пакет имеет РАЗМЕР = (480, 640). И поскольку следующее изображение имеет другой РАЗМЕР = (640, 426), его не удалось добавить в пакет. Это означает, что каким-то образом он не может применить функцию .map() к каждой отдельной партии. Есть какая-нибудь помощь/идея ?

2. Не могли бы вы добавить код о том, как вы создаете свои наборы данных, в свой вопрос? Думаю, я догадываюсь, в чем может быть проблема.

3. Я обновил вопрос о том, как я создаю набор данных. Жду вашего ответа.

4. Обновленный ответ-

5. размер пакета=16. Это выдает ту же ошибку с размером пакета > 1.