Reputation: 535
I have a TF dataset to classify cats and dogs:
import tensorflow_datasets as tfds
SPLIT_WEIGHTS = (8, 1, 1)
splits = tfds.Split.TRAIN.subsplit(weighted=SPLIT_WEIGHTS)
(raw_train, raw_validation, raw_test), metadata = tfds.load(
'cats_vs_dogs', split=list(splits),
with_info=True, as_supervised=True)
In the example they use some image augmentation with a map function. I was wondering if that could also be done with the nice ImageDataGenerator
class such as described here:
from tensorflow.keras.preprocessing.image import ImageDataGenerator
train_image_generator = ImageDataGenerator(rescale=1./255) # Generator for our training data
train_data_gen = train_image_generator.flow_from_directory(batch_size=batch_size,
directory=train_dir,
shuffle=True,
target_size=(IMG_HEIGHT, IMG_WIDTH),
class_mode='binary')
The problem I'm facing is that I can only see 3 ways to use the ImageDataGenerator
: pandas dataframe, numpy array and directory of images.
Is there any way to also use a Tensorflow dataset and combine these methods?
Upvotes: 15
Views: 6754
Reputation: 137
One way could be to add the data augmentation via keras preprocessing layers, making the augmentation part of your model (see here: https://www.tensorflow.org/tutorials/images/data_augmentation#data_augmentation_2).
Example from the docs:
data_augmentation = tf.keras.Sequential([
layers.RandomFlip("horizontal_and_vertical"),
layers.RandomRotation(0.2),
])
Upvotes: 1
Reputation: 1710
One idea is you could create a generator wrapper function that uses your tfds Dataset to load multiples of your batch size. Then pass those images, labels to ImageDataGenerator's flow method which would yield augmented data at a rate of your desired batch size.
For example:
def tfds_imgen(ds, imgen, batch_size, batches_per):
for images, labels in ds:
flow_ = imgen.flow(images, labels, batch_size=batch_size)
for _ in range(batches_per):
yield next(flow_)
raw_train_ds = tfds.load(
'cats_vs_dogs', split='train',
batch_size=SOME_MULTIPLE_OF_32,
as_supervised=True)
imgen = ImageDataGenerator(...)
train_ds = tfds_imgen(
raw_train_ds.as_numpy_iterator(), imgen,
batch_size=32, batches_per=SOME_MULTIPLE_OF_32 // 32)
Upvotes: 3
Reputation: 1498
Yes, it is but it's a bit tricky.
Keras ImageDataGenerator
works on numpy.array
s and not on tf.Tensor
's so we have to use Tensorflow's numpy_function. This will allow us to perform operations on tf.data.Dataset
content just like it was numpy arrays.
First, let's declare the function that we will .map
over our dataset (assuming your dataset consists of image, label pairs):
# We will take 1 original image and create 5 augmented images:
HOW_MANY_TO_AUGMENT = 5
def augment(image, label):
# Create generator and fit it to an image
img_gen = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255)
img_gen.fit(image)
# We want to keep original image and label
img_results = [(image/255.).astype(np.float32)]
label_results = [label]
# Perform augmentation and keep the labels
augmented_images = [next(img_gen.flow(image)) for _ in range(HOW_MANY_TO_AUGMENT)]
labels = [label for _ in range(HOW_MANY_TO_AUGMENT)]
# Append augmented data and labels to original data
img_results.extend(augmented_images)
label_results.extend(labels)
return img_results, label_results
Now, in order to use this function inside tf.data.Dataset
we must declare a numpy_function
:
def py_augment(image, label):
func = tf.numpy_function(augment, [image, label], [tf.float32, tf.int32])
return func
py_augment
can be safely used like:
augmented_dataset_ds = image_label_dataset.map(py_augment)
The image
part in dataset is now in shape
(HOW_MANY_TO_AUGMENT, image_height, image_width, channels)
.
To convert it to simple (1, image_height, image_width, channels)
you can simply use unbatch
:
unbatched_augmented_dataset_ds = augmented_dataset_ds.unbatch()
So the entire section looks like this:
HOW_MANY_TO_AUGMENT = 5
def augment(image, label):
# Create generator and fit it to an image
img_gen = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255)
img_gen.fit(image)
# We want to keep original image and label
img_results = [(image/255.).astype(np.float32)]
label_results = [label]
# Perform augmentation and keep the labels
augmented_images = [next(img_gen.flow(image)) for _ in range(HOW_MANY_TO_AUGMENT)]
labels = [label for _ in range(HOW_MANY_TO_AUGMENT)]
# Append augmented data and labels to original data
img_results.extend(augmented_images)
label_results.extend(labels)
return img_results, label_results
def py_augment(image, label):
func = tf.numpy_function(augment, [image, label], [tf.float32, tf.int32])
return func
unbatched_augmented_dataset_ds = augmented_dataset_ds.map(py_augment).unbatch()
# Iterate over the dataset for preview:
for image, label in unbatched_augmented_dataset_ds:
...
Upvotes: 7