From 85d0cb478a356bd396354ec3eebba183657d4625 Mon Sep 17 00:00:00 2001 From: xmuyzz Date: Fri, 4 Feb 2022 23:37:13 -0500 Subject: [PATCH] first commit --- __pycache__/yaml.cpython-38.pyc | Bin 0 -> 337 bytes get_data/.DS_Store | Bin 0 -> 6148 bytes get_data/__ini__.py | 0 .../__pycache__/data_gen_flow.cpython-38.pyc | Bin 0 -> 2891 bytes .../data_respacing_reg.cpython-38.pyc | Bin 0 -> 3887 bytes .../__pycache__/exval_dataset.cpython-38.pyc | Bin 0 -> 6014 bytes .../__pycache__/get_data_arr.cpython-38.pyc | Bin 0 -> 6823 bytes .../get_data_arr_df.cpython-38.pyc | Bin 0 -> 6826 bytes .../get_img_dataset.cpython-38.pyc | Bin 0 -> 4765 bytes .../get_pat_dataset.cpython-38.pyc | Bin 0 -> 4482 bytes .../__pycache__/pred_dataset.cpython-38.pyc | Bin 0 -> 2973 bytes .../preprocess_data.cpython-38.pyc | Bin 0 -> 4221 bytes .../respacing_reg_crop.cpython-38.pyc | Bin 0 -> 4128 bytes .../__pycache__/test_dataset.cpython-38.pyc | Bin 0 -> 5076 bytes .../__pycache__/train_dataset.cpython-38.pyc | Bin 0 -> 2833 bytes .../train_val_split.cpython-38.pyc | Bin 0 -> 4306 bytes .../__pycache__/val_dataset.cpython-38.pyc | Bin 0 -> 2846 bytes get_data/data_gen_flow.py | 157 ++++++ get_data/exval_dataset.py | 214 +++++++++ get_data/get_img_dataset.py | 167 +++++++ get_data/get_pat_dataset.py | 174 +++++++ get_data/preprocess_data.py | 187 ++++++++ go_model/.DS_Store | Bin 0 -> 6148 bytes go_model/__init__.py | 0 go_model/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 154 bytes go_model/__pycache__/callbacks.cpython-38.pyc | Bin 0 -> 1309 bytes .../__pycache__/evaluate_model.cpython-38.pyc | Bin 0 -> 3137 bytes .../__pycache__/exval_model.cpython-38.pyc | Bin 0 -> 2382 bytes .../__pycache__/finetune_model.cpython-38.pyc | Bin 0 -> 2426 bytes go_model/__pycache__/get_model.cpython-38.pyc | Bin 0 -> 1830 bytes .../__pycache__/pred_model.cpython-38.pyc | Bin 0 -> 2109 bytes .../__pycache__/test_model.cpython-38.pyc | Bin 0 -> 2223 bytes .../__pycache__/train_model.cpython-38.pyc | Bin 0 -> 1839 bytes go_model/__pycache__/val_model.cpython-38.pyc | Bin 0 -> 2075 bytes .../__pycache__/write_text.cpython-38.pyc | Bin 0 -> 1470 bytes go_model/callbacks.py | 74 +++ go_model/evaluate_model.py | 122 +++++ go_model/finetune_model.py | 106 +++++ go_model/get_model.py | 91 ++++ go_model/pred_model.py | 100 ++++ go_model/train_model.py | 115 +++++ models/.DS_Store | Bin 0 -> 6148 bytes models/EfficientNet.py | 109 +++++ models/GoogleNet.py | 119 +++++ models/Inception.py | 113 +++++ models/ResNet.py | 140 ++++++ models/TLNet.py | 91 ++++ models/VGGNet.py | 107 +++++ models/ViT.py | 289 ++++++++++++ models/__pycache__/EffNet.cpython-38.pyc | Bin 0 -> 2179 bytes .../__pycache__/EffNetB5_model.cpython-38.pyc | Bin 0 -> 1881 bytes .../__pycache__/EffNet_model.cpython-38.pyc | Bin 0 -> 1733 bytes .../__pycache__/EfficientNet.cpython-38.pyc | Bin 0 -> 2199 bytes models/__pycache__/Inception.cpython-38.pyc | Bin 0 -> 2433 bytes models/__pycache__/ResNet.cpython-38.pyc | Bin 0 -> 2401 bytes models/__pycache__/ResNet50.cpython-38.pyc | Bin 0 -> 1313 bytes .../__pycache__/ResNet50_model.cpython-38.pyc | Bin 0 -> 1964 bytes .../__pycache__/ResNet_model.cpython-38.pyc | Bin 0 -> 1929 bytes models/__pycache__/TLNet.cpython-38.pyc | Bin 0 -> 2188 bytes models/__pycache__/TL_ResNet50.cpython-38.pyc | Bin 0 -> 1843 bytes .../TL_ResNet50_model.cpython-38.pyc | Bin 0 -> 1967 bytes models/__pycache__/VGG16.cpython-38.pyc | Bin 0 -> 2182 bytes models/__pycache__/VGGNet.cpython-38.pyc | Bin 0 -> 2180 bytes models/__pycache__/cnn_model.cpython-38.pyc | Bin 0 -> 1742 bytes models/__pycache__/models.cpython-38.pyc | Bin 0 -> 2849 bytes models/__pycache__/simple_cnn.cpython-38.pyc | Bin 0 -> 1757 bytes models/simple_cnn.py | 82 ++++ .../__pycache__/data_prepro.cpython-38.pyc | Bin 0 -> 3329 bytes .../__pycache__/model_pred.cpython-38.pyc | Bin 0 -> 2776 bytes prediction/data_prepro.py | 133 ++++++ prediction/model_pred.py | 93 ++++ run_step1_data.py | 49 ++ run_step2_train.py | 76 +++ run_step3_test.py | 64 +++ run_step4_exval.py | 88 ++++ run_step5_pred.py | 39 ++ utils/.crop_roi.py.swo | Bin 0 -> 16384 bytes utils/.crop_roi.py.swp | Bin 0 -> 20480 bytes utils/HN_patient_meta.py | 446 ++++++++++++++++++ utils/] | 147 ++++++ utils/__init__.py | 0 utils/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 151 bytes utils/__pycache__/acc_loss.cpython-38.pyc | Bin 0 -> 1079 bytes utils/__pycache__/cm_all.cpython-38.pyc | Bin 0 -> 2155 bytes .../__pycache__/cm_patient_pos.cpython-38.pyc | Bin 0 -> 1874 bytes .../cm_patient_prob.cpython-38.pyc | Bin 0 -> 1835 bytes utils/__pycache__/crop_image.cpython-38.pyc | Bin 0 -> 2294 bytes .../get_stats_plots.cpython-38.pyc | Bin 0 -> 3102 bytes utils/__pycache__/make_plots.cpython-38.pyc | Bin 0 -> 3046 bytes utils/__pycache__/mean_CI.cpython-38.pyc | Bin 0 -> 620 bytes utils/__pycache__/nrrd_reg.cpython-38.pyc | Bin 0 -> 1605 bytes utils/__pycache__/plot_cm.cpython-38.pyc | Bin 0 -> 1174 bytes utils/__pycache__/plot_prc.cpython-38.pyc | Bin 0 -> 1616 bytes utils/__pycache__/plot_roc.cpython-38.pyc | Bin 0 -> 1535 bytes .../plot_train_curve.cpython-38.pyc | Bin 0 -> 1163 bytes utils/__pycache__/prc_all.cpython-38.pyc | Bin 0 -> 1113 bytes utils/__pycache__/prc_img.cpython-38.pyc | Bin 0 -> 962 bytes .../prc_patient_mean_prob.cpython-38.pyc | Bin 0 -> 1036 bytes utils/__pycache__/resize_3d.cpython-38.pyc | Bin 0 -> 1232 bytes utils/__pycache__/respacing.cpython-38.pyc | Bin 0 -> 1973 bytes utils/__pycache__/roc_all.cpython-38.pyc | Bin 0 -> 1135 bytes .../__pycache__/roc_bootstrap.cpython-38.pyc | Bin 0 -> 1409 bytes utils/__pycache__/roc_img.cpython-38.pyc | Bin 0 -> 943 bytes .../roc_patient_mean_prob.cpython-38.pyc | Bin 0 -> 1130 bytes .../roc_patient_median_prob.cpython-38.pyc | Bin 0 -> 998 bytes .../roc_patient_pos_rate.cpython-38.pyc | Bin 0 -> 992 bytes .../roc_patient_thr.cpython-38.pyc | Bin 0 -> 2526 bytes utils/__pycache__/tensorboard.cpython-38.pyc | Bin 0 -> 165 bytes utils/__pycache__/write_txt.cpython-38.pyc | Bin 0 -> 3200 bytes utils/acc_loss.py | 33 ++ utils/chest_patient_meta.py | 343 ++++++++++++++ utils/cm_all.py | 111 +++++ utils/contrast_metadata.py | 60 +++ utils/crop_image.py | 125 +++++ utils/delete_row.py | 14 + utils/error_analysis.py | 286 +++++++++++ utils/get_pat_img_df.py | 94 ++++ utils/get_pred_img_df.py | 48 ++ utils/get_stats_plots.py | 147 ++++++ utils/gradcam.py | 301 ++++++++++++ utils/gradcam2.py | 401 ++++++++++++++++ utils/gradcam_new.py | 312 ++++++++++++ utils/import_to_tensorboard.py | 48 ++ utils/mean_CI.py | 42 ++ utils/nrrd_reg.py | 56 +++ utils/plot_cm.py | 42 ++ utils/plot_prc.py | 71 +++ utils/plot_roc.py | 49 ++ utils/plot_train_curve.py | 34 ++ utils/prc_all.py | 46 ++ utils/resize_3d.py | 57 +++ utils/respacing.py | 95 ++++ utils/roc_all.py | 53 +++ utils/roc_bootstrap.py | 55 +++ utils/save_npy_to_h5.py | 10 + utils/save_pred.py | 17 + utils/save_prepro_scan.py | 119 +++++ utils/save_sitk_from_arr.py | 45 ++ utils/scan_meta.py | 56 +++ utils/tensorboard.py | 7 + utils/write_txt.py | 111 +++++ 141 files changed, 6980 insertions(+) create mode 100644 __pycache__/yaml.cpython-38.pyc create mode 100644 get_data/.DS_Store create mode 100644 get_data/__ini__.py create mode 100644 get_data/__pycache__/data_gen_flow.cpython-38.pyc create mode 100644 get_data/__pycache__/data_respacing_reg.cpython-38.pyc create mode 100644 get_data/__pycache__/exval_dataset.cpython-38.pyc create mode 100644 get_data/__pycache__/get_data_arr.cpython-38.pyc create mode 100644 get_data/__pycache__/get_data_arr_df.cpython-38.pyc create mode 100644 get_data/__pycache__/get_img_dataset.cpython-38.pyc create mode 100644 get_data/__pycache__/get_pat_dataset.cpython-38.pyc create mode 100644 get_data/__pycache__/pred_dataset.cpython-38.pyc create mode 100644 get_data/__pycache__/preprocess_data.cpython-38.pyc create mode 100644 get_data/__pycache__/respacing_reg_crop.cpython-38.pyc create mode 100644 get_data/__pycache__/test_dataset.cpython-38.pyc create mode 100644 get_data/__pycache__/train_dataset.cpython-38.pyc create mode 100644 get_data/__pycache__/train_val_split.cpython-38.pyc create mode 100644 get_data/__pycache__/val_dataset.cpython-38.pyc create mode 100644 get_data/data_gen_flow.py create mode 100644 get_data/exval_dataset.py create mode 100644 get_data/get_img_dataset.py create mode 100644 get_data/get_pat_dataset.py create mode 100644 get_data/preprocess_data.py create mode 100644 go_model/.DS_Store create mode 100644 go_model/__init__.py create mode 100644 go_model/__pycache__/__init__.cpython-38.pyc create mode 100644 go_model/__pycache__/callbacks.cpython-38.pyc create mode 100644 go_model/__pycache__/evaluate_model.cpython-38.pyc create mode 100644 go_model/__pycache__/exval_model.cpython-38.pyc create mode 100644 go_model/__pycache__/finetune_model.cpython-38.pyc create mode 100644 go_model/__pycache__/get_model.cpython-38.pyc create mode 100644 go_model/__pycache__/pred_model.cpython-38.pyc create mode 100644 go_model/__pycache__/test_model.cpython-38.pyc create mode 100644 go_model/__pycache__/train_model.cpython-38.pyc create mode 100644 go_model/__pycache__/val_model.cpython-38.pyc create mode 100644 go_model/__pycache__/write_text.cpython-38.pyc create mode 100644 go_model/callbacks.py create mode 100644 go_model/evaluate_model.py create mode 100644 go_model/finetune_model.py create mode 100644 go_model/get_model.py create mode 100644 go_model/pred_model.py create mode 100644 go_model/train_model.py create mode 100644 models/.DS_Store create mode 100644 models/EfficientNet.py create mode 100644 models/GoogleNet.py create mode 100644 models/Inception.py create mode 100644 models/ResNet.py create mode 100644 models/TLNet.py create mode 100644 models/VGGNet.py create mode 100644 models/ViT.py create mode 100644 models/__pycache__/EffNet.cpython-38.pyc create mode 100644 models/__pycache__/EffNetB5_model.cpython-38.pyc create mode 100644 models/__pycache__/EffNet_model.cpython-38.pyc create mode 100644 models/__pycache__/EfficientNet.cpython-38.pyc create mode 100644 models/__pycache__/Inception.cpython-38.pyc create mode 100644 models/__pycache__/ResNet.cpython-38.pyc create mode 100644 models/__pycache__/ResNet50.cpython-38.pyc create mode 100644 models/__pycache__/ResNet50_model.cpython-38.pyc create mode 100644 models/__pycache__/ResNet_model.cpython-38.pyc create mode 100644 models/__pycache__/TLNet.cpython-38.pyc create mode 100644 models/__pycache__/TL_ResNet50.cpython-38.pyc create mode 100644 models/__pycache__/TL_ResNet50_model.cpython-38.pyc create mode 100644 models/__pycache__/VGG16.cpython-38.pyc create mode 100644 models/__pycache__/VGGNet.cpython-38.pyc create mode 100644 models/__pycache__/cnn_model.cpython-38.pyc create mode 100644 models/__pycache__/models.cpython-38.pyc create mode 100644 models/__pycache__/simple_cnn.cpython-38.pyc create mode 100644 models/simple_cnn.py create mode 100644 prediction/__pycache__/data_prepro.cpython-38.pyc create mode 100644 prediction/__pycache__/model_pred.cpython-38.pyc create mode 100644 prediction/data_prepro.py create mode 100644 prediction/model_pred.py create mode 100644 run_step1_data.py create mode 100644 run_step2_train.py create mode 100644 run_step3_test.py create mode 100644 run_step4_exval.py create mode 100644 run_step5_pred.py create mode 100644 utils/.crop_roi.py.swo create mode 100644 utils/.crop_roi.py.swp create mode 100644 utils/HN_patient_meta.py create mode 100644 utils/] create mode 100644 utils/__init__.py create mode 100644 utils/__pycache__/__init__.cpython-38.pyc create mode 100644 utils/__pycache__/acc_loss.cpython-38.pyc create mode 100644 utils/__pycache__/cm_all.cpython-38.pyc create mode 100644 utils/__pycache__/cm_patient_pos.cpython-38.pyc create mode 100644 utils/__pycache__/cm_patient_prob.cpython-38.pyc create mode 100644 utils/__pycache__/crop_image.cpython-38.pyc create mode 100644 utils/__pycache__/get_stats_plots.cpython-38.pyc create mode 100644 utils/__pycache__/make_plots.cpython-38.pyc create mode 100644 utils/__pycache__/mean_CI.cpython-38.pyc create mode 100644 utils/__pycache__/nrrd_reg.cpython-38.pyc create mode 100644 utils/__pycache__/plot_cm.cpython-38.pyc create mode 100644 utils/__pycache__/plot_prc.cpython-38.pyc create mode 100644 utils/__pycache__/plot_roc.cpython-38.pyc create mode 100644 utils/__pycache__/plot_train_curve.cpython-38.pyc create mode 100644 utils/__pycache__/prc_all.cpython-38.pyc create mode 100644 utils/__pycache__/prc_img.cpython-38.pyc create mode 100644 utils/__pycache__/prc_patient_mean_prob.cpython-38.pyc create mode 100644 utils/__pycache__/resize_3d.cpython-38.pyc create mode 100644 utils/__pycache__/respacing.cpython-38.pyc create mode 100644 utils/__pycache__/roc_all.cpython-38.pyc create mode 100644 utils/__pycache__/roc_bootstrap.cpython-38.pyc create mode 100644 utils/__pycache__/roc_img.cpython-38.pyc create mode 100644 utils/__pycache__/roc_patient_mean_prob.cpython-38.pyc create mode 100644 utils/__pycache__/roc_patient_median_prob.cpython-38.pyc create mode 100644 utils/__pycache__/roc_patient_pos_rate.cpython-38.pyc create mode 100644 utils/__pycache__/roc_patient_thr.cpython-38.pyc create mode 100644 utils/__pycache__/tensorboard.cpython-38.pyc create mode 100644 utils/__pycache__/write_txt.cpython-38.pyc create mode 100644 utils/acc_loss.py create mode 100644 utils/chest_patient_meta.py create mode 100644 utils/cm_all.py create mode 100644 utils/contrast_metadata.py create mode 100644 utils/crop_image.py create mode 100644 utils/delete_row.py create mode 100644 utils/error_analysis.py create mode 100644 utils/get_pat_img_df.py create mode 100644 utils/get_pred_img_df.py create mode 100644 utils/get_stats_plots.py create mode 100644 utils/gradcam.py create mode 100644 utils/gradcam2.py create mode 100644 utils/gradcam_new.py create mode 100644 utils/import_to_tensorboard.py create mode 100644 utils/mean_CI.py create mode 100644 utils/nrrd_reg.py create mode 100644 utils/plot_cm.py create mode 100644 utils/plot_prc.py create mode 100644 utils/plot_roc.py create mode 100644 utils/plot_train_curve.py create mode 100644 utils/prc_all.py create mode 100644 utils/resize_3d.py create mode 100644 utils/respacing.py create mode 100644 utils/roc_all.py create mode 100644 utils/roc_bootstrap.py create mode 100644 utils/save_npy_to_h5.py create mode 100644 utils/save_pred.py create mode 100644 utils/save_prepro_scan.py create mode 100644 utils/save_sitk_from_arr.py create mode 100644 utils/scan_meta.py create mode 100644 utils/tensorboard.py create mode 100644 utils/write_txt.py diff --git a/__pycache__/yaml.cpython-38.pyc b/__pycache__/yaml.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b3bc89bc51ef820df2dc21c6132df0ce989d294f GIT binary patch literal 337 zcmYjKu}T9$5Z&3kB$t>pD*nSYIS}kb)I_loR76P=*32Y1-Mc+&6`*0?`!iPZ83+!@=OF3f>k3$fLa(}hz)G60&K1H zNK=3jwx9I#&L|{x-ylsPc5q`AfVxq8>Ox(rTS>T5arZ;g)H#D;m@ps%#R0>BVaRY2 zYlb6+<7I@#n|_^HhR4$wcoS)AD(lBn>mJ9YuPwz*4r?PDA*K^ADi<8Of^u%T%1={E zGOV)VMJ=}E=dXP3CUcijayNf4d7dn3Id}OinWcWbpu#0XqGI53zc8gw*6IDC=4d~2 WSXnv%i7B^5BCX-CzQXTjy9+=0!CNZ; literal 0 HcmV?d00001 diff --git a/get_data/.DS_Store b/get_data/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0^eq<-`~f8X0Ea%pA#xl(SIN59vuE8sIflz~*i4jbCGqAL52afJbfvbBj^@FmBbF8G2R)ebM z&7>C8^msjJKyN3_v=y}Un3F7|i@_rB73xy&8#7pl1|p3u^S~Ozh7PTLTTAxX zJ~YK8X&gAnna0?n*0#mpqxQa&)j)DU+MuOuqU73u@6(lX5UWDPbud#bcX>+m#aleXYpfEULa`e_1Pe7w5vkNvIsxHR@? zV{FnTx=dFdyZdF?psSB?wPgs@Idai7j7l3?k24}!n?hjPgIQ$S{hYTYCvg^M13e~~ zyi(By*ZE+WvP_DPw1__Pp$uuv+YbvOqy6^f%WY)ZGRKi(DBGNgVIswq7ZzP2dr>Ij z5oN&o&tKWa^;#_1rW{q>%Bph^kpvP9Xi z&m-&jPiWi&|1-I7VNC@4m(M!_~V_LDG%mx0RPM0`Zeat7@?iXEo$( zCl)O1!5J{F76eI)UF-CD;PF0H$iwBWBrE7IL>M|z8bXHYFu&Jp~8RdC8<(H-h zc*?%qkCP-!p^%l+dq5J^jB*~2@=TH>>?d)d>JJ!~ac?Gi5HApzS}iz(lkCBlVKZTW znDulBs(7>^BuPr7YJtlb6cC$G6iF;qiORi#9lr(PzklQ^b61&r$`!*dRDr6@b5}Wn zF{(Tf4g39sjn3W2k~vXElj5RBO=aamIan3S*~;TgxokHUQm9h84V9y;tWZvp6RNC& zDj#Z+hL}4Z5s-8QzD@+LN#WKaQZVjdZaDoU-%+K4LvF2Abk=TRCGxj%w0^2oRBe{i zI73`CMp2#( zI)fN;fE9UXld<9k91c{N>u}oo?xV#^GsPHQwxC6 zMUu-T?gDHe2a_kgT_jT3pWnX2Ei`imN#0czxVc2mH8NVVADo-Z>)Uwkuj5UU2#!B~ z!KE{!r>=~3{S%?VR>KF!-JFOi;vv_8^*;u;!-Q?%Qp73{-<&)(Kr`f^cPh940@?yb ADF6Tf literal 0 HcmV?d00001 diff --git a/get_data/__pycache__/data_respacing_reg.cpython-38.pyc b/get_data/__pycache__/data_respacing_reg.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c92a5f09097ba1331f65dd42649fcd9eb0f0fba GIT binary patch literal 3887 zcmbUjO>Y~=b!WI-Ey?AFD2e)3SluK<>d8enm8mX^i-q-5-ff6i#ib#)Pe_F=;2&XxIjf z)SqaYwn_RrDLbWjX*&aB!nc}PJKN0JImOrgd^2z72eJjb05VB`vRSl?ie~sz&1riY z<`c~PLbGRhiKX`GF%iFIX?vDs>^WxH^DJvGu$;Zf^7axd*vo9vUg31_os(6*%!)l~ zUtv@BRW@y3W5mABX6ze^dy_BrNceY7Pu|5@Kp7$0fV0#i$0STE9?Z}2mKE=&;=%kJ zZ$=9%Sr&A2^IEqUrB5<(u}40m z-Kp+$WW`f^*(1=8ES8UQaqfs5QKEq_*@2g2^5N!PALX2=Yhx<48MlqH~F|ygQI!lbAJXdG98*>+Y#X5sM zU?!Wtyv~m4!LKiPIuEt85YO*rvBI!=!0&uK8JFU@c)p>tMYi;kb{9ZuIbIk^#TL`{ z6UPbhS-cQ0uob9*LTvR2ROuqroJHMMeKZoCNy%7=1?3{}dZQFOAXX6#dWf zM0Y8gj`MLUPRE5f!>+KaFOBYUG!rk!CQz2xwU;{4juQs~)1V1m->$(8F?=Y@67W^w z3kv`mTC9C;c1cWrLl5+-_N^vNAb(3GUEh$j9w=0SS38y6R+Cqr?(VxmQ0ef_ZYyY1 z8eRnbsNJeO{Q25uD}YuPMr)hf+iQ5ZJ>UGcOrwj<9gaCy)DbQTS)ox4Tbd1fNwd%u1Qr~>=>7z~>VV~Ja^*N6} z+EL7^VpjXiw@dRVs{;G>7-}Jkz5N&P4U-|0kGAe_Zk}RtIvF+ZFS`S`gXJ}Xmf(&T zF#f!gk6MxIueROD<3SYO7Bip&QWIX#_T3sk{TJj%YGuPV)vdxjAv0bOanW|77i}&x z0e=S9l)8IldSJ)#n9K+s9SXR|P|6J5UvUSN%epkZX2Vf?kga>qIdc#PmbeB>iyOFp zNg82GM4U;z;kTa3q`C)Th=kXchTCrQfJxeBGPMO8@v(5>Zk%YfBd-;Na)Nmv@4k>$ z(|zvLTK+SSMY~d0YwCi#OeWg`f&hcNE_j#lc_58iE2z1V)VQHS6Y54S>*K?&SRX|TEj3##&kpdeY&*o6#mVJE|y8#s`yx`SRw z9q}`?3cnY1M|mJAtJ`V72J5IT5;t)PJp6rtg(!>4opWavk4^Xu04=#hbz+kDh(!w| zO)ZimX_8Cmq(qj0Zc$zFZy}$i$$38D)Ew}0=k(94BDIhx!IvgQu(v{Tbb*?QA~66z z`}71QWA7^?J;eSgKEW7F$7$m4m(%&6(&Wr@&pOzHZRg2H)%PnfZho@8^W=VYr?LUH z>o&L}+-E9>|A$Vs^Zwhd1H0Mz!P}{*ZR}KDuQV!TsJb^)7?e6HtiOJR;lV-qjn~VM z9Uj!c!?W?DjdgXD4c`m6E6NE`gq>616t64s0f4e4?%@2V2<{^I8G?HV?j!gR0saDC zcCtFDCx^l64*Y1}K2>I>Ku}JE9GV0R%WI;HxDoCiMxHNeE0jjt4VW8>HMEEhNGiCT z4flO;F<5W5826o!`*6oWlM;B-fR0xKzDx#(O}N`=(3U#>&c5i`~ku;F#MRp!n z3)hDSO!Xu?*QcuX3^`IgYFNtwbVkP+%YuqVrDBwYd$eSQ~j y*6=9+6VC40YpGcQdI9tYVwvWPH1vS;Z|;mj7od;83w>gErpM1Z&i`TLP4Zt&^z#$| literal 0 HcmV?d00001 diff --git a/get_data/__pycache__/exval_dataset.cpython-38.pyc b/get_data/__pycache__/exval_dataset.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0f7cc14666bdb5e0911bb8994277374be444393 GIT binary patch literal 6014 zcmbVQ&2QYs6(_mh->a3ZFUvCHIJM=-l4Z9*tGI3ht3lnQP8z!xChnHr;jSog$@P$H zTWf9+KK`ART+{U(jpsMGw3dD4=_DsQZ!r-s67It_=hwmosnP%)FU-^WK}^ z>`yC|qJrly(Z9{os-par3dtvr!uRp{WmQpxB2-5);#@UUE^CIydCEw0o-wkV=ZqZZ zc_Ytx!609-%9-iT7&D_b4WofJX=k=OXUs7#`7FR?Ho0Tz^Wa?TnZg3#fThsCSgL#S_xg?KLgRcDjMxwW7Qq z$Tm@HYLpjdV2$W{AWhqC29_T*{hnh7sILZIvxO!dPueZh0jD4>-yT@aYXXI0OM1Pg z-8DNTjH-HO%XT{`G+Ze~Q(B#-v^zFYZE#C=x6{O!rf&r&f5gO2euX5;I<{+>@+5_V zk#l>^mSgu$u$4-Sc6R)n0a3tr6QBPF4Lhw4^MSIb9%&)==U5vSCZ!Zg#ZgHWX_QJ_ zN`-}E)!zw}qcl;fGU67&HJA%(!YzT@V=kx>R||_G19{~jbCey8P!ic=?N}XF!pdGw z=|T0BGA;SpBD#XZKdwY?umj;heZA&Wg3C z+2L|K_pa>pyOtaH z+gzj(AbzhYY^gu#nZfQ*zkFFILJvH>3t#7Ys}(@1BP{vOq@ULhUegUz>^p&u2EBgZ z$33{)`_{oDPYT`q-&wfUBLox<4EmFx-*YUU$z-)U1(bDNSZ%ZK=$~w@>-b%}H=T~O zg1$s#I_O#Y6KI9IbJU3y2c>k)KsVcgC9$TlR-v%5)L>h`KmqB(R4A#uX+3T^eUXql zW-M|711JY&UM?n! zxsLaBD!_KJhTh9dWv1t_HR!AcvV(~NldFs)Db~rm@-b^}Y7oBp(9%f}`c!@K;Dbw> zT{qY?EgATZd4F@~-MjB@e$aP2n|D9_=;ntzKlpf)BRVNzFiYv??K?L=xXIC+;(U~~ z9zQglttbzxWA32BEv{VI+Muq38su6|lbIVW|DjQ91;X>KX3KM7>V6QF0~{wU54PB} zMU*n#gS%^~DC_s{!_1@9uJ2AVbqx@cxi(una)Hbd}THEC!C(m~_cfGE)d4G4`bluH?HQ4o_g^nFy zl|65B$Fh1iM-$%cSa1~51+RU4vTgJZqT+Q24&3s(y&F3;i!_p~qA7#7;-)aLN%QND zhr`vsu`%s4$ZlRDr9D?SPrkwvsm&8wm84~!WRMI}w|2G%nYQg%+k-5tc6+cmR;BK@ zOjn1CTaR_@%C;me3|3svz5Kc+(6n}ICrXpVD8mAu*AtU%lbQ$hzza-ACzQZrY~PmW zNOX1VIW(}py{5_qV$ZzP+}}PC#IFk*7{ff+=$en4(tG6Ff5*z?o2ZM@wD7g8k&g|| zmVgaYw}?uKcoG#yj!O1YO2i4E9GE%O=v613Pf(# zgb8~xutb#ZINtpz!|)1OAnjh18X(DnCX{KbC@;oK8G>uHqqF$P48$}2hsY+g^x*S;qMl;w{C@fC{f;^2D(M-Y#QoFl|K zTo^6}IpDr3TF^+fAP;PZ`%jn>GfzvZ^5Bc0AR2_o+#g|Pvy5g}!#qYUV8kNO{|o&I zD<}J-_k2(cizD1x5DO$r)acG+eu8k#2Pgu(?gwUT-x?p8zGDNj>22wC;~?u;4=sn_ znCS|=0=O?MA`Q*PoETFh0?V(!Dn0L#z()0xRM}ghHZEM+xx-TT>K)<<;0-s8&g{__xtD?-8nUN}jngU9_x z2TTIsUYqJ+a_GF=iz-aV9!&8v!Ta&oB0w2Eu_4;*BCpWE^sX@p z4}=W-_1N>*^;Q!X|`W;#*+* zAj{dODJtPuZ2gG*CXhV=`t+l0Xc)}S^zNnvn++`WS&<0>;6qZSN++FOk1UgTY6 zWS-%6w`GkcQIa1b1F9K?swl(JL;eI@`7u=$r&d0pVV+TjEh;J{JXwA~y?%^jpmU_- z=*V!A6pVA#$F<19a>N_&5vrs+1-THk!8jD@&b*;5QnUX*-(}I zIk5?!#7Pps@Xp7I{skKN4iaUh3J8*?Hj_Wz*+vfr-(DInvx7uQAf36%}#t^c*j6 z?EBHol*Vm~xac3}sh&dxyR{ErNz2I)){t9YhO0nwZkv=;F%hG=0m7 zj}NwoXg;u9!1lJ|J=)l}a6jFkx%!gOl~H4Q1805wXQ<2(w;pr(UjTM{2Gfbg@xLGp zwvB4sn6Djxo(#T?#qo`DgNVdt#g<7(FUs(d$BImBGH*BUbpATl(|4>JbXWArgeMyq fZ!$1y9ypWIa`H`M^A&6quBrL|6x&6nw6FdPOWntE literal 0 HcmV?d00001 diff --git a/get_data/__pycache__/get_data_arr.cpython-38.pyc b/get_data/__pycache__/get_data_arr.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b31ab98f397e2e50e2dd624e92f7bb58987f7de GIT binary patch literal 6823 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 F`Y-wYaYFzA literal 0 HcmV?d00001 diff --git a/get_data/__pycache__/get_data_arr_df.cpython-38.pyc b/get_data/__pycache__/get_data_arr_df.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7006db758b3f0f13e5c4816adf00ac105b5de40a GIT binary patch literal 6826 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 JIUCOAe*wvMa&G_t literal 0 HcmV?d00001 diff --git a/get_data/__pycache__/get_img_dataset.cpython-38.pyc b/get_data/__pycache__/get_img_dataset.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e34370eeb51ba4de56179abaf844829cbb81fb55 GIT binary patch literal 4765 zcmd5=OK%)m6|Psms^4wLkGv?2W)duU95QB@;6y+alfjG#(oB$0&4i}Xx4K<+byek7 zIf*;BSDBGmfRsHWWN(o84=fO?`3Io8LSp4TY(^mSom*{pCnONTX1Y}8KF+=8o^$Sb zRsW=3w>A9!_~(Cm-(S+SztUj#XJYU@yy*YXVjhrcY#$| ziIqRq-9=Vm_NSV=#PpA}mD&+_)b6b4vXRBzo|pN9mkI?C$K#|M z484JWL;}iElyrMxko2lVVe-3(wv6`{-t-Ft;_Kt;KpWTa)-&xuXF4;OF)$DHaU(N? zA&KE?B)fP2Y2d?pJYe8Go#*3m3T8l*Gnf{>h;F72?bJuvL z^Up=&pd}jlDXN9^s%G{S5Tl zir$x4aYi&)`&Bgi7G!H<)q-fVj_SXPKJF=}FLX8s>IT1-Egmefc~K8^+}x5_I#@pC zrgUg1Et%|$Sjy!gj-y|~UY7Bmg^gIEMmBQqA}r+G_`Enb(8c+AZG1s2ADk5zlzxl{ zKcrJcT`aSO&p~s%#1^x~opU?qyBC1D$d=eLNe#P#;j_ihjAf1N>vgzc798cQ_pzmE zwu9fm&zv;#db>VzltVaa81($q>GOE#q&+WkLjR#3I=AjRUc{VU%;8B%9I+r8IH7mn z4^xHM+Dxz8isVf`NH;MgJQ*cjNUohREG~MN1 zG=O%ZWX?rEm=b&)T|@9$bx$|{kBaU`UGUw# z+S{JqFljXMhQ4C+s>7UUZmN`1t4x;CT^X#gGNBI}9S#c#oyt9hH=eyp_7ygoAf!r9rkUDg0XE(`}ZzF`}_+l=xw5Z6CCXNAsq*d4&)hQ8PS@e>`kC-ed$z(w}zAPc!lB77) zd+19eP9Ya!n6!3cP^jJKvB!E|nsu{SRuEewMCTL>6DBL05c@V6wJcIals=S2a(*0A ziW4%PRF)>P%PI-qi^IJkX6@9^y7`ioi?TKw8)W$;%*cX@veHD1l*QaPq&*D?r+s*` zEGXxLrYODSK~L77^9@-!HF#O*#d}fa*7MZ}ANPF@UMZq>`;jzZ0MeqU;VJ7&7`OBea4nPC1$|y`nYR9hUIWh7FQKjJ z3q{*#0MjvuP6zW1qXk&Q-g%vG;fx;n+sTgvZs1jA?k~_m;iw-N0!1f^qhe-aWQpRT zp1zrxC}7b;MQ$*YSp#!iVg*)YCG^TviZUC$N>-#|7{yLWlttxf5e3iMC&jTnu8wQt z`nZAOwIwKOso7FOXBt@*gSYQGkd})}GMKhZE8h+zZraLc{-8 z4}F!h@ku=*%O^o~)KV6VqMO8M+XB`%?B_ zLq-@i=%|@-Q=K%OSgG=>hzk520_z06N#I=q-y-lHfolZ5O<;q-b$~l7HnLF4-=c|2 z1l|Oo41_42%EE9L^(>z!1O=t(#+8;*&JLCQ>R7jY;va4?w`jLAF`+oiPbC)JYClR( zbTl16bhk(t*8ea3Q+QV~p8o(n*`(+_6QmzwG9{62wqQoe%K{JQ|s;J&o+QIdpl76$ia zd)h}Hp;=j+v}O|0An5IeKGHLl-Mm9I7LfJRnD@i@(b}$$+_Xj~rkrXj*;M_t)4xc# z`ctHp&rbg<;rM9OWaXn^n1ue;-4FNzjjio>afrG>5-``_3z{t77{+Wb^uI&LrDO(_ S+on%V^(lL)9x3P8?)?iHM{OJc literal 0 HcmV?d00001 diff --git a/get_data/__pycache__/get_pat_dataset.cpython-38.pyc b/get_data/__pycache__/get_pat_dataset.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..abf210c71559c2d05027e31e7f9415925b73e523 GIT binary patch literal 4482 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 ID`)-i-$UHGHUIzs literal 0 HcmV?d00001 diff --git a/get_data/__pycache__/pred_dataset.cpython-38.pyc b/get_data/__pycache__/pred_dataset.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..edb8dee3482241a5a23f468ab0f34f90ec6b8dad GIT binary patch literal 2973 zcmb7GOK%*<5uWLJ?Ck85G{uKR$+S(|TFTrdDJMY$K?p!Oh=70y#8`($jLC4jcRA#| z+|z5>i`gImb&LS#luHh~x%poN$iL`oPCVx708XlUS0p0@0WyuLs;;h{>8Yx(YCdl? zd;_jOef3%Rw`IflJ5Fw16`1@C?&6hc7|dX1Y|uG3sj26L63s1YYwl22^9rqK?om(k zDy?enCpB6(=g(-+2IxrKOj@*+v}s%Gt#~O}qDu>#Wx5PDcKlAVLRYlTiEkyVbXCi4 zyq2ufbuCxojpR1Htz|F1lWfvWEmz~a$vt{c%YOWBvPHK*uCe+ngTBWatO@*m{sC*9 zlW$G&d)B7goSc0){fK|emS%+BXUnw1-hr_)wCE1I1!EPY2mBMZ#?~(g-PQJ8zCAO) zHH!mIrahEX!c5cP%hSD?LHGH4e1|{a_xUd0W*e^^Sb3js^IdlPHMz1n;1Agy?oL1D z2Yi3sV4HWqQ!~QuURZEu^VsOzyTte$b%=6@iHs7iyh4frN*z;qER=j9x1|W9G?2WI zL6OIig!QJ(f73eK!(P|E8VBY7r&cSLH3!+$SXq{(wv%Q zm04@X%)Bt48W-lIR@T67XHqZgQ^M?-$(#)XBp0L#NY>1lG)jMJmyH=X_{%-%Ox@Bi zYs~vInVFNOtgvd?JoC`{0-RfVVB6HT9<2GYT6z~Iu1LSEln%@+c;k!TSd-SIJ!wL6 zT%WD=*4dh@4ri#r8T_~3TQ;Y4**I%XTcusLXK)rc-CvjiVQU=q0YO(60m*{<81AAC zWN4Je7v97y%|DVa?PKFRL)1b29Z{sar^q1HQ%$}<>YZi@@12~UhiTdy^YLkx4tv8$ zLQwLo_w2>)VU|Mh3%PrEbhP^`k)83r>5)$wib(Fggu$uB@1+${K@t1z>~@P%h%B_)3wi zP&PQ~RM}@)ly;UDP?~S7rTis8P(w*I2GQ5xe+`^Ss9F(6eIAG~9dgwSPm@J#0tSoz zAT0vz|ADF^%urh>4`HY!iwz1Wu!|LCJ$qUx3pR-sx&l935jLQe#RgR!q>Jahcafwi*l{63f zF+|r!S9Ds`L7PJKU=b|C6CM{7A`*l`1pEEdEBPVF4(QoHbnroS!R1Im<^3k-`~NXJ zpf$)RE^@s_8&R5%WY9ka9P^mgvXR6&)?->tGm*^A>bb}?UNPp>6QlW~J5{j-uM>Ch ze2@|$!&DR>pl%z&lOxF}0|8VumEapUOt@Rx^sd;49CW5>GuEj(B}0dk4% zx%?}VKOn(MC4P(&e)#3rKst{22(=$0xsOD<44u~@*FGFqpO4b5UqkbKbg9p6CsA5- zek9hw5Gu~;NJg;^iE{HWWnnQ7mjhK0DlbWM@hMso!IhtcGLJJEM<;6OT80XZTG&75 zRvzOd>YvA4+1Tp^b`ya)EU;cS1E-v*z=G-| z=g1n255iF_1MNO+0-=z~E@YN#z(79`*(hZ0d;Jz^&@2B29pV(#-s;Q5IY!dG#ApYrk8XP~kj%WTGAnOw< literal 0 HcmV?d00001 diff --git a/get_data/__pycache__/preprocess_data.cpython-38.pyc b/get_data/__pycache__/preprocess_data.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af0dc6a135814f930cbb7219e65b0eb00e1aabdc GIT binary patch literal 4221 zcmbVP&2QYs73Yv#F8BLG-yfD~JB_`HB{>CJ1d3rakZQze92k{zA?hyZo!J#7E=di! zwY27*l;$Qt1#%1u=~5uKqK6)X9DC@or@8i|f5ATC;UoXY7ou4cmZP z^|D>lHpy^I&d$lcyj_4=^NQV)UFw$Yvh3HrO1ENHMzU4A3Njh5)~(xh$uqpE?zBBE z+gWd>J8REE+oXi%;LUe5T4?LENK2m+dybMFrBS{BtCde1L{ww$xPCKcQQV9|&y5?Z zFgo4X?J{9TF>hlFb1jQwC6lF_i_XIHpXXN|Mr&{5grfh?O%PTV!a3mK1>dmE=Ne%q;@< zFOmzbF}DQVzez5%MqD*1(j3fErg>WUT!}NWab_eYEzT+D8F`!E9VhX7=jGXlISi$5To5m>jD_ z0b42~D~*Yj&+ZRuSL|83^%Kw2;J^1Dd z#>EuI#kazEeS(o%sd&pxP`(y#AHaItxMC~D8>|AY(VK6Mw^Qw4?~0{M$Nare7<(935Ro&S{Vh`omK3i5MTd-vu+<9jDKPi?FZV|xL#zz!JdI#ywi{R!f~HmHM~ zjQ*rfSLt0uN=vZ1I{zXm4p#h!DgKh=|1rq}e?7%tk^H|V1>n!7_%|f~?@4K}5-%pT zB$KGny9V>DpBRJd@ltXOlEA-w;W^m(FKlm+Og}nW~v#Tmf z-h$@|XCV(-EQ%~h&5q^zEw4u*CvRHg^n4eZ9T&2=6T5+b*TSS6g2;z?sp2Ev>2;YO zN1KvFt*EL=U2eS$op^U(t*u$uu;ReldMbNA9Lom1_&XCGs+BDc>blXDjLIGkHYA+ufrLMbfIo1r_}0q;7o z<+NkQt#Irs0Dsq_tnGk(o7QbSyKYaEO7!dy9huU4$pA;V;H{y4+GFUHq3ffl$5LuP z4g8d)@+6FQ9W>Mr*B#ECBWbPQ>xM_*3ZNx8Fcxf`tP!pUKCTZK!mCgvGw|ffTG9;w zIlc4~l*2<7aLP6Kwe;L%$s^}?%tA+46ZZ{WVps1CkMJgUUNpBp{_OFOI6HPFE%W5@ z$D)>IJl_6jYpY*JH*KJTrgSkx<%G90;nk!ZD4DRPBulE9Fbg1Z?`QYc(drgnE-oL~ z9131BcHoPuP;`_;d1eksLjIs0=sU{S3axv$F57s%yYKk^ zMxXU}1HZG;abvjCgu%u(W8qfd!)A)&jShp@M^KZ{lJt6bB=Yw?*mJF*8$Nh|cIZ$T zimLQ)T@}9X1ue&m9;{DB^o@;Gge5e<_=;>?`~c4@vNpUvA-)T|dcrMDeY7p~w(GGA z3JkuC1^S|&gOM^YHw9_-jjg9^JC7a<9o^s0V>#^X=Y~B`e)&{(?#RxaVdwwm)rk{1 zIz2^BAvq1j0KTy&L`E*?1$u@rK_PSxy!y1E2_xz~k60}9eQ>wPx_;<6E!Ho{0Yls1 zdRcTke!yAN^(i~-SK=UcJWN!$Kch{!*YbBk>&5rrJE5aH8-{ISjR{K>Qaef%Qi(_8 zM>?7=6@^rafvYg=5**ynh3R%XO;l!=+U_Bvutz%B#qT1VbtoDck(0CG@<{gRLM+n& zUmMF^6KW6%9aF5(_X5`!20Mh{iA1KmkER+?z+*;*-tmIxA|r3;P{rI0h2exD^Qlln zDstNpj1Rd3xAtrhVz!S&mbxJC9EoDrIc&B9?}ba_UEYA%h0d8nMJD9n3ebnyNoXGP zMTO$UokIR~TX5r#gwYCoXbIhoz&sthMMnBvXni;2cy;GlEKDdGH|3v#JVU^e;R(Ut z!zqinUz($2%HPKkt5DdbNnQZNk{qQ3B#Yy0fbJ-#5&_ahITn9DE~y>)D}odzi7`5&kOplxL4n*>fEr8}#xD4baXS;W9KQ+fZOfb} zbfhoI9l+mi%GrcU+jbtPOUxJ=d;>9OF=W67TgDQm{40A#RIcO-+(^+spipMYAMJpTYH2>IP!?0WpCk_a82 zIuXAQ#4vJI&cs}_@4;`QzupZf^O_O!SPNsGV}2Ay=(eC=WaI%WR6i6t{<(5I8Mw?j zm_mhtOgFYGI9;?pzG=viw#!4V+{Um{GQ>r7$dpIAJS}C=OY>6;h$jrk1rz3du(0Q` e2bh?ncc3uAU6*fBwyLlICVcS15}Atn!@mI@Wr58A literal 0 HcmV?d00001 diff --git a/get_data/__pycache__/respacing_reg_crop.cpython-38.pyc b/get_data/__pycache__/respacing_reg_crop.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3bd723ad453abf20a53adb648fbdc97d8baffcbd GIT binary patch literal 4128 zcmbVP-EZ5-5$CQbijwtZ*^&Hh=klrKZ29icq796oS37A^AZ`#jEkN|ZGTN0)o1&B@ z-6R5q7V!102He*}QCRm-^j`Z19QtPz`&6J$e(6(z0_o?>N|t0dXp)NHaCbhIGqbb5 zp`KhLYaoi#-y&=JceDBcC!tw1H%)M~+EQR5BQ! zlPxRR$4Um{bFvjB+g36d4`lh1TRqLX%|GPxe3sAg1-{G|`4V5@SNK(ajo)CUqcps` zueZwA*bJXIsqj^Pdq!ik^BSA$Q8s@VgG`XBR$e&8-PJM-CUI2(mwlUH_!Aae?nQj zN0>3I!6*SEb5>(Xmih$+POPVuwC-e-JJF-b9&E((t6}`ah+qXa@oU;6-2zL8@x8>J zX(yus%YbcmR_jiMsgsFlsz-iIyVKoEVLF=L%N&Azm}a>!8|4nkAtf64k{x(SMqZ+4 z7yQbDU&+2d-C|T^llxSB9c95QkbqZT<28)zTM${{4J$xogBS;EU5M>p7h)@o#a4Q! z*rwhX8y$;nu~>O-zmo8 zC`MKqtFzcB%5!ywyD@XYSFAJG0|1cOH`m!Qeg4Y}p3X&cdpS1OAZ(s3u*D-dc2G@A zz|COIVLd@4`KS<;qSe0LJ`Ce><_0H+^dq)dn1 zjfvkydUw&kAC340#s5PT2maa^|B~YWF*1N(9OKU@{$HX*cQJ%Yh>}q%nv4+IuN)cO zrLY(+MW)ifdZc%k!&0;i@(jBcEwbxJ@$SlT?D@}`2C#Kwy9WJb_@gk2!Ci$r$OCC; zk@lI{B@y`*eXduvuQY*R^A(kJZC%p3ucnH;-l^=in!NICci-{-N{4rLTYjU`a6>?_ zcB}I6hpU?{AHXpPS2wq}S09Vk9psK!tq>cWdoeGPU8kL0D@w+jr;D zD?m8S(5TkA{s#RGuT^ur;O^SUsAE)aA)w0G<3|r(lgjD8;rE)Xo?_gm81QKv2!J$w zxFz+v>+w?t3{gUYN}VKFuAqx|B(Xcj=FV#M;UlSUK6v`5lS0}Tc9MO`<9jU{4Enx7cS*Yqza_Zs`i#Hqqa_q^@jOrfN{k z6>`G$LoV8O_^Qq2gwJ0<$5U-grUu5g%jAUM;emjbh+1ag{FK{ZT-K%OHXF9uoJ`$) z$r-e_28zUWjN_(?1I6XIOsaR{?kM4)9R|Bjn~NJ~+T|GtwTa9Ox3ZYw_ocbJT~ z1w;(%;d^Dw?91!`I(-cqdLWHj%ZHKF-N5ywj$#>Cd6lt_+ZG0Tn?Pa$DPNXp zC@*yaFm##0f;vB~j;2|)ZKL@F+Ty1Inx>WM_^djVW%`B#p}2+bzl!7<63n1XV~N}R zWz9QaT&yB1R)@629&QC9jcTg03|6<{t?GP0cwi%qT}TcWRy?RVz72V-+vtkaaeg+f z!WU27R&Gej>Q)M7XD`F87G)q-0!skAv{Y>%2wW(BS@`dBkA4Ne2cpFns7_3BlcZ^$ zq-dICNs44+Iw_I`;HRmsv*+y3Rz+@s>q|X925F?@f5t4rfoOzqtWhsTxoLVI%g>QVB3Cjuli91o|`|| z-g)wAb*Hl4{|dB)^FpQh|FEfcK6=0Rz=n4|c|Q}i)1AuOjYb6z`~HpwgCl_kYj1zU z@PMKI`rGx#4jEeDAzS~(`kF#2!*hM^h;mF!!OkgiiY-&YL^&8TSvG&|j(FmOB0o5h@DRL9ii8 z1DS4Uqt4{i~5P$$tRUxH_Kz literal 0 HcmV?d00001 diff --git a/get_data/__pycache__/test_dataset.cpython-38.pyc b/get_data/__pycache__/test_dataset.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..891068f67cd20b745ad8ff814baa4cff73966f42 GIT binary patch literal 5076 zcmbUkOKcoTwSV*V{A`cMe|v1ljwg;ialA=(t!T9?hgBjGvr4jvZ8qv{PnE}Q_jGrv z+nZ!m1BdKhkRpW}5=hK&;|dpq5aP&z6UVwB0YdT>5JJ&P%X`)1-?B?~nO0T3SMSxU zSMOIf@8$DZ1%5yMrl5sH( znU0Xuf%ZaWes#=L2~9K$(vK8U`~^wIYUb3EEzPbWXPN&!{$6G zn+s&byg(}EA{jN8$e4MNjGN13!mN==_@(&wWG@`0XpEfFKS0oMKbF?u@`=+D;e2z9FX?v0e@HyJ7qAOs=PlDxG0I*v<3d9=P<83py4!NZ>_M{D}@4 zmSOi2FYgzLKCa;Iyv)mF5LTCXz6ZK@Mu208kHob7!Ozq8hsZD~KZljTH*#Lm{xBJA zs-yxOqtDUsW0A67-XF0lpvNc~Clft9uimM^xkjOlMPuD@uoMJt6SW%$U8csBTgnUd z;g32Kyh5}aN~aQ~c;y+K2P`o06)Z5xCwcxL2`Tmq&~chi;#pp(k3QfA8J4N{$7q62 z@C3&spLuR{rubB6+A9NnR?`0#O?IcEY0zjon&Hzt&9fjAOF{K{vNOYHK{2&_J zuf`l?`8m*dwL8~Spr7w4n$nr;%=0RrA5-`o_LEAd3Njb?IIp6_o(5JNg9w<7eT;Ds zT{y-tt{!f77C^Iw|I+>fpCq+WgSJH|ZqJgG5+vVfk zMLr(yBS(lrIm)Q)8J$J2|7E_oUzE`lTVH2yk=4$SSLjSvP<`)Pa; zW5weLL zz85h&jE>;$Qy6wNP3Z5_P-xqJyVB^Bt6{_Ts*YEUXc%oakni}X0iZ*>4=pS~*HpOj zFx^qP`a|uJabNkl!mu)eTy5=&&~`nE%I@WMea~;v`p({g?RoV!ZSVPBv)*(fi_yTZ z-}(0Hv4X2x+uN(}GJl^oqI#1?7O^9{j_OD#q|sXNRAg_uP8c=(R`BL5>H~#XNkU$< zrQ#=?H(eiUd-&$sDSJCvzvMAS%*5SWZ*OhARO?5d!SAKIdW0uDLP<4UXm@T2W7l!1 zNVxV6b=#%Lk8D?pmqyxT!@$`t&?gP|kWe`nZa#pkBjtF3Yd2_n@N-MTO_A7jSQvr) zWO%p(XA#B$r2(tp&IIL^h!euG^5DN~_!iYJ6NZLU_Jt8uo}Rhc(8UoF(?6#zwTGFO8 zLrts8YEB#trE7n*+Reia9NWV;yf znCb1cuOJ^UoMZk%>^-#G8WisKDSlt#`Irvpm1r?W+FcZQyAWBI4hF(bIJ7gm24IY`v3UOP_fivd7n~GJ}rG(+>eX=>G+RiDXsno&-t^Z literal 0 HcmV?d00001 diff --git a/get_data/__pycache__/train_dataset.cpython-38.pyc b/get_data/__pycache__/train_dataset.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02085b7af63beef0e6b1db460b557708f72fd179 GIT binary patch literal 2833 zcmZ`5O>Y~=b!PU19MiE6$2sX}Pe+HFJ=?CW=Ym_% zb8%d99lSVOcFP)9ajP(ss5Y*`XXTXxw3&;L27-%QeIe2csD8aZ7Bm1v`5JywY z$%!#F1voe(Gn_kb;E2YRsgYNPD6hh(acgQ*gPPPF8o*x{&I9ybBhi=^sXfEAuz_Gzgq5*@Xz7%&Khe@#R0pUQP|LLPH39z846UKO8NYZ3UlpYM zhgRRBDjgsNv?+PzD{>yb#d)Exc1m7g_G4Omi~deY0@J!a^BH$ULl9v=A{)=myd_%s zO56r|Q`1MhJgtZZ#At*K{SObmDY1RZ;LiWaph}OLg)e>Lwxd8+7_z<;3>X` zsG--Zr{+B*O0=UT{{wPR1CBMiJ|rN;8j_ijcj9}%u>l-}Rk(FhUvSXP=RgykX=8=} zHfM;Sd@WxW9kITS#F~OzDDQyGmZ*!4k~kxf*Xnddte$|C8)ydE5X@3hmP5XvBNh-SyZ19h}_O^ zNPaEviEBeFdTo?n7u(Yyx}qkw>G%Gq`f-=uwn?*++d_7Q^w-Q^#aEjCsJ)1y^~=rS3B6q&^RWn0p~*O;GC= zGyk`Nk}&VVHbmdPdKLZ!tO5hzy3=C9pMufzzbPXNfE+|&+9%TTy>S@p$$vU2WKlQ> zZf36W!#i}+(6R7*#(LW9yOY)>##0yRUYwp!s+Tymox|OU`X;ksVig6t3aG$MnwOkj z96Zs7ub}P>MhXQskz+6@@{fXOPSv>v4YM@z&-cduQ;#K2vhaT(Z{jS((PPNg!qgu1 zx!+R}y4&BCB!v*Nz^7gs4o)EhB*~-+ksC?#Bmt*MoJuPgr~F)6DT63U3-p9);w(&= z3CdkF3;C&A`X$JDq&^!=IVkN#Or@#9C=D=Fh|@{8m>&-V zS%B}6-kU0CvU0={pAP(tdpwaP@F~vHBnzZ50m*jo6ogS(dH|k%#QZUwPf{KxaV87P zu*kBC%OHu)#(;IQfO`uTkd`cc@86KiJgMq}IswZefvlmNsa&bX58l}Qh_w|%HH=k( z4n}?)2N7EZ9D^SNBujC^#*6waEL%qwmW?B=L2?#zw;6@tn=ULbJ{une%q?gXWZ8h) za+i5jO~N`L)zuxK&0A4h$e~IpRfQ-`m14I5OZbWprE&1{U%SS^qeF>9X#j?8t0O9m zVXO|gj@G5C$t~)&;@VMMl{bvCHSq(tcu`h=M(kr?d-;p~Q8Etpk4C59{{AGGjFNb` zKMc9Yf;8EG^sC$VlNb(V{Pz9B!`q**|n>d#3;&3+u0-)cpbbB)CO*W zoQ9$14sPRy;ox0d0g5_nYM%FC{~FWL+FL-akp{pD$Y^7B2)6ZCr8$g}BWY)&Gag24 z7m(6U{h0b0>#2>YFD=60dK&vYjS?P(N3!{P4ZbTZoLbCA8mY~qIW4G-^qk!UNp1&f zK1HS5;(dhhbDPB*06SYTiO-s$eurt2(T0SC(1;=OS~&!XT% Ubu(p~03h5~-iltU;8g7Y0~W_6-2eap literal 0 HcmV?d00001 diff --git a/get_data/__pycache__/train_val_split.cpython-38.pyc b/get_data/__pycache__/train_val_split.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4aeb8d62482afdf4e24dfa1d9643f0bce145abd GIT binary patch literal 4306 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 literal 0 HcmV?d00001 diff --git a/get_data/data_gen_flow.py b/get_data/data_gen_flow.py new file mode 100644 index 0000000..23d3fd2 --- /dev/null +++ b/get_data/data_gen_flow.py @@ -0,0 +1,157 @@ + +import os +import numpy as np +import pandas as pd +import seaborn as sn +import matplotlib.pyplot as plt +from PIL import Image +import glob +import tensorflow as tf +from tensorflow.keras.models import Model +from tensorflow.keras.preprocessing.image import ImageDataGenerator + + + +def train_generator(proj_dir, batch_size, input_channel=3): + + """ + create data generator for training dataset; + + Arguments: + out_dir {path} -- path to output results; + batch_size {int} -- batch size for data generator; + input_channel {int} -- input channel for image; + + Return: + Keras data generator; + + """ + + pro_data_dir = os.path.join(proj_dir, 'pro_data') + if not os.path.exists(pro_data_dir): + os.mkdir(pro_data_dir) + + ### load train data based on input channels + if input_channel == 1: + fn = 'train_arr_1ch.npy' + elif input_channel == 3: + #fn = 'train_arr_3ch_crop.npy' + fn = 'train_arr_3ch.npy' + x_train = np.load(os.path.join(pro_data_dir, fn)) + + ### load val labels + train_df = pd.read_csv(os.path.join(pro_data_dir, 'train_img_df.csv')) + y_train = np.asarray(train_df['label']).astype('int').reshape((-1, 1)) + + ## data generator + datagen = ImageDataGenerator( + featurewise_center=False, + samplewise_center=False, + featurewise_std_normalization=False, + samplewise_std_normalization=False, + zca_whitening=False, + zca_epsilon=1e-06, + rotation_range=5, + width_shift_range=0.1, + height_shift_range=0.1, + brightness_range=None, + shear_range=0.0, + zoom_range=0, + channel_shift_range=0.0, + fill_mode="nearest", + cval=0.0, + horizontal_flip=False, + vertical_flip=False, + rescale=None, + preprocessing_function=None, + data_format=None, + validation_split=0.0, + dtype=None, + ) + + ### Train generator + train_gen = datagen.flow( + x=x_train, + y=y_train, + subset=None, + batch_size=batch_size, + seed=42, + shuffle=True, + ) + print('Train generator created') + + return train_gen + + + + +def val_generator(proj_dir, batch_size, input_channel=3): + + """ + create data generator for validation dataset; + + Arguments: + out_dir {path} -- path to output results; + batch_size {int} -- batch size for data generator; + input_channel {int} -- input channel for image; + + Return: + Keras data generator; + + """ + + pro_data_dir = os.path.join(proj_dir, 'pro_data') + if not os.path.exists(pro_data_dir): + os.mkdir(pro_data_dir) + + ### load val data based on input channels + if input_channel == 1: + fn = 'val_arr_1ch.npy' + elif input_channel == 3: + fn = 'val_arr_3ch.npy' + x_val = np.load(os.path.join(pro_data_dir, fn)) + + ### load val labels + val_df = pd.read_csv(os.path.join(pro_data_dir, 'val_img_df.csv')) + y_val = np.asarray(val_df['label']).astype('int').reshape((-1, 1)) + + datagen = ImageDataGenerator( + featurewise_center=False, + samplewise_center=False, + featurewise_std_normalization=False, + samplewise_std_normalization=False, + zca_whitening=False, + zca_epsilon=1e-06, + rotation_range=0, + width_shift_range=0.0, + height_shift_range=0.0, + brightness_range=None, + shear_range=0.0, + zoom_range=0, + channel_shift_range=0.0, + fill_mode="nearest", + cval=0.0, + horizontal_flip=False, + vertical_flip=False, + rescale=None, + preprocessing_function=None, + data_format=None, + validation_split=0.0, + dtype=None, + ) + + datagen = ImageDataGenerator() + val_gen = datagen.flow( + x=x_val, + y=y_val, + subset=None, + batch_size=batch_size, + seed=42, + shuffle=True, + ) + print('val generator created') + + return x_val, y_val, val_gen + + + diff --git a/get_data/exval_dataset.py b/get_data/exval_dataset.py new file mode 100644 index 0000000..d1fbdfd --- /dev/null +++ b/get_data/exval_dataset.py @@ -0,0 +1,214 @@ +import glob +import shutil +import os +import pandas as pd +import numpy as np +import nrrd +import re +import matplotlib +import matplotlib.pyplot as plt +import pickle +from time import gmtime, strftime +from datetime import datetime +import timeit +from sklearn.model_selection import train_test_split +from tensorflow.keras.utils import to_categorical +from utils.resize_3d import resize_3d +from utils.crop_image import crop_image +from utils.respacing import respacing +from utils.nrrd_reg import nrrd_reg_rigid_ref +from get_data.get_img_dataset import img_dataset + + + +def exval_pat_dataset(out_dir, proj_dir, crop_shape=[192, 192, 140], + interp_type='linear', input_channel=3, + norm_type='np_clip', data_exclude=None, new_spacing=[1, 1, 3]): + + """ + Preprocess data (respacing, registration, cropping) for chest CT dataset; + + Arguments: + proj_dir {path} -- path to main project folder; + out_dir {path} -- path to result outputs; + + Keyword arguments: + new_spacing {tuple} -- respacing size, defaul [1, 1, 3]; + return_type {str} -- image data format after preprocessing, default: 'nrrd'; + data_exclude {str} -- exclude patient data due to data issue, default: None; + crop_shape {np.array} -- numpy array size afer cropping; + interp_type {str} -- interpolation type for respacing, default: 'linear'; + + Return: + save nrrd image data; + """ + + NSCLC_data_dir = '/mnt/aertslab/DATA/Lung/TOPCODER/nrrd_data' + NSCLC_reg_dir = os.path.join(out_dir, 'data/NSCLC_data_reg') + exval1_dir = os.path.join(out_dir, 'exval1') + pro_data_dir = os.path.join(proj_dir, 'pro_data') + + if not os.path.exists(NSCLC_reg_dir): + os.mkdir(NSCLC_reg_dir) + if not os.path.exists(exval1_dir): + os.mkdir(exval1_dir) + if not os.path.exists(pro_data_dir): + os.mkdir(pro_data_dir) + + reg_temp_img = os.path.join(exval1_dir, 'NSCLC001.nrrd') + + df_label = pd.read_csv(os.path.join(pro_data_dir, 'label_NSCLC.csv')) + df_label.dropna(subset=['ctdose_contrast', 'top_coder_id'], how='any', inplace=True) + df_id = pd.read_csv(os.path.join(pro_data_dir, 'harvard_rt.csv')) + + ## create df for dir, ID and labels on patient level + fns = [] + IDs = [] + labels = [] + list_fn = [fn for fn in sorted(glob.glob(NSCLC_data_dir + '/*nrrd'))] + for fn in list_fn: + ID = fn.split('/')[-1].split('_')[2][0:5].strip() + for label, top_coder_id in zip(df_label['ctdose_contrast'], df_label['top_coder_id']): + tc_id = top_coder_id.split('_')[2].strip() + if tc_id == ID: + IDs.append(ID) + labels.append(label) + fns.append(fn) + ## exclude scans with certain conditions + print('ID:', len(IDs)) + print('file:', len(fns)) + print('label:', len(labels)) + print('contrast scan in ex val:', labels.count(1)) + print('non-contrast scan in ex val:', labels.count(0)) + df = pd.DataFrame({'ID': IDs, 'file': fns, 'label': labels}) + df.to_csv(os.path.join(pro_data_dir, 'exval_pat_df.csv')) + print('total test scan:', df.shape[0]) + + ## delete excluded scans and repeated scans + if data_exclude != None: + df_exclude = df[df['ID'].isin(data_exclude)] + print('exclude scans:', df_exclude) + df.drop(df[df['ID'].isin(test_exclude)].index, inplace=True) + print('total test scans:', df.shape[0]) + pd.options.display.max_columns = 100 + pd.set_option('display.max_rows', 500) + #print(df[0:50]) + + ### registration, respacing, cropping + for fn, ID in zip(df['file'], df['ID']): + print(ID) + ## respacing + img_nrrd = respacing( + nrrd_dir=fn, + interp_type=interp_type, + new_spacing=new_spacing, + patient_id=ID, + return_type='nrrd', + save_dir=None + ) + ## registration + img_reg = nrrd_reg_rigid_ref( + img_nrrd=img_nrrd, + fixed_img_dir=reg_temp_img, + patient_id=ID, + save_dir=None + ) + ## crop image from (500, 500, 116) to (180, 180, 60) + img_crop = crop_image( + nrrd_file=img_reg, + patient_id=ID, + crop_shape=crop_shape, + return_type='nrrd', + save_dir=NSCLC_reg_dir + ) + + +def exval_img_dataset(proj_dir, slice_range=range(50, 120), input_channel=3, + norm_type='np_clip', split=True, fn_arr_1ch=None): + + """ + get stacked image slices from scan level CT and corresponding labels and IDs; + + Args: + run_type {str} -- train, val, test, external val, pred; + pro_data_dir {path} -- path to processed data; + nrrds {list} -- list of paths for CT scan files in nrrd format; + IDs {list} -- list of patient ID; + labels {list} -- list of patient labels; + slice_range {np.array} -- image slice range in z direction for cropping; + run_type {str} -- train, val, test, or external val; + pro_data_dir {path} -- path to processed data; + fn_arr_1ch {str} -- filename for 1 d numpy array for stacked image slices; + fn_arr_3ch {str} -- filename for 3 d numpy array for stacked image slices; + fn_df {str} -- filename for dataframe contains image path, image labels and image ID; + + Keyword args: + input_channel {str} -- image channel, default: 3; + norm_type {str} -- image normalization type: 'np_clip' or 'np_linear'; + + Returns: + img_df {pd.df} -- dataframe contains preprocessed image paths, label, ID (image level); + + """ + + pro_data_dir = os.path.join(proj_dir, 'pro_data') + df = pd.read_csv(os.path.join(pro_data_dir, 'exval_pat_df.csv')) + fns = df['file'] + labels = df['label'] + IDs = df['ID'] + + ## split dataset for fine-tuning model and test model + if split == True: + data_exval1, data_exval2, label_exval1, label_exval2, ID_exval1, ID_exval2 = train_test_split( + fns, + labels, + IDs, + stratify=labels, + shuffle=True, + test_size=0.2, + random_state=42 + ) + nrrds = [data_exval1, data_exval2] + labels = [label_exval1, label_exval2] + IDs = [ID_exval1, ID_exval2] + fn_arrs = ['exval1_arr1.npy', 'exval1_arr2.npy'] + fn_dfs = ['exval1_img_df1.csv', 'exval1_img_df2.csv'] + + ## creat numpy array for image slices + for nrrd, label, ID, fn_arr, fn_df in zip(nrrds, labels, IDs, fn_arrs, fn_dfs): + img_dataset( + pro_data_dir=pro_data_dir, + run_type='exval', + nrrds=nrrds, + IDs=IDs, + labels=labels, + fn_arr_1ch=None, + fn_arr_3ch=fn_arr_3ch, + fn_df=fn_df, + slice_range=slice_range, + input_channel=3, + norm_type=norm_type, + ) + print('train and test datasets created!') + + ## use entire exval data to test model + elif split == False: + nrrds = fns + labels = labels + IDs = IDs + img_dataset( + pro_data_dir=pro_data_dir, + run_type='exval', + nrrds=nrrds, + IDs=IDs, + labels=labels, + fn_arr_1ch=None, + fn_arr_3ch='exval1_arr.npy', + fn_df='exval1_img_df.csv', + slice_range=slice_range, + input_channel=3, + norm_type=norm_type, + ) + print('total patient:', len(IDs)) + print('exval datasets created!') + diff --git a/get_data/get_img_dataset.py b/get_data/get_img_dataset.py new file mode 100644 index 0000000..88aec52 --- /dev/null +++ b/get_data/get_img_dataset.py @@ -0,0 +1,167 @@ + +import glob +import shutil +import os +import pandas as pd +import nrrd +import re +import matplotlib +import matplotlib.pyplot as plt +import pickle +import numpy as np +from tensorflow.keras.utils import to_categorical +from utils.resize_3d import resize_3d +from utils.crop_image import crop_image +import SimpleITK as sitk +import h5py + + + + +def img_dataset(pro_data_dir, run_type, nrrds, IDs, labels, fn_arr_1ch, fn_arr_3ch, fn_df, + slice_range, input_channel=3, norm_type='np_clip'): + + """ + get stacked image slices from scan level CT and corresponding labels and IDs; + + Args: + run_type {str} -- train, val, test, external val, pred; + pro_data_dir {path} -- path to processed data; + nrrds {list} -- list of paths for CT scan files in nrrd format; + IDs {list} -- list of patient ID; + labels {list} -- list of patient labels; + slice_range {np.array} -- image slice range in z direction for cropping; + run_type {str} -- train, val, test, or external val; + pro_data_dir {path} -- path to processed data; + fn_arr_1ch {str} -- filename for 1 d numpy array for stacked image slices; + fn_arr_3ch {str} -- filename for 3 d numpy array for stacked image slices; + fn_df {str} -- filename for dataframe contains image path, image labels and image ID; + + Keyword args: + input_channel {str} -- image channel, default: 3; + norm_type {str} -- image normalization type: 'np_clip' or 'np_linear'; + + Returns: + img_df {pd.df} -- dataframe contains preprocessed image paths, label, ID (image level); + + """ + + # get image slice and save them as numpy array + count = 0 + slice_numbers = [] + list_fn = [] + arr = np.empty([0, 192, 192]) + + for nrrd, patient_id in zip(nrrds, IDs): + count += 1 + print(count) + nrrd = sitk.ReadImage(nrrd, sitk.sitkFloat32) + img_arr = sitk.GetArrayFromImage(nrrd) + #data = img_arr[30:78, :, :] + #data = img_arr[17:83, :, :] + data = img_arr[slice_range, :, :] + ### clear signals lower than -1024 + data[data <= -1024] = -1024 + ### strip skull, skull UHI = ~700 + data[data > 700] = 0 + ### normalize UHI to 0 - 1, all signlas outside of [0, 1] will be 0; + if norm_type == 'np_interp': + data = np.interp(data, [-200, 200], [0, 1]) + elif norm_type == 'np_clip': + data = np.clip(data, a_min=-200, a_max=200) + MAX, MIN = data.max(), data.min() + data = (data - MIN) / (MAX - MIN) + ## stack all image arrays to one array for CNN input + arr = np.concatenate([arr, data], 0) + + ### create patient ID and slice index for img + slice_numbers.append(data.shape[0]) + for i in range(data.shape[0]): + img = data[i, :, :] + fn = patient_id + '_' + 'slice%s'%(f'{i:03d}') + list_fn.append(fn) + + ### covert 1 channel input to 3 channel inputs for CNN + if input_channel == 1: + img_arr = arr.reshape(arr.shape[0], arr.shape[1], arr.shape[2], 1) + print('img_arr shape:', img_arr.shape) + np.save(os.path.join(pro_data_dir, fn_arr_1ch), img_arr) + elif input_channel == 3: + img_arr = np.broadcast_to(arr, (3, arr.shape[0], arr.shape[1], arr.shape[2])) + img_arr = np.transpose(img_arr, (1, 2, 3, 0)) + print('img_arr shape:', img_arr.shape) + np.save(os.path.join(pro_data_dir, fn_arr_3ch), img_arr) + #fn = os.path.join(pro_data_dir, 'exval_arr_3ch.h5') + #h5f = h5py.File(fn, 'w') + #h5f.create_dataset('dataset_exval_arr_3ch', data=img_arr) + + ### generate labels for CT slices + if run_type == 'pred': + ### makeing dataframe containing img dir and labels + img_df = pd.DataFrame({'fn': list_fn}) + img_df.to_csv(os.path.join(pro_data_dir, fn_df)) + print('data size:', img_df.shape[0]) + else: + list_label = [] + list_img = [] + for label, slice_number in zip(labels, slice_numbers): + list_1 = [label] * slice_number + list_label.extend(list_1) + ### makeing dataframe containing img dir and labels + img_df = pd.DataFrame({'fn': list_fn, 'label': list_label}) + pd.options.display.max_columns = 100 + pd.set_option('display.max_rows', 500) + print(img_df[0:100]) + img_df.to_csv(os.path.join(pro_data_dir, fn_df)) + print('data size:', img_df.shape[0]) + + + +def get_img_dataset(proj_dir, run_type, data_tot, ID_tot, label_tot, slice_range): + + """ + Get np arrays for stacked images slices, labels and IDs for train, val, test dataset; + + Args: + run_type {str} -- train, val, test, external val, pred; + pro_data_dir {path} -- path to processed data; + data_tot {list} -- list of data paths: ['data_train', 'data_val', 'data_test']; + ID_tot {list} -- list of image IDs: ['ID_train', 'ID_val', 'ID_test']; + label_tot {list} -- list of image labels: ['label_train', 'label_val', 'label_test']; + slice_range {np.array} -- image slice range in z direction for cropping; + + Keyword args: + input_channel {str} -- image channel, default: 3; + norm_type {str} -- image normalization type: 'np_clip' or 'np_linear'; + + """ + + pro_data_dir = ps.path.join(proj_dir, 'pro_data') + if not os.path.exists(pro_data_dir): + os.mkdir(pro_data_dir) + + fns_arr_1ch = ['train_arr_1ch.npy', 'val_arr_1ch.npy', 'test_arr_1ch.npy'] + fns_arr_3ch = ['train_arr_3ch.npy', 'val_arr_3ch.npy', 'test_arr_3ch.npy'] + fns_df = ['train_img_df.csv', 'val_img_df.csv', 'test_img_df.csv'] + + for nrrds, IDs, labels, fn_arr_1ch, fn_arr_3ch, fn_df in zip( + data_tot, ID_tot, label_tot, fns_arr_1ch, fns_arr_3ch, fns_df): + + img_dataset( + pro_data_dir=pro_data_dir, + run_type=run_type, + nrrds=nrrds, + IDs=IDs, + labels=labels, + fn_arr_1ch=fn_arr_1ch, + fn_arr_3ch=fn_arr_3ch, + fn_df=fn_df, + slice_range=slice_range, + input_channel=input_channel, + norm_type=norm_type, + ) + + + + + diff --git a/get_data/get_pat_dataset.py b/get_data/get_pat_dataset.py new file mode 100644 index 0000000..5072dbf --- /dev/null +++ b/get_data/get_pat_dataset.py @@ -0,0 +1,174 @@ + + +import glob +import shutil +import os +import pandas as pd +import nrrd +import re +from sklearn.model_selection import train_test_split +import pickle +import numpy as np +from time import gmtime, strftime +from datetime import datetime +import timeit + + + + +def pat_df(label_dir, label_file, cohort, data_reg_dir, MDACC_data_dir): + + """ + create dataframe to contain data path, patient ID and label on the + patient level; + + Arguments: + label_dir {path} -- path for label csv file; + label_file {csv} -- csv file contain lable info; + cohort {str} -- patient cohort name (PMH, CHUM, MDACC, CHUS); + MDACC_data_dir {patyh} -- path to MDACC patient data; + + Return: + panda dataframe for patient data; + + """ + + ## labels + labels = [] + df_label = pd.read_csv(os.path.join(label_dir, label_file)) + df_label['Contrast'] = df_label['Contrast'].map({'Yes': 1, 'No': 0}) + if cohort == 'CHUM': + for file_ID, label in zip(df_label['File ID'], df_label['Contrast']): + scan = file_ID.split('_')[2].strip() + if scan == 'CT-SIM': + labels.append(label) + elif scan == 'CT-PET': + continue + elif cohort == 'CHUS': + labels = df_label['Contrast'].to_list() + elif cohort == 'PMH': + labels = df_label['Contrast'].to_list() + elif cohort == 'MDACC': + fns = [fn for fn in sorted(glob.glob(MDACC_data_dir + '/*nrrd'))] + IDs = [] + for fn in fns: + ID = 'MDACC' + fn.split('/')[-1].split('-')[2][1:4].strip() + IDs.append(ID) + labels = df_label['Contrast'].to_list() + print('MDACC label:', len(labels)) + print('MDACC ID:', len(IDs)) + ## check if labels and data are matched + for fn in fns: + fn = fn.split('/')[-1] + if fn not in df_label['File ID'].to_list(): + print(fn) + ## make df and delete duplicate patient scans + df = pd.DataFrame({'ID': IDs, 'labels': labels}) + df.drop_duplicates(subset=['ID'], keep='last', inplace=True) + labels = df['labels'].to_list() + #print('MDACC label:', len(labels)) + + ## data + fns = [fn for fn in sorted(glob.glob(data_reg_dir + '/*nrrd'))] + + ## patient ID + IDs = [] + for fn in fns: + ID = fn.split('/')[-1].split('.')[0].strip() + IDs.append(ID) + ## check id and labels + if cohort == 'MDACC': + list1 = list(set(IDs) - set(df['ID'].to_list())) + print(list1) + ## create dataframe + print('cohort:', cohort) + print('ID:', len(IDs)) + print('file:', len(fns)) + print('label:', len(labels)) + df = pd.DataFrame({'ID': IDs, 'file': fns, 'label': labels}) + + return df + + + +def get_pat_dataset(data_dir, out_dir, proj_dir): + + """ + get data path, patient ID and label for all the cohorts; + + Arguments: + data_dir {path} -- path to the CT data; + lab_drive_dir {path} -- path to outputs; + proj_dir {path} -- path to processed data; + CHUM_label_csv {csv} -- label file for CHUM cohort; + CHUS_label_csv {csv} -- label file for CHUS cohort; + PMH_label_csv {csv} -- label file for PMH cohort; + MDACC_label_csv {csv} -- label file for MDACC cohort; + + Return: + lists for patient data, labels and IDs; + + """ + + MDACC_data_dir = os.path.join(data_dir, '0_image_raw_MDACC') + CHUM_reg_dir = os.path.join(lab_drive_dir, 'data/CHUM_data_reg') + CHUS_reg_dir = os.path.join(lab_drive_dir, 'data/CHUS_data_reg') + PMH_reg_dir = os.path.join(lab_drive_dir, 'data/PMH_data_reg') + MDACC_reg_dir = os.path.join(lab_drive_dir, 'data/MDACC_data_reg') + label_dir = os.path.join(lab_drive_dir, 'data_pro') + pro_data_dir = os.path.join(proj_dir, 'pro_data') + + cohorts = ['CHUM', 'CHUS', 'PMH', 'MDACC'] + label_files = ['label_CHUM.csv', 'label_CHUS.csv', 'label_PMH.csv', 'label_MDACC.csv'] + data_reg_dirs = [CHUM_reg_dir, CHUS_reg_dir, PMH_reg_dir, MDACC_reg_dir] + df_tot = [] + for cohort, label_file, data_reg_dir in zip(cohorts, label_files, data_reg_dirs): + df = pat_df( + label_dir=label_dir, + label_file=label_file, + cohort=cohort, + data_reg_dir=data_reg_dir, + MDACC_data_dir=MDACC_data_dir + ) + df_tot.append(df) + + ## get df for different cohorts + df_CHUM = df_tot[0] + df_CHUS = df_tot[1] + df_PMH = df_tot[2] + df_MDACC = df_tot[3] + + ## train-val split + df = pd.concat([df_PMH, df_CHUM, df_CHUS], ignore_index=True) + data = df['file'] + label = df['label'] + ID = df['ID'] + data_train, data_val, label_train, label_val, ID_train, ID_val = train_test_split( + data, + label, + ID, + stratify=label, + test_size=0.3, + random_state=42 + ) + + ## test patient data + data_test = df_MDACC['file'] + label_test = df_MDACC['label'] + ID_test = df_MDACC['ID'] + + ## save train, val, test df on patient level + train_pat_df = pd.DataFrame({'ID': ID_train, 'file': data_train, 'label': label_train}) + val_pat_df = pd.DataFrame({'ID': ID_val, 'file': data_val, 'label': label_val}) + test_pat_df = pd.DataFrame({'ID': ID_test, 'file': data_test, 'label': label_test}) + train_pat_df.to_csv(os.path.join(pro_data_dir, 'train_pat_df.csv')) + val_pat_df.to_csv(os.path.join(pro_data_dir, 'val_pat_df.csv')) + test_pat_df.to_csv(os.path.join(pro_data_dir, 'test_pat_df.csv')) + + ## save data, label and ID as list + data_tot = [data_train, data_val, data_test] + label_tot = [label_train, label_val, label_test] + ID_tot = [ID_train, ID_val, ID_test] + + return data_tot, label_tot, ID_tot + diff --git a/get_data/preprocess_data.py b/get_data/preprocess_data.py new file mode 100644 index 0000000..2120010 --- /dev/null +++ b/get_data/preprocess_data.py @@ -0,0 +1,187 @@ +import glob +import shutil +import os +import pandas as pd +import nrrd +import re +from sklearn.model_selection import train_test_split +import pickle +import numpy as np +from time import gmtime, strftime +from datetime import datetime +import timeit +from utils.respacing import respacing +from utils.nrrd_reg import nrrd_reg_rigid_ref +from utils.crop_image import crop_image + + + +def preprocess_data(data_dir, out_dir, new_spacing=(1, 1, 3), data_exclude=None, + crop_shape=[192, 192, 10], interp_type='linear'): + + """ + Preprocess data including: respacing, registration, cropping; + + Arguments: + data_dir {path} -- path to CT data; + out_dir {path} -- path to result outputs; + + Keyword arguments: + new_spacing {tuple} -- respacing size; + return_type {str} -- image data format after preprocessing, default: 'nrrd'; + data_exclude {str} -- exclude patient data due to data issue, default: None; + crop_shape {np.array} -- numpy array size afer cropping; + interp_type {str} -- interpolation type for respacing, default: 'linear'; + + Return: + save nrrd image data; + """ + + CHUM_data_dir = os.path.join(data_dir, '0_image_raw_CHUM') + CHUS_data_dir = os.path.join(data_dir, '0_image_raw_CHUS') + PMH_data_dir = os.path.join(data_dir, '0_image_raw_PMH') + MDACC_data_dir = os.path.join(data_dir, '0_image_raw_MDACC') + CHUM_reg_dir = os.path.join(out_dir, 'data/CHUM_data_reg') + CHUS_reg_dir = os.path.join(out_dir, 'data/CHUS_data_reg') + PMH_reg_dir = os.path.join(out_dir, 'data/PMH_data_reg') + MDACC_reg_dir = os.path.join(out_dir, 'data/MDACC_data_reg') + + if not os.path.exists(CHUM_reg_dir): + os.mkdir(CHUM_reg_dir) + if not os.path.exists(CHUS_reg_dir): + os.mkdir(CHUS_reg_dir) + if not os.path.exists(PMH_reg_dir): + os.mkdir(PMH_reg_dir) + if not os.path.exists(MDACC_reg_dir): + os.mkdir(MDACC_reg_dir) + + reg_temp_img = os.path.join(PMH_reg_dir, 'PMH050.nrrd') + + # get PMH data + #------------------ + fns = [fn for fn in sorted(glob.glob(PMH_data_dir + '/*nrrd'))] + ## PMH patient ID + IDs = [] + for fn in fns: + ID = 'PMH' + fn.split('/')[-1].split('-')[1][2:5].strip() + IDs.append(ID) + ## PMH dataframe + df_PMH = pd.DataFrame({'ID': IDs, 'file': fns}) + pd.options.display.max_colwidth = 100 + #print(df_pmh) + file = df_PMH['file'][0] + data, header = nrrd.read(file) + print(data.shape) + print('PMH data:', len(IDs)) + + # get CHUM data + #----------------- + fns = [] + for fn in sorted(glob.glob(CHUM_data_dir + '/*nrrd')): + scan_ = fn.split('/')[-1].split('_')[2].strip() + if scan_ == 'CT-SIM': + fns.append(fn) + else: + continue + ## CHUM patient ID + IDs = [] + for fn in fns: + ID = 'CHUM' + fn.split('/')[-1].split('_')[1].split('-')[2].strip() + IDs.append(ID) + ## CHUM dataframe + df_CHUM = pd.DataFrame({'ID': IDs, 'file': fns}) + #print(df_chum) + pd.options.display.max_colwidth = 100 + file = df_CHUM['file'][0] + data, header = nrrd.read(file) + print(data.shape) + print('CHUM data:', len(IDs)) + + # get CHUS data + #--------------- + fns = [] + for fn in sorted(glob.glob(CHUS_data_dir + '/*nrrd')): + scan = fn.split('/')[-1].split('_')[2].strip() + if scan == 'CT-SIMPET': + fns.append(fn) + else: + continue + ## CHUS patient ID + IDs = [] + for fn in fns: + ID = 'CHUS' + fn.split('/')[-1].split('_')[1].split('-')[2].strip() + IDs.append(ID) + ## CHUS dataframe + df_CHUS = pd.DataFrame({'ID': IDs, 'file': fns}) + pd.options.display.max_colwidth = 100 + #print(df_chus) + file = df_CHUS['file'][0] + data, header = nrrd.read(file) + print(data.shape) + print('CHUS data:', len(IDs)) + + # get MDACC dataset + #------------------ + fns = [fn for fn in sorted(glob.glob(MDACC_data_dir + '/*nrrd'))] + ## MDACC patient ID + IDs = [] + for fn in fns: + ID = 'MDACC' + fn.split('/')[-1].split('-')[2][1:4].strip() + IDs.append(ID) + ## MDACC dataframe + df_MDACC = pd.DataFrame({'ID': IDs, 'file': fns}) + df_MDACC.drop_duplicates(subset=['ID'], keep='last', inplace=True) + print('MDACC data:', df_MDACC.shape[0]) + + # combine dataset for train-val split + #------------------------------------- + df = pd.concat([df_PMH, df_CHUM, df_CHUS, df_MDACC], ignore_index=True) + print('total patients:', df.shape[0]) + #print(df[700:]) + ## exclude data with certain conditions + if data_exclude != None: + df_exclude = df[df['ID'].isin(data_exclude)] + print(df_exclude) + df.drop(df[df['ID'].isin(data_exclude)].index, inplace=True) + print(df.shape[0]) + + for fn, ID in zip(df['file'], df['ID']): + + print(ID) + + ## set up save dir + if ID[:-3] == 'PMH': + save_dir = PMH_reg_dir + elif ID[:-3] == 'CHUM': + save_dir = CHUM_reg_dir + elif ID[:-3] == 'CHUS': + save_dir = CHUS_reg_dir + elif ID[:-3] == 'MDACC': + save_dir = MDACC_reg_dir + + ## respacing + img_nrrd = respacing( + nrrd_dir=fn, + interp_type=interp_type, + new_spacing=new_spacing, + patient_id=ID, + return_type='nrrd', + save_dir=None + ) + + ## registration + img_reg = nrrd_reg_rigid_ref( + img_nrrd=img_nrrd, + fixed_img_dir=reg_temp_img, + patient_id=ID, + save_dir=None + ) + + ## crop image from (500, 500, 116) to (180, 180, 60) + img_crop = crop_image( + nrrd_file=img_reg, + patient_id=ID, + crop_shape=crop_shape, + return_type='nrrd', + save_dir=save_dir + ) diff --git a/go_model/.DS_Store b/go_model/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0g`kf~u7KL=gQLL?8o3AjbiSi&=m~3PUi1CZpdl9|cO7P=J@5)u*;QjnObDsib$Rk6Sc_P`bib z+0NX!?hk-dd*ml@;(z$cY5oB%7oMG+WrZ#Keec<^-}n4pew@uF9<*yB@Y~q){<6yb zhCq1^Lot|$M||exgY6HmPtQ!;1mwkrJi1wmt3}W(kI0|;Zd%kQ$N0()@ z#+)y&>2)J`y{ubZNZ7mln$rG6a4oUY%UTqjWw5L;V`?-0>~rNn`DRd zh@_y+V9ddmkfWP;7i@zqs?jdoh7gPgr`Ml2Ul*?&<)@FIklp96E-@>ZQ0nR8$!fP~ zzQ|*1!Bw@lZoV)l(xe) zRc3n86G*0E->jFyI#`rg!($%NuB3!OzNumT;XOk~HHI&-NeGdx4*gv;zQYIG-_i7cwzpA=0`yOq$j{(}?1B6ggc%Od$_x_5@ZI`RsD5!Rd;n&_58YCuV{FF z@Xf!S+YL?ohwN-WIdne2$G$;BG@=KZol)J^Ro}34ikfy_(Slu6v=o-@ik`_y|a|Jc zuGm+UzN_|C&_Zx6v}{X_6~SM(uPeS3+z4;lHx*w7f6KlFzCmiF{-ti;Ci=(Pp!o)S z)qXM1MgCdnOgu2fC-FSu9tB$>4)?epdILigCZXg+iE%ns80P#uCSCx+N)S6_6mEJu zZs0KHkA2tSejJUcm&BAqtmVehc+MmiI-L3^|CH7>?e9MkMdnY!*e7lwgH+o`EBo?f z*hhw@aqXqPHd3AFr^c$V=^4a8uc&&tv~a4kBd)DY$?4gQTv`PBkwU@gTB?!U%%GnV zb5$bwRhbl4mFNSYMWCFNfR=$)fW8B?3iJ=82DA>e0kj9S3G^?d1+)#cv#Rj?`fN?b zv~+5m>Z>X*u1lMB7SpoAYLF|ZHPY>Cr~0}=dT6U;2hw$3+sGEu+^O+}zG|e6bv>4>R?|B6+Jc5=+9JDsjqIV{-t_l3{SI%De%k)R!2Vn0QreMRnkScM##cHe7dn5Z zjdgNBu6$Wan@=^^>qbYAyQ<{=d@k2Zd*m9q_FIGOkBz>zYV!QcHn4qS{n1!;C)#QU z`?=oNo@y^XUG2g*H)aJ``dhlY?vR_OI=R)?z}*IiHLts;+G;Plllc&G|9VUA&bC~- zv+kvP^3=Z4Kl^JcE!};jrA^R#pk2@bC{S{pbjdw3nB=j>{d9l5bME!oG3S-H z$vo>dQd1dJ;ATIO6M7eB_&mB0f9_F-WnyaD*pECVu;mJO3I|wEKFL(5r_^Ip?8crDL7KwIaiPmoMSH#IX~h9Q+ZKoDSg3=ug) zSrJB*2vc6S!bpfHQ4bfdjI*M{aI(%q6tLYz;)$ZnM1aU7{EvrAx9#3@Mmr*M)bph3oVagmMEI#km;Qh%PgG4-TN7!x_f zAH56Mu5P>vGspC{6g03D+WHGvTf-N)0BGjtN0G|2K30cqxTH@9JY`H zRS=a)gFEydc$@%!zvRJ0^DtS^2MSA^h&YTM%1)7a&T&j5VX#QF@q?8FF%SG>#JJ={ zVMB9Cz-=?4l;F5rKUoYJcVvKN#9+)Wf9!`z;63}~xox6U&xAP%;$xBf<+GnrnarZ< z#zBDl2&I`(SqAJjoB8q&a)^A37;e!J`FhS8h$fj=D2LbumBoO{KRYbScV{@43il!w zQ(PUN+j|%Ma3=4lp^6(ui*lL_XS>NT$)V9O^bn>lqh&cH|L>LmvO|?cBIDG_mL5UP eJn$aN9k3yq%kru9PS{VKM0+H2Z-?~K8=`1 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 TCQL4bQvQ4ZsResjZR5>9QGLmw literal 0 HcmV?d00001 diff --git a/go_model/__pycache__/get_model.cpython-38.pyc b/go_model/__pycache__/get_model.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7bb7684098ef9ff5453078695a099d79dae3d66c GIT binary patch literal 1830 zcmZ`3$&MT~wDzT!Su!CkCkrA)Es<^#IG}*gz(67d>O%-~C?U0Kx?J6!sj^G9J44UZ zoWcod_y#!UCs6KOIr++IegO#L+0|Xsk-(*Teyja9zi<2fjs@%Y-(IF4#+LPmCN`G~ z#6JAuM*x|X*@cxfyPY_Ob`#g^UgDYEPkh*&A}GTow6(5VL}i@B2KS0i*-g3z_lsWH zPx=NAimh@x**17s?3BC7uEC??M!A>l0p8ELS?>osxtZDDSc9!A@W&btY}Kg>CMPA! zaG@zd%BsMVDWyR5zQ}VjC78-FmOuxOv1qXPOFG4sBuuLXhmRgLZ2!sEfIN4vUcl*G zr{+p$;Ndg?xL%MaD}?_5e(?f;w2tkcof@+9i*pgwcIKQo{I+y!w{|l3(zyufF>pQL z?n^K8YQOf3%)j(OHU?P$vhSpy1+6SJvil&5Kqh4XcKW>d(gmBhfZqZBPcqCptxZ?k z$fypV+4kD2K*qKG%+7kRTi{(JJ9St`kj;L!b?Mnw9YQv@(_d}N>L6G%OfgR-Mvy^d zvMM@8@F|~(2L{t>d_kL>qjMqo1-g3|P3MdWj6{W}1hS3}#^dGyB$pNbs5MQek~~dy z>WwX89OVlNOh(N)8}c{8 zkyC z-B4FPxIt**>ehEucOy{Mxz4HTHgDkGd-o3S4}2AHEGU*LS_h$en?CbC^f%uEpdxro z@HjsEVBjYK&U3KhyIOifgI146Kn`5K^9omW+Xr{}J{%B}_51s;GtRtWMQ2Y2p7Ko# zs!*~?P718LV$M#R@(HzL2$S0hO-n2WUF9&LyegG*d6a#oq?`pZlXZEAi-P zev(o;I>TpkMrWfLkrR$9Hu@auc)%zeBIIbs8vD@8v3s&{9pQIln+e`o;b9ER4p?m zRcyAZSO^u(cv{V$k~4KnVk#KV3wAm@fyp6;3rPxLQkv{G17vtTlubR7y%m3RJe&F? z{gtF?Qd78OXT@tf$P}mGvdmxJ&8M^%LW*2L!@ z0pbY^@h40SF&ra0gP0OSThunNL>&X&q)aP#CgV}h@Ty6T*5|wh+AzE(Z2}~5D{0fV zS+nBBq(eJ%xus;8t{B{oyGfUJ4PJ^@lUwvwa+}_s%dOEhqwfyA1JH@@ChK(Fths=1 z&<%r^<1dnX^q#>hfZwP00ryz-4WbViet|Y?AK`>(e-o?HUg8f!K-}HzG?gI-w9I@N z$|wmpiE@UC##C8IJ}^Ak^FzkM7zDjI^I1QcQ%gbY3lR;Xz?V^$_Ia3RT!L6L$kM^7 z(7eQ#JUR<1+Wh=m1?a-CV2BSGA&CmNC>PG9^%TAOaA1uqV{crQctWIgWKT*3E-F(p z#p9ZEChj%QGrYPi&pD_dQ*w#N3&p~uQq(}c@f#Xoh8cNNE*eh}tTl&d+$!qKT18;% z(YRf-Cmyq>n3Yx$U=Cm|VAUxaFBYvytyr95j84u9y{o*9iUz<6z$QQsAaI$*D$E<& zt7zOQI!EtF+&BUX%+pBj!PZ%2RX4} zGx@0Zay`8VN|D~z3ZK1)!j&W5kCGv;!P2K^Tca!qw+=_gewuDwgcqYM9c~RHY07bH z@8`$6St_|Npe-97zDsp}BJWJP%W*9GPiZWiy`T z${q6TG(S97b`tujD&;&%rLtfP>>K%csA{kM_%!SX`Z5ultEysdhdztATJT2+1Q#wB z-_TXzzrG4KZ)#u4I+Z!k4pnWCiat0#50<7N#WTv)l+U^HuGnm#oIz@M6@Xfj4QO?a z>H|{utRJ3%Z;F6$$vDNkV3X_Xhp%Ys_8^KwMIuJ4Y;bMPsJZfly0CI}^pNS`f^t)N zyrC7fv?5P4XZ5rWw0Hs&vX-q5>5w+@aNDwR4X?o{o4<)~S#8|GZQKOfhT&m{*mwc3 z4*Yk4&v!w`=0koDfGVY@Nq){ZH16bn%6!2e0zq97`iB{ZM2l3l;Ihr*Ovce6)Uw7v zcm}OJmdZ{!XR0cKC_nEB=}V!LuK^K4RbE6%9*27`zo)j4(Xq0JF(k%%zW0pl&Q;YQ zi(}{=U6Nd1-n1o-BmJ86pubWv$atbEHzWrFLM!W1rT2-xKFw3mcXv4Ph($ literal 0 HcmV?d00001 diff --git a/go_model/__pycache__/test_model.cpython-38.pyc b/go_model/__pycache__/test_model.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0794f40ecfb4639920f7b33109cc4ea88304df2a GIT binary patch literal 2223 zcmYin$!;4rl;n&?qs@|Rc~R^n&ep<3BX_w4ZcrcrTDPeYG-(e+17SRzk;a-WB<0$| z=;Q?Lr9k=z(lN*WO~Gq>@h{}kK2plU0r?g_K0dx}erhzT2*!o@&if0ne>CyoAV7Q# zQ~V1HLk!1=E+D4Z(1cnB+O%Y#L(2xbNrhJNLbgV0hF4D-bY;nF(w5=1X&WGkSCcim zX7;SOldRMArQAlcNw*Ac$K9k$y9O`CSCVbIo$Sz^rQB6|)#$rMuK{%8>&Y(NHG5^i zZ_pbCcjKGMEqcq~6~J%P+kjVD?FFKD7=D8G>X-0RbhL+6=`isI0U+fE**KK}2h?Sr z3}lo9dqg>dL}SVol81%|XMV_75QAVf&OFvnmejf*dqPBEhQVP&b)9ElV=9qs#_ci!Lm8wxRNvGxnMs6PbTr|t9$kXto!NAKiT zi&e-4^pY?f!W+R>a?%DKW@~?tX?uXCYw+Gq7d=GJKAm=o&TN&f&oSHRBEU8Q150OX z;PLfhZPqDZ52IK3IW6?LtpikOe{})Y0bT*v2Dl9nc3Foxtjn&j?E(0Cx>0P**55^8 zM@IqP)+|~ffvmfFbP3@_la>sG?0cN|Klg{dG(W%8vHyVm<$^tF8o?+T^jX;R#i=UA z-f<9ft>seJw6da9?vcy?VDOq&Qu%S5?mmT#y{Bvu_+nDkI(Fksh38 z*X(Voit%8dMO@W$p7o*0d4{S9?=)DpF|NCjYD8&1mVJK+@d{#9^?Vtfnx>}Jdt7pz z9LhOcIE%25?`lTLT)}T>+!iv+LEO)HpdBHEvLN&LP1vNhrCY&{V}9L;hfy3T5;0n5 zp;2pmY!ZlXfCS&tVkVmxX|W5m_!bsqx!A=HOdEHs4({M9)+(+7y@l(z1z#0cEts&Q z*A`x}Y}nbsDIcKUS{3t)~f%~K|UIajdpp_?4lE=Z})9)#`EE*|$ z5NF5AdVKhZ>v~i*KZ|3i2l#ymt{Vkyi&3QiT0N+lRD>B%pyb}61QI~kgtAQ{>U!q- ze*;EE{s^25K1@Z%!#MkuZobERqX0^s_c0Dz mL`c{90yAN94?xX04({uVg?8CnN4wzo#nkKKYndP{Dd&IaIBTi^ literal 0 HcmV?d00001 diff --git a/go_model/__pycache__/train_model.cpython-38.pyc b/go_model/__pycache__/train_model.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b53739395a577d519cdc0b9363adf1ab3e9d57d7 GIT binary patch literal 1839 zcmb7F&2J+$6rY)7I+KrfyY04RSp>c^aj=9o_5oO z8(01ZuKV|J^Oe(HIUXe>(IuVbmGkWIfRGe2A^up6U@m}?qK1RA<41Nme`$hPD zFdDwaD1%p{&?c9vk%j}3X~7IPWyMF4?Fp4L;TW5012ZoUZTzh&xIkO2aer+T>Wa-RJ0I{9WQte~gpClC7~EI68M@jr2NQ zC+k7c{rLz}=wRqE`x9&3C;0gtN##Q7d%;VX>ldDKYOf%xT*?jkBYFOu?6^;NIA~cMB!KPfarc-9}MW)MJ zPFAHfep4?aUgoy-1rmxCSdrF{*D7DMHoKB}c?(mG$%+!ls%B>4y*t9EgQ~hQWL65U z_Y7)7R(Oa6cb+vecjV4~JDH$xY98iv@ z394T1JHx1FnD?eAf-{h=C$LShoqX$k$4vuruU~gnU^(6*mM@chBo%d zw;^7eSED1Ex&}7On%iVsFxz#8wu6pnC$BV3^D2`HDs2*w`P8?Y9SRBLU!^#L*CraIvw!LU7{fo)6GbZDZe|HumiX zX9$Bw4%@+_anH@R_Hd5EHvxQz%O77(7OLWt>Een>Il1Mx3nk~1d1*42#pEl_>q~{D zXWC5WY7^^Zv$v0CTrcflk3U7Hn&1?C9fj{0zk>sfp4dof8_T9bKas5MLCs{rw2kV* zCYrOU0%;>HZFerz)DChb1ous+!TljX2PfNgk0*2;A3MHpxYP=;J8o=bX7r-v$5-xE z8T;Th93YyW+$A>NJ-LuL#d%{&p~v^%UP#>@PyXfe?goH{o2G8mqumAu6 literal 0 HcmV?d00001 diff --git a/go_model/__pycache__/val_model.cpython-38.pyc b/go_model/__pycache__/val_model.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b8b8001a8cdd09d8c5d4537fb93132298812773 GIT binary patch literal 2075 zcmYjSOK%%D5GJYBYW0?E`60({oTM#kpppByrA81mxfp4UplN#`8VK}qC9Soukd$i~ zt5Z_6mjdanN9mZK(<_l%4~73iF71#~;_hNO-wcN{bHq8GY z0?Y&WXniGX(598OXbUKb+VL7) zvvY-LJ?_xXN^c|Hq+1p{(RRE|w=FJ4-FS!Y#5d@TmEJDhwf6St9#A*>Aiha&+PMC)MUbUJfm+K?lfgt9 zS>>#>H=W|w3e6h@`B8& zu)dyGdAW;t0r=XA$1A?B8oZL%UJ#4`Rr9(*d6Cy(pKriUGY5~e#;<6Ad7U?2dU@j! z0$*Sqz=Y2H0CYo@vT3#OzsG6OVD@YHzY@V~I&xGiI?vHv&HoY>byOw^r zlwLNBS{M)ee9-gdxh_WRG>CK|Ow<9n`WGHig@wY}EQ9iXluCK;(le0x{ykli;V@1^ z?i-bNkLl7PeCzPZT^NQaN6$Jl%u$XxrFA0b*GoJmbD3CvnWgRC@t9 znZOkRiUKYTu8Wy~r0SA!8v4q}hj0pAlvxxiAkg#~NV6;eZC9nnAMC7b1J>uE(6vmY zeYkg+<*JgM2dkS!mRsQ>x2U8(Z=L z49M}Yi#_7tj?=+Ag*EKq8g9W`!IgpsW5*!i8wCeuc5wqVmTw1li1-?89DF410MW%{ z5@%C!%V0NS31?D#Y?zV^*l8*tZ!*zsxJt7qRZ(~f6=x7AFKHo*6m=3IxUNb+%%(l5 zn35(Bh7c)f`DqwuQE>eIW9mp1j&u`N(6t>f(O|!LIHyB6^sh1uq(e=cEX+lW5z=82&;#V%gOR_d2i6h$`Tjia` z9sY#vg2Z3L-B(Wh3!HdPn&BaVquBoPJwLzqz1Xih9S=c!@crxi%_c%WG%%Y5;1RrB zcM*zkiu`KCzE$FeZ^LM%PUiRy)?6cPW-Y&!xxNe7j+{@(Z%23^4Vyo~3)&lEprJJhqVr_B~^9mY1iyYTWIjF3P__`;G9%cXVJ6AjsmtaIxN{(Spv zO|D(oQR5P0RL*SSM2={lqjPHmU43mzY@n7jIaf9e&Xu--+R`zQCz}RZk}U%*OV>ah z*)~vDn3f&6WN<5T*+8qZW1ybw8fZ^=7%$$(7c}!!IG10uUf!p7?4?5+m_pwo;1M)5hha- z1^dT}FeRKPhe=3;-sg`i@E;WLG0sImj;rKKS0*8*tlX+3;(~@#bLTq010up{pC|Jx zi57`Rj23hz`V2Iz7nZK76&cN7_gPj^C6UI20c&bJfx_sExA1Otr!+$SH%# zq5!^I3+}fT3agvf6%823!o>BBK~I0xj6~P$x=8`~IHjV>E7b_JerkCkS)e6T-HNGo zQk`0!iuTIvbrq)yAAUpZ4oL9p*?0=Njt{0ENS=@9@q9}2$#{~8GSu;t*P~sU!~f33 zXm@XK^qkS7I27aR1Y|Ud({bfjAB3&qRJ9lIM7a9Cxd$DxySR%zyaE0H9}ly;U_bO& z3kK!nvkY8Xd7+vG$s@v*RYXi*smj)etSn=teQ)&Kwi literal 0 HcmV?d00001 diff --git a/go_model/callbacks.py b/go_model/callbacks.py new file mode 100644 index 0000000..f1c65c9 --- /dev/null +++ b/go_model/callbacks.py @@ -0,0 +1,74 @@ +#---------------------------------------------------------------------- +# Deep learning for classification for contrast CT; +# Transfer learning using Google Inception V3; +#------------------------------------------------------------------------------------------- + +import os +import numpy as np +import pandas as pd +import seaborn as sn +import tensorflow as tf +from tensorflow.keras.callbacks import EarlyStopping +from tensorflow.keras.callbacks import LearningRateScheduler +from tensorflow.keras.callbacks import ModelCheckpoint +from tensorflow.keras.callbacks import TensorBoard + + +# ---------------------------------------------------------------------------------- +# scheduler +# ---------------------------------------------------------------------------------- +def scheduler(epoch, lr): + if epoch < 10: + return lr + else: + return lr * tf.math.exp(-0.1) + +# ---------------------------------------------------------------------------------- +# scheduler +# ---------------------------------------------------------------------------------- +def callbacks(log_dir): + + check_point = ModelCheckpoint( + filepath=os.path.join(log_dir, 'model.{epoch:02d}-{val_loss:.2f}.h5'), + monitor='val_acc', + verbose=1, + save_best_model_only=True, + save_weights_only=True, + mode='max' + ) + + tensor_board = TensorBoard( + log_dir=log_dir, + histogram_freq=0, + write_graph=True, + write_images=False, + update_freq='epoch', + profile_batch=2, + embeddings_freq=0, + embeddings_metadata=None + ) + + early_stopping = EarlyStopping( + monitor='val_loss', + min_delta=0, + patience=20, + verbose=0, + mode='auto', + baseline=None, + restore_best_weights=False + ) + + my_callbacks = [ + #ModelSave(), + early_stopping, + #LearningRateScheduler(shcheduler), + #check_point, + tensor_board + ] + + return my_callbacks + + + + + diff --git a/go_model/evaluate_model.py b/go_model/evaluate_model.py new file mode 100644 index 0000000..7be0b9f --- /dev/null +++ b/go_model/evaluate_model.py @@ -0,0 +1,122 @@ + +import os +import timeit +import numpy as np +import pandas as pd +import seaborn as sn +import matplotlib.pyplot as plt +import nrrd +import scipy.stats as ss +import SimpleITK as stik +import glob +from PIL import Image +from collections import Counter +import skimage.transform as st +from datetime import datetime +from time import gmtime, strftime +import pickle +import tensorflow +from tensorflow.keras.models import Model +from tensorflow.keras.models import load_model +from sklearn.metrics import classification_report +from sklearn.metrics import confusion_matrix + + + + +def evaluate_model(run_type, out_dir, proj_dir, saved_model, + threshold=0.5, activation='sigmoid'): + + """ + Evaluate model for validation/test/external validation data; + + Args: + out_dir {path} -- path to main output folder; + proj_dir {path} -- path to main project folder; + saved_model {str} -- saved model name; + tuned_model {Keras model} -- finetuned model for chest CT; + + Keyword args: + threshold {float} -- threshold to determine postive class; + activation {str or function} -- activation function, default: 'sigmoid'; + + Returns: + training accuracy, loss, model + + """ + + # check folder + #--------------- + model_dir = os.path.join(out_dir, 'model') + pro_data_dir = os.path.join(proj_dir, 'pro_data') + + if not os.path.exists(model_dir): + os.mkdir(model_dir) + if not os.path.exists(pro_data_dir): + os.mkdir(pro_data_dir) + + # load data and label based on run type + #--------------------------------------- + if run_type == 'val': + fn_data = 'val_arr_3ch.npy' + fn_label = 'val_img_df.csv' + fn_pred = 'val_img_pred.csv' + elif run_type == 'test': + fn_data = 'test_arr_3ch.npy' + fn_label = 'test_img_df.csv' + fn_pred = 'test_img_pred.csv' + elif run_type == 'exval1': + fn_data = 'exval1_arr2.npy' + fn_label = 'exval1_img_df2.csv' + fn_pred = 'exval1_img_pred.csv' + elif run_type == 'exval2': + fn_data = 'rtog_0617_arr.npy' + fn_label = 'rtog_img_df.csv' + fn_pred = 'exval2_img_pred.csv' + + x_data = np.load(os.path.join(pro_data_dir, fn_data)) + df = pd.read_csv(os.path.join(pro_data_dir, fn_label)) + y_label = np.asarray(df['label']).astype('int').reshape((-1, 1)) + + ## load saved model and evaluate + #------------------------------- + model = load_model(os.path.join(model_dir, saved_model)) + y_pred = model.predict(x_data) + score = model.evaluate(x_data, y_label) + loss = np.around(score[0], 3) + acc = np.around(score[1], 3) + print('loss:', loss) + print('acc:', acc) + + if activation == 'sigmoid': + y_pred = model.predict(x_data) + y_pred_class = [1 * (x[0] >= threshold) for x in y_pred] + elif activation == 'softmax': + y_pred_prob = model.predict(x_data) + y_pred = y_pred_prob[:, 1] + y_pred_class = np.argmax(y_pred_prob, axis=1) + + # save a dataframe for prediction + #---------------------------------- + ID = [] + for file in df['fn']: + if run_type in ['val', 'test', 'exval1']: + id = file.split('\\')[-1].split('_')[0].strip() + elif run_type == 'exval2': + id = file.split('\\')[-1].split('_s')[0].strip() + ID.append(id) + df['ID'] = ID + df['y_pred'] = y_pred + df['y_pred_class'] = y_pred_class + df_test_pred = df[['ID', 'fn', 'label', 'y_pred', 'y_pred_class']] + df_test_pred.to_csv(os.path.join(pro_data_dir, fn_pred)) + + return loss, acc + + + + + + + + diff --git a/go_model/finetune_model.py b/go_model/finetune_model.py new file mode 100644 index 0000000..6718a9d --- /dev/null +++ b/go_model/finetune_model.py @@ -0,0 +1,106 @@ +import os +import numpy as np +import pandas as pd +import seaborn as sn +import glob +from collections import Counter +from datetime import datetime +from time import localtime, strftime +import tensorflow as tf +from tensorflow.keras.models import Model +from tensorflow.keras.models import load_model + + + +def finetune_model(out_dir, proj_dir, HN_model, batch_size, epoch, + freeze_layer, input_channel=3): + + """ + Fine tune head anc neck model using chest CT data; + + Args: + out_dir {path} -- path to main output folder; + proj_dir {path} -- path to main project folder; + saved_model {str} -- saved model name; + batch_size {int} -- batch size to load the data; + epoch {int} -- running epoch to fine tune model, 10 or 20; + freeeze_layer {int} -- number of layers in HN model to freeze durding fine tuning; + i + Keyword args: + input_channel {int} -- image channel: 1 or 3; + + Returns: + Finetuned model for chest CT. + + """ + + model_dir = os.path.join(out_dir, 'model') + pro_data_dir = os.path.join(proj_dir, 'pro_data') + if not os.path.exists(model_dir): + os.mkdir(model_dir) + if not os.path.exists(pro_data_dir): + os.mkdir(pro_data_dir) + + ### load train data + if input_channel == 1: + fn = 'exval1_arr1.npy' + elif input_channel == 3: + fn = 'exval1_arr1.npy' + x_train = np.load(os.path.join(pro_data_dir, fn)) + ### load train labels + train_df = pd.read_csv(os.path.join(pro_data_dir, 'exval1_img_df1.csv')) + y_train = np.asarray(train_df['label']).astype('int').reshape((-1, 1)) + print("sucessfully load data!") + + ## load saved model + model = load_model(os.path.join(model_dir, HN_model)) + model.summary() + + ### freeze specific number of layers + if freeze_layer != None: + for layer in model.layers[0:freeze_layer]: + layer.trainable = False + for layer in model.layers: + print(layer, layer.trainable) + else: + for layer in model.layers: + layer.trainable = True + model.summary() + + ### fit data into dnn models + history = model.fit( + x=x_train, + y=y_train, + batch_size=batch_size, + epochs=epoch, + validation_data=None, + verbose=1, + callbacks=None, + validation_split=0.3, + shuffle=True, + class_weight=None, + sample_weight=None, + initial_epoch=0 + ) + +# ### valudation acc and loss +# score = model.evaluate(x_val, y_val) +# loss = np.around(score[0], 3) +# acc = np.around(score[1], 3) +# print('val loss:', loss) +# print('val acc:', acc) + + #### save final model + run_model = saved_model.split('_')[0].strip() + model_fn = 'Tuned' + '_' + str(run_model) + '_' + \ + str(strftime('%Y_%m_%d_%H_%M_%S', localtime())) + model.save(os.path.join(model_dir, model_fn)) + tuned_model = model + print('fine tuning model complete!!') + print('saved fine-tuned model as:', model_fn) + + return tuned_model, model_fn + + + + diff --git a/go_model/get_model.py b/go_model/get_model.py new file mode 100644 index 0000000..11f97e6 --- /dev/null +++ b/go_model/get_model.py @@ -0,0 +1,91 @@ +import os +import numpy as np +import pydot +import pydotplus +import graphviz +from tensorflow.keras.utils import plot_model +from models.simple_cnn import simple_cnn +from models.EfficientNet import EfficientNet +from models.ResNet import ResNet +from models.Inception import Inception +from models.VGGNet import VGGNet +from models.TLNet import TLNet + + + + +def get_model(out_dir, run_model, activation, input_shape=(192, 192, 3), freeze_layer=None, transfer=False): + + """ + generate cnn models + + Args: + run_model {str} -- choose specific CNN model type; + activation {str or function} -- activation function in last layer: 'sigmoid', 'softmax', etc; + + Keyword args:i + input_shape {np.array} -- input data shape; + transfer {boolean} -- decide if transfer learning; + freeze_layer {int} -- number of layers to freeze; + + Returns: + deep learning model; + + """ + + + train_dir = os.path.join(out_dir, 'train') + + if run_model == 'cnn': + my_model = simple_cnn( + input_shape=input_shape, + activation=activation, + ) + elif run_model == 'ResNet101V2': + my_model = ResNet( + resnet='ResNet101V2', #'ResNet50V2', + transfer=transfer, + freeze_layer=freeze_layer, + input_shape=input_shape, + activation=activation, + ) + elif run_model == 'EffNetB4': + my_model = EfficientNet( + effnet='EffNetB4', + transfer=transfer, + freeze_layer=freeze_layer, + input_shape=input_shape, + activation=activation + ) + elif run_model == 'TLNet': + my_model = TLNet( + resnet='ResNet101V2', + input_shape=input_shape, + activation=activation + ) + elif run_model == 'InceptionV3': + my_model = Inception( + inception='InceptionV3', + transfer=transfer, + freeze_layer=freeze_layer, + input_shape=input_shape, + activation=activation + ) + + print(my_model) + + # plot cnn architectures and save png + fn = os.path.join(train_dir, str(run_model) + '.png') + plot_model( + model=my_model, + to_file=fn, + show_shapes=True, + show_layer_names=True + ) + + + return my_model + + + + diff --git a/go_model/pred_model.py b/go_model/pred_model.py new file mode 100644 index 0000000..40a87a8 --- /dev/null +++ b/go_model/pred_model.py @@ -0,0 +1,100 @@ + +""" + ---------------------------------------------- + DeepContrast - run DeepContrast pipeline step5 + ---------------------------------------------- + ---------------------------------------------- + Author: AIM Harvard + + Python Version: 3.8.5 + ---------------------------------------------- + +""" + +import os +import numpy as np +import pandas as pd +import seaborn as sn +import matplotlib.pyplot as plt +import nrrd +import scipy.stats as ss +import SimpleITK as stik +import glob +from PIL import Image +from collections import Counter +import skimage.transform as st +from datetime import datetime +from time import gmtime, strftime +import pickle +import tensorflow +from tensorflow.keras.models import Model +from tensorflow.keras.models import load_model +from sklearn.metrics import classification_report +from sklearn.metrics import confusion_matrix + + + + +def pred_model(model_dir, pred_data_dir, saved_model, thr_img, thr_prob, fns_pat_pred, + fns_img_pred, fns_arr, fns_img_df): + + """ + model prediction + + @params: + model_dir - required : path to load CNN model + pro_data_dir - required : path to folder that saves all processed data + saved_model - required : CNN model name from training step + input_channel - required : 1 or 3, usually 3 + threshold - required : threshold to decide predicted label + + """ + + for fn_arr, fn_img_df, fn_img_pred in zip(fns_arr, fns_img_df, fns_img_pred): + + ### load numpy array + x_exval = np.load(os.path.join(pred_data_dir, fn_arr)) + ### load ID + df = pd.read_csv(os.path.join(pred_data_dir, fn_img_df)) + ### load saved model + model = load_model(os.path.join(model_dir, saved_model)) + ## prediction + y_pred = model.predict(x_exval) + y_pred_class = [1 * (x[0] >= thr_img) for x in y_pred] + + ### save a dataframe for test and prediction + ID = [] + for file in df['fn']: + id = file.split('\\')[-1].split('_s')[0].strip() + ID.append(id) + df['ID'] = ID + df['y_pred'] = y_pred + df['y_pred_class'] = y_pred_class + df_img_pred = df[['ID', 'fn', 'y_pred', 'y_pred_class']] + df_img_pred.to_csv(os.path.join(pred_data_dir, fn_img_pred), index=False) + + ## calcualte patient level prediction + for fn_img_pred, fn_pat_pred in zip(fns_img_pred, fns_pat_pred): + df = pd.read_csv(os.path.join(pred_data_dir, fn_img_pred)) + df.drop(['fn'], axis=1, inplace=True) + df_mean = df.groupby(['ID']).mean() + preds = df_mean['y_pred'] + y_pred = [] + for pred in preds: + if pred > thr_prob: + pred = 1 + else: + pred = 0 + y_pred.append(pred) + df_mean['predictions'] = y_pred + df_mean.drop(['y_pred', 'y_pred_class'], axis=1, inplace=True) + df_mean.to_csv(os.path.join(pred_data_dir, fn_pat_pred)) + print(str(fn_pat_pred.split('_')[0]) + str(' cohort')) + print('Total scan:', df_mean.shape[0]) + print(df_mean['predictions'].value_counts()) + + + + + + diff --git a/go_model/train_model.py b/go_model/train_model.py new file mode 100644 index 0000000..063dca4 --- /dev/null +++ b/go_model/train_model.py @@ -0,0 +1,115 @@ + +import os +import numpy as np +import pandas as pd +import seaborn as sn +import glob +from collections import Counter +from datetime import datetime +from time import localtime, strftime +import tensorflow as tf +from tensorflow.keras.models import Model +from go_model.callbacks import callbacks +from utils.plot_train_curve import plot_train_curve +from tensorflow.keras.optimizers import Adam +from utils.write_txt import write_txt + + + +def train_model(out_dir, model, run_model, train_gen, val_gen, x_val, y_val, batch_size, epoch, + opt, loss_func, lr): + + """ + train model + + Args: + model {cnn model} -- cnn model; + run_model {str} -- cnn model name; + train_gen {Keras data generator} -- training data generator with data augmentation; + val_gen {Keras data generator} -- val data generator; + x_val {np.array} -- np array for validation data; + y_val {np.array} -- np array for validation label; + batch_size {int} -- batch size for data loading; + epoch {int} -- training epoch; + out_dir {path} -- path for output files; + opt {str or function} -- optimized function: 'adam'; + loss_func {str or function} -- loss function: 'binary_crossentropy'; + lr {float} -- learning rate; + + Returns: + training accuracy, loss, model + + """ + + model_dir = os.path.join(out_dir, 'model') + log_dir = os.path.join(out_dir, 'log') + if not os.path.exists(model_dir): + os.mkdir(model_dir) + if not os.path.exists(log_dir): + os.mkdir(log_dir) + + ## compile model + print('complie model') + model.compile( + optimizer=opt, + loss=loss_func, + metrics=['acc'] + ) + + ## call back functions + my_callbacks = callbacks(log_dir) + + ## fit models + history = model.fit( + train_gen, + steps_per_epoch=train_gen.n//batch_size, + epochs=epoch, + validation_data=val_gen, + #validation_data=(x_val, y_val), + validation_steps=val_gen.n//batch_size, + #validation_steps=y_val.shape[0]//batch_size, + verbose=2, + callbacks=my_callbacks, + validation_split=None, + shuffle=True, + class_weight=None, + sample_weight=None, + initial_epoch=0 + ) + + ## valudation acc and loss + score = model.evaluate(x_val, y_val) + loss = np.around(score[0], 3) + acc = np.around(score[1], 3) + print('val loss:', loss) + print('val acc:', acc) + + ## save final model + saved_model = str(run_model) + '_' + str(strftime('%Y_%m_%d_%H_%M_%S', localtime())) + model.save(os.path.join(model_dir, saved_model)) + print(saved_model) + + ## save validation results to txt file + write_txt( + run_type='train', + out_dir=out_dir, + loss=1, + acc=1, + cms=None, + cm_norms=None, + reports=None, + prc_aucs=None, + roc_stats=None, + run_model=run_model, + saved_model=saved_model, + epoch=epoch, + batch_size=batch_size, + lr=lr + ) + + + + + + + diff --git a/models/.DS_Store b/models/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c1fab0679216d1c22988c20c407116537e1b2904 GIT binary patch literal 6148 zcmeHK!Ab)$5S`SjX+`WoQ1FtAw+daUpa)^8dhjN!=s{(>+ZMY}cT2mq)LPjO(LeI; z?>LjBid1hRQf6TCCX<U`fK%x@n3fO!jG)_7rIqM-5dX5GJ(113y zz@Ld`$A4si&Ta)y%ZDzE@a#U%&ku+FZrDjejCc*Zov5EyDsLjUkY8L{ma-^|tHm3y zoeaG+NC*8|(7U4ES&+6vYo8}c=fe)di>Nj9OY28ToCZgnXD*}{%2AF|)V!+;Ua%G-2#d~1}n1NqmfbIu{ zO6Xe54C<=`8~S~u@jM|3+VqwnR2E%}nL!*u5hfMUqze1Q5GEb{%Eq}CGlM1_gkBlv zu`3JvLJ@j(^eY_>!ZpY(Gr$adWnjT{OH}_)et!RdP2wIizzob410sLy9oKM6wzke} wj%uw!t)r4qTxRei1r1$_F_ud4CaM4vWijEum6n$KX7Q#xqk{I)qXhafAV6F&dfJ6`3X!DZbTcP3rLgrKcQ%o zpl`KK>kQVX9d?kgzRC$rI)HHhhoZD=VaFl}^x+&RRsDS(y)#0n#mE&Mach> zoXjM~%&9aYHD7S8vMQ%S#3as3bLM7u0dWB#99=;+0cikIas5q%pvvtTmHXJ7HGo<( zkK50+%6pE0a|n!c-jxX;!At$ESgSHGuqJO*jjBnOUg|TyGDz!4e}&jBi0+YPvhqT| zsqJe&db5BSRnRk@{3{XAv}XZtRzVd!HlFK9dxf6Z;Ju=}f0%o>=idIgH(1?$r~g5= zHpiqrlCyz*zw&?ef76gw`A|x$_zy^d7s9ic-ST0$cW*1)yAR)k9Z%uAY*ao}oV2FH zVMcj)T5>iE@7#IQJ3Nm0I3)3KNEwt%nBXa8<<5n3oD~z^E62E?;b~TEW5)1Qg;7XI zFvp>SYaiwKC7mAUjD+}&b#R8W5~c}H`+1&FoT)H`4q`&Xco<&bfUzvjMwhD?GD=Tq zPc1GyjWe!Xvq{>AaQSe)>X7GQZF%X52RweNE|offFlXU#l7TX>r(eL&^T z55wE@yK#F9*nG%S{OtC^X1}3)!m@?)Ud&FI%3Hr9)1Xd9k?({Xr+7pmD+h-ifsUqpB?x~HB$ zKZ*Z=(86RinaJjHx$85iDXpUmTZJrz11$++mrUx= zt}B+UN;CJoEQG&U1#CkGT9Fn3p+h`L=2gntpo_ddE@@94KD#N6&@*;Rj+W91OHKZ} z#y$Y$e>>fAp3-iAe1x;CdqPjfc{b{fV%}r4$h-TL7LRghw@?&aaM}= zz+4^U;E)B9xheF#6c*eJSec#`!Y*(|a4GbH2y+C95gv#CPtJx(ejIIF^4mv{u5!EH zp32Z$&^l#x5>+~7fiU@x5bs?Jl@~lt;}d8n%#jw?CrjjStYj{r(dIS(lyVjiO4g9@ z``2Jx6iGZ#SG&AYoa#_iY8S|sT!Z@&Y;cm$k7Ws#n?PJR*!uO+8XR~1t~vh$di_>6 literal 0 HcmV?d00001 diff --git a/models/__pycache__/EffNetB5_model.cpython-38.pyc b/models/__pycache__/EffNetB5_model.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a9083e88856d703ae7bdc948c59d744881c46d55 GIT binary patch literal 1881 zcmaJ>&2Jnv6d%v-%+AivZa&&3{YXn_)1pOcwn^2B146V3je6*+q9O-NXz|WCn`!Ow z)b^$%+ML2Q{{SaAA};(V+#Iq+&0)foP&p$suKR>^(uiEX{gYWx4fBxoI zK)>6?>CcD7=P=YCKr$~w%)`};uT*l89d{7bvDZ{;0q21fG^^U{1U#z=kOe#$MgI$zRa)SD{emoU03l{ha2n~ zU%(3vN9;Pkfp0k6WFPQFyy$SuKIAv?O@~|T7GJ_k4kv(bUuXc;hHFRR@Y7PjC^~{H;pHZXiW9}}H!_l4wy z<$FZuq5$>r3;KFY3!M`NtNNpiaF|<9ir&%J3{~D2U#%jv+&SA+6^b+!TU4+mTdf=WESl=buej8YR8&l-2gkH zNt`tg{rwi;=DPQFyq{!fk__V4zks!|v$plJHK|X6tTjSea##cVTR$W}dUl4uxBE{Z z){QqbA8l)8T6vMOaYiBVrCpR`Jy6@EqU_tA zEBBy=-Xic{*EgZlp-cl}3(C}9I~BZS`H?CUku)7klJ0^PbtaTDO#u<*`5u*~!Ks#c zs=94?0o0kGlzE{|Sm}}p@MLI-Eos@Z#FnoK+@&UDWS2_Ns3pk@GGvfu_V(NYHRhbi zT~Iw|k93AM?FR3kB`-VX$)g4~H`~_0!PdBdw~9jEurf(jCGG$!fnw(+&;li7YH$R% zwa|nOQbv@b;NMcxEO*D12`ah3nfa3^d!X;Hes3FG+Z%4bB1O^LqkG$;81+WE9!Oe> z-qX#yP*3n6rSGow`*)v9@sg&x=Txa4WVg%f%7o=|*CqTZ)&kt2J^|u2edPXkVG#Kt z>Y&)~plOsG(K?#;JGB@^e&pAkWc^(jM!sBuv)zx3FW>>in8Ou*QJSzMMMjkI%M5%= z$xz6`_^L27oamAX&GI3bV-d(!bFF0B)JM=bObo3+3Ax3@8$5r4mtR3UQOniiTx{{u znO$&cDMd<^f|j$K+wLT5V5Ef1^rS85|4><_MwLCb2gt~hrX?rIDP?A(`A_Q0+45A2LaD-Y$gmArGlq@=2_%}&l(;p^uL u%UO_d#pL4XQtlMF2fHTv6#PAA^fOypY6(b$sy}gaf-a&2Ey6hU2Y&{}k-(DI1IxPfLm2~Mo6iJQRPGG#5?vTI)1 zW^;Is&Et8tfETRHFBjPje8b`Z@Dg5P%Xpcs;1#xtSJ_Q`lik9%?0N{c*6^CeQF)u) z!FMc<%e!nHuUp&zd=KBVI03wYH!N-fzK`z%PV?3e4t|iM7fz>r1`#@Y9i)S2Jf~&H z(`mtmeZ~7k2y&$3l9Rj-BE9&Oks<97MV`=#3Zl4xZ7WYoJ|N}8Ly!#VbI!}68t(Ku zp$>O>b-2^h=`-@(gTQi<|7d9Iz0*~TpBX69##|hv@&-o@fZsus(>?f~&1HTjNcXH62`qLn94pE+- zdJswSQ~I-GIFvwZ?=fUOdQFSrNJ-r+s;nI66iT2rlX|TBaztu6Nd`pH4Q9&(>6mc_ z2^h1&fp&rGO-QDSLz}p>@8J^95Ri_mLu*6o2(k>u7XeRBMEzMfcys-LG2eXC1`t^$=x?te1FS{eS%kE(Gl2lc9LMJ0$4ZFia z^#!eY_vzQ0&`J=2RGYhddz;S%KcJcFT2rzMy#O^mJ8`Q%(&<&f8rYYg0C5^Fvj3Yf zh}{siQR24IJW4OA+x8L^yRqwAP5(_8#;(|gy`A^8%fb0-%-{gGs&!bCDkoCAbq*Iv z$$*Ordvd$d3yi3`z7MaZ? ze*fAk+-EI#Mx}(OW~(r-lJLMw;TvKa`9Dm~3Sm|&ruRi`NW+lJ(y`^#0-3&j?bM5! zl%fq9`0h0gQbSc|wgIG=GnVHKSun&Duf0l}@hjv5SkUpO5b?OApP5pY4}ipI_NSJo NXbGig1IESg{{t;x=+poJ literal 0 HcmV?d00001 diff --git a/models/__pycache__/EfficientNet.cpython-38.pyc b/models/__pycache__/EfficientNet.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a622f96a5cf50d438e28f4a35410aeee4d1f288e GIT binary patch literal 2199 zcmZ`*OK%)S5T2g*&dzL{jd2`DNOK}u;drGyE(rqc7$bqO5Rc0UwCdgN*`0V^>FJHL z_UtKejFdkBf+PQf6IX71LgIq&oDfJ<_pI%Wo%Cv|yXvc|epGe!ejNm!2JM~S{=+}C zHSJHyEH?v~58Di5qwpRG_BSN zI?!2%=yNOgfir;HAm-d6*4(a9Z3Dka(A>#QV$7XdBU17S*J`VFYD7%pyfEi(eg_a2 z5W>*~WF3$eAT`%tMF^_hfl<4U&3Ox`HTSsvT&un32u2Qpeld1sU`X&ne=XLj%`>dc zTXm~$lPfRuxnCQkbD+OOYy-S|WQAP)UcaI3YTtVEfEaZ!FrNG^5s-A}0dLnq9XvLk z>qvWvp4ec0Rat+tuSQQ^+1iJHjU$)^6U7tWsX+2%o%4H=SC`sVE zVp4u0tHFs}SubEvqBZ7A5&#h`2p1HiVM>LiY=zyJGPwyREtx5aF176@uY^@Hh#r)i zX_i4D>ve?Pq$gXIYVK(c2!A=`Y+X9)N{xWf7*EqhO0sJpi@YJO=s=w`+mK4=8M`Ta zOUZ<#CjUia?}G53?f#_5Xn#04z)LPTY}+<$u=S9SR+`W7P#3&-z{`;* zsSZ?k4R#ym>KS|cOcv9sP&?azAP*prtWaYEK93bN6C4WW9lPo!cGQwP`ae2m6K3^4U0UE7e zvd<`I$*5xT-$C^DB^Z}wnvB$yt}Y~{IvEw^C9)~k;JydDoTl`BnT?f9Cl^k)`T1xa K&b;}pI{yPnm|FD! literal 0 HcmV?d00001 diff --git a/models/__pycache__/Inception.cpython-38.pyc b/models/__pycache__/Inception.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bef2d26021f29a8c5763db22afb043d8594323a3 GIT binary patch literal 2433 zcmZ`)-ESL35Wnv`pFfhiPMWqLElDtW<%-Gw{aW!`nR}@k0Iu)CXqwY{zNZJMHew{AOlncjo54sa9PDp7%CiVWX-j z{|ILG(Si8{2LA&lq7XGuykb>7P4c>D$kp^rShc_kZO>MPjvhE+$t#7f=Z0mkEM*4Z z6|W*`GpL3&uO?|Lm<#8?n)9a+r_ye!x%p*GXwcMibB9Kp=JpPYlX#fs^?Nw&4%#sbao``})Q=-jcOTQI zLmH(%4uCVZ2o7Pb-}58P4!2p%Ie_5d5Tvz2y4CFt8SV;+^KF+VV^8JJqcO9)0nnu> z`-t*3O?TJt=Qj8IVeFHxDWV-ebFf-4wqfv}RYl1(qV8*~MzoO`Re;lhyGD$WNz9Rz zshI`*3QMyiWHPcCHUwf&tHSk^{mr3I%?W(e)d|w&Wh@RCt?o0KlBQTKFN42z^)w0?X z_e4__K;8lShO}p6`-L~yfA&`U@1n*S6Z@&-{hnvl-zvW=s*+WqycY2u7eQ_}(WDMm&Vb#G8D>%gcchPplwppx+ietzV&$k8Gc@FM6AjWd;hU}2zR+0p z;}%YQ7)juFWySKAbTHk0cjNsuPHrOH?V`2STem)Jp2uO|NYvh}*6p>8=89A+qPqFP z`gw(0x7P|cQfZ6z`KF}A@oBFI4$(1B*$CaZfj;f^{H_n*2wVe42Yxz0#P9Ve0}%@F zA!WQcvoQ9fWSDmN04Ee3N69K?3?E825l!=b?;A792MS|0a+Y`TehL&GU{IECs}7&Ln26dH`eMDuSN zxqU$W{vhRf8Ll%J654@c63jGBxt+l6!tJrLu&cn--;mE=AN&8p9=XA2Fob^Ro}Im^ z<(Blpa~nD@IO9xgdn(%A={!u$stn=GX>Lk`+$!|*#RoyWhl8zW@E!E&S49l#JInxM zCXU9$iE^u`u)GA>hK{`#(A-QIWE4(53`1z)&AGfZtrfN?jGRenLaXgv1H=so-i{#2Qi)vxw&zN7o;3|f{ zZs;FDcVV{BxVTv$e%+iofO<<8>_eI|zsp%k!0%pwXFKj|l`5}8rgmm(i5Q%Z;DEz` V-W8?EMP)dU9`RPx28@~4_#bWro*n=I literal 0 HcmV?d00001 diff --git a/models/__pycache__/ResNet.cpython-38.pyc b/models/__pycache__/ResNet.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..18b7a111b67a177fe73d88c47aac3a0e6c75f27f GIT binary patch literal 2401 zcmb_eL2ukd6t=y#*X#9el1-YVO^XbdXr(mUt_X(~RkV$Q1T;b^^k50CCYjm9jXic} zW}7DQR!D_or3!K22sgMO#DOzMg!lz>LV^SN3kW3MjCYeXZEx7xH}Ab~-kUe`-aPNe zjfQW)bNSbQ(GN|-_(KV&p9%=?z>og~1RK~)jHn#V$kK8pvi0ah4vbdfq;BMzYEC7o zre5S}-cD+%ANiVhfUigOG>C$<5jE0g)J!L$iF7iW)VK?FQ_+;>t4S-Jj;1y5B{S){ z=$z(j$!t0o&873ve0n}QpI(SAqzlnPdNH~PcoPS>@tGN2!scD0J#hrF8|!UTI=h4+ zu9!O+CJ8XJ?u)8XQJLu7|URKuW>>n>ALIABvVJ_{l(5ph*IkoU9&e{vjJF)M>(Mg-qYJ;= z@Z-OjhEZ79+_hK}TSJEiAXPxRfbF4!ouONpg$wc$HiuPe%x0t6!j-2a5X%ITX_1ZHI@qx_A{0P;TL`p z;F+h^uu)WSZP$8cvOajJ<8yfSbL+gZYJ3t5o48Umd%-jF!3~9ht~G3mM$s&q_k$;v zX#lbT_Osgl+mZd;OY9%N(*6rNGs4t*=4id|i^kW@Zw%8Y8c^Kx=x0c$AHq}C#IhY#3g?Jd%017mJ+`$NfHGP?ceV_6g%lVZ2P1 z!^Pu*TfBbkD5m*9^!N_SNq9)}WyBcT*Df^@VkA(g;nGGMzEAe|GKNF+Tug!?%3YGeQnXPlp>G##CriI!HIcUDhA%BNSO!&OEV1 zTOxj_uc|(bFk@kVKtY+6%b&u>`Y@*8kdIF1TDUm6ZHr65W_^*ON23GMo82H{!04&7 zFOM-yRr@)0C?82LPN4xoUe?yzm80L`p0ZgWN2&qCoi1G;SxP^n^pJFeO#x}!vbsm& z?H$2oJ*Hd90F$1`awQz6M^^K)wXv46n^pwcy}?vtV3R=NzhEV4Gm;Ec-#XcP+mf#K z#AOv49k}F7Eq%aJl|Pv}5i=~_D4Ub>|@v%!XWis;R`WueI$XSzgZfrCYM|;D~<-#F+6+*Yr%gVgq$8yE2wN(;C13c<%opxtA(eo_$``CH%AmBF0C9(AHID&WBnxCW2zXV zxf8{*{<08gw+<~)YtjJ9W8 Hj>r0ch!%TE literal 0 HcmV?d00001 diff --git a/models/__pycache__/ResNet50.cpython-38.pyc b/models/__pycache__/ResNet50.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72a250d83164f14b05c06ff88714983ba378c2c8 GIT binary patch literal 1313 zcmaJ>%Wf1$6zzBSyfC)G7z~a{B&1>CiH$&6L{Y#a3=2jmfETstl)H>_35Ux>nzKj9l{%Zgt>ij-T`#IZ!=D)qhRblrNLd%Ir@23-%@`X7J({5ti#50;#5 z0gxZzQSZT&Ug=X0wcba8;~|Qik5S@$$}-gP+n6rux_3R)10T>n8=!$33+V;6gq9qR z0AEBG*)m#YD`KN-va9Fl;>@y^}dgWV#u=ltQ1jju=EVn0nrFBFk#bWDzCf=}mUG!{j}KzTLVuk@qla5Urj-t)W= zTuZEU{`eq&D#a^O=#g7Vjs6vXyPmt_ZNS266O5My{+a$M=|sRYKl(dKvcSa-Z<;_T z6Z3}Ea})5|BsJzGRwk%RlPZD_h2$ns+$=M!YbrFY4q+#YAhz{kO|^-pP?4tF>im+5 zH|WZLc>6WfsoHK&EN$}sXO!YOXd!LUNLwv3wkEL#zI9L9>X995>`%aKe6jp##+1tp zy8T52u4`HquFBOmluTRA`OzKOhb7TB=TA7APIvCffsMR%exxLd+YiEtjBcD8ESKz+ kY&o1UUjsGS_aIn9$q!ZowFM@F^x5A3Z2SHe;M1P`16q4_&Hw-a literal 0 HcmV?d00001 diff --git a/models/__pycache__/ResNet50_model.cpython-38.pyc b/models/__pycache__/ResNet50_model.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d4da8fbb48e8249bf662c46c3a28434911e26d98 GIT binary patch literal 1964 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 R{jtTdJLAUg60Gw-_y^XA6~X`j literal 0 HcmV?d00001 diff --git a/models/__pycache__/TL_ResNet50_model.cpython-38.pyc b/models/__pycache__/TL_ResNet50_model.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d2611793500c7173f7ffe0ced8c4dcd3e8554084 GIT binary patch literal 1967 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 OPOu5=EPU^Hz5f7N2}ET8 literal 0 HcmV?d00001 diff --git a/models/__pycache__/VGGNet.cpython-38.pyc b/models/__pycache__/VGGNet.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc9b22f19544300474b4d5e1c5b3e8ddbaa55069 GIT binary patch literal 2180 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 aYyhAFkzH>X>5h(f1K@SDE5tO+xBmruZ@n7; literal 0 HcmV?d00001 diff --git a/prediction/__pycache__/model_pred.cpython-38.pyc b/prediction/__pycache__/model_pred.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..184b4fa98717acb69460783155b772d89c751efd GIT binary patch literal 2776 zcmbUjU60$wahIYfiuyR6eD;0VP8iz>asjNfuW^$Y2e*mc0Ef6n0lR%zAR&0%mBbUj zvb(Zv^i zg)+{wfU_d!64-iCo(-o$)6!7#`020uc7%TZQ{+^TS^KbRTqi>OfDw}D5T96Ojp9>l z=Dflzz#0atmCh*^yAqw)8peA*5Yvz58hM0P^^_5$C_Yj=7qsWf7t8i@uo9q4<*J5g z4a(_q_{>7c^WkTT!7n(YaipujALiWO|A!AHAbBXHXShD*qiM=ADQ+873({c_rz8Ja zK?ig=^RHj`p$fwx4^!qpiRIWAQJDE*M*Z+<945X<;)wbC_Xe*tfOrsc?mx?l0qo(C z(UMKm;>b6+FJu@^7_IbGYGPgfl`^qVWxNPz49cMHD{ErTBAN3e#59q@&!@7O%0U&m&)CtEoI?rvUN-!sLq(dT!LB&G8Rq9)?bFh2TTULH+F9ZH$Dt@ z-w$ql6x{kCxcLWFkH$<$^6sN)2H60{IMOx)`zP-&1U>*T*!^R0>()yjrjs6h2ce9{ zfryV;1f%NJ5BdPr;W-D7xCF}xmFV-e8K!z9;7#-mn$*|OKdmoqXdLUMmgRc+5in&9 zZYccpN0`R*WOA+N-*c~+Pq`lOkHAYCT2H7(oE1qJG2^{zE$kLXjn=Gb z!Et}7G)?K=_Kw$HtAROnyQi#NKoNv;tn9-)&fsPNT@2+hF4#Sy`c+ zki*-Lsv2~=a%3KW;mW2wFH~a$N5#RBvQrjjz*!V5quYJeJjf{oBji#!mD}J_HG)(F zdRDhKUx%xRNhTPjnpLFXfVjptZB%P%Z16Vl^NV`XwL@A<@l@6@BMl%JJL+0q@Xi_6 zS-4EW@C#bQ1}w^3BzF^V@d+$wt&6*O3vXF2cCd@>nv45Z7x(KuzuA zST^vxcn$C#tRB2)K+Cqp;X4YMW&3S@6<}32 700] = 0 + ## normalize UHI to 0 - 1, all signlas outside of [0, 1] will be 0; + if norm_type == 'np_interp': + data = np.interp(data, [-200, 200], [0, 1]) + elif norm_type == 'np_clip': + data = np.clip(data, a_min=-200, a_max=200) + MAX, MIN = data.max(), data.min() + data = (data - MIN) / (MAX - MIN) + ## stack all image arrays to one array for CNN input + arr = np.concatenate([arr, data], 0) + + ## create image ID and slice index for img + slice_numbers.append(data.shape[0]) + for i in range(data.shape[0]): + img = data[i, :, :] + img_id = pat_id + '_' + 'slice%s'%(f'{i:03d}') + img_ids.append(img_id) + pat_ids.append(pat_id) + + # generate patient and slice ID + df_img = pd.DataFrame({'pat_id': pat_ids, 'img_id': img_ids}) + #print('data size:\n', df_img) + + # covert 1 channel input to 3 channel inputs for CNN + if input_channel == 1: + img_arr = arr.reshape(arr.shape[0], arr.shape[1], arr.shape[2], 1) + #print('img_arr shape:', img_arr.shape) + #np.save(os.path.join(pro_data_dir, fn_arr_1ch), img_arr) + elif input_channel == 3: + img_arr = np.broadcast_to(arr, (3, arr.shape[0], arr.shape[1], arr.shape[2])) + img_arr = np.transpose(img_arr, (1, 2, 3, 0)) + + return df_img, img_arr + diff --git a/prediction/model_pred.py b/prediction/model_pred.py new file mode 100644 index 0000000..d11c2ba --- /dev/null +++ b/prediction/model_pred.py @@ -0,0 +1,93 @@ + +import os +import numpy as np +import pandas as pd +import seaborn as sn +import matplotlib.pyplot as plt +import nrrd +import scipy.stats as ss +import SimpleITK as stik +import glob +from PIL import Image +from collections import Counter +import skimage.transform as st +from datetime import datetime +from time import gmtime, strftime +import pickle +import tensorflow +from tensorflow.keras.models import Model +from tensorflow.keras.models import load_model +from sklearn.metrics import classification_report +from sklearn.metrics import confusion_matrix + + + +def model_pred(body_part, df_img, img_arr, out_dir, thr_img=0.5, thr_pat=0.5): + + """ + model prediction for IV contrast + + Arguments: + df_img {pd.df} -- dataframe with scan and axial slice ID. + img_arr {np.array} -- numpy array stacked with axial image slices. + model_dir {str} -- directory for saved model. + saved_model {str} -- saved model name. + pred_dir {str} -- directory for results output. + + Keyword arguments: + thr_img {float} -- threshold to determine prediction class on image level. + thr_pat {float} -- threshold to determine prediction class on patient level. + + return: + dataframes of model predictions on image level and patient level + """ + + model_dir = os.path.join(out_dir, 'model') + pred_dir = os.path.join(out_dir, 'pred') + os.mkdir(model_dir) if not os.path.isdir(model_dir) else None + os.mkdir(pred_dir) if not os.path.isdir(pred_dir) else None + + if body_part == 'head_and_neck': + saved_model = 'EffNet_2021_08_24_09_57_13' + elif body_part == 'chest': + saved_model = 'Tuned_EfficientNetB4_2021_08_27_20_26_55' + + ## load saved model + print(saved_model) + model = load_model(os.path.join(model_dir, saved_model)) + ## prediction + y_pred = model.predict(img_arr, batch_size=32) + y_pred_class = [1 * (x[0] >= thr_img) for x in y_pred] + #print(y_pred) + #print(y_pred_class) + + ## save a dataframe for prediction on image level + df_img['y_pred'] = np.around(y_pred, 3) + df_img['y_pred_class'] = y_pred_class + df_img_pred = df_img[['pat_id', 'img_id', 'y_pred', 'y_pred_class']] + fn = 'df_img_pred' + '_' + str(saved_model) + '.csv' + df_img_pred.to_csv(os.path.join(out_dir, fn), index=False) + + ## calcualte patient level prediction + df_img_pred.drop(['img_id'], axis=1, inplace=True) + df_mean = df_img_pred.groupby(['pat_id']).mean() + preds = df_mean['y_pred'] + y_pred = [] + for pred in preds: + if pred > thr_pat: + pred = 1 + else: + pred = 0 + y_pred.append(pred) + df_mean['predictions'] = y_pred + df_mean.drop(['y_pred', 'y_pred_class'], axis=1, inplace=True) + df_pat_pred = df_mean + fn = 'df_pat_pred' + '_' + str(saved_model) + '.csv' + df_pat_pred.to_csv(os.path.join(out_dir, fn)) + print('image level pred:\n', df_img_pred) + print('patient level pred:\n', df_pat_pred) + + + + + diff --git a/run_step1_data.py b/run_step1_data.py new file mode 100644 index 0000000..a2c74b2 --- /dev/null +++ b/run_step1_data.py @@ -0,0 +1,49 @@ +import os +import numpy as np +import pandas as pd +import matplotlib +import matplotlib.pyplot as plt +import glob +from time import gmtime, strftime +from datetime import datetime +import timeit +import yaml +import argparse +from get_data.get_img_dataset import get_img_dataset +from get_data.get_pat_dataset import get_pat_dataset +from get_data.preprocess_data import preprocess_data + + + + +if __name__ == '__main__': + + proj_dir = '/home/bhkann/zezhong/git_repo/IV-Contrast-CNN-Project' + data_dir = '/media/bhkann/HN_RES1/HN_CONTRAST' + out_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection' + data_exclude = None + + print('\n--- STEP 1 - GET DATA ---\n') + + preprocess_data( + data_dir=data_dir, + out_dir=out_dir, + new_spacing=(1, 1, 3), + data_exclude=None, + crop_shape=[192, 192, 100] + ) + + data_tot, label_tot, ID_tot = get_pat_dataset( + data_dir=data_dir, + out_dir=out_dir, + proj_dir=proj_dir, + ) + + get_img_dataset( + proj_dir=proj_dir, + run_type=run_type, + data_tot=data_tot, + ID_tot=ID_tot, + label_tot=label_tot, + slice_range=range(17, 83), + ) diff --git a/run_step2_train.py b/run_step2_train.py new file mode 100644 index 0000000..c954a4e --- /dev/null +++ b/run_step2_train.py @@ -0,0 +1,76 @@ + +import os +import numpy as np +import pandas as pd +import seaborn as sn +import matplotlib +import matplotlib.pyplot as plt +import glob +from time import gmtime, strftime +from datetime import datetime +import timeit +import yaml +import argparse +import pydot +import pydotplus +import graphviz +#from pydotplus import graphviz +from tensorflow.keras.optimizers import Adam +from tensorflow.keras.optimizers import SGD +from tensorflow.keras.losses import BinaryCrossentropy +from tensorflow.keras.utils import plot_model +from get_data.data_gen_flow import train_generator +from get_data.data_gen_flow import val_generator +from go_model.get_model import get_model +from go_model.train_model import train_model +from go_model.train_model import callbacks + + +if __name__ == '__main__': + + out_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection' + proj_dir = '/home/bhkann/zezhong/git_repo/IV-Contrast-CNN-Project' + batch_size = 32 + lr = 1e-5 + epoch = 1 + activation = 'sigmoid' # 'softmax' + loss_func = BinaryCrossentropy(from_logits=True) + opt = Adam(learning_rate=lr) + run_model = 'EffNetB4' + + # data generator for train and val data + train_gen = train_generator( + proj_dir=proj_dir, + batch_size=batch_size, + ) + + x_val, y_val, val_gen = val_generator( + proj_dir=proj_dir, + batch_size=batch_size, + ) + + my_model = get_model( + out_dir=out_dir, + run_model=run_model, + activation=activation, + input_shape=(192, 192, 3), + freeze_layer=None, + transfer=False + ) + + ### train model + train_model( + out_dir=out_dir, + model=my_model, + run_model=run_model, + train_gen=train_gen, + val_gen=val_gen, + x_val=x_val, + y_val=y_val, + batch_size=batch_size, + epoch=epoch, + opt=opt, + loss_func=loss_func, + lr=lr + ) + diff --git a/run_step3_test.py b/run_step3_test.py new file mode 100644 index 0000000..5ff59a6 --- /dev/null +++ b/run_step3_test.py @@ -0,0 +1,64 @@ + + +import os +import numpy as np +import pandas as pd +import seaborn as sn +import matplotlib +import matplotlib.pyplot as plt +import glob +from time import gmtime, strftime +from datetime import datetime +import timeit +import yaml +import argparse +from tensorflow.keras.optimizers import Adam +from go_model.evaluate_model import evaluate_model +from utils.write_txt import write_txt +from utils.get_stats_plots import get_stats_plots + + + + +if __name__ == '__main__': + + + proj_dir = '/home/bhkann/zezhong/git_repo/IV-Contrast-CNN-Project' + out_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection' + saved_model = 'EffNet_2021_08_21_22_41_34' + # 'ResNet_2021_07_18_06_28_40', 'cnn_2021_07_19_21_56_34', 'inception_2021_08_21_15_16_12' + batch_size = 32 + epoch = 500 + lr = 1e-5 + run_model = 'EffNet' + + + print('\n--- STEP 3 - MODEL EVALUATION ---\n') + + for run_type in ['val', 'test']: + # evalute model + loss, acc = evaluate_model( + run_type=run_type, + out_dir=out_dir, + proj_dir=proj_dir, + saved_model=saved_model, + threshold=0.5, + activation='sigmoid' + ) + # get statistic and plots + get_stats_plots( + out_dir=out_dir, + proj_dir=proj_dir, + run_type=run_type, + run_model=run_model, + loss=None, + acc=None, + saved_model=saved_model, + epoch=epoch, + batch_size=batch_size, + lr=lr, + thr_img=0.5, + thr_prob=0.5, + thr_pos=0.5, + bootstrap=1000, + ) diff --git a/run_step4_exval.py b/run_step4_exval.py new file mode 100644 index 0000000..a818403 --- /dev/null +++ b/run_step4_exval.py @@ -0,0 +1,88 @@ + +import os +import numpy as np +import pandas as pd +import seaborn as sn +import matplotlib +import matplotlib.pyplot as plt +import glob +from time import gmtime, strftime +from datetime import datetime +import timeit +import yaml +import argparse +from tensorflow.keras.optimizers import Adam +from go_model.finetune_model import finetune_model +from utils.write_txt import write_txt +from utils.get_stats_plots import get_stats_plots +from go_model.evaluate_model import evaluate_model +from get_data.exval_dataset import exval_pat_dataset +from get_data.exval_dataset import exval_img_dataset + + + +if __name__ == '__main__': + + + out_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection' + proj_dir = '/home/bhkann/zezhong/git_repo/IV-Contrast-CNN-Project' + batch_size = 32 + lr = 1e-5 + activation = 'sigmoid' + freeze_layer = None + epoch = 1 + get_data = False + fine_tune = False + HN_modiel = 'EffNet_2021_08_21_22_41_34' + saved_model = 'Tuned_EfficientNetB4_2021_08_27_20_26_55' + run_model = 'EffNetB4' + + print('\n--- STEP 4 - MODEL EX VAL ---\n') + + # get chest CT data anddo preprocessing + if get_data == True: + exval_pat_dataset( + out_dir=out_dir, + proj_dir=proj_dir, + crop_shape=[192, 192, 140], + ) + + exval_img_dataset( + proj_dir=proj_dir, + slice_range=range(50, 120), + ) + + ## fine tune models by freezing some layers + if fine_tune == True: + tuned_model, model_fn = finetune_model( + out_dir=out_dir, + proj_dir=proj_dir, + HN_model=HN_model, + batch_size=batch_size, + epoch=epoch, + freeze_layer=freeze_layer, + ) + + ## evaluate finetuned model on chest CT data + for run_type in ['exval1', 'exval2']: + loss, acc = evaluate_model( + run_type=run_type, + out_dir=out_dir, + proj_dir=proj_dir, + saved_model=saved_model, + ) + get_stats_plots( + out_dir=out_dir, + proj_dir=proj_dir, + run_type=run_type, + run_model=run_model, + loss=loss, + acc=acc, + saved_model=saved_model, + epoch=epoch, + batch_size=batch_size, + lr=lr + ) + + + diff --git a/run_step5_pred.py b/run_step5_pred.py new file mode 100644 index 0000000..cd3db34 --- /dev/null +++ b/run_step5_pred.py @@ -0,0 +1,39 @@ + +import os +import numpy as np +import pandas as pd +import glob +import yaml +import argparse +from time import gmtime, strftime +from datetime import datetime +import timeit +from prediction.data_prepro import data_prepro +from prediction.model_pred import model_pred + + + + +if __name__ == '__main__': + + body_part = 'head_and_neck' + out_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection' + reg_temp_img = 'PMH049.nrrd' + + print('\n--- STEP 5 - MODEL PREDICTION ---\n') + + # data preprocessing + df_img, img_arr = data_prepro( + body_part=body_part, + out_dir=out_dir, + reg_temp_img=reg_temp_img + ) + + # model prediction + model_pred( + body_part=body_part, + df_img=df_img, + img_arr=img_arr, + out_dir=out_dir, + ) + diff --git a/utils/.crop_roi.py.swo b/utils/.crop_roi.py.swo new file mode 100644 index 0000000000000000000000000000000000000000..6932cd83404f80a82504a2d4e0d92636d24876a0 GIT binary patch literal 16384 zcmeHNU5I2y6~5UdMzh%%&H5ryD!oYGmY$xUpe_SVT$kNscXYGt%c@S~^K~RJ7JN46lJ2P)S zD0ShR+g(4W&iU%pIaM{?Ub=YZQ|x$SIlyyY5U`uCTxMS!9XfvcaM12w;Zjn?!H;RT z(9b$r%D#=CHVqd?V$@G%Z?PAvRxYx1@yx}Alc`iWFVw=xbLSS$=jjuoqZUgQC&gkX zPqS8@#*J*auV`Q48So4o#6YRK3&)RaKD4sDM9q#>9%T2wck`egzRok?8So5v20R0v z0ndPEz%$?(_&;MnWw!^t;42>1c;Jn$8u3p@@y415eY47_1AoQ^ z$eX}5;CWyRcmVhn2>b@H3A_im1Gt7ulFtF9y)0q$T;Opbiq$}W!JjY^nUGN|dn}c# znhRCtvZaQZsMc88W$je;Sv=r9!MKcA!rLNgjC-GoRbS+cvm#bkY_qgYKcZkM70Ysv z4H?gKK4g)|<82YKZk`TUte6&ZL60}3Pg<(1RIP5BpnXdZ$HGG8HI{K`qEcq6=u^SB zh0%z~Jdeg&&B`#!A1K2#nUOI?gc*O8S!1hYYs*+!B{Qqq%9ggWI^jnDex&I0Ot3JC z3sqz0i&LEHGw@*sXW?$04eM-F8=FA}w^V9KXpweKlk=7eLyxZM#)+7W?Ye5&dv=|e z%x>u#3#nT1c(hF!KUoUmpDI`!O~#41;)PI7uXB7L$Z2e`X?G&;+4q? z2pK0Rh=iafqLpTbJZ3_G4nA0rW@dG zoqKHK)aos+yhE|n_G}9V-{c7;qlD)@9bUPdBcjgQr9!~kMs+dZNrHC|8CfD0bEB3=XhjQV>AN4u}4=6%6m=?=UE|3;iGiGS$B@~N zyC}zwf$_38KqG7@QfW2`v?O#Q?+Gb#uEcoaTePlPG8>u=*4$D=Lbn-#Qn&UYn!|DG zMtW;jDwMRfui32a+!JKvdEL1VI%&VrE%hIU$EK1*$PmH3)O(>2-D)+4bh3gBf-j)Y zCJcma#W5c%%rs@&JVDx+BnvAKof-qa8)_-bkTQs6JSYdIJ$XBM>hj+Gkd1Y}rTzLX zP4$CzO}GNs>0?u^RoBKLG|@J{*{tYoBsKtKuKq(7?Hdanc_s=P%4}=e6;E#rv@o|Y zn-teu_FFhB#eH+5UJyJokvECu71{*TCfAHutyZ<%@ha7>kLWzHN+Ls!o#WE-)Mw-@t-_pr&I+Pn-EMtMLl^P8Nt`)%Gx3!A z(B6MoAJr&$m~a)2P_R4c>kPStJvg?>JDnH@t$2{7*!JK!M;jkgLqBGB6}NCv2R#R` zx4_C=$2$>cHr6N}kkYQg##i`j=8DpcmT;Q8?^Z-w81>R32k5ezzaYM^ngc!j{`@6n>gdY3VaiI3HSnV z1$YSf7tZ$I0lo!%7I+F60UrcD0MH%4mvQdD5BLqv`&WS_;BMd$@JF2WZvf8$8^8m= zFLB=gIcoINJdfDnHX(0+n;qyGaJ0r+>AO4FnW{sZ!uCV2n= literal 0 HcmV?d00001 diff --git a/utils/.crop_roi.py.swp b/utils/.crop_roi.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..c743cfb1a963524f4dc39ac473427cd9b03cf8f4 GIT binary patch literal 20480 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 literal 0 HcmV?d00001 diff --git a/utils/__pycache__/cm_all.cpython-38.pyc b/utils/__pycache__/cm_all.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..851c2667dfd3bca66b1771e6269361cb4df9e941 GIT binary patch literal 2155 zcmah~&5zqe6t~At?0kQ0ve{&J%OWa7E)56Zu&NMJODirFQpLprSzga1>s@>7&P=x5 z)tE~`oGSHCNG?bmkT~+ERCDFD{{rH`n@Ng-RD>eA8;xEqH#ydGu@3 zvi{J_?t_5&0zUp54A{bUY6Tl=2gvYF;2K&9s)l-*AJpuPPCclD45f{%88ow2&@yr- zZD;MEz18Ui9nf*pZq^HWSwH9-c_rP;2Eia321CPFf!`1I4fX(!f>Cx59Ax8QY-D~q z$qs`I@t#CbAL;z%Sp4H+qNCR8`h>3qqxB#Q(~UV!y>6vs;z z#n&N^b4I3)s%t}Wo?x;z|I!e7gpJj^@rAXCuLJMCAxIk z6YQ2v>f#DW8mp>ou5H_Tc3*faUpByI16RuyeFK&~u>3~)s~Ywf_S#<6!FpS^cRaQK z(~|?9et)N@pLaafz!L>e^-J{HW{>{IR|B$YKnxv|RR^-VTXtnvx|c4@{_tJiUfa+5 zJN}wwOEhIoHeRB#Cwr?FZmn(EyJ%wbYIn$DK6-+|x60 z7x!K{<(}-}${05C*eVBrfoi@SZn2i{Z*{bMB=_ZLWWhLqaR?u9;Nbuy`OZ|=aM8a`1@k}Xsvjm}7_s@EaxV|;7F zwq$y^sm0kzl0^%0s(J;S6GBCpk%)p`ex}e@4{y4lvHf$Rg{P|fZ^fLe=EiPlPTWmr zcSg=nl{ZiGNPPAQoEA5Fk?>pHscsz@KvKoYA`x6UEPAe7nzL-`Dksi9R$iQi;<_LZ zJ-LGDeI8wr5GQOpRH(qpXC%U5%&!!J7+i?zT)7u{LX|J_kS??0T2-T>AQUS^3*|*T zVl28=-a@yFvuow*RFwzXX#&kgpJ155jZ=p&Gv!h8Tt^I2m_jkrhgww%mQbObo1|bZ zoy8r9WeFV#f@SY%4Nsi2P#50Uz|_)+3SBHO_B%xTc}VBDL@rV7RSmF1q&QOS>Qsu`;;AnJEptXFBba}$e*XP^E@N7v-8V{(%B8UInU{01}kU! z>g*vQ#eM3a7=@_Y>25o9VsHTw_!V6A_H~ zWlADO?`1hAX~;=RVtp;KidL;^)y_XE1n#Ano6Tl6C97#AefzV99^m%3f|e=sz3ug5 oHlj(*tk=%)2$Yq7AN(#;^0{`$k6_pq2MV?+x*m1$2X?daH&RegKL7v# literal 0 HcmV?d00001 diff --git a/utils/__pycache__/cm_patient_pos.cpython-38.pyc b/utils/__pycache__/cm_patient_pos.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..21892db96ab403bf14a9feb17e56bc197e8c2e2e GIT binary patch literal 1874 zcmY*a&yV9Y6i%EpNz>occ4oS}h($t3J?!)X2qA=kT?y@h5z+#O2oy4L+GZxdu-(~h zm7W%k`#*G!-1s{-i?xL`|#=BD&$Bod8@910etMJN(3LSAL5kfSeI zxv8G5H_K&~JW~|ROyex#JYA-7B+{}7SyGiu$YER-%MDjd9toDdfc1V9#~T*K+mOd4 zOXikrvoa3BT_@6tHnBXeSQ4jt6+o0_cavD_R#_>+IFBvmfAc$rbkQE8@!w6usBs-v zggp?(!4%}uI@mR#hZb$E%p3FI+!%Yiwy85S4lZyD*ah~0 zot3e7Yv+ma!^6E-d$oCGvmN#J?ZT$*eYw%(W6(^z9%Mi?sRZA2e9;!$wE( zqk346P7L&Y^aC`E;MEbBp&A2^>k&T815dQKV>*JjV~r;oPwH_!IWcG#I#bP0H9r*- zI#zzcKfjTkMmGM|ow~QQkf@V$XWkTVM#>8DvhtQM3RzA z8%k?wUzTYh-IaPt7h7rPNmNKrlwq;StF3f_vXl$yL{*g(RFa~SZp0(TqAl~3%TC0# zUl!lM%OZKMP6HMep>OTdiuIt!)WeXiilsQxvGmvmZ&{tEE!HgI>oTM4fwqTOYEzKf z8)q8|wn>6EP7^W|1X#ZdYT&)5a2nhPn<)b-QYwP%o;KGM0J5wkSr(EANKxQ@$Fa0T z#n2Tju<_hK_|;&aW6j@RE!Jh8EH2hpQBf>*$!=X1s|BK~tI6W&caI*I18qF8+3-{nG+Mu8k$|JOKf1zbRfH?Z~5lC zVDFja_4kkG^-a&}Vpe^pjnpHeR^lT})t>oV>>UW%(wE5Q3OiL)(1^3Q)xhORbWt)y zgBNmyf~cxlmR`VHg`j*Vttu1JUS;J45*;O(sU&48;h3$BDl!$p2}f2%tT@X{nq(nQ zGQr2p#|c literal 0 HcmV?d00001 diff --git a/utils/__pycache__/cm_patient_prob.cpython-38.pyc b/utils/__pycache__/cm_patient_prob.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77ad0a4fd5cc631d4d62d92e5f7f1bb3055c1e86 GIT binary patch literal 1835 zcmZ8i&5k2A5bkz&`X`hBWOinDcBK*GlEcni03n1Buq&axj1Vh0v_PTLc9NdyKWt}q zC+eIQj{6SDksHszBXINyiPO9S;=pH5gBH@V>#NV@vRy7$zV3G07Jl>YZ{I%#`a>&k z{|J;%(Znz4s739}iteQy5u-biYh)|(jP&y$3hjHFcGLz(vV*)6b@Fc1HN2Dc@?O+y zZ2D0jHf}b^htV(}MI*zvvT;6%Ci!7>Xmk(ygXn?5KJZa=lpjaO`AKwQxFCC&pGK#^ zA#ML?MUSZc!kQl3Vb|8>lqheR%QR;y5Rxx6n%XL$v1A4lHIO_`i)LZyk&|82j))V{S9%>{_(7wDI-5duDxiwhii_wy#~jp}{sNTpEJ$cVPFnZT+y4;jUf#Sl5>ab*uJYlWkasyAEyd z?K-^b(tsYkv2o4TPeg`Sy#+b3POWVl^ZIq0cJ>5#P=|GUNobe$mgF_Ty2szb&-OqL z>kw=oryIO+vEJ`;q*tLsI(p-5I~Wa5EE=3x+b%Gq*0;R|YrfyuXns)l>%ozQzKgzx z1`WFzg6V4=cvKJYVH|jDb~&O$>~duA*x+$Js>eqb?Z9SY_=({sa!kjs5f?eG!$!6n z+0jy;v+gV$BC(jva)ykc=aq7G9&UOdO+q#-dHzhbvUtui<=q<7J7j^y zb*KDAR>ty!_mDkq{3;c9`sFHz$1jyzlsrdHA!S)1r>-Ii)FhwD+ls*>yMf0*#5Zh4 zQ+}s&4qiN(E>kHsgCsv|rk+*2oPRW(C{j^%z*$UZRhnF9Op#J37gj6fUX^K~{H1<8 z^IPTSEG|?a%UQ9`t6Sy8RmBRbNKq+27BT1XEf4foy;zw2IAV?673`%x1z0$Oy|YOx z-i004Pe8RQp5iEnD&T9pT78aCt~e8`GNb&l@rURcSCD2Ch?(ST7GW-=+6hAeyo0m-%l_HBm5eX>^JlrHvj;uJkf+N)HImc<>iwvd3nVWd9jvhCN8jg6J^|NIX_3N`v2pO zu!#5yo#h1f#71)lcE|2GE^uIb#D&&&+V;EP9@^xe@2?=}Ij}R|aU+dJ^gMio>!LD! z%-?}fEpt_TsH;>B#=8XFV6FlvTP<;hIpWe@|HIBv}yle6Nb8)2y^`=VR)#G^uHHB z&|OEHcTF(QoAP;|F#2fbg5K&r*T%ueSa+SVPxNx)5xOQE2vK0`(CS{xiQQ@a4VxSK Ac>n+a literal 0 HcmV?d00001 diff --git a/utils/__pycache__/crop_image.cpython-38.pyc b/utils/__pycache__/crop_image.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f84dff414734b80f46a8200dd348925175b004b GIT binary patch literal 2294 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 literal 0 HcmV?d00001 diff --git a/utils/__pycache__/make_plots.cpython-38.pyc b/utils/__pycache__/make_plots.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8f0924a7d4da36b2c3564972d541388bb44b77f GIT binary patch literal 3046 zcmbVO-EZ7P5Vw7IKA&^nmygRw+awg|K@%sJwn$Yepio356i|}#umV|*cat;m7wf&y zo_sG9@m7gHp?!mYX`hgIY2J~jkPyu7oztWripuTn%LNvOa*@b{Cn3~ zZYathVsQGW!{B4+$%3jVSi!2Vpb=M*CdWCHm%M-qz%^eFil`VE$WVn|&MyTeRFbml zo5%z#?@t6}RF+u5uLM<8m9p;Ff;y^8x#%~7CTdFA@LRzongqFo&7TxBMW*q@q4tYP z+qjHoa0Rt-70u!rn!|N8j~i$KH_-*$LKpEQx`e0DBA!M|cm^#4_SU#x2L3i~qj&Hu zdKb^3D|jAt@B(^|n2+CoqM)l}>Y<`4*i3xMc=ECW&mS-0%9~s}|hSS5DVLPooR@3Pxs;Z=QkY=RR0I4k`rb%02na7Q>b@NeaIGZ*>XIARu zG9BC+YfL^ehI45PH0FfH_dS*e9X&0kMq0yDvkK5@NoRo8B~q729Sfu(k%mMX5@|`K zC6Sgyni6SBq$!b7YNiuuIj!LKqc*I)fM-+lp(drdhvIDtcwTUD=^mAUfnh{16uuSTnNE=W` zNE}hNo(bJd07F?<`YsrbB`_RI8@!M(2h1evj@Sk-iRcCqhPY0MXyNgo0F$^qZtgkE z?b(SpAYAk5peA(|rNN?;+#FOBk|c1B7fFbhmX^3`c63@0@25L)3?LP^3M~p!5^y1= zUdTH0qe#FtbSnI`Er!oSctH#=3bHDQCCHi}>wm1OZqM(+qu#vw?! zE>BP*cb#i)fQsW#bA1$5o)zJv6(`pokaD{u~p9s6*uR^tcUd_EM%F8LLRk~h@6s;e*3yp{)i z)J)CDY3d8@S=lsIXfHoewZHU(PpBft%b-0dZTG{)UHH_vc`Obm&j((Zbk?{A^{){2 z12|+_7;`;#LhK}5i!sQad*~CmXuyn$Qbfh{G%xIR0eTFT<#AmdeXz(JMwJmX{&JD2 zoMAFUd|zfZs*d!sZy9O`zqJu~@=+u6|KtdTPC#s%o1+6F_8e8t904?OYJ*Yv^!9-kq!;-Zm1H5e;UzK=*3K(l2nW-4dyvsk{PHf1V){Zkk~hRF zN)NAKvhVbLX3KcWIzySL9pK}-jHyGTJ`_K(L8r)v;+b&^%4gpvx5d4Xd;!FO+!C#> ZmVuVDrW>w#ssdz{;5A$`G+i^){{TQ{O@{yg literal 0 HcmV?d00001 diff --git a/utils/__pycache__/mean_CI.cpython-38.pyc b/utils/__pycache__/mean_CI.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5b623d03f5264c091224064e387843323bd34110 GIT binary patch literal 620 zcmYjPy>1jS5ccohaz7FXkl+!n$vyxGkSNy(&mlyhBp* zEWE+ARB3nsM8SCPii|b$&FnXx8ISh!Y&Iq+-*u;c2||A7;IS|aKA^@^G@58?Nqsu1>AN^ka4N32?JXKa~?!%l*7Cb)DBpTKogXWOD23#7B3kGJeqbc zL1>j!D6ufG#TiK3eKi5()_W6vlJFdfzi+Bd*P7~bbFG}KZp_W5bL(o|q>EsDSDhN; z7oAH%#Z+xmV`G)uUo6f}{0>G(e~Ojj3p9j1r3GblOiNn+D+;tMyu|PG8L%#bblcYN zLf$JBUOBBIFt6cRtCF`}vdtyd%^uPR{d5!zYJ%$H8 aGI<~<-?p9JTJtV%Fy=vVCOlI-W$_&2QT_6sKfak`=p2^UR#rL|1?QI{f>$UtWw2pk*g4#uN+AcvQ!V>gDTPRYe6>Eafs> zZ>9X4iiq+StufA$e)2Vr`!rmeGrVS;q~A8!B^?_C$L2ze0WnixNp=L|XM4D%QX$1m zyC{=1RUJ6F!J-7qS)B-7<~O+zr2JSrlBqLYs8dX;367{Bp{b-wVsFIQs zfw^p_BB^qw7>=O`p_g|1F%{2tZjAcL|M~B+7;KuTM^_(+Gw-okWeQ zPD=EX235cc*4mOatQ3{CzUt$+%1sHAnd$pCJ`J4oV$-8ZLZb`q*0&CKj%xbhe_W2K zXhBD&&qXj+O>@08#xOJ;n8ptn2dwmCFdp?3OH7|za$jJ=AZ~|B6Rtm+H#b^1rgBfD z$#LgGGLRY_)>e80oEr0}z~KkT>DR6rIdJ^jH!#0b^~aQae4Y zI@;-`jF*erHzspabg2#Dq-kWrARgSzM4F6fU*dCWv|-`KDGZYI9568fx&XjTZ%?=) z=Fn~?6S%|0Hsd&M-g>Rx218-f9*CaR8HJvysM+Q}A+ZV9|K0OSCrxPOv~z|zuUyj0 zPRgT7#1sVg(#j5{z3r4kdZ{XX#bQ~RB!E&jdl&FMnrt961&f&B+8$(;2e2Xgj^#Lx zWy9lI?t91gts*>uRkWvCu)KFY-&%&pH+t(@Z>amu@g3V}@7dy0$XE7Z{ufew47_%t zI6c*c1~V8{Pref7XRPz^e}J-V-kU^BTAbS|#>ao2qy#>`7v@8kE^Hf?Z~M7_0JuK( AJpcdz literal 0 HcmV?d00001 diff --git a/utils/__pycache__/plot_cm.cpython-38.pyc b/utils/__pycache__/plot_cm.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d16ca0e14f6c93b7400ea8104a57796692e34935 GIT binary patch literal 1174 zcmY*XKaVRl5VyVFKgs3(0xxhp=qS>>Jc$x`5Amp914t;%%88xLCf@a8J9o(`R{`a9 zG$4w*l8-<|%V(GhiR!)qLPBEfJ5I+~JO2H~Gvk@@ezm{fCn(?l?!^zBkUs-?EDXt~ z__^n(B#~5;35`3OBys0WI?3qWSw6`nlgW2vQb_s*8Sz_;BCltd`9X3-;m%k1ISeE_ zD(Q;Z1IaFVJ3y8oJC*#Bd$OcEB2!$M%qv<`S;(I3|IFmxj?01EzbY=%UAkoQKpx5? zd3=>n68XvhcyfRK#{GFB@6P|@T)v6X!S>b_j~UYIOg_1x0bl18xktqV>d9fS`Tq6Z z4Z-f;_E`D1bV%eoLxPw^d^#kTneQ+6{lW6^`e>KzvKuDfl~3h+p%RQfj?n~X_5N$% zt+5RdNUS&qD!+u(*Rz$Ye)=)UufN@%;>>_gjg}(@+|?Hf`g7Czb5&Oh59w!mtz3mp zD1W>h<&cWDH6HkEDl8O1m#cFJy=ab*&l=G|GH*Qeb=|7-TKa{v_$~zK>NdRGC;_^K zbY`@%(0g#Y4gW?s+t??F>@b8RX6;8Dda|}^=4;bJrd3<{1*9=EWHF6{#G4LM_03x7 zNiu6npydUB*0mKRsOqmVIrf;%aU#M)BD;EY0?f6LFO=}1=3HzRp_l;9BG?P#Dm!6C z;~?FLP3_>sDPP7hmYs2REa@=VHo>UHRA~p%uzh6c2M^&o>;dc19tJ*4xYS~c4MO6) zh1`iVHLojR#sTjH_Q;o}ZgEoDIEB;4zMeyl&G~JoAl2$jX(&QiiSx0?VPH&bfX&-q zi2VfV-ye+^rcvYRVuepMzEBqn(^liE_N7&w8Go)+_X;1}3g^dbUu!q+wDIMvdDd+q zzsDJ7axYL3R?v(Vlrv6QTm`a-{>t`_Xk6@s4ro8Ff<2`i8G8JarS>Ie81=AGC8ox< z_5V<_%z{4T~G1)fFQfu?HFC{he3rTvA0Jm)CR@Jjgf1uUW9+m6{tdS I16r_u0mGe7GXMYp literal 0 HcmV?d00001 diff --git a/utils/__pycache__/plot_prc.cpython-38.pyc b/utils/__pycache__/plot_prc.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc489ca9ffbc36994b777c774360c04b2c1fe22d GIT binary patch literal 1616 zcmaJ>Pj4eN6t_KpGLwHz+HSk;wonOi$fZH_x}sgW5}Z~DSP4Z)VLEmu(>gPQ?X*dl zoC@O94*=qj9Jz4fOYjl!4ehD7J;Q}(C(G_$ur=?!pWpNI^Lx+pywRv47(dcHItvkc zVZfV%062qBeglLeiaF}7IPMXPot|sCH}ZQGywU}uuvcBnwO$SMgx5!nUSrhkHSM|s zc%!!gvP-@1QLjbu7pUXk!zt)lhiE4n$GRc%xDP^K#<^e}tal2*;#8)2)(0WtydRIn zHH$s7Z9N*mEAXAeCx6EXDRhhGo~6E}l?2TLg-cwLTk^Ft_vZe*qR7H2{nAUw63@dj z1nia}M2>zS6c5NATH-x450wY2fPezNCE$rH{4$`<(#VxExW(pHDkz;>yo;!}i{@1o zE~-m3ua&h$jS@4L&b(gM7j^0{!Cr-z4Rdi|IuyJ9kAS@ zyKCDA^x<20hwfQl`@azS=#JRPcj>|UrVrKL<^JMPiOTAd&_ntTeRt(&aj-;ij{rL1 z=$U}hp?galka}(*;{MD%F0w>>?Avj~XW^6Q=SS~h9#PS?Vpr^$&OFJ7(OpC+&k z#>>w%om(wIGv=<2xO?4#_ ziOhu(<`HW@68Siz+ABnw!A_|Jxa1JvAWg=C=~_f-Uyd&do-19IO!XsKuvo$QX2qTG zbfn#>p_OPdG8VV+dPzYTyjRJp-p7;4@ z{STjNGRU-J4~r(hB5?-he?RUH^AYP_46mXr>(1D0m}f~hNfrE8ioE-jvEm#`C?cu4 zW0i8*H93F);^ShfgIB;*RDKEs5g(iBk``{_eX@z`PT;g~0H}&xOxDx?$FDSDt+k#9 z_`pirxa#bafQXO5SEnsbfN3uqkBX@{G_qer8I7bS1=Ve+yorFP7Z8mtj=$pKJ!4yw zaav4|rHYi)-ii?kwJD%Poaqe-G!ohIh$)fA()NcikIc4^jlrhr4f4b|4jzwkI_B&n U^S_m*_XD_Lfa@>=xJTXp7pL&P1^@s6 literal 0 HcmV?d00001 diff --git a/utils/__pycache__/plot_roc.cpython-38.pyc b/utils/__pycache__/plot_roc.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..18c9fef8a8ddb5ce4f048efb4c9e2ca1719f5f31 GIT binary patch literal 1535 zcmaJ>&2Jk;6rY*>^!hWYo1|$7;lRO%#-K`^N(~AUw@N5fLMtR|y)#~Kvff>0$BAv$ zTms^hKLCV~edNNqH_rSC%&qFl;?gq`5^u&Xv=?Ty@4erf_j}Lt-mah4>mGvj{QJLy z2R1@46nK3R03X3qBp@iFI6*$vn)rs+=GgLWyg74xNBdmg)w<$WwD$ZeXp+>%b-%7- z4X_)2L)#|UO}`1YMeT2q-=g>&b(}R+L{B*DZar733P)NmX@}bj+U;#B`(Pg`O;We3wz-TQd*^bLl%xOmS*WtyaJmN zVU@-W-bd8fM~jNEm(B_;ywY2`)Kq(AEUIO7S)tYnVXdx$!>mhvyF z6c`cNL@jHyLcI+ts~2@ZRa&F4RHbcgQJJCRM> z^e){~v2fP0PmRvA;YhQCV3L zdQ9J@C!0LWdn*L@aA1({JXsqs3g1-YnwzJ28m(a}NLuWxNs!DdA3r@idH223hr^B` ztuRY6E-Oi#vg??Nk+hOvz>?_M@ppfI_Uz~1xS~d1w}1Wg`TS*1;>)!O4q)OLcfsP7 zEu8)bqaS}z>-XN)_@fs+)a%77&$<@@4c5!vx&=y_gDjyPBHdw@iXxsf=_%`)#nDL2 zo9~}II62Q*7!Ttx7PAiFyTIZ*ApGh%ON&^2upBnzdP z^DO^d8qk>d3;5u`Zx1s*4umvl9(N9ut2KhigWQxVQn< z0mue!ZTAsA(pDRLti1pL literal 0 HcmV?d00001 diff --git a/utils/__pycache__/plot_train_curve.cpython-38.pyc b/utils/__pycache__/plot_train_curve.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b2e29f191c6225f5e91a3d40e009e90eb165d1fe GIT binary patch literal 1163 zcmZuxO>fgM7`EMfx81rAHX(7?0g!s3962Ba+PI-z7#wn`(%4Hf(j>B-jYi!GBu@Je z?UBFaE2sViPP}%yt{`mL@AG~=_WL@@r+z;`Fn-W7IA)igOgU z5{Ja-ozODU4jm)isTcYfQ9?W4Q5aDC5e=;sWTW#KPS^||kOS)!mUxO0ns=^%|1r9? z3?hJRgF1j5gDgO<#FEVMH<-7c>|9w3o7zi^5nzWp)V(u!;ZhH#zw8)~H}~ci4QTi7 zL3e0xqx-Z^ch;E!-EH|6T{>8!9^G4`me^?wQI&65L z+%yxi3qE?HS-cXF1nf+|~P5<;D?rZnZu zT4{;2yn&qPnnAuIrGUIhrpmf1vqIT1bde2x6_mBCYB{DES2i;RlFNekp>?4zb?bEZ z!=QzbB$TTk0gPOK@S$6bP1;YypMw7TY?PK$HkzbgqM{fztVzov86}w*#vIV_PUjP033$Tl)AM& Z)8yXkX-Vsxz0u#D(0?8eZlGt6`~eyWKNJ7} literal 0 HcmV?d00001 diff --git a/utils/__pycache__/prc_all.cpython-38.pyc b/utils/__pycache__/prc_all.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ecd4cecaca3acc44c22bd52e1176c7f68be1f4e GIT binary patch literal 1113 zcmah|KabNe6p!8fanq#N)3LCf1PooS5@IR{A>A#v+yG*UkfJp9wYf`T%XUs`l};cr z@f});g|EU#@XEwjfQ9F@oO&$Svft0o@6XTA_WL*v9cZ5^&)#~D^TRF<8-~Rj80rH| z)S;+!lAS~e?$*7;?WC7@AaUteK@uQq(<@2UPx>8p%di?GgARLTRK-aQ*r&lKCmB-o z&KZ-RkioecBNJ3CtJCvKfJBsBr;_J@V-V?#bFH+@#Qmm_c}}eBz70VJFwSA9?=Wd+ zgEYSBZQRytgDu{oHE9t=w^&YFLh)l~<87UFA8>DvL+Y*v^$l2gU?p0z@u@#YTeJzl zGHRm(ujs%l0IwI|MZoLpf4utlxx)28lh$whZP=pLrGqctI@bMm@DU*gWNc*{Lye*< z(<`dE@ycw*O5-n6AsJnTP=nK=%I0ipjzy*mR_nAYqL6%M@a4to*qZFOPOWxoMh`8y zGSMzxnwObUtI-1&uBKzp5S87rlorzF*0N#JmLB-9JK*ExyyS8m8Z4+Gl4Uf_)t$i* z+J$5b+ui*GVV{6`RZ!w^?0JR&2H4^?Wif;4w16Cl`!~b6&$kRC?0rqEwSEKv|yIW9_l! zt)9c=;76FC2)d658vTP@jM07n?Gu6sme0Uzd?Zgm7;jfCTnpoitfrZg$5!u)BEKz} zi8}^o`)?!dpEbjs?(hGRJ*zy~DIa_!>B3HOXb$YVtLQj%ZJ$NfA7uXUy#vT&XEoBrm!O=iVQ%e3!&KDw~wS^MX5_sX8>}}v!YQKEN&xb&47?x`o>{PQMRi;5+y^(L?DWXE=EQJ*De zrGY6*k-YtQbRueKi8et zKMat6_x+6s@d;AT;LQAint`&oV`xz=p;viLbFC)MKdW-%UJox)O{ za=XwbH`?O8LR!P2eKRK-TAHhRQ8A;+LM!+GsKAL3L^|$xEN&lY*W#|Qt~vL_Q&$an XRSDW~_S#KCJElMcJ|GBk>NNfbeGCQS literal 0 HcmV?d00001 diff --git a/utils/__pycache__/prc_patient_mean_prob.cpython-38.pyc b/utils/__pycache__/prc_patient_mean_prob.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d7510ab33e1ccd7948a85052f39d1f624f7aae81 GIT binary patch literal 1036 zcmZuwJ8#=C5EiMY-%gqq=+Fm>E*@g-MNtGumn=qsJ_aqSEU~s?iX=!XfDL78*Y-Eq zWB*NOu0@CLo!X^GISEpr6m<99eKdLBo&0t%=sK`wzrMZy;XBTE8{A$Pf)jWZ1yeW$ zqE52+D8X&)CSL3PDo8@K=c1~UbgytP=>d;vzZxWiYM2b$ybJLt8MWA>r}`* z2Sqo_>I~sMg;#w6(}0>4R%7sZh2>N2Y>~$2?$&F(!rkE=y7YnbfIInz^RGC63-vD0 z4sC;-a~U*25#D#UA>a^jbcG`wHC_`oQG*+|=zR7sJGxVJKO*GlPSdfz>44te%(xdB zHNKWv$u@)HLkc0tAjMP|PZOm#Lr7Y1oyBJ_jZd>Vp(a?j$%$_wm0gguD5cG5Srgd< zwI+auq#u~^b56P3L=ZkMtHrZP*I-eYo+Me3iZWkOVlY?66PaEb@0^#+gbT@QF<%?6 zA{jH0<|(Tyv6i;M#uu_=8qSGi%~HJ<#B_PBMXhZWatM3m$eMmz2WU`rm4x8!X>wqx zw#3ku2s8fb$On-8{U%=Wip2BfDq}3(kj;{_MZ73AOkD8z?A`GxXV5OCk56Z_<5!ZO zlU&EOE~$#`SsYinM1fxJptpmV|0WfbOgSS`YuK{&z~TQP=w_p z*fqH$?N5x~Ho{;gpu-s}G9|}0e^6!GeuuQQms$k;-n)Wo(^GjV)>EZ3t&G2SSSi@F zAHhh$ATr6O716THm9+mv>00C2^M|d<>Gh}HPNFTWZQU*N(C&_&SG=exd0_{pY~dgT L9uNq&73%*5$yFk1 literal 0 HcmV?d00001 diff --git a/utils/__pycache__/resize_3d.cpython-38.pyc b/utils/__pycache__/resize_3d.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59e772d82fe3283497ca48d40170fd10e0118524 GIT binary patch literal 1232 zcmYjR%Z}SN6eT5Dk|ol9S*wyvXD{w5l6J=orr_p7sYyh$fp* zhg-5En+Tdh#~S{(iH@zjNwjD6T>`7;tdPzM?X14DLOLt-UGmo7^fk?cOHW=X@50~E z>Ra$0_!>L_AAyJ9e}eZmRC(JFxf7jiBkk=dB|x$6cc_ork{#(-3O2ZALrpvTNQXPR zA&7OLv3HZ|AdfD+zo`5UwnM}p=)v|#-MBd3-qgK3-hQS19o_Bo4Ys%PeZIkVWc^VC znG(cET#WlVo#GbI*>ob&I`Y{Gdr2lNM8z}7qO-QKkR4jeTQ#e=m@m$1X@W}1d>wt< z+_T`d@vCNa@->To;%X|^oFx`C*}16ZCTNAaWQmo(>;tD3|2W+TKelADPcZ4?v*TZR zo0Uz$UzXXNzYvAu(nQlP`XRuprD~Q}IoQFq<&_k3QL#P>=-`d(wP!-|b0zAE4Y2$_ zv)(T&q-g3QQ#DTh`~yEV>RJR|`A1DvAFl1$u8wDwg6b(f%i$w`cvUv ziPt|%{9uUj+f^(379F9*O;M{toFThKChWA|6v_o~=lVk2wvO?`vd)(Uf7}CW?ayRi H)WH7_f16*{ literal 0 HcmV?d00001 diff --git a/utils/__pycache__/respacing.cpython-38.pyc b/utils/__pycache__/respacing.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e84851c7d4763664e877c872d9b79c5d09732056 GIT binary patch literal 1973 zcmah}&5zqe6!+MUKa=cdzoAHl16!c(mV!6{>XO~kiX~fZQb4{~vzghfcm1XDr0oWW z14ZJ5#DN3HMB>7gKWDC-`Uh~Sgw!_^Z(2BjM}G5uZ{C~tX+LbWJPn>7Nsj;WHSJFc zUVR(@ci=651wu4}5-mh(*F#!oz#t&L(QJa`2I{Sq*LWfwPOYG1iPUsUi zY*OR6HEmO$HfMUcM7(gB_~8m^hO49%u2C;sR~qN2H$!Dbjp=#GWjaAfqZVDF%XEdV z(lxqH+CN$01%i6o#?mv0PU{XVxz$e_b{TyK-tv2-XG+1!6~ z`Z{rxeRF!g@+AjWS0y)TRVHyybhxRg{GJBLQ|P;@)q~lAKKWe+jnJMcq5~(aB+e*i z!agVq$%1n7oQ>^oCpR+2NMD$86Gm2?ZoDDPGUg}3-KUt`Pw|in`*X^B z@t6uvG6f#Q*-#j9#zlkWql}2gVUlAmJZZQ)ck4_3v)*%J)XR7%lwRAW_bj7Dsz59>$q)A)1<9U7T&lj1G95XQBzmFOjsqpMi@a zPcYA!SO>v2J;bAgKZu{u&uAIEUb^!h5 zV>&*{vtclddBkXu2iueuojl_VmpmBpI4J|TeD3i^aVp#ef}f;T-T|T+9&(V=xQ1Z; zZMwkM%Q76~!D_>jw23^Wds#{iPxp`sFZ&Sg5Ka`OIL@MIycwjF#P}@4d);V%r?(lv z-r4K+_qTfe;ARxdNskzQ96k8*-Z=Pw6Hto?<`~i(=HuHzn(+Wr#>)gB1P^;V`@JBn zi!rG4j|k=S;SA`LM>rW@|EJaVR(~tl9YOi}dk;E$+dKQgyzAg*yq1-NWDqeP^*c9j zZmJKLnULv*!Q=<8$TI`D(CPL%yHbhS^4wx?%kvFMu1oTcB=1V{9*~WT!tKTCL+pRS zT#yK(JT2KZ$u+W33SH8(Vo}u$#yN_@=Ar;Fl%X#XR6-6}=&I}#+g72>3#E<@;aek6 z?IC+p2Jn_7{~4ZW#HAejMtTAp+mr@!@LnMw02I#0X--B7{j>q7yalA58pHX>F-&x} P+6&IqFY7vbWj%iZaoap7 literal 0 HcmV?d00001 diff --git a/utils/__pycache__/roc_all.cpython-38.pyc b/utils/__pycache__/roc_all.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a9af76bf4ab4cac57b6559c74b34a75fe01ba6e GIT binary patch literal 1135 zcmah|y>8nu5Ek`I$&%x=S-NFu3orH3$qkAE$&w`~P;}8E&?aLmkwk%{0>qG~cFjB3 zK$kv4UnJn#seObN0eY0(#OYEJ#P=xE+VMSVP29M8|(d=sGL&jRQKGF8+$ztD_Y_|@LcB}`FExFY3 z#$Prqh?I-LBMO&e?M{k@4Lyw|xndd3g~>pPh6z)C$ckM-R^F5sTnrr@-d~sK!+vKp z>}k8E8Vg2fHkDV}hPW*ioF{sXUtIT8Q> literal 0 HcmV?d00001 diff --git a/utils/__pycache__/roc_bootstrap.cpython-38.pyc b/utils/__pycache__/roc_bootstrap.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8e92ab2cca37fa57c2d745cb37c102f0556a270 GIT binary patch literal 1409 zcmY*ZOK%%D5GJ{AX?OLoUD!nrL4h9BwAQJIAVrbDZqZzf!f`H(7Q%W-%X0P=k}51? z_oNmD3Iym6=s`aAFY(%w|3cBA9dZ>XS%Smw8_o=8IHF!eQ4hhl_uHHFv4_y#n!NuJ zAWvWl34$VubCh%|PKe=FVw=@T99T*27GC1v&c-i-q-U^|_lqzI4Yu>Bh?Cf02k;;n z80-QbCPTm;^?yOhh~npH65PSb=wyPG&zVSzI%j~rf~Do`=o!F>SJ^CGW;2mhT<=*x zUyZx7Z!%p;iMcZ|>+wH8bprUTQJs6lIdjQ+qs*{vs$#0^>FHEw-L z8lPfmz12|i+G+!7H@FE7P(!p_lOL_N*Y?{`IvW=(`)hoQ+emsFUwT(T(`zDXy(R|t z-(hMuy?ykKH~miYfiCR!sq?1;G2NPQS;p~`t2WTH{`nUS#(XZr!9H4g4SesBr zALe(9F>2gR*bKC%VUA(`mf40uEqd?OKGYFQh_uGEcT3vgmb+U%+Hz;h$6Ic1`3_X- zH^XMsjGLXgP5U&Y(cGdj9nc{ifo2T1xZCVPuXj$)CPaCh)p?pR6_m5C;R4#}EK16* z@1S95;wbB)=E{;rlw1*cp=^1{84qEf>;Cdcivfr`-F{{3U(AjF=&R2j96fvR|St~c~ z_DM}uaGc8YJDwIyxz8D2Fad5=rrZU+F4AkTt!q|NZfj?7td>A@mXkddoL7|;lBW=I zHIsY^F;=shGs?$c!S(N#?`p9FLg6cupYq^(78w&=?dBGA?FzqA7CeN+)32ebWnx3M zBC(-dEl)r?x?qv`LZ)&CW`806Ik^7wbb478Y_?ii(E|g8TaWQ)~oJq-fKn>RXhQKh>g1ulMsJ|_i+q7gfYasIQ-ZC&-Onx z+87VX5VSBX8~3n}dv9Eqe*rNjV?`>VoN`&zEB-NPlv}4IO$Gl%6MLRl=Sd`9<}Bsq zp~OQBCb=c6+3>-V1ZJ|w*#jRpwTxA^<;S^PrQ26Og!|WynL%#rOp)BOY~nPz=hL$Dk8IKO;UA-aC|pzhGB1O*|Wx zc~+&96Hu8dq)D}$fj$C}o=ItHqcb(5F1%kc$Pj1(RDS{1LUl`A?}EfjtX~>*;~9ME z-vliY{w==sHWYjT_>TYck={oI-9tCz7Hz_oim=z2p)F~{mbJL`1-mAj$b@a+5D{Jb zn}K0s@X7O#VQtjQ&TgZ2aE_tNc*+Tszo@K#l@;reI8Q;QO6TH0*}&wrS?>Volsl9Z z*;!s#y6S+0SXO6Oc`6p#VMaH(X3(>xZb0XiB{Nw_o%lAopO$tf+eNJzM7>y)7q64Z zVkK;>^Gu{_F`6%FNsbmlBF(My6F$s+IQL27yaMlwMROi7nGjm70~x?Lan0 zHO7^s-C5|NVE8)MM8>bakLsHth9X*tC8H4Eej> zQ;z`+y`hi2`|l5pdi^gY3?dlTdk~+DwENkTu6K(oWvR+4k=1&~;dDDLKI(EG^!HV} z2f}x@*xkkL2FIUlL08zrnYd|o>ALnGvF9!ads9l$6!{T6MO3@AU4c*}G~o@&KL-`_ AT>t<8 literal 0 HcmV?d00001 diff --git a/utils/__pycache__/roc_patient_mean_prob.cpython-38.pyc b/utils/__pycache__/roc_patient_mean_prob.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ea3dfd8db602c6cb8f7608f44adb1fd37f585704 GIT binary patch literal 1130 zcmZuwO>fgM7`C0yCQVl~n1sY(0)*7VT4_gw5Mo?*nI`dZ$R(t8wxvsA%XS8>vJ+hQ zAKD}Tg5SW=M>y@y353M!wEIB9k>B_A>nHZ}KDOURQGlQ&yWie_cMWhd^mv0VgZ7>5u$)cuU&F2%3Wz`KN1 z(ZK*~uguaa+1rQE=LJ)VU=xTVaLJf4C6!3|1Q=eoE<_&mJ?Qc)m42>?V^HB9@?-K@GuL;&ZG`^4j9c*aAsSZV!(uC)e(<0M^No@o4MB7saC3(k!)qFhDw)s!{ ziX~ND@|pG+S2?Ss_STejXgC9px`b>XGnK3$!ZJi`L)YOVuhgKceNk75n(<5rtX8~M zrZmwrzzt*i#+a+3T~JQeC2Vt)7z#IK#dcaxc4J4XRPBC2;vv9)y&0XbG8>IgPSdIy z&9nIltByyYnj@;;9%Hz2*uv} z?~EZScfla5kGHUo?~*?5!WZDKP4M;i+x6G+AJ_E(=M(W5{u)GL6P$KdijsfgM7>=FT`Di?2#_=D{qCL$EzYK09&5&R zSr4-B{>hR}wx%n>=oqchimsidU;E5|?5upS1F(aw9qOPabx?;ju3Z*hl4YdB+OrWB zUAoJbj#%pxLXM8=Xsa8)j_TGKhQqc8hQNWc!nhY{zUZ*C1au-5XFX{=%}c%5fuJ?6 zqWJj8kUSmo+|YRw>=F}{=><<%rff{BimMQqFx1j^Rv0o8xlmo#gx4IZ78Ep9i$_qk zCsUpALhsEu%hF;`DlvT7Z5b??X(^tvL}sID&J7l&@t}BOymJA-{jn029L^2htTcfZ zNm0$@T-nE=T#JK9l@+?%Hi4>&M9(ERk*Kt+w8gCK6{?OEJ66~yXDA%ItY&ctdeiv^ zvGLMsbYMuSQ+@EoQBPsPuhafS%y@q|nWjb2U+~356yyFlgD1qL=pUc%ABqCVF7^K5 zV6gvQiE}>E{Yqzf*|$7x8QAUy%!*-8&dtOBObeEl%C-e(L-tOCu6Ok#G>n>io1letbDNgt ergcNcE%D6u3tr6xt8)H^fLB_&8!!L}0F%E2#Sk6< literal 0 HcmV?d00001 diff --git a/utils/__pycache__/roc_patient_pos_rate.cpython-38.pyc b/utils/__pycache__/roc_patient_pos_rate.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..295bbfb979c10391e35367352ec63cf7190f6796 GIT binary patch literal 992 zcmZuwJ#X7E5EUs=UzSrBO@{()k)ex+SWBj&C;}u)7NbCr4q8;%WNgQ>NRU(j8}ihy z`49Hi{+*6oJM}NLDA1$gd=w}R-aFo%&hH+ji*7e|U|po&Kh1r|`C$)_7ly+zY;_8T zIuw;oc8e&(?b*$|7D+|`ap^CDEI_u#EyG2WMc{jsd~vdvqI0M3|AbD?u#ZeAO0ILs z$KZDX@)74+X<3LdP`O_TcmeDa*y<~o258K&>H*?8mT$DPK^kAV8?PbM-Qq1;`;hY> zcl=L|>~j8wP=A89Xyb34b&xqsFrl8sE6< zj*e;PGeVAzn|O!qxrv+3C5BOxp&>BNtTOI(QLei5G6&3sWON{mr-QDAgw>RK+#R=}a1bnQK`yi?*5Zt$LT0A!A;0*>_EJFQv5iD?4CT z&!Or-6ne%geIU3xkcDP%`yGP?H66(cnv2W>Tf|;1tTGZMymi7{5*P1phP3J{br7O0Y@@kS!X7CCz!PB#khbO!Oxs^UV z84eFWNPfk}I<57rRH+qcH+l}E=XUggSepI+C3^68Y5>M@duShpXdf`aiHp(w@3$ZA z{6D@QLQRO}Yj~pHkxu~(X$3O462=!rMGGaL*toxwc(3I(xcdZJCC#(jpyl*%fmYtO ebt}Uo^UC%M-z_+;OZMJ=GRn%_k^w>qO2}W=G7e+_ literal 0 HcmV?d00001 diff --git a/utils/__pycache__/roc_patient_thr.cpython-38.pyc b/utils/__pycache__/roc_patient_thr.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9b9e6bed8debbc483f40f97a52a126ce6558a14 GIT binary patch literal 2526 zcmaJ@%a0>R8Sj_dkFh=WjGyz$1QNi&&Onw#2t^ENSBY{NE!iZ8mQd5vRc`l8zfxVj zYn%2db&GNu<&q;pij-f~csE&)s8)U7@B7tPudiPHeyi2c zknTVF$8Y}K(zMT{@y%qw_yuzQX9%JZJ<^;ztviOYO~+EY;@C=8o%Y6O+u4SX6?NjS(~WnW9mQ9o-MHuU;=a>YwhepW3>2;c51nB= zaz^pk8H1~l`tLMnkLZuJN#hnH)y^k|u!ER~F%`9(v#CTAUDSxjsltZnaOQ=H%W2Nt zJc~jux^J_z$R5v%=`^B`C52}Ce&pqOI1PP|hiT$6nx%}3j-MveB9~0;aTdNg-{@=mMq5?ODzV4fx(Zwat^(J9 z>w&gvl=X+&ua8$-| zt+KUl5pZSevW=D4y4JCt+Q$ZOTy9T|v9_{6~X)>)KEwJ0#y$ zbAM%JVw^`e8d&Y}rE(yn7?|YMKqucJ-zDDzo?M%&Lw-!Y|B;;4aXHxN!;NljbX#h) za5yz^PJeJN%qL#7Y>}x8?c!jNlT29hI4;{D)w$fT#yby1CGsw4B$^B5>Z((DtK*C? zDMWP|r5=CdCpd3{9c3d?OA|UkiIy(Z9@s`B8>>Hn%Xo?cpfgPXh`&whQW+4x1#hZWyee! zaI%zxeOCzEj8b1%8B4SGmb>!0$#756gu9s2NtOhn@_`o>xPLHxVZQh1U5N8{QL%~; z6mgP^N|+EjUv^OWcvJZpm47yQSr{1+Ta0?d%|ibwqQXdXbmsAyurAXu5!FCmnHLLT z#nekgji+u>#Mwerfub`vVEa7RgxiD2#=*kh1E{XL3~~c%pzk=NLR% zUTkzk134lqU?IUyh|-*j`a_RHuXtFYJ$m$QO8^(@U@h9^i4M(ty#_KcEiND(F{BVl-RVb0wj3v(gG+acp(`XtNa80+Nn|i}+=&wLO`$o?Z7}RU)4dpFs zjPxDYZN0Pk4)g=FhjPoTnX*mfFKt_Ts?T5{`kw5w6DUzpJCVI1v7LDd@pASl>JI&sd&Q=hm7r2-31Ht5-!sMO8g`kg3ioK3EzP9V-N=!FakLaKwQiMBvKfH88jLFRx%WUgb~CqFa3=C+*JLf zjO@g`JpHQFs*L=+bp7Typp2C;u2kFKR?}oqWrAXtJsK)e literal 0 HcmV?d00001 diff --git a/utils/__pycache__/write_txt.cpython-38.pyc b/utils/__pycache__/write_txt.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77b6509cbf99eddd1c669d6d38c472898cacb2f8 GIT binary patch literal 3200 zcmcInPmkL~6!$oG{><(s*>pGC0xbh=S_0j!sKjLp2(&$Lp@>!ph>)VG?QGoopJpbd zn|e>n0dZ-?kwdk;6^Rq#Gw=ntpt*ADS3pAWX5u*ZZo3!2R_yux=Dp|l-n=(cf6>3&hVh32#lr;f0sOeSxQ2@YV=%j;fvIt2V8PuC?9d+ANX=D(YFHc8!up^N z*mCV}j6uUiw~T)E8CWrH^pUK)RCvM^a&{Hi$C`yv&US$PMYAwMMheNhYd%8kJGZ)jDsAqsW!|i%r@)(aVW;bE z?ZABa!+tmI9#-AD)>5N}sJU3w4~zq|Yvk-wS}CCAv^cktHVSji6c7)^ z>2d+h{O%q$o*2mZ+<3?M)VOJUVF>J=I)I2CnbA3j%G6OkcGn=P*8WrKFxA+I*(DfO4 zkWew!3#nH!rn$S<%QP31u|&gvUx%rcLOJw6#;R z0%amCRh6>p-3Rb?cE37pX^CMF@A50cyDuc7)9ZNi-yhRe>@W{rj4n~{z-`jw)79RW zo4s(e=aSw>+r5vsd$*>`7~COH&stzbBh?TZHZhw(xvCb^xQ$gJ+yaBK&bFTI;J~9y zwIN~fT+OxcVks3N5uTpq69=lFk0wE|M|5g+5@3ZP3DXsDXpxU;Q>Cqd+p`27W3CpM zo&O+#kiRT5y3=(uO{^;459zLsjv@Gln67IQYRdN_K@t|;gZg|I8#MC0Nj8)zUCnYy7jgz{ zUBhx#s(L4qX21rGS!;r?gPN*IHK^-qR3n_#;bMGMNysY1KO9By^$D$_8LUGb>P(=~ zj|S^OoPWNzHI74X>-P9AjiRloHyy{(?$)j^hAM`w>z@FRuP>)R8)uQM>vNZ;xlz literal 0 HcmV?d00001 diff --git a/utils/acc_loss.py b/utils/acc_loss.py new file mode 100644 index 0000000..eb082ed --- /dev/null +++ b/utils/acc_loss.py @@ -0,0 +1,33 @@ +import os +import numpy as np +import pandas as pd +import seaborn as sn +import matplotlib.pyplot as plt +import glob +from time import gmtime, strftime +from datetime import datetime +import timeit +from sklearn.metrics import accuracy_score +from tensorflow.keras.models import load_model + +def acc_loss(run_type, saved_model, output_dir, save_dir): + + ### determine if this is train or test + if run_type == 'val': + fn = 'df_val_pred.p' + if run_type == 'test': + fn = 'df_test_pred.p' + + df_sum = pd.read_pickle(os.path.join(save_dir, fn)) + y_true = df_sum['label'].to_numpy() + y_pred = df_sum['y_pred_class'].to_numpy() + + ### acc and loss + model = load_model(os.path.join(output_dir, saved_model)) + score = model.evaluate(x_val, y_val) + loss = np.around(score[0], 3) + acc = np.around(score[1], 3) + print('loss:', loss) + print('acc:', acc) + + return acc, loss diff --git a/utils/chest_patient_meta.py b/utils/chest_patient_meta.py new file mode 100644 index 0000000..1e7346a --- /dev/null +++ b/utils/chest_patient_meta.py @@ -0,0 +1,343 @@ +""" + ---------------------------------------- + get patient and CT metadata for chest CT + ---------------------------------------- + ---------------------------------------- + Author: AIM Harvard + + Python Version: 3.8.8 + ---------------------------------------- + After the data (CT-mask pair, or just CT) is processed by the first script, + export downsampled versions to be used for heart-localisation purposes. + During this downsampling step, resample and crop/pad images - log all the + information needed for upsampling (and thus obtain a rough segmentation that + will be used for the localisation). + +""" + + +import os +import numpy as np +import pandas as pd +import glob +from sklearn.model_selection import train_test_split + +#---------------------------------------------------------------------------------------- +# external val dataset using lung CT +#---------------------------------------------------------------------------------------- +def chest_metadata(harvard_rt_dir, data_exclude, pro_data_dir, data_pro_dir, split): + + """ + + Get patient and scan metadata for chest CT + + @params: + data_sitk - required : SimpleITK image, resulting from sitk.ImageFileReader().Execute() + new_spacing - required : desired spacing (equal for all the axes), in mm, of the output data + method - required : SimpleITK interpolation method (e.g., sitk.sitkLinear) + + FIXME: change this into something like downsample_sitk (also, data_sitk to img_sitk for homog.) + (as this function is especially used for downsampling, right?) + + """ + + #---------------------------- + ## rotg_0617 test dataset + #----------------------------- + df_pat = pd.read_csv(os.path.join(pro_data_dir, 'rtog_pat_df.csv')) + df = pd.read_csv(os.path.join(pro_data_dir, 'rtog_final_curation.csv')) + df['gender'].replace([1], 0, inplace=True) + df['gender'].replace([2], 1, inplace=True) + + IDs = [] + ages = [] + genders = [] + stages = [] + thks = [] + histologys = [] + sizes = [] + spacings = [] + print(df['patid']) + print(df_pat['ID']) + for patid, age, gender, stage, thk, histology, size, spacing in zip( + df['patid'], + df['age'], + df['gender'], + df['ajcc_stage_grp'], + df['spacing_Z'], + df['histology'], + df['size_X'], + df['spacing_X'], + ): + if patid in df_pat['ID'].to_list(): + IDs.append(patid) + ages.append(age) + genders.append(gender) + stages.append(stage) + thks.append(thk) + histologys.append(histology) + sizes.append(size) + spacings.append(spacing) + + ## patient meta - test set + df_test = pd.DataFrame({ + 'ID': IDs, + 'gender': genders, + 'age': ages, + 'stage': stages, + 'histology': histologys + }) + print('df_test:', df_test.shape[0]) + + ## CT scan meta data + df_scan1 = pd.DataFrame({ + 'ID': IDs, + 'thk': thks, + 'size': sizes, + 'spacing': spacings + }) + print('df_scan1:', df_scan1.shape[0]) + + #--------------------------------------- + ## harvard-rt train and val dataset + #---------------------------------------- + df1 = pd.read_csv(os.path.join(data_pro_dir, 'harvard_rt_meta.csv')) + df1.dropna(subset=['ctdose_contrast', 'top_coder_id'], how='any', inplace=True) + df2 = pd.read_csv(os.path.join(data_pro_dir, 'harvard_rt.csv')) + + ## all scan ID to list + IDs = [] + list_fn = [fn for fn in sorted(glob.glob(harvard_rt_dir + '/*nrrd'))] + for fn in list_fn: + ID = fn.split('/')[-1].split('.')[0].strip() + IDs.append(ID) + print('IDs:', len(IDs)) + print('top coder ID:', df1['top_coder_id'].shape[0]) + + #------------------------------ + # meta file 1 - harvard_rt_meta + #------------------------------ + genders = [] + scanners = [] + kvps = [] + thks = [] + tstages = [] + nstages = [] + mstages = [] + stages = [] + labels = [] + for top_coder_id, label, gender, scanner, \ + kvp, thk, tstage, nstage, mstage, stage in zip( + df1['top_coder_id'], + df1['ctdose_contrast'], + df1['gender'], + df1['scanner_type'], + df1['kvp_value'], + df1['slice_thickness'], + df1['clin_tstage'], + df1['clin_nstage'], + df1['clin_mstage'], + df1['clin_stage'] + ): + tc_id = top_coder_id.split('_')[2].strip() + if tc_id in IDs: + labels.append(label) + genders.append(gender) + scanners.append(scanner) + kvps.append(kvp) + thks.append(thk) + tstages.append(tstage) + nstages.append(nstage) + mstages.append(mstage) + stages.append(stage) + + #------------------------- + # meta file 2 - harvard_rt + #------------------------- + ages = [] + histologys = [] + sizes = [] + spacings = [] + for topcoder_id, age, histology, size, spacing in zip( + df2['topcoder_id'], + df2['age'], + df2['histology'], + df2['raw_size_x'], + df2['raw_spacing_x'], + ): + if topcoder_id in IDs: + ages.append(age) + histologys.append(histology) + sizes.append(size) + spacings.append(spacing) + + ## delete excluded scans and repeated scans + if data_exclude != None: + df_exclude = df[df['ID'].isin(data_exclude)] + print('exclude scans:', df_exclude) + df.drop(df[df['ID'].isin(test_exclude)].index, inplace=True) + print('total scans:', df.shape[0]) + pd.options.display.max_columns = 100 + pd.set_option('display.max_rows', 500) + #print(df[0:50]) + + #--------------------------------------------------- + # split dataset for fine-tuning model and test model + #--------------------------------------------------- + if split == True: + ID1, ID2, gender1, gender2, age1, age2, tstage1, tstage2, nstage1, \ + nstage2, mstage1, mstage2, stage1, stage2, histo1, histo2 = train_test_split( + IDs, + genders, + ages, + tstages, + nstages, + mstages, + stages, + histologys, + stratify=labels, + shuffle=True, + test_size=0.2, + random_state=42 + ) + + ## patient meta - train df + df_train = pd.DataFrame({ + 'ID': ID1, + 'gender': gender1, + 'age': age1, + 'stage': stage1, + 'histology': histo1, + }) + #df.to_csv(os.path.join(pro_data_dir, 'exval_pat_df.csv')) + print('train set:', df_train.shape[0]) + + ## patient meta - val df + df_val = pd.DataFrame({ + 'ID': ID2, + 'gender': gender2, + 'age': age2, + 'stage': stage2, + 'histology': histo2, + }) + print('val set:', df_val.shape[0]) + + ## patient meta - train + val - test + df_tot = pd.concat([df_train, df_val, df_test]) + + ## print patient meta + dfs = [df_train, df_val, df_test, df_tot] + datasets = ['train', 'val', 'test', 'all'] + + for df, dataset in zip(dfs, datasets): + print('\n') + print('----------------------------') + print(dataset) + print('----------------------------') + print('patient number:', df.shape[0]) + print('median age:', df['age'].median().round(3)) + print('age max:', df['age'].max().round(3)) + print('age min:', df['age'].min().round(3)) + print('\n') + print(df['gender'].value_counts()) + print(df['gender'].value_counts(normalize=True).round(3)) + print('\n') + print(df['stage'].value_counts()) + print(df['stage'].value_counts(normalize=True).round(3)) + print('\n') + print(df['histology'].value_counts()) + print(df['histology'].value_counts(normalize=True).round(3)) + + #----------------------------------------- + ## print scan meta data + #----------------------------------------- + ## CT scan meta data + df_scan2 = pd.DataFrame({ + 'ID': IDs, + 'thk': thks, + 'size': sizes, + 'spacing': spacings + }) + #print('scanner:', scanners) + #print('kvp:', kvps) + ## scan parameters + df_scan3 = pd.DataFrame({ + 'scanner': scanners, + 'kvp': kvps, + }) + + ## print scan metadata + df_scan = pd.concat([df_scan1, df_scan2]) + df = df_scan + print('\n') + print('-------------------') + print('thickness and size') + print('-------------------') + print('print scan metadata:') + print('patient number:', df.shape[0]) + print(df['thk'].value_counts()) + print(df['thk'].value_counts(normalize=True).round(3)) + print(df['size'].value_counts()) + print(df['size'].value_counts(normalize=True).round(3)) + ## slice thickness + print('\n') + print('-------------------') + print('slice thickness') + print('-------------------') + print('thk mean:', df['thk'].mean().round(3)) + print('thk median:', df['thk'].median()) + print('thk mode:', df['thk'].mode()) + print('thk std:', df['thk'].std().round(3)) + print('thk min:', df['thk'].min()) + print('thk max:', df['thk'].max()) + ## voxel spacing + print('\n') + print('-------------------') + print('spacing info') + print('-------------------') + print('spacing mean:', df['spacing'].mean().round(3)) + print('spacing median:', df['spacing'].median()) + print('spacing mode:', df['spacing'].mode()) + print('spacing std:', df['spacing'].std().round(3)) + print('spacing min:', df['spacing'].min()) + print('spacing max:', df['spacing'].max()) + + df = df_scan3 + print('\n') + print('-------------------') + print('scanner info') + print('-------------------') + print('patient number:', df.shape[0]) + ## scanner type + print(df['scanner'].value_counts()) + print(df['scanner'].value_counts(normalize=True).round(3)) + ## tued voltage (kvp) + print('\n') + print('-------------------') + print('KVP') + print('-------------------') + print('kvp mean:', df['kvp'].mean().round(3)) + print('kvp median:', df['kvp'].median()) + print('kvp mode:', df['kvp'].mode()) + print('kvp std:', df['kvp'].std().round(3)) + print('kvp min:', df['kvp'].min()) + print('kvp max:', df['kvp'].max()) + +#----------------------------------------------------------------------------------- +# run funtions +#----------------------------------------------------------------------------------- +if __name__ == '__main__': + + pro_data_dir = '/home/bhkann/zezhong/git_repo/IV-Contrast-CNN-Project/pro_data' + data_pro_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data_pro' + harvard_rt_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data/NSCLC_data_reg' + + chest_metadata( + harvard_rt_dir=harvard_rt_dir, + data_exclude=None, + pro_data_dir=pro_data_dir, + data_pro_dir=data_pro_dir, + split=True, + ) + + diff --git a/utils/cm_all.py b/utils/cm_all.py new file mode 100644 index 0000000..d0f6504 --- /dev/null +++ b/utils/cm_all.py @@ -0,0 +1,111 @@ +#---------------------------------------------------------------------- +# Deep learning for classification for contrast CT; +# Transfer learning using Google Inception V3; +#----------------------------------------------------------------------------------------- +import os +import numpy as np +import pandas as pd +import seaborn as sn +import matplotlib.pyplot as plt +import glob +from time import gmtime, strftime +from datetime import datetime +import timeit +from sklearn.model_selection import train_test_split, GroupShuffleSplit +from sklearn.metrics import classification_report, confusion_matrix +from sklearn.metrics import accuracy_score +from sklearn.metrics import roc_curve, auc, precision_recall_curve +from utils.plot_cm import plot_cm + +# ---------------------------------------------------------------------------------- +# plot ROI +# ---------------------------------------------------------------------------------- +def cm_all(run_type, level, thr_img, thr_prob, thr_pos, pro_data_dir, save_dir, fn_df_pred): + + df_sum = pd.read_csv(os.path.join(pro_data_dir, fn_df_pred)) + + if level == 'img': + y_true = df_sum['label'].to_numpy() + preds = df_sum['y_pred'].to_numpy() + y_pred = [] + for pred in preds: + if pred > thr_img: + pred = 1 + else: + pred = 0 + y_pred.append(pred) + y_pred = np.asarray(y_pred) + print_info = 'cm image:' + + elif level == 'patient_mean_prob': + df_mean = df_sum.groupby(['ID']).mean() + y_true = df_mean['label'].to_numpy() + preds = df_mean['y_pred'].to_numpy() + y_pred = [] + for pred in preds: + if pred > thr_prob: + pred = 1 + else: + pred = 0 + y_pred.append(pred) + y_pred = np.asarray(y_pred) + print_info = 'cm patient prob:' + + elif level == 'patient_mean_pos': + df_mean = df_sum.groupby(['ID']).mean() + y_true = df_mean['label'].to_numpy() + pos_rates = df_mean['y_pred_class'].to_list() + y_pred = [] + for pos_rate in pos_rates: + if pos_rate > thr_pos: + pred = 1 + else: + pred = 0 + y_pred.append(pred) + y_pred = np.asarray(y_pred) + print_info = 'cm patient pos:' + + ### using confusion matrix to calculate AUC + cm = confusion_matrix(y_true, y_pred) + cm_norm = cm.astype('float64') / cm.sum(axis=1)[:, np.newaxis] + cm_norm = np.around(cm_norm, 2) + + ## classification report + report = classification_report(y_true, y_pred, digits=3) + + # statistics + fp = cm[0][1] + fn = cm[1][0] + tp = cm[1][1] + tn = cm[0][0] + acc = (tp + tn)/(tp + fp + fn + tn) + tpr = tp/(tp + fn) + tnr = tn/(tn + fp) + tpr = np.around(tpr, 3) + tnr = np.around(tnr, 3) + auc5 = (tpr + tnr)/2 + auc5 = np.around(auc5, 3) + + print(print_info) + print(cm) + print(cm_norm) + print(report) + + ## plot cm + for cm0, cm_type in zip([cm, cm_norm], ['raw', 'norm']): + plot_cm( + cm0=cm0, + cm_type=cm_type, + level=level, + save_dir=save_dir + ) + + return cm, cm_norm, report + + + + + + + + diff --git a/utils/contrast_metadata.py b/utils/contrast_metadata.py new file mode 100644 index 0000000..27d79da --- /dev/null +++ b/utils/contrast_metadata.py @@ -0,0 +1,60 @@ +import os +import pandas as pd +import numpy as np +import glob + + +pro_data_dir = '/home/bhkann/zezhong/git_repo/IV-Contrast-CNN-Project/pro_data' + +## actual data from df +train_data = pd.read_csv(os.path.join(pro_data_dir, 'train_pat_df.csv')) +val_data = pd.read_csv(os.path.join(pro_data_dir, 'val_pat_df.csv')) +test_data = pd.read_csv(os.path.join(pro_data_dir, 'test_pat_df.csv')) +data = pd.concat([train_data, val_data, test_data]) + +## clinical meta data +df = pd.read_csv(os.path.join(pro_data_dir, 'clinical_meta_data.csv')) +df['contrastbolusagent'] = df['contrastbolusagent'].fillna(2) + +pat_ids = [] +contrasts = [] +for patientid, contrast in zip(df['patientid'], df['contrastbolusagent']): + if patientid[3:7] == 'CHUM': + pat_id = 'CHUM' + patientid[-3:] + elif patientid[3:7] == 'CHUS': + pat_id = 'CHUS' + patientid[-3:] + elif patientid[:3] == 'OPC': + pat_id = 'PMH' + patientid[-3:] + elif patientid[:5] == 'HNSCC': + pat_id = 'MDACC' + patientid[-3:] + if pat_id in data['ID'].to_list(): + pat_ids.append(pat_id) + ## change contrast annotation in meta data + if contrast in ['N', 'n', 'NO']: + contrast = 0 + elif contrast == 2: + contrast = contrast + else: + contrast = 1 + contrasts.append(contrast) +df = pd.DataFrame({'ID': pat_ids, 'contrast': contrasts}) + +## match metadata annotations with clinical expert +ids = [] +contrasts = [] +labels = [] +for ID, label in zip(data['ID'], data['label']): + for pat_id, contrast in zip(df['ID'], df['contrast']): + if pat_id == ID and contrast != 2 and contrast != label: + ids.append(pat_id) + contrasts.append(contrast) + labels.append(label) +print('mismatch ID:', ids) +print('mismatch label:', labels) +print('mismatch label:', contrasts) +print('mismatch number:', len(contrasts)) + +print('total patient:', df['contrast'].shape[0]) +print(df['contrast'].value_counts()) +print(df['contrast'].value_counts(normalize=True)) + diff --git a/utils/crop_image.py b/utils/crop_image.py new file mode 100644 index 0000000..edd1f53 --- /dev/null +++ b/utils/crop_image.py @@ -0,0 +1,125 @@ +import os +import itertools +import operator +import numpy as np +import SimpleITK as sitk +from scipy import ndimage +from tensorflow import keras +import tensorflow as tf +import matplotlib.pyplot as plt +import matplotlib.cm as cm +import cv2 +import nrrd +from PIL import Image + + +#-------------------------------------------------------------------------------------- +# crop image +#------------------------------------------------------------------------------------- +def crop_image(nrrd_file, patient_id, crop_shape, return_type, save_dir): + + ## load stik and arr + img_arr = sitk.GetArrayFromImage(nrrd_file) + ## Return top 25 rows of 3D volume, centered in x-y space / start at anterior (y=0)? +# img_arr = np.transpose(img_arr, (2, 1, 0)) +# print("image_arr shape: ", img_arr.shape) + c, y, x = img_arr.shape +# x, y, c = image_arr.shape +# print('c:', c) +# print('y:', y) +# print('x:', x) + + ## Get center of mass to center the crop in Y plane + mask_arr = np.copy(img_arr) + mask_arr[mask_arr > -500] = 1 + mask_arr[mask_arr <= -500] = 0 + mask_arr[mask_arr >= -500] = 1 + #print("mask_arr min and max:", np.amin(mask_arr), np.amax(mask_arr)) + centermass = ndimage.measurements.center_of_mass(mask_arr) # z,x,y + cpoint = c - crop_shape[2]//2 + #print("cpoint, ", cpoint) + centermass = ndimage.measurements.center_of_mass(mask_arr[cpoint, :, :]) + #print("center of mass: ", centermass) + startx = int(centermass[0] - crop_shape[0]//2) + starty = int(centermass[1] - crop_shape[1]//2) + #startx = x//2 - crop_shape[0]//2 + #starty = y//2 - crop_shape[1]//2 + startz = int(c - crop_shape[2]) + #print("start X, Y, Z: ", startx, starty, startz) + + ## crop image using crop shape + if startz < 0: + img_arr = np.pad( + img_arr, + ((abs(startz)//2, abs(startz)//2), (0, 0), (0, 0)), + 'constant', + constant_values=-1024 + ) + img_crop_arr = img_arr[ + 0:crop_shape[2], + starty:starty + crop_shape[1], + startx:startx + crop_shape[0] + ] + else: + img_crop_arr = img_arr[ +# 0:crop_shape[2], + startz:startz + crop_shape[2], + starty:starty + crop_shape[1], + startx:startx + crop_shape[0] + ] + if img_crop_arr.shape[0] < crop_shape[2]: + print('initial cropped image shape too small:', img_arr.shape) + print(crop_shape[2], img_crop_arr.shape[0]) + img_crop_arr = np.pad( + img_crop_arr, + ((int(crop_shape[2] - img_crop_arr.shape[0]), 0), (0, 0), (0, 0)), + 'constant', + constant_values=-1024 + ) + print("padded size: ", img_crop_arr.shape) + #print(img_crop_arr.shape) + ## get nrrd from numpy array + img_crop_nrrd = sitk.GetImageFromArray(img_crop_arr) + img_crop_nrrd.SetSpacing(nrrd_file.GetSpacing()) + img_crop_nrrd.SetOrigin(nrrd_file.GetOrigin()) + + if save_dir != None: + fn = str(patient_id) + '.nrrd' + writer = sitk.ImageFileWriter() + writer.SetFileName(os.path.join(save_dir, fn)) + writer.SetUseCompression(True) + writer.Execute(img_crop_nrrd) + + if return_type == 'nrrd': + return img_crop_nrrd + + elif return_type == 'npy': + return img_crop_arr + +#----------------------------------------------------------------------- +# run codes to test +#----------------------------------------------------------------------- +if __name__ == '__main__': + +# output_dir = '/media/bhkann/HN_RES1/HN_CONTRAST/output' + output_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/test' + file_dir = '/media/bhkann/HN_RES1/HN_CONTRAST/0_image_raw_PMH' + test_file = 'PMH_OPC-00050_CT-SIM_raw_raw_raw_xx.nrrd' + return_type = 'sitk' + crop_shape = [192, 192, 110] + + train_file = os.path.join(file_dir, test_file) + + img_crop = crop_image( + nrrd_file=train_file, + crop_shape=crop_shape, + return_type=return_type, + output_dir=output_dir + ) + print('crop arr shape:', img_crop) +# arr_crop = image_arr_crop[0, :, :] +# img_dir = os.path.join(output_dir, 'arr_crop.jpg') +# plt.imsave(img_dir, arr_crop, cmap='gray') + print('successfully save image!!!') + + diff --git a/utils/delete_row.py b/utils/delete_row.py new file mode 100644 index 0000000..bf0c6b8 --- /dev/null +++ b/utils/delete_row.py @@ -0,0 +1,14 @@ +import pandas as pd +import os + + +#data_dir = '/home/bhkann/zezhong/git_repo/IV-Contrast-CNN-Project/ahmed_data' +#df = pd.read_csv(os.path.join(data_dir, 'rtog-0617_pat.csv')) +#df.drop(df.index[[343]], inplace=True) +#df.to_csv(os.path.join(data_dir, 'rtog-0617_pat.csv')) + + +data_dir = '/home/bhkann/zezhong/git_repo/IV-Contrast-CNN-Project/pro_data' +df = pd.read_csv(os.path.join(data_dir, 'rtog_img_df.csv')) +df.drop(df.index[range(24010, 24080)], axis=0, inplace=True) +df.to_csv(os.path.join(data_dir, 'rtog_img_df.csv')) diff --git a/utils/error_analysis.py b/utils/error_analysis.py new file mode 100644 index 0000000..920b28e --- /dev/null +++ b/utils/error_analysis.py @@ -0,0 +1,286 @@ + +import os +import numpy as np +import pandas as pd +import seaborn as sn +import matplotlib as mpl +import matplotlib.pyplot as plt +import glob +from collections import Counter +from datetime import datetime +from time import localtime, strftime +import matplotlib.pyplot as plt +import tensorflow as tf +from tensorflow.keras.models import load_model +from crop_image import crop_image +from resize_3d import resize_3d +import SimpleITK as sitk + +#---------------------------------------------------------------- +# error analysis on image level +#--------------------------------------------------------------- +def error_img(run_type, val_save_dir, test_save_dir, input_channel, crop, pro_data_dir): + + ### load train data based on input channels + if run_type == 'val': + file_dir = val_save_dir + input_fn = 'df_val_pred.csv' + output_fn = 'val_error_img.csv' + col = 'y_val' + elif run_type == 'test': + file_dir = test_save_dir + input_fn = 'df_test_pred.csv' + output_fn = 'test_error_img.csv' + col = 'y_test' + elif run_type == 'exval2': + file_dir = pro_data_dir + input_fn = 'exval2_img_pred.csv' + output_fn = 'exval2_error_img.csv' + col = 'y_exval2' + + ### dataframe with label and predictions + df = pd.read_csv(os.path.join(file_dir, input_fn)) + print(df[0:10]) + #df.drop([col, 'ID'], axis=1, inplace=True) + df = df.drop(df[df['y_pred_class'] == df['label']].index) + df[['y_pred']] = df[['y_pred']].round(3) + pd.options.display.max_columns = 100 + pd.set_option('display.max_rows', 500) + print(df[0:200]) + df.to_csv(os.path.join(file_dir, output_fn)) + df_error_img = df + + return df_error_img +#---------------------------------------------------- +# generate images for error check +#---------------------------------------------------- +def save_error_img(df_error_img, run_type, n_img, val_img_dir, test_img_dir, + val_error_dir, test_error_dir, pro_data_dir): + + ### store all indices that has wrong predictions + indices = [] + df = df_error_img + for i in range(df.shape[0]): + indices.append(i) + print(x_val.shape) + arr = x_val.take(indices=indices, axis=0) + print(arr.shape) + arr = arr[:, :, :, 0] + print(arr.shape) + arr = arr.reshape((arr.shape[0], 192, 192)) + print(arr.shape) + + ## load img data + if run_type == 'val': + if input_channel == 1: + fn = 'val_arr_1ch.npy' + elif input_channel == 3: + fn = 'val_arr_3ch.npy' + x_val = np.load(os.path.join(data_pro_dir, fn)) + file_dir = val_error_dir + elif run_type == 'test': + if input_channel == 1: + fn = 'test_arr_1ch.npy' + elif input_channel == 3: + fn = 'test_arr_3ch.npy' + x_val = np.load(os.path.join(data_pro_dir, fn)) + file_dir = test_error_dir + elif run_type == 'exval2': + if input_channel == 1: + fn = 'exval2_arr_1ch.npy' + elif input_channel == 3: + fn = 'exval2_arr.npy' + x_val = np.load(os.path.join(pro_data_dir, fn)) + file_dir = pro_data_dir + + ### display images for error checks + count = 0 + for i in range(n_img): + # for i in range(arr.shape[0]): + #count += 1 + #print(count) + img = arr[i, :, :] + fn = str(i) + '.jpeg' + img_fn = os.path.join(file_dir, fn) + mpl.image.imsave(img_fn, img, cmap='gray') + + print('save images complete!!!') + +#---------------------------------------------------------------------- +# error analysis on patient level +#---------------------------------------------------------------------- +def error_pat(run_type, val_dir, test_dir, exval2_dir, threshold, pro_data_dir): + + if run_type == 'val': + input_fn = 'val_img_pred.csv' + output_fn = 'val_pat_error.csv' + drop_col = 'y_val' + save_dir = val_dir + elif run_type == 'test': + input_fn = 'test_img_pred.csv' + output_fn = 'test_pat_error.csv' + drop_col = 'y_test' + save_dir = test_dir + elif run_type == 'exval2': + input_fn = 'rtog_img_pred.csv' + output_fn = 'rtog_pat_error.csv' + drop_col = 'y_exval2' + save_dir = exval2_dir + + df_sum = pd.read_csv(os.path.join(pro_data_dir, input_fn)) + df_mean = df_sum.groupby(['ID']).mean() + y_true = df_mean['label'] + y_pred = df_mean['y_pred'] + y_pred_classes = [] + for pred in y_pred: + if pred < threshold: + y_pred_class = 0 + elif pred >= threshold: + y_pred_class = 1 + y_pred_classes.append(y_pred_class) + df_mean['y_pred_class_thr'] = y_pred_classes + df = df_mean + df[['y_pred', 'y_pred_class']] = df[['y_pred', 'y_pred_class']].round(3) + df['label'] = df['label'].astype(int) + df = df.drop(df[df['y_pred_class_thr'] == df['label']].index) + #df.drop(['y_exval'], inplace=True, axis=1) + df.drop([drop_col], inplace=True, axis=1) + pd.options.display.max_columns = 100 + pd.set_option('display.max_rows', 500) + print(df) + #print("miss predicted scan:", df.shape[0]) + df_error_patient = df + df.to_csv(os.path.join(save_dir, output_fn)) + +#---------------------------------------------------------------------- +# save error scan +#---------------------------------------------------------------------- +def save_error_pat(run_type, val_save_dir, test_save_dir, val_error_dir, test_error_dir, norm_type, + interp_type, output_size, PMH_reg_dir, CHUM_reg_dir, CHUS_reg_dir): + count = 0 + dirs = [] + + if run_type == 'val': + save_dir = val_error_dir + df = pd.read_csv(os.path.join(val_save_dir, 'val_error_patient_new.csv')) + IDs = df['ID'].to_list() + for ID in IDs: + print('error scan:', ID) + fn = str(ID) + '.nrrd' + if ID[:-3] == 'PMH': + dir = os.path.join(PMH_reg_dir, fn) + elif ID[:-3] == 'CHUS': + dir = os.path.join(CHUS_reg_dir, fn) + elif ID[:-3] == 'CHUM': + dir = os.path.join(CHUM_reg_dir, fn) + dirs.append(dir) + + elif run_type == 'test': + save_dir = test_error_dir + df = pd.read_csv(os.path.join(test_save_dir, 'test_error_patient_new.csv')) + IDs = df['ID'].to_list() + for ID in IDs: + print('error scan:', ID) + fn = str(ID) + '.nrrd' + dir = os.path.join(MDACC_reg_dir, fn) + dirs.append(dir) + + for file_dir, patient_id in zip(dirs, IDs): + count += 1 + print(count) + nrrd = sitk.ReadImage(file_dir, sitk.sitkFloat32) + img_arr = sitk.GetArrayFromImage(nrrd) + data = img_arr[30:78, :, :] + data[data <= -1024] = -1024 + data[data > 700] = 0 + if norm_type == 'np_interp': + arr_img = np.interp(data, [-200, 200], [0, 1]) + elif norm_type == 'np_clip': + arr_img = np.clip(data, a_min=-200, a_max=200) + MAX, MIN = arr_img.max(), arr_img.min() + arr_img = (arr_img - MIN) / (MAX - MIN) + ## save npy array to image + img = sitk.GetImageFromArray(arr_img) + fn = str(patient_id) + '.nrrd' + sitk.WriteImage(img, os.path.join(save_dir, fn)) + print('save error scans!') + + ## save error scan as nrrd file + +#---------------------------------------------------------------------------- +# main funtion +#--------------------------------------------------------------------------- +if __name__ == '__main__': + + val_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/val' + test_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/test' + exval2_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/exval2' + val_error_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/val/error' + test_error_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/test/error' + mdacc_data_dir = '/media/bhkann/HN_RES1/HN_CONTRAST/0_image_raw_mdacc' + CHUM_reg_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data/CHUM_data_reg' + CHUS_reg_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data/CHUS_data_reg' + PMH_reg_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data/PMH_data_reg' + MDACC_reg_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data/MDACC_data_reg' + pro_data_dir = '/home/bhkann/zezhong/git_repo/IV-Contrast-CNN-Project/pro_data' + + input_channel = 3 + crop = True + n_img = 20 + save_img = False + save_pat = False + error_image = False + thr_prob = 0.5 + run_type = 'val' + norm_type = 'np_clip' + crop_shape = [192, 192, 110] + return_type1 = 'nrrd' + return_type2 = 'npy' + interp_type = 'linear' + output_size = (96, 96, 36) + + error_pat( + run_type=run_type, + val_dir=val_dir, + test_dir=test_dir, + exval2_dir=exval2_dir, + threshold=thr_prob, + pro_data_dir=pro_data_dir + ) + + if error_image == True: + df = error_img( + run_type=run_type, + val_save_dir=val_save_dir, + test_save_dir=test_save_dir, + input_channel=input_channel, + crop=crop, + pro_data_dir=pro_data_dir + ) + + if save_pat == True: + save_error_pat( + run_type=run_type, + val_save_dir=val_save_dir, + test_save_dir=test_save_dir, + val_error_dir=val_error_dir, + test_error_dir=test_error_dir, + norm_type=norm_type, + interp_type=interp_type, + output_size=output_size, + PMH_reg_dir=PMH_reg_dir, + CHUM_reg_dir=CHUM_reg_dir, + CHUS_reg_dir=CHUS_reg_dir + ) + + if save_img == True: + save_error_img( + df=df, + n_img=n_img, + run_type=run_type, + val_img_dir=val_img_dir, + test_img_dir=test_img_dir, + val_error_dir=vel_error_dir, + test_error_dir=test_error_dir, + pro_data_dir=pro_data_dir + ) diff --git a/utils/get_pat_img_df.py b/utils/get_pat_img_df.py new file mode 100644 index 0000000..a6b7462 --- /dev/null +++ b/utils/get_pat_img_df.py @@ -0,0 +1,94 @@ +import pandas as pd +import numpy as np +import os +import glob + + +#------------------------------------------------------------------------- +# create patient df with ID, label and dir +#------------------------------------------------------------------------- +def get_pat_df(pro_data_dir, reg_data_dir, label_file, fn_pat_df): + + ## create df for dir, ID and labels on patient level + df_label = pd.read_csv(os.path.join(pro_data_dir, label_file)) + df_label['Contrast'] = df_label['Contrast'].map({'Yes': 1, 'No': 0}) + labels = df_label['Contrast'].to_list() + fns = [fn for fn in sorted(glob.glob(reg_data_dir + '/*nrrd'))] + IDs = [] + for fn in fns: + ID = fn.split('/')[-1].split('_')[1].split('.')[0].strip() + IDs.append(ID) + pat_ids = [] + labels = [] + for pat_id, pat_label in zip(df_label['Patient ID'], df_label['Contrast']): + if pat_id in IDs: + pat_id = 'rtog' + '_' + str(pat_id) + pat_ids.append(pat_id) + labels.append(pat_label) + print("ID:", len(pat_ids)) + print("dir:", len(fns)) + print("label:", len(labels)) + print('contrast scan in ex val:', labels.count(1)) + print('non-contrast scan in ex val:', labels.count(0)) + df = pd.DataFrame({'ID': IDs, 'file': fns, 'label': labels}) + df.to_csv(os.path.join(pro_data_dir, fn_pat_df)) + print('total scan:', df.shape[0]) + +#------------------------------------------------------------------------- +# create img df with ID, label +#------------------------------------------------------------------------- +def get_img_df(pro_data_dir, fn_pat_df, fn_img_df, slice_range): + + pat_df = pd.read_csv(os.path.join(pro_data_dir, fn_pat_df)) + ## img ID + slice_number = len(slice_range) + img_ids = [] + for pat_id in pat_df['ID']: + for i in range(slice_number): + img_id = 'rtog' + '_' + pat_id + '_' + 'slice%s'%(f'{i:03d}') + img_ids.append(img_id) + #print(img_ids) + print(len(img_ids)) + ## img label + pat_label = pat_df['label'].to_list() + img_label = [] + for label in pat_label: + list2 = [label] * slice_number + img_label.extend(list2) + #print(img_label) + print(len(img_label)) + ### makeing dataframe containing img IDs and labels + df = pd.DataFrame({'fn': img_ids, 'label': img_label}) + print(df[0:10]) + df.to_csv(os.path.join(pro_data_dir, fn_img_df)) + print('total img:', df.shape[0]) + +#------------------------------------------------------------------------- +# create patient df with ID, label and dir +#------------------------------------------------------------------------- +if __name__ == '__main__': + + pro_data_dir = '/home/bhkann/zezhong/git_repo/IV-Contrast-CNN-Project/pro_data' + slice_range = range(50, 120) + reg_data_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/ahmed_data/rtog-0617_reg' + label_file = 'label_RTOG0617.csv' + fn_pat_df = 'rtog_pat_df.csv' + fn_img_df = 'rtog_img_df.csv' + + get_pat_df( + pro_data_dir=pro_data_dir, + reg_data_dir=reg_data_dir, + label_file=label_file, + fn_pat_df=fn_pat_df + ) + + get_img_df( + pro_data_dir=pro_data_dir, + fn_pat_df=fn_pat_df, + fn_img_df=fn_img_df, + slice_range=slice_range + ) + + + + diff --git a/utils/get_pred_img_df.py b/utils/get_pred_img_df.py new file mode 100644 index 0000000..7ed2e3f --- /dev/null +++ b/utils/get_pred_img_df.py @@ -0,0 +1,48 @@ +import pandas as pd +import numpy as np +import os +from utils.make_plots import make_plots + +def get_pred_img_df(ahmed_data_dir, pro_data_dir, slice_range): + + df = pd.read_csv(os.path.join(ahmed_data_dir, 'rtog-0617_img_pred.csv')) + df_label = pd.read_csv(os.path.join(pro_data_dir, 'label_RTOG0617.csv')) + df_label['Contrast'] = df_label['Contrast'].map({'Yes': 1, 'No': 0}) + pat_label = df_label['Contrast'].to_list() + img_label = [] + for label in pat_label: + list1 = [label] * slice_number + img_label.extend(list1) + df['label'] = img_label + df.to_csv(os.path.join(pro_data_dir, 'exval2_img_pred.csv')) + +if __name__ == '__main__': + + ahmed_data_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data_pro' + pro_data_dir = '/home/bhkann/zezhong/git_repo/IV-Contrast-CNN-Project/pro_data' + exval2_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/d_pro' + slice_range = range(50, 70) + saved_model = 'FineTuned_model_2021_07_27_16_44_40' + + get_pred_img_df( + ahmed_data_dir=ahmed_data_dir, + pro_data_dir=pro_data_dir, + slice_range=slice_range + ) + + make_plots( + run_type='exval2', + thr_img=0.321, + thr_prob=0.379, + thr_pos=0.575, + bootstrap=1000, + pro_data_dir=pro_data_dir, + save_dir=exval2_dir, + loss=None, + acc=None, + run_model='ResNet', + saved_model=saved_model, + epoch=100, + batch_size=32, + lr=1e-5 + ) diff --git a/utils/get_stats_plots.py b/utils/get_stats_plots.py new file mode 100644 index 0000000..b8d44d1 --- /dev/null +++ b/utils/get_stats_plots.py @@ -0,0 +1,147 @@ +import os +import numpy as np +import pandas as pd +import pickle +from time import gmtime, strftime +from datetime import datetime +import timeit +from utils.cm_all import cm_all +from utils.roc_all import roc_all +from utils.prc_all import prc_all +#from utils.acc_loss import acc_loss +from utils.write_txt import write_txt + + + +def get_stats_plots(out_dir, proj_dir, run_type, run_model, loss, acc, + saved_model, epoch, batch_size, lr, thr_img=0.5, + thr_prob=0.5, thr_pos=0.5, bootstrap=1000): + + """ + generate model val/test statistics and plot curves; + + Args: + loss {float} -- validation loss; + acc {float} -- validation accuracy; + run_model {str} -- cnn model name; + batch_size {int} -- batch size for data loading; + epoch {int} -- training epoch; + out_dir {path} -- path for output files; + opt {str or function} -- optimized function: 'adam'; + lr {float} -- learning rate; + + Keyword args: + bootstrap {int} -- number of bootstrap to calculate 95% CI for AUC; + thr_img {float} -- threshold to determine positive class on image level; + thr_prob {float} -- threshold to determine positive class on patient + level (mean prob score); + thr_pos {float} -- threshold to determine positive class on patient + level (positive class percentage); + Returns: + Model prediction statistics and plots: ROC, PRC, confusion matrix, etc. + + """ + + pro_data_dir = os.path.join(proj_dir, 'pro_data') + train_dir = os.path.join(out_dir, 'train') + val_dir = os.path.join(out_dir, 'val') + test_dir = os.path.join(out_dir, 'test') + exval1_dir = os.path.join(out_dir, 'exval1') + exval2_dir = os.path.join(out_dir, 'exval2') + + if not os.path.exists(train_dir): + os.mkdir(train_dir) + if not os.path.exists(val_dir): + os.mkdir(val_dir) + if not os.path.exists(test_dir): + os.mkdir(test_dir) + if not os.path.exists(exval1_dir): + os.mkdir(exval1_dir) + if not os.path.exists(exval2_dir): + os.mkdir(exval2_dir) + + ### determine if this is train or test + if run_type == 'val': + fn_df_pred = 'val_img_pred.csv' + save_dir = val_dir + elif run_type == 'test': + fn_df_pred = 'test_img_pred.csv' + save_dir = test_dir + elif run_type == 'exval1': + fn_df_pred = 'exval1_img_pred.csv' + save_dir = exval1_dir + elif run_type == 'exval2': + fn_df_pred = 'exval2_img_pred.csv' + save_dir = exval2_dir + + cms = [] + cm_norms = [] + reports = [] + roc_stats = [] + prc_aucs = [] + levels = ['img', 'patient_mean_prob', 'patient_mean_pos'] + + for level in levels: + + ## confusion matrix + cm, cm_norm, report = cm_all( + run_type=run_type, + level=level, + thr_img=thr_img, + thr_prob=thr_prob, + thr_pos=thr_pos, + pro_data_dir=pro_data_dir, + save_dir=save_dir, + fn_df_pred=fn_df_pred + ) + cms.append(cm) + cm_norms.append(cm_norm) + reports.append(report) + + ## ROC curves + roc_stat = roc_all( + run_type=run_type, + level=level, + thr_prob=thr_prob, + thr_pos=thr_pos, + bootstrap=bootstrap, + color='blue', + pro_data_dir=pro_data_dir, + save_dir=save_dir, + fn_df_pred=fn_df_pred + ) + roc_stats.append(roc_stat) + + ## PRC curves + prc_auc = prc_all( + run_type=run_type, + level=level, + thr_prob=thr_prob, + thr_pos=thr_pos, + color='red', + pro_data_dir=pro_data_dir, + save_dir=save_dir, + fn_df_pred=fn_df_pred + ) + prc_aucs.append(prc_auc) + + ### save validation results to txt + write_txt( + run_type=run_type, + out_dir=out_dir, + loss=loss, + acc=acc, + cms=cms, + cm_norms=cm_norms, + reports=reports, + prc_aucs=prc_aucs, + roc_stats=roc_stats, + run_model=run_model, + saved_model=saved_model, + epoch=epoch, + batch_size=batch_size, + lr=lr + ) + + print('saved model as:', saved_model) + diff --git a/utils/gradcam.py b/utils/gradcam.py new file mode 100644 index 0000000..676fd90 --- /dev/null +++ b/utils/gradcam.py @@ -0,0 +1,301 @@ +from tensorflow.keras.models import Model +import tensorflow as tf +from tensorflow import keras +import matplotlib.pyplot as plt +import matplotlib.cm as cm +import numpy as np +import cv2 +import numpy as np +import pandas as pd +from tensorflow.keras.models import load_model +import tensorflow as tf +import os + +#--------------------------------------------------------------------------------- +# get data +#--------------------------------------------------------------------------------- +def data(input_channel, i, val_save_dir, test_save_dir): + + ### load train data based on input channels + if run_type == 'val': + if input_channel == 1: + fn = 'val_arr_1ch.npy' + elif input_channel == 3: + fn = 'val_arr_3ch.npy' + data = np.load(os.path.join(pro_data_dir, fn)) + df = pd.read_csv(os.path.join(val_save_dir, 'val_pred_df.csv')) + elif run_type == 'test': + if input_channel == 1: + fn = 'test_arr_1ch.npy' + elif input_channel == 3: + fn = 'test_arr_3ch.npy' + data = np.load(os.path.join(pro_data_dir, fn)) + df = pd.read_csv(os.path.join(test_save_dir, 'test_pred_df.csv')) + elif run_type == 'exval': + if input_channel == 1: + fn = 'exval_arr_1ch.npy' + elif input_channel == 3: + fn = 'exval_arr_3ch.npy' + data = np.load(os.path.join(pro_data_dir, fn)) + df = pd.read_csv(os.path.join(exval_save_dir, 'exval_pred_df.csv')) + + ### load label + y_true = df['label'] + y_pred_class = df['y_pred_class'] + y_pred = df['y_pred'] + ID = df['fn'] + ### find the ith image to show grad-cam map + img = data[i, :, :, :] + img = img.reshape((1, 192, 192, 3)) + label = y_true[i] + pred_index = y_pred_class[i] + y_pred = y_pred[i] + ID = ID[i] + + return img, label, pred_index, y_pred, ID + +#------------------------------------------------------------------------------------ +# find last conv layer +#----------------------------------------------------------------------------------- +def find_target_layer(model, saved_model): + + # find the final conv layer by looping layers in reverse order + for layer in reversed(model.layers): + # check to see if the layer has a 4D output + if len(layer.output_shape) == 4: + return layer.name + raise ValueError("Could not find 4D layer. Cannot apply GradCAM.") + +#---------------------------------------------------------------------------------- +# calculate gradient class actiavtion map +#---------------------------------------------------------------------------------- +def compute_heatmap(model, saved_model, image, pred_index, last_conv_layer): + + """ + construct our gradient model by supplying (1) the inputs + to our pre-trained model, (2) the output of the (presumably) + final 4D layer in the network, and (3) the output of the + softmax activations from the model + """ + gradModel = Model( + inputs=[model.inputs], + outputs=[model.get_layer(last_conv_layer).output, model.output] + ) + + # record operations for automatic differentiation + with tf.GradientTape() as tape: + """ + cast the image tensor to a float-32 data type, pass the + image through the gradient model, and grab the loss + associated with the specific class index + """ + print(pred_index) + inputs = tf.cast(image, tf.float32) + print(image.shape) + last_conv_layer_output, preds = gradModel(inputs) + print(preds) + print(preds.shape) + # class_channel = preds[:, pred_index] + class_channel = preds + # use automatic differentiation to compute the gradients + grads = tape.gradient(class_channel, last_conv_layer_output) + """ + This is a vector where each entry is the mean intensity of the gradient + over a specific feature map channel + """ + pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2)) + """ + We multiply each channel in the feature map array + by "how important this channel is" with regard to the top predicted class + then sum all the channels to obtain the heatmap class activation + """ + last_conv_layer_output = last_conv_layer_output[0] + heatmap = last_conv_layer_output @ pooled_grads[..., tf.newaxis] + heatmap = tf.squeeze(heatmap) + + # For visualization purpose, we will also normalize the heatmap between 0 & 1 + heatmap = tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap) + heatmap = heatmap.numpy() + + return heatmap + +#------------------------------------------------------------------------------------ +# save gradcam heat map +#----------------------------------------------------------------------------------- +def save_gradcam(image, heatmap, val_gradcam_dir, test_gradcam_dir, alpha, i): + +# print('heatmap:', heatmap.shape) + # Rescale heatmap to a range 0-255 + heatmap = np.uint8(255 * heatmap) + # Use jet colormap to colorize heatmap + jet = cm.get_cmap("jet") + # Use RGB values of the colormap + jet_colors = jet(np.arange(256))[:, :3] + jet_heatmap = jet_colors[heatmap] + + # resize heatmap + jet_heatmap = keras.preprocessing.image.array_to_img(jet_heatmap) + jet_heatmap0 = jet_heatmap.resize(re_size) + jet_heatmap1 = keras.preprocessing.image.img_to_array(jet_heatmap0) +# print('jet_heatmap:', jet_heatmap1.shape) + + # resize background CT image + img = image.reshape((192, 192, 3)) + img = keras.preprocessing.image.array_to_img(img) + img0 = img.resize(re_size) + img1 = keras.preprocessing.image.img_to_array(img0) +# print('img shape:', img1.shape) + + # Superimpose the heatmap on original image + superimposed_img = jet_heatmap1 * alpha + img1 + superimposed_img = keras.preprocessing.image.array_to_img(superimposed_img) + + # Save the superimposed image + if run_type == 'val': + save_dir = val_gradcam_dir + elif run_type == 'test': + save_dir = test_gradcam_dir + elif run_type == 'exval': + save_dir = exval_gradcam_dir + + fn1 = str(conv_n) + '_' + str(i) + '_' + 'gradcam.png' + fn2 = str(conv_n) + '_' + str(i) + '_' + 'heatmap.png' + fn3 = str(conv_n) + '_' + str(i) + '_' + 'heatmap_raw.png' + fn4 = str(i) + '_' + 'CT.png' + superimposed_img.save(os.path.join(save_dir, fn1)) +# jet_heatmap0.save(os.path.join(save_dir, fn2)) +# jet_heatmap.save(os.path.join(save_dir, fn3)) +# img0.save(os.path.join(save_dir, fn4)) + + +if __name__ == '__main__': + + train_img_dir = '/media/bhkann/HN_RES1/HN_CONTRAST/train_img_dir' + val_save_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/val' + test_save_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/test' + exval_save_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/exval' + val_gradcam_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/val/gradcam' + test_gradcam_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/test/gradcam' + exval_gradcam_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/test/gradcam' + pro_data_dir = '/home/bhkann/zezhong/git_repo/IV-Contrast-CNN-Project/pro_data' + model_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/model' + input_channel = 3 + re_size = (192, 192) + i = 72 + crop = True + alpha = 0.9 + saved_model = 'ResNet_2021_07_18_06_28_40' + show_network = False + conv_n = 'conv5' + run_type = 'val' + + #--------------------------------------------------------- + # run main function + #-------------------------------------------------------- + if run_type == 'val': + save_dir = val_save_dir + elif run_type == 'test': + save_dir = test_save_dir + + ## load model and find conv layers + model = load_model(os.path.join(model_dir, saved_model)) +# model.summary() + + list_i = [100, 105, 110, 115, 120, 125] + for i in list_i: + image, label, pred_index, y_pred, ID = data( + input_channel=input_channel, + i=i, + val_save_dir=val_save_dir, + test_save_dir=test_save_dir + ) + + conv_list = ['conv2', 'conv3', 'conv4', 'conv5'] + conv_list = ['conv4'] + for conv_n in conv_list: + if conv_n == 'conv2': + last_conv_layer = 'conv2_block3_1_conv' + elif conv_n == 'conv3': + last_conv_layer = 'conv3_block4_1_conv' + elif conv_n == 'conv4': + last_conv_layer = 'conv4_block6_1_conv' + elif conv_n == 'conv5': + last_conv_layer = 'conv5_block3_out' + + heatmap = compute_heatmap( + model=model, + saved_model=saved_model, + image=image, + pred_index=pred_index, + last_conv_layer=last_conv_layer + ) + + save_gradcam( + image=image, + heatmap=heatmap, + val_gradcam_dir=val_gradcam_dir, + test_gradcam_dir=test_gradcam_dir, + alpha=alpha, + i=i + ) + + print('label:', label) + print('ID:', ID) + print('y_pred:', y_pred) + print('prediction:', pred_index) + print('conv layer:', conv_n) + + + +# if last_conv_layer is None: +# last_conv_layer = find_target_layer( +# model=model, +# saved_model=saved_model +# ) +# print(last_conv_layer) +# +# if show_network == True: +# for idx in range(len(model.layers)): +# print(model.get_layer(index = idx).name) + +# # compute the guided gradients +# castConvOutputs = tf.cast(convOutputs > 0, "float32") +# castGrads = tf.cast(grads > 0, "float32") +# guidedGrads = castConvOutputs * castGrads * grads +# # the convolution and guided gradients have a batch dimension +# # (which we don't need) so let's grab the volume itself and +# # discard the batch +# convOutputs = convOutputs[0] +# guidedGrads = guidedGrads[0] +# +# # compute the average of the gradient values, and using them +# # as weights, compute the ponderation of the filters with +# # respect to the weights +# weights = tf.reduce_mean(guidedGrads, axis=(0, 1)) +# cam = tf.reduce_sum(tf.multiply(weights, convOutputs), axis=-1) +# +# # grab the spatial dimensions of the input image and resize +# # the output class activation map to match the input image +# # dimensions +## (w, h) = (image.shape[2], image.shape[1]) +## heatmap = cv2.resize(cam.numpy(), (w, h)) +# heatmap = cv2.resize(heatmap.numpy(), (64, 64)) +# # normalize the heatmap such that all values lie in the range +## # [0, 1], scale the resulting values to the range [0, 255], +## # and then convert to an unsigned 8-bit integer +# numer = heatmap - np.min(heatmap) +# eps = 1e-8 +# denom = (heatmap.max() - heatmap.min()) + eps +# heatmap = numer / denom +# heatmap = (heatmap * 255).astype("uint8") +# colormap=cv2.COLORMAP_VIRIDIS +# heatmap = cv2.applyColorMap(heatmap, colormap) +# print('heatmap shape:', heatmap.shape) +## img = image[:, :, :, 0] +## print('img shape:', img.shape) +# img = image.reshape((64, 64, 3)) +# print(img.shape) +# output = cv2.addWeighted(img, 0.5, heatmap, 0.5, 0) +# +# +# return heatmap, output diff --git a/utils/gradcam2.py b/utils/gradcam2.py new file mode 100644 index 0000000..869e2bc --- /dev/null +++ b/utils/gradcam2.py @@ -0,0 +1,401 @@ +from tensorflow.keras.models import Model +import tensorflow as tf +from tensorflow import keras +import matplotlib.pyplot as plt +import matplotlib.cm as cm +import numpy as np +import cv2 +import numpy as np +import pandas as pd +from tensorflow.keras.models import load_model +import tensorflow as tf +import os +import SimpleITK as sitk + +#------------------------------------------------------------------------------------ +# find last conv layer +#----------------------------------------------------------------------------------- +def find_target_layer(cnn_model): + + # find the final conv layer by looping layers in reverse order + for layer in reversed(cnn_model.layers): + # check to see if the layer has a 4D output + if len(layer.output_shape) == 4: + return layer.name + raise ValueError("Could not find 4D layer. Cannot apply GradCAM.") + +#---------------------------------------------------------------------------------- +# calculate gradient class actiavtion map +#---------------------------------------------------------------------------------- +def compute_heatmap(cnn_model, image, pred_index, last_conv_layer): + + """ + construct our gradient model by supplying (1) the inputs + to our pre-trained model, (2) the output of the (presumably) + final 4D layer in the network, and (3) the output of the + softmax activations from the model + """ + gradModel = Model( + inputs=[cnn_model.inputs], + outputs=[cnn_model.get_layer(last_conv_layer).output, cnn_model.output] + ) + + # record operations for automatic differentiation + with tf.GradientTape() as tape: + """ + cast the image tensor to a float-32 data type, pass the + image through the gradient model, and grab the loss + associated with the specific class index + """ + #print(pred_index) + inputs = tf.cast(image, tf.float32) + #print(image.shape) + last_conv_layer_output, preds = gradModel(inputs) + #print(preds) + #print(preds.shape) + # class_channel = preds[:, pred_index] + class_channel = preds + + ## use automatic differentiation to compute the gradients + grads = tape.gradient(class_channel, last_conv_layer_output) + """ + This is a vector where each entry is the mean intensity of the gradient + over a specific feature map channel + """ + pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2)) + """ + We multiply each channel in the feature map array + by "how important this channel is" with regard to the top predicted class + then sum all the channels to obtain the heatmap class activation + """ + last_conv_layer_output = last_conv_layer_output[0] + heatmap = last_conv_layer_output @ pooled_grads[..., tf.newaxis] + heatmap = tf.squeeze(heatmap) + + # For visualization purpose, we will also normalize the heatmap between 0 & 1 + heatmap = tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap) + heatmap = heatmap.numpy() + + return heatmap + +#------------------------------------------------------------------------------------ +# save gradcam heat map +#----------------------------------------------------------------------------------- +def save_gradcam(run_type, img_back, heatmap, val_gradcam_dir, test_gradcam_dir, + exval2_gradcam_dir, alpha, img_id): + +# print('heatmap:', heatmap.shape) + # Rescale heatmap to a range 0-255 + heatmap = np.uint8(255 * heatmap) + # Use jet colormap to colorize heatmap + jet = cm.get_cmap('jet') + # Use RGB values of the colormap + jet_colors = jet(np.arange(256))[:, :3] + jet_heatmap = jet_colors[heatmap] + + # resize heatmap + jet_heatmap = keras.preprocessing.image.array_to_img(jet_heatmap) + jet_heatmap0 = jet_heatmap.resize(re_size) + jet_heatmap1 = keras.preprocessing.image.img_to_array(jet_heatmap0) +# print('jet_heatmap:', jet_heatmap1.shape) + + # resize background CT image + img = img_back.reshape((192, 192, 3)) + img = keras.preprocessing.image.array_to_img(img) + ## resize if resolution of raw image too low + #img0 = img.resize(re_size) + img1 = keras.preprocessing.image.img_to_array(img) +# print('img shape:', img1.shape) + + # Superimpose the heatmap on original image + superimposed_img = jet_heatmap1 * alpha + img1 + superimposed_img = keras.preprocessing.image.array_to_img(superimposed_img) + + # Save the superimposed image + if run_type == 'val': + save_dir = val_gradcam_dir + elif run_type == 'test': + save_dir = test_gradcam_dir + elif run_type == 'exval2': + save_dir = exval2_gradcam_dir + + fn1 = str(img_id) + '_' + str(conv_n) + '_' + 'gradcam.png' + fn2 = str(img_id) + '_' + str(conv_n) + '_' + 'heatmap.png' + fn3 = str(img_id) + '_' + str(conv_n) + '_' + 'heatmap_raw.png' + fn4 = str(img_id) + '_' + 'CT.png' + superimposed_img.save(os.path.join(save_dir, fn1)) +# jet_heatmap0.save(os.path.join(save_dir, fn2)) +# jet_heatmap.save(os.path.join(save_dir, fn3)) +# img0.save(os.path.join(save_dir, fn4)) + +#--------------------------------------------------------------------------------- +# get background image +#--------------------------------------------------------------------------------- +def get_background(img_id, slice_range, PMH_reg_dir, CHUM_reg_dir, CHUS_reg_dir, + MDACC_reg_dir): + + pat_id = img_id.split('_')[0] + if pat_id[:-3] == 'PMH': + reg_dir = PMH_reg_dir + elif pat_id[:-3] == 'CHUM': + reg_dir = CHUM_reg_dir + elif pat_id[:-3] == 'CHUS': + reg_dir = CHUS_reg_dir + elif pat_id[:-3] == 'MDACC': + reg_dir = MDACC_reg_dir + elif pat_id[:4] == 'rtog': + reg_dir = rtog_reg_dir + pat_id = img_id.split('_s')[0] + + nrrd_id = str(pat_id) + '.nrrd' + data_dir = os.path.join(reg_dir, nrrd_id) + ### get image slice and save them as numpy array + nrrd = sitk.ReadImage(data_dir, sitk.sitkFloat32) + img_arr = sitk.GetArrayFromImage(nrrd) + print(img_arr.shape) + data = img_arr[slice_range, :, :] + #slice_n = img_id.split('_')[1][6:] + slice_n = img_id.split('slice0')[1] + slice_n = int(slice_n) + print(slice_n) + arr = data[slice_n, :, :] + arr = np.clip(arr, a_min=-200, a_max=200) + MAX, MIN = arr.max(), arr.min() + arr = (arr - MIN) / (MAX - MIN) + #print(arr.shape) + arr = np.repeat(arr[..., np.newaxis], 3, axis=-1) + arr = arr.reshape((1, 192, 192, 3)) + #print(arr.shape) + img_back = arr + #np.save(os.path.join(pro_data_dir, fn_arr_3ch), img_arr) + + return img_back + +#--------------------------------------------------------------------------------- +# get data +#--------------------------------------------------------------------------------- +def gradcam(run_type, input_channel, img_IDs, conv_list, val_dir, test_dir, + exval_dir, model_dir, saved_model, data_pro_dir, pro_data_dir, + run_model): + + ## load model and find conv layers + cnn_model = load_model(os.path.join(model_dir, saved_model)) + # model.summary() + + ### load train data based on input channels + if run_type == 'val': + if input_channel == 1: + fn = 'val_arr_1ch.npy' + elif input_channel == 3: + fn = 'val_arr_3ch.npy' + data = np.load(os.path.join(data_pro_dir, fn)) + df = pd.read_csv(os.path.join(pro_data_dir, 'val_img_pred.csv')) + save_dir = val_dir + elif run_type == 'test': + if input_channel == 1: + fn = 'test_arr_1ch.npy' + elif input_channel == 3: + fn = 'test_arr_3ch.npy' + data = np.load(os.path.join(data_pro_dir, fn)) + df = pd.read_csv(os.path.join(test_dir, 'df_test_pred.csv')) + save_dir = test_dir + elif run_type == 'exval2': + if input_channel == 1: + fn = 'exval_arr_1ch.npy' + elif input_channel == 3: + fn = 'rtog_arr.npy' + data = np.load(os.path.join(pro_data_dir, fn)) + df = pd.read_csv(os.path.join(pro_data_dir, 'rtog_img_pred.csv')) + save_dir = exval2_dir + print("successfully load data!") + print(img_IDs) + print(df[0:10]) + ## load data for gradcam + img_inds = df[df['fn'].isin(img_IDs)].index.tolist() + print(img_inds) + if img_inds == []: + print("list is empty. Choose other slices.") + else: + for i, img_id in zip(img_inds, img_IDs): + print('image ID:', img_id) + print('index:', i) + image = data[i, :, :, :] + image = image.reshape((1, 192, 192, 3)) + label = df['label'][i] + pred_index = df['y_pred_class'][i] + y_pred = df['y_pred'][i] + ## get background CT image + img_back = get_background( + img_id=img_id, + slice_range=slice_range, + PMH_reg_dir=PMH_reg_dir, + CHUM_reg_dir=CHUM_reg_dir, + CHUS_reg_dir=CHUS_reg_dir, + MDACC_reg_dir=MDACC_reg_dir + ) + if run_model == 'ResNet101V2': + for conv_n in conv_list: + if conv_n == 'conv2': + last_conv_layer = 'conv2_block3_1_conv' + elif conv_n == 'conv3': + last_conv_layer = 'conv3_block4_1_conv' + elif conv_n == 'conv4': + last_conv_layer = 'conv4_block6_1_conv' + elif conv_n == 'conv5': + last_conv_layer = 'conv5_block3_out' + elif run_model == 'EfficientNetB4': + last_conv_layer = 'top_conv' + #last_conv_layer = 'top_activation' + ## compute heatnap + heatmap = compute_heatmap( + cnn_model=cnn_model, + image=image, + pred_index=pred_index, + last_conv_layer=last_conv_layer + ) + ## save heatmap + save_gradcam( + run_type=run_type, + img_back=img_back, + heatmap=heatmap, + val_gradcam_dir=val_gradcam_dir, + test_gradcam_dir=test_gradcam_dir, + exval2_gradcam_dir=exval2_gradcam_dir, + alpha=alpha, + img_id=img_id + ) + + print('label:', label) + print('ID:', img_id) + print('y_pred:', y_pred) + print('pred class:', pred_index) + #print('conv layer:', conv_n) +#--------------------------------------------------------------------------------- +# get data +#--------------------------------------------------------------------------------- +if __name__ == '__main__': + + train_img_dir = '/media/bhkann/HN_RES1/HN_CONTRAST/train_img_dir' + val_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/val' + test_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/test' + exval_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/exval' + exval2_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/exval2' + val_gradcam_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/val/gradcam' + test_gradcam_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/test/gradcam' + exval_gradcam_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/exval/gradcam' + exval2_gradcam_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/exval2/gradcam' + pro_data_dir = '/home/bhkann/zezhong/git_repo/IV-Contrast-CNN-Project/pro_data' + model_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/model' + data_pro_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data_pro' + CHUM_reg_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data/CHUM_data_reg' + CHUS_reg_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data/CHUS_data_reg' + PMH_reg_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data/PMH_data_reg' + MDACC_reg_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data/MDACC_data_reg' + rtog_reg_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/ahmed_data/rtog-0617_reg' + input_channel = 3 + re_size = (192, 192) + crop = True + alpha = 0.9 + + run_type = 'exval2' + run_model = 'EfficientNetB4' + if run_type in ['exval', 'exval2']: + slice_range = range(50, 120) + slice_ids = ["{0:03}".format(i) for i in range(70)] + #saved_model = 'FineTuned_model_2021_08_02_17_22_10' + saved_model = 'Tuned_EfficientNetB4_2021_08_27_20_26_55' + elif run_type in ['val', 'test']: + slice_range = range(17, 83) + slice_ids = ["{0:03}".format(i) for i in range(66)] + #saved_model = 'ResNet_2021_07_18_06_28_40' + saved_model = 'EffNet_2021_08_24_09_57_13' + print(run_type) + print(slice_range) + print(slice_ids) + show_network = False + conv_n = 'conv5' + conv_list = ['conv2', 'conv3', 'conv4', 'conv5'] + + ## image ID + pat_id = 'PMH423' + pat_ids = ['rtog_0617-438343'] + pat_ids = ['PMH574', 'PMH146', 'PMH135'] + pat_ids = ['PMH433', 'PMH312', 'PMH234', 'PMH281', 'PMH511', 'PMH405'] + pat_ids = ['PMH465', 'PMH287', 'PMH308', 'PMH276', 'PMH595', 'PMH467'] + pat_ids = ['rtog_0617-349454', 'rtog_0617-438343', 'rtog_0617-292370', 'rtog_0617-349454'] + img_IDs = [] + for pat_id in pat_ids: + for slice_id in slice_ids: + img_id = pat_id + '_' + 'slice' + str(slice_id) + img_IDs.append(img_id) + + gradcam( + run_type=run_type, + input_channel=input_channel, + img_IDs=img_IDs, + conv_list=conv_list, + val_dir=val_dir, + test_dir=test_dir, + exval_dir=exval_dir, + model_dir=model_dir, + saved_model=saved_model, + data_pro_dir=data_pro_dir, + pro_data_dir=pro_data_dir, + run_model=run_model + ) + + + + +# if last_conv_layer is None: +# last_conv_layer = find_target_layer( +# model=model, +# saved_model=saved_model +# ) +# print(last_conv_layer) +# +# if show_network == True: +# for idx in range(len(model.layers)): +# print(model.get_layer(index = idx).name) + +# # compute the guided gradients +# castConvOutputs = tf.cast(convOutputs > 0, "float32") +# castGrads = tf.cast(grads > 0, "float32") +# guidedGrads = castConvOutputs * castGrads * grads +# # the convolution and guided gradients have a batch dimension +# # (which we don't need) so let's grab the volume itself and +# # discard the batch +# convOutputs = convOutputs[0] +# guidedGrads = guidedGrads[0] +# +# # compute the average of the gradient values, and using them +# # as weights, compute the ponderation of the filters with +# # respect to the weights +# weights = tf.reduce_mean(guidedGrads, axis=(0, 1)) +# cam = tf.reduce_sum(tf.multiply(weights, convOutputs), axis=-1) +# +# # grab the spatial dimensions of the input image and resize +# # the output class activation map to match the input image +# # dimensions +## (w, h) = (image.shape[2], image.shape[1]) +## heatmap = cv2.resize(cam.numpy(), (w, h)) +# heatmap = cv2.resize(heatmap.numpy(), (64, 64)) +# # normalize the heatmap such that all values lie in the range +## # [0, 1], scale the resulting values to the range [0, 255], +## # and then convert to an unsigned 8-bit integer +# numer = heatmap - np.min(heatmap) +# eps = 1e-8 +# denom = (heatmap.max() - heatmap.min()) + eps +# heatmap = numer / denom +# heatmap = (heatmap * 255).astype("uint8") +# colormap=cv2.COLORMAP_VIRIDIS +# heatmap = cv2.applyColorMap(heatmap, colormap) +# print('heatmap shape:', heatmap.shape) +## img = image[:, :, :, 0] +## print('img shape:', img.shape) +# img = image.reshape((64, 64, 3)) +# print(img.shape) +# output = cv2.addWeighted(img, 0.5, heatmap, 0.5, 0) +# +# +# return heatmap, output diff --git a/utils/gradcam_new.py b/utils/gradcam_new.py new file mode 100644 index 0000000..d748268 --- /dev/null +++ b/utils/gradcam_new.py @@ -0,0 +1,312 @@ +from tensorflow.keras.models import Model +import tensorflow as tf +from tensorflow import keras +import matplotlib.pyplot as plt +import matplotlib.cm as cm +import numpy as np +import cv2 +import numpy as np +import pandas as pd +from tensorflow.keras.models import load_model +import tensorflow as tf +import os + +#--------------------------------------------------------------------------------- +# get data +#--------------------------------------------------------------------------------- +def pat_data(pat_id, run_type, input_channel, i, val_save_dir, test_save_dir): + + ### load data and labels + if run_type == 'val': + df = pd.read_csv(os.path.join(data_pro_dir, 'df_pat_val.csv')) + if pat_id[:-3] == 'PMH': + data_dir = PMH_reg_dir + elif pat_id[:-3] == 'CHUM': + data_dir = CHUM_reg_dir + elif pat_id[:-3] == 'CHUS': + data_dir = CHUS_reg_dir + elif run_type == 'test': + df = pd.read_csv(os.path.join(data_pro_dir, 'df_pat_test.csv')) + data_dir = MDACC_reg_dir + elif run_type == 'exval': + df = pd.read_csv(os.path.join(data_pro_dir, 'df_pat_exval.csv')) + data_dir = NSCLS_reg_dir + + ## create numpy array + scan_dir = os.path.join(data_dir, pat_id) + nrrd = sitk.ReadImage(scan_dir, sitk.sitkFloat32) + img_arr = sitk.GetArrayFromImage(nrrd) + data = img_arr[slice_range, :, :] + ### clear signals lower than -1024 + data[data <= -1024] = -1024 + ### strip skull, skull UHI = ~700 + data[data > 700] = 0 + ### normalize UHI to 0 - 1, all signlas outside of [0, 1] will be 0; + if norm_type == 'np_interp': + data = np.interp(data, [-200, 200], [0, 1]) + elif norm_type == 'np_clip': + data = np.clip(data, a_min=-200, a_max=200) + MAX, MIN = data.max(), data.min() + data = (data - MIN) / (MAX - MIN) + ## stack all image arrays to one array for CNN input + arr = np.concatenate([arr, data], 0) + + + ### load label + y_true = df['label'].loc[df['ID'] == pat_id] + + + y_pred_class = df['y_pred_class'] + y_pred = df['y_pred'] + ID = df['fn'] + ### find the ith image to show grad-cam map + img = data[i, :, :, :] + img = img.reshape((1, 192, 192, 3)) + label = y_true[i] + pred_index = y_pred_class[i] + y_pred = y_pred[i] + ID = ID[i] + + return img, label, pred_index, y_pred, ID + +#------------------------------------------------------------------------------------ +# find last conv layer +#----------------------------------------------------------------------------------- +def find_target_layer(model, saved_model): + + # find the final conv layer by looping layers in reverse order + for layer in reversed(model.layers): + # check to see if the layer has a 4D output + if len(layer.output_shape) == 4: + return layer.name + raise ValueError("Could not find 4D layer. Cannot apply GradCAM.") + +#---------------------------------------------------------------------------------- +# calculate gradient class actiavtion map +#---------------------------------------------------------------------------------- +def compute_heatmap(model, saved_model, image, pred_index, last_conv_layer): + + """ + construct our gradient model by supplying (1) the inputs + to our pre-trained model, (2) the output of the (presumably) + final 4D layer in the network, and (3) the output of the + softmax activations from the model + """ + gradModel = Model( + inputs=[model.inputs], + outputs=[model.get_layer(last_conv_layer).output, model.output] + ) + + # record operations for automatic differentiation + with tf.GradientTape() as tape: + """ + cast the image tensor to a float-32 data type, pass the + image through the gradient model, and grab the loss + associated with the specific class index + """ + print(pred_index) + inputs = tf.cast(image, tf.float32) + print(image.shape) + last_conv_layer_output, preds = gradModel(inputs) + print(preds) + print(preds.shape) + # class_channel = preds[:, pred_index] + class_channel = preds + # use automatic differentiation to compute the gradients + grads = tape.gradient(class_channel, last_conv_layer_output) + """ + This is a vector where each entry is the mean intensity of the gradient + over a specific feature map channel + """ + pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2)) + """ + We multiply each channel in the feature map array + by "how important this channel is" with regard to the top predicted class + then sum all the channels to obtain the heatmap class activation + """ + last_conv_layer_output = last_conv_layer_output[0] + heatmap = last_conv_layer_output @ pooled_grads[..., tf.newaxis] + heatmap = tf.squeeze(heatmap) + + # For visualization purpose, we will also normalize the heatmap between 0 & 1 + heatmap = tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap) + heatmap = heatmap.numpy() + + return heatmap + + +#------------------------------------------------------------------------------------ +# save gradcam heat map +#----------------------------------------------------------------------------------- +def save_gradcam(image, heatmap, val_gradcam_dir, test_gradcam_dir, alpha, i): + +# print('heatmap:', heatmap.shape) + # Rescale heatmap to a range 0-255 + heatmap = np.uint8(255 * heatmap) + # Use jet colormap to colorize heatmap + jet = cm.get_cmap("jet") + # Use RGB values of the colormap + jet_colors = jet(np.arange(256))[:, :3] + jet_heatmap = jet_colors[heatmap] + + # resize heatmap + jet_heatmap = keras.preprocessing.image.array_to_img(jet_heatmap) + jet_heatmap0 = jet_heatmap.resize(re_size) + jet_heatmap1 = keras.preprocessing.image.img_to_array(jet_heatmap0) +# print('jet_heatmap:', jet_heatmap1.shape) + + # resize background CT image + img = image.reshape((192, 192, 3)) + img = keras.preprocessing.image.array_to_img(img) + img0 = img.resize(re_size) + img1 = keras.preprocessing.image.img_to_array(img0) +# print('img shape:', img1.shape) + + # Superimpose the heatmap on original image + superimposed_img = jet_heatmap1 * alpha + img1 + superimposed_img = keras.preprocessing.image.array_to_img(superimposed_img) + + # Save the superimposed image + if run_type == 'val': + save_dir = val_gradcam_dir + elif run_type == 'test': + save_dir = test_gradcam_dir + fn1 = str(conv_n) + '_' + str(i) + '_' + 'gradcam.png' + fn2 = str(conv_n) + '_' + str(i) + '_' + 'heatmap.png' + fn3 = str(conv_n) + '_' + str(i) + '_' + 'heatmap_raw.png' + fn4 = str(i) + '_' + 'CT.png' + superimposed_img.save(os.path.join(save_dir, fn1)) +# jet_heatmap0.save(os.path.join(save_dir, fn2)) +# jet_heatmap.save(os.path.join(save_dir, fn3)) +# img0.save(os.path.join(save_dir, fn4)) + + +if __name__ == '__main__': + + train_img_dir = '/media/bhkann/HN_RES1/HN_CONTRAST/train_img_dir' + val_save_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/val' + test_save_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/test' + val_gradcam_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/val/gradcam' + test_gradcam_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/test/gradcam' + data_pro_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data_pro' + model_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/model' + input_channel = 3 + re_size = (192, 192) + i = 72 + crop = True + alpha = 0.9 + saved_model = 'ResNet_2021_07_18_06_28_40' + show_network = False + conv_n = 'conv5' + run_type = 'val' + + #--------------------------------------------------------- + # run main function + #-------------------------------------------------------- + if run_type == 'val': + save_dir = val_save_dir + elif run_type == 'test': + save_dir = test_save_dir + + ## load model and find conv layers + model = load_model(os.path.join(model_dir, saved_model)) +# model.summary() + + list_i = [100, 105, 110, 115, 120, 125] + for i in list_i: + image, label, pred_index, y_pred, ID = data( + input_channel=input_channel, + i=i, + val_save_dir=val_save_dir, + test_save_dir=test_save_dir + ) + + conv_list = ['conv2', 'conv3', 'conv4', 'conv5'] + conv_list = ['conv4'] + for conv_n in conv_list: + if conv_n == 'conv2': + last_conv_layer = 'conv2_block3_1_conv' + elif conv_n == 'conv3': + last_conv_layer = 'conv3_block4_1_conv' + elif conv_n == 'conv4': + last_conv_layer = 'conv4_block6_1_conv' + elif conv_n == 'conv5': + last_conv_layer = 'conv5_block3_out' + + heatmap = compute_heatmap( + model=model, + saved_model=saved_model, + image=image, + pred_index=pred_index, + last_conv_layer=last_conv_layer + ) + + save_gradcam( + image=image, + heatmap=heatmap, + val_gradcam_dir=val_gradcam_dir, + test_gradcam_dir=test_gradcam_dir, + alpha=alpha, + i=i + ) + + print('label:', label) + print('ID:', ID) + print('y_pred:', y_pred) + print('prediction:', pred_index) + print('conv layer:', conv_n) + + + +# if last_conv_layer is None: +# last_conv_layer = find_target_layer( +# model=model, +# saved_model=saved_model +# ) +# print(last_conv_layer) +# +# if show_network == True: +# for idx in range(len(model.layers)): +# print(model.get_layer(index = idx).name) + +# # compute the guided gradients +# castConvOutputs = tf.cast(convOutputs > 0, "float32") +# castGrads = tf.cast(grads > 0, "float32") +# guidedGrads = castConvOutputs * castGrads * grads +# # the convolution and guided gradients have a batch dimension +# # (which we don't need) so let's grab the volume itself and +# # discard the batch +# convOutputs = convOutputs[0] +# guidedGrads = guidedGrads[0] +# +# # compute the average of the gradient values, and using them +# # as weights, compute the ponderation of the filters with +# # respect to the weights +# weights = tf.reduce_mean(guidedGrads, axis=(0, 1)) +# cam = tf.reduce_sum(tf.multiply(weights, convOutputs), axis=-1) +# +# # grab the spatial dimensions of the input image and resize +# # the output class activation map to match the input image +# # dimensions +## (w, h) = (image.shape[2], image.shape[1]) +## heatmap = cv2.resize(cam.numpy(), (w, h)) +# heatmap = cv2.resize(heatmap.numpy(), (64, 64)) +# # normalize the heatmap such that all values lie in the range +## # [0, 1], scale the resulting values to the range [0, 255], +## # and then convert to an unsigned 8-bit integer +# numer = heatmap - np.min(heatmap) +# eps = 1e-8 +# denom = (heatmap.max() - heatmap.min()) + eps +# heatmap = numer / denom +# heatmap = (heatmap * 255).astype("uint8") +# colormap=cv2.COLORMAP_VIRIDIS +# heatmap = cv2.applyColorMap(heatmap, colormap) +# print('heatmap shape:', heatmap.shape) +## img = image[:, :, :, 0] +## print('img shape:', img.shape) +# img = image.reshape((64, 64, 3)) +# print(img.shape) +# output = cv2.addWeighted(img, 0.5, heatmap, 0.5, 0) +# +# +# return heatmap, output diff --git a/utils/import_to_tensorboard.py b/utils/import_to_tensorboard.py new file mode 100644 index 0000000..fab0626 --- /dev/null +++ b/utils/import_to_tensorboard.py @@ -0,0 +1,48 @@ +"""Imports a protobuf model as a graph in Tensorboard.""" + + +from tensorflow.python.client import session +from tensorflow.python.framework import importer +from tensorflow.python.framework import ops +from tensorflow.python.summary import summary +from tensorflow.python.tools import saved_model_utils + +# Try importing TensorRT ops if available +# TODO(aaroey): ideally we should import everything from contrib, but currently +# tensorrt module would cause build errors when being imported in +# tensorflow/contrib/__init__.py. Fix it. +# pylint: disable=unused-import,g-import-not-at-top,wildcard-import +try: + from tensorflow.contrib.tensorrt.ops.gen_trt_engine_op import * +except ImportError: + pass +# pylint: enable=unused-import,g-import-not-at-top,wildcard-import + + +def import_to_tensorboard(model_dir, log_dir, tag_set): + """View an SavedModel as a graph in Tensorboard. + Args: + model_dir: The directory containing the SavedModel to import. + log_dir: The location for the Tensorboard log to begin visualization from. + tag_set: Group of tag(s) of the MetaGraphDef to load, in string format, + separated by ','. For tag-set contains multiple tags, all tags must be + passed in. + Usage: Call this function with your SavedModel location and desired log + directory. Launch Tensorboard by pointing it to the log directory. View your + imported SavedModel as a graph. + """ + with session.Session(graph=ops.Graph()) as sess: + input_graph_def = saved_model_utils.get_meta_graph_def(model_dir, + tag_set).graph_def + importer.import_graph_def(input_graph_def) + + pb_visual_writer = summary.FileWriter(log_dir) + pb_visual_writer.add_graph(sess.graph) + print("Model Imported. Visualize by running: " + "tensorboard --logdir={}".format(log_dir)) + + + +import_to_tensorboard(model_dir, log_dir, tag_set) + + diff --git a/utils/mean_CI.py b/utils/mean_CI.py new file mode 100644 index 0000000..3402865 --- /dev/null +++ b/utils/mean_CI.py @@ -0,0 +1,42 @@ +import os +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +import scipy.stats as ss +import pickle + + +# ---------------------------------------------------------------------------------- +# define function for calculating mean and 95% CI +# ---------------------------------------------------------------------------------- +def mean_CI(data): + + mean = np.mean(np.array(data)) + CI = ss.t.interval( + alpha=0.95, + df=len(data)-1, + loc=np.mean(data), + scale=ss.sem(data) + ) + lower = CI[0] + upper = CI[1] + + return mean, lower, upper + +##def mean_CI(metric): +## alpha = 0.95 +## mean = np.mean(np.array(metric)) +## p_up = (1.0 - alpha)/2.0*100 +## lower = max(0.0, np.percentile(metric, p_up)) +## p_down = ((alpha + (1.0 - alpha)/2.0)*100) +## upper = min(1.0, np.percentile(metric, p_down)) +## return mean, lower, upper + +##def mean_CI(stat, confidence=0.95): +## alpha = 0.95 +## mean = np.mean(np.array(stat)) +## p_up = (1.0 - alpha)/2.0*100 +## lower = max(0.0, np.percentile(stat, p_up)) +## p_down = ((alpha + (1.0 - alpha)/2.0)*100) +## upper = min(1.0, np.percentile(stat, p_down)) +## return mean, lower, upper diff --git a/utils/nrrd_reg.py b/utils/nrrd_reg.py new file mode 100644 index 0000000..0849b9c --- /dev/null +++ b/utils/nrrd_reg.py @@ -0,0 +1,56 @@ +import sys, os, glob +import SimpleITK as sitk +#import pydicom +import numpy as np + + +def nrrd_reg_rigid_ref(img_nrrd, fixed_img_dir, patient_id, save_dir): + + fixed_img = sitk.ReadImage(fixed_img_dir, sitk.sitkFloat32) + moving_img = img_nrrd +# moving_img = sitk.ReadImage(img_nrrd, sitk.sitkUInt32) + #moving_img = sitk.ReadImage(input_path, sitk.sitkFloat32) + + transform = sitk.CenteredTransformInitializer( + fixed_img, + moving_img, + sitk.Euler3DTransform(), + sitk.CenteredTransformInitializerFilter.GEOMETRY + ) + + # multi-resolution rigid registration using Mutual Information + registration_method = sitk.ImageRegistrationMethod() + registration_method.SetMetricAsMattesMutualInformation(numberOfHistogramBins=50) + registration_method.SetMetricSamplingStrategy(registration_method.RANDOM) + registration_method.SetMetricSamplingPercentage(0.01) + registration_method.SetInterpolator(sitk.sitkLinear) + + registration_method.SetOptimizerAsGradientDescent( + learningRate=1.0, + numberOfIterations=100, + convergenceMinimumValue=1e-6, + convergenceWindowSize=10 + ) + + registration_method.SetOptimizerScalesFromPhysicalShift() + registration_method.SetShrinkFactorsPerLevel(shrinkFactors=[4, 2, 1]) + registration_method.SetSmoothingSigmasPerLevel(smoothingSigmas=[2, 1, 0]) + registration_method.SmoothingSigmasAreSpecifiedInPhysicalUnitsOn() + registration_method.SetInitialTransform(transform) + final_transform = registration_method.Execute(fixed_img, moving_img) + moving_img_resampled = sitk.Resample( + moving_img, + fixed_img, + final_transform, + sitk.sitkLinear, + 0.0, + moving_img.GetPixelID() + ) + img_reg = moving_img_resampled + + if save_dir != None: + nrrd_fn = str(patient_id) + '.nrrd' + sitk.WriteImage(img_red, os.path.join(save_dir, nrrd_fn)) + + return img_reg + #return fixed_img, moving_img, final_transform diff --git a/utils/plot_cm.py b/utils/plot_cm.py new file mode 100644 index 0000000..aaceb3c --- /dev/null +++ b/utils/plot_cm.py @@ -0,0 +1,42 @@ +import seaborn as sn +import numpy as np +import matplotlib.pyplot as plt +import os + +def plot_cm(cm0, cm_type, level, save_dir): + + if cm_type == 'norm': + fmt = '' + elif cm_type == 'raw': + fmt = 'd' + + ax = sn.heatmap( + cm0, + annot=True, + cbar=True, + cbar_kws={'ticks': [-0.1]}, + annot_kws={'size': 26, 'fontweight': 'bold'}, + cmap='Blues', + fmt=fmt, + linewidths=0.5 + ) + + ax.axhline(y=0, color='k', linewidth=4) + ax.axhline(y=2, color='k', linewidth=4) + ax.axvline(x=0, color='k', linewidth=4) + ax.axvline(x=2, color='k', linewidth=4) + + ax.tick_params(direction='out', length=4, width=2, colors='k') + ax.xaxis.set_ticks_position('top') + ax.set_xticklabels([]) + ax.set_yticklabels([]) + ax.set_aspect('equal') + plt.tight_layout() + + fn = 'cm' + '_' + str(cm_type) + '_' + str(level) + '.png' + plt.savefig( + os.path.join(save_dir, fn), + format='png', + dpi=600 + ) + plt.close() diff --git a/utils/plot_prc.py b/utils/plot_prc.py new file mode 100644 index 0000000..252c5b2 --- /dev/null +++ b/utils/plot_prc.py @@ -0,0 +1,71 @@ +#---------------------------------------------------------------------- +# Deep learning for classification for contrast CT; +# Transfer learning using Google Inception V3; +#----------------------------------------------------------------------------------------- +import os +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +import scipy.stats as ss +import pickle +from sklearn.metrics import auc, roc_auc_score +from sklearn.metrics import precision_recall_curve + + +# ---------------------------------------------------------------------------------- +# precision recall curve +# ---------------------------------------------------------------------------------- +def plot_prc(save_dir, y_true, y_pred, level, color): + + precision = dict() + recall = dict() + threshold = dict() + prc_auc = [] + + precision, recall, threshold = precision_recall_curve(y_true, y_pred) + RP_2D = np.array([recall, precision]) + RP_2D = RP_2D[np.argsort(RP_2D[:, 0])] + #prc_auc.append(auc(RP_2D[1], RP_2D[0])) + prc_auc = auc(RP_2D[1], RP_2D[0]) + prc_auc = np.around(prc_auc, 3) + #print('PRC AUC:', prc_auc) + #prc_auc = auc(precision, recall) + print(prc_auc) + #prc_auc = 1 + + fn = 'prc' + '_' + str(level) + '.png' + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + ax.set_aspect('equal') + plt.plot( + recall, + precision, + color=color, + linewidth=3, + label='AUC %0.3f' % prc_auc + ) + plt.xlim([0, 1.03]) + plt.ylim([0, 1.03]) + ax.axhline(y=0, color='k', linewidth=4) + ax.axhline(y=1.03, color='k', linewidth=4) + ax.axvline(x=0, color='k', linewidth=4) + ax.axvline(x=1.03, color='k', linewidth=4) + plt.xticks([0, 0.2, 0.4, 0.6, 0.8, 1.0], fontsize=16, fontweight='bold') + plt.yticks([0, 0.2, 0.4, 0.6, 0.8, 1.0], fontsize=16, fontweight='bold') + plt.xlabel('recall', fontweight='bold', fontsize=16) + plt.ylabel('precision', fontweight='bold', fontsize=16) + plt.legend(loc='lower left', prop={'size': 16, 'weight': 'bold'}) + plt.grid(True) +# plt.tight_layout(pad=0.2, h_pad=None, w_pad=None, rect=None) +# plt.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.1) + plt.savefig(os.path.join(save_dir, fn), format='png', dpi=600) + #plt.show() + plt.close() + + return prc_auc + + + + + + diff --git a/utils/plot_roc.py b/utils/plot_roc.py new file mode 100644 index 0000000..7c5d16e --- /dev/null +++ b/utils/plot_roc.py @@ -0,0 +1,49 @@ +import pandas as pd +import seaborn as sn +import matplotlib.pyplot as plt +import glob +import pickle +import os +import numpy as np +from sklearn.metrics import roc_auc_score +from sklearn.metrics import auc +from sklearn.metrics import roc_curve + + +def plot_roc(save_dir, y_true, y_pred, level, color): + + fpr = dict() + tpr = dict() + roc_auc = dict() + threshold = dict() + + ### calculate auc + fpr, tpr, threshold = roc_curve(y_true, y_pred) + roc_auc = auc(fpr, tpr) + roc_auc = np.around(roc_auc, 3) + #print('ROC AUC:', roc_auc) + + fn = 'roc'+ '_' + str(level) + '.png' + ### plot roc + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + ax.set_aspect('equal') + plt.plot(fpr, tpr, color=color, linewidth=3, label='AUC %0.3f' % roc_auc) + plt.xlim([-0.03, 1]) + plt.ylim([0, 1.03]) + ax.axhline(y=0, color='k', linewidth=4) + ax.axhline(y=1.03, color='k', linewidth=4) + ax.axvline(x=-0.03, color='k', linewidth=4) + ax.axvline(x=1, color='k', linewidth=4) + plt.xticks([0, 0.2, 0.4, 0.6, 0.8, 1.0], fontsize=16, fontweight='bold') + plt.yticks([0, 0.2, 0.4, 0.6, 0.8, 1.0], fontsize=16, fontweight='bold') + plt.xlabel('1 - Specificity', fontweight='bold', fontsize=16) + plt.ylabel('Sensitivity', fontweight='bold', fontsize=16) + plt.legend(loc='lower right', prop={'size': 16, 'weight': 'bold'}) + plt.grid(True) +# plt.tight_layout(pad=1.08, h_pad=None, w_pad=None, rect=None) + plt.savefig(os.path.join(save_dir, fn), format='png', dpi=600) + #plt.show() + plt.close() + + return roc_auc diff --git a/utils/plot_train_curve.py b/utils/plot_train_curve.py new file mode 100644 index 0000000..68330c3 --- /dev/null +++ b/utils/plot_train_curve.py @@ -0,0 +1,34 @@ +import os +import numpy as np +import pandas as pd +import seaborn as sn +import matplotlib.pyplot as plt + +def plot_train_curve(output_dir, epoch, fn, history): + + train_acc = history.history['accuracy'] + train_loss = history.history['loss'] + val_acc = history.history['val_accuracy'] + val_loss = history.history['val_loss'] + n_epoch = list(range(epoch)) + ## accuracy curves + plt.style.use('ggplot') + plt.figure(figsize=(15, 15)) + plt.subplot(2, 2, 1) + plt.plot(n_epoch, train_acc, label='Train Acc') + plt.plot(n_epoch, val_acc, label='Val Acc') + plt.legend(loc='lower right') + plt.title('Train and Tune Accuracy') + plt.xlabel('Epoch') + plt.ylabel('Accuracy') + ## loss curves + plt.subplot(2, 2, 2) + plt.plot(n_epoch, train_loss, label='Train Loss') + plt.plot(n_epoch, val_loss, label='Val Loss') + plt.legend(loc='upper right') + plt.title('Train and Val Loss') + plt.xlabel('Epoch') + plt.ylabel('Loss') + plt.show() + plt.savefig(os.path.join(output_dir, fn)) + plt.close() diff --git a/utils/prc_all.py b/utils/prc_all.py new file mode 100644 index 0000000..952ebf0 --- /dev/null +++ b/utils/prc_all.py @@ -0,0 +1,46 @@ +import os +import numpy as np +import pandas as pd +import pickle +from utils.mean_CI import mean_CI +from utils.plot_roc import plot_roc +from utils.roc_bootstrap import roc_bootstrap +from utils.plot_prc import plot_prc + + +# ---------------------------------------------------------------------------------- +# plot ROI +# ---------------------------------------------------------------------------------- + +def prc_all(run_type, level, thr_prob, thr_pos, color, pro_data_dir, save_dir, fn_df_pred): + + df_sum = pd.read_csv(os.path.join(pro_data_dir, fn_df_pred)) + + if level == 'img': + y_true = df_sum['label'].to_numpy() + y_pred = df_sum['y_pred'].to_numpy() + print_info = 'prc image:' + elif level == 'patient_mean_prob': + df_mean = df_sum.groupby(['ID']).mean() + y_true = df_mean['label'].to_numpy() + y_pred = df_mean['y_pred'].to_numpy() + print_info = 'prc patient prob:' + elif level == 'patient_mean_pos': + df_mean = df_sum.groupby(['ID']).mean() + y_true = df_mean['label'].to_numpy() + y_pred = df_mean['y_pred_class'].to_numpy() + print_info = 'prc patient pos:' + + prc_auc = plot_prc( + save_dir=save_dir, + y_true=y_true, + y_pred=y_pred, + level=level, + color=color, + ) + + print(print_info) + print(prc_auc) + + return prc_auc + diff --git a/utils/resize_3d.py b/utils/resize_3d.py new file mode 100644 index 0000000..9d3e0eb --- /dev/null +++ b/utils/resize_3d.py @@ -0,0 +1,57 @@ +#-------------------------------------------------------------------------- +# rescale to a common "more compact" size (either downsample or upsample) +#-------------------------------------------------------------------------- + +import SimpleITK as sitk +import sys +import os +import matplotlib.pyplot as plt + + +def resize_3d(img_nrrd, interp_type, output_size, patient_id, return_type, save_dir): + + ### calculate new spacing +# image = sitk.ReadImage(nrrd_image) + image = img_nrrd + input_size = image.GetSize() + input_spacing = image.GetSpacing() + output_spacing = ( + (input_size[0] * input_spacing[0]) / output_size[0], + (input_size[1] * input_spacing[1]) / output_size[1], + (input_size[2] * input_spacing[2]) / output_size[2] + ) + #print('{} {}'.format('input spacing: ', input_spacing)) + #print('{} {}'.format('output spacing: ', output_spacing)) + + ### choose interpolation algorithm + if interp_type == 'linear': + interp_type = sitk.sitkLinear + elif interp_type == 'bspline': + interp_type = sitk.sitkBSpline + elif interp_type == 'nearest_neighbor': + interp_type = sitk.sitkNearestNeighbor + + ### interpolate + resample = sitk.ResampleImageFilter() + resample.SetSize(output_size) + resample.SetOutputSpacing(output_spacing) + resample.SetOutputOrigin(image.GetOrigin()) + resample.SetOutputDirection(image.GetDirection()) + resample.SetInterpolator(interp_type) + img_nrrd = resample.Execute(image) + + ## save as numpy array + img_arr = sitk.GetArrayFromImage(img_nrrd) + + if return_type == 'nrrd': + writer = sitk.ImageFileWriter() + writer.SetFileName(os.path.join(save_dir, '{}.nrrd'.format(patient_id))) + writer.SetUseCompression(True) + writer.Execute(img_nrrd) + return img_nrrd + + elif return_type == 'npy': + return img_arr + + + diff --git a/utils/respacing.py b/utils/respacing.py new file mode 100644 index 0000000..3054b7c --- /dev/null +++ b/utils/respacing.py @@ -0,0 +1,95 @@ +#-------------------------------------------------------------------------- +# rescale to a common "more compact" size (either downsample or upsample) +#-------------------------------------------------------------------------- + +import SimpleITK as sitk +import sys +import os +import numpy as np + + +def respacing(nrrd_dir, interp_type, new_spacing, patient_id, return_type, save_dir): + + ### calculate new spacing + img = sitk.ReadImage(nrrd_dir) + old_size = img.GetSize() + old_spacing = img.GetSpacing() + #print('{} {}'.format('old size: ', old_size)) + #print('{} {}'.format('old spacing: ', old_spacing)) + + new_size = [ + int(round((old_size[0] * old_spacing[0]) / float(new_spacing[0]))), + int(round((old_size[1] * old_spacing[1]) / float(new_spacing[1]))), + int(round((old_size[2] * old_spacing[2]) / float(new_spacing[2]))) + ] + + #print('{} {}'.format('new size: ', new_size)) + + ### choose interpolation algorithm + if interp_type == 'linear': + interp_type = sitk.sitkLinear + elif interp_type == 'bspline': + interp_type = sitk.sitkBSpline + elif interp_type == 'nearest_neighbor': + interp_type = sitk.sitkNearestNeighbor + + ### interpolate + resample = sitk.ResampleImageFilter() + resample.SetOutputSpacing(new_spacing) + resample.SetSize(new_size) + resample.SetOutputOrigin(img.GetOrigin()) + resample.SetOutputDirection(img.GetDirection()) + resample.SetInterpolator(interp_type) + resample.SetDefaultPixelValue(img.GetPixelIDValue()) + resample.SetOutputPixelType(sitk.sitkFloat32) + img_nrrd = resample.Execute(img) + + ## save nrrd images + if save_dir != None: + writer = sitk.ImageFileWriter() + writer.SetFileName(os.path.join(save_dir, '{}.nrrd'.format(patient_id))) + writer.SetUseCompression(True) + writer.Execute(img_nrrd) + + ## save as numpy array + img_arr = sitk.GetArrayFromImage(img_nrrd) + + if return_type == 'nrrd': + return img_nrrd + + elif return_type == 'npy': + return img_arr + + +#----------------------------------------------------------------------- +# main function +#---------------------------------------------------------------------- +if __name__ == '__main__': + + + PMH_data_dir = '/media/bhkann/HN_RES1/HN_CONTRAST/0_image_raw_PMH' + PMH_reg_dir = '/media/bhkann/HN_RES1/HN_CONTRAST/PMH_data_rego' + exval_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/exval' + NSCLC_data_dir = '/mnt/aertslab/DATA/Lung/TOPCODER/nrrd_data' + fixed_img = 'nsclc_rt_TC001.nrrd' + nrrd_dir = os.path.join(NSCLC_data_dir, fixed_img) + interp_type = 'linear' + new_spacing = (1, 1, 3) + patient_id = 'NSCLC001' + return_type = 'nrrd' + save_dir = exval_dir + + os.mkdir(exval_dir) if not os.path.isdir(exval_dir) else None + + img_nrrd = respacing( + nrrd_dir=nrrd_dir, + interp_type=interp_type, + new_spacing=new_spacing, + patient_id=patient_id, + return_type=return_type, + save_dir=save_dir + ) + + + + diff --git a/utils/roc_all.py b/utils/roc_all.py new file mode 100644 index 0000000..48f5d4c --- /dev/null +++ b/utils/roc_all.py @@ -0,0 +1,53 @@ +import os +import numpy as np +import pandas as pd +import pickle +from utils.mean_CI import mean_CI +from utils.plot_roc import plot_roc +from utils.roc_bootstrap import roc_bootstrap + + + +# ---------------------------------------------------------------------------------- +# plot ROI +# ---------------------------------------------------------------------------------- + +def roc_all(run_type, level, thr_prob, thr_pos, bootstrap, color, pro_data_dir, save_dir, + fn_df_pred): + + df_sum = pd.read_csv(os.path.join(pro_data_dir, fn_df_pred)) + + if level == 'img': + y_true = df_sum['label'].to_numpy() + y_pred = df_sum['y_pred'].to_numpy() + print_info = 'roc image:' + elif level == 'patient_mean_prob': + df_mean = df_sum.groupby(['ID']).mean() + y_true = df_mean['label'].to_numpy() + y_pred = df_mean['y_pred'].to_numpy() + print_info = 'roc patient prob:' + elif level == 'patient_mean_pos': + df_mean = df_sum.groupby(['ID']).mean() + y_true = df_mean['label'].to_numpy() + y_pred = df_mean['y_pred_class'].to_numpy() + print_info = 'roc patient pos:' + + auc = plot_roc( + save_dir=save_dir, + y_true=y_true, + y_pred=y_pred, + level=level, + color='blue' + ) + ### calculate roc, tpr, tnr with 1000 bootstrap + roc_stat = roc_bootstrap( + bootstrap=bootstrap, + y_true=y_true, + y_pred=y_pred + ) + + print(print_info) + print(roc_stat) + + return roc_stat + diff --git a/utils/roc_bootstrap.py b/utils/roc_bootstrap.py new file mode 100644 index 0000000..7de5fc6 --- /dev/null +++ b/utils/roc_bootstrap.py @@ -0,0 +1,55 @@ +#-------------------------------------------- +# calculate auc, tpr, tnr with n bootstrap +#------------------------------------------- + +import os +import numpy as np +import pandas as pd +import glob +from sklearn.utils import resample +import scipy.stats as ss +from utils.mean_CI import mean_CI +from sklearn.metrics import roc_auc_score +from sklearn.metrics import auc +from sklearn.metrics import roc_curve + + +def roc_bootstrap(bootstrap, y_true, y_pred): + + AUC = [] + THRE = [] + TNR = [] + TPR = [] + for j in range(bootstrap): + #print("bootstrap iteration: " + str(j+1) + " out of " + str(n_bootstrap)) + index = range(len(y_pred)) + indices = resample(index, replace=True, n_samples=int(len(y_pred))) + fpr, tpr, thre = roc_curve(y_true[indices], y_pred[indices]) + q = np.arange(len(tpr)) + roc = pd.DataFrame( + {'fpr' : pd.Series(fpr, index=q), + 'tpr' : pd.Series(tpr, index=q), + 'tnr' : pd.Series(1 - fpr, index=q), + 'tf' : pd.Series(tpr - (1 - fpr), index=q), + 'thre': pd.Series(thre, index=q)} + ) + ### calculate optimal TPR, TNR under uden index + roc_opt = roc.loc[(roc['tpr'] - roc['fpr']).idxmax(),:] + AUC.append(roc_auc_score(y_true[indices], y_pred[indices])) + TPR.append(roc_opt['tpr']) + TNR.append(roc_opt['tnr']) + THRE.append(roc_opt['thre']) + ### calculate mean and 95% CI + AUCs = np.around(mean_CI(AUC), 3) + TPRs = np.around(mean_CI(TPR), 3) + TNRs = np.around(mean_CI(TNR), 3) + THREs = np.around(mean_CI(THRE), 3) + #print(AUCs) + ### save results into dataframe + stat_roc = pd.DataFrame( + [AUCs, TPRs, TNRs, THREs], + columns=['mean', '95% CI -', '95% CI +'], + index=['AUC', 'TPR', 'TNR', 'THRE'] + ) + + return stat_roc diff --git a/utils/save_npy_to_h5.py b/utils/save_npy_to_h5.py new file mode 100644 index 0000000..24934b7 --- /dev/null +++ b/utils/save_npy_to_h5.py @@ -0,0 +1,10 @@ +import numpy as np +import h5py + +file_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/data_pro/exval_arr_3ch1.npy' +data = np.load(file_dir) + +with h5py.File('exval_arr_3ch1.h5', 'w') as hf: + hf.create_dataset("name-of-dataset", data=data_to_write) + +print("H5 created.") diff --git a/utils/save_pred.py b/utils/save_pred.py new file mode 100644 index 0000000..9af46e1 --- /dev/null +++ b/utils/save_pred.py @@ -0,0 +1,17 @@ +import pandas as pd +import os + +proj_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection/ahmed_data/results' +df_pred = pd.read_csv(os.path.join(proj_dir, 'rtog-0617_pat_pred.csv')) +df_id = pd.read_csv(os.path.join(proj_dir, 'check_list.csv')) +list_id = df_id['patient_id'].to_list() + +IDs = [] +preds = [] +for ID, pred in zip(df_pred['ID'], df_pred['predictions']): + if ID.split('_')[1] in list_id: + IDs.append(ID) + preds.append(pred) + +df_check = pd.DataFrame({'ID': IDs, 'pred': preds}) +df_check.to_csv(os.path.join(proj_dir, 'rtog_check.csv'), index=False) diff --git a/utils/save_prepro_scan.py b/utils/save_prepro_scan.py new file mode 100644 index 0000000..a02c5ea --- /dev/null +++ b/utils/save_prepro_scan.py @@ -0,0 +1,119 @@ +import glob +import shutil +import os +import pandas as pd +import nrrd +import re +from sklearn.model_selection import train_test_split +import pickle +import numpy as np +from time import gmtime, strftime +from datetime import datetime +import timeit +from respacing import respacing +from nrrd_reg import nrrd_reg_rigid_ref +from crop_image import crop_image + + + +def save_prepro_scan(PMH_data_dir, CHUM_data_dir, CHUS_data_dir, MDACC_data_dir, + PMH_reg_dir, CHUM_reg_dir, CHUS_reg_dir, MDACC_reg_dir, fixed_img_dir, + interp_type, new_spacing, return_type, data_exclude, crop_shape): + + for fn, ID in zip(fns, IDs): + + print(ID) + + ## set up save dir + if ID[:-3] == 'PMH': + save_dir = PMH_reg_dir + file_dir = os.path.join(PMH_data_dir, fn) + elif ID[:-3] == 'CHUM': + save_dir = CHUM_reg_dir + file_dir = os.path.join(CHUM_data_dir, fn) + elif ID[:-3] == 'CHUS': + save_dir = CHUS_reg_dir + file_dir = os.path.join(CHUS_data_dir, fn) + elif ID[:-3] == 'MDACC': + save_dir = MDACC_reg_dir + file_dir = os.path.join(MDACC_data_dir, fn) + + ## respacing + img_nrrd = respacing( + nrrd_dir=file_dir, + interp_type=interp_type, + new_spacing=new_spacing, + patient_id=ID, + return_type=return_type, + save_dir=None + ) + + ## registration + img_reg = nrrd_reg_rigid_ref( + img_nrrd=img_nrrd, + fixed_img_dir=fixed_img_dir, + patient_id=ID, + save_dir=None + ) + + ## crop image from (500, 500, 116) to (180, 180, 60) + img_crop = crop_image( + nrrd_file=img_reg, + patient_id=ID, + crop_shape=crop_shape, + return_type='nrrd', + save_dir=save_dir + ) + +if __name__ == '__main__': + + + fns = [ + 'mdacc_HNSCC-01-0028_CT-SIM-09-06-1999-_raw_raw_raw_xx.nrrd', + 'mdacc_HNSCC-01-0168_CT-SIM-11-19-2001-_raw_raw_raw_xx.nrrd', + 'mdacc_HNSCC-01-0181_CT-SIM-07-16-2002-_raw_raw_raw_xx.nrrd' + ] + IDs = ['MDACC028', 'MDACC168', 'MDACC181'] + + val_save_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/val' + test_save_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/test' + MDACC_data_dir = '/media/bhkann/HN_RES1/HN_CONTRAST/0_image_raw_mdacc' + PMH_data_dir = '/media/bhkann/HN_RES1/HN_CONTRAST/0_image_raw_pmh' + CHUM_data_dir = '/media/bhkann/HN_RES1/HN_CONTRAST/0_image_raw_chum' + CHUS_data_dir = '/media/bhkann/HN_RES1/HN_CONTRAST/0_image_raw_chus' + CHUM_reg_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/data/CHUM_data_reg' + CHUS_reg_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/data/CHUS_data_reg' + PMH_reg_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/data/PMH_data_reg' + MDACC_reg_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/data/MDACC_data_reg' + data_pro_dir = '/mnt/aertslab/USERS/Zezhong/constrast_detection/data_pro' + fixed_img_dir = os.path.join(data_pro_dir, 'PMH050.nrrd') + input_channel = 3 + crop = True + n_img = 20 + save_img = False + data_exclude = None + thr_prob = 0.5 + run_type = 'test' + norm_type = 'np_clip' + new_spacing = [1, 1, 3] + crop_shape = [192, 192, 100] + return_type = 'nrrd' + interp_type = 'linear' + + save_prepro_scan( + PMH_data_dir=PMH_data_dir, + CHUM_data_dir=CHUM_data_dir, + CHUS_data_dir=CHUS_data_dir, + MDACC_data_dir=MDACC_data_dir, + PMH_reg_dir=PMH_reg_dir, + CHUM_reg_dir=CHUM_reg_dir, + CHUS_reg_dir=CHUS_reg_dir, + MDACC_reg_dir=MDACC_reg_dir, + fixed_img_dir=fixed_img_dir, + interp_type=interp_type, + new_spacing=new_spacing, + return_type=return_type, + data_exclude=data_exclude, + crop_shape=crop_shape + ) + print("successfully save prepro scan!") diff --git a/utils/save_sitk_from_arr.py b/utils/save_sitk_from_arr.py new file mode 100644 index 0000000..350d2f1 --- /dev/null +++ b/utils/save_sitk_from_arr.py @@ -0,0 +1,45 @@ +#-------------------------------------------------------------------------------------------- +# save image as itk +#------------------------------------------------------------------------------------------- +def save_sitk_from_arr(img_sitk, new_arr, resize, save_dir): + + """ + When resize == True: Used for saving predictions where padding needs to be added to increase the size + of the prediction and match that of input to model. This function matches the size of the array in + image_sitk_obj with the size of pred_arr, and saves it. This is done equally on all sides as the + input to model and model output have different dims to allow for shift data augmentation. + When resize == False: the image_sitk_obj is only used as a reference for spacing and origin. The numpy + array is not resized. + image_sitk_obj: sitk object of input to model + pred_arr: returned prediction from model - should be squeezed. + NOTE: image_arr.shape will always be equal or larger than pred_arr.shape, but never smaller given that + we are always cropping in data.py + """ + + if resize == True: + # get array from sitk object + img_arr = sitk.GetArrayFromImage(img_sitk) + # change pred_arr.shape to match image_arr.shape + # getting amount of padding needed on each side + z_diff = int((img_arr.shape[0] - new_arr.shape[0]) / 2) + y_diff = int((img_arr.shape[1] - new_arr.shape[1]) / 2) + x_diff = int((img_arr.shape[2] - new_arr.shape[2]) / 2) + # pad, defaults to 0 + new_arr = np.pad(new_arr, ((z_diff, z_diff), (y_diff, y_diff), (x_diff, x_diff)), 'constant') + assert img_arr.shape == new_arr.shape, "returned array shape does not match your requested shape." + + # save sitk obj + new_sitk = sitk.GetImageFromArray(new_arr) + new_sitk.SetSpacing(img_sitk.GetSpacing()) + new_sitk.SetOrigin(img_sitk.GetOrigin()) + + if output_dir != None: +# fn = "{}_{}_image_interpolated_roi_raw_gt.nrrd".format(dataset, patient_id) + fn = 'test_stik.nrrd' + img_dir = os.path.join(output_dir, fn) + writer = sitk.ImageFileWriter() + writer.SetFileName(img_dir) + writer.SetUseCompression(True) + writer.Execute(new_sitk) + + return new_sitk diff --git a/utils/scan_meta.py b/utils/scan_meta.py new file mode 100644 index 0000000..523d91c --- /dev/null +++ b/utils/scan_meta.py @@ -0,0 +1,56 @@ +import pandas as pd +import os +import numpy as np + + +data_dir = '/mnt/aertslab/USERS/Zezhong/contrast_detection' +meta_file = 'clinical_meta_data.csv' +df = pd.read_csv(os.path.join(data_dir, meta_file)) +IDs = [] +for manufacturer, model in zip(df['manufacturer'], df['manufacturermodelname']): + ID = str(manufacturer) + ' ' + str(model) + IDs.append(ID) +df['ID'] = IDs +#print(df['manufacturer'].value_counts()) +#print(df['manufacturermodelname'].value_counts()) +#print(df['ID'].value_counts()) +#print(df.shape[0]) + +## KVP +print('kvp mean:', df['kvp'].mean().round(3)) +print('kvp median:', df['kvp'].median()) +print('kvp mode:', df['kvp'].mode()) +print('kvp std:', df['kvp'].std()) +print('kvp min:', df['kvp'].min()) +print('kvp max:', df['kvp'].max()) + +## slice thickness +print('thk mean:', df['slicethickness'].mean().round(3)) +print('thk median:', df['slicethickness'].median()) +print('thk mode:', df['slicethickness'].mode()) +print('thk std:', df['slicethickness'].std().round(3)) +print('thk min:', df['slicethickness'].min()) +print('thk max:', df['slicethickness'].max()) +print(df['slicethickness'].value_counts()) +print(df['slicethickness'].shape[0]) + +## spatial resolution +print(df['rows'].value_counts()) + +## pixel spacing +pixels = [] +for pixel in df['pixelspacing']: + pixel = pixel.split("'")[1] + pixel = float(pixel) + pixels.append(pixel) +df['pixel'] = pixels +df['pixel'].round(3) +print('pixel mean:', df['pixel'].mean().round(3)) +print('pixel median:', df['pixel'].median().round(3)) +print('pixel mode:', df['pixel'].mode().round(3)) +print('pixel std:', df['pixel'].std()) +print('pixel min:', df['pixel'].min()) +print('pixel max:', df['pixel'].max()) + + + diff --git a/utils/tensorboard.py b/utils/tensorboard.py new file mode 100644 index 0000000..2936f6b --- /dev/null +++ b/utils/tensorboard.py @@ -0,0 +1,7 @@ + + + + +#log_path ='/media/bhkann/HN_RES1/HN_CONTRAST/log/train/events.out.tfevents.1624041416.bhkann-hpc1.862300.3087.v2' + +#tensorboard --logdir='/media/bhkann/HN_RES1/HN_CONTRAST/log/train/events.out.tfevents.1624041416.bhkann-hpc1.862300.3087.v2' diff --git a/utils/write_txt.py b/utils/write_txt.py new file mode 100644 index 0000000..31d1533 --- /dev/null +++ b/utils/write_txt.py @@ -0,0 +1,111 @@ +import os +import numpy as np +import pandas as pd +from datetime import datetime +from time import localtime, strftime + + + + + +def write_txt(run_type, out_dir, loss, acc, cms, cm_norms, reports, prc_aucs, + roc_stats, run_model, saved_model, epoch, batch_size, lr): + + """ + write model training, val, test results to txt files; + + Args: + loss {float} -- loss value; + acc {float} -- accuracy value; + cms {list} -- list contains confusion matrices; + cm_norms {list} -- list containing normalized confusion matrices; + reports {list} -- list containing classification reports; + prc_aucs {list} -- list containing prc auc; + roc_stats {list} -- list containing ROC statistics; + batch_size {int} -- batch size for data loading; + epoch {int} -- training epoch; + out_dir {path} -- path for output files; + + Returns: + save model results and parameters to txt file + + """ + + train_dir = os.path.join(out_dir, 'train') + val_dir = os.path.join(out_dir, 'val') + test_dir = os.path.join(out_dir, 'test') + exval1_dir = os.path.join(out_dir, 'exval1') + exval2_dir = os.path.join(out_dir, 'exval2') + + if not os.path.exists(train_dir): + os.mkdir(train_dir) + if not os.path.exists(val_dir): + os.mkdir(val_dir) + if not os.path.exists(test_dir): + os.mkdir(test_dir) + if not os.path.exists(exval1_dir): + os.mkdir(exval1_dir) + if not os.path.exists(exval2_dir): + os.mkdir(exval2_dir) + + if run_type == 'train': + log_fn = 'train_logs.text' + save_dir = train_dir + write_path = os.path.join(save_dir, log_fn) + with open(write_path, 'a') as f: + f.write('\n-------------------------------------------------------------------') + f.write('\ncreated time: %s' % strftime('%Y-%m-%d %H:%M:%S', localtime())) + f.write('\nval acc: %s' % acc) + f.write('\nval loss: %s' % loss) + f.write('\nrun model: %s' % run_model) + f.write('\nsaved model: %s' % saved_model) + f.write('\nepoch: %s' % epoch) + f.write('\nlearning rate: %s' % lr) + f.write('\nbatch size: %s' % batch_size) + f.write('\n') + f.close() + print('successfully save train logs.') + else: + if run_type == 'val': + log_fn = 'val_logs.text' + save_dir = val_dir + elif run_type == 'test': + log_fn = 'test_logs.text' + save_dir = test_dir + elif run_type == 'exval1': + log_fn = 'exval1_logs.text' + save_dir = exval1_dir + elif run_type == 'exval2': + log_fn = 'exval2_logs.text' + save_dir = exval2_dir + + write_path = os.path.join(save_dir, log_fn) + with open(write_path, 'a') as f: + f.write('\n------------------------------------------------------------------') + f.write('\ncreated time: %s' % strftime('%Y-%m-%d %H:%M:%S', localtime())) + f.write('\nval accuracy: %s' % acc) + f.write('\nval loss: %s' % loss) + f.write('\nprc image: %s' % prc_aucs[0]) + f.write('\nprc patient prob: %s' % prc_aucs[1]) + f.write('\nprc patient pos: %s' % prc_aucs[2]) + f.write('\nroc image:\n %s' % roc_stats[0]) + f.write('\nroc patient prob:\n %s' % roc_stats[1]) + f.write('\nroc patient pos:\n %s' % roc_stats[2]) + f.write('\ncm image:\n %s' % cms[0]) + f.write('\ncm image:\n %s' % cm_norms[0]) + f.write('\ncm patient prob:\n %s' % cms[1]) + f.write('\ncm patient prob:\n %s' % cm_norms[1]) + f.write('\ncm patient pos:\n %s' % cms[2]) + f.write('\ncm patient pos:\n %s' % cm_norms[2]) + f.write('\nreport image:\n %s' % reports[0]) + f.write('\nreport patient prob:\n %s' % reports[1]) + f.write('\nreport patient pos:\n %s' % reports[2]) + f.write('\nrun model: %s' % run_model) + f.write('\nsaved model: %s' % saved_model) + f.write('\nepoch: %s' % epoch) + f.write('\nlearning rate: %s' % lr) + f.write('\nbatch size: %s' % batch_size) + f.write('\n') + f.close() + print('successfully save logs.') +