# NiBabel

### Access a cacophony of neuro-imaging file formats

# `parrec`

Read images in PAR/REC format

This is yet another MRI image format generated by Philips scanners. It is an ASCII header (PAR) plus a binary blob (REC).

This implementation aims to read version 4.0 through 4.2 of this format. Other versions could probably be supported, but we need example images to test against. If you want us to support another version, and have an image we can add to the test suite, let us know. You would make us very happy by submitting a pull request.

## PAR file format

The PAR format appears to have two sections:

### General information

This is a set of lines each giving one key : value pair, examples:

    .    EPI factor        <0,1=no EPI>     :   39
    .    Dynamic scan      <0=no 1=yes> ?   :   1
    .    Diffusion         <0=no 1=yes> ?   :   0

(from `nibabel/tests/data/phantom_EPI_asc_CLEAR_2_1.PAR`)

### Image information

There is a `#` prefixed list of fields under the heading “IMAGE INFORMATION DEFINITION”. From the same file, here is the start of this list:

    # === IMAGE INFORMATION DEFINITION =============================================
    #  The rest of this file contains ONE line per image, this line contains the following information:
    #
    #  slice number                             (integer)
    #  echo number                              (integer)
    #  dynamic scan number                      (integer)

There follows a space separated table with values for these fields, each row containing all the named values. Here are the first few lines from the example file above:

    # === IMAGE INFORMATION ==========================================================
    #  sl ec  dyn ph ty    idx pix scan% rec size                (re)scale              window        angulation              offcentre        thick   gap   info      spacing     echo     dtime   ttime    diff  avg  flip    freq   RR-int  turbo delay b grad cont anis         diffusion       L.ty

    1   1    1  1 0 2     0  16    62   64   64     0.00000   1.29035 4.28404e-003  1070  1860 -13.26  -0.00  -0.00    2.51   -0.81   -8.69  6.000  2.000 0 1 0 2  3.750  3.750  30.00    0.00     0.00    0.00   0   90.00     0    0    0    39   0.0  1   1    8    0   0.000    0.000    0.000  1
    2   1    1  1 0 2     1  16    62   64   64     0.00000   1.29035 4.28404e-003  1122  1951 -13.26  -0.00  -0.00    2.51    6.98  -10.53  6.000  2.000 0 1 0 2  3.750  3.750  30.00    0.00     0.00    0.00   0   90.00     0    0    0    39   0.0  1   1    8    0   0.000    0.000    0.000  1
    3   1    1  1 0 2     2  16    62   64   64     0.00000   1.29035 4.28404e-003  1137  1977 -13.26  -0.00  -0.00    2.51   14.77  -12.36  6.000  2.000 0 1 0 2  3.750  3.750  30.00    0.00     0.00    0.00   0   90.00     0    0    0    39   0.0  1   1    8    0   0.000    0.000    0.000  1

### Orientation

PAR files refer to orientations “ap”, “fh” and “rl”.

Nibabel’s required affine output axes are RAS (left to Right, posterior to Anterior, inferior to Superior). The correspondence of the PAR file’s axes to RAS axes is:

- ap = anterior -> posterior = negative A in RAS = P

- fh = foot -> head = S in RAS = S

- rl = right -> left = negative R in RAS = L

We therefore call the PAR file’s axis system “PSL” (Posterior, Superior, Left).

The orientation of the PAR file axes corresponds to DICOM’s LPS coordinate system (right to Left, anterior to Posterior, inferior to Superior), but in a different order.

### Data type

It seems that everyone agrees that Philips stores REC data in little-endian format - see https://github.com/nipy/nibabel/issues/274

Philips XML header files, and some previous experience, suggest that the REC data is always stored as 8 or 16 bit unsigned integers - see https://github.com/nipy/nibabel/issues/275

### Data Sorting

PAR/REC files have a large number of potential image dimensions. To handle sorting of volumes in PAR/REC files based on these fields and not the order slices first appear in the PAR file, the `strict_sort` flag of `nibabel.load` (or `parrec.load`) should be set to `True`. The fields that are taken into account during sorting are:

