class_docstr: ### Description
          This is a generic decoder composed of
          fully-connected layers followed by
          {upsampling then transpose convolution} blocks.
          There is no batch normalisation on
          the final transpose convolutional layer.
class_docstr_tok: ["#","#","#","Description","This","is","a","generic","decoder","composed","of","fully-connected","layers","followed","by","{","upsampling","then","transpose","convolution","}","blocks",".","There","is","no","batch","normalisation","on","the","final","transpose","convolutional","layer","."]
docstr: :param codes: tensor, input latent space after gaussian sampling
  :param is_training: boolean, True if network is in training mode
  :return: tensor, output of decoding branch
docstr_tok: [":","param","codes",":","tensor",",","input","latent","space","after","gaussian","sampling",":","param","is_training",":","boolean",",","True","if","network","is","in","training","mode",":","return",":","tensor",",","output","of","decoding","branch"]
code: def layer_op(self, codes, is_training):
      """
  
      :param codes: tensor, input latent space after gaussian sampling
      :param is_training: boolean, True if network is in training mode
      :return: tensor, output of decoding branch
      """
  
      # Define the decoding fully-connected layers
      decoders_fc = []
      for i in range(0, len(self.layer_sizes_decoder)):
          decoders_fc.append(
              FullyConnectedLayer(
                  n_output_chns=self.layer_sizes_decoder[i],
                  with_bias=True,
                  feature_normalization="batch",
                  acti_func=self.acti_func_decoder[i],
                  w_initializer=self.initializers["w"],
                  w_regularizer=self.regularizers["w"],
                  name="decoder_fc_{}".format(
                      self.layer_sizes_decoder[i]
                  ),
              )
          )
          print(decoders_fc[-1])
  
      # Define the decoding convolutional layers
      decoders_cnn = []
      decoders_upsamplers = []
      for i in range(0, len(self.trans_conv_output_channels)):
          if self.upsampling_mode == "DECONV":
              decoders_upsamplers.append(
                  DeconvolutionalLayer(
                      n_output_chns=self.trans_conv_output_channels[i],
                      kernel_size=self.trans_conv_unpooling_factors[i],
                      stride=self.trans_conv_unpooling_factors[i],
                      padding="SAME",
                      with_bias=True,
                      feature_normalization="batch",
                      w_initializer=self.initializers["w"],
                      w_regularizer=None,
                      acti_func=None,
                      name="decoder_upsampler_{}_{}".format(
                          self.trans_conv_unpooling_factors[i],
                          self.trans_conv_unpooling_factors[i],
                      ),
                  )
              )
              print(decoders_upsamplers[-1])
  
          decoders_cnn.append(
              DeconvolutionalLayer(
                  n_output_chns=self.trans_conv_output_channels[i],
                  kernel_size=self.trans_conv_kernel_sizes[i],
                  stride=1,
                  padding="SAME",
                  with_bias=True,
                  feature_normalization="batch",
                  # feature_normalization=not (i == len(self.trans_conv_output_channels) - 1),
                  # No BN on output
                  w_initializer=self.initializers["w"],
                  w_regularizer=None,
                  acti_func=self.acti_func_trans_conv[i],
                  name="decoder_trans_conv_{}_{}".format(
                      self.trans_conv_kernel_sizes[i],
                      self.trans_conv_output_channels[i],
                  ),
              )
          )
          print(decoders_cnn[-1])
  
      # Fully-connected decoder layers
      flow = codes
      for i in range(0, len(self.layer_sizes_decoder)):
          flow = decoders_fc[i](flow, is_training)
  
      # Reconstitute the feature maps
      flow = tf.reshape(flow, [-1] + self.downsampled_shape)
  
      # Convolutional decoder layers
      for i in range(0, len(self.trans_conv_output_channels)):
          if self.upsampling_mode == "DECONV":
              flow = decoders_upsamplers[i](flow, is_training)
          elif self.upsampling_mode == "CHANNELWISE_DECONV":
              flow = UpSampleLayer(
                  "CHANNELWISE_DECONV",
                  kernel_size=self.trans_conv_unpooling_factors[i],
                  stride=self.trans_conv_unpooling_factors[i],
              )(flow)
          elif self.upsampling_mode == "REPLICATE":
              flow = UpSampleLayer(
                  "REPLICATE",
                  kernel_size=self.trans_conv_unpooling_factors[i],
                  stride=self.trans_conv_unpooling_factors[i],
              )(flow)
          flow = decoders_cnn[i](flow, is_training)
  
      return flow
  
imports: from __future__ import absolute_import, print_function, division
  import numpy
  import tensorflow
  from niftynet.layer.base_layer import TrainableLayer
  from niftynet.layer.convolution import ConvolutionalLayer
  from niftynet.layer.deconvolution import DeconvolutionalLayer
  from niftynet.layer.fully_connected import FullyConnectedLayer
  from niftynet.layer.upsample import UpSampleLayer
label_desc: Use image node_id 2 for calling the ConvDecoder obj's underlying member method code with example usage: obj.layer_op(codes, is_training) and returns: flow