From bb442c9ba4986b51107deb59dace790c18326bea Mon Sep 17 00:00:00 2001 From: Deepraj Majumdar Date: Tue, 22 Mar 2022 20:28:55 +0530 Subject: [PATCH 01/12] build : Add random_resized_crop.py to implement RandomResizedCrop Layer --- keras_cv/layers/preprocessing/random_resized_crop.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 keras_cv/layers/preprocessing/random_resized_crop.py diff --git a/keras_cv/layers/preprocessing/random_resized_crop.py b/keras_cv/layers/preprocessing/random_resized_crop.py new file mode 100644 index 0000000000..e69de29bb2 From e19e6652983f62934a668c0577e9089304c84c8b Mon Sep 17 00:00:00 2001 From: Deepraj Majumdar Date: Thu, 24 Mar 2022 14:26:42 +0530 Subject: [PATCH 02/12] build : Add a new random resized crop layer for the random resizing of the images in batches --- .../preprocessing/random_resized_crop.py | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/keras_cv/layers/preprocessing/random_resized_crop.py b/keras_cv/layers/preprocessing/random_resized_crop.py index e69de29bb2..26175dbad1 100644 --- a/keras_cv/layers/preprocessing/random_resized_crop.py +++ b/keras_cv/layers/preprocessing/random_resized_crop.py @@ -0,0 +1,90 @@ +from keras import backend +from keras.engine import base_layer +from keras.engine import base_preprocessing_layer +from keras.layers.preprocessing import preprocessing_utils as utils +from keras.utils import image_utils +from keras.utils import tf_utils +import numpy as np +import tensorflow as tf +from tensorflow.python.util.tf_export import keras_export +from tensorflow.tools.docs import doc_controls + +H_AXIS = -3 +W_AXIS = -2 + + +class RandomResizedCrop(base_layer.Layer): + ''' + This layer is used to + Crop a random portion of image and resize it to a given size. + A crop of the original image is made: the crop has a random area (H * W) and a random aspect ratio. + This crop is finally resized to the given size. + This is popularly used to train the Inception networks. + ''' + + def __init__(self, height, width, seed=None, **kwargs): + base_preprocessing_layer.keras_kpl_gauge.get_cell('RandomCrop').set(True) + super(RandomResizedCrop, self).__init__(**kwargs, autocast=False, seed=seed, + force_generator=True) + # Attribute to crop image + self.height = height + # Attribute to take the custom height + self.width = width + # Attribute to take the custom width + self.seed = seed + # Attribute to take random seed + + #Methoid for taking the inputs + def call(self, inputs, training=True): + inputs = utils.ensure_tensor(inputs, dtype=self.compute_dtype) + if training: + input_shape = tf.shape(inputs) + h_diff = input_shape[H_AXIS] - self.height + w_diff = input_shape[W_AXIS] - self.width + return tf.cond( + tf.reduce_all((h_diff >= 0, w_diff >= 0)), + lambda: self._random_crop(inputs), + lambda: self._resize(inputs)) + else: + return self._resize(inputs) + + #Method for the random crop + def _random_crop(self, inputs): + input_shape = tf.shape(inputs) + h_diff = input_shape[H_AXIS] - self.height + w_diff = input_shape[W_AXIS] - self.width + dtype = input_shape.dtype + rands = self._random_generator.random_uniform([2], 0, dtype.max, dtype) + h_start = rands[0] % (h_diff + 1) + w_start = rands[1] % (w_diff + 1) + return tf.image.crop_to_bounding_box(inputs, h_start, w_start, + self.height, self.width) + + # method for resizing the images + def _resize(self, inputs): + outputs = image_utils.smart_resize(inputs, [self.height, self.width]) + # smart_resize will always output float32, so we need to re-cast. + return tf.cast(outputs, self.compute_dtype) + + # Method for computing the output shape + def compute_output_shape(self, input_shape): + input_shape = tf.TensorShape(input_shape).as_list() + input_shape[H_AXIS] = self.height + input_shape[W_AXIS] = self.width + return tf.TensorShape(input_shape) + + #Configuration Method + def get_config(self): + config = { + 'height': self.height, + 'width': self.width, + 'seed': self.seed, + } + base_config = super(RandomResizedCrop, self).get_config() + return dict(list(base_config.items()) + list(config.items())) + + # Method for Random Resizing of the inputs + def _random_resized_crop(self,inputs): + return self._resize(self,self._random_crop(self,inputs)) + + From 56680d53ae00506838e3b12da483a4c19160875a Mon Sep 17 00:00:00 2001 From: Deepraj Majumdar Date: Mon, 28 Mar 2022 01:23:17 +0530 Subject: [PATCH 03/12] fix : Made the necessary changes for Implementing the Random Resized Crop --- keras_cv/layers/preprocessing/random_resized_crop.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/keras_cv/layers/preprocessing/random_resized_crop.py b/keras_cv/layers/preprocessing/random_resized_crop.py index 26175dbad1..1fa58e1b9f 100644 --- a/keras_cv/layers/preprocessing/random_resized_crop.py +++ b/keras_cv/layers/preprocessing/random_resized_crop.py @@ -6,6 +6,7 @@ from keras.utils import tf_utils import numpy as np import tensorflow as tf +import random from tensorflow.python.util.tf_export import keras_export from tensorflow.tools.docs import doc_controls @@ -23,9 +24,7 @@ class RandomResizedCrop(base_layer.Layer): ''' def __init__(self, height, width, seed=None, **kwargs): - base_preprocessing_layer.keras_kpl_gauge.get_cell('RandomCrop').set(True) - super(RandomResizedCrop, self).__init__(**kwargs, autocast=False, seed=seed, - force_generator=True) + super().__init__ # Attribute to crop image self.height = height # Attribute to take the custom height @@ -62,7 +61,9 @@ def _random_crop(self, inputs): # method for resizing the images def _resize(self, inputs): - outputs = image_utils.smart_resize(inputs, [self.height, self.width]) + self.new_height=random.randint(0,self.height) + self.new_width=random.randint(0,self.width) + outputs = image_utils.smart_resize(inputs, [self.new_height, self.new_width]) # smart_resize will always output float32, so we need to re-cast. return tf.cast(outputs, self.compute_dtype) From 113b6f74b3615eca0cbde51c17c51b4a70d3d440 Mon Sep 17 00:00:00 2001 From: Deepraj Majumdar Date: Thu, 31 Mar 2022 14:55:11 +0530 Subject: [PATCH 04/12] fix : Add a subclass tf.keras.internal.layers.BaseImageAugmentationLayer istead of base_layer.Layer --- keras_cv/layers/preprocessing/random_resized_crop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keras_cv/layers/preprocessing/random_resized_crop.py b/keras_cv/layers/preprocessing/random_resized_crop.py index 1fa58e1b9f..b50c502bef 100644 --- a/keras_cv/layers/preprocessing/random_resized_crop.py +++ b/keras_cv/layers/preprocessing/random_resized_crop.py @@ -14,7 +14,7 @@ W_AXIS = -2 -class RandomResizedCrop(base_layer.Layer): +class RandomResizedCrop(tf.keras.internal.layers.BaseImageAugmentationLayer): ''' This layer is used to Crop a random portion of image and resize it to a given size. From a7373b8398d3ad9befc5f1357fb0a7cb08344679 Mon Sep 17 00:00:00 2001 From: Deepraj Majumdar Date: Fri, 22 Apr 2022 22:16:06 +0530 Subject: [PATCH 05/12] build : Add subclass tf.keras.internal.layers.BaseImageAugmentationLayer --- .../preprocessing/random_resized_crop.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/keras_cv/layers/preprocessing/random_resized_crop.py b/keras_cv/layers/preprocessing/random_resized_crop.py index b50c502bef..68d13c3e77 100644 --- a/keras_cv/layers/preprocessing/random_resized_crop.py +++ b/keras_cv/layers/preprocessing/random_resized_crop.py @@ -61,7 +61,7 @@ def _random_crop(self, inputs): # method for resizing the images def _resize(self, inputs): - self.new_height=random.randint(0,self.height) + self.new_height=self.random.randint(0,self.height) self.new_width=random.randint(0,self.width) outputs = image_utils.smart_resize(inputs, [self.new_height, self.new_width]) # smart_resize will always output float32, so we need to re-cast. @@ -84,6 +84,23 @@ def get_config(self): base_config = super(RandomResizedCrop, self).get_config() return dict(list(base_config.items()) + list(config.items())) + def get_random_transformation(self, image=None, label=None, bounding_box=None): + x = self._get_shear_amount(self.x) + + y = self._get_shear_amount(self.y) + return (x, y) + + def _get_shear_amount(self, constraint): + if constraint is None: + return None + + negate = self._random_generator.random_uniform((), 0, 1, dtype=tf.float32) > 0.5 + negate = tf.cond(negate, lambda: -1.0, lambda: 1.0) + + return negate * self._random_generator.random_uniform( + (), constraint[0], constraint[1] + ) + # Method for Random Resizing of the inputs def _random_resized_crop(self,inputs): return self._resize(self,self._random_crop(self,inputs)) From fbf7642882167a1e17937c75f88c486896560bfc Mon Sep 17 00:00:00 2001 From: Deepraj Majumdar Date: Sat, 23 Apr 2022 01:25:23 +0530 Subject: [PATCH 06/12] fix : Use tf.random and applied get_random_transformation API from BaseImageAugmentation layer. --- keras_cv/layers/preprocessing/random_resized_crop.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/keras_cv/layers/preprocessing/random_resized_crop.py b/keras_cv/layers/preprocessing/random_resized_crop.py index 68d13c3e77..7ffb7fe223 100644 --- a/keras_cv/layers/preprocessing/random_resized_crop.py +++ b/keras_cv/layers/preprocessing/random_resized_crop.py @@ -3,10 +3,10 @@ from keras.engine import base_preprocessing_layer from keras.layers.preprocessing import preprocessing_utils as utils from keras.utils import image_utils +from keras_cv.utils import preprocessing from keras.utils import tf_utils import numpy as np import tensorflow as tf -import random from tensorflow.python.util.tf_export import keras_export from tensorflow.tools.docs import doc_controls @@ -61,8 +61,7 @@ def _random_crop(self, inputs): # method for resizing the images def _resize(self, inputs): - self.new_height=self.random.randint(0,self.height) - self.new_width=random.randint(0,self.width) + self.new_height,self.new_width=self.get_random_transformation() outputs = image_utils.smart_resize(inputs, [self.new_height, self.new_width]) # smart_resize will always output float32, so we need to re-cast. return tf.cast(outputs, self.compute_dtype) @@ -85,16 +84,17 @@ def get_config(self): return dict(list(base_config.items()) + list(config.items())) def get_random_transformation(self, image=None, label=None, bounding_box=None): - x = self._get_shear_amount(self.x) + x = self._get_shear_amount(self.height) + + y = self._get_shear_amount(self.width) - y = self._get_shear_amount(self.y) return (x, y) def _get_shear_amount(self, constraint): if constraint is None: return None - negate = self._random_generator.random_uniform((), 0, 1, dtype=tf.float32) > 0.5 + negate = tf.random_uniform((), 0, self.constraint, dtype=tf.float32) negate = tf.cond(negate, lambda: -1.0, lambda: 1.0) return negate * self._random_generator.random_uniform( From 1eec92a9c580340939b93995fad617c67e8f0aa6 Mon Sep 17 00:00:00 2001 From: Deepraj Majumdar Date: Sat, 23 Apr 2022 01:32:47 +0530 Subject: [PATCH 07/12] fix : Use augment_image instead of call for subclassing BaseImageAugmentationLayer. --- keras_cv/layers/preprocessing/random_resized_crop.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/keras_cv/layers/preprocessing/random_resized_crop.py b/keras_cv/layers/preprocessing/random_resized_crop.py index 7ffb7fe223..aadc8e8505 100644 --- a/keras_cv/layers/preprocessing/random_resized_crop.py +++ b/keras_cv/layers/preprocessing/random_resized_crop.py @@ -33,8 +33,8 @@ def __init__(self, height, width, seed=None, **kwargs): self.seed = seed # Attribute to take random seed - #Methoid for taking the inputs - def call(self, inputs, training=True): + #Method for taking the inputs + def augment_image(self, inputs, training=True): inputs = utils.ensure_tensor(inputs, dtype=self.compute_dtype) if training: input_shape = tf.shape(inputs) @@ -94,7 +94,7 @@ def _get_shear_amount(self, constraint): if constraint is None: return None - negate = tf.random_uniform((), 0, self.constraint, dtype=tf.float32) + negate = tf.random_uniform((), 0, self.constraint, dtype=tf.float32) > 0.5 negate = tf.cond(negate, lambda: -1.0, lambda: 1.0) return negate * self._random_generator.random_uniform( From 4bf8af74ce0e46bc725c42bf649ea5bfd8d6ad59 Mon Sep 17 00:00:00 2001 From: Deepraj Majumdar Date: Fri, 29 Apr 2022 01:13:02 +0530 Subject: [PATCH 08/12] fix : use correct importing format for keras library --- .../layers/preprocessing/random_resized_crop.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/keras_cv/layers/preprocessing/random_resized_crop.py b/keras_cv/layers/preprocessing/random_resized_crop.py index aadc8e8505..03fcb184e3 100644 --- a/keras_cv/layers/preprocessing/random_resized_crop.py +++ b/keras_cv/layers/preprocessing/random_resized_crop.py @@ -1,14 +1,7 @@ -from keras import backend -from keras.engine import base_layer -from keras.engine import base_preprocessing_layer +import tensorflow as tf from keras.layers.preprocessing import preprocessing_utils as utils from keras.utils import image_utils -from keras_cv.utils import preprocessing -from keras.utils import tf_utils -import numpy as np -import tensorflow as tf -from tensorflow.python.util.tf_export import keras_export -from tensorflow.tools.docs import doc_controls + H_AXIS = -3 W_AXIS = -2 @@ -24,7 +17,7 @@ class RandomResizedCrop(tf.keras.internal.layers.BaseImageAugmentationLayer): ''' def __init__(self, height, width, seed=None, **kwargs): - super().__init__ + super().__init__(**kwargs) # Attribute to crop image self.height = height # Attribute to take the custom height From 9273df46d8b442d310065b16e2f2ddb11c5bbcef Mon Sep 17 00:00:00 2001 From: Deepraj Majumdar Date: Fri, 29 Apr 2022 01:16:19 +0530 Subject: [PATCH 09/12] fix : remove obvious comments --- keras_cv/layers/preprocessing/random_resized_crop.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/keras_cv/layers/preprocessing/random_resized_crop.py b/keras_cv/layers/preprocessing/random_resized_crop.py index 03fcb184e3..8d03d8b4d5 100644 --- a/keras_cv/layers/preprocessing/random_resized_crop.py +++ b/keras_cv/layers/preprocessing/random_resized_crop.py @@ -18,13 +18,9 @@ class RandomResizedCrop(tf.keras.internal.layers.BaseImageAugmentationLayer): def __init__(self, height, width, seed=None, **kwargs): super().__init__(**kwargs) - # Attribute to crop image self.height = height - # Attribute to take the custom height self.width = width - # Attribute to take the custom width self.seed = seed - # Attribute to take random seed #Method for taking the inputs def augment_image(self, inputs, training=True): From be11a7cf0ea29afe5a53c9c8bb0a510457d43a94 Mon Sep 17 00:00:00 2001 From: Deepraj Majumdar Date: Fri, 29 Apr 2022 01:22:54 +0530 Subject: [PATCH 10/12] fix : remove the constants declared outside the class --- keras_cv/layers/preprocessing/random_resized_crop.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/keras_cv/layers/preprocessing/random_resized_crop.py b/keras_cv/layers/preprocessing/random_resized_crop.py index 8d03d8b4d5..5aeec71544 100644 --- a/keras_cv/layers/preprocessing/random_resized_crop.py +++ b/keras_cv/layers/preprocessing/random_resized_crop.py @@ -3,10 +3,6 @@ from keras.utils import image_utils -H_AXIS = -3 -W_AXIS = -2 - - class RandomResizedCrop(tf.keras.internal.layers.BaseImageAugmentationLayer): ''' This layer is used to @@ -58,8 +54,8 @@ def _resize(self, inputs): # Method for computing the output shape def compute_output_shape(self, input_shape): input_shape = tf.TensorShape(input_shape).as_list() - input_shape[H_AXIS] = self.height - input_shape[W_AXIS] = self.width + input_shape[-3] = self.height + input_shape[-2] = self.width return tf.TensorShape(input_shape) #Configuration Method From 664fbf39810369587e6ee8aa8c14d8716216d74b Mon Sep 17 00:00:00 2001 From: Deepraj Majumdar Date: Fri, 29 Apr 2022 01:53:30 +0530 Subject: [PATCH 11/12] fix : remove the H_AXIS and W_AXIS outside the class --- keras_cv/layers/preprocessing/random_resized_crop.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/keras_cv/layers/preprocessing/random_resized_crop.py b/keras_cv/layers/preprocessing/random_resized_crop.py index 5aeec71544..c1a35f1310 100644 --- a/keras_cv/layers/preprocessing/random_resized_crop.py +++ b/keras_cv/layers/preprocessing/random_resized_crop.py @@ -23,8 +23,8 @@ def augment_image(self, inputs, training=True): inputs = utils.ensure_tensor(inputs, dtype=self.compute_dtype) if training: input_shape = tf.shape(inputs) - h_diff = input_shape[H_AXIS] - self.height - w_diff = input_shape[W_AXIS] - self.width + h_diff = input_shape[-3] - self.height + w_diff = input_shape[-2] - self.width return tf.cond( tf.reduce_all((h_diff >= 0, w_diff >= 0)), lambda: self._random_crop(inputs), @@ -35,8 +35,8 @@ def augment_image(self, inputs, training=True): #Method for the random crop def _random_crop(self, inputs): input_shape = tf.shape(inputs) - h_diff = input_shape[H_AXIS] - self.height - w_diff = input_shape[W_AXIS] - self.width + h_diff = input_shape[-3] - self.height + w_diff = input_shape[-2] - self.width dtype = input_shape.dtype rands = self._random_generator.random_uniform([2], 0, dtype.max, dtype) h_start = rands[0] % (h_diff + 1) From 9462864b61d857d2df7a36109340fcf47e5dd1fb Mon Sep 17 00:00:00 2001 From: Deepraj Majumdar Date: Sun, 1 May 2022 01:32:17 +0530 Subject: [PATCH 12/12] build : a demo framework for random_resized_crop made --- .../preprocessing/random_resized_crop.py | 4 ++-- .../preprocessing/random_resized_crop_test.py | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 keras_cv/layers/preprocessing/random_resized_crop_test.py diff --git a/keras_cv/layers/preprocessing/random_resized_crop.py b/keras_cv/layers/preprocessing/random_resized_crop.py index c1a35f1310..71be2e66c0 100644 --- a/keras_cv/layers/preprocessing/random_resized_crop.py +++ b/keras_cv/layers/preprocessing/random_resized_crop.py @@ -2,7 +2,7 @@ from keras.layers.preprocessing import preprocessing_utils as utils from keras.utils import image_utils - +@tf.keras.utils.register_keras_serializable(package="keras_cv") class RandomResizedCrop(tf.keras.internal.layers.BaseImageAugmentationLayer): ''' This layer is used to @@ -13,7 +13,7 @@ class RandomResizedCrop(tf.keras.internal.layers.BaseImageAugmentationLayer): ''' def __init__(self, height, width, seed=None, **kwargs): - super().__init__(**kwargs) + super().__init__(seed=seed,**kwargs) self.height = height self.width = width self.seed = seed diff --git a/keras_cv/layers/preprocessing/random_resized_crop_test.py b/keras_cv/layers/preprocessing/random_resized_crop_test.py new file mode 100644 index 0000000000..0cb010b703 --- /dev/null +++ b/keras_cv/layers/preprocessing/random_resized_crop_test.py @@ -0,0 +1,24 @@ +import tensorflow as tf + +from keras_cv.layers import preprocessing + + +class RandomResizedCropTest(tf.test.TestCase): + def test_aggressive_shear_fills_at_least_some_pixels(self): + img_shape = (50, 50, 3) + xs = tf.stack( + [2 * tf.ones(img_shape), tf.ones(img_shape)], + axis=0, + ) + xs = tf.cast(xs, tf.float32) + + fill_value = 0.0 + layer = preprocessing.RandomResizedCrop( + height=3, width=5, seed=0) + xs = layer(xs) + + # Some pixels should be replaced with fill value + self.assertTrue(tf.math.reduce_any(xs[0] == fill_value)) + self.assertTrue(tf.math.reduce_any(xs[0] == 2.0)) + self.assertTrue(tf.math.reduce_any(xs[1] == fill_value)) + self.assertTrue(tf.math.reduce_any(xs[1] == 1.0)) \ No newline at end of file