Пример #1
0
def read_sdat_spar_pair(sdat_file, spar_file, tag=None):

    spar_params = read_spar(spar_file)
    data = read_sdat(sdat_file, spar_params['samples'], spar_params['rows'])

    if data.ndim < 4:
        data = data.reshape((1, 1, 1) + data.shape)

    # Move to right handed frame
    data = data.conj()

    # Dwelltime
    dwelltime = 1.0 / float(spar_params["sample_frequency"])

    # Meta
    meta = spar_to_nmrs_hdrext(spar_params)
    meta.set_standard_def('OriginalFile', [sdat_file.name])

    if tag is not None:
        meta.set_dim_info(0, tag)

    # Orientation
    if spar_params["volume_selection_enable"] == "yes":
        affine = _philips_orientation(spar_params)
    else:
        # Use default affine
        affine = np.diag(np.array([10000, 10000, 10000, 1]))
    orientation = NIFTIOrient(affine)

    return [
        nifti_mrs.NIfTI_MRS(data, orientation.Q44, dwelltime, meta),
    ]
Пример #2
0
def read_data_list_pair(data_file, list_file, spar_file):

    df, num_dict, coord_dict, os_dict = _read_list(list_file)
    sorted_data_dict = _read_data(data_file, df)

    spar_params = read_spar(spar_file)

    # Dwelltime
    dwelltime = 1.0 / float(spar_params["sample_frequency"])

    # Orientation
    affine = _philips_orientation(spar_params)
    orientation = NIFTIOrient(affine)

    data_out = []
    name_out = []
    for data_type in sorted_data_dict:
        data = sorted_data_dict[data_type]
        name = data_type

        # Meta
        meta = spar_to_nmrs_hdrext(spar_params)
        meta.set_standard_def('OriginalFile',
                              [data_file.name, list_file.name, spar_file.name])

        kept_ind = []
        for ii, sha in zip(indicies, data.shape[1:]):
            if sha > 1:
                kept_ind.append(ii)

        out_data = data.squeeze()
        if len(kept_ind) > 3:
            raise TooManyDimError(
                'Number of additional dimensions > 3.'
                f' Dimensions are {kept_ind} with shape {out_data.shape[1:]}.'
                ' NIFTI-MRS can only handle three dynamic dimensions. Unsure how to proceed.'
            )

        unknown_counter = 0
        for idx, ki in enumerate(kept_ind):
            if ki in defaults:
                meta.set_dim_info(idx,
                                  defaults[ki],
                                  info=f'data/list dim {ki}')
            else:
                meta.set_dim_info(idx,
                                  f'DIM_USER_{unknown_counter}',
                                  info=f'data/list dim {ki}')
                unknown_counter += 1

        # Insert spatial dimensions
        out_data = out_data.reshape((1, 1, 1) + out_data.shape)
        # Apply conjugate
        out_data = out_data.conj()

        data_out.append(
            nifti_mrs.NIfTI_MRS(out_data, orientation.Q44, dwelltime, meta))
        name_out.append(name)

    return data_out, name_out
Пример #3
0
def _process_mrsi_pfile(pfile):
    '''Handle MRSI data

    :param Pfile pfile: Pfile object
    :return: List of NIFTI MRS data objects
    :return: List of file name suffixes
    '''
    psd = pfile.hdr.rhi_psdname.decode('utf-8').lower()

    if psd != 'probe-p':
        raise UnsupportedPulseSequenceError('Unrecognised sequence, psdname must be "probe-p".')

    warn('The interpretation of pfile CSI data is poorly tested; rotations or transpositions of the'
         ' CSI grid could be present. Spec2nii currently lacks a complete set of CSI test data.'
         ' Please get in touch to help solve this issue!')

    data = np.transpose(pfile.map.raw_data, [0, 1, 2, 5, 4, 3])
    if data.shape[5] == 1:
        data = data.squeeze(axis=5)

    # Perform fft
    def fft_and_shift(x, axis):
        return np.fft.fftshift(np.fft.fft(x, axis=axis), axes=axis)

    data = fft_and_shift(data, 0)
    data = fft_and_shift(data, 1)
    data = fft_and_shift(data, 2)

    dwelltime = 1 / pfile.hdr.rhr_spectral_width
    meta = _populate_metadata(pfile)
    orientation = NIFTIOrient(_calculate_affine_mrsi(pfile))

    return [nifti_mrs.NIfTI_MRS(data, orientation.Q44, dwelltime, meta), ], ['', ]
