STT-tensorflow/tf/tensorflow/python/keras/preprocessing/image_test.py
Mihai Maruseac 06923bb4fe initial
2021-01-21 09:06:36 -08:00

446 lines
16 KiB
Python

# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for image preprocessing utils."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import shutil
import tempfile
from absl.testing import parameterized
import numpy as np
from tensorflow.python.data import Dataset
from tensorflow.python.keras import keras_parameterized
from tensorflow.python.keras import layers
from tensorflow.python.keras import testing_utils
from tensorflow.python.keras.engine import sequential
from tensorflow.python.keras.preprocessing import image as preprocessing_image
from tensorflow.python.platform import test
try:
import PIL # pylint:disable=g-import-not-at-top
except ImportError:
PIL = None
def _generate_test_images():
img_w = img_h = 20
rgb_images = []
gray_images = []
for _ in range(8):
bias = np.random.rand(img_w, img_h, 1) * 64
variance = np.random.rand(img_w, img_h, 1) * (255 - 64)
imarray = np.random.rand(img_w, img_h, 3) * variance + bias
im = preprocessing_image.array_to_img(imarray, scale=False)
rgb_images.append(im)
imarray = np.random.rand(img_w, img_h, 1) * variance + bias
im = preprocessing_image.array_to_img(imarray, scale=False)
gray_images.append(im)
return [rgb_images, gray_images]
class TestImage(keras_parameterized.TestCase):
@testing_utils.run_v2_only
def test_smart_resize(self):
test_input = np.random.random((20, 40, 3))
output = preprocessing_image.smart_resize(test_input, size=(50, 50))
self.assertIsInstance(output, np.ndarray)
self.assertListEqual(list(output.shape), [50, 50, 3])
output = preprocessing_image.smart_resize(test_input, size=(10, 10))
self.assertListEqual(list(output.shape), [10, 10, 3])
output = preprocessing_image.smart_resize(test_input, size=(100, 50))
self.assertListEqual(list(output.shape), [100, 50, 3])
output = preprocessing_image.smart_resize(test_input, size=(5, 15))
self.assertListEqual(list(output.shape), [5, 15, 3])
@parameterized.named_parameters(
('size1', (50, 50)),
('size2', (10, 10)),
('size3', (100, 50)),
('size4', (5, 15)))
@testing_utils.run_v2_only
def test_smart_resize_tf_dataset(self, size):
test_input_np = np.random.random((2, 20, 40, 3))
test_ds = Dataset.from_tensor_slices(test_input_np)
resize = lambda img: preprocessing_image.smart_resize(img, size=size)
test_ds = test_ds.map(resize)
for sample in test_ds.as_numpy_iterator():
self.assertIsInstance(sample, np.ndarray)
self.assertListEqual(list(sample.shape), [size[0], size[1], 3])
def test_smart_resize_errors(self):
with self.assertRaisesRegex(ValueError, 'a tuple of 2 integers'):
preprocessing_image.smart_resize(
np.random.random((20, 20, 2)), size=(10, 5, 3))
with self.assertRaisesRegex(ValueError, 'incorrect rank'):
preprocessing_image.smart_resize(np.random.random((20, 40)), size=(10, 5))
def test_image_data_generator(self):
if PIL is None:
return # Skip test if PIL is not available.
for test_images in _generate_test_images():
img_list = []
for im in test_images:
img_list.append(preprocessing_image.img_to_array(im)[None, ...])
images = np.vstack(img_list)
generator = preprocessing_image.ImageDataGenerator(
featurewise_center=True,
samplewise_center=True,
featurewise_std_normalization=True,
samplewise_std_normalization=True,
zca_whitening=True,
rotation_range=90.,
width_shift_range=0.1,
height_shift_range=0.1,
shear_range=0.5,
zoom_range=0.2,
channel_shift_range=0.,
brightness_range=(1, 5),
fill_mode='nearest',
cval=0.5,
horizontal_flip=True,
vertical_flip=True)
# Basic test before fit
x = np.random.random((32, 10, 10, 3))
generator.flow(x)
# Fit
generator.fit(images, augment=True)
for x, _ in generator.flow(
images,
np.arange(images.shape[0]),
shuffle=True):
self.assertEqual(x.shape[1:], images.shape[1:])
break
def test_image_data_generator_with_split_value_error(self):
with self.assertRaises(ValueError):
preprocessing_image.ImageDataGenerator(validation_split=5)
def test_image_data_generator_invalid_data(self):
generator = preprocessing_image.ImageDataGenerator(
featurewise_center=True,
samplewise_center=True,
featurewise_std_normalization=True,
samplewise_std_normalization=True,
zca_whitening=True,
data_format='channels_last')
# Test fit with invalid data
with self.assertRaises(ValueError):
x = np.random.random((3, 10, 10))
generator.fit(x)
# Test flow with invalid data
with self.assertRaises(ValueError):
generator.flow(np.arange(5))
# Invalid number of channels: will work but raise a warning
x = np.random.random((32, 10, 10, 5))
generator.flow(x)
with self.assertRaises(ValueError):
generator = preprocessing_image.ImageDataGenerator(
data_format='unknown')
generator = preprocessing_image.ImageDataGenerator(zoom_range=(2., 2.))
def test_image_data_generator_fit(self):
generator = preprocessing_image.ImageDataGenerator(
featurewise_center=True,
samplewise_center=True,
featurewise_std_normalization=True,
samplewise_std_normalization=True,
zca_whitening=True,
data_format='channels_last')
# Test grayscale
x = np.random.random((32, 10, 10, 1))
generator.fit(x)
# Test RBG
x = np.random.random((32, 10, 10, 3))
generator.fit(x)
generator = preprocessing_image.ImageDataGenerator(
featurewise_center=True,
samplewise_center=True,
featurewise_std_normalization=True,
samplewise_std_normalization=True,
zca_whitening=True,
data_format='channels_first')
# Test grayscale
x = np.random.random((32, 1, 10, 10))
generator.fit(x)
# Test RBG
x = np.random.random((32, 3, 10, 10))
generator.fit(x)
def test_directory_iterator(self):
if PIL is None:
return # Skip test if PIL is not available.
num_classes = 2
temp_dir = self.get_temp_dir()
self.addCleanup(shutil.rmtree, temp_dir)
# create folders and subfolders
paths = []
for cl in range(num_classes):
class_directory = 'class-{}'.format(cl)
classpaths = [
class_directory, os.path.join(class_directory, 'subfolder-1'),
os.path.join(class_directory, 'subfolder-2'), os.path.join(
class_directory, 'subfolder-1', 'sub-subfolder')
]
for path in classpaths:
os.mkdir(os.path.join(temp_dir, path))
paths.append(classpaths)
# save the images in the paths
count = 0
filenames = []
for test_images in _generate_test_images():
for im in test_images:
# rotate image class
im_class = count % num_classes
# rotate subfolders
classpaths = paths[im_class]
filename = os.path.join(classpaths[count % len(classpaths)],
'image-{}.jpg'.format(count))
filenames.append(filename)
im.save(os.path.join(temp_dir, filename))
count += 1
# Test image loading util
fname = os.path.join(temp_dir, filenames[0])
_ = preprocessing_image.load_img(fname)
_ = preprocessing_image.load_img(fname, grayscale=True)
_ = preprocessing_image.load_img(fname, target_size=(10, 10))
_ = preprocessing_image.load_img(fname, target_size=(10, 10),
interpolation='bilinear')
# create iterator
generator = preprocessing_image.ImageDataGenerator()
dir_iterator = generator.flow_from_directory(temp_dir)
# check number of classes and images
self.assertEqual(len(dir_iterator.class_indices), num_classes)
self.assertEqual(len(dir_iterator.classes), count)
self.assertEqual(set(dir_iterator.filenames), set(filenames))
def preprocessing_function(x):
"""This will fail if not provided by a Numpy array.
Note: This is made to enforce backward compatibility.
Args:
x: A numpy array.
Returns:
An array of zeros with the same shape as the given array.
"""
self.assertEqual(x.shape, (26, 26, 3))
self.assertIs(type(x), np.ndarray)
return np.zeros_like(x)
# Test usage as Sequence
generator = preprocessing_image.ImageDataGenerator(
preprocessing_function=preprocessing_function)
dir_seq = generator.flow_from_directory(
str(temp_dir),
target_size=(26, 26),
color_mode='rgb',
batch_size=3,
class_mode='categorical')
self.assertEqual(len(dir_seq), count // 3 + 1)
x1, y1 = dir_seq[1]
self.assertEqual(x1.shape, (3, 26, 26, 3))
self.assertEqual(y1.shape, (3, num_classes))
x1, y1 = dir_seq[5]
self.assertTrue((x1 == 0).all())
def directory_iterator_with_validation_split_test_helper(
self, validation_split):
if PIL is None:
return # Skip test if PIL is not available.
num_classes = 2
tmp_folder = tempfile.mkdtemp(prefix='test_images')
# create folders and subfolders
paths = []
for cl in range(num_classes):
class_directory = 'class-{}'.format(cl)
classpaths = [
class_directory,
os.path.join(class_directory, 'subfolder-1'),
os.path.join(class_directory, 'subfolder-2'),
os.path.join(class_directory, 'subfolder-1', 'sub-subfolder')
]
for path in classpaths:
os.mkdir(os.path.join(tmp_folder, path))
paths.append(classpaths)
# save the images in the paths
count = 0
filenames = []
for test_images in _generate_test_images():
for im in test_images:
# rotate image class
im_class = count % num_classes
# rotate subfolders
classpaths = paths[im_class]
filename = os.path.join(classpaths[count % len(classpaths)],
'image-{}.jpg'.format(count))
filenames.append(filename)
im.save(os.path.join(tmp_folder, filename))
count += 1
# create iterator
generator = preprocessing_image.ImageDataGenerator(
validation_split=validation_split)
with self.assertRaises(ValueError):
generator.flow_from_directory(tmp_folder, subset='foo')
num_validation = int(count * validation_split)
num_training = count - num_validation
train_iterator = generator.flow_from_directory(
tmp_folder, subset='training')
self.assertEqual(train_iterator.samples, num_training)
valid_iterator = generator.flow_from_directory(
tmp_folder, subset='validation')
self.assertEqual(valid_iterator.samples, num_validation)
# check number of classes and images
self.assertEqual(len(train_iterator.class_indices), num_classes)
self.assertEqual(len(train_iterator.classes), num_training)
self.assertEqual(
len(set(train_iterator.filenames) & set(filenames)), num_training)
model = sequential.Sequential([layers.Flatten(), layers.Dense(2)])
model.compile(optimizer='sgd', loss='mse')
model.fit(train_iterator, epochs=1)
shutil.rmtree(tmp_folder)
@keras_parameterized.run_all_keras_modes
def test_directory_iterator_with_validation_split_25_percent(self):
self.directory_iterator_with_validation_split_test_helper(0.25)
@keras_parameterized.run_all_keras_modes
def test_directory_iterator_with_validation_split_40_percent(self):
self.directory_iterator_with_validation_split_test_helper(0.40)
@keras_parameterized.run_all_keras_modes
def test_directory_iterator_with_validation_split_50_percent(self):
self.directory_iterator_with_validation_split_test_helper(0.50)
def test_img_utils(self):
if PIL is None:
return # Skip test if PIL is not available.
height, width = 10, 8
# Test channels_first data format
x = np.random.random((3, height, width))
img = preprocessing_image.array_to_img(
x, data_format='channels_first')
self.assertEqual(img.size, (width, height))
x = preprocessing_image.img_to_array(
img, data_format='channels_first')
self.assertEqual(x.shape, (3, height, width))
# Test 2D
x = np.random.random((1, height, width))
img = preprocessing_image.array_to_img(
x, data_format='channels_first')
self.assertEqual(img.size, (width, height))
x = preprocessing_image.img_to_array(
img, data_format='channels_first')
self.assertEqual(x.shape, (1, height, width))
# Test channels_last data format
x = np.random.random((height, width, 3))
img = preprocessing_image.array_to_img(x, data_format='channels_last')
self.assertEqual(img.size, (width, height))
x = preprocessing_image.img_to_array(img, data_format='channels_last')
self.assertEqual(x.shape, (height, width, 3))
# Test 2D
x = np.random.random((height, width, 1))
img = preprocessing_image.array_to_img(x, data_format='channels_last')
self.assertEqual(img.size, (width, height))
x = preprocessing_image.img_to_array(img, data_format='channels_last')
self.assertEqual(x.shape, (height, width, 1))
def test_batch_standardize(self):
if PIL is None:
return # Skip test if PIL is not available.
# ImageDataGenerator.standardize should work on batches
for test_images in _generate_test_images():
img_list = []
for im in test_images:
img_list.append(preprocessing_image.img_to_array(im)[None, ...])
images = np.vstack(img_list)
generator = preprocessing_image.ImageDataGenerator(
featurewise_center=True,
samplewise_center=True,
featurewise_std_normalization=True,
samplewise_std_normalization=True,
zca_whitening=True,
rotation_range=90.,
width_shift_range=0.1,
height_shift_range=0.1,
shear_range=0.5,
zoom_range=0.2,
channel_shift_range=0.,
brightness_range=(1, 5),
fill_mode='nearest',
cval=0.5,
horizontal_flip=True,
vertical_flip=True)
generator.fit(images, augment=True)
transformed = np.copy(images)
for i, im in enumerate(transformed):
transformed[i] = generator.random_transform(im)
transformed = generator.standardize(transformed)
def test_img_transforms(self):
x = np.random.random((3, 200, 200))
_ = preprocessing_image.random_rotation(x, 20)
_ = preprocessing_image.random_shift(x, 0.2, 0.2)
_ = preprocessing_image.random_shear(x, 2.)
_ = preprocessing_image.random_zoom(x, (0.5, 0.5))
_ = preprocessing_image.apply_channel_shift(x, 2, 2)
_ = preprocessing_image.apply_affine_transform(x, 2)
with self.assertRaises(ValueError):
preprocessing_image.random_zoom(x, (0, 0, 0))
_ = preprocessing_image.random_channel_shift(x, 2.)
if __name__ == '__main__':
test.main()