class_docstr: implementation of ScaleNet:
      Fidon et al., "Scalable multimodal convolutional
      networks for brain tumour segmentation", MICCAI '17
  
  ### Diagram
  
  INPUT --> [BACKEND] ----> [MERGING] ----> [FRONTEND] ---> OUTPUT
  
  [BACKEND] and [MERGING] are provided by the ScaleBlock below
  [FRONTEND]: it can be any NiftyNet network (default: HighRes3dnet)
  
  ### Constraints:
  - Input image size should be divisible by 8
  - more than one modality should be used
class_docstr_tok: ["implementation","of","ScaleNet",":","Fidon","et","al.",",","``","Scalable","multimodal","convolutional","networks","for","brain","tumour","segmentation","''",",","MICCAI","'17","#","#","#","Diagram","INPUT","--",">","[","BACKEND","]","--","--",">","[","MERGING","]","--","--",">","[","FRONTEND","]","--","-",">","OUTPUT","[","BACKEND","]","and","[","MERGING","]","are","provided","by","the","ScaleBlock","below","[","FRONTEND","]",":","it","can","be","any","NiftyNet","network","(","default",":","HighRes3dnet",")","#","#","#","Constraints",":","-","Input","image","size","should","be","divisible","by","8","-","more","than","one","modality","should","be","used"]
docstr: :param images: tensor, concatenation of multiple input modalities
  :param is_training: boolean, True if network is in training mode
  :param layer_id: not in use
  :param unused_kwargs:
  :return: predicted tensor
docstr_tok: [":","param","images",":","tensor",",","concatenation","of","multiple","input","modalities",":","param","is_training",":","boolean",",","True","if","network","is","in","training","mode",":","param","layer_id",":","not","in","use",":","param","unused_kwargs",":",":","return",":","predicted","tensor"]
code: def layer_op(
      self, images, is_training=True, layer_id=-1, **unused_kwargs
  ):
      """
  
      :param images: tensor, concatenation of multiple input modalities
      :param is_training: boolean, True if network is in training mode
      :param layer_id: not in use
      :param unused_kwargs:
      :return: predicted tensor
      """
      n_modality = images.shape.as_list()[-1]
      rank = images.shape.ndims
      assert n_modality > 1
      roots = tf.split(images, n_modality, axis=rank - 1)
      for idx, root in enumerate(roots):
          conv_layer = ConvolutionalLayer(
              n_output_chns=self.n_features,
              kernel_size=3,
              w_initializer=self.initializers["w"],
              w_regularizer=self.regularizers["w"],
              acti_func=self.acti_func,
              name="conv_{}".format(idx),
          )
          roots[idx] = conv_layer(root, is_training)
      roots = tf.stack(roots, axis=-1)
  
      back_end = ScaleBlock("AVERAGE", n_layers=1)
      output_tensor = back_end(roots, is_training)
  
      front_end = HighRes3DNet(self.num_classes)
      output_tensor = front_end(output_tensor, is_training)
      return output_tensor
  
file_path: NiftyNet/niftynet/network/scalenet.py
imports: from __future__ import absolute_import, print_function, division
  import tensorflow
  from niftynet.layer.base_layer import TrainableLayer
  from niftynet.layer.convolution import ConvolutionalLayer
  from niftynet.network.base_net import BaseNet
  from niftynet.network.highres3dnet import HighRes3DNet, HighResBlock
  from niftynet.utilities.util_common import look_up_operations
label_desc: Use image node_id 2 for calling the ScaleNet obj's underlying member method code with example usage: obj.layer_op(images, is_training, layer_id) and returns: output_tensor