Пример #4
0
def _process_svs_pfile(pfile):
    '''Handle SVS data

    :param Pfile pfile: Pfile object
    :return: List of NIFTI MRS data objects
    :return: List of file name suffixes
    '''
    psd = pfile.hdr.rhi_psdname.decode('utf-8').lower()

    if psd == 'probe-p':
        data, meta, dwelltime, fname_suffix = _process_probe_p(pfile)
    elif psd == 'oslaser':
        data, meta, dwelltime, fname_suffix = _process_oslaser(pfile)
    else:
        raise UnsupportedPulseSequenceError(
            'Unrecognised sequence, psdname must be "prob-p" or "oslaser".')

    orientation = NIFTIOrient(_calculate_affine(pfile))

    out_nmrs = []
    for dd, mm in zip(data, meta):
        out_nmrs.append(nifti_mrs.NIfTI_MRS(dd, orientation.Q44, dwelltime,
                                            mm))

    return out_nmrs, fname_suffix
Пример #5
0
def read_bruker(args):
    """

    :param args:
    :return list imageOut: 
    :return list fileoutNames:
    """
    imageOut = []
    fileoutNames = []

    # for all Bruker datasets compliant all queries
    for data, properties in yield_bruker(args):
            orientation = NIFTIOrient(np.reshape(np.array(properties['affine']), (4,4)))
            imageOut.append(
                nifti_mrs.NIfTI_MRS(data,
                                   orientation.Q44,
                                   properties['dwell_s'],
                                   nifti_mrs.hdr_ext(
                                       properties['SpectrometerFrequency'],
                                       properties['ResonantNucleus']
                                   ))
            )           
            fileoutNames.append(properties['id'])

    return imageOut, fileoutNames
Пример #6
0
def _proc_dataset(d, args):
    """
    Yield data and properties of a single dataset

    """
    # merge 2dseq complex frame group if present
    if d.is_complex and d.type == '2dseq':
        d = FrameGroupMerger().merge(d, 'FG_COMPLEX')

    # prepare the data array
    if d.is_svs:
        data = _prep_data_svs(d)
    elif d.is_mrsi:
        data = _prep_data_mrsi(d)
    else:
        data = d.data

    # get properties
    properties = d.to_dict()

    # Orientation information
    if d.type == 'fid':
        orientation = NIFTIOrient(_fid_affine_from_params(d))
    else:
        orientation = NIFTIOrient(np.reshape(np.array(properties['affine']), (4, 4)))

    # Meta data
    if d.type == 'fid':
        meta = _fid_meta(d, dump=args.dump_headers)
    else:
        meta = _2dseq_meta(d, dump=args.dump_headers)

    # Dwelltime - to do resolve this factor of 2 issue
    if d.type == 'fid':
        dwelltime = d.dwell_s * 2
    else:
        dwelltime = d.dwell_s * 2

    if args.fileout:
        name = args.fileout + '_' + d.id.rstrip('_')
    else:
        name = d.id.rstrip('_')

    yield data, orientation, dwelltime, meta, name
