def calc_DCE_properties_single(filepath, T1_tissue=1000, T1_blood=1440, relaxivity=.0045, TR=5, TE=2.1, scan_time_seconds=(11*60), hematocrit=0.45, injection_start_time_seconds=60, flip_angle_degrees=30, label_file=[], label_suffix=[], label_value=1, mask_value=0, mask_threshold=0, T1_map_file=[], T1_map_suffix='-T1Map', AIF_label_file=[],  AIF_value_data=[], AIF_value_suffix=[], convert_AIF_values=True, AIF_mode='label_average', AIF_label_suffix=[], AIF_label_value=1, label_mode='separate', param_file=[], default_population_AIF=False, initial_fitting_function_parameters=[.01,.1], outputs=['ktrans','ve','auc'], outfile_prefix='', processes=1, gaussian_blur=.65, gaussian_blur_axis=2):

    """ This is a master function that creates ktrans, ve, and auc values from raw intensity 1D-4D volumes.
    """

    print('\n')

    # NaN values are cleaned for ease of calculation.
    if isinstance(filepath, str):
        image = np.nan_to_num(nifti_2_numpy(filepath))
    else:
        image = np.nan_to_num(np.copy(filepath))

    # Unlikely that this circumstance will apply to 4D images in the future..
    dimension = len(image.shape)
    if dimension > 4:
        print('Error: Images greater than dimension 4 are currently not supported. Skipping this volume...')
        return []

    # Convenience variables created from input parameters.
    flip_angle_radians = flip_angle_degrees*np.pi/180
    time_interval_seconds = float(scan_time_seconds / image.shape[dimension-1])
    timepoints = image.shape[-1]
    bolus_time = int(np.ceil((injection_start_time_seconds / scan_time_seconds) * timepoints))

    # This step applies a gaussian blur to the provided axes. Blurring greatly increases DCE accuracy on noisy data.
    image = preprocess_dce(image, gaussian_blur=gaussian_blur, gaussian_blur_axis=gaussian_blur_axis)

    # Data store in other files may be relevant to DCE calculations. This utility function collects them and stores them in local variables.
    AIF_label_image, label_image, T1_image, AIF = retreive_data_from_files(filepath, label_file, label_mode, label_suffix, label_value, AIF_label_file, AIF_label_value, AIF_mode, AIF_label_suffix, T1_map_file, T1_map_suffix, AIF_value_data, AIF_value_suffix, image)

    # If no pre-set AIF text file is provided, one must be generated either from a label-map or a population AIF.
    if AIF == []:
        AIF = generate_AIF(scan_time_seconds, injection_start_time_seconds, time_interval_seconds, image, AIF_label_image, AIF_value_data, AIF_mode, dimension, AIF_label_value)

    # Error-catching for broken AIFs.
    if AIF == []:
        print('Problem calculating AIF. Skipping this volume...')
        return []

    # Signal conversion is required in order for raw data to interface with the Tofts model.
    contrast_image = convert_intensity_to_concentration(image, T1_tissue, TR, flip_angle_degrees, injection_start_time_seconds, relaxivity, time_interval_seconds, hematocrit)

    # Depending on where the AIF is derived from, AIF values may also need to be convered into Gd concentration.
    if AIF_mode == 'population':
        contrast_AIF = AIF
    elif AIF_value_data != [] and convert_AIF_values == False:
        contrast_AIF = AIF
    else:
        contrast_AIF = convert_intensity_to_concentration(AIF, T1_tissue, TR, flip_angle_degrees, injection_start_time_seconds, relaxivity, time_interval_seconds, hematocrit, T1_blood=T1_blood)

    # The optimization portion of the program is run here.
    parameter_maps = simplex_optimize(contrast_image, contrast_AIF, time_interval_seconds, bolus_time, image, label_image, mask_value, mask_threshold, initial_fitting_function_parameters, outputs, processes)

    # Outputs are saved, and then returned.
    for param_idx, param in enumerate(outputs):
        save_numpy_2_nifti(parameter_maps[...,param_idx], filepath, outfile_prefix + param + '.nii.gz')
    return outputs
