From aecafd51be651b0c9f115ffab753ab57962b76c4 Mon Sep 17 00:00:00 2001 From: holy2020 Date: Tue, 28 Jun 2022 16:36:47 +0200 Subject: [PATCH] update --- documentation/tutorial/visualization.py | 3 +- .../__pycache__/__init__.cpython-36.pyc | Bin 225 -> 0 bytes .../losses/__pycache__/losses.cpython-36.pyc | Bin 5960 -> 0 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 513 -> 0 bytes .../get_conv_blocks.cpython-36.pyc | Bin 7806 -> 0 bytes .../__pycache__/lfbnet.cpython-36.pyc | Bin 11067 -> 0 bytes src/LFBNet/postprocessing/__init__.py | 2 +- src/LFBNet/postprocessing/postprocessing.py | 68 +++++ .../utilities/compute_surrogate_features.py | 2 +- ...mage_compute_TMTV_Dmax_save_as_csv_file.py | 237 +++++++++--------- src/__pycache__/__init__.cpython-36.pyc | Bin 496 -> 0 bytes src/run/__pycache__/__init__.cpython-36.pyc | Bin 197 -> 0 bytes .../__pycache__/parse_argument.cpython-36.pyc | Bin 2287 -> 0 bytes src/run/__pycache__/trainer.cpython-36.pyc | Bin 19505 -> 0 bytes src/run/trainer.py | 4 +- 15 files changed, 194 insertions(+), 122 deletions(-) delete mode 100644 src/LFBNet/losses/__pycache__/__init__.cpython-36.pyc delete mode 100644 src/LFBNet/losses/__pycache__/losses.cpython-36.pyc delete mode 100644 src/LFBNet/network_architecture/__pycache__/__init__.cpython-36.pyc delete mode 100644 src/LFBNet/network_architecture/__pycache__/get_conv_blocks.cpython-36.pyc delete mode 100644 src/LFBNet/network_architecture/__pycache__/lfbnet.cpython-36.pyc create mode 100644 src/LFBNet/postprocessing/postprocessing.py rename read_3D_nifti_mask_image_compute_TMTV_Dmax_save_as_csv_file.py => src/LFBNet/utilities/read_3D_nifti_mask_image_compute_TMTV_Dmax_save_as_csv_file.py (96%) delete mode 100644 src/__pycache__/__init__.cpython-36.pyc delete mode 100644 src/run/__pycache__/__init__.cpython-36.pyc delete mode 100644 src/run/__pycache__/parse_argument.cpython-36.pyc delete mode 100644 src/run/__pycache__/trainer.cpython-36.pyc diff --git a/documentation/tutorial/visualization.py b/documentation/tutorial/visualization.py index 7010636..0168171 100644 --- a/documentation/tutorial/visualization.py +++ b/documentation/tutorial/visualization.py @@ -38,6 +38,7 @@ from medpy.metric import binary import ntpath +from scipy.ndimage import label def superimpose_segmentation_images(pet_gt_prd_display, file_name, logzscore=None): @@ -176,8 +177,6 @@ def read_predicted_images(path: str = None): pred[pred>0.5] =1 pred[pred<0.5] = 0 - print(pet.shape) - for coronal_sagittal in range(2): if len(gt) and len(pred): pet_gt_prd_display = [pet[coronal_sagittal], gt[coronal_sagittal], pred[coronal_sagittal]] diff --git a/src/LFBNet/losses/__pycache__/__init__.cpython-36.pyc b/src/LFBNet/losses/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 570bdd712af76636847a2b032a14217dcc39ca58..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmXr!<>hJ?oS)>!z`*brh~a<<$Z`PUVg(?P!jQt4!;s4u#mLBz!j!_C!xRFcnSnG* zFoP!ROQ0$SO~zaNiAlxzIi)43@tL^=`9&o!86o0~T7H@=x47ct(@IN9i&Ep`i$E6K zVz|YYlV4n%TD+2>hyy49CVrW?TE(PimSmJB#i!&a$0TN&q~>I%rN$H&CCB)lIYq;;_lhPbtkwwF6mM%mO5M7&#aL6e~H! diff --git a/src/LFBNet/losses/__pycache__/losses.cpython-36.pyc b/src/LFBNet/losses/__pycache__/losses.cpython-36.pyc deleted file mode 100644 index faba8c2a27f1b525c4b86ed2467ce1f9d8e1b90b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5960 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 J+R!Wde*p~kDck@6 diff --git a/src/LFBNet/network_architecture/__pycache__/__init__.cpython-36.pyc b/src/LFBNet/network_architecture/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index d2e85ac42c89e8fb51403dfef788f32b70a76a8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 513 zcmY*W!D<^Z5Y;*fH48c9-oucd95(8%W2eNF(mFW7trJRP=w)G%R}-&@EG?2YZjU7& z(sRC}f3eq|@(Vq6w!O51c{4opW_U9v`}^_3!S~g-h_RpSpVy=MErowhAeoFbTQb?B z94#64G_igpu%$!$N4PFoDAb3THa`XB8?q zwuRJ#xw$|Mh!j%=RybO)QyeO1Wp(uF$we7#&SlMWN(;dn067-{f?>FBJ Ble_=` diff --git a/src/LFBNet/network_architecture/__pycache__/get_conv_blocks.cpython-36.pyc b/src/LFBNet/network_architecture/__pycache__/get_conv_blocks.cpython-36.pyc deleted file mode 100644 index 548c98ad75f23091311ebedd9e439f8864ba9ecc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7806 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 0.5] = 1 + binary_mask[binary_mask < 0.5] = 0 + coronal[coronal >= 0.5] = 1 + coronal[coronal < 0.5] = 0 + + labelled_mask, num_labels = label(binary_mask) + # Let us now remove all the small regions, i.e., less than the specified mm. + print(binary_mask.shape) + print(labelled_mask.shape) + print(coronal.shape) + for get_label in range(num_labels): + refined_mask = np.zeros(binary_mask.shape) + refined_mask[labelled_mask == get_label] = 1 + x, y = np.nonzero(refined_mask) + x1, y1 = np.max(x), np.min(y) + x2, y2 = np.min(x), np.max(y) + refined_mask[:, y1-5:y2+5] = 1 + if (refined_mask * coronal).sum() == 0: + binary_mask[labelled_mask == get_label] = 0 + + predicted[0, ...] = np.expand_dims(binary_mask, axis=-1) + + return predicted + + +# Read .nii files using itk +if __name__ == '__main__': + print("remove wrong segmentation on sagittal views") diff --git a/src/LFBNet/utilities/compute_surrogate_features.py b/src/LFBNet/utilities/compute_surrogate_features.py index 2fd5670..9fe048c 100644 --- a/src/LFBNet/utilities/compute_surrogate_features.py +++ b/src/LFBNet/utilities/compute_surrogate_features.py @@ -119,7 +119,7 @@ def get_features(mask_to_compute_feature_on: ndarray = None): @staticmethod def compute_features_in_physical_space( - sagittal: dict = None, coronal: dic = None, voxel_size=None + sagittal: dict = None, coronal: dict = None, voxel_size=None ): """ Compute features in physical space. Basically multiply the given TMV or dissemination by the voxel space. diff --git a/read_3D_nifti_mask_image_compute_TMTV_Dmax_save_as_csv_file.py b/src/LFBNet/utilities/read_3D_nifti_mask_image_compute_TMTV_Dmax_save_as_csv_file.py similarity index 96% rename from read_3D_nifti_mask_image_compute_TMTV_Dmax_save_as_csv_file.py rename to src/LFBNet/utilities/read_3D_nifti_mask_image_compute_TMTV_Dmax_save_as_csv_file.py index 049ada3..6ce8ce1 100644 --- a/read_3D_nifti_mask_image_compute_TMTV_Dmax_save_as_csv_file.py +++ b/src/LFBNet/utilities/read_3D_nifti_mask_image_compute_TMTV_Dmax_save_as_csv_file.py @@ -1,117 +1,120 @@ -""" Script to compute metabolic tumor volume (TMTV) and dissemination from a given 3D mask images. -1. read .nii files -2. read the pixel spacing -3. compute the total metabolic tumor volume (TMTV) -4. Compute the lesion dissemination (Dmax) -5. calculate TMTV and Dmax in physical spacing, using the pixel spacing -6. save in .CSV as columns of: patient name (anonymous), pixel_spaing, TMTV, Dmax -""" - -# import important libraries -import os -import glob -from tqdm import tqdm -import argparse -from numpy.random import seed -seed(1) - -import nibabel as nib -import numpy as np -import csv - -# library for dmax computation -from skimage.measure import label, regionprops -from skimage import data, util -from scipy.spatial import distance - -import pathlib - - - -# function to write csv file -def write_to_csv_file(array, output_path, file_name="csv"): - """ - :param array: array that consists rows and columns to be saved to csv file - :param output_path: The directory to save csv file - :param file_name: Name of the csv file - :return: saved file_name.csv in the older output_path - """ - array = np.array(array) - file_name = str(output_path) + '/' + str(file_name) + '.csv' - - with open(file_name, 'w', newline='') as output: - output_data = csv.writer(output, delimiter=",") - for row in range(array.shape[0]): - output_data.writerow(array[row][:]) - - print("saved at: ", file_name) - - -# function to read .nii and compute biomarker values -def read_nii_mask_save_csv_tmtv_dmax(input_path, output_path): - """ - :param input_path: Path to the directory that consists the directory for .nii files - :param output_path: The directory to save csv file, after computing the TMTV and Dmax, pixel spacing - :return: read .nii, compute TMTV, Dmax - """ - case_ids = os.listdir(input_path) - print("Total number of cases to read: 0.1%d", len(case_ids)) - name_x_y_z_TMTV_dmax = [["ID", "X", "Y", "Z", 'TMTV', "Dmax"]] - - - for n, case_name in tqdm(enumerate(case_ids), total=len(case_ids)): - path_img_nii = str(input_path) + "/" + str(case_name) + "/" + str(case_name) + ".nii.gz" - try: - # Read .nii files - gt = nib.load(path_img_nii) - res_pet = gt.header.get_zooms() - gt = np.asanyarray(gt.dataobj) - - # Compute TMTV - def compute_TMTV(gt_): - gt_[gt_ > 0] = 1 - gt_[gt_ <= 0] = 0 - return np.sum(gt_ > 0) - - #compute dmax - def compute_dmax(gt_): - # label images - gt_ = util.img_as_ubyte(gt_) > 0 - gt_= label(gt_, connectivity=gt_.ndim) - props = regionprops(gt_) - - dist_all = [] - for k in range(len(props)): - # physical space - a = np.multiply(np.array(props[k].centroid), np.array(res_pet)) - - # compute the distance between all other centroids: - if len(props) >1: - for kk in range(len(props)): - b = np.multiply(np.array(props[kk].centroid), np.array(res_pet)) - dist = distance.euclidean(a, b) - dist_all.append(dist) - else: - dist_all.append(0) - return np.max(dist_all) - - tmtv = compute_TMTV(gt.copy()) - dmax = compute_dmax(gt.copy()) - name_x_y_z_TMTV_dmax.append([str(case_name), res_pet[0], res_pet[1], res_pet[2], - tmtv * res_pet[0] * res_pet[1] * res_pet[2], dmax]) - except: - print(f"Error reading {path_img_nii}") - continue - - write_to_csv_file(name_x_y_z_TMTV_dmax, output_path, file_name="vienna_data_xyz_tmtv_dmax") - print('Total number of patients correctly read and their volume calculated: ', len(name_x_y_z_TMTV_dmax) - 1) - print("Done !!") - - -if __name__ == "__main__": - # We assume the .nii file name and the folder name are the same - parser = argparse.ArgumentParser(description="script to read nii files and compute TMTV and Dmax") - parser.add_argument("--input_dir", dest='input_dir', type=pathlib.Path, help="Input directory path to .nii files") - parser.add_argument("--output_dir", dest='output_dir', type=pathlib.Path, help='output directory path') - args = parser.parse_args() - read_nii_mask_save_csv_tmtv_dmax(args.input_dir, args.output_dir) +""" Script to compute metabolic tumor volume (TMTV) and dissemination from a given 3D mask images. +1. read .nii files +2. read the pixel spacing +3. compute the total metabolic tumor volume (TMTV) +4. Compute the lesion dissemination (Dmax) +5. calculate TMTV and Dmax in physical spacing, using the pixel spacing +6. save in .CSV as columns of: patient name (anonymous), pixel_spaing, TMTV, Dmax +""" + +# import important libraries +import os +import glob +from tqdm import tqdm +import argparse +from numpy.random import seed +seed(1) + +import nibabel as nib +import numpy as np +import csv + +# library for dmax computation +from skimage.measure import label, regionprops +from skimage import data, util +from scipy.spatial import distance + +import pathlib + + + +# function to write csv file +def write_to_csv_file(array, output_path, file_name="csv"): + """ + :param array: array that consists rows and columns to be saved to csv file + :param output_path: The directory to save csv file + :param file_name: Name of the csv file + :return: saved file_name.csv in the older output_path + """ + array = np.array(array) + file_name = str(output_path) + '/' + str(file_name) + '.csv' + + with open(file_name, 'w', newline='') as output: + output_data = csv.writer(output, delimiter=",") + for row in range(array.shape[0]): + output_data.writerow(array[row][:]) + + print("saved at: ", file_name) + + +# function to read .nii and compute biomarker values +def read_nii_mask_save_csv_tmtv_dmax(input_path, output_path): + """ + :param input_path: Path to the directory that consists the directory for .nii files + :param output_path: The directory to save csv file, after computing the TMTV and Dmax, pixel spacing + :return: read .nii, compute TMTV, Dmax + """ + case_ids = os.listdir(input_path) + print("Total number of cases to read: 0.1%d", len(case_ids)) + name_x_y_z_TMTV_dmax = [["ID", "X", "Y", "Z", 'TMTV', "Dmax"]] + + + for n, case_name in tqdm(enumerate(case_ids), total=len(case_ids)): + path_img_nii = str(input_path) + "/" + str(case_name) + "/gt/" + path_img_nii = glob.glob(path_img_nii + "/*.nii.gz")[0] + try: + # Read .nii files + gt = nib.load(path_img_nii) + res_pet = gt.header.get_zooms() + gt = np.asanyarray(gt.dataobj) + gt[gt > 0] = 1 + gt[gt <= 0] = 0 + + # Compute TMTV + def compute_TMTV(gt_): + gt_[gt_ > 0] = 1 + gt_[gt_ <= 0] = 0 + return np.sum(gt_ > 0) + + #compute dmax + def compute_dmax(gt_): + # label images + gt_ = util.img_as_ubyte(gt_) > 0 + gt_= label(gt_, connectivity=gt_.ndim) + props = regionprops(gt_) + + dist_all = [] + for k in range(len(props)): + # physical space + a = np.multiply(np.array(props[k].centroid), np.array(res_pet)) + + # compute the distance between all other centroids: + if len(props) >1: + for kk in range(len(props)): + b = np.multiply(np.array(props[kk].centroid), np.array(res_pet)) + dist = distance.euclidean(a, b) + dist_all.append(dist) + else: + dist_all.append(0) + return np.max(dist_all) + + tmtv = compute_TMTV(gt.copy()) + dmax = compute_dmax(gt.copy()) + name_x_y_z_TMTV_dmax.append([str(case_name), res_pet[0], res_pet[1], res_pet[2], + tmtv * res_pet[0] * res_pet[1] * res_pet[2], dmax]) + except: + print(f"Error reading {path_img_nii}") + continue + + write_to_csv_file(name_x_y_z_TMTV_dmax, output_path, file_name="data_xyz_tmtv_dmax") + print('Total number of patients correctly read and their volume calculated: ', len(name_x_y_z_TMTV_dmax) - 1) + print("Done !!") + + +if __name__ == "__main__": + # We assume the .nii file name and the folder name are the same + parser = argparse.ArgumentParser(description="script to read nii files and compute TMTV and Dmax") + parser.add_argument("--input_dir", dest='input_dir', type=pathlib.Path, help="Input directory path to .nii files") + parser.add_argument("--output_dir", dest='output_dir', type=pathlib.Path, help='output directory path') + args = parser.parse_args() + read_nii_mask_save_csv_tmtv_dmax(args.input_dir, args.output_dir) diff --git a/src/__pycache__/__init__.cpython-36.pyc b/src/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index af3f7d90f342025b205b1db39fbc6ee0ec7be56d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 496 zcmYjN&59F25bjB`?lQOtK0#fxWClbJ5=3-=LF2Nx!NU?5dZsI}w0~y1tI?c&5YIk^ z?~$vgeFaa}m3DXenET~R0;nb8la zW^5*Qyj14kR!tv0NSB%xyC=F$!DhyWTpuFqrvhA-aAQ#?gpL@2KO?Mzwc#824fMLl zIES))U&Cn)R}{0G!J>wb*y?QHZGBtASzyM9IsEy0#nssJjX_`3feRt4Dv9eRYR6LaTKK-pO=BvcmSM9diSH{FEO)t>W8doWH dtEM4O+%$E+-5tGh!DNfC2dXrY^td=0{{#L)l<5Ef diff --git a/src/run/__pycache__/__init__.cpython-36.pyc b/src/run/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index bdd7defc3602b91498b9aec6573f3236871ba87f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197 zcmXr!<>hJ?oS$UQz`*brh~a<<$Z`PUVhJFT!jQt4!;s4u#mLBz!j!_C!xX}h!V=7& z$@&r~%b>}4i$5``I6tSfBsD%Ww;;c$Hq)$ diff --git a/src/run/__pycache__/parse_argument.cpython-36.pyc b/src/run/__pycache__/parse_argument.cpython-36.pyc deleted file mode 100644 index 9e7a2c59227d7a20474d8c8c8cb0800dff190fc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2287 zcmbtV&2Qv16i+hAOeXza+U^1pG6$rM)ESVL(+Htb*xdtCRly>pQKiU@JyUlxN!E5+ zO|(cz<+^_ZXZ|4$zH;JU;KX}QIv-WJuo=yZ{cJzK-~0IG+16HcyZ8IWuYJe)%UO9m z7$3u5y#T?SvBO;EW$xIuZ*T0e4)cF=#vK;0F1&pnu-BJ#4*gvx0(tv z`G|eM_SwPIV~6ZycxhR|`qQb73Es=V z(2AX+(h${kLyBH=Yps;Llts#w;>?-^1rw=;nV8WjfO$GEDw5Kih-qF($dQg$j5Dff zBG`yjxp-5-DgzIsH9ZyAJSVt8alLW`=C4NtB|<8>gab2jF>k_F<*61$PE?g%5(=@D zROArGBXUhM0oGvgh-j`fDBJi+t}B_VQCmL{GM?%}&TZU|ND}~ZU%dwX*>`t%F*J+- zhP-IJt~XY4Vfu@-ysGz`dR;dh`^IPBp7Hg(i@zFwT&RYr#071lGw!WSrax%zYaLo1$ePAl#~eYLhntx(AI-Xr}lu?wB6q z6Ix|j`p_A&3!>f}43@QEqUE?gcrZk9M|SY=(n5bQXcIo(X$R1!Vj{S#KmMty0MwIH zsOQfCNV^#O_bkZ(9qJASSOyR&lz|0l0t>IUXcn*`9W82##W5=wZ@n|U^s*38z!qa1 znh@y2_MzGSrX>={9MWOTvqw{!h$8TVL3PZQ?3)MUizRyu%(`W za5h5McOUt_+jT>KAI7`hp&NSgLs)y5@s45TKI5^D<+6K>*J{HB4+aa~>QnLmc?-h3 zLRjz1SPv1+aD_IO<+|j<_oK1K`VTF6EUs!54Lz<9Z`?+=dW5Gx@#Hy!mV`RS`SOCpZ{v!uH3?-+bDKW z?4sC1u@55lLoh0?~ zOW>lW+Lxa@GjoeGHx00m6VZFz0SX`9@$0UgfN-1(}0&LG@ h1&PIE;e=lSUsM_Y8re*tAs<{0JK?9{9=xID{spqVqpkn| diff --git a/src/run/__pycache__/trainer.cpython-36.pyc b/src/run/__pycache__/trainer.cpython-36.pyc deleted file mode 100644 index edc9ee5d937e7ec609a8ccce3db7b1feac37ddbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19505 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