Example #1
0
def parse_one(self):
    """
    Composer function, parses all metadata that can be parsed from a single dicom.

    Called by NIMSDicom init, if dicom manufacturer is Siemens.

    """
    mr.parse_standard_mr_tags(self)

    self.psd_name = self.getelem(
        self._hdr, 'CsaSeries.MrPhoenixProtocol.tSequenceFileName', str,
        '').lower().replace('%', '', 1)
    self.psd_iname = self.getelem(self._hdr, 'SeriesDescription')
    self.fov_x = self.getelem(
        self._hdr,
        'CsaSeries.MrPhoenixProtocol.sSliceArray.asSlice[0].dPhaseFOV',
        float)  # XXX fov-x = PhaseFOV?
    self.fov_y = self.getelem(
        self._hdr,
        'CsaSeries.MrPhoenixProtocol.sSliceArray.asSlice[0].dReadoutFOV',
        float)  # XXX fov-y = ReadoutFOV?
    self.receive_coil_name = self.getelem(self._hdr, 'CsaImage.ImaCoilString')
    slice_duration = self.getelem(self._hdr,
                                  'CsaImage.SliceMeasurementDuration', float,
                                  0.)
    self.slice_duration = slice_duration / 1e6 if slice_duration else None
    self.prescribed_duration = self.getelem(
        self._hdr, 'CsaSeries.MrPhoenixProtocol.lScanTimeSec')  # FIXME
    self.duration = self.getelem(
        self._hdr, 'CsaSeries.MrPhoenixProtocol.lTotalScanTimeSec'
    )  # FIXME: not guaranteed
    self.acq_no = None  # siemens acq # indicates the brain volume instance. varies within one scan.

    # this is not an ideal solution, as it assumes that EPI timeserise are always stored in mosaic
    # for mosaics, lReps = prescribed timepoints.  non-mosaics and non-timeseries will not have this tag
    lRep = self.getelem(self._hdr, 'CsaSeries.MrPhoenixProtocol.lRepetitions',
                        int)
    self.num_timepoints = (lRep + 1) if lRep else None

    self.dwi_dirs = self.getelem(
        self._hdr, 'CsaSeries.MrPhoenixProtocol.sDiffusion.lDiffDirections',
        int, None)
    if (self.dwi_dirs or 0) > 1:
        self.is_dwi = True
        self.num_timepoints = 1

    # some siemens MR dicoms are not reconstructable
    if 'CSAPARALLEL' in self.image_type:
        self.is_non_image = True
    if 'POSDISP' in self.image_type:
        self.is_non_image = True
    if self.image_type == SIEMENS_TYPE_DIS2D:
        # had 2 image orientations, and other metdata that varied between dicoms, and is not a localizer. AFNI cannot reconstsruct.
        # this is a specific hack fix. A more general fix is to check each dicom to see if the orientation matches the first dicom
        # but checking EVERY dicom isn't very ideal solution (some datasets might have an ENORMOUS number of dicoms)
        self.is_non_image = True

    infer_psd_type(self)
    mr.adjust_fov_acqmat(self)
    mr.infer_scan_type(self)