> - slice number

> - echo number

> - cardiac phase number

> - gradient orientation number

> - diffusion b value number

> - label type (ASL tag vs. control)

> - dynamic scan number

> - image_type_mr (Re, Im, Mag, Phase)

Slices are sorted into the third dimension and the order of preference for sorting along the 4th dimension corresponds to the order in the list above. If the image data has more than 4 dimensions these will all be concatenated along the 4th dimension. For example, for a scan with two echos and two dynamics, the 4th dimension will have both echos of dynamic 1 prior to the two echos for dynamic 2.

The``get_volume_labels`` method of the header returns a dictionary containing the PAR field labels for this 4th dimension.

The volume sorting described above can be enabled in the parrec2nii command utility via the option “–strict-sort”. The dimension info can be exported to a CSV file by adding the option “–volume-info”.

`PARRECArrayProxy`(file_like, header, *[, ...]) | Initialize PARREC array proxy
`PARRECError` | Exception for PAR/REC format related problems.
`PARRECHeader`(info, image_defs[, ...]) | PAR/REC header
`PARRECImage`(dataobj, affine[, header, ...]) | PAR/REC image
`exts2pars`(exts_source) | Parse, return any PAR headers from NIfTI extensions in exts_source
`one_line`(long_str) | Make maybe mutli-line long_str into one long line
`parse_PAR_header`(fobj) | Parse a PAR header and aggregate all information into useful containers.
`vol_is_full`(slice_nos, slice_max[, slice_min]) | Vector with True for slices in complete volume, False otherwise
`vol_numbers`(slice_nos) | Calculate volume numbers inferred from slice numbers slice_nos

## `PARRECArrayProxy`

class nibabel.parrec.PARRECArrayProxy(file_like, header, *, mmap=True, scaling='dv')

  Bases: `object`

  Initialize PARREC array proxy

  Parameters:
    file_like file-like object

      Filename or object implementing `read, seek, tell`

    header PARRECHeader instance

      Implementing `get_data_shape, get_data_dtype`, `get_sorted_slice_indices`, `get_data_scaling`, `get_rec_shape`.

    mmap{True, False, ‘c’, ‘r’}, optional, keyword only

      mmap controls the use of numpy memory mapping for reading data. If False, do not try numpy `memmap` for data array. If one of {‘c’, ‘r’}, try numpy memmap with `mode=mmap`. A mmap value of True gives the same behavior as `mmap='c'`. If file_like cannot be memory-mapped, ignore mmap value and read array from file.

    scaling{‘fp’, ‘dv’}, optional, keyword only

      Type of scaling to use - see header `get_data_scaling` method.

  __init__(file_like, header, *, mmap=True, scaling='dv')

    Initialize PARREC array proxy

    Parameters:
      file_like file-like object

        Filename or object implementing `read, seek, tell`

      header PARRECHeader instance

        Implementing `get_data_shape, get_data_dtype`, `get_sorted_slice_indices`, `get_data_scaling`, `get_rec_shape`.

      mmap{True, False, ‘c’, ‘r’}, optional, keyword only

        mmap controls the use of numpy memory mapping for reading data. If False, do not try numpy `memmap` for data array. If one of {‘c’, ‘r’}, try numpy memmap with `mode=mmap`. A mmap value of True gives the same behavior as `mmap='c'`. If file_like cannot be memory-mapped, ignore mmap value and read array from file.

      scaling{‘fp’, ‘dv’}, optional, keyword only

        Type of scaling to use - see header `get_data_scaling` method.

  property dtype
  get_unscaled()

    Read data from file

    This is an optional part of the proxy API

  property is_proxy
  property ndim
  property shape

## `PARRECError`

class nibabel.parrec.PARRECError

  Bases: `Exception`

  Exception for PAR/REC format related problems.

  To be raised whenever PAR/REC is not happy, or we are not happy with PAR/REC.

  __init__(*args, **kwargs)

## `PARRECHeader`

