File size: 7,017 Bytes
d014c5a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
"""
Use this to run the nnU-Net inference. You can run multiple instances of this code in parallel!
Just change the `part_id` and `gpu` arguments to split the task into multiple parts.
Example of splitting it in 4 parts:
    python PredictSubOrgansnUnet.py --num_parts 4 --part_id 0 --gpu 0
    python PredictSubOrgansnUnet.py --num_parts 4 --part_id 1 --gpu 1
    python PredictSubOrgansnUnet.py --num_parts 4 --part_id 2 --gpu 2
    python PredictSubOrgansnUnet.py --num_parts 4 --part_id 3 --gpu 3
"""


import argparse
from nnunetv2.paths import nnUNet_results, nnUNet_raw
import torch
from batchgenerators.utilities.file_and_folder_operations import join
from nnunetv2.inference.predict_from_raw_data import nnUNetPredictor
import os
import pandas as pd

def split_files(files_input, files_output, num_parts, part_id):
    """
    Splits the files_input and files_output into num_parts and selects the partition for the given part_id.

    Args:
        files_input (list): List of input files.
        files_output (list): List of output files.
        num_parts (int): Number of partitions.
        part_id (int): Partition ID to select (starting from 0).

    Returns:
        tuple: (files_input_partition, files_output_partition) for the given part_id.
    """
    assert len(files_input) == len(files_output), "files_input and files_output must have the same length"
    assert 0 <= part_id < num_parts, "Invalid part_id. It must be in the range [0, num_parts-1]"

    # Split the files based on num_parts
    total_files = len(files_input)
    files_per_part = (total_files + num_parts - 1) // num_parts  # Ensure rounding up
    start_idx = part_id * files_per_part
    end_idx = min(start_idx + files_per_part, total_files)

    # Return the partition
    return files_input[start_idx:end_idx], files_output[start_idx:end_idx]

def parse_args():
    parser = argparse.ArgumentParser(description="Run nnUNet prediction with custom arguments.")
    parser.add_argument('--pth', type=str, default='/projects/bodymaps/Data/image_only/AbdomenAtlasPro/AbdomenAtlasPro/', 
                        help="Path to input images folder")
    parser.add_argument('--outdir', type=str, default='/projects/bodymaps/Pedro/data/JHH_liver_segments/', 
                        help="Path to output folder for predictions")
    parser.add_argument('--checkpoint', type=str, 
                        default='./nnUNetOrgansAndSubSegments/', 
                        help="Path to model checkpoint folder")
    parser.add_argument('--num_parts', type=int, default=1, 
                        help="Number of parts to split the task into")
    parser.add_argument('--part_id', type=int, default=0, 
                        help="ID of the current part (0-indexed)")
    parser.add_argument('--gpu', type=int, default=0, 
                        help="ID of the gpu to be used")
    parser.add_argument('--workers', type=int, default=2, 
                        help="Number of worker processes for preprocessing and segmentation export")
    parser.add_argument('--BDMAP_format', action='store_true', 
                    help="Enable BDMAP format")
    parser.add_argument('--ids', default=None, help='Path to csv with a BDMAP ID column and the cases you want to inference')            
    parser.add_argument('--reset', action='store_true', 
                    help="Overwrites old cases")
    return parser.parse_args()


def filter_existing_outputs(files_input, files_output):
    """
    Filter out cases where the output file already exists.
    
    Parameters:
        files_input (list): A list of input file paths (or lists of file paths).
        files_output (list): A list of output file paths.
        
    Returns:
        tuple: Two lists, (filtered_files_input, filtered_files_output), where
               each pair corresponds to an output that does not already exist.
    """
    filtered_input = []
    filtered_output = []
    
    for inp, out in zip(files_input, files_output):
        if not os.path.exists(out+'.nii.gz'):
            filtered_input.append(inp)
            filtered_output.append(out)
        else:
            print(f"Skipping already saved output: {out}")
    
    return filtered_input, filtered_output

def main():
    args = parse_args()

    # Instantiate the nnUNetPredictor
    predictor = nnUNetPredictor(
        tile_step_size=0.5,
        use_gaussian=True,
        use_mirroring=True,
        perform_everything_on_device=True,
        device=torch.device('cuda', args.gpu),
        verbose=False,
        verbose_preprocessing=False,
        allow_tqdm=True
    )

    if args.ids is not None:
        ids = pd.read_csv(args.ids)
        ids = sorted(ids['BDMAP ID'].to_list())
    else:
        ids = sorted(os.listdir(args.pth))
        
    print(f'Number of loaded ids: {len(ids)}')
    

    # Collect input and output files
    if args.BDMAP_format:
        print('Path:',args.pth)
        print('Files in path:',len(ids))

        files_input = [[os.path.join(args.pth, folder, 'ct.nii.gz')] 
                        for folder in ids 
                        if ('BDMAP' in folder)]
        print('Input files before split:',len(files_input))
        files_output = [os.path.join(args.outdir, folder) 
                        for folder in ids 
                        if ('BDMAP' in folder)]
        files_input, files_output = filter_existing_outputs(files_input, files_output)
        files_input, files_output = split_files(files_input, files_output, args.num_parts, args.part_id)
    else:
        if args.ids is None:
            files_input = [[os.path.join(args.pth, file)] for file in ids if (file.endswith('.nii.gz') and file[0]!='.')]
            files_output = [os.path.join(args.outdir, file.replace('.nii.gz','')) for file in ids if (file.endswith('.nii.gz') and file[0]!='.')]
        else:
            files_input = [[os.path.join(args.pth, file+'.nii.gz')] for file in ids]
            files_output = [os.path.join(args.outdir, file) for file in ids]
        files_input, files_output = filter_existing_outputs(files_input, files_output)
        
        files_input, files_output = split_files(files_input, files_output, args.num_parts, args.part_id)
        

    print('Input:', files_input[:10])
    print('Output:', files_output[:10])
    print(f'Cases to predict in part {args.part_id}: {len(files_input)}')


    # Initialize the network architecture and load the checkpoint
    predictor.initialize_from_trained_model_folder(
        join(args.checkpoint),
        use_folds=('all',),
        checkpoint_name='checkpoint_final.pth',
    )

    # Run the prediction
    predictor.predict_from_files(
        files_input, files_output,
        save_probabilities=False,
        overwrite=args.reset,
        num_processes_preprocessing=args.workers,
        num_processes_segmentation_export=args.workers,
        folder_with_segs_from_prev_stage=None,
        num_parts=1,
        part_id=0
    )


if __name__ == '__main__':
    main()