class_docstr: Wrapper for Enhanced MR Storage SOP Class
  
  Tested with Philips' Enhanced DICOM implementation.
  
  The specification for the Enhanced MR image IOP / SOP began life as `DICOM
  supplement 49 <ftp://medical.nema.org/medical/dicom/final/sup49_ft.pdf>`_,
  but as of 2016 it is part of the standard. In particular see:
  
  * `A.36 Enhanced MR Information Object Definitions
    <http://dicom.nema.org/medical/dicom/current/output/pdf/part03.pdf#sect_A.36>`_;
  * `C.7.6.16 Multi-Frame Functional Groups Module
    <http://dicom.nema.org/medical/dicom/current/output/pdf/part03.pdf#sect_C.7.6.16>`_;
  * `C.7.6.17 Multi-Frame Dimension Module
    <http://dicom.nema.org/medical/dicom/current/output/pdf/part03.pdf#sect_C.7.6.17>`_.
  
  Attributes
  ----------
  is_multiframe : boolean
      Identifies `dcmdata` as multi-frame
  frames : sequence
      A sequence of ``dicom.dataset.Dataset`` objects populated by the
      ``dicom.dataset.Dataset.PerFrameFunctionalGroupsSequence`` attribute
  shared : object
      The first (and only) ``dicom.dataset.Dataset`` object from a
      ``dicom.dataset.Dataset.SharedFunctionalgroupSequence``.
  
  Methods
  -------
  image_shape(self)
  image_orient_patient(self)
  voxel_sizes(self)
  image_position(self)
  series_signature(self)
  get_data(self)
docstr: The array shape as it will be returned by ``get_data()``
  
  The shape is determined by the *Rows* DICOM attribute, *Columns*
  DICOM attribute, and the set of frame indices given by the
  *FrameContentSequence[0].DimensionIndexValues* DICOM attribute of each
  element in the *PerFrameFunctionalGroupsSequence*.  The first two
  axes of the returned shape correspond to the rows, and columns
  respectively. The remaining axes correspond to those of the frame
  indices with order preserved.
  
  What each axis in the frame indices refers to is given by the
  corresponding entry in the *DimensionIndexSequence* DICOM attribute.
  **WARNING**: Any axis referring to the *StackID* DICOM attribute will
  have been removed from the frame indices in determining the shape. This
  is because only a file containing a single stack is currently allowed by
  this wrapper.
  
  References
  ----------
  * C.7.6.16 Multi-Frame Functional Groups Module:
    http://dicom.nema.org/medical/dicom/current/output/pdf/part03.pdf#sect_C.7.6.16
  * C.7.6.17 Multi-Frame Dimension Module:
    http://dicom.nema.org/medical/dicom/current/output/pdf/part03.pdf#sect_C.7.6.17
  * Diagram of DimensionIndexSequence and DimensionIndexValues:
    http://dicom.nema.org/medical/dicom/current/output/pdf/part03.pdf#figure_C.7.6.17-1
