Search code examples
tensorflow

Why does tensorflow's image.adjust_contrast method claim it's missing a positional input


I've constructed a mini-version of a model I want to train for the sake of debugging with this function:

def build_mini_model(contrast_factor=1.25):
    # Define input layer with variable input shape
    inputs = tf.keras.Input(shape=(None, None, 3))  # Accepts images of any size with 3 channels (RGB)

    # Convert RGB to grayscale
    x= tf.image.rgb_to_grayscale(inputs)

    # Enhance contrast
    x = tf.image.adjust_contrast(x, contrast_factor)

    # Create a functional model
    mini_model = Model(inputs=inputs, outputs=x)
    return mini_model

I then create and compile it:

mini_model = build_mini_model()
mini_model.compile(optimizer=Adam(), loss='sparse_categorical_crossentropy', 
              metrics=['accuracy'])
mini_model.summary()

and see the following summary:

Model: "model_3"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_5 (InputLayer)        [(None, None, None, 3)]   0         
                                                                 
 tf.image.rgb_to_grayscale_  (None, None, None, 1)     0         
 4 (TFOpLambda)                                                  
                                                                 
 tf.image.adjust_contrast_4  (None, None, None, 1)     0         
  (TFOpLambda)                                                   
                                                                 
=================================================================
Total params: 0 (0.00 Byte)
Trainable params: 0 (0.00 Byte)
Non-trainable params: 0 (0.00 Byte)

But, when I try to test pusing an image through it like this:

l_in = tf.random.normal((1, 32, 32, 3))
for x in range(len(mini_model.layers)):
    print(x,mini_model.layers[x].name, l_in.shape)
    l_out = mini_model.layers[x](l_in)  
    print("              ", l_out.shape)
    l_in = l_out

I get the following output:

0 input_5 (1, 32, 32, 3)
               (1, 32, 32, 3)
1 tf.image.rgb_to_grayscale_4 (1, 32, 32, 3)
               (1, 32, 32, 1)
2 tf.image.adjust_contrast_4 (1, 32, 32, 1)


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[68], line 4
      2 for x in range(len(mini_model.layers)):
      3     print(x,mini_model.layers[x].name, l_in.shape)
----> 4     l_out = mini_model.layers[x](l_in) 
      5     print("              ", l_out.shape)
      6     l_in = l_out

File ~/anaconda3/envs/IK_env/lib/python3.11/site-packages/keras/src/utils/traceback_utils.py:70, in filter_traceback.<locals>.error_handler(*args, **kwargs)
     67     filtered_tb = _process_traceback_frames(e.__traceback__)
     68     # To get the full stack trace, call:
     69     # `tf.debugging.disable_traceback_filtering()`
---> 70     raise e.with_traceback(filtered_tb) from None
     71 finally:
     72     del filtered_tb

File ~/anaconda3/envs/IK_env/lib/python3.11/site-packages/tensorflow/python/util/dispatch.py:1170, in add_dispatch_support.<locals>.decorator.<locals>.op_dispatch_handler(*args, **kwargs)
   1168 if iterable_params is not None:
   1169   args, kwargs = replace_iterable_params(args, kwargs, iterable_params)
-> 1170 result = api_dispatcher.Dispatch(args, kwargs)
   1171 if result is not NotImplemented:
   1172   return result

TypeError: Missing required positional argument

When I go to the documentation at TF_Docs, I see that tf.image.adjust_contrast only expects 2 arguments:

tf.image.adjust_contrast(
    images, contrast_factor
)

which I've given it.

If I break this down layer-by-layer and explicitly call the tf.image.adjust_contrast, everything is fine:

l_in = tf.random.normal((1, 32, 32, 3))
l_out = model.layers[0](l_in)
print(0,model.layers[0].name, l_in.shape)
print("    ",l_out.shape)

l_in = l_out
l_out = model.layers[1](l_in)
print(1,model.layers[1].name, l_in.shape)
print("    ",l_out.shape)

l_in = l_out
print(x,model.layers[2].name, l_in.shape)
l_out = tf.image.adjust_contrast(l_in, 1.25)
print("    ",l_out.shape)

Gives this output:

0 input_3 (1, 32, 32, 3)
     (1, 32, 32, 3)
1 tf.image.rgb_to_grayscale_2 (1, 32, 32, 3)
     (1, 32, 32, 1)
2 tf.image.adjust_contrast_2 (1, 32, 32, 1)
     (1, 32, 32, 1)

What am I doing wrong here?


Solution

  • It seems like(?) you're invoking every layer's call method individually by mini_model.layers[x](l_in), so for adjust_contrast it should be mini_model.layers[x](l_in, constrast_factor).

    The easiest way to solve this is to create a new layer by subclassing:

    class AdjustContrastLayer(tf.keras.layers.Layer):
        def __init__(self, contrast_factor, **kwargs):
            super(AdjustContrastLayer, self).__init__(**kwargs)
            self.contrast_factor = contrast_factor
    
        def call(self, inputs):
            return tf.image.adjust_contrast(inputs, self.contrast_factor)
    
        def get_config(self):
            config = super(AdjustContrastLayer, self).get_config()
            config.update({"contrast_factor": self.contrast_factor})
            return config
    

    The model construction looks like this:

    def build_mini_model(contrast_factor=1.25):
        inputs = tf.keras.Input(shape=(None, None, 3))
    
        x= tf.image.rgb_to_grayscale(inputs)
    
        x = AdjustContrastLayer(contrast_factor)(x)
    
        mini_model = Model(inputs=inputs, outputs=x)
        return mini_model
    

    After that your for loop should also execute normally.