class_docstr: PAR/REC header
class_docstr_tok: ["PAR\/REC","header"]
docstr: Compute affine transformation into scanner space.
  
  The method only considers global rotation and offset settings in the
  header and ignores potentially deviating information in the image
  definitions.
  
  Parameters
  ----------
  origin : {'scanner', 'fov'}
      Transformation origin. By default the transformation is computed
      relative to the scanner's iso center. If 'fov' is requested the
      transformation origin will be the center of the field of view
      instead.
  
  Returns
  -------
  aff : (4, 4) array
      4x4 array, with output axis order corresponding to RAS or (x,y,z)
      or (lr, pa, fh).
  
  Notes
  -----
  Transformations appear to be specified in (ap, fh, rl) axes.  The
  orientation of data is recorded in the "slice orientation" field of the
  PAR header "General Information".
  
  We need to:
  
  * translate to coordinates in terms of the center of the FOV
  * apply voxel size scaling
  * reorder / flip the data to Philips' PSL axes
  * apply the rotations
  * apply any isocenter scaling offset if `origin` == "scanner"
  * reorder and flip to RAS axes
docstr_tok: ["Compute","affine","transformation","into","scanner","space",".","The","method","only","considers","global","rotation","and","offset","settings","in","the","header","and","ignores","potentially","deviating","information","in","the","image","definitions",".","Parameters","--","--","--","--","--","origin",":","{","'scanner","'",",","'fov","'","}","Transformation","origin",".","By","default","the","transformation","is","computed","relative","to","the","scanner","'s","iso","center",".","If","'fov","'","is","requested","the","transformation","origin","will","be","the","center","of","the","field","of","view","instead",".","Returns","--","--","--","-","aff",":","(","4",",","4",")","array","4x4","array",",","with","output","axis","order","corresponding","to","RAS","or","(","x",",","y",",","z",")","or","(","lr",",","pa",",","fh",")",".","Notes","--","--","-","Transformations","appear","to","be","specified","in","(","ap",",","fh",",","rl",")","axes",".","The","orientation","of","data","is","recorded","in","the","``","slice","orientation","''","field","of","the","PAR","header","``","General","Information","''",".","We","need","to",":","*","translate","to","coordinates","in","terms","of","the","center","of","the","FOV","*","apply","voxel","size","scaling","*","reorder","\/","flip","the","data","to","Philips","'","PSL","axes","*","apply","the","rotations","*","apply","any","isocenter","scaling","offset","if","`","origin","`","==","``","scanner","''","*","reorder","and","flip","to","RAS","axes"]
code: def get_affine(self, origin="scanner"):
      """Compute affine transformation into scanner space.
  
      The method only considers global rotation and offset settings in the
      header and ignores potentially deviating information in the image
      definitions.
  
      Parameters
      ----------
      origin : {'scanner', 'fov'}
          Transformation origin. By default the transformation is computed
          relative to the scanner's iso center. If 'fov' is requested the
          transformation origin will be the center of the field of view
          instead.
  
      Returns
      -------
      aff : (4, 4) array
          4x4 array, with output axis order corresponding to RAS or (x,y,z)
          or (lr, pa, fh).
  
      Notes
      -----
      Transformations appear to be specified in (ap, fh, rl) axes.  The
      orientation of data is recorded in the "slice orientation" field of the
      PAR header "General Information".
  
      We need to:
  
      * translate to coordinates in terms of the center of the FOV
      * apply voxel size scaling
      * reorder / flip the data to Philips' PSL axes
      * apply the rotations
      * apply any isocenter scaling offset if `origin` == "scanner"
      * reorder and flip to RAS axes
      """
      # shape, zooms in original data ordering (ijk ordering)
      ijk_shape = np.array(self.get_data_shape()[:3])
      to_center = from_matvec(np.eye(3), -(ijk_shape - 1) / 2.0)
      zoomer = np.diag(list(self.get_zooms()[:3]) + [1])
      slice_orientation = self.get_slice_orientation()
      permute_to_psl = ACQ_TO_PSL.get(slice_orientation)
      if permute_to_psl is None:
          raise PARRECError(
              f"Unknown slice orientation ({slice_orientation})."
          )
      # hdr has deg, we need radians
      # Order is [ap, fh, rl]
      ap_rot, fh_rot, rl_rot = self.general_info["angulation"] * DEG2RAD
      Mx = euler2mat(x=ap_rot)
      My = euler2mat(y=fh_rot)
      Mz = euler2mat(z=rl_rot)
      # By trial and error, this unexpected order of rotations seem to give
      # the closest to the observed (converted NIfTI) affine.
      rot = from_matvec(dot_reduce(Mz, Mx, My))
      # compose the PSL affine
      psl_aff = dot_reduce(rot, permute_to_psl, zoomer, to_center)
      if origin == "scanner":
          # offset to scanner's isocenter (in ap, fh, rl)
          iso_offset = self.general_info["off_center"]
          psl_aff[:3, 3] += iso_offset
      # Currently in PSL; apply PSL -> RAS
      return np.dot(PSL_TO_RAS, psl_aff)
  
imports: import re
  import warnings
  from collections import OrderedDict
  from copy import deepcopy
  from io import StringIO
  from locale import getpreferredencoding
  import numpy
  from .affines import apply_affine, dot_reduce, from_matvec
  from .eulerangles import euler2mat
  from .fileslice import fileslice, strided_scalar
  from .nifti1 import unit_codes
  from .openers import ImageOpener
  from .spatialimages import SpatialHeader, SpatialImage
  from .volumeutils import Recoder, array_from_file
label_desc: Use image node_id 15 for calling the PARRECHeader obj's underlying member method code with example usage: obj.get_affine(origin) and returns: np