Пример #7
0
def process_uih_csi(img, verbose):
    """Process UIH DICOM MRSI data"""

    specData = np.frombuffer(img.dcm_data[('5600', '0020')].value, dtype=np.single)
    specDataCmplx = specData[0::2] + 1j * specData[1::2]

    warn('The orientation of UIH MRSI is relatively untested.'
         ' Please contribute data to help fix this!')
    shape = (img.dcm_data.Columns,
             img.dcm_data.Rows,
             int(img.dcm_data.NumberOfFrames),
             img.dcm_data.SpectroscopyAcquisitionDataColumns)

    specDataCmplx = specDataCmplx.reshape(shape)

    # WTC 05/01/21 From the limited test data I have I think there is this transposition.
    specDataCmplx = np.swapaxes(specDataCmplx, 0, 1)

    # Extract dicom parameters
    imageOrientationPatient = img.image_orient_patient.T
    imagePositionPatient = img.image_position
    xyzMM = np.asarray(img.voxel_sizes)

    currNiftiOrientation = dcm_to_nifti_orientation(imageOrientationPatient,
                                                    imagePositionPatient,
                                                    xyzMM,
                                                    specDataCmplx.shape[:3],
                                                    half_shift=True,
                                                    verbose=verbose)

    # UIH specific tweaks
    Q44 = currNiftiOrientation.Q44
    # 1) negate 3rd direction if ('0065', 'ff06') 3rd element is negative
    if float(img.dcm_data[('0065', 'ff06')].value[2]) < 0.0:
        Q44[:3, :3] = Q44[:3, :3] @ np.array([[1, 0, 0], [0, 1, 0], [0, 0, -1]])

    # 2) A half voxel shift is needed in the third dimension
    v = np.asarray([0, 0, 0.5]) @ Q44[:3, :3].T
    Q44[:3, 3] += v

    currNiftiOrientation = NIFTIOrient(Q44)

    dwelltime = 1.0 / img.dcm_data.SpectralWidth
    meta = extractDicomMetadata(img)

    return specDataCmplx, currNiftiOrientation, dwelltime, meta
Пример #8
0
def jmrui_mrui(args):
    '''Process .mrui format files.'''

    data, header, str_info = read_mrui(args.file)

    newshape = (1, 1, 1) + data.shape
    data = data.reshape(newshape)

    # meta-data
    dwelltime = header['sampling_interval'] * 1E-3

    meta = jmrui_hdr_to_obj_mrui(header, str_info)
    meta.set_standard_def('OriginalFile', [
        args.file.name,
    ])

    if data.ndim > 4:
        meta.set_dim_info(0, 'DIM_USER_0', info='jMRUI frames')

    # Read optional affine file
    if args.affine:
        affine = np.loadtxt(args.affine)
    else:
        tmp = np.array([10000, 10000, 10000, 1])
        affine = np.diag(tmp)

    nifti_orientation = NIFTIOrient(affine)

    img_out = [
        nifti_mrs.NIfTI_MRS(data, nifti_orientation.Q44, dwelltime, meta),
    ]

    # File names
    if args.fileout:
        fname_out = [
            args.fileout,
        ]
    else:
        fname_out = [
            args.file.stem,
        ]

    # Place in data output format
    return img_out, fname_out
Пример #9
0
def dcm_to_nifti_orientation(imageOrientationPatient,
                             imagePositionPatient,
                             xyzMM,
                             data_shape,
                             half_shift=False,
                             verbose=False):
    """Convert DCM orientation parameters to nifti orientations.

    :arg numpy.ndarray imageOrientationPatient: DICOM imageOrientationPatient tag. Shape = 2,3.
    :arg numpy.ndarray imagePositionPatient: DICOM imagePositionPatient tag.
    :arg numpy.ndarray xyzMM: Array containing the voxel sizes as contained in the
    nibabel.nicom.dicomwrappers.Wrapper voxel_sizes property.
    :arg data_shape: Shape of the spatial dimensions (dimensions 0-2).
    :arg bool half_shift: Apply half voxel shift to the x and y dimension.
    :arg bool verbose: Print debugging output.

    :return: NIFTIOrient object.
    :rtype: NIFTIOrient
    """
    # in style of dcm2niix
    # 1) calculate Q44
    Q44 = nifti_dicom2mat(imageOrientationPatient,
                          imagePositionPatient,
                          xyzMM,
                          verbose=verbose)

    # 2) calculate nifti quaternion parameters
    # From github.com/rordenlab/dcm2niix/blob/
    # 081c6300d0cf47088f0873cd586c9745498f637a/console/nii_dicom.cpp#L604
    _, Q44 = verify_slice_dir(Q44,
                              data_shape,
                              imagePositionPatient,
                              verbose=verbose)
    Q44[:2, :] *= -1

    # 3) If required apply the half-voxel shift in the first two dimensions
    if half_shift:
        Q44 = apply_half_voxel_shift(Q44)

    if verbose:
        print(f'Final Q44:\n {Q44}')

    # 4) place in data class for nifti orientation parameters
    return NIFTIOrient(Q44)
