15

Keras introduced tf.keras.preprocessing.image_dataset_from_directory function recently, which is more efficient than previously ImageDataGenerator.flow_from_directory method in tensorflow 2.x.

I am practising on the catsvsdogs problems and using this function to build a data pipeline for my model. After training the model, I use preds = model.predict(test_ds) to get the predictions for my test dataset. How should I match the preds with the name of pictures? (There is generator.filenames before, but doesn't exist in the new method any more.) Thanks!

J.Kim
  • 151
  • 1
  • 3
  • 1
    I have the same puzzle as you. The tutorial stops at validation. Now in practical use, I want to load the image from folder and predict and then resave into labelled folder, but I am yet to find a way to do it. Do you have any luck? – Herbz Aug 26 '20 at 14:43

4 Answers4

14

Expanding on @Daniel Woolcott's and @Almog David's answers, the file paths are returned by the image_dataset_from_directory() function in Tensorflow v2.4. already. No need to change the source code of the function.

To be more exact – you can easily retrieve the paths with the file_paths attribute.

Try this:

img_folder = "your_image_folder/"

img_generator = keras.preprocessing.image_dataset_from_directory(
    img_folder, 
    batch_size=32, 
    image_size=(224,224)
)

file_paths = img_generator.file_paths
print(file_paths)

Prints out:

your_file_001.jpg
your_file_002.jpg
…
petezurich
  • 9,280
  • 9
  • 43
  • 57
  • 1
    can we find which one predict false, it means that find false predict and its address, I am using sequential model – CC7052 Jan 15 '22 at 11:03
11

I had a similar issue. The solution was to take the underlying tf.keras.preprocessing.image_dataset_from_directory function and add the 'image_paths' variable to the return statement. This incurs no computational overhead as the filenames have already been retrieved.

The main function code is taken from the GitHub at: https://github.com/tensorflow/tensorflow/blob/v2.3.0/tensorflow/python/keras/preprocessing/image_dataset.py#L34-L206

See below:

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np

from tensorflow.python.data.ops import dataset_ops
from tensorflow.python.keras.layers.preprocessing import image_preprocessing
from tensorflow.python.keras.preprocessing import dataset_utils
from tensorflow.python.ops import image_ops
from tensorflow.python.ops import io_ops
from tensorflow.python.util.tf_export import keras_export

WHITELIST_FORMATS = ('.bmp', '.gif', '.jpeg', '.jpg', '.png')

## Tensorflow override method to return fname as list as well as dataset

def image_dataset_from_directory(directory,
                                 labels='inferred',
                                 label_mode='int',
                                 class_names=None,
                                 color_mode='rgb',
                                 batch_size=32,
                                 image_size=(256, 256),
                                 shuffle=True,
                                 seed=None,
                                 validation_split=None,
                                 subset=None,
                                 interpolation='bilinear',
                                 follow_links=False):
  
  if labels != 'inferred':
    if not isinstance(labels, (list, tuple)):
      raise ValueError(
          '`labels` argument should be a list/tuple of integer labels, of '
          'the same size as the number of image files in the target '
          'directory. If you wish to infer the labels from the subdirectory '
          'names in the target directory, pass `labels="inferred"`. '
          'If you wish to get a dataset that only contains images '
          '(no labels), pass `label_mode=None`.')
    if class_names:
      raise ValueError('You can only pass `class_names` if the labels are '
                       'inferred from the subdirectory names in the target '
                       'directory (`labels="inferred"`).')
  if label_mode not in {'int', 'categorical', 'binary', None}:
    raise ValueError(
        '`label_mode` argument must be one of "int", "categorical", "binary", '
        'or None. Received: %s' % (label_mode,))
  if color_mode == 'rgb':
    num_channels = 3
  elif color_mode == 'rgba':
    num_channels = 4
  elif color_mode == 'grayscale':
    num_channels = 1
  else:
    raise ValueError(
        '`color_mode` must be one of {"rbg", "rgba", "grayscale"}. '
        'Received: %s' % (color_mode,))
  interpolation = image_preprocessing.get_interpolation(interpolation)
  dataset_utils.check_validation_split_arg(
      validation_split, subset, shuffle, seed)

  if seed is None:
    seed = np.random.randint(1e6)
  image_paths, labels, class_names = dataset_utils.index_directory(
      directory,
      labels,
      formats=WHITELIST_FORMATS,
      class_names=class_names,
      shuffle=shuffle,
      seed=seed,
      follow_links=follow_links)

  if label_mode == 'binary' and len(class_names) != 2:
    raise ValueError(
        'When passing `label_mode="binary", there must exactly 2 classes. '
        'Found the following classes: %s' % (class_names,))

  image_paths, labels = dataset_utils.get_training_or_validation_split(
      image_paths, labels, validation_split, subset)

  dataset = paths_and_labels_to_dataset(
      image_paths=image_paths,
      image_size=image_size,
      num_channels=num_channels,
      labels=labels,
      label_mode=label_mode,
      num_classes=len(class_names),
      interpolation=interpolation)
  if shuffle:
    # Shuffle locally at each iteration
    dataset = dataset.shuffle(buffer_size=batch_size * 8, seed=seed)
  dataset = dataset.batch(batch_size)
  # Users may need to reference `class_names`.
  dataset.class_names = class_names
  return dataset, image_paths

def paths_and_labels_to_dataset(image_paths,
                                image_size,
                                num_channels,
                                labels,
                                label_mode,
                                num_classes,
                                interpolation):
  """Constructs a dataset of images and labels."""
  # TODO(fchollet): consider making num_parallel_calls settable
  path_ds = dataset_ops.Dataset.from_tensor_slices(image_paths)
  img_ds = path_ds.map(
      lambda x: path_to_image(x, image_size, num_channels, interpolation))
  if label_mode:
    label_ds = dataset_utils.labels_to_dataset(labels, label_mode, num_classes)
    img_ds = dataset_ops.Dataset.zip((img_ds, label_ds))
  return img_ds


def path_to_image(path, image_size, num_channels, interpolation):
  img = io_ops.read_file(path)
  img = image_ops.decode_image(
      img, channels=num_channels, expand_animations=False)
  img = image_ops.resize_images_v2(img, image_size, method=interpolation)
  img.set_shape((image_size[0], image_size[1], num_channels))
  return img

Which would then work as:

train_dir = '/content/drive/My Drive/just_monkeying_around/monkey_training'
BATCH_SIZE = 32
IMG_SIZE = (224, 224)

train_dataset, train_paths = image_dataset_from_directory(train_dir,
                                             shuffle=True,
                                             batch_size=BATCH_SIZE,
                                             image_size=IMG_SIZE)

train_paths returns a list of file strings.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Daniel Woolcott
  • 111
  • 1
  • 3
  • This is amazing! I can't believe it is there just needs to be returned, thanks so much for this!! – Paul Sep 20 '20 at 17:41
5

As of Tensorflow 2.4 the dataset has a field named: file_paths So it can be used in order to get the file paths.

If you use shuffle=True in the dataset creation please pay attention that you have to disable this line in the dataset creation code (method: image_dataset_from_directory):

  if shuffle:
      # Shuffle locally at each iteration
      dataset = dataset.shuffle(buffer_size=batch_size * 8, seed=seed)
JumbaMumba
  • 560
  • 4
  • 11
-1
datagen = ImageDataGenerator()
test_data = datagen.flow_from_directory('.', classes=['test'])
test_data.filenames

Based on this https://datascience.stackexchange.com/a/93308/149241

More details can here as well https://kylewbanks.com/blog/loading-unlabeled-images-with-imagedatagenerator-flowfromdirectory-keras