Example #2
0
def parse_all(self):
    """
    Composer function, parses all metadata that requires all dicoms.

    Called by NIMSDicom load_data, if dicom manufacturer is Siemens.

    """
    if 'MOSAIC' not in self.image_type:
        log.debug('SIEMENS SINGLE SLICE DICOM')
        self.total_num_slices = len(self._dcm_list)
        # num slices from CSA
        self.num_slices = self.getelem(self._hdr, 'CsaSeries.MrPhoenixProtocol.sSliceArray.lSize', int,
                                       self.getelem(self._hdr, 'CsaSeries.MrPhoenixProtocol.sGroupArray.asGroup[0].nSize', int, None)
                                       )
        self.num_timepoints = self.total_num_slices / self.num_slices if self.num_slices else None
    elif 'MOSAIC' in self.image_type:           # explicit for readability
        log.debug('SIEMENS MOSAIC')
        self.num_slices = self.getelem(self._hdr, 'CsaImage.NumberOfImagesInMosaic', int, self.getelem(self._hdr, 'NumberOfImagesInMosaic', int))
        self.num_timepoints = len(self._dcm_list)
        mosaic_dim = int(self.num_slices ** 0.5)
        if mosaic_dim ** 2 < self.num_slices:
            mosaic_dim += 1
        self.size = [x / mosaic_dim for x in self.size]
        self.fov = [x / mosaic_dim for x in self.fov]
        self.total_num_slices = self.num_slices * self.num_timepoints

    log.debug('num slices / vol: %s' % str(self.num_slices))  # stringify to be able to log NoneType

    self.duration = self.num_timepoints * (self.num_averages or 1) * self.tr if self.num_timepoints and self.tr else None

    # CsaSeries.MrPhoenixProtocol.sSliceArray.ucMode indicates siemens slice order:
    # Siemens; 1 Ascending, 2 Descending, and 4 Interleaved Ascending.
    # NIFTI;   1 Ascending, 2 Descending, and 4 Interleaving Descending
    # Siemens slice order 4 could be nifti slice order 3 or 5 depending on num_slices
    # - nifti slice order 3: interleave_asc, odd first, odd num slices, interleave INC
    # - nifti slice order 5: interleave_asc, even first, even num slices, interleave INC 2
    self.slice_order = self.getelem(self._hdr, 'CsaSeries.MrPhoenixProtocol.sSliceArray.ucMode', None, 0)
    if self.slice_order == 4:   # don't try to guess if num_slices can't be determined
        if self.num_slices % 2 != 0:
            self.slice_order = 3  # interleaved ascending, odd first
        else:
            self.slice_order = 5  # interleaved ascending, even first

    self.num_receivers = len([self._hdr[key] for key in self._hdr if key.endswith('sCoilElementID.tCoilID')])

    if self.total_num_slices < MAX_LOC_DCMS:
        slice_norms = [np.cross(np.matrix(d.get('ImageOrientationPatient')[0:3]), np.matrix(d.get('ImageOrientationPatient')[3:6]))[0] for d in self._dcm_list]
        norm_diff = [np.abs(np.dot(slice_norms[0], n)).round(2) for n in slice_norms]
        self.is_localizer = bool(len(set(norm_diff)) > 1)

    if self.is_dwi:
        self.bvals = np.array([MetaExtractor(d).get(TAG_BVALUE, 0.) for d in self._dcm_list[0:self.num_slices]])
        self.bvecs = np.array([MetaExtractor(d).get(TAG_BVEC, [0., 0., 0.]) for d in self._dcm_list[0:self.num_slices]]).transpose()

    mr.infer_scan_type(self)  # infer scan type again after determining all info
Example #3
0
def parse_one(self):
    """
    Composer function, parses all metadata that can be parsed from a single dicom.

    Called by NIMSDicom init, if dicom manufacturer is Siemens.

    """
    mr.parse_standard_mr_tags(self)

    self.psd_name = self.getelem(self._hdr, 'CsaSeries.MrPhoenixProtocol.tSequenceFileName', str, '').lower().replace('%', '', 1)
    self.psd_iname = self.getelem(self._hdr, 'SeriesDescription')
    self.fov_x = self.getelem(self._hdr, 'CsaSeries.MrPhoenixProtocol.sSliceArray.asSlice[0].dPhaseFOV', float)    # XXX fov-x = PhaseFOV?
    self.fov_y = self.getelem(self._hdr, 'CsaSeries.MrPhoenixProtocol.sSliceArray.asSlice[0].dReadoutFOV', float)  # XXX fov-y = ReadoutFOV?
    self.receive_coil_name = self.getelem(self._hdr, 'CsaImage.ImaCoilString')
    slice_duration = self.getelem(self._hdr, 'CsaImage.SliceMeasurementDuration', float, 0.)
    self.slice_duration = slice_duration / 1e6 if slice_duration else None
    self.prescribed_duration = self.getelem(self._hdr, 'CsaSeries.MrPhoenixProtocol.lScanTimeSec')   # FIXME
    self.duration = self.getelem(self._hdr, 'CsaSeries.MrPhoenixProtocol.lTotalScanTimeSec')         # FIXME: not guaranteed
    self.acq_no = None      # siemens acq # indicates the brain volume instance. varies within one scan.

    # this is not an ideal solution, as it assumes that EPI timeserise are always stored in mosaic
    # for mosaics, lReps = prescribed timepoints.  non-mosaics and non-timeseries will not have this tag
    lRep = self.getelem(self._hdr, 'CsaSeries.MrPhoenixProtocol.lRepetitions', int)
    self.num_timepoints = (lRep + 1) if lRep else None

    self.dwi_dirs = self.getelem(self._hdr, 'CsaSeries.MrPhoenixProtocol.sDiffusion.lDiffDirections', int, None)
    if (self.dwi_dirs or 0) > 1:
        self.is_dwi = True
        self.num_timepoints = 1

    # some siemens MR dicoms are not reconstructable
    if 'CSAPARALLEL' in self.image_type:
        self.is_non_image = True
    if 'POSDISP' in self.image_type:
        self.is_non_image = True
    if self.image_type == SIEMENS_TYPE_DIS2D:
        # had 2 image orientations, and other metdata that varied between dicoms, and is not a localizer. AFNI cannot reconstsruct.
        # this is a specific hack fix. A more general fix is to check each dicom to see if the orientation matches the first dicom
        # but checking EVERY dicom isn't very ideal solution (some datasets might have an ENORMOUS number of dicoms)
        self.is_non_image = True

    infer_psd_type(self)
    mr.adjust_fov_acqmat(self)
    mr.infer_scan_type(self)
