a b/fetal/preprocess.py
1
"""
2
Tools for converting, normalizing, and fixing the brats data.
3
"""
4
5
import glob
6
import os
7
import warnings
8
import shutil
9
10
import SimpleITK as sitk
11
import numpy as np
12
from nipype.interfaces.ants import N4BiasFieldCorrection
13
14
15
# from brats.train_fetal import config
16
17
18
def append_basename(in_file, append):
19
    dirname, basename = os.path.split(in_file)
20
    base, ext = basename.split(".", 1)
21
    return os.path.join(dirname, base + append + "." + ext)
22
23
24
def get_background_mask(in_folder, out_file, truth_name="GlistrBoost_ManuallyCorrected"):
25
    """
26
    This function computes a common background mask for all of the data in a subject folder.
27
    :param in_folder: a subject folder from the BRATS dataset.
28
    :param out_file: an image containing a mask that is 1 where the image data for that subject contains the background.
29
    :param truth_name: how the truth file is labeled int he subject folder
30
    :return: the path to the out_file
31
    """
32
    background_image = None
33
    for name in config["all_modalities"] + [truth_name]:
34
        image = sitk.ReadImage(get_image(in_folder, name))
35
        if background_image:
36
            if name == truth_name and not (image.GetOrigin() == background_image.GetOrigin()):
37
                image.SetOrigin(background_image.GetOrigin())
38
            background_image = sitk.And(image == 0, background_image)
39
        else:
40
            background_image = image == 0
41
    sitk.WriteImage(background_image, out_file)
42
    return os.path.abspath(out_file)
43
44
45
def convert_image_format(in_file, out_file):
46
    sitk.WriteImage(sitk.ReadImage(in_file), out_file)
47
    return out_file
48
49
50
def window_intensities_data(data, min_percent=1, max_percent=99):
51
    image = sitk.GetImageFromArray(data)
52
    image = sitk.IntensityWindowing(image,
53
                                    np.percentile(data, min_percent),
54
                                    np.percentile(data, max_percent))
55
    return sitk.GetArrayFromImage(image)
56
57
58
def window_intensities(in_file, out_file, min_percent=1, max_percent=99):
59
    image = sitk.ReadImage(in_file, sitk.sitkFloat32)
60
    image_data = sitk.GetArrayFromImage(image)
61
    out_image = sitk.IntensityWindowing(image, np.percentile(image_data, min_percent),
62
                                               np.percentile(image_data, max_percent))
63
    sitk.WriteImage(out_image, out_file)
64
    return os.path.abspath(out_file)
65
66
67
def correct_bias(in_file, out_file, image_type=sitk.sitkFloat64):
68
    """
69
    Corrects the bias using ANTs N4BiasFieldCorrection. If this fails, will then attempt to correct bias using SimpleITK
70
    :param in_file: input file path
71
    :param out_file: output file path
72
    :param image_type:
73
    :return: file path to the bias corrected image
74
    """
75
    correct = N4BiasFieldCorrection()
76
    correct.inputs.input_image = in_file
77
    correct.inputs.output_image = out_file
78
    try:
79
        done = correct.run()
80
        return done.outputs.output_image
81
    except IOError:
82
        warnings.warn(RuntimeWarning("ANTs N4BIasFieldCorrection could not be found."
83
                                     "Will try using SimpleITK for bias field correction"
84
                                     " which will take much longer. To fix this problem, add N4BiasFieldCorrection"
85
                                     " to your PATH system variable. (example: EXPORT PATH=${PATH}:/path/to/ants/bin)"))
86
        input_image = sitk.ReadImage(in_file, image_type)
87
        output_image = sitk.N4BiasFieldCorrection(input_image, input_image > 0)
88
        sitk.WriteImage(output_image, out_file)
89
        return os.path.abspath(out_file)
90
91
92
def rescale(in_file, out_file, minimum=0, maximum=20000):
93
    image = sitk.ReadImage(in_file)
94
    sitk.WriteImage(sitk.RescaleIntensity(image, minimum, maximum), out_file)
95
    return os.path.abspath(out_file)
96
97
98
def get_image(subject_folder, name):
99
    file_card = os.path.join(subject_folder, "*" + name + ".nii")
100
    try:
101
        return glob.glob(file_card)[0]
102
    except IndexError:
103
        raise RuntimeError("Could not find file matching {}".format(file_card))
104
105
106
def background_to_zero(in_file, background_file, out_file):
107
    sitk.WriteImage(sitk.Mask(sitk.ReadImage(in_file), sitk.ReadImage(background_file, sitk.sitkUInt8) == 0),
108
                    out_file)
109
    return out_file
110
111
112
def check_origin(in_file, in_file2):
113
    image = sitk.ReadImage(in_file)
114
    image2 = sitk.ReadImage(in_file2)
115
    if not image.GetOrigin() == image2.GetOrigin():
116
        image.SetOrigin(image2.GetOrigin())
117
        sitk.WriteImage(image, in_file)
118
119
120
def normalize_image(in_file, out_file, bias_correction=True):
121
    if bias_correction:
122
        correct_bias(in_file, out_file)
123
    else:
124
        shutil.copy(in_file, out_file)
125
    return out_file
126
127
128
def convert_brats_folder(in_folder, out_folder, truth_name="truth",
129
                         no_bias_correction_modalities=None):
130
    for name in config["all_modalities"]:
131
        image_file = get_image(in_folder, name)
132
        out_file = os.path.abspath(os.path.join(out_folder, name + ".nii"))
133
        perform_bias_correction = no_bias_correction_modalities and name not in no_bias_correction_modalities
134
        normalize_image(image_file, out_file, bias_correction=perform_bias_correction)
135
    # copy the truth file
136
    try:
137
        truth_file = get_image(in_folder, truth_name)
138
    except RuntimeError:
139
        truth_file = get_image(in_folder, truth_name.split("_")[0])
140
    out_file = os.path.abspath(os.path.join(out_folder, "truth.nii.gz"))
141
    shutil.copy(truth_file, out_file)
142
    check_origin(out_file, get_image(in_folder, config["all_modalities"][0]))
143
144
145
def convert_brats_data(brats_folder, out_folder, overwrite=False, no_bias_correction_modalities=("flair",)):
146
    """
147
    Preprocesses the BRATS data and writes it to a given output folder. Assumes the original folder structure.
148
    :param brats_folder: folder containing the original brats data
149
    :param out_folder: output folder to which the preprocessed data will be written
150
    :param overwrite: set to True in order to redo all the preprocessing
151
    :param no_bias_correction_modalities: performing bias correction could reduce the signal of certain modalities. If
152
    concerned about a reduction in signal for a specific modality, specify by including the given modality in a list
153
    or tuple.
154
    :return:
155
    """
156
    for subject_folder in glob.glob(os.path.join(brats_folder, "*")):
157
        if os.path.isdir(subject_folder):
158
            subject = os.path.basename(subject_folder)
159
            new_subject_folder = os.path.join(out_folder, os.path.basename(os.path.dirname(subject_folder)),
160
                                              subject)
161
            if not os.path.exists(new_subject_folder) or overwrite:
162
                if not os.path.exists(new_subject_folder):
163
                    os.makedirs(new_subject_folder)
164
                print('subject_folder: ' + subject_folder)
165
                convert_brats_folder(subject_folder, new_subject_folder,
166
                                     no_bias_correction_modalities=no_bias_correction_modalities)