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)
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)
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)
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()
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()