Esempio n. 2
0
def generate_numpy_images(imagepath,
                          labels=False,
                          label_suffix='-label',
                          set_label='',
                          label_images=[],
                          mask_value=0,
                          levels=255,
                          use_labels=[-1],
                          erode=0,
                          mode="whole_volume",
                          constant_label=None):

    image_list = []
    unmodified_image_list = []
    imagename_list = []
    attributes_list = []

    # nifti_util.save_alternate_nifti(imagepath, levels, mask_value=mask_value)
    image = nifti_util.nifti_2_numpy(imagepath)

    # This is likely redundant with the basic assert function in nifti_util
    if not nifti_util.assert_3D(image):
        print 'Warning: image at path ' + imagepath + ' has multiple time points or otherwise greater than 3 dimensions, and will be skipped.'
        return [[], [], [], []]

    if labels:

        if constant_label is not None:
            label_path = constant_label
        elif set_label != '':
            label_path = os.path.join(
                os.path.dirname(imagepath),
                os.path.basename(os.path.normpath(set_label)))
        else:
            head, tail = os.path.split(imagepath)
            split_path = str.split(tail, '.')
            label_path = split_path[0] + label_suffix + '.' + '.'.join(
                split_path[1:])
            label_path = os.path.join(head, label_path)

        if os.path.isfile(label_path):
            label_image = nifti_util.nifti_2_numpy(label_path)

            if label_image.shape != image.shape:
                print 'Warning: image and label do not have the same dimensions. Imaging padding support has not yet been added. This image will be skipped.'
                return [[], [], [], []]

            # In the future: create an option to analyze each frame separately.
            if not nifti_util.assert_3D(label_image):
                print 'Warning: image at path ' + imagepath + ' has multiple time points or otherwise greater than 3 dimensions, and will be skipped.'
                return [[], [], [], []]

            label_image = label_image.astype(int)
            label_indices = np.unique(label_image)

            if label_indices.size == 1:
                print 'Warning: image at path ' + imagepath + ' has an empty label-map, and will be skipped.'
                return [[], [], [], []]

            # Will break if someone puts in '0' as a label to use.
            if use_labels[0] != -1:
                label_indices = np.array(
                    [0] + [x for x in label_indices if x in use_labels])

            split_images = split_image(image,
                                       label_image,
                                       label_indices,
                                       mask_value=mask_value)
            masked_images = [truncate_image(x) for x in split_images]

            for masked_image in masked_images:

                unmodified_image_list += [np.copy(masked_image)]

                masked_image = nifti_util.coerce_levels(masked_image,
                                                        levels=levels,
                                                        reference_image=image,
                                                        method="divide",
                                                        mask_value=mask_value)

                # It would be nice in the future to check if an image is too small to erode. Maybe a minimum-size parameter?
                # Or maybe a "maximum volume reduction by erosion?" Hmm..
                masked_image = nifti_util.erode_label(masked_image,
                                                      iterations=erode)

                # This is very ineffecient. TODO: Restructure this section.
                if mode == "maximal_slice":
                    image_list += [
                        extract_maximal_slice(masked_image,
                                              mode='non_mask')[:, :,
                                                               np.newaxis]
                    ]
                else:
                    image_list += [masked_image]

            if set_label == '':
                filename = str.split(label_path, '\\')[-1]
            else:
                filename = imagepath

            if label_indices.size == 2:
                imagename_list += [filename]
            else:
                split_filename = str.split(filename, '.')
                for labelval in label_indices[1:]:
                    filename = split_filename[0] + '_' + str(
                        int(labelval)) + '.' + split_filename[1]
                    imagename_list += [filename]

            attributes_list += [nifti_util.return_nifti_attributes(imagepath)
                                ] * (label_indices.size - 1)
            print 'Finished... ' + str.split(imagepath, '\\')[-1]

        else:
            print 'Warning: image at path ' + imagepath + ' has no label-map, and will be skipped.'
            return [[], [], [], []]

    else:
        image = nifti_util.coerce_levels(image,
                                         levels=levels,
                                         reference_image=image,
                                         method="divide",
                                         mask_value=mask_value)
        image_list += [image]
        unmodified_image_list += [image]
        imagename_list += [imagepath]
        attributes_list += [nifti_util.return_nifti_attributes(imagepath)]

    return [image_list, unmodified_image_list, imagename_list, attributes_list]
