def dicom_to_nifti(dicom_input, output_file): """ This function will convert an anatomical dicom series to a nifti Examples: See unit test :param output_file: filepath to the output nifti :param dicom_input: directory with the dicom files for a single scan, or list of read in dicoms """ if len(dicom_input) <= 0: raise ConversionError('NO_DICOM_FILES_FOUND') # remove duplicate slices based on position and data dicom_input = _remove_duplicate_slices(dicom_input) # remove localizers based on image type dicom_input = _remove_localizers_by_imagetype(dicom_input) if settings.validate_slicecount: # remove_localizers based on image orientation (only valid if slicecount is validated) dicom_input = _remove_localizers_by_orientation(dicom_input) # validate all the dicom files for correct orientations common.validate_slicecount(dicom_input) if settings.validate_orientation: # validate that all slices have the same orientation common.validate_orientation(dicom_input) if settings.validate_orthogonal: # validate that we have an orthogonal image (to detect gantry tilting etc) common.validate_orthogonal(dicom_input) # sort the dicoms dicom_input = common.sort_dicoms(dicom_input) if settings.validate_sliceincrement: # validate that all slices have a consistent slice increment common.validate_sliceincrement(dicom_input) # Get data; originally z,y,x, transposed to x,y,z data = common.get_volume_pixeldata(dicom_input) affine = common.create_affine(dicom_input) # Convert to nifti nii_image = nibabel.Nifti1Image(data, affine) # Set TR and TE if available if Tag(0x0018, 0x0081) in dicom_input[0] and Tag(0x0018, 0x0081) in dicom_input[0]: common.set_tr_te(nii_image, float(dicom_input[0].RepetitionTime), float(dicom_input[0].EchoTime)) # Save to disk if output_file is not None: logger.info('Saving nifti to disk %s' % output_file) nii_image.to_filename(output_file) return {'NII_FILE': output_file, 'NII': nii_image}
def _convert_slice_incement_inconsistencies(dicom_input): """ If there is slice increment inconsistency detected, for the moment CT images, then split the volumes into subvolumes based on the slice increment and process each volume separately using a space constructed based on the highest resolution increment """ # Estimate the "first" slice increment based on the 2 first slices increment = numpy.array(dicom_input[0].ImagePositionPatient) - numpy.array( dicom_input[1].ImagePositionPatient) # Create as many volumes as many changes in slice increment. NB Increments might be repeated in different volumes max_slice_increment = 0 slice_incement_groups = [] current_group = [dicom_input[0], dicom_input[1]] previous_image_position = numpy.array(dicom_input[1].ImagePositionPatient) for dicom in dicom_input[2:]: current_image_position = numpy.array(dicom.ImagePositionPatient) current_increment = previous_image_position - current_image_position max_slice_increment = max(max_slice_increment, numpy.linalg.norm(current_increment)) if numpy.allclose(increment, current_increment, rtol=0.05, atol=0.1): current_group.append(dicom) if not numpy.allclose( increment, current_increment, rtol=0.05, atol=0.1): slice_incement_groups.append(current_group) current_group = [current_group[-1], dicom] increment = current_increment previous_image_position = current_image_position slice_incement_groups.append(current_group) # Create nibabel objects for each volume based on the corresponding headers slice_incement_niftis = [] slice_increments = [] voxel_sizes = {} for dicom_slices in slice_incement_groups: data = common.get_volume_pixeldata(dicom_slices) affine, _ = common.create_affine(dicom_slices) current_volume = nibabel.Nifti1Image(data.squeeze(), affine) slice_increment = numpy.linalg.norm(current_volume.header.get_zooms()) voxel_sizes['%.5f' % slice_increment] = current_volume.header.get_zooms() slice_increments.extend([slice_increment] * (len(dicom_slices) - 1)) slice_incement_niftis.append(current_volume) tenth_percentile_incement = numpy.percentile(slice_increments, 15) most_used_increment = min(slice_increments, key=lambda x: abs(x - tenth_percentile_incement)) voxel_size = voxel_sizes['%.5f' % most_used_increment] nifti_volume = resample.resample_nifti_images(slice_incement_niftis, voxel_size=voxel_size) return nifti_volume, max_slice_increment
def _convert_slice_incement_inconsistencies(dicom_input): """ If there is slice increment inconsistency detected, for the moment CT images, then split the volumes into subvolumes based on the slice increment and process each volume separately using a space constructed based on the highest resolution increment """ # Estimate the "first" slice increment based on the 2 first slices increment = numpy.array(dicom_input[0].ImagePositionPatient) - numpy.array(dicom_input[1].ImagePositionPatient) # Create as many volumes as many changes in slice increment. NB Increments might be repeated in different volumes max_slice_increment = 0 slice_incement_groups = [] current_group = [dicom_input[0], dicom_input[1]] previous_image_position = numpy.array(dicom_input[1].ImagePositionPatient) for dicom in dicom_input[2:]: current_image_position = numpy.array(dicom.ImagePositionPatient) current_increment = previous_image_position - current_image_position max_slice_increment = max(max_slice_increment, numpy.linalg.norm(current_increment)) if numpy.allclose(increment, current_increment, rtol=0.05, atol=0.1): current_group.append(dicom) if not numpy.allclose(increment, current_increment, rtol=0.05, atol=0.1): slice_incement_groups.append(current_group) current_group = [current_group[-1], dicom] increment = current_increment previous_image_position = current_image_position slice_incement_groups.append(current_group) # Create nibabel objects for each volume based on the corresponding headers slice_incement_niftis = [] for dicom_slices in slice_incement_groups: data = common.get_volume_pixeldata(dicom_slices) affine, _ = common.create_affine(dicom_slices) slice_incement_niftis.append(nibabel.Nifti1Image(data, affine)) nifti_volume = resample.resample_nifti_images(slice_incement_niftis) return nifti_volume, max_slice_increment
def _timepoint_to_block(timepoint_dicoms): """ Convert slices to a block of data by reading the headers and appending """ # similar way of getting the block to anatomical however here we are creating the dicom series our selves return common.get_volume_pixeldata(timepoint_dicoms)
def _stack_to_block(timepoint_dicoms): """ Convert a mosaic slice to a block of data by reading the headers, splitting the mosaic and appending """ return common.get_volume_pixeldata(timepoint_dicoms)
def dicom_to_nifti(dicom_input, output_file): """ This function will convert an anatomical dicom series to a nifti Examples: See unit test :param output_file: filepath to the output nifti :param dicom_input: directory with the dicom files for a single scan, or list of read in dicoms """ if len(dicom_input) <= 0: raise ConversionError('NO_DICOM_FILES_FOUND') # remove duplicate slices based on position and data dicom_input = remove_duplicate_slices(dicom_input) # remove localizers based on image type dicom_input = remove_localizers_by_imagetype(dicom_input) # if no dicoms remain we should raise exception if len(dicom_input) < 1: raise ConversionValidationError('TOO_FEW_SLICES/LOCALIZER') if settings.validate_slicecount: common.validate_slicecount(dicom_input) # remove_localizers based on image orientation (only valid if slicecount is validated) dicom_input = remove_localizers_by_orientation(dicom_input) # validate all the dicom files for correct orientations common.validate_slicecount(dicom_input) if settings.validate_orientation: # validate that all slices have the same orientation common.validate_orientation(dicom_input) if settings.validate_orthogonal: # validate that we have an orthogonal image (to detect gantry tilting etc) common.validate_orthogonal(dicom_input) # sort the dicoms dicom_input = common.sort_dicoms(dicom_input) # validate slice increment inconsistent slice_increment_inconsistent = False if settings.validate_slice_increment: # validate that all slices have a consistent slice increment common.validate_slice_increment(dicom_input) elif common.is_slice_increment_inconsistent(dicom_input): slice_increment_inconsistent = True if settings.validate_instance_number: # validate that all slices have a consistent instance_number common.validate_instance_number(dicom_input) # if inconsistent increment and we allow resampling then do the resampling based conversion to maintain the correct geometric shape if slice_increment_inconsistent and settings.resample: nii_image, max_slice_increment = _convert_slice_incement_inconsistencies(dicom_input) # do the normal conversion else: # Get data; originally z,y,x, transposed to x,y,z data = common.get_volume_pixeldata(dicom_input) affine, max_slice_increment = common.create_affine(dicom_input) # Convert to nifti nii_image = nibabel.Nifti1Image(data.squeeze(), affine) # Set TR and TE if available if Tag(0x0018, 0x0080) in dicom_input[0] and Tag(0x0018, 0x0081) in dicom_input[0]: common.set_tr_te(nii_image, float(dicom_input[0].RepetitionTime), float(dicom_input[0].EchoTime)) # Save to disk if output_file is not None: logger.info('Saving nifti to disk %s' % output_file) nii_image.header.set_slope_inter(1, 0) nii_image.header.set_xyzt_units(2) # set units for xyz (leave t as unknown) nii_image.to_filename(output_file) return {'NII_FILE': output_file, 'NII': nii_image, 'MAX_SLICE_INCREMENT': max_slice_increment}
def dicom_to_nifti(dicom_input, output_file): """ This function will convert an anatomical dicom series to a nifti Examples: See unit test :param output_file: filepath to the output nifti :param dicom_input: directory with the dicom files for a single scan, or list of read in dicoms """ if len(dicom_input) <= 0: raise ConversionError('NO_DICOM_FILES_FOUND') # remove duplicate slices based on position and data dicom_input = _remove_duplicate_slices(dicom_input) # remove localizers based on image type dicom_input = _remove_localizers_by_imagetype(dicom_input) if settings.validate_slicecount: # remove_localizers based on image orientation (only valid if slicecount is validated) dicom_input = _remove_localizers_by_orientation(dicom_input) # validate all the dicom files for correct orientations common.validate_slicecount(dicom_input) if settings.validate_orientation: # validate that all slices have the same orientation common.validate_orientation(dicom_input) if settings.validate_orthogonal: # validate that we have an orthogonal image (to detect gantry tilting etc) common.validate_orthogonal(dicom_input) # sort the dicoms dicom_input = common.sort_dicoms(dicom_input) # validate slice increment inconsistent slice_increment_inconsistent = False if settings.validate_slice_increment: # validate that all slices have a consistent slice increment common.validate_slice_increment(dicom_input) elif common.is_slice_increment_inconsistent(dicom_input): slice_increment_inconsistent = True # if inconsistent increment and we allow resampling then do the resampling based conversion to maintain the correct geometric shape if slice_increment_inconsistent and settings.resample: nii_image, max_slice_increment = _convert_slice_incement_inconsistencies(dicom_input) # do the normal conversion else: # Get data; originally z,y,x, transposed to x,y,z data = common.get_volume_pixeldata(dicom_input) affine, max_slice_increment = common.create_affine(dicom_input) # Convert to nifti nii_image = nibabel.Nifti1Image(data, affine) # Set TR and TE if available if Tag(0x0018, 0x0081) in dicom_input[0] and Tag(0x0018, 0x0081) in dicom_input[0]: common.set_tr_te(nii_image, float(dicom_input[0].RepetitionTime), float(dicom_input[0].EchoTime)) # Save to disk if output_file is not None: logger.info('Saving nifti to disk %s' % output_file) nii_image.to_filename(output_file) return {'NII_FILE': output_file, 'NII': nii_image, 'MAX_SLICE_INCREMENT': max_slice_increment}