Пример #10
0
def lcm_raw(args):
    '''Processing for LCModel .RAW (and .H2O) files.
    Currently only handles one FID per file.
    '''
    # Read data from file
    data, header = readLCModelRaw(args.file, conjugate=True)

    newshape = (1, 1, 1) + data.shape
    data = data.reshape(newshape)

    # meta
    dwelltime = header['dwelltime']

    meta = nifti_mrs.hdr_ext(header['centralFrequency'],
                             args.nucleus)

    meta.set_standard_def('ConversionMethod', f'spec2nii v{spec2nii_ver}')
    conversion_time = datetime.now().isoformat(sep='T', timespec='milliseconds')
    meta.set_standard_def('ConversionTime', conversion_time)
    meta.set_standard_def('OriginalFile', [basename(args.file), ])

    # Read optional affine file
    if args.affine:
        affine = np.loadtxt(args.affine)
    else:
        tmp = np.array([10000, 10000, 10000, 1])
        affine = np.diag(tmp)

    nifti_orientation = NIFTIOrient(affine)

    img_out = [nifti_mrs.NIfTI_MRS(data,
                                   nifti_orientation.Q44,
                                   dwelltime,
                                   meta), ]

    # File names
    if args.fileout:
        fname_out = [args.fileout, ]
    else:
        fname_out = [splitext(basename(args.file))[0], ]

    # Place in data output format
    return img_out, fname_out
Пример #11
0
def text(args):
    '''Processing for simple ascii formatted columns of data.'''
    # Read text from file
    data = np.loadtxt(args.file)
    data = data[:, 0] + 1j * data[:, 1]

    newshape = (1, 1, 1) + data.shape
    data = data.reshape(newshape)

    # Interpret required arguments (frequency and bandwidth)
    dwelltime = 1.0 / args.bandwidth

    meta = nifti_mrs.hdr_ext(args.imagingfreq,
                             args.nucleus)

    meta.set_standard_def('ConversionMethod', 'spec2nii')
    conversion_time = datetime.now().isoformat(sep='T', timespec='milliseconds')
    meta.set_standard_def('ConversionTime', conversion_time)
    meta.set_standard_def('OriginalFile', [basename(args.file), ])

    # Read optional affine file
    if args.affine:
        affine = np.loadtxt(args.affine)
    else:
        tmp = np.array([10000, 10000, 10000, 1])
        affine = np.diag(tmp)

    nifti_orientation = NIFTIOrient(affine)

    img_out = [nifti_mrs.NIfTI_MRS(data,
                                   nifti_orientation.Q44,
                                   dwelltime,
                                   meta), ]

    # File names
    if args.fileout:
        fname_out = [args.fileout, ]
    else:
        fname_out = [splitext(basename(args.file))[0], ]

    # Place in data output format
    return img_out, fname_out
Пример #12
0
def _process_philips_fid(img, verbose):
    """Process Philips DICOM FID data"""

    specData = np.frombuffer(img.dcm_data[('5600', '0020')].value,
                             dtype=np.single)
    specDataCmplx = specData[0::2] + 1j * specData[1::2]

    # In the one piece of data I have been provided the data is twice as long as indicated (1 avg)
    # and the second half is a water reference.
    spec_points = img.dcm_data.SpectroscopyAcquisitionDataColumns
    spec_data_main = specDataCmplx[:spec_points]
    spec_data_ref = specDataCmplx[spec_points:]

    # 1) Extract dicom parameters
    defaultaffine = np.diag(np.array([10000, 10000, 10000, 1]))
    currNiftiOrientation = NIFTIOrient(defaultaffine)

    dwelltime = 1.0 / img.dcm_data.SpectralWidth
    meta = _extractDicomMetadata(img)
    meta_r = _extractDicomMetadata(img, water_suppressed=False)

    return spec_data_main, spec_data_ref, currNiftiOrientation, dwelltime, meta, meta_r
