class_docstr: Affine Grid Warper class.
  
  The affine grid warper generates a reference grid of n-dimensional points
  and warps it via an affine transformation model determined by an input
  parameter Tensor. Some of the transformation parameters can be fixed at
  construction time via an `AffineWarpConstraints` object.
docstr: Creates all the matrices needed to compute the output warped grids.
docstr_tok: ["Creates","all","the","matrices","needed","to","compute","the","output","warped","grids","."]
code: def _create_features(self, constraints):
      """
      Creates all the matrices needed to compute the output warped grids.
      """
      affine_warp_constraints = constraints
      if not isinstance(affine_warp_constraints, AffineWarpConstraints):
          affine_warp_constraints = AffineWarpConstraints(constraints)
      psi = _create_affine_features(
          output_shape=self._output_shape,
          source_shape=self._source_shape,
          relative=True,
      )
      psi = np.asarray(psi)
      scales = [(x - 1.0) * 0.5 for x in self._source_shape]
      offsets = scales
  
      # Transforming a point x's i-th coordinate via an affine transformation
      # is performed via the following dot product:
      #
      #  x_i' = s_i * (T_i * x) + t_i                                    (1)
      #
      # where Ti is the i-th row of an affine matrix, and the scalars
      # s_i and t_i define a decentering and global scaling into
      # the source space.
      #
      # In the AffineGridWarper some of the entries of Ti are provided via the
      # input, some others are instead fixed, according to the constraints
      # assigned in the constructor.
      # In create_features the internal dot product (1) is accordingly
      # broken down into two parts:
      #
      # x_i' = Ti[uncon_i] * x[uncon_i, :] + offset(con_var)             (2)
      #
      # i.e. the sum of the dot product of the free parameters (coming
      # from the input) indexed by uncond_i and an offset obtained by
      # precomputing the fixed part of (1) according to the constraints.
      # This step is implemented by analyzing row by row
      # the constraints matrix and saving into a list
      # the x[uncon_i] and offset(con_var) data matrices
      # for each output dimension.
      #
      # constraint -- None, indicates dynamic element of in the affine mat.
  
      spatial_rank = len(self._source_shape)
      features = []
      # computes dynamic elements in the affine
      for i in range(spatial_rank):
          is_fixed = affine_warp_constraints[i]
          x_i = np.array(
              [
                  x
                  for x, fixed_var in zip(psi, is_fixed)
                  if fixed_var is None
              ]
          )
          features.append(x_i * scales[i] if len(x_i) else None)
  
      # computes fixed elements in the affine
      for i in range(spatial_rank):
          all_elements = np.asarray(affine_warp_constraints[i])
          dynamic_elements = all_elements == np.array(None)
          if np.all(dynamic_elements):
              x_i = None
          else:
              all_elements[dynamic_elements] = 0.0
              x_i = np.dot(all_elements, psi) * scales[i]
          features.append(x_i)
  
      # appending global offsets to the list
      features = features + offsets
      return features
  
imports: from __future__ import absolute_import, division, print_function
  from itertools import chain
  import numpy
  import tensorflow
  from niftynet.layer.base_layer import Layer, LayerFromCallable, Invertible
label_desc: Use image node_id 2 for calling the AffineGridWarperLayer obj's underlying member method code with example usage: obj._create_features(constraints) and returns: features