docstr_tok: ["The","array","shape","as","it","will","be","returned","by","``","get_data","(",")","``","The","shape","is","determined","by","the","*","Rows","*","DICOM","attribute",",","*","Columns","*","DICOM","attribute",",","and","the","set","of","frame","indices","given","by","the","*","FrameContentSequence","[","0","]",".DimensionIndexValues","*","DICOM","attribute","of","each","element","in","the","*","PerFrameFunctionalGroupsSequence","*",".","The","first","two","axes","of","the","returned","shape","correspond","to","the","rows",",","and","columns","respectively",".","The","remaining","axes","correspond","to","those","of","the","frame","indices","with","order","preserved",".","What","each","axis","in","the","frame","indices","refers","to","is","given","by","the","corresponding","entry","in","the","*","DimensionIndexSequence","*","DICOM","attribute",".","*","*","WARNING","*","*",":","Any","axis","referring","to","the","*","StackID","*","DICOM","attribute","will","have","been","removed","from","the","frame","indices","in","determining","the","shape",".","This","is","because","only","a","file","containing","a","single","stack","is","currently","allowed","by","this","wrapper",".","References","--","--","--","--","--","*","C.7.6.16","Multi-Frame","Functional","Groups","Module",":","http",":","\/\/dicom.nema.org\/medical\/dicom\/current\/output\/pdf\/part03.pdf","#","sect_C.7.6.16","*","C.7.6.17","Multi-Frame","Dimension","Module",":","http",":","\/\/dicom.nema.org\/medical\/dicom\/current\/output\/pdf\/part03.pdf","#","sect_C.7.6.17","*","Diagram","of","DimensionIndexSequence","and","DimensionIndexValues",":","http",":","\/\/dicom.nema.org\/medical\/dicom\/current\/output\/pdf\/part03.pdf","#","figure_C.7.6.17-1"]
code: def image_shape(self):
      """The array shape as it will be returned by ``get_data()``
  
      The shape is determined by the *Rows* DICOM attribute, *Columns*
      DICOM attribute, and the set of frame indices given by the
      *FrameContentSequence[0].DimensionIndexValues* DICOM attribute of each
      element in the *PerFrameFunctionalGroupsSequence*.  The first two
      axes of the returned shape correspond to the rows, and columns
      respectively. The remaining axes correspond to those of the frame
      indices with order preserved.
  
      What each axis in the frame indices refers to is given by the
      corresponding entry in the *DimensionIndexSequence* DICOM attribute.
      **WARNING**: Any axis referring to the *StackID* DICOM attribute will
      have been removed from the frame indices in determining the shape. This
      is because only a file containing a single stack is currently allowed by
      this wrapper.
  
      References
      ----------
      * C.7.6.16 Multi-Frame Functional Groups Module:
        http://dicom.nema.org/medical/dicom/current/output/pdf/part03.pdf#sect_C.7.6.16
      * C.7.6.17 Multi-Frame Dimension Module:
        http://dicom.nema.org/medical/dicom/current/output/pdf/part03.pdf#sect_C.7.6.17
      * Diagram of DimensionIndexSequence and DimensionIndexValues:
        http://dicom.nema.org/medical/dicom/current/output/pdf/part03.pdf#figure_C.7.6.17-1
      """
      rows, cols = self.get("Rows"), self.get("Columns")
      if None in (rows, cols):
          raise WrapperError("Rows and/or Columns are empty.")
  
      # Check number of frames
      first_frame = self.frames[0]
      n_frames = self.get("NumberOfFrames")
      # some Philips may have derived images appended
      has_derived = False
      if hasattr(first_frame, "get") and first_frame.get(
          [0x18, 0x9117]
      ):
          # DWI image may include derived isotropic, ADC or trace volume
          try:
              self.frames = pydicom.Sequence(
                  frame
                  for frame in self.frames
                  if frame.MRDiffusionSequence[
                      0
                  ].DiffusionDirectionality
                  != "ISOTROPIC"
              )
          except IndexError:
              # Sequence tag is found but missing items!
              raise WrapperError("Diffusion file missing information")
          except AttributeError:
              # DiffusionDirectionality tag is not required
              pass
          else:
              if n_frames != len(self.frames):
                  warnings.warn("Derived images found and removed")
                  n_frames = len(self.frames)
                  has_derived = True
  
      assert len(self.frames) == n_frames
      frame_indices = np.array(
          [
              frame.FrameContentSequence[0].DimensionIndexValues
              for frame in self.frames
          ]
      )
      # Check that there is only one multiframe stack index
      stack_ids = {
          frame.FrameContentSequence[0].StackID for frame in self.frames
      }
      if len(stack_ids) > 1:
          raise WrapperError(
              "File contains more than one StackID. Cannot handle multi-stack files"
          )
      # Determine if one of the dimension indices refers to the stack id
      dim_seq = [
          dim.DimensionIndexPointer
          for dim in self.get("DimensionIndexSequence")
      ]
      stackid_tag = pydicom.datadict.tag_for_keyword("StackID")
      # remove the stack id axis if present
      if stackid_tag in dim_seq:
          stackid_dim_idx = dim_seq.index(stackid_tag)
          frame_indices = np.delete(
              frame_indices, stackid_dim_idx, axis=1
          )
          dim_seq.pop(stackid_dim_idx)
      if has_derived:
          # derived volume is included
          derived_tag = pydicom.datadict.tag_for_keyword(
              "DiffusionBValue"
          )
          if derived_tag not in dim_seq:
              raise WrapperError(
                  "Missing information, cannot remove indices with confidence."
              )
          derived_dim_idx = dim_seq.index(derived_tag)
          frame_indices = np.delete(
              frame_indices, derived_dim_idx, axis=1
          )
      # account for the 2 additional dimensions (row and column) not included
      # in the indices
      n_dim = frame_indices.shape[1] + 2
      # Store frame indices
      self._frame_indices = frame_indices
      if n_dim < 4:  # 3D volume
          return rows, cols, n_frames
      # More than 3 dimensions
      ns_unique = [len(np.unique(row)) for row in self._frame_indices.T]
      shape = (rows, cols) + tuple(ns_unique)
      n_vols = np.prod(shape[3:])
      if n_frames != n_vols * shape[2]:
          raise WrapperError(
              "Calculated shape does not match number of frames."
          )
      return tuple(shape)
  
imports: import operator
  import warnings
  import numpy
  from nibabel.optpkg import optional_package
  from ..onetime import auto_attr
  from ..openers import ImageOpener
  from .None import csareader
  from .dwiparams import B2q, nearest_pos_semi_def, q2bg
label_desc: Use image node_id 2 for calling the MultiframeWrapper obj's underlying member method code with example usage: obj.image_shape() and returns: tuple, rows,  cols,  n_frames