Personalización de funciones de decodificación

La API tfds.decode le permite anular la función de decodificación predeterminada. El caso de uso principal es omitir la decodificación de imágenes para obtener un mejor rendimiento.

Ejemplos de uso

Saltarse la decodificación de imágenes

Para mantener un control total sobre el proceso de decodificación o para aplicar un filtro antes de que las imágenes se decodifiquen (para un mejor rendimiento), puede omitir la decodificación de la imagen por completo. Esto funciona tanto con tfds.features.Image como con 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

Filtrar/mezclar conjuntos de datos antes de decodificar las imágenes

De manera similar al ejemplo anterior, puede usar tfds.decode.SkipDecoding() para insertar una personalización adicional de la canalización tf.data antes de decodificar la imagen. De esa manera, las imágenes filtradas no se decodificarán y podrás utilizar un búfer aleatorio más grande.

# 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)

Recortar y decodificar al mismo tiempo

Para anular la operación tf.io.decode_image predeterminada, puede crear un nuevo objeto tfds.decode.Decoder usando el decorador 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(),
})

Lo que equivale a:

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']))

Personalizar la decodificación de vídeo

Los vídeos son Sequence(Image()) . Al aplicar decodificadores personalizados, se aplicarán a fotogramas individuales. Esto significa que los decodificadores de imágenes son automáticamente compatibles con el vídeo.

@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(),
})

Lo que equivale a:

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

Decodifica solo un subconjunto de características.

También es posible omitir por completo algunas funciones especificando solo las que necesita. Todas las demás funciones se ignorarán/omitirán.

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

TFDS seleccionará el subconjunto de builder.info.features que coincida con la estructura tfds.decode.PartialDecoding dada.

En el código anterior, los elementos destacados se extraen implícitamente para que coincidan con builder.info.features . También es posible definir explícitamente las características. El código anterior es equivalente a:

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=[]),
    }),
})

Los metadatos originales (nombres de etiquetas, forma de imagen,...) se reutilizan automáticamente por lo que no es necesario proporcionarlos.

tfds.decode.SkipDecoding se puede pasar a tfds.decode.PartialDecoding , a través de los kwargs PartialDecoding(..., decoders={}) .