Example #1
0
 def get_bvecs_bvals(self):
     tensor_file = os.path.join(self.dirpath,
                                '_' + self.basename + '_tensor.dat')
     if not os.path.exists(tensor_file):
         log.warning('tensor file not found!')
         self.bvecs = None
         self.bvals = None
         return
     with open(tensor_file) as fp:
         try:
             uid = fp.readline().rstrip()
             ndirs = int('0' + fp.readline().rstrip())
         except:
             fp.seek(0, 0)
             ndirs = int('0' + fp.readline().rstrip())
         bvecs = np.fromfile(fp, sep=' ')
     if ndirs != self.dwi_numdirs or self.dwi_numdirs != bvecs.size / 3.:
         log.warning(
             'tensor file numdirs does not match PFile header numdirs!')
         self.bvecs = None
         self.bvals = None
     else:
         num_nondwi = self.num_timepoints_available - self.dwi_numdirs
         bvals = np.concatenate((np.zeros(num_nondwi, dtype=float),
                                 np.tile(self.dwi_bvalue,
                                         self.dwi_numdirs)))
         bvecs = np.hstack(
             (np.zeros((3, num_nondwi),
                       dtype=float), bvecs.reshape(self.dwi_numdirs, 3).T))
         self.bvecs, self.bvals = nimsmrdata.adjust_bvecs(
             bvecs, bvals, self.scanner_type, self.image_rotation)
Example #2
0
 def get_bvecs_bvals(self):
     tensor_file = os.path.join(self.dirpath, '_'+self.basename+'_tensor.dat')
     if not os.path.exists(tensor_file):
         log.warning('tensor file not found!')
         self.bvecs = None
         self.bvals = None
         return
     with open(tensor_file) as fp:
         uid = fp.readline().rstrip()
         ndirs = int('0'+fp.readline().rstrip())
         bvecs = np.fromfile(fp, sep=' ')
     if uid != self._hdr.series.series_uid:
         raise NIMSPFileError('tensor file UID does not match PFile UID!')
     if ndirs != self.dwi_numdirs or self.dwi_numdirs != bvecs.size / 3.:
         log.warning('tensor file numdirs does not match PFile header numdirs!')
         self.bvecs = None
         self.bvals = None
     else:
         num_nondwi = self.num_timepoints_available - self.dwi_numdirs # FIXME: assumes that all the non-dwi images are acquired first.
         bvals = np.concatenate((np.zeros(num_nondwi, dtype=float), np.tile(self.dwi_bvalue, self.dwi_numdirs)))
         bvecs = np.hstack((np.zeros((3,num_nondwi), dtype=float), bvecs.reshape(self.dwi_numdirs, 3).T))
         self.bvecs,self.bvals = nimsmrdata.adjust_bvecs(bvecs, bvals, self.scanner_type, self.image_rotation)
Example #3
0
 def get_bvecs_bvals(self):
     tensor_file = os.path.join(self.dirpath, '_'+self.basename+'_tensor.dat')
     if not os.path.exists(tensor_file):
         log.warning('tensor file not found!')
         self.bvecs = None
         self.bvals = None
         return
     with open(tensor_file) as fp:
         try:
             uid = fp.readline().rstrip()
             ndirs = int('0'+fp.readline().rstrip())
         except:
             fp.seek(0, 0)
             ndirs = int('0'+fp.readline().rstrip())
         bvecs = np.fromfile(fp, sep=' ')
     if ndirs != self.dwi_numdirs or self.dwi_numdirs != bvecs.size / 3.:
         log.warning('tensor file numdirs does not match PFile header numdirs!')
         self.bvecs = None
         self.bvals = None
     else:
         num_nondwi = self.num_timepoints_available - self.dwi_numdirs
         bvals = np.concatenate((np.zeros(num_nondwi, dtype=float), np.tile(self.dwi_bvalue, self.dwi_numdirs)))
         bvecs = np.hstack((np.zeros((3,num_nondwi), dtype=float), bvecs.reshape(self.dwi_numdirs, 3).T))
         self.bvecs,self.bvals = nimsmrdata.adjust_bvecs(bvecs, bvals, self.scanner_type, self.image_rotation)
