/usr/bin/parrec2nii is in python-nibabel 1.2.2-1.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | #!/usr/bin/python
"""PAR/REC to NIfTI converter
"""
from optparse import OptionParser, Option
import sys
import os
import gzip
import numpy as np
import nibabel
import nibabel.parrec as pr
import nibabel.nifti1 as nifti1
from nibabel.loadsave import read_img_data
from nibabel.filename_parser import splitext_addext
# global verbosity switch
verbose_switch = False
def get_opt_parser():
    # use module docstring for help output
    p = OptionParser(
                usage="%s [OPTIONS] <PAR files>\n\n" % sys.argv[0] + __doc__,
                version="%prog " + nibabel.__version__)
    p.add_option(
        Option("-v", "--verbose", action="store_true",
               dest="verbose", default=False,
               help="Make some noise."))
    p.add_option(
        Option("-o", "--output-dir",
               action="store", type="string", dest="outdir",
               default=None,
               help=\
"""Destination directory for NIfTI files. Default: current directory."""))
    p.add_option(
        Option("-c", "--compressed", action="store_true",
               dest="compressed", default=False,
               help="Whether to write compressed NIfTI files or not."))
    p.add_option(
        Option("--origin", action="store",
               dest="origin", default="scanner",
               help=\
"""Reference point of the q-form transformation of the NIfTI image. If 'scanner'
the (0,0,0) coordinates will refer to the scanner's iso center. If 'fov', this
coordinate will be the center of the recorded volume (field of view). Default:
'scanner'."""))
    p.add_option(
        Option("--minmax", action="store", nargs=2,
               dest="minmax", help=\
"""Mininum and maximum settings to be stored in the NIfTI header. If any of
them is set to 'parse', the scaled data is scanned for the actual minimum and
maximum. To bypass this potentially slow and memory intensive step (the data
has to be scaled and fully loaded into memory), fixed values can be provided as
space-separated pair, e.g. '5.4 120.4'. It is possible to set a fixed minimum
as scan for the actual maximum (and vice versa). Default: 'parse parse'."""))
    p.set_defaults(minmax=('parse', 'parse'))
    p.add_option(
        Option("--store-header", action="store_true",
               dest="store_header", default=False,
               help=\
"""If set, all information from the PAR header is stored in an extension of
the NIfTI file header. Default: off"""))
    p.add_option(
        Option("--scaling", action="store", dest="scaling", default='dv',
               help=\
"""Choose data scaling setting. The PAR header defines two different data
scaling settings: 'dv' (values displayed on console) and 'fp' (floating point
values). Either one can be chosen, or scaling can be disabled completely
('off').  Note that neither method will actually scale the data, but just store
the corresponding settings in the NIfTI header. Default: 'dv'"""))
    return p
def verbose(msg, indent=0):
    if verbose_switch:
        print "%s%s" % (' ' * indent, msg)
def error(msg, exit_code):
    print  >> sys.stderr, msg
    sys.exit(exit_code)
def proc_file(infile, opts):
    # load the PAR header
    pr_img = pr.load(infile)
    pr_hdr = pr_img.get_header()
    # get the raw unscaled data form the REC file
    raw_data = read_img_data(pr_img, prefer='unscaled')
    # compute affine with desired origin
    affine = pr_hdr.get_affine(origin=opts.origin)
    # create an nifti image instance -- to get a matching header
    nimg = nifti1.Nifti1Image(raw_data, affine)
    nhdr = nimg.get_header()
    if 'parse' in opts.minmax:
        # need to get the scaled data
        verbose('Load (and scale) the data to determine value range')
        if opts.scaling == 'off':
            scaled_data = raw_data
        else:
            slope, intercept = pr_hdr.get_data_scaling(method=opts.scaling)
            scaled_data = slope * raw_data
            scaled_data += intercept
    if opts.minmax[0] == 'parse':
        nhdr.structarr['cal_min'] = scaled_data.min()
    else:
        nhdr.structarr['cal_min'] = float(opts.minmax[0])
    if opts.minmax[1] == 'parse':
        nhdr.structarr['cal_max'] = scaled_data.max()
    else:
        nhdr.structarr['cal_max'] = float(opts.minmax[1])
    # container for potential NIfTI1 header extensions
    exts = nifti1.Nifti1Extensions()
    if opts.store_header:
        # dump the full PAR header content into an extension
        fobj = open(infile, 'r')
        hdr_dump = fobj.read()
        dump_ext = nifti1.Nifti1Extension('comment', hdr_dump)
        fobj.close()
        exts.append(dump_ext)
    # put any extensions into the image
    nimg.extra['extensions'] = exts
    # image description
    descr = "%s;%s;%s;%s" % (
                pr_hdr.general_info['exam_name'],
                pr_hdr.general_info['patient_name'],
                pr_hdr.general_info['exam_date'].replace(' ',''),
                pr_hdr.general_info['protocol_name'])
    nhdr.structarr['descrip'] = descr[:80]
    if pr_hdr.general_info['max_dynamics'] > 1:
        # fMRI
        nhdr.structarr['pixdim'][4] = pr_hdr.general_info['repetition_time']
        # store units -- always mm and msec
        nhdr.set_xyzt_units('mm', 'msec')
    else:
        # anatomical or DTI
        nhdr.set_xyzt_units('mm', 'unknown')
    # get original scaling
    if opts.scaling == 'off':
        slope = 1.0
        intercept = 0.0
    else:
        slope, intercept = pr_hdr.get_data_scaling(method=opts.scaling)
        nhdr.set_slope_inter(slope, intercept)
    # finalize the header: set proper data offset, pixdims, ...
    nimg.update_header()
    # figure out the output filename
    outfilename = splitext_addext(os.path.basename(infile))[0]
    if not opts.outdir is None:
        # set output path
        outfilename = os.path.join(opts.outdir, outfilename)
    # prep a file
    if opts.compressed:
        verbose('Using gzip compression')
        outfilename += '.nii.gz'
        outfile = gzip.open(outfilename, 'w')
    else:
        outfilename += '.nii'
        outfile = open(outfilename, 'w')
    verbose('Writing %s' % outfilename)
    # first write the header
    nimg._write_header(outfile, nhdr, slope, intercept)
    # now the data itself, but prevent any casting or scaling
    nibabel.volumeutils.array_to_file(
            raw_data,
            outfile,
            offset=nhdr.get_data_offset())
    # done
    outfile.close()
def main():
    parser = get_opt_parser()
    (opts, infiles) = parser.parse_args()
    global verbose_switch
    verbose_switch = opts.verbose
    if not opts.origin in ['scanner', 'fov']:
        error("Unrecognized value for --origin: '%s'." % opts.origin, 1)
    # store any exceptions
    errs = []
    for infile in infiles:
        verbose('Processing %s' % infile)
        try:
            proc_file(infile, opts)
        except Exception:
            err = sys.exc_info()[1]
            errs.append('%s: %s' % (infile, err))
    if len(errs):
        error('Caught %i exceptions. Dump follows:\n\n %s'
                % (len(errs), '\n'.join(errs)), 1)
    else:
        verbose('Done')
if __name__ == '__main__':
    main()
 |