Пример #13
0
def read_varian(args):
    """read_varian -- load a varian fid.
    Note that this format is very flexible and some rather large assumptions are made.
    At the moment, this assumes little.
    :param file: path to the varian .fid directory, containing fid and procpar files.
    returns img_out, fname_out
    """
    dic, data = v.read(args.file)

    # extract number of coils
    number_of_coils = 0
    for i in dic['procpar']['rcvrs']['values'][0]:
        if re.search(i, 'y'):
            number_of_coils += 1

    # number of time points -- probably
    number_of_time_points = float(
        dic['procpar']['arraydim']['values'][0]) / number_of_coils
    if (not number_of_time_points.is_integer()):
        raise ValueError('Coil reshaping failed')

    number_of_time_points = int(number_of_time_points)

    # spectral number of points
    number_of_spectral_points = int(int(dic['np']) / 2)

    # reshape
    newshape = (1, 1, 1, number_of_spectral_points, number_of_coils,
                number_of_time_points)
    data = data.transpose()
    data = np.resize(data, newshape)

    # extract additional spectral metadata
    dwelltime = 1.0 / float(dic['procpar']['sw']['values'][0])
    imagingfreq = float(dic['procpar']['sfrq']['values'][0])
    nucleus = dic['procpar']['tn']['values'][0]
    # reshape to be in the form '13C' rather than 'C13'
    nucleus = nucleus[1:] + nucleus[0]

    try:
        repitition_time = float(dic['procpar']['tr']['values'][0])
    except KeyError:
        pass

    try:
        echotime = float(
            dic['procpar']['te']['values'][0])  # In ms if 'tis there
    except KeyError:
        pass
    try:
        echotime = float(dic['procpar']['pw']['values'][0]) + float(
            dic['procpar']['alfa']['values'][0])
    except KeyError:
        pass
    try:
        echotime = float(dic['procpar']['p1']['values'][0]) + float(
            dic['procpar']['alfa']['values'][0])
    except KeyError:
        pass

    # Parse 3D localisation
    sequence_name = dic['procpar']['seqfil']['values'][0]
    if (sequence_name.count('press') or sequence_name.count('steam')):
        affine = _varian_orientation_3d(dic)
    else:
        affine = np.diag(np.array([10000, 10000, 10000,
                                   1]))  # 10 m slab for now....

    # TODO: Jack should implement the affine matrix correctly for all sequences

    orientation = NIFTIOrient(affine)

    # create object
    meta = nifti_mrs.hdr_ext(imagingfreq, nucleus)
    meta.set_standard_def('ConversionMethod', f'spec2nii v{spec2nii_ver}')
    meta.set_standard_def('EchoTime', echotime)
    meta.set_standard_def('RepetitionTime', repitition_time)
    meta.set_standard_def('Manufacturer', 'Varian')
    meta.set_standard_def('ProtocolName',
                          dic['procpar']['seqfil']['values'][0])
    meta.set_standard_def('PatientName',
                          dic['procpar']['comment']['values'][0])

    # stuff that is nice to have:
    try:
        meta.set_standard_def('SoftwareVersions',
                              dic['procpar']['parver']['values'][0])
        meta.set_standard_def('TxCoil', dic['procpar']['rfcoil']['values'][0])
        meta.set_standard_def('RxCoil', dic['procpar']['rfcoil']['values'][0])
    except KeyError:
        warnings.warn('Expected standard metadata keying failed')
    try:
        meta.set_standard_def('InversionTime',
                              dic['procpar']['ti']['values'][0])
    except KeyError:
        pass
    try:
        meta.set_standard_def('ExcitationFlipAngle',
                              dic['procpar']['flip1']['values'][0])
    except KeyError:
        pass
    conversion_time = datetime.now().isoformat(sep='T',
                                               timespec='milliseconds')
    meta.set_standard_def('ConversionTime', conversion_time)
    meta.set_standard_def('OriginalFile', [basename(args.file)])

    # k-space
    meta.set_standard_def('kSpace', [False, False, False])

    # set tag dimensions
    meta.set_dim_info(0, "DIM_COIL")
    meta.set_dim_info(1, args.tag6)

    # Stuff full headers into user fields
    if args.dump_headers:
        meta.set_user_def(key='VarianProcpar',
                          doc='Varian procpar metadata.',
                          value=dic['procpar'])

    # File names
    if args.fileout:
        fname_out = [
            args.fileout,
        ]
    else:
        fname_out = [
            splitext(basename(args.file))[0],
        ]

    return [
        nifti_mrs.NIfTI_MRS(data, orientation.Q44, dwelltime, meta),
    ], fname_out