Added RandomZoom to preprocessing layers (#149)

* added random Zoom

* added seed

* update docstring

* added name

* added name
This commit is contained in:
divyasreepat 2023-05-11 18:00:43 -07:00 committed by Francois Chollet
parent e56c7650e2
commit 8a355940ed
3 changed files with 200 additions and 0 deletions

@ -77,6 +77,7 @@ from keras_core.layers.preprocessing.random_contrast import RandomContrast
from keras_core.layers.preprocessing.random_crop import RandomCrop
from keras_core.layers.preprocessing.random_flip import RandomFlip
from keras_core.layers.preprocessing.random_translation import RandomTranslation
from keras_core.layers.preprocessing.random_zoom import RandomZoom
from keras_core.layers.preprocessing.rescaling import Rescaling
from keras_core.layers.preprocessing.resizing import Resizing
from keras_core.layers.preprocessing.string_lookup import StringLookup

@ -0,0 +1,116 @@
import numpy as np
import tensorflow as tf
from keras_core import backend
from keras_core.api_export import keras_core_export
from keras_core.layers.layer import Layer
@keras_core_export("keras_core.layers.RandomZoom")
class RandomZoom(Layer):
"""A preprocessing layer which randomly zooms images during training.
This layer will randomly zoom in or out on each axis of an image
independently, filling empty space according to `fill_mode`.
Input pixel values can be of any range (e.g. `[0., 1.)` or `[0, 255]`) and
of integer or floating point dtype.
By default, the layer will output floats.
Args:
height_factor: a float represented as fraction of value,
or a tuple of size 2 representing lower and upper bound
for zooming vertically. When represented as a single float,
this value is used for both the upper and
lower bound. A positive value means zooming out,
while a negative value
means zooming in. For instance, `height_factor=(0.2, 0.3)`
result in an output zoomed out by a random amount
in the range `[+20%, +30%]`.
`height_factor=(-0.3, -0.2)` result in an output zoomed
in by a random amount in the range `[+20%, +30%]`.
width_factor: a float represented as fraction of value,
or a tuple of size 2 representing lower and upper bound
for zooming horizontally. When
represented as a single float, this value is used
for both the upper and
lower bound. For instance, `width_factor=(0.2, 0.3)`
result in an output
zooming out between 20% to 30%.
`width_factor=(-0.3, -0.2)` result in an
output zooming in between 20% to 30%. `None` means
i.e., zooming vertical and horizontal directions
by preserving the aspect ratio. Defaults to `None`.
fill_mode: Points outside the boundaries of the input are
filled according to the given mode
(one of `{"constant", "reflect", "wrap", "nearest"}`).
- *reflect*: `(d c b a | a b c d | d c b a)`
The input is extended by reflecting about
the edge of the last pixel.
- *constant*: `(k k k k | a b c d | k k k k)`
The input is extended by filling all values beyond
the edge with the same constant value k = 0.
- *wrap*: `(a b c d | a b c d | a b c d)` The input is extended by
wrapping around to the opposite edge.
- *nearest*: `(a a a a | a b c d | d d d d)`
The input is extended by the nearest pixel.
interpolation: Interpolation mode. Supported values: `"nearest"`,
`"bilinear"`.
seed: Integer. Used to create a random seed.
fill_value: a float represents the value to be filled outside
the boundaries when `fill_mode="constant"`.
Example:
>>> input_img = np.random.random((32, 224, 224, 3))
>>> layer = keras_core.layers.RandomZoom(.5, .2)
>>> out_img = layer(input_img)
Input shape:
3D (unbatched) or 4D (batched) tensor with shape:
`(..., height, width, channels)`, in `"channels_last"` format.
Output shape:
3D (unbatched) or 4D (batched) tensor with shape:
`(..., height, width, channels)`, in `"channels_last"` format.
"""
def __init__(
self,
height_factor,
width_factor=None,
fill_mode="reflect",
interpolation="bilinear",
seed=None,
fill_value=0.0,
name=None,
**kwargs,
):
super().__init__(name=name, **kwargs)
self.seed = seed or backend.random.make_default_seed()
self.layer = tf.keras.layers.RandomZoom(
height_factor=height_factor,
width_factor=width_factor,
fill_mode=fill_mode,
interpolation=interpolation,
seed=self.seed,
name=name,
fill_value=fill_value,
**kwargs,
)
def call(self, inputs, training=True):
if not isinstance(inputs, (tf.Tensor, np.ndarray, list, tuple)):
inputs = tf.convert_to_tensor(np.array(inputs))
outputs = self.layer.call(inputs)
if backend.backend() != "tensorflow":
outputs = backend.convert_to_tensor(outputs)
return outputs
def compute_output_shape(self, input_shape):
return tuple(self.layer.compute_output_shape(input_shape))
def get_config(self):
config = self.layer.get_config()
config.update({"seed": self.seed})
return config

@ -0,0 +1,83 @@
import numpy as np
from absl.testing import parameterized
from keras_core import backend
from keras_core import layers
from keras_core import testing
class RandomZoomTest(testing.TestCase, parameterized.TestCase):
@parameterized.named_parameters(
("random_zoom_in_4_by_6", -0.4, -0.6),
("random_zoom_in_2_by_3", -0.2, -0.3),
("random_zoom_in_tuple_factor", (-0.4, -0.5), (-0.2, -0.3)),
("random_zoom_out_4_by_6", 0.4, 0.6),
("random_zoom_out_2_by_3", 0.2, 0.3),
("random_zoom_out_tuple_factor", (0.4, 0.5), (0.2, 0.3)),
)
def test_random_zoom(self, height_factor, width_factor):
self.run_layer_test(
layers.RandomZoom,
init_kwargs={
"height_factor": height_factor,
"width_factor": width_factor,
},
input_shape=(2, 3, 4),
expected_output_shape=(2, 3, 4),
supports_masking=False,
)
def test_random_zoom_out_correctness(self):
input_image = np.reshape(np.arange(0, 25), (1, 5, 5, 1))
expected_output = np.asarray(
[
[0, 0, 0, 0, 0],
[0, 5, 7, 9, 0],
[0, 10, 12, 14, 0],
[0, 20, 22, 24, 0],
[0, 0, 0, 0, 0],
]
)
expected_output = backend.convert_to_tensor(
np.reshape(expected_output, (1, 5, 5, 1))
)
self.run_layer_test(
layers.RandomZoom,
init_kwargs={
"height_factor": (0.5, 0.5),
"width_factor": (0.8, 0.8),
"interpolation": "nearest",
"fill_mode": "constant",
},
input_shape=None,
input_data=input_image,
expected_output=expected_output,
supports_masking=False,
)
def test_random_zoom_in_correctness(self):
input_image = np.reshape(np.arange(0, 25), (1, 5, 5, 1))
expected_output = np.asarray(
[
[6, 7, 7, 8, 8],
[11, 12, 12, 13, 13],
[11, 12, 12, 13, 13],
[16, 17, 17, 18, 18],
[16, 17, 17, 18, 18],
]
)
expected_output = backend.convert_to_tensor(
np.reshape(expected_output, (1, 5, 5, 1))
)
self.run_layer_test(
layers.RandomZoom,
init_kwargs={
"height_factor": (-0.5, -0.5),
"width_factor": (-0.5, -0.5),
"interpolation": "nearest",
},
input_shape=None,
input_data=input_image,
expected_output=expected_output,
supports_masking=False,
)