Example #4
0
    def load_all_metadata(self):
        # TODO: make this less expensive. We can probably get away with a much more selective
        # loading of dicoms. E.g, maybe just the first volume and then the first slice of each subsequent vol?
        if self.dcm_list == None:
            self.load_dicoms()
        if self.is_dwi:
            self.bvals = np.array([getelem(dcm, TAG_BVALUE, float)[0] for dcm in self.dcm_list[0::self.num_slices]])
            self.bvecs = np.array([[getelem(dcm, TAG_BVEC[i], float) for i in range(3)] for dcm in self.dcm_list[0::self.num_slices]]).transpose()

        # Try to carry on on incomplete datasets. Also, some weird scans like MRVs don't set the
        # number of slices correctly in the dicom header. (Or at least they set it in a weird way
        # that we don't understand.)
        if len(self.dcm_list) < self.num_slices:
            self.num_slices = len(self.dcm_list)
        if len(self.dcm_list) < self.total_num_slices:
            self.total_num_slices = len(self.dcm_list)

        image_position = [tuple(getelem(dcm, 'ImagePositionPatient', float, [0., 0., 0.])) for dcm in self.dcm_list]

        if self.num_timepoints == 1:
            unique_slice_pos = np.unique(image_position).astype(np.float)
            # crude check for a 3-plane localizer. When we get one of these, we actually
            # want each plane to be a different time point.
            slice_distance = np.sqrt((np.diff(unique_slice_pos,axis=0)**2).sum(1))
            # ugly hack! The number of time points is the number of big (>10mm) jumps in slice-to-slice distance.
            self.num_timepoints = np.sum((slice_distance - np.median(slice_distance)) > 10) + 1
            self.num_slices = self.total_num_slices / self.num_timepoints

        cosines = getelem(self._hdr, 'ImageOrientationPatient', float, 6 * [np.nan])
        row_cosines = np.array(cosines[0:3])
        col_cosines = np.array(cosines[3:6])

        # Compute the slice_norm. From the NIFTI-1 header:
        #     The third column of R will be either the cross-product of the first 2 columns or
        #     its negative. It is possible to infer the sign of the 3rd column by examining
        #     the coordinates in DICOM attribute (0020,0032) "Image Position (Patient)" for
        #     successive slices. However, this method occasionally fails for reasons that I
        #     (RW Cox) do not understand.
        # For Siemens data, it seems that looking at 'SliceNormalVector' can help resolve this.
        #    dicom_slice_norm = getelem(self._hdr, 'SliceNormalVector', float, None)
        #    if dicom_slice_norm != None and np.dot(self.slice_norm, dicom_slice_norm) < 0.:
        #        self.slice_norm = -self.slice_norm
        # But otherwise, we'll have to fix this up after we load all the dicoms and check
        # the slice-to-slice position deltas.
        slice_norm = np.cross(row_cosines, col_cosines)

        # FIXME: the following could fail if the acquisition was before a full volume was aquired.
        if np.dot(slice_norm, image_position[0]) > np.dot(slice_norm, image_position[self.num_slices-1]):
            log.debug('flipping slice order')
            #slice_norm = -slice_norm
            self.reverse_slice_order = True
            self.origin = image_position[self.num_slices-1] * np.array([-1, -1, 1])
        else:
            self.origin = image_position[0] * np.array([-1, -1, 1])

        self.slice_order = nimsmrdata.SLICE_ORDER_UNKNOWN
        if self.total_num_slices >= self.num_slices and getelem(self.dcm_list[0], 'TriggerTime', float) is not None:
            trigger_times = np.array([getelem(dcm, 'TriggerTime', float) for dcm in self.dcm_list[0:self.num_slices]])
            if self.reverse_slice_order:
                # the slice order will be flipped when the image is saved, so flip the trigger times
                trigger_times = trigger_times[::-1]
            trigger_times_from_first_slice = trigger_times[0] - trigger_times
            if self.num_slices > 2:
                self.slice_duration = float(min(abs(trigger_times_from_first_slice[1:]))) / 1000.  # msec to sec
                if trigger_times_from_first_slice[1] < 0:
                    # slice 1 happened after slice 0, so this must be either SEQ_INC or ALT_INC
                    self.slice_order = nimsmrdata.SLICE_ORDER_SEQ_INC if trigger_times[2] > trigger_times[1] else nimsmrdata.SLICE_ORDER_ALT_INC
                else:
                    # slice 1 before slice 0, so must be ALT_DEC or SEQ_DEC
                    self.slice_order = nimsmrdata.SLICE_ORDER_ALT_DEC if trigger_times[2] > trigger_times[1] else nimsmrdata.SLICE_ORDER_SEQ_DEC
            else:
                self.slice_duration = trigger_times[0]
                self.slice_order = nimsmrdata.SLICE_ORDER_SEQ_INC

        rot = nimsmrdata.compute_rotation(row_cosines, col_cosines, slice_norm)
        if self.is_dwi:
            self.bvecs,self.bvals = nimsmrdata.adjust_bvecs(self.bvecs, self.bvals, self.scanner_type, rot)
        self.qto_xyz = nimsmrdata.build_affine(rot, self.mm_per_vox, self.origin)
        super(NIMSDicom, self).load_all_metadata()
