diff --git a/src/model_modules/advanced_paddings.py b/src/model_modules/advanced_paddings.py new file mode 100644 index 0000000000000000000000000000000000000000..0929eef8cf0e8eeed41c001d3335293fff407990 --- /dev/null +++ b/src/model_modules/advanced_paddings.py @@ -0,0 +1,157 @@ +__author__ = 'Felix Kleinert' +__date__ = '2020-03-02' + +import tensorflow as tf +import numpy as np +import keras.backend as K + +from keras.layers.convolutional import _ZeroPadding +from keras.legacy import interfaces +from keras.utils import conv_utils +from keras.utils.generic_utils import transpose_shape +from keras.backend.common import normalize_data_format + + +# class pad_utils: +# @staticmethod +# def get_padding_for_same(kernel_size, strides=1): +# ''' +# This methods calculates the padding size to keep input and output dimensions equal for a given kernel size +# (STRIDES HAVE TO BE EQUAL TO ONE!) +# :param kernel_size: +# :return: +# ''' +# if strides != 1: +# raise NotImplementedError("Strides other than 1 not implemented!") +# ks = np.array(kernel_size, dtype=np.int64) +# if (d & 0x1 for d in ks): +# pad = ((ks - 1) / 2).astype(np.int64) +# # pad = ((pad[0], pad[0]), (pad[1], pad[1])) +# return pad +# else: +# raise NotImplementedError("even kernel size not implemented") +# + + + +class ReflectionPadding2D(_ZeroPadding): + """Zero-padding layer for 2D input (e.g. picture). + + This layer can add rows and columns of zeros + at the top, bottom, left and right side of an image tensor. + + # Arguments + padding: int, or tuple of 2 ints, or tuple of 2 tuples of 2 ints. + - If int: the same symmetric padding + is applied to height and width. + - If tuple of 2 ints: + interpreted as two different + symmetric padding values for height and width: + `(symmetric_height_pad, symmetric_width_pad)`. + - If tuple of 2 tuples of 2 ints: + interpreted as + `((top_pad, bottom_pad), (left_pad, right_pad))` + data_format: A string, + one of `"channels_last"` or `"channels_first"`. + The ordering of the dimensions in the inputs. + `"channels_last"` corresponds to inputs with shape + `(batch, height, width, channels)` while `"channels_first"` + corresponds to inputs with shape + `(batch, channels, height, width)`. + It defaults to the `image_data_format` value found in your + Keras config file at `~/.keras/keras.json`. + If you never set it, then it will be "channels_last". + + # Input shape + 4D tensor with shape: + - If `data_format` is `"channels_last"`: + `(batch, rows, cols, channels)` + - If `data_format` is `"channels_first"`: + `(batch, channels, rows, cols)` + + # Output shape + 4D tensor with shape: + - If `data_format` is `"channels_last"`: + `(batch, padded_rows, padded_cols, channels)` + - If `data_format` is `"channels_first"`: + `(batch, channels, padded_rows, padded_cols)` + """ + + @interfaces.legacy_zeropadding2d_support + def __init__(self, + padding=(1, 1), + data_format=None, + **kwargs): + if isinstance(padding, int): + normalized_padding = ((padding, padding), (padding, padding)) + elif hasattr(padding, '__len__'): + if len(padding) != 2: + raise ValueError('`padding` should have two elements. ' + 'Found: ' + str(padding)) + height_padding = conv_utils.normalize_tuple(padding[0], 2, + '1st entry of padding') + width_padding = conv_utils.normalize_tuple(padding[1], 2, + '2nd entry of padding') + normalized_padding = (height_padding, width_padding) + else: + raise ValueError('`padding` should be either an int, ' + 'a tuple of 2 ints ' + '(symmetric_height_pad, symmetric_width_pad), ' + 'or a tuple of 2 tuples of 2 ints ' + '((top_pad, bottom_pad), (left_pad, right_pad)). ' + 'Found: ' + str(padding)) + super(ReflectionPadding2D, self).__init__(normalized_padding, + data_format, + **kwargs) + + @staticmethod + def spatial_2d_padding(x, padding=((1, 1), (1, 1)), data_format=None): + """Pads the 2nd and 3rd dimensions of a 4D tensor. + + # Arguments + x: Tensor or variable. + padding: Tuple of 2 tuples, padding pattern. + data_format: string, `"channels_last"` or `"channels_first"`. + + # Returns + A padded 4D tensor. + + # Raises + ValueError: if `data_format` is neither `"channels_last"` or `"channels_first"`. + """ + assert len(padding) == 2 + assert len(padding[0]) == 2 + assert len(padding[1]) == 2 + data_format = normalize_data_format(data_format) + + pattern = [[0, 0], + list(padding[0]), + list(padding[1]), + [0, 0]] + pattern = transpose_shape(pattern, data_format, spatial_axes=(1, 2)) + return pattern + + def call(self, inputs, mask=None): + pattern = self.spatial_2d_padding(inputs, padding=self.padding, data_format=self.data_format) + return tf.pad(inputs, pattern, 'REFLECT') + + +if __name__ == '__main__': + from keras.models import Model + from keras.layers import Conv2D, Flatten, Dense, Input + x = np.array(range(2000)).reshape(-1,10,10,1) + y = x.mean(axis=(1,2)) + + x_input = Input(shape=x.shape[1:]) + x_out = ReflectionPadding2D(padding=(1,1))(x_input) + print(x_out.get_shape()) + x_out = Conv2D(10, kernel_size=(3,3), activation='relu')(x_out) + x_out = Flatten()(x_out) + x_out = Dense(1, activation='linear')(x_out) + + model = Model(inputs=x_input, outputs=x_out) + model.compile('adam', loss='mse') + model.summary() + hist = model.fit(x, y, epochs=10) + +