3
votes

I need to implement salt & pepper layer in keras like Gaussian noise, I tried to use the following code but it produces several errors. could you please tell me what is the problem? do you have any other suggestion for implementing S&P layer? Thank you.

from keras.engine.topology import Layer

class SaltAndPepper(Layer):

    def __init__(self, ratio, **kwargs):
        super(SaltAndPepper, self).__init__(**kwargs)
        self.supports_masking = True
        self.ratio = ratio

    def call(self, inputs, training=None):
        def noised():
            r = self.ratio*10
            s = inputs.shape[1]
            n = int( s * r/10 )
            perm = np.random.permutation(r)[:n]
            inputs[perm] = (np.random.rand(n) > 0.5)
            return inputs

        return K.in_train_phase(noised(), inputs, training=training)

    def get_config(self):
        config = {'ratio': self.ratio}
        base_config = super(SaltAndPepper, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

Traceback (most recent call last):

File "", line 125, in decoded_noise=SaltAndPepper(0.5)(decoded)

File "D:\software\Anaconda3\envs\py36\lib\site-packages\keras\engine\base_layer.py", line 457, in call output = self.call(inputs, **kwargs)

File "", line 57, in call return K.in_train_phase(noised(), inputs, training=training)

File "", line 52, in noised n = int( s * r/10 )

TypeError: unsupported operand type(s) for /: 'Dimension' and 'int'

Update:

I used @today's solution and wrote the following code:

decoded_noise=call(0.05,bncv11)#16

which bncv11 is the output of batch normalization layer before it.

but it produces this error, why does it happen?

Traceback (most recent call last):

File "", line 59, in decoded_noise=call(0.05,bncv11)#16

File "", line 34, in call return K.in_train_phase(noised(), inputs, training=training)

File "", line 29, in noised mask_select = K.random_binomial(shape=shp, p=self.ratio)

AttributeError: 'float' object has no attribute 'ratio'

after saving the model and used it produces this error:

Traceback (most recent call last):

File "", line 1, in b=load_model('Desktop/los4x4_con_tile_convolw_FBN_SigAct_SandPAttack05.h5',custom_objects={'tf':tf})

File "D:\software\Anaconda3\envs\py36\lib\site-packages\keras\engine\saving.py", line 419, in load_model model = _deserialize_model(f, custom_objects, compile)

File "D:\software\Anaconda3\envs\py36\lib\site-packages\keras\engine\saving.py", line 225, in _deserialize_model model = model_from_config(model_config, custom_objects=custom_objects)

File "D:\software\Anaconda3\envs\py36\lib\site-packages\keras\engine\saving.py", line 458, in model_from_config return deserialize(config, custom_objects=custom_objects)

File "D:\software\Anaconda3\envs\py36\lib\site-packages\keras\layers__init__.py", line 55, in deserialize printable_module_name='layer')

File "D:\software\Anaconda3\envs\py36\lib\site-packages\keras\utils\generic_utils.py", line 145, in deserialize_keras_object list(custom_objects.items())))

File "D:\software\Anaconda3\envs\py36\lib\site-packages\keras\engine\network.py", line 1022, in from_config process_layer(layer_data)

File "D:\software\Anaconda3\envs\py36\lib\site-packages\keras\engine\network.py", line 1008, in process_layer custom_objects=custom_objects)

File "D:\software\Anaconda3\envs\py36\lib\site-packages\keras\layers__init__.py", line 55, in deserialize printable_module_name='layer')

File "D:\software\Anaconda3\envs\py36\lib\site-packages\keras\utils\generic_utils.py", line 138, in deserialize_keras_object ': ' + class_name)

ValueError: Unknown layer: SaltAndPepper

I put this code in my program where I define my network structure:

from keras.engine.topology import Layer

class SaltAndPepper(Layer):

    def __init__(self, ratio, **kwargs):
        super(SaltAndPepper, self).__init__(**kwargs)
        self.supports_masking = True
        self.ratio = ratio

    # the definition of the call method of custom layer
    def call(self, inputs, training=True):
        def noised():
            shp = K.shape(inputs)[1:]
            mask_select = K.random_binomial(shape=shp, p=self.ratio)
            mask_noise = K.random_binomial(shape=shp, p=0.5) # salt and pepper have the same chance
            out = inputs * (1-mask_select) + mask_noise * mask_select
            return out

        return K.in_train_phase(noised(), inputs, training=training)

    def get_config(self):
        config = {'ratio': self.ratio}
        base_config = super(SaltAndPepper, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))
1
The stacktrace say that your ratio is a Dimension object and not a number variable.Nakeuh
what does it mean? because I am a beginner and I do not know what this code does. what should I solve it?david
is it possible to implement s&p noise with lambda layer or we should produce a layer like this?david
To be honest I don't know much about s&p layers. But the error you have is a pretty basic python error. In your code, you try to do the following division r/10, and the error message tells you that it's not possible since the r variable is not a number. The r value come from the ratio (the second parameter you use when creating your Layer. It should be a number, and I suppose that it is not. Can you provide the code you use to create your Layer ?Nakeuh

1 Answers

2
votes

In image processing, salt and pepper noise basically changes the value of a ratio of pixels, which are selected randomly, to either salt (i.e. white, which is usually 1 or 255 depending on the range of image values) or pepper (i.e. black which is usually 0). Although, we can use the same idea in other domains besides image processing. So you must specify three things first:

  1. How many of the pixels should be changed? (noise ratio)
  2. Which pixels should be selected and changed?
  3. Which of those selected pixels should be salted (and the other peppered)?

Since there is a function in Keras backend to generate random values from a binomial distribution (i.e. 0 or 1) with a given probability, we can easily do all the above steps by generating two masks: one for selecting the pixels with the given ratio, and the other one for applying either of salt or pepper to those selected pixels. Here is how to do it:

from keras import backend as K

# NOTE: this is the definition of the call method of custom layer class (i.e. SaltAndPepper)
def call(self, inputs, training=None):
    def noised():
        shp = K.shape(inputs)[1:]
        mask_select = K.random_binomial(shape=shp, p=self.ratio)
        mask_noise = K.random_binomial(shape=shp, p=0.5) # salt and pepper have the same chance
        out = inputs * (1-mask_select) + mask_noise * mask_select
        return out

    return K.in_train_phase(noised(), inputs, training=training)

Note in the code above I have assumed a few things:

  • The value of the given noise ratio is in the range [0,1].
  • If you are using this layer to directly apply it on images, then you must note that it assumes that the images are grayscale (i.e. single color channel). To use it for RGB images (i.e. three color channels) you may need to modify it a little such that a pixel with all of its channels are selected for adding the noise (although, this depends on how you define and use salt and pepper noise).
  • It assumes the value of salt is 1 and the value of pepper is 0. Though, you can easily change the value of salt to x and pepper to y by changing the definition of mask_noise as follow:

    mask_noise = K.random_binomial(shape=shp, p=0.5) * (x-y) + y
    
  • The same noise pattern is applied on the all the samples in a batch (however, it would be different from batch to batch).