Example #5
0
    def load_all_metadata(self):
        # TODO: make this less expensive. We can probably get away with a much more selective
        # loading of dicoms. E.g, maybe just the first volume and then the first slice of each subsequent vol?
        if self.dcm_list == None:
            self.load_dicoms()
        if self.is_dwi:
            self.bvals = np.array([
                getelem(dcm, TAG_BVALUE, float)[0]
                for dcm in self.dcm_list[0::self.num_slices]
            ])
            self.bvecs = np.array(
                [[getelem(dcm, TAG_BVEC[i], float) for i in range(3)]
                 for dcm in self.dcm_list[0::self.num_slices]]).transpose()

        # Try to carry on on incomplete datasets. Also, some weird scans like MRVs don't set the
        # number of slices correctly in the dicom header. (Or at least they set it in a weird way
        # that we don't understand.)
        if len(self.dcm_list) < self.num_slices:
            self.num_slices = len(self.dcm_list)
        if len(self.dcm_list) < self.total_num_slices:
            self.total_num_slices = len(self.dcm_list)

        image_position = [
            tuple(getelem(dcm, 'ImagePositionPatient', float, [0., 0., 0.]))
            for dcm in self.dcm_list
        ]

        if self.num_timepoints == 1:
            unique_slice_pos = np.unique(image_position).astype(np.float)
            # crude check for a 3-plane localizer. When we get one of these, we actually
            # want each plane to be a different time point.
            slice_distance = np.sqrt((np.diff(unique_slice_pos,
                                              axis=0)**2).sum(1))
            # ugly hack! The number of time points is the number of big (>10mm) jumps in slice-to-slice distance.
            self.num_timepoints = np.sum(
                (slice_distance - np.median(slice_distance)) > 10) + 1
            self.num_slices = self.total_num_slices / self.num_timepoints

        cosines = getelem(self._hdr, 'ImageOrientationPatient', float,
                          6 * [np.nan])
        row_cosines = np.array(cosines[0:3])
        col_cosines = np.array(cosines[3:6])

        # Compute the slice_norm. From the NIFTI-1 header:
        #     The third column of R will be either the cross-product of the first 2 columns or
        #     its negative. It is possible to infer the sign of the 3rd column by examining
        #     the coordinates in DICOM attribute (0020,0032) "Image Position (Patient)" for
        #     successive slices. However, this method occasionally fails for reasons that I
        #     (RW Cox) do not understand.
        # For Siemens data, it seems that looking at 'SliceNormalVector' can help resolve this.
        #    dicom_slice_norm = getelem(self._hdr, 'SliceNormalVector', float, None)
        #    if dicom_slice_norm != None and np.dot(self.slice_norm, dicom_slice_norm) < 0.:
        #        self.slice_norm = -self.slice_norm
        # But otherwise, we'll have to fix this up after we load all the dicoms and check
        # the slice-to-slice position deltas.
        slice_norm = np.cross(row_cosines, col_cosines)

        # FIXME: the following could fail if the acquisition was before a full volume was aquired.
        if np.dot(slice_norm, image_position[0]) > np.dot(
                slice_norm, image_position[self.num_slices - 1]):
            log.debug('flipping slice order')
            #slice_norm = -slice_norm
            self.reverse_slice_order = True
            self.origin = image_position[self.num_slices - 1] * np.array(
                [-1, -1, 1])
        else:
            self.origin = image_position[0] * np.array([-1, -1, 1])

        self.slice_order = nimsmrdata.SLICE_ORDER_UNKNOWN
        if self.total_num_slices >= self.num_slices and getelem(
                self.dcm_list[0], 'TriggerTime', float) is not None:
            trigger_times = np.array([
                getelem(dcm, 'TriggerTime', float)
                for dcm in self.dcm_list[0:self.num_slices]
            ])
            if self.reverse_slice_order:
                # the slice order will be flipped when the image is saved, so flip the trigger times
                trigger_times = trigger_times[::-1]
            trigger_times_from_first_slice = trigger_times[0] - trigger_times
            if self.num_slices > 2:
                self.slice_duration = float(
                    min(abs(trigger_times_from_first_slice[1:]))
                ) / 1000.  # msec to sec
                if trigger_times_from_first_slice[1] < 0:
                    # slice 1 happened after slice 0, so this must be either SEQ_INC or ALT_INC
                    self.slice_order = nimsmrdata.SLICE_ORDER_SEQ_INC if trigger_times[
                        2] > trigger_times[
                            1] else nimsmrdata.SLICE_ORDER_ALT_INC
                else:
                    # slice 1 before slice 0, so must be ALT_DEC or SEQ_DEC
                    self.slice_order = nimsmrdata.SLICE_ORDER_ALT_DEC if trigger_times[
                        2] > trigger_times[
                            1] else nimsmrdata.SLICE_ORDER_SEQ_DEC
            else:
                self.slice_duration = trigger_times[0]
                self.slice_order = nimsmrdata.SLICE_ORDER_SEQ_INC

        rot = nimsmrdata.compute_rotation(row_cosines, col_cosines, slice_norm)
        if self.is_dwi:
            self.bvecs, self.bvals = nimsmrdata.adjust_bvecs(
                self.bvecs, self.bvals, self.scanner_type, rot)
        self.qto_xyz = nimsmrdata.build_affine(rot, self.mm_per_vox,
                                               self.origin)
        super(NIMSDicom, self).load_all_metadata()