Repository: keras-team/keras
Pull request: Fixed RandomGrayscall.call for single unbatched image.
Fixes [#21276](https://github.com/keras-team/keras/issues/21276).
When we were passing a single image without including a batch dimension, it was not being handled properly and causing problem. The documentation mentions both 3D(unbatched -> W, H, C) and 4D(batched -> batch, W, H, C) images are accepted by RandomGrayscale layer.

Now it has been fixed.

Problem statement:
RandomGrayscale fails for unbatched images.
When we try to pass unbatched single rgb image to RandomGrayscale it fails.

Code to reproduce

```
import keras
from keras import layers
import matplotlib.pyplot as plt
import numpy as np
img=np.random.uniform(size=(224,224,3))
# plt.imshow(img)
# plt.show()
out=layers.RandomGrayscale(1)(np.array(img))
# plt.imshow(out)
# plt.show()
```

I get the following error

```
/usr/local/bin/python3 /Users/hellorahul/PyCharmMiscProject/script.py 
Traceback (most recent call last):
  File "/Users/hellorahul/PyCharmMiscProject/script.py", line 8, in <module>
    out=layers.RandomGrayscale(1)(np.array(img))
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/keras/src/layers/preprocessing/tf_data_layer.py", line 49, in __call__
    return super().__call__(inputs, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/keras/src/utils/traceback_utils.py", line 122, in error_handler
    raise e.with_traceback(filtered_tb) from None
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/keras/src/backend/tensorflow/numpy.py", line 2616, in squeeze
    raise ValueError(
ValueError: Exception encountered when calling RandomGrayscale.call().

Cannot squeeze axis=0, because the dimension is not 1.

Arguments received by RandomGrayscale.call():
  • data=tf.Tensor(shape=(224, 224, 3), dtype=float32)
  • training=True

```

Hints from issue discussion:

Commit message: Update random_grayscale.py

Fixed issue with passing a single image without batch dimension.

Patch to review:
diff --git a/keras/src/layers/preprocessing/image_preprocessing/random_grayscale.py b/keras/src/layers/preprocessing/image_preprocessing/random_grayscale.py
index 2dbcca6e502..99ecf860eae 100644
--- a/keras/src/layers/preprocessing/image_preprocessing/random_grayscale.py
+++ b/keras/src/layers/preprocessing/image_preprocessing/random_grayscale.py
@@ -59,12 +59,20 @@ def __init__(self, factor=0.5, data_format=None, seed=None, **kwargs):
     def get_random_transformation(self, images, training=True, seed=None):
         if seed is None:
             seed = self._get_seed_generator(self.backend._backend)
-        random_values = self.backend.random.uniform(
-            shape=(self.backend.core.shape(images)[0],),
-            minval=0,
-            maxval=1,
-            seed=seed,
-        )
+        if len(images.shape) == 4:
+            random_values = self.backend.random.uniform(
+                shape=(self.backend.core.shape(images)[0],),
+                minval=0,
+                maxval=1,
+                seed=seed,
+            )
+        else:
+            random_values = self.backend.random.uniform(
+                shape=(1,),
+                minval=0,
+                maxval=1,
+                seed=seed,
+            )
         should_apply = self.backend.numpy.expand_dims(
             random_values < self.factor, axis=[1, 2, 3]
         )


Code review comments:
diff_hunk: @@ -59,12 +59,20 @@ def __init__(self, factor=0.5, data_format=None, seed=None, **kwargs):
     def get_random_transformation(self, images, training=True, seed=None):
         if seed is None:
             seed = self._get_seed_generator(self.backend._backend)
-        random_values = self.backend.random.uniform(
-            shape=(self.backend.core.shape(images)[0],),
-            minval=0,
-            maxval=1,
-            seed=seed,
-        )
+        if len(images.shape) == 4:
+            random_values = self.backend.random.uniform(
+                shape=(self.backend.core.shape(images)[0],),
+                minval=0,
+                maxval=1,
+                seed=seed,
+            )
+        else:
+            random_values = self.backend.random.uniform(
+                shape=(1,),
+                minval=0,
+                maxval=1,
+                seed=seed,
+            )
path: keras/src/layers/preprocessing/image_preprocessing/random_grayscale.py
text: @user1:
This may be a cleaner way to write this

```suggestion
        # Base case: Unbatched data
        batch_size = 1
        if len(images.shape) == 4:
            # This is a batch of images (4D input) 
            batch_size = self.backend.core.shape(images)[0]

        random_values = self.backend.random.uniform(
                shape=(batch_size,),
                minval=0,
                maxval=1,
                seed=seed,
            )
```