Skip to content

Data types in PAR / REC files #275

Closed
@matthew-brett

Description

@matthew-brett

PAR files have a field in the image information definition called "image pixel size (in bits)".

We currently assume that this value refers to the number of bits of a signed integer (int16, int32).

Bennett Landman has a PAR / REC toolbox at http://iacl.ece.jhu.edu/~bennett/research.shtml that includes the following code (in loadREC.m):

switch(par.scn.pix_bits)
    case 32
        type = 'float32';
    case 16
        type = 'uint16'; % fixed?
    case 8
        type='uint8';
    otherwise
        error('Unsupported data type');
end

So, he thinks the ints are unsigned and that '32' means float not (u)int32.

We have test images at:

All of the images are '16' bit width, so we can't check what '8' or '32' mean from these images.

These sets of PAR / REC data files have matching NIfTI files written by the Philips software. These all have NIfTI image data type <i2 (signed little-endian int16). The unscaled data array is identical between our PAR / REC conversion and the Philips conversion to NIfTI. Here's loading in the nitest-balls1 directory:

In [24]: import numpy as np
In [25]: import nibabel as nib
In [27]: n_img = nib.load('NIFTI/T1.nii.gz')
In [28]: p_img = nib.load('PARREC/T1.PAR')
In [29]: np.all(n_img.dataobj.get_unscaled() == p_img.dataobj.get_unscaled())
Out[29]: memmap(True, dtype=bool)

Only one of all the sample images has a negative minimum both on Philips conversion and our conversion - nitest-balls1/PARREC/fieldmap.PAR / nitest-balls1/NIFTI1/fieldmap.nii.gz:

In [30]: p_img = nib.load('PARREC/fieldmap.PAR')
In [31]: n_img = nib.load('NIFTI/fieldmap.nii.gz')
In [32]: np.all(n_img.dataobj.get_unscaled() == p_img.dataobj.get_unscaled())
Out[32]: False
In [33]: n_img.get_data().min()
Out[33]: -500.0
In [34]: p_img.get_data().min()
Out[34]: memmap(-500.0)

EDIT - these negative values in fact came from the data scaling and not the raw data.

In this case, the written NIfTI image is 3D shape (80, 80, 10), and our image is 4D with two volumes, shape (80, 80, 10, 2).

Loading the equivalent data in DICOM:

In [50]: import nibabel.nicom.dicomreaders as dcr
In [51]: img = dcr.wrapper_from_file('DICOM/fieldmap.dcm')
In [52]: res = img.get_pixel_array()
In [53]: res.shape
Out[53]: (20, 80, 80)
In [54]: res.dtype
Out[54]: dtype('uint16')

Investigating further, the T1 image also has 'uint16' DICOM data:

In [63]: t1_img = dcr.wrapper_from_file('DICOM/T1.dcm')
In [64]: t1_res = t1_img.get_pixel_array()
In [65]: t1_res.shape
Out[65]: (10, 80, 80)
In [66]: t1_res.dtype
Out[66]: dtype('uint16')
In [67]: t1_p_img = nib.load('PARREC/T1.PAR')
In [68]: np.all(t1_res.T == t1_p_img.dataobj.get_unscaled())
Out[68]: memmap(True, dtype=bool)

So far, it is possible that we and the Philips NIfTI conversions are wrong in interpreting the datatype as 'int16' instead of 'uint16'.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions