File size: 13,965 Bytes
da9fb1e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
#coding:utf-8
'''

write by ygq

create on 2025-07-24

update kaggle data clean



依次解析train.csv以及test.csv文件,获取每个数据集基本信息;

根据解析的id查找对应的train/test目录下的影像并做规范处理,同时查找label的segment目录下的标签,提取不同部位的CT的标签位置保存到json文件中;

完成后保存json并退出



'''
import os
import glob
import pandas as pd
import SimpleITK as sitk
import argparse
import json
from tqdm import tqdm
from util import meta_data
import util
import numpy as np
# from bert_helper import *

# model_name = "bert-large-uncased"
# reduce_method = 'mean'
# max_words_num = 32  # max number of words in the caption > 2
    
# embeder, tokenizer = get_frozen_embeder(model_name)

# string1 = "modality: ct, gender: female, age: 51, roi: abdomen"
# embeder_output1 = str2emb(string1, max_words_num, embeder, tokenizer, reduce_method=reduce_method)

# string2 = "modality: ct, gender: female, age: 50, roi: head"

# embeder_output2 = str2emb(string2, max_words_num, embeder, tokenizer, reduce_method=reduce_method)

# input_size = embeder.config.vocab_size
# in_size = embeder.config.hidden_size
    
# print(embeder, input_size, in_size)
# print(tokenizer)
    

# print(embeder_output1)
# print(embeder_output1.shape)  # torch.Size([1, 8, 768])
    
    
# print(embeder_output2)
# print(embeder_output2.shape)  # torch.Size([1, 8, 768])
    

# error = torch.abs(embeder_output1 - embeder_output2)
# print(error)
# print("Embedding distance between the two sentences: ")
# print(f"String1: {string1}")
# print(f"String2: {string2}")
# print(torch.mean(error))


# exit()


meta_id_name='Patient'
meta_weeks_name='Weeks'
meta_fvc_name='FVC'
meta_percent_name='Percent'
meta_age_name='Age'
meta_sex_name='Sex'
meta_status_name='SmokingStatus'

TASK_VALUE="segmentation"
CLAMP_RANGE_CT = [-300,300]
CLAMP_RANGE_MRI = [-1,0] # MRI images threshold placeholder TBC...


# def find_metadata_files(path):
#     # for Cancer Image Archive (TCIA) dataset
#     search_pattern = os.path.join(path, '**', 'metadata.csv')
#     return glob.glob(search_pattern, recursive=True)

def find_metadata_files(path):
    # for Cancer Image Archive (TCIA) dataset
    search_pattern = os.path.join(path, '*.csv')
    return glob.glob(search_pattern, recursive=True)
##added by yanguoqing on 20250527
def find_image_dirs(path):
    return os.listdir(path)

##modify by yanguoqing on 20250527
def load_dicom_images(folder_path):
    reader = sitk.ImageSeriesReader()
    dicom_names = reader.GetGDCMSeriesFileNames(folder_path)
    reader.SetFileNames(dicom_names)
    image = reader.Execute()
    return dicom_names,image

##added by yanguoqing on 20250527
def load_dicom_tag(imgs):
    reader = sitk.ImageFileReader()
    # dicom_names = reader.GetGDCMSeriesFileNames(folder_path)
    reader.SetFileName(imgs)
    reader.ReadImageInformation()  # 仅读取元信息,不加载像素数据
    # metadata_keys = reader.GetMetaDataKeys()
    tag=reader.Execute()
    return tag

def load_nrrd(fp):
    return sitk.ReadImage(fp)

def save_nifti(image, output_path, folder_path):
    # Set metadata in the NIfTI file's header
    output_dirpath = os.path.dirname(output_path)
    if not os.path.exists(output_dirpath):
        print(f"Creating directory {output_dirpath}")
        os.makedirs(output_dirpath)
    # Set metadata in the NIfTI file's header
    image.SetMetaData("FolderPath", folder_path)
    sitk.WriteImage(image, output_path)

##modify by yanguoqing on 20250527
def convert_windows_to_linux_path(windows_path):
    # Replace backslashes with forward slashes and remove the drive letter
    # Some meta files have windows paths, but the data is stored on a linux server
    linux_path = windows_path.replace('\\', '/')
    if ':' in linux_path:
        linux_path = linux_path.split(':', 1)[1]
    return linux_path

def main(target_path, output_dir):
    metadata_files = find_metadata_files(target_path)
    pid_dirs=find_image_dirs(target_path)
    failed_files = []
    if not os.path.isdir(output_dir):
        os.makedirs(output_dir)
    json_output_path = os.path.join(output_dir, 'nifti_mappings.json')
    failed_files_path = os.path.join(output_dir, 'failed_files.json')
    meta = meta_data()
    
    # Initialize the JSON file
    if not os.path.exists(json_output_path):
        with open(json_output_path, 'w') as json_file:
            json.dump({}, json_file)
    
    if pid_dirs:
        for pid_dir in tqdm(pid_dirs, desc="Processing pid dirs"):
            if not os.path.isdir(os.path.join(target_path,pid_dir)):
                continue
            meta_file=os.path.join(target_path,'%s.csv'%pid_dir)
            if os.path.isfile(meta_file):
                mf_flag=True
                df_meta=pd.read_csv(meta_file,sep=',')
            else:
                mf_flag=False
            image_dirs=find_image_dirs(os.path.join(target_path,pid_dir))
            for data_dir in tqdm(image_dirs, desc="Processing images files"):
            
                location=data_dir
                
                full_path=os.path.join(target_path,pid_dir,data_dir)
                data_info_row=df_meta[df_meta[meta_id_name]==data_dir]
                
                if data_info_row.shape[0]>0:
                    data_info_row=data_info_row.reset_index()
                    #print(data_info_row[meta_id_name])
                    meta_image_id=data_info_row[meta_id_name][0]
                    meta_weeks=data_info_row[meta_weeks_name][0]
                    meta_fvc=data_info_row[meta_fvc_name][0]
                    meta_percent=data_info_row[meta_percent_name][0]
                    meta_age=data_info_row[meta_age_name][0]
                    meta_sex=data_info_row[meta_sex_name][0]
                    meta_status=data_info_row[meta_status_name][0]
                else:
                    meta_image_id=data_dir
                    meta_weeks=''
                    meta_fvc=''
                    meta_percent=''
                    meta_age=''
                    meta_sex=''
                    meta_status=''
                # full_path = convert_windows_to_linux_path(full_path)
                if not os.path.isdir(full_path):
                    continue
                try:
                    print(full_path)
                    dicom_fp,dicom_image = load_dicom_images(full_path)
                
                    spacing_info = dicom_image.GetSpacing()
                    print('SPACING INFO:', spacing_info)
                    
                    metadata_keys = dicom_image.GetMetaDataKeys()
                    
                    dtag=load_dicom_tag(dicom_fp[0])
                    uid=dtag.GetMetaData('0020|000e') ##Series Instance UID
                    modality=dtag.GetMetaData('0008|0060')##Modality
                    study='OSIC_PFP'##Dataset_name
                    CIA_other_info = {
                    'Study_UID':uid,
                    'metadata_file':''
                    # 'Series_Description':serise_desc
                    }
                    CIA_other_info['split'] = pid_dir
                    if mf_flag:
                        CIA_other_info['metadata_file']=meta_file

                    size = list(dicom_image.GetSize())
                    resampler =util.get_unisize_resampler(dicom_image, interpolator='linear', spacing=spacing_info, size=size)

                    # resize the image 
                    if resampler is not None:
                        proces_image = resampler.Execute(dicom_image)
                        print('SPACIE INFO AFTER', proces_image.GetSpacing())
                        CIA_other_info['Resample'] = True
                    else:
                        proces_image = dicom_image
                        CIA_other_info['Resample'] = False
                    
                    ##
                    CIA_other_info['Image_id']=meta_image_id
                    CIA_other_info['Weeks']=str(meta_weeks)
                    CIA_other_info['FVC']=str(meta_fvc)
                    CIA_other_info['Percent']=str(meta_percent)
                    CIA_other_info['Age']=str(meta_age)
                    CIA_other_info['Sex']=meta_sex
                    CIA_other_info['Smoke_Status']=meta_status
                    # threshold the image
                    if 'CT' in modality:
                        proces_image = util.clamp_image(proces_image, CLAMP_RANGE_CT)
                    else:
                        pass

                    output_path = os.path.join(output_dir,data_dir, f"{data_dir}.nii.gz")
                    # output_path=convert_windows_to_linux_path(output_path)
                    save_nifti(proces_image, output_path, full_path)
                    print(f"Saved NIfTI file to {output_path}")

                    ##segment
                    label_path_dict = {}
                    label_flag=True
                    pare_path=os.path.dirname(target_path)
                    label_paths = os.path.join(pare_path, 'GT')
                    label_files=glob.glob("%s/*/*/%s_*.nrrd"%(label_paths,data_dir))
                    #print(label_paths,label_files)
                    if len(label_files)>0:
                        for lf in label_files:
                            lf_name=os.path.basename(lf)
                            lf_id=lf_name.split("_")[0]
                            lf_tissue=os.path.basename(os.path.dirname(lf)).split("_")[1]
                            label_image=load_nrrd(lf)
                            resampler =util.get_unisize_resampler(label_image, interpolator='nearest', spacing=spacing_info, size=size)
                            if resampler is not None:
                                proces_label = resampler.Execute(label_image)
                            else:
                                proces_label = label_image

                            label_output_path = os.path.join(output_dir, lf_id, TASK_VALUE, f"{lf_name}.nii.gz")

                            label_path_dict[lf_tissue] = label_output_path
                            util.save_nifti(proces_label, label_output_path, lf)
                            print(f"Saved Label Segment NIfTI file to {label_output_path}")
                            
                    else:
                        label_flag=False
                except RuntimeError:
                    failed_files.append(full_path)
                    print(f"Failed to load DICOM images from {full_path}")
                    continue
                
                '''

                meta.add_keyvalue('Image_id',meta_image_id)

                meta.add_keyvalue('Weeks',meta_weeks)

                meta.add_keyvalue('FVC',meta_fvc)

                meta.add_keyvalue('Percent',meta_percent)

                meta.add_keyvalue('Age',meta_age)

                meta.add_keyvalue('Sex',meta_sex)

                meta.add_keyvalue('Smoke_Status',meta_status)

                '''
                print(proces_image.GetSize(),proces_label.GetSize())
                try:
                    assert proces_image.GetSize() == proces_label.GetSize()
                except Exception as e:
                    failed_files.append(full_path)
                    continue
                size_processed = list(proces_image.GetSize())


                meta.add_keyvalue('Image_id',meta_image_id)
                meta.add_keyvalue('Spacing_mm',min(spacing_info))
                meta.add_keyvalue('OriImg_path',full_path)
                meta.add_keyvalue('Size',size_processed)  # 这里用处理后的size -- YH Jachin
                meta.add_keyvalue('Modality',modality)
                meta.add_keyvalue('Dataset_name',study)
                meta.add_keyvalue('ROI','whole-body')

                if label_flag:
                    print(label_path_dict.keys())
                    meta.add_keyvalue('Task',TASK_VALUE)
                    meta.add_keyvalue('Label_tissue',list(label_path_dict.keys()))
                    meta.add_keyvalue('Label_path',{TASK_VALUE:label_path_dict})
                
                meta.add_extra_keyvalue('Metadata',CIA_other_info)

                
                

                # Write the mapping to the JSON file on the fly
                with open(json_output_path, 'r+') as json_file:
                    existing_mappings = json.load(json_file)
                    existing_mappings[output_path] = meta.get_meta_data()
                    json_file.seek(0)
                    json.dump(existing_mappings, json_file, indent=4)
                    json_file.truncate()
    else:
        print("No metadata.csv files found.")
    
    with open(failed_files_path, "w") as json_file:
        json.dump(failed_files, json_file)
        
    print(f"The list has been written to {failed_files_path}")
    print(f"Saved NIfTI mappings to {json_output_path}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Process DICOM files and save as NIfTI.")
    parser.add_argument("--target_path", type=str, help="Path to the target directory containing metadata files.", default="/home/data/Github/data/data_gen_def/DATASETS/Kaggle/osic_pulmonary_fibrosis_progression_Segmentation/osic-pulmonary-fibrosis-progression")
    parser.add_argument("--output_dir", type=str, help="Directory to save the NIfTI files.", default="/home/data/Github/data/data_gen_def/DATASETS_processed/Kaggle_osic_new/")
    args = parser.parse_args()
    print(args.target_path, args.output_dir)
    main(args.target_path, args.output_dir)