Example #4
0
def parse_one(self):
    """
    Composer function, parses all metadata that can be parsed from a single dicom.

    Called by NIMSData init, if dicom manufacturer is GE Medical Sytems.

    """

    mr.parse_standard_mr_tags(self)
    self.psd_name = self.getelem(self._hdr, 'PulseSequenceName', str,
                                 '').lower()
    self.psd_iname = self.getelem(self._hdr, 'InternalPulseSequenceName')
    self.fov_x, self.fov_y = 2 * [
        self.getelem(self._hdr, 'ReconstructionDiameter', float)
    ]
    self.receive_coil_name = self.getelem(self._hdr, 'ReceiveCoilName')
    self.mt_offset_hz = self.getelem(self._hdr, 'OffsetFrequency', float)
    effective_echo_spacing = self.getelem(self._hdr, 'EffectiveEchoSpacing',
                                          float)
    self.effective_echo_spacing = effective_echo_spacing / 1e6 if effective_echo_spacing else None
    asset_r = self.getelem(self._hdr, 'AssetRFactors', None, [None, None])
    if isinstance(
            asset_r, unicode
    ) and '\\' in asset_r:  # GE Signa HDxt stores asset as string '1\1'
        asset_r = map(
            int, asset_r.split('\\'))  # reformat to [1, 1] for consistency
    elif isinstance(asset_r, float):  # asset_r can be single item float
        asset_r = [None, None]
    self.phase_encode_undersample, self.slice_encode_undersample = asset_r
    # some very old Ge systems will output dicoms that don't define Locations in Acquition, or define it in a way
    # that is weird.  It may incorrectly label the value type as OB, but not be able to translate the value, resulting
    # in the MetaExtractor excluding it from the it's output metadata.
    self.num_slices = self.getelem(self._hdr, 'LocationsInAcquisition', int)
    self.total_num_slices = self.getelem(self._hdr, 'ImagesInAcquisition', int)
    self.num_timepoints = self.getelem(self._hdr, 'NumberOfTemporalPositions',
                                       int)

    # slice check could end up wrong, if both total_num_slices and num_slices are None
    # could force num_slices and total_num_slices into different ORs, to prevent matching if both are None
    # thus only when they are both defined, AND not equal, can this test pass
    if (self.total_num_slices or 1) == (self.num_slices or 0):
        self.total_num_slices = (self.num_slices or 1) * (self.num_timepoints
                                                          or 1)
        log.debug('adjusted total_num_slices from %3d to %3d' %
                  (self.num_slices,
                   self.total_num_slices))  # num_slices == 'old' total_num

    # some localizer don't have header field to indicate the number of slices
    # per acquisition.  If the total_number of slices is set, and the num_timepoints is 1
    # then the number of slices should be equal to total number of slices
    if not self.num_slices and (self.num_timepoints or 1) == 1:
        self.num_slices = self.total_num_slices

    prescribed_duration = (self.tr or 0) * (self.num_timepoints or 0) * (
        self.num_averages or 1)  # FIXME: only works for fMRI, not anatomical
    if prescribed_duration != 0:
        self.prescribed_duration = prescribed_duration
        self.duration = prescribed_duration
    else:
        self.prescribed_duration = None
        self.duration = None

    dwi_dirs = self.getelem(self._hdr,
                            'UserData24{#DTIDiffusionDir.,Release10.0&Above}',
                            float)
    self.dwi_dirs = int(dwi_dirs) if dwi_dirs else None
    if self.image_type == GEMS_TYPE_ORIG and (self.dwi_dirs or 0) >= 6:
        self.is_dwi = True
        self.num_timepoints = 1

    if self.image_type == GEMS_TYPE_DERIVED_RFMT:
        self.is_non_image = True

    infer_psd_type(self)
    mr.adjust_fov_acqmat(self)
    mr.infer_scan_type(self)