Esempio n. 3
0
def Convert_BRATS_Data():

    subdirs = []

    for grade in GRADES:
        subdirs += glob.glob(os.path.join(BRATS_PATH, grade, '*') + '/')

    for subdir in subdirs:

        print(subdir)

        #Flair
        Flair_dir = glob.glob(subdir + '*Flair*/')
        Flair_file = glob.glob(Flair_dir[0] + '/*Flair*.mha')[0]

        #T1
        T1_dir = glob.glob(subdir + '/*T1*/')
        T1_file = glob.glob(T1_dir[0] + '/*T1*.mha')[0]

        #T1c
        T1c_dir = glob.glob(subdir + '/*T1c*/')
        T1c_file = glob.glob(T1c_dir[0] + '/*T1c*.mha')[0]

        #T2
        T2_dir = glob.glob(subdir + '/*T2*/')
        T2_file = glob.glob(T2_dir[0] + '/*T2*.mha')[0]

        #ROI
        ROI_dir = glob.glob(subdir + '/*more*/')
        if len(ROI_dir) == 0:
            ROI_dir = glob.glob(subdir + '/*OT*/')
            ROI_file = glob.glob(ROI_dir[0] + '/*OT*.mha')[0]
        else:
            ROI_file = glob.glob(ROI_dir[0] + '/*more*.mha')[0]

        output_directory = os.path.join(
            OUTPUT_PATH, os.path.basename(os.path.dirname(subdir)))

        if not os.path.exists(output_directory):
            os.mkdir(output_directory)

        images_mha = [Flair_file, T1_file, T1c_file, T2_file, ROI_file]
        images_output = ['FLAIR', 'T1', 'T1c', 'T2', 'ROI']
        images_output_filenames = [
            label + '.nii.gz' for label in images_output
        ]
        images_output_filenames_preprocessed = [
            label + '_pp.nii.gz' for label in images_output
        ]

        for img_idx, image_mha in enumerate(images_mha):

            output_filename = os.path.join(output_directory,
                                           images_output_filenames[img_idx])
            output_filename_preprocessed = os.path.join(
                output_directory,
                images_output_filenames_preprocessed[img_idx])

            print(output_filename)
            if not os.path.exists(output_filename):
                Slicer_Command = [
                    'Slicer', '--launch', 'ResampleScalarVectorDWIVolume',
                    image_mha, output_filename
                ]
                call(' '.join(Slicer_Command), shell=True)

            if not os.path.exists(output_filename_preprocessed):
                img = nifti_2_numpy(output_filename)

                if images_output[img_idx] == 'FLAIR':
                    mask = np.copy(img)
                    mask[mask > 0] = 1
                    save_numpy_2_nifti(
                        mask, output_filename,
                        os.path.join(output_directory, 'MASK.nii.gz'))

                if images_output[img_idx] == 'ROI':
                    img[img > 0] = 1
                else:
                    masked_img = np.ma.masked_where(img == 0, img)
                    normed_img = (
                        img - np.ma.mean(masked_img)) / np.ma.std(masked_img)
                    normed_img[img == 0] = 0
                    img = normed_img

                print(images_output_filenames[img_idx])
                save_numpy_2_nifti(img, output_filename,
                                   output_filename_preprocessed)

    return
