mirror of
https://github.com/wassname/keras-contrib.git
synced 2026-07-25 13:20:46 +08:00
539 lines
25 KiB
Python
539 lines
25 KiB
Python
# -*- coding: utf-8 -*-
|
|
from __future__ import absolute_import
|
|
import functools
|
|
|
|
from .. import backend as K
|
|
from keras import activations
|
|
from keras import initializers
|
|
from keras import regularizers
|
|
from keras import constraints
|
|
from keras.engine import Layer
|
|
from keras.engine import InputSpec
|
|
from keras.layers.convolutional import Convolution3D
|
|
from keras.utils.generic_utils import get_custom_objects
|
|
from keras.utils.conv_utils import conv_output_length
|
|
from keras.utils.conv_utils import normalize_data_format
|
|
import numpy as np
|
|
|
|
|
|
class Deconvolution3D(Convolution3D):
|
|
"""Transposed convolution operator for filtering windows of 3-D inputs.
|
|
|
|
The need for transposed convolutions generally arises from the desire to
|
|
use a transformation going in the opposite direction
|
|
of a normal convolution, i.e., from something that has the shape
|
|
of the output of some convolution to something that has the shape
|
|
of its input while maintaining a connectivity pattern
|
|
that is compatible with said convolution.
|
|
|
|
When using this layer as the first layer in a model,
|
|
provide the keyword argument `input_shape`
|
|
(tuple of integers, does not include the sample axis),
|
|
e.g. `input_shape=(3, 128, 128, 128)` for a 128x128x128 volume with
|
|
three channels.
|
|
|
|
To pass the correct `output_shape` to this layer,
|
|
one could use a test model to predict and observe the actual output shape.
|
|
|
|
# Examples
|
|
|
|
```python
|
|
# TH dim ordering.
|
|
# apply a 3x3x3 transposed convolution
|
|
# with stride 1x1x1 and 3 output filters on a 12x12x12 image:
|
|
model = Sequential()
|
|
model.add(Deconvolution3D(3, 3, 3, 3, output_shape=(None, 3, 14, 14, 14),
|
|
padding='valid',
|
|
input_shape=(3, 12, 12, 12)))
|
|
|
|
# we can predict with the model and print the shape of the array.
|
|
dummy_input = np.ones((32, 3, 12, 12, 12))
|
|
preds = model.predict(dummy_input)
|
|
print(preds.shape) # (None, 3, 14, 14, 14)
|
|
|
|
# apply a 3x3x3 transposed convolution
|
|
# with stride 2x2x2 and 3 output filters on a 12x12x12 image:
|
|
model = Sequential()
|
|
model.add(Deconvolution3D(3, 3, 3, 3, output_shape=(None, 3, 25, 25, 25),
|
|
strides=(2, 2, 2),
|
|
padding='valid',
|
|
input_shape=(3, 12, 12, 12)))
|
|
model.summary()
|
|
|
|
# we can predict with the model and print the shape of the array.
|
|
dummy_input = np.ones((32, 3, 12, 12, 12))
|
|
preds = model.predict(dummy_input)
|
|
print(preds.shape) # (None, 3, 25, 25, 25)
|
|
```
|
|
|
|
```python
|
|
# TF dim ordering.
|
|
# apply a 3x3x3 transposed convolution
|
|
# with stride 1x1x1 and 3 output filters on a 12x12x12 image:
|
|
model = Sequential()
|
|
model.add(Deconvolution3D(3, 3, 3, 3, output_shape=(None, 14, 14, 14, 3),
|
|
padding='valid',
|
|
input_shape=(12, 12, 12, 3)))
|
|
|
|
# we can predict with the model and print the shape of the array.
|
|
dummy_input = np.ones((32, 12, 12, 12, 3))
|
|
preds = model.predict(dummy_input)
|
|
print(preds.shape) # (None, 14, 14, 14, 3)
|
|
|
|
# apply a 3x3x3 transposed convolution
|
|
# with stride 2x2x2 and 3 output filters on a 12x12x12 image:
|
|
model = Sequential()
|
|
model.add(Deconvolution3D(3, 3, 3, 3, output_shape=(None, 25, 25, 25, 3),
|
|
strides=(2, 2, 2),
|
|
padding='valid',
|
|
input_shape=(12, 12, 12, 3)))
|
|
model.summary()
|
|
|
|
# we can predict with the model and print the shape of the array.
|
|
dummy_input = np.ones((32, 12, 12, 12, 3))
|
|
preds = model.predict(dummy_input)
|
|
print(preds.shape) # (None, 25, 25, 25, 3)
|
|
```
|
|
|
|
# Arguments
|
|
filters: Number of transposed convolution filters to use.
|
|
kernel_size: kernel_size: An integer or tuple/list of 3 integers, specifying the
|
|
dimensions of the convolution window.
|
|
output_shape: Output shape of the transposed convolution operation.
|
|
tuple of integers
|
|
`(nb_samples, filters, conv_dim1, conv_dim2, conv_dim3)`.
|
|
It is better to use
|
|
a dummy input and observe the actual output shape of
|
|
a layer, as specified in the examples.
|
|
init: name of initialization function for the weights of the layer
|
|
(see [initializers](../initializers.md)), or alternatively,
|
|
Theano function to use for weights initialization.
|
|
This parameter is only relevant if you don't pass
|
|
a `weights` argument.
|
|
activation: name of activation function to use
|
|
(see [activations](../activations.md)),
|
|
or alternatively, elementwise Theano/TensorFlow function.
|
|
If you don't specify anything, no activation is applied
|
|
(ie. "linear" activation: a(x) = x).
|
|
weights: list of numpy arrays to set as initial weights.
|
|
padding: 'valid', 'same' or 'full'
|
|
('full' requires the Theano backend).
|
|
strides: tuple of length 3. Factor by which to oversample output.
|
|
Also called strides elsewhere.
|
|
kernel_regularizer: instance of [WeightRegularizer](../regularizers.md)
|
|
(eg. L1 or L2 regularization), applied to the main weights matrix.
|
|
bias_regularizer: instance of [WeightRegularizer](../regularizers.md),
|
|
applied to the use_bias.
|
|
activity_regularizer: instance of [ActivityRegularizer](../regularizers.md),
|
|
applied to the network output.
|
|
kernel_constraint: instance of the [constraints](../constraints.md) module
|
|
(eg. maxnorm, nonneg), applied to the main weights matrix.
|
|
bias_constraint: instance of the [constraints](../constraints.md) module,
|
|
applied to the use_bias.
|
|
data_format: 'channels_first' or 'channels_last'. In 'channels_first' mode, the channels dimension
|
|
(the depth) is at index 1, in 'channels_last' mode is it at index 4.
|
|
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 "tf".
|
|
use_bias: whether to include a use_bias
|
|
(i.e. make the layer affine rather than linear).
|
|
|
|
# Input shape
|
|
5D tensor with shape:
|
|
`(samples, channels, conv_dim1, conv_dim2, conv_dim3)` if data_format='channels_first'
|
|
or 5D tensor with shape:
|
|
`(samples, conv_dim1, conv_dim2, conv_dim3, channels)` if data_format='channels_last'.
|
|
|
|
# Output shape
|
|
5D tensor with shape:
|
|
`(samples, filters, nekernel_conv_dim1, nekernel_conv_dim2, nekernel_conv_dim3)` if data_format='channels_first'
|
|
or 5D tensor with shape:
|
|
`(samples, nekernel_conv_dim1, nekernel_conv_dim2, nekernel_conv_dim3, filters)` if data_format='channels_last'.
|
|
`nekernel_conv_dim1`, `nekernel_conv_dim2` and `nekernel_conv_dim3` values might have changed due to padding.
|
|
|
|
# References
|
|
- [A guide to convolution arithmetic for deep learning](https://arxiv.org/abs/1603.07285v1)
|
|
- [Transposed convolution arithmetic](http://deeplearning.net/software/theano_versions/dev/tutorial/conv_arithmetic.html#transposed-convolution-arithmetic)
|
|
- [Deconvolutional Networks](http://www.matthewzeiler.com/pubs/cvpr2010/cvpr2010.pdf)
|
|
"""
|
|
|
|
def __init__(self, filters, kernel_size,
|
|
output_shape, activation=None, weights=None,
|
|
padding='valid', strides=(1, 1, 1), data_format=None,
|
|
kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None,
|
|
kernel_constraint=None, bias_constraint=None,
|
|
use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', **kwargs):
|
|
if padding not in {'valid', 'same', 'full'}:
|
|
raise ValueError('Invalid border mode for Deconvolution3D:', padding)
|
|
if len(output_shape) == 4:
|
|
# missing the batch size
|
|
output_shape = (None,) + tuple(output_shape)
|
|
|
|
self.output_shape_ = output_shape
|
|
|
|
super(Deconvolution3D, self).__init__(kernel_size=kernel_size,
|
|
filters=filters,
|
|
activation=activation,
|
|
weights=weights,
|
|
padding=padding,
|
|
strides=strides,
|
|
data_format=data_format,
|
|
kernel_regularizer=kernel_regularizer,
|
|
bias_regularizer=bias_regularizer,
|
|
activity_regularizer=activity_regularizer,
|
|
kernel_constraint=kernel_constraint,
|
|
bias_constraint=bias_constraint,
|
|
use_bias=use_bias,
|
|
kernel_initializer=kernel_initializer,
|
|
bias_initializer=bias_initializer,
|
|
**kwargs)
|
|
|
|
def compute_output_shape(self, input_shape):
|
|
if self.data_format == 'channels_first':
|
|
conv_dim1 = self.output_shape_[2]
|
|
conv_dim2 = self.output_shape_[3]
|
|
conv_dim3 = self.output_shape_[4]
|
|
return (input_shape[0], self.filters, conv_dim1, conv_dim2, conv_dim3)
|
|
elif self.data_format == 'channels_last':
|
|
conv_dim1 = self.output_shape_[1]
|
|
conv_dim2 = self.output_shape_[2]
|
|
conv_dim3 = self.output_shape_[3]
|
|
return (input_shape[0], conv_dim1, conv_dim2, conv_dim3, self.filters)
|
|
else:
|
|
raise ValueError('Invalid data format: ', self.data_format)
|
|
|
|
def call(self, x, mask=None):
|
|
kernel_shape = K.get_value(self.kernel).shape
|
|
output = K.deconv3d(x, self.kernel, self.output_shape_,
|
|
strides=self.strides,
|
|
padding=self.padding,
|
|
data_format=self.data_format,
|
|
filter_shape=kernel_shape)
|
|
if self.use_bias:
|
|
if self.data_format == 'channels_first':
|
|
output += K.reshape(self.bias, (1, self.filters, 1, 1, 1))
|
|
elif self.data_format == 'channels_last':
|
|
output += K.reshape(self.bias, (1, 1, 1, 1, self.filters))
|
|
else:
|
|
raise ValueError('Invalid data_format: ', self.data_format)
|
|
output = self.activation(output)
|
|
return output
|
|
|
|
def get_config(self):
|
|
config = {'output_shape': self.output_shape_}
|
|
base_config = super(Deconvolution3D, self).get_config()
|
|
return dict(list(base_config.items()) + list(config.items()))
|
|
|
|
|
|
Deconv3D = Deconvolution3D
|
|
get_custom_objects().update({'Deconvolution3D': Deconvolution3D})
|
|
get_custom_objects().update({'Deconv3D': Deconv3D})
|
|
|
|
|
|
class CosineConvolution2D(Layer):
|
|
"""Cosine Normalized Convolution operator for filtering windows of two-dimensional inputs.
|
|
Cosine Normalization: Using Cosine Similarity Instead of Dot Product in Neural Networks
|
|
https://arxiv.org/pdf/1702.05870.pdf
|
|
|
|
When using this layer as the first layer in a model,
|
|
provide the keyword argument `input_shape`
|
|
(tuple of integers, does not include the sample axis),
|
|
e.g. `input_shape=(3, 128, 128)` for 128x128 RGB pictures.
|
|
|
|
# Examples
|
|
|
|
```python
|
|
# apply a 3x3 convolution with 64 output filters on a 256x256 image:
|
|
model = Sequential()
|
|
model.add(CosineConvolution2D(64, 3, 3,
|
|
padding='same',
|
|
input_shape=(3, 256, 256)))
|
|
# now model.output_shape == (None, 64, 256, 256)
|
|
|
|
# add a 3x3 convolution on top, with 32 output filters:
|
|
model.add(CosineConvolution2D(32, 3, 3, padding='same'))
|
|
# now model.output_shape == (None, 32, 256, 256)
|
|
```
|
|
|
|
# Arguments
|
|
filters: Number of convolution filters to use.
|
|
kernel_size: kernel_size: An integer or tuple/list of 2 integers, specifying the
|
|
dimensions of the convolution window.
|
|
init: name of initialization function for the weights of the layer
|
|
(see [initializers](../initializers.md)), or alternatively,
|
|
Theano function to use for weights initialization.
|
|
This parameter is only relevant if you don't pass
|
|
a `weights` argument.
|
|
activation: name of activation function to use
|
|
(see [activations](../activations.md)),
|
|
or alternatively, elementwise Theano function.
|
|
If you don't specify anything, no activation is applied
|
|
(ie. "linear" activation: a(x) = x).
|
|
weights: list of numpy arrays to set as initial weights.
|
|
padding: 'valid', 'same' or 'full'
|
|
('full' requires the Theano backend).
|
|
strides: tuple of length 2. Factor by which to strides output.
|
|
Also called strides elsewhere.
|
|
kernel_regularizer: instance of [WeightRegularizer](../regularizers.md)
|
|
(eg. L1 or L2 regularization), applied to the main weights matrix.
|
|
bias_regularizer: instance of [WeightRegularizer](../regularizers.md),
|
|
applied to the use_bias.
|
|
activity_regularizer: instance of [ActivityRegularizer](../regularizers.md),
|
|
applied to the network output.
|
|
kernel_constraint: instance of the [constraints](../constraints.md) module
|
|
(eg. maxnorm, nonneg), applied to the main weights matrix.
|
|
bias_constraint: instance of the [constraints](../constraints.md) module,
|
|
applied to the use_bias.
|
|
data_format: 'channels_first' or 'channels_last'. In 'channels_first' mode, the channels dimension
|
|
(the depth) is at index 1, in 'channels_last' mode is it at index 3.
|
|
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 "tf".
|
|
use_bias: whether to include a use_bias
|
|
(i.e. make the layer affine rather than linear).
|
|
|
|
# Input shape
|
|
4D tensor with shape:
|
|
`(samples, channels, rows, cols)` if data_format='channels_first'
|
|
or 4D tensor with shape:
|
|
`(samples, rows, cols, channels)` if data_format='channels_last'.
|
|
|
|
# Output shape
|
|
4D tensor with shape:
|
|
`(samples, filters, nekernel_rows, nekernel_cols)` if data_format='channels_first'
|
|
or 4D tensor with shape:
|
|
`(samples, nekernel_rows, nekernel_cols, filters)` if data_format='channels_last'.
|
|
`rows` and `cols` values might have changed due to padding.
|
|
"""
|
|
|
|
def __init__(self, filters, kernel_size,
|
|
kernel_initializer='glorot_uniform', activation=None, weights=None,
|
|
padding='valid', strides=(1, 1), data_format=None,
|
|
kernel_regularizer=None, bias_regularizer=None,
|
|
activity_regularizer=None,
|
|
kernel_constraint=None, bias_constraint=None,
|
|
use_bias=True, **kwargs):
|
|
if data_format is None:
|
|
data_format = K.image_data_format()
|
|
if padding not in {'valid', 'same', 'full'}:
|
|
raise ValueError('Invalid border mode for CosineConvolution2D:', padding)
|
|
self.filters = filters
|
|
self.kernel_size = kernel_size
|
|
self.nb_row, self.nb_col = self.kernel_size
|
|
self.kernel_initializer = initializers.get(kernel_initializer)
|
|
self.activation = activations.get(activation)
|
|
self.padding = padding
|
|
self.strides = tuple(strides)
|
|
self.data_format = normalize_data_format(data_format)
|
|
self.kernel_regularizer = regularizers.get(kernel_regularizer)
|
|
self.bias_regularizer = regularizers.get(bias_regularizer)
|
|
self.activity_regularizer = regularizers.get(activity_regularizer)
|
|
|
|
self.kernel_constraint = constraints.get(kernel_constraint)
|
|
self.bias_constraint = constraints.get(bias_constraint)
|
|
|
|
self.use_bias = use_bias
|
|
self.input_spec = [InputSpec(ndim=4)]
|
|
self.initial_weights = weights
|
|
super(CosineConvolution2D, self).__init__(**kwargs)
|
|
|
|
def build(self, input_shape):
|
|
if self.data_format == 'channels_first':
|
|
stack_size = input_shape[1]
|
|
self.kernel_shape = (self.filters, stack_size, self.nb_row, self.nb_col)
|
|
self.kernel_norm_shape = (1, stack_size, self.nb_row, self.nb_col)
|
|
elif self.data_format == 'channels_last':
|
|
stack_size = input_shape[3]
|
|
self.kernel_shape = (self.nb_row, self.nb_col, stack_size, self.filters)
|
|
self.kernel_norm_shape = (self.nb_row, self.nb_col, stack_size, 1)
|
|
else:
|
|
raise ValueError('Invalid data_format:', self.data_format)
|
|
self.W = self.add_weight(self.kernel_shape,
|
|
initializer=functools.partial(self.kernel_initializer),
|
|
name='{}_W'.format(self.name),
|
|
regularizer=self.kernel_regularizer,
|
|
constraint=self.kernel_constraint)
|
|
|
|
self.kernel_norm = K.variable(np.ones(self.kernel_norm_shape), name='{}_kernel_norm'.format(self.name))
|
|
|
|
if self.use_bias:
|
|
self.b = self.add_weight((self.filters,),
|
|
initializer='zero',
|
|
name='{}_b'.format(self.name),
|
|
regularizer=self.bias_regularizer,
|
|
constraint=self.bias_constraint)
|
|
else:
|
|
self.b = None
|
|
|
|
if self.initial_weights is not None:
|
|
self.set_weights(self.initial_weights)
|
|
del self.initial_weights
|
|
self.built = True
|
|
|
|
def compute_output_shape(self, input_shape):
|
|
if self.data_format == 'channels_first':
|
|
rows = input_shape[2]
|
|
cols = input_shape[3]
|
|
elif self.data_format == 'channels_last':
|
|
rows = input_shape[1]
|
|
cols = input_shape[2]
|
|
else:
|
|
raise ValueError('Invalid data_format:', self.data_format)
|
|
|
|
rows = conv_output_length(rows, self.nb_row,
|
|
self.padding, self.strides[0])
|
|
cols = conv_output_length(cols, self.nb_col,
|
|
self.padding, self.strides[1])
|
|
|
|
if self.data_format == 'channels_first':
|
|
return (input_shape[0], self.filters, rows, cols)
|
|
elif self.data_format == 'channels_last':
|
|
return (input_shape[0], rows, cols, self.filters)
|
|
|
|
def call(self, x, mask=None):
|
|
b, xb = 0., 0.
|
|
if self.data_format == 'channels_first':
|
|
kernel_sum_axes = [1, 2, 3]
|
|
if self.use_bias:
|
|
b = K.reshape(self.b, (self.filters, 1, 1, 1))
|
|
xb = 1.
|
|
elif self.data_format == 'channels_last':
|
|
kernel_sum_axes = [0, 1, 2]
|
|
if self.use_bias:
|
|
b = K.reshape(self.b, (1, 1, 1, self.filters))
|
|
xb = 1.
|
|
|
|
Wnorm = K.sqrt(K.sum(K.square(self.W), axis=kernel_sum_axes, keepdims=True) + K.square(b) + K.epsilon())
|
|
xnorm = K.sqrt(K.conv2d(K.square(x), self.kernel_norm, strides=self.strides,
|
|
padding=self.padding,
|
|
data_format=self.data_format,
|
|
filter_shape=self.kernel_norm_shape) + xb + K.epsilon())
|
|
|
|
W = self.W / Wnorm
|
|
|
|
output = K.conv2d(x, W, strides=self.strides,
|
|
padding=self.padding,
|
|
data_format=self.data_format,
|
|
filter_shape=self.kernel_shape)
|
|
|
|
if K.backend() == 'theano':
|
|
xnorm = K.pattern_broadcast(xnorm, [False, True, False, False])
|
|
|
|
output /= xnorm
|
|
|
|
if self.use_bias:
|
|
b /= Wnorm
|
|
if self.data_format == 'channels_first':
|
|
b = K.reshape(b, (1, self.filters, 1, 1))
|
|
elif self.data_format == 'channels_last':
|
|
b = K.reshape(b, (1, 1, 1, self.filters))
|
|
else:
|
|
raise ValueError('Invalid data_format:', self.data_format)
|
|
b /= xnorm
|
|
output += b
|
|
output = self.activation(output)
|
|
return output
|
|
|
|
def get_config(self):
|
|
config = {'filters': self.filters,
|
|
'kernel_size': self.kernel_size,
|
|
'kernel_initializer': initializers.serialize(self.kernel_initializer),
|
|
'activation': activations.serialize(self.activation),
|
|
'padding': self.padding,
|
|
'strides': self.strides,
|
|
'data_format': self.data_format,
|
|
'kernel_regularizer': regularizers.serialize(self.kernel_regularizer),
|
|
'bias_regularizer': regularizers.serialize(self.bias_regularizer),
|
|
'activity_regularizer': regularizers.serialize(self.activity_regularizer),
|
|
'kernel_constraint': constraints.serialize(self.kernel_constraint),
|
|
'bias_constraint': constraints.serialize(self.bias_constraint),
|
|
'use_bias': self.use_bias}
|
|
base_config = super(CosineConvolution2D, self).get_config()
|
|
return dict(list(base_config.items()) + list(config.items()))
|
|
|
|
|
|
CosineConv2D = CosineConvolution2D
|
|
get_custom_objects().update({'CosineConvolution2D': CosineConvolution2D})
|
|
get_custom_objects().update({'CosineConv2D': CosineConv2D})
|
|
|
|
|
|
class SubPixelUpscaling(Layer):
|
|
""" Sub-pixel convolutional upscaling layer based on the paper "Real-Time Single Image
|
|
and Video Super-Resolution Using an Efficient Sub-Pixel Convolutional Neural Network"
|
|
(https://arxiv.org/abs/1609.05158).
|
|
|
|
This layer requires a Convolution2D prior to it, having output filters computed according to
|
|
the formula :
|
|
|
|
filters = k * (scale_factor * scale_factor)
|
|
where k = a user defined number of filters (generally larger than 32)
|
|
scale_factor = the upscaling factor (generally 2)
|
|
|
|
This layer performs the depth to space operation on the convolution filters, and returns a
|
|
tensor with the size as defined below.
|
|
|
|
# Example :
|
|
```python
|
|
# A standard subpixel upscaling block
|
|
x = Convolution2D(256, 3, 3, padding='same', activation='relu')(...)
|
|
u = SubPixelUpscaling(scale_factor=2)(x)
|
|
|
|
[Optional]
|
|
x = Convolution2D(256, 3, 3, padding='same', activation='relu')(u)
|
|
```
|
|
|
|
In practice, it is useful to have a second convolution layer after the
|
|
SubPixelUpscaling layer to speed up the learning process.
|
|
|
|
However, if you are stacking multiple SubPixelUpscaling blocks, it may increase
|
|
the number of parameters greatly, so the Convolution layer after SubPixelUpscaling
|
|
layer can be removed.
|
|
|
|
# Arguments
|
|
scale_factor: Upscaling factor.
|
|
data_format: Can be None, 'channels_first' or 'channels_last'.
|
|
|
|
# Input shape
|
|
4D tensor with shape:
|
|
`(samples, k * (scale_factor * scale_factor) channels, rows, cols)` if data_format='channels_first'
|
|
or 4D tensor with shape:
|
|
`(samples, rows, cols, k * (scale_factor * scale_factor) channels)` if data_format='channels_last'.
|
|
|
|
# Output shape
|
|
4D tensor with shape:
|
|
`(samples, k channels, rows * scale_factor, cols * scale_factor))` if data_format='channels_first'
|
|
or 4D tensor with shape:
|
|
`(samples, rows * scale_factor, cols * scale_factor, k channels)` if data_format='channels_last'.
|
|
|
|
"""
|
|
|
|
def __init__(self, scale_factor=2, data_format=None, **kwargs):
|
|
super(SubPixelUpscaling, self).__init__(**kwargs)
|
|
|
|
self.scale_factor = scale_factor
|
|
self.data_format = normalize_data_format(data_format)
|
|
|
|
def build(self, input_shape):
|
|
pass
|
|
|
|
def call(self, x, mask=None):
|
|
y = K.depth_to_space(x, self.scale_factor, self.data_format)
|
|
return y
|
|
|
|
def compute_output_shape(self, input_shape):
|
|
if self.data_format == 'channels_first':
|
|
b, k, r, c = input_shape
|
|
return (b, k // (self.scale_factor ** 2), r * self.scale_factor, c * self.scale_factor)
|
|
else:
|
|
b, r, c, k = input_shape
|
|
return (b, r * self.scale_factor, c * self.scale_factor, k // (self.scale_factor ** 2))
|
|
|
|
def get_config(self):
|
|
config = {'scale_factor': self.scale_factor,
|
|
'data_format': self.data_format}
|
|
base_config = super(SubPixelUpscaling, self).get_config()
|
|
return dict(list(base_config.items()) + list(config.items()))
|
|
|
|
|
|
get_custom_objects().update({'SubPixelUpscaling': SubPixelUpscaling})
|