class_docstr: For a concrete example::
  
      _input_sources define multiple modality mappings, e.g.,
      _input_sources {'image': ('T1', 'T2'), 'label': ('manual_map',)}
  
  means:
  
  'image' consists of two components, formed by
  concatenating 'T1' and 'T2' input source images.
  'label' consists of one component, loading from 'manual_map'
  
  :param self._names: a tuple of the output names of this reader.
      ``('image', 'labels')``
  
  :param self._shapes: the shapes after combining input sources
      ``{'image': (192, 160, 192, 1, 2), 'label': (192, 160, 192, 1, 1)}``
  
  :param self._dtypes: store the dictionary of tensorflow shapes
      ``{'image': tf.float32, 'label': tf.float32}``
  
  :param self.output_list: a list of dictionaries, with each item::
  
      {'image': <niftynet.io.image_type.SpatialImage4D object>,
      'label': <niftynet.io.image_type.SpatialImage3D object>}
docstr: this layer returns dictionaries::
  
      keys: self.output_fields
      values: image volume array
docstr_tok: ["this","layer","returns","dictionaries",":",":","keys",":","self.output_fields","values",":","image","volume","array"]
code: def layer_op(self, idx=None, shuffle=True):
      """
      this layer returns dictionaries::
  
          keys: self.output_fields
          values: image volume array
  
      """
      if idx is None:
          if shuffle:
              # training, with random list output
              idx = np.random.randint(len(self.output_list))
          else:
              # testing, with sequential output
              # accessing self.current_id, not suitable for multi-thread
              idx = self.current_id + 1
              self.current_id = idx
  
      try:
          image_dict = self.output_list[idx]
      except (IndexError, TypeError):
          return -1, None, None
  
      image_data_dict = {
          field: image.get_data()
          for (field, image) in image_dict.items()
      }
      interp_order_dict = {
          field: image.interp_order
          for (field, image) in image_dict.items()
      }
  
      preprocessors = [deepcopy(layer) for layer in self.preprocessors]
      # dictionary of masks is cached
      mask = None
      for layer in preprocessors:
          # import time; local_time = time.time()
          if layer is None:
              continue
          if isinstance(layer, RandomisedLayer):
              if "random_elastic_deformation" not in layer.name:
                  layer.randomise()
              else:
                  layer.randomise(image_data_dict)
  
              image_data_dict = layer(
                  image_data_dict, interp_order_dict
              )
          elif isinstance(layer, Layer):
              image_data_dict, mask = layer(image_data_dict, mask)
              # print('%s, %.3f sec'%(layer, -local_time + time.time()))
      return idx, image_data_dict, interp_order_dict
  
imports: from __future__ import absolute_import, division, print_function
  from copy import deepcopy
  import argparse
  import numpy
  import pandas
  import tensorflow
  from six import string_types
  from niftynet.io.misc_io import dtype_casting
  from niftynet.io.image_sets_partitioner import COLUMN_UNIQ_ID
  from niftynet.io.image_type import ImageFactory
  from niftynet.layer.base_layer import Layer, DataDependentLayer, RandomisedLayer
  from niftynet.utilities.user_parameters_helper import make_input_tuple
  from niftynet.utilities.util_common import print_progress_bar, ParserNamespace
  from niftynet.io.image_sets_partitioner import ImageSetsPartitioner
  from niftynet.utilities.util_common import look_up_operations
label_desc: Use image node_id 5 for calling the ImageReader obj's underlying member method code with example usage: obj.layer_op(idx, shuffle) and returns: idx,  image_data_dict,  interp_order_dict, unknown,  None,  None