def retreive_data_from_files(filepath, label_file, label_mode, label_suffix, label_value, AIF_label_file, AIF_label_value, AIF_mode, AIF_label_suffix, T1_map_file, T1_map_suffix, AIF_value_data, AIF_value_suffix, image=[]):

    """ Check for associated data relevant to DCE calculation either via provided filenames or provided filename suffixes (e.g. '-T1map')
    """

    # TODO: Clean up redundancies in this function.

    # Check for a provided region of interest for calculating parameter values.
    if label_mode == 'none':
        label_image = []
    elif label_file != []:
        label_image = nifti_2_numpy(label_file)
    elif label_suffix != []:
        split_path = str.split(filepath, '.nii')
        if os.path.isfile(split_path[0] + label_suffix + '.nii' + split_path[1]):
            label_image = nifti_2_numpy(split_path[0] + label_suffix + '.nii' + split_path[1])
        elif os.path.isfile(split_path[0] + AIF_label_suffix + '.nii.gz'):
            label_image = nifti_2_numpy(split_path[0] + label_suffix + '.nii.gz')
        else:
            print("No labelmap found at provided label suffix. Continuing without...")
            label_image = []
    else:
        label_image = []

    # Check for a provided region of interest for determining an AIF.
    if AIF_mode == 'label_average':
        if AIF_label_file != []:
            AIF_label_image = nifti_2_numpy(AIF_label_file)
        elif AIF_label_suffix != []:
            split_path = str.split(filepath, '.nii')
            if os.path.isfile(split_path[0] + AIF_label_suffix + '.nii' + split_path[1]):
                AIF_label_image = nifti_2_numpy(split_path[0] + AIF_label_suffix + '.nii' + split_path[1])
            elif os.path.isfile(split_path[0] + AIF_label_suffix + '.nii.gz'):
                AIF_label_image = nifti_2_numpy(split_path[0] + AIF_label_suffix + '.nii.gz')
            else:
                print("No AIF labelmap found at provided label suffix. Continuing without...")
                AIF_label_image = []
        elif label_mode == 'separate':
            print('No label found for this AIF. If AIF label is in the same file as ROI, change the label_mode parameter to \'combined\'. Skipping this volume...')
            AIF_label_image = []
        elif label_mode == 'combined':
            if label_file != []:
                AIF_label_image = np.copy(label_image)
                AIF_label_image[label_image != AIF_label_value] = 0
            else:
                print('No label found for this AIF. If the AIF label is in a separate file from the ROI, change the label_mode parameter to \'separate\'. If not, be sure that the AIF_label_value parameter matches the AIF label value in your ROI. Skipping this volume...')
                AIF_label_image = []
    elif AIF_mode == 'population':
        AIF_label_image = []
    else:
        print('Invalid AIF_mode parameter. This volume will be skipped. \n')
        AIF_label_image = []

    # Check for a provided T1 mapping file, which will be relevant to signal conversion.
    if T1_map_file != []:
        T1_image = nifti_2_numpy(T1_map_file)
    elif T1_map_suffix != []:
        split_path = str.split(filepath, '.nii')
        if os.path.isfile(split_path[0] + T1_map_suffix + '.nii' + split_path[1]):
            T1_image = nifti_2_numpy(split_path[0] + T1_map_suffix + '.nii' + split_path[1])
        elif os.path.isfile(split_path[0] + T1_map_suffix + '.nii.gz' + split_path[1]):
            T1_image = nifti_2_numpy(split_path[0] + T1_map_suffix + '.nii.gz' + split_path[1])
        else:
            T1_image = []
            print('No T1 map found at provided T1 map file suffix. Continuing without... \n')       
    else:
        T1_image = []

    if T1_image != [] and (image.shape[0:-1] != T1_image.shape):
        print('T1 map and DCE image are not the same shape. T1 map processing will be skipped. \n')
        T1_image = []


    # This option is for text files that have AIF values in either raw or signal-converted format.
    # TODO: Address different delimiters between files? Or maybe others have to do this.
    if AIF_value_data != []:
        if AIF_value_suffix != []:
            split_path = str.split(filepath, '.nii')
            if os.path.isfile(split_path[0] + AIF_value_suffix + '.txt'):
                try:
                    AIF = np.loadtxt(split_path[0] + AIF_value_suffix + '.txt', dtype=object, delimiter=';')
                    AIF = [value for value in AIF if value != '']

                    if len(AIF) != image.shape[-1]:
                        print('AIF does not have as many timepoints as image. Assuming AIF timepoints are post-baseline, and filling pre-baseline points with zeros. \n')
                        new_AIF = np.zeros(image.shape[-1], dtype=float)
                        new_AIF[-len(AIF):] = AIF
                        AIF = new_AIF
                except:
                    print("Error reading AIF values file. AIF reader requires text files with semicolons (;) as delimiters. Skipping this volume... \n")
                    AIF = []
            else:
                AIF_value_data = []
                print('No AIF values found at provided AIF value suffix. Continuing without... \n')   
        if isinstance(AIF_value_data, str):
            try:
                AIF = np.loadtxt(AIF_value_data, dtype=object, delimiter=';')
                AIF = [value for value in AIF if value != '']

                if len(AIF) != image.shape[-1]:
                    print('AIF does not have as many timepoints as image. Assuming AIF timepoints are post-baseline, and filling pre-baseline points with zeros. \n')
                    new_AIF = np.zeros(image.shape[-1], dtype=float)
                    new_AIF[-len(AIF):] = AIF
                    AIF = new_AIF
            except:
                print("Error reading AIF values file. AIF reader requires text files with semicolons (;) as delimiters. Skipping this volume... \n")
                AIF = []
        elif AIF_value_data != []:
            AIF = AIF_value_data
        else:
            AIF = []
    else:
        AIF = []

    return AIF_label_image, label_image, T1_image, AIF