Настройка декодирования объектов

API tfds.decode позволяет переопределить декодирование функций по умолчанию. Основной вариант использования — пропустить декодирование изображения для повышения производительности.

Примеры использования

Пропуск декодирования изображения

Чтобы сохранить полный контроль над конвейером декодирования или применить фильтр перед декодированием изображений (для повышения производительности), вы можете полностью пропустить декодирование изображения. Это работает как с tfds.features.Image , так и tfds.features.Video .

ds = tfds.load('imagenet2012', split='train', decoders={
    'image': tfds.decode.SkipDecoding(),
})

for example in ds.take(1):
  assert example['image'].dtype == tf.string  # Images are not decoded

Фильтровать/перетасовать набор данных перед декодированием изображений

Как и в предыдущем примере, вы можете использовать tfds.decode.SkipDecoding() , чтобы вставить дополнительные настройки конвейера tf.data перед декодированием изображения. Таким образом, отфильтрованные изображения не будут декодированы, и вы сможете использовать буфер перемешивания большего размера.

# Load the base dataset without decoding
ds, ds_info = tfds.load(
    'imagenet2012',
    split='train',
    decoders={
        'image': tfds.decode.SkipDecoding(),  # Image won't be decoded here
    },
    as_supervised=True,
    with_info=True,
)
# Apply filter and shuffle
ds = ds.filter(lambda image, label: label != 10)
ds = ds.shuffle(10000)
# Then decode with ds_info.features['image']
ds = ds.map(
    lambda image, label: ds_info.features['image'].decode_example(image), label)

Обрезка и декодирование одновременно

Чтобы переопределить операцию tf.io.decode_image по умолчанию, вы можете создать новый объект tfds.decode.Decoder , используя декоратор tfds.decode.make_decoder() .

@tfds.decode.make_decoder()
def decode_example(serialized_image, feature):
  crop_y, crop_x, crop_height, crop_width = 10, 10, 64, 64
  return tf.image.decode_and_crop_jpeg(
      serialized_image,
      [crop_y, crop_x, crop_height, crop_width],
      channels=feature.feature.shape[-1],
  )

ds = tfds.load('imagenet2012', split='train', decoders={
    # With video, decoders are applied to individual frames
    'image': decode_example(),
})

Что эквивалентно:

def decode_example(serialized_image, feature):
  crop_y, crop_x, crop_height, crop_width = 10, 10, 64, 64
  return tf.image.decode_and_crop_jpeg(
      serialized_image,
      [crop_y, crop_x, crop_height, crop_width],
      channels=feature.shape[-1],
  )

ds, ds_info = tfds.load(
    'imagenet2012',
    split='train',
    with_info=True,
    decoders={
        'image': tfds.decode.SkipDecoding(),  # Skip frame decoding
    },
)
ds = ds.map(functools.partial(decode_example, feature=ds_info.features['image']))

Настройка декодирования видео

Видео — это Sequence(Image()) . При применении пользовательских декодеров они будут применяться к отдельным кадрам. Это означает, что декодеры изображений автоматически совместимы с видео.

@tfds.decode.make_decoder()
def decode_example(serialized_image, feature):
  crop_y, crop_x, crop_height, crop_width = 10, 10, 64, 64
  return tf.image.decode_and_crop_jpeg(
      serialized_image,
      [crop_y, crop_x, crop_height, crop_width],
      channels=feature.feature.shape[-1],
  )

ds = tfds.load('ucf101', split='train', decoders={
    # With video, decoders are applied to individual frames
    'video': decode_example(),
})

Что эквивалентно:

def decode_frame(serialized_image):
  """Decodes a single frame."""
  crop_y, crop_x, crop_height, crop_width = 10, 10, 64, 64
  return tf.image.decode_and_crop_jpeg(
      serialized_image,
      [crop_y, crop_x, crop_height, crop_width],
      channels=ds_info.features['video'].shape[-1],
  )


def decode_video(example):
  """Decodes all individual frames of the video."""
  video = example['video']
  video = tf.map_fn(
      decode_frame,
      video,
      dtype=ds_info.features['video'].dtype,
      parallel_iterations=10,
  )
  example['video'] = video
  return example


ds, ds_info = tfds.load('ucf101', split='train', with_info=True, decoders={
    'video': tfds.decode.SkipDecoding(),  # Skip frame decoding
})
ds = ds.map(decode_video)  # Decode the video

Декодируйте только подмножество функций.

Также можно полностью пропустить некоторые функции, указав только те функции, которые вам нужны. Все остальные функции будут игнорироваться/пропускаться.

builder = tfds.builder('my_dataset')
builder.as_dataset(split='train', decoders=tfds.decode.PartialDecoding({
    'image': True,
    'metadata': {'num_objects', 'scene_name'},
    'objects': {'label'},
})

TFDS выберет подмножество builder.info.features , соответствующее заданной структуре tfds.decode.PartialDecoding .

В приведенном выше коде функции неявно извлекаются в соответствии с builder.info.features . Также возможно явно определить признаки. Приведенный выше код эквивалентен:

builder = tfds.builder('my_dataset')
builder.as_dataset(split='train', decoders=tfds.decode.PartialDecoding({
    'image': tfds.features.Image(),
    'metadata': {
        'num_objects': tf.int64,
        'scene_name': tfds.features.Text(),
    },
    'objects': tfds.features.Sequence({
        'label': tfds.features.ClassLabel(names=[]),
    }),
})

Исходные метаданные (названия меток, форма изображения и т. д.) автоматически используются повторно, поэтому их не требуется предоставлять.

tfds.decode.SkipDecoding можно передать в tfds.decode.PartialDecoding через kwargs PartialDecoding(..., decoders={}) .