class nibabel.parrec.PARRECHeader(info, image_defs, permit_truncated=False, strict_sort=False)

  Bases: `SpatialHeader`

  PAR/REC header

  Parameters:
    info dict

      “General information” from the PAR file (as returned by parse_PAR_header()).

    image_defs array

      Structured array with image definitions from the PAR file (as returned by parse_PAR_header()).

    permit_truncated bool, optional

      If True, a warning is emitted instead of an error when a truncated recording is detected.

    strict_sort bool, optional, keyword-only

      If True, a larger number of header fields are used while sorting the REC data array. This may produce a different sort order than strict_sort=False, where volumes are sorted by the order in which the slices appear in the .PAR file.

  __init__(info, image_defs, permit_truncated=False, strict_sort=False)
    Parameters:
      info dict

        “General information” from the PAR file (as returned by parse_PAR_header()).

      image_defs array

        Structured array with image definitions from the PAR file (as returned by parse_PAR_header()).

      permit_truncated bool, optional

        If True, a warning is emitted instead of an error when a truncated recording is detected.

      strict_sort bool, optional, keyword-only

        If True, a larger number of header fields are used while sorting the REC data array. This may produce a different sort order than strict_sort=False, where volumes are sorted by the order in which the slices appear in the .PAR file.

  as_analyze_map()

    Convert PAR parameters to NIFTI1 format

  copy()

    Copy object to independent representation

    The copy should not be affected by any changes to the original object.

  classmethod from_fileobj(fileobj, permit_truncated=False, strict_sort=False)
  classmethod from_header(header=None)
  get_affine(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

  get_bvals_bvecs()

    Get bvals and bvecs from data

    Returns:
      b_vals None or array

        Array of b values, shape (n_directions,), or None if not a diffusion acquisition.

      b_vectors None or array

        Array of b vectors, shape (n_directions, 3), or None if not a diffusion acquisition.

  get_data_offset()

    PAR header always has 0 data offset (into REC file)

  get_data_scaling(method='dv')

    Returns scaling slope and intercept.

    Parameters:
      method{‘fp’, ‘dv’}

        Scaling settings to be reported – see notes below.

    Returns:
      slope array

        scaling slope

      intercept array

        scaling intercept

    Notes

    The PAR header contains two different scaling settings: ‘dv’ (value on console) and ‘fp’ (floating point value). Here is how they are defined:

    DV = PV * RS + RI FP = DV / (RS * SS)

    where:

    PV: value in REC RS: rescale slope RI: rescale intercept SS: scale slope

  get_def(name)

    Return a single image definition field (or None if missing)

  get_echo_train_length()

    Echo train length of the recording

  get_q_vectors()

    Get Q vectors from the data

    Returns:
      q_vectors None or array

        Array of q vectors (bvals * bvecs), or None if not a diffusion acquisition.

  get_rec_shape()
  get_slice_orientation()

    Returns the slice orientation label.

    Returns:
      orientation{‘transverse’, ‘sagittal’, ‘coronal’}
  get_sorted_slice_indices()

    Return indices to sort (and maybe discard) slices in REC file.

    If the recording is truncated, the returned indices take care of discarding any slice indices from incomplete volumes.

    If self.strict_sort is True, a more complicated sorting based on multiple fields from the .PAR file is used. This may produce a different sort order than strict_sort=False, where volumes are sorted by the order in which the slices appear in the .PAR file.

    Returns:
      slice_indices list

        List for indexing into the last (third) dimension of the REC data array, and (equivalently) the only dimension of `self.image_defs`.

  get_volume_labels()

    Dynamic labels corresponding to the final data dimension(s).

    This is useful for custom data sorting. A subset of the info in `self.image_defs` is returned in an order that matches the final data dimension(s). Only labels that have more than one unique value across the dataset will be returned.

    Returns:
      sort_info dict

        Each key corresponds to volume labels for a dynamically varying sequence dimension. The ordering of the labels matches the volume ordering determined via `self.get_sorted_slice_indices`.

  get_water_fat_shift()

    Water fat shift, in pixels

  set_data_offset(offset)

    PAR header always has 0 data offset (into REC file)

## `PARRECImage`

class nibabel.parrec.PARRECImage(dataobj: ArrayLike, affine: np.ndarray | None, header: FileBasedHeader | ty.Mapping | None = None, extra: ty.Mapping | None = None, file_map: FileMap | None = None)

  Bases: `SpatialImage`

  PAR/REC image

  Initialize image

  The image is a combination of (array-like, affine matrix, header), with optional metadata in extra, and filename / file-like objects contained in the file_map mapping.

  Parameters:
    dataobj object

      Object containing image data. It should be some object that returns an array from `np.asanyarray`. It should have a `shape` attribute or property

    affine None or (4,4) array-like

      homogeneous affine giving relationship between voxel coordinates and world coordinates. Affine can also be None. In this case, `obj.affine` also returns None, and the affine as written to disk will depend on the file format.

    header None or mapping or header instance, optional

      metadata for this image format

    extra None or mapping, optional

      metadata to associate with image that cannot be stored in the metadata of this image type

    file_map mapping, optional

      mapping giving file information for this image format

  __init__(dataobj: ArrayLike, affine: np.ndarray | None, header: FileBasedHeader | ty.Mapping | None = None, extra: ty.Mapping | None = None, file_map: FileMap | None = None)

    Initialize image

    The image is a combination of (array-like, affine matrix, header), with optional metadata in extra, and filename / file-like objects contained in the file_map mapping.

    Parameters:
      dataobj object

        Object containing image data. It should be some object that returns an array from `np.asanyarray`. It should have a `shape` attribute or property

      affine None or (4,4) array-like

        homogeneous affine giving relationship between voxel coordinates and world coordinates. Affine can also be None. In this case, `obj.affine` also returns None, and the affine as written to disk will depend on the file format.

      header None or mapping or header instance, optional

        metadata for this image format

      extra None or mapping, optional

        metadata to associate with image that cannot be stored in the metadata of this image type

      file_map mapping, optional

        mapping giving file information for this image format

  ImageArrayProxy

    alias of `PARRECArrayProxy`

  files_types = (('image', '.rec'), ('header', '.par'))
  classmethod from_file_map(file_map, *, mmap=True, permit_truncated=False, scaling='dv', strict_sort=False)

    Create PARREC image from file map file_map

    Parameters:
      file_map dict

        dict with keys `image, header` and values being fileholder objects for the respective REC and PAR files.

      mmap{True, False, ‘c’, ‘r’}, optional, keyword only

        mmap controls the use of numpy memory mapping for reading image array data. If False, do not try numpy `memmap` for data array. If one of {‘c’, ‘r’}, try numpy memmap with `mode=mmap`. A mmap value of True gives the same behavior as `mmap='c'`. If image data file cannot be memory-mapped, ignore mmap value and read array from file.

      permit_truncated{False, True}, optional, keyword-only

        If False, raise an error for an image where the header shows signs that fewer slices / volumes were recorded than were expected.

      scaling{‘dv’, ‘fp’}, optional, keyword-only

        Scaling method to apply to data (see `PARRECHeader.get_data_scaling()`).

      strict_sort bool, optional, keyword-only

        If True, a larger number of header fields are used while sorting the REC data array. This may produce a different sort order than strict_sort=False, where volumes are sorted by the order in which the slices appear in the .PAR file.

  classmethod from_filename(filename, *, mmap=True, permit_truncated=False, scaling='dv', strict_sort=False)

    Create PARREC image from filename filename

    Parameters:
      filename str

        Filename of “PAR” or “REC” file

      mmap{True, False, ‘c’, ‘r’}, optional, keyword only

        mmap controls the use of numpy memory mapping for reading image array data. If False, do not try numpy `memmap` for data array. If one of {‘c’, ‘r’}, try numpy memmap with `mode=mmap`. A mmap value of True gives the same behavior as `mmap='c'`. If image data file cannot be memory-mapped, ignore mmap value and read array from file.

      permit_truncated{False, True}, optional, keyword-only

        If False, raise an error for an image where the header shows signs that fewer slices / volumes were recorded than were expected.

      scaling{‘dv’, ‘fp’}, optional, keyword-only

        Scaling method to apply to data (see `PARRECHeader.get_data_scaling()`).

      strict_sort bool, optional, keyword-only

        If True, a larger number of header fields are used while sorting the REC data array. This may produce a different sort order than strict_sort=False, where volumes are sorted by the order in which the slices appear in the .PAR file.

  header_class

    alias of `PARRECHeader`

  classmethod load(filename, *, mmap=True, permit_truncated=False, scaling='dv', strict_sort=False)

    Create PARREC image from filename filename

    Parameters:
      filename str

        Filename of “PAR” or “REC” file

      mmap{True, False, ‘c’, ‘r’}, optional, keyword only

        mmap controls the use of numpy memory mapping for reading image array data. If False, do not try numpy `memmap` for data array. If one of {‘c’, ‘r’}, try numpy memmap with `mode=mmap`. A mmap value of True gives the same behavior as `mmap='c'`. If image data file cannot be memory-mapped, ignore mmap value and read array from file.

      permit_truncated{False, True}, optional, keyword-only

        If False, raise an error for an image where the header shows signs that fewer slices / volumes were recorded than were expected.

      scaling{‘dv’, ‘fp’}, optional, keyword-only

        Scaling method to apply to data (see `PARRECHeader.get_data_scaling()`).

      strict_sort bool, optional, keyword-only

        If True, a larger number of header fields are used while sorting the REC data array. This may produce a different sort order than strict_sort=False, where volumes are sorted by the order in which the slices appear in the .PAR file.

  makeable = False
  rw = False
  valid_exts = ('.rec', '.par')

## exts2pars

nibabel.parrec.exts2pars(exts_source)

  Parse, return any PAR headers from NIfTI extensions in exts_source

  Parameters:
    exts_source sequence or Nifti1Image, Nifti1Header instance

      A sequence of extensions, or header containing NIfTI extensions, or an image containing a header with NIfTI extensions.

  Returns:
    par_headers list

      A list of PARRECHeader objects, usually empty or with one element, each element contains a PARRECHeader read from the contained extensions.

## one_line

nibabel.parrec.one_line(long_str)

  Make maybe mutli-line long_str into one long line

## parse_PAR_header

nibabel.parrec.parse_PAR_header(fobj)

  Parse a PAR header and aggregate all information into useful containers.

  Parameters:
    fobj file-object

      The PAR header file object.

  Returns:
    general_info dict

      Contains all “General Information” from the header file

    image_info ndarray

      Structured array with fields giving all “Image information” in the header

## vol_is_full

nibabel.parrec.vol_is_full(slice_nos, slice_max, slice_min=1)

  Vector with True for slices in complete volume, False otherwise

  Parameters:
    slice_nos sequence

      Sequence of slice numbers, e.g. `[1, 2, 3, 4, 1, 2, 3, 4]`.

    slice_max int

      Highest slice number for a full slice set. Slice set will be `range(slice_min, slice_max+1)`.

    slice_min int, optional

      Lowest slice number for full slice set. Default is 1.

  Returns:
    is_full array

      Bool vector with True for slices in full volumes, False for slices in partial volumes. A full volume is a volume with all slices in the `slice set` as defined above.

  Raises:
    ValueError

      if any value in slice_nos is outside slice set indices.

## vol_numbers

nibabel.parrec.vol_numbers(slice_nos)

  Calculate volume numbers inferred from slice numbers slice_nos

  The volume number for each slice is the number of times this slice number has occurred previously in the slice_nos sequence

  Parameters:
    slice_nos sequence

      Sequence of slice numbers, e.g. `[1, 2, 3, 4, 1, 2, 3, 4]`.

  Returns:
    vol_nos list

      A list, the same length of slice_nos giving the volume number for each corresponding slice number.