Example #5
0
def parse_all(self):
    """
    Composer function, parses all metadata that requires all dicoms.

    Called by NIMSDicom load_data, if dicom manufacturer is Siemens.

    """
    if 'MOSAIC' not in self.image_type:
        log.debug('SIEMENS SINGLE SLICE DICOM')
        self.total_num_slices = len(self._dcm_list)
        # num slices from CSA
        self.num_slices = self.getelem(
            self._hdr, 'CsaSeries.MrPhoenixProtocol.sSliceArray.lSize', int,
            self.getelem(
                self._hdr,
                'CsaSeries.MrPhoenixProtocol.sGroupArray.asGroup[0].nSize',
                int, None))
        self.num_timepoints = self.total_num_slices / self.num_slices if self.num_slices else None
    elif 'MOSAIC' in self.image_type:  # explicit for readability
        log.debug('SIEMENS MOSAIC')
        self.num_slices = self.getelem(
            self._hdr, 'CsaImage.NumberOfImagesInMosaic', int,
            self.getelem(self._hdr, 'NumberOfImagesInMosaic', int))
        self.num_timepoints = len(self._dcm_list)
        mosaic_dim = int(self.num_slices**0.5)
        if mosaic_dim**2 < self.num_slices:
            mosaic_dim += 1
        self.size = [x / mosaic_dim for x in self.size]
        self.fov = [x / mosaic_dim for x in self.fov]
        self.total_num_slices = self.num_slices * self.num_timepoints

    log.debug('num slices / vol: %s' %
              str(self.num_slices))  # stringify to be able to log NoneType

    self.duration = self.num_timepoints * (
        self.num_averages
        or 1) * self.tr if self.num_timepoints and self.tr else None

    # CsaSeries.MrPhoenixProtocol.sSliceArray.ucMode indicates siemens slice order:
    # Siemens; 1 Ascending, 2 Descending, and 4 Interleaved Ascending.
    # NIFTI;   1 Ascending, 2 Descending, and 4 Interleaving Descending
    # Siemens slice order 4 could be nifti slice order 3 or 5 depending on num_slices
    # - nifti slice order 3: interleave_asc, odd first, odd num slices, interleave INC
    # - nifti slice order 5: interleave_asc, even first, even num slices, interleave INC 2
    self.slice_order = self.getelem(
        self._hdr, 'CsaSeries.MrPhoenixProtocol.sSliceArray.ucMode', None, 0)
    if self.slice_order == 4:  # don't try to guess if num_slices can't be determined
        if self.num_slices % 2 != 0:
            self.slice_order = 3  # interleaved ascending, odd first
        else:
            self.slice_order = 5  # interleaved ascending, even first

    self.num_receivers = len([
        self._hdr[key] for key in self._hdr
        if key.endswith('sCoilElementID.tCoilID')
    ])

    if self.total_num_slices < MAX_LOC_DCMS:
        slice_norms = [
            np.cross(np.matrix(d.get('ImageOrientationPatient')[0:3]),
                     np.matrix(d.get('ImageOrientationPatient')[3:6]))[0]
            for d in self._dcm_list
        ]
        norm_diff = [
            np.abs(np.dot(slice_norms[0], n)).round(2) for n in slice_norms
        ]
        self.is_localizer = bool(len(set(norm_diff)) > 1)

    if self.is_dwi:
        self.bvals = np.array(
            [MetaExtractor(d).get(TAG_BVALUE, 0.) for d in self._dcm_list[:]])
        self.bvecs = np.array([
            MetaExtractor(d).get(TAG_BVEC, [0., 0., 0.])
            for d in self._dcm_list[:]
        ]).transpose()

    mr.infer_scan_type(
        self)  # infer scan type again after determining all info
Example #6
0
File: ge.py Project: garikoitz/data
def parse_one(self):
    """
    Composer function, parses all metadata that can be parsed from a single dicom.

    Called by NIMSData init, if dicom manufacturer is GE Medical Sytems.

    """

    mr.parse_standard_mr_tags(self)
    self.psd_name = self.getelem(self._hdr, 'PulseSequenceName', str, '').lower()
    self.psd_iname = self.getelem(self._hdr, 'InternalPulseSequenceName')
    self.fov_x, self.fov_y = 2 * [self.getelem(self._hdr, 'ReconstructionDiameter', float)]
    self.receive_coil_name = self.getelem(self._hdr, 'ReceiveCoilName')
    self.mt_offset_hz = self.getelem(self._hdr, 'OffsetFrequency', float)
    effective_echo_spacing = self.getelem(self._hdr, 'EffectiveEchoSpacing', float)
    self.effective_echo_spacing = effective_echo_spacing / 1e6 if effective_echo_spacing else None
    asset_r = self.getelem(self._hdr, 'AssetRFactors', None, [None, None])
    if isinstance(asset_r, unicode) and '\\' in asset_r:    # GE Signa HDxt stores asset as string '1\1'
        asset_r = map(int, asset_r.split('\\'))             # reformat to [1, 1] for consistency
    elif isinstance(asset_r, float):                        # asset_r can be single item float
        asset_r = [None, None]
    self.phase_encode_undersample, self.slice_encode_undersample = asset_r
    # some very old Ge systems will output dicoms that don't define Locations in Acquition, or define it in a way
    # that is weird.  It may incorrectly label the value type as OB, but not be able to translate the value, resulting
    # in the MetaExtractor excluding it from the it's output metadata.
    self.num_slices = self.getelem(self._hdr, 'LocationsInAcquisition', int)
    self.total_num_slices = self.getelem(self._hdr, 'ImagesInAcquisition', int)
    self.num_timepoints = self.getelem(self._hdr, 'NumberOfTemporalPositions', int)

    # slice check could end up wrong, if both total_num_slices and num_slices are None
    # could force num_slices and total_num_slices into different ORs, to prevent matching if both are None
    # thus only when they are both defined, AND not equal, can this test pass
    if (self.total_num_slices or 1) == (self.num_slices or 0):
        self.total_num_slices = (self.num_slices or 1) * (self.num_timepoints or 1)
        log.debug('adjusted total_num_slices from %3d to %3d' % (self.num_slices, self.total_num_slices))  # num_slices == 'old' total_num

    # some localizer don't have header field to indicate the number of slices
    # per acquisition.  If the total_number of slices is set, and the num_timepoints is 1
    # then the number of slices should be equal to total number of slices
    if not self.num_slices and (self.num_timepoints or 1) == 1:
        self.num_slices = self.total_num_slices

    prescribed_duration = (self.tr or 0) * (self.num_timepoints or 0) * (self.num_averages or 1)  # FIXME: only works for fMRI, not anatomical
    if prescribed_duration != 0:
        self.prescribed_duration = prescribed_duration
        self.duration = prescribed_duration
    else:
        self.prescribed_duration = None
        self.duration = None

    dwi_dirs = self.getelem(self._hdr, 'UserData24{#DTIDiffusionDir.,Release10.0&Above}', float)
    self.dwi_dirs = int(dwi_dirs) if dwi_dirs else None
    if self.image_type == GEMS_TYPE_ORIG and (self.dwi_dirs or 0) >= 6:
        self.is_dwi = True
        self.num_timepoints = 1

    if self.image_type == GEMS_TYPE_DERIVED_RFMT:
        self.is_non_image = True

    infer_psd_type(self)
    mr.adjust_fov_acqmat(self)
    mr.infer_scan_type(self)