class Evaluation(): def __init__(self, hparam): self.hparam = hparam # init data and loss self.data = Data(hparam) self.loss = Loss(hparam, self.data.csv_table) # deposition matrix (#voxels, #bixels) self.deposition = convert_depoMatrix_to_tensor(self.data.deposition, self.hparam.device) # MC dose if hparam.MCPlan or hparam.MCJYPlan or hparam.MCMURefinedPlan: self.mc = MonteCarlo(hparam, self.data) self.unitMUDose = self.mc.get_unit_MCdose() def load_MonteCarlo_OrganDose(self, MUs, name, scale=1): MUs = np.abs(MUs) / self.hparam.dose_scale # x1000 MCdoses = self.unitMUDose * MUs * scale MCdoses = MCdoses.sum(axis=0, keepdims=False) # (#slice, H, W) MCdoses = torch.tensor(MCdoses, dtype=torch.float32, device=self.hparam.device) dict_organ_doses = parse_MonteCarlo_dose(MCdoses, self.data) # parse organ_doses to obtain individual organ doses return OrderedBunch({'dose':dict_organ_doses, 'name':name}) def load_JYMonteCarlo_OrganDose(self, name, dosefilepath, scale=1): MCdoses = self.mc.get_JY_MCdose(dosefilepath) * scale MCdoses = torch.tensor(MCdoses, dtype=torch.float32, device=self.hparam.device) dict_organ_doses = parse_MonteCarlo_dose(MCdoses, self.data) # parse organ_doses to obtain individual organ doses return OrderedBunch({'dose':dict_organ_doses, 'name':name}) def load_Depos_OrganDose(self, name, scale=1): cprint(f'deposPlan uses following parameters:{self.hparam.optimized_segments_MUs_file_path}; {self.hparam.deposition_pickle_file_path}; {self.hparam.valid_ray_file};', 'yellow') # get seg and MU file_name = self.hparam.optimized_segments_MUs_file_path+'/optimized_segments_MUs.pickle' if not os.path.isfile(file_name): raise ValueError(f'file not exist: {file_name}') cprint(f'load segments and MUs from {file_name}', 'yellow') segments_and_MUs = unpickle_object(file_name) dict_segments, dict_MUs = OrderedBunch(), OrderedBunch() for beam_id, seg_MU in segments_and_MUs.items(): dict_segments[beam_id] = torch.tensor(seg_MU['Seg'], dtype=torch.float32, device=self.hparam.device) dict_MUs[beam_id] = torch.tensor(seg_MU['MU'], dtype=torch.float32, device=self.hparam.device, requires_grad=True) # compute fluence fluence, _ = computer_fluence(self.data, dict_segments, dict_MUs) fluence = fluence / self.hparam.dose_scale * scale # * 1000 dict_FMs = self.data.project_to_fluenceMaps(to_np(fluence)) # compute dose doses = cal_dose(self.deposition, fluence) # split organ_doses to obtain individual organ doses dict_organ_doses = split_doses(doses, self.data.organName_ptsNum) return OrderedBunch({'fluence':to_np(fluence), 'dose':dict_organ_doses, 'fluenceMaps': dict_FMs, 'name': name}) def load_TPS_OrganDose(self, name='TPSOptimResult'): # intensity fluence = np.loadtxt(self.hparam.tps_ray_inten_file) fluence = torch.tensor(fluence, dtype=torch.float32, device=self.hparam.device) dict_FMs = self.data.project_to_fluenceMaps(to_np(fluence)) # dose doses = cal_dose(self.deposition, fluence) # split organ_doses to obtain individual organ doses dict_organ_doses = split_doses(doses, self.data.organName_ptsNum) return OrderedBunch({'fluence':to_np(fluence), 'dose':dict_organ_doses, 'fluenceMaps': dict_FMs, 'name': name}) def load_fluenceOptim_OrganDose(self, name): # intensity fluence = loosen_object(os.path.join(self.hparam.optimized_fluence_file_path+'/optimized_fluence.pickle')) fluence = torch.tensor(fluence, device=self.hparam.device) fluence = torch.abs(fluence) fluence = torch.where(fluence>self.hparam.max_fluence, self.hparam.max_fluence, ray_inten) fluence = fluence / self.hparam.dose_scale # * 1000 # compute dose doses = cal_dose(self.deposition, fluence) # split organ_doses to obtain individual organ doses dict_organ_doses = split_doses(doses, self.data.organName_ptsNum) return OrderedBunch({'fluence':to_np(fluence), 'dose':dict_organ_doses, 'fluenceMaps': dict_FMs, 'name': name}) def comparison_plots(self, plans): ''' plans: list of plan ''' ## print loss and breaking pts num for plan in plans: dict_organ_doses = plan.dose.copy() for name, dose in dict_organ_doses.copy().items(): dict_organ_doses.update({name: dose*self.hparam.dose_scale}) # /1000 plan_loss, plan_breaking_points_nums = self.loss.loss_func(dict_organ_doses) print(f'{plan.name} breaking points #: ', end='') for organ_name, breaking_points_num in plan_breaking_points_nums.items(): print(f'{organ_name}: {breaking_points_num} ', end='') print(f'loss={plan_loss}\n\n') ## plot DVH # pop unnecessary organ dose to avoid mess dvh organ_filter = self.hparam.organ_filter for plan in plans: for name in plan.dose.copy().keys(): if name not in organ_filter: plan.dose.pop(name) print(f'pop unnecessary organ for dvh: {name}') # plot fig, ax = plt.subplots(figsize=(20, 10)) max_dose = 12000 organ_names = list(plans[0].dose.keys()) colors = cm.jet(np.linspace(0,1,len(organ_names))) # linestyles = ['solid', 'dotted', 'dashed', 'dashdot'] linestyles = ['solid', 'dashed', 'dashdot'] if len(plans) > 3: raise NotImplementedError for i, organ_name in enumerate(organ_names): if self.data.organName_ptsNum[organ_name] != 0: for pi, plan in enumerate(plans): n, bins, patches = ax.hist(to_np(plan.dose[organ_name]), bins=12000, linestyle=linestyles[pi], color=colors[i], range=(0, max_dose), density=True, histtype='step', cumulative=-1, label=f'{plan.name}_{organ_name}_maxDose{int(to_np(plan.dose[organ_name].max()))}') ax.grid(True) ax.legend(loc='upper left', bbox_to_anchor=(1.05,1.0)) ax.set_title('Dose volume Histograms') ax.set_xlabel('Absolute Dose cGy') ax.set_ylabel('Relative Volume %') plt.tight_layout() plt.savefig('./tmp.pdf') #plt.show() cprint(f'dvh.pdf has been written to ./tmp.pdf', 'green') def run(self): plans_to_compare = [] if self.hparam.CGDeposPlan: plans_to_compare.append(self.load_Depos_OrganDose('CG_depos', scale=self.hparam.CGDeposPlan_doseScale)) if self.hparam.MCPlan: plans_to_compare.append(self.load_MonteCarlo_OrganDose(self.mc.old_MUs, 'CG_MC')) if self.hparam.MCMURefinedPlan: plans_to_compare.append(self.load_MonteCarlo_OrganDose(unpickle_object(os.path.join(self.hparam.refined_segments_MUs_file,'optimized_MUs.pickle')), 'CG_MC_MURefined')) if self.hparam.MCJYPlan: plans_to_compare.append(self.load_JYMonteCarlo_OrganDose('CG_JY_MC', '/mnt/win_share2/20200918_NPC_MCDOse_verify_by_JY_congliuReCal/dpm_result*Ave.dat')) if self.hparam.TPSFluenceOptimPlan: plans_to_compare.append(self.load_TPS_OrganDose('TPSOptim')) if self.hparam.FluenceOptimPlan: plans_to_compare.append(self.load_fluenceOptim_OrganDose('FluenceOptim')) self.comparison_plots(plans_to_compare)
class Evaluation(): def __init__(self, hparam): self.hparam = hparam # init data and loss self.data = Data(hparam) self.loss = Loss(hparam, self.data.csv_table) # deposition matrix (#voxels, #bixels) self.deposition = torch.tensor(self.data.deposition, dtype=torch.float32, device=hparam.device) # MC dose self.mc = MonteCarlo(hparam, self.data) self.unitMUDose = self.mc.get_unit_MCdose() def load_MonteCarlo_organ_dose(self, MUs, name, scale=1): MUs = np.abs(MUs) / self.hparam.dose_scale # x1000 MCdoses = self.unitMUDose * MUs * scale MCdoses = MCdoses.sum(axis=0, keepdims=False) # (#slice, H, W) MCdoses = torch.tensor(MCdoses, dtype=torch.float32, device=self.hparam.device) dict_organ_doses = parse_MonteCarlo_dose(MCdoses, self.data) # parse organ_doses to obtain individual organ doses return OrderedBunch({'dose':dict_organ_doses, 'name':name}) def load_JYMonteCarlo_organ_dose(self, name, dosefilepath, scale=1): MCdoses = self.mc.get_JY_MCdose(dosefilepath) * scale MCdoses = torch.tensor(MCdoses, dtype=torch.float32, device=self.hparam.device) dict_organ_doses = parse_MonteCarlo_dose(MCdoses, self.data) # parse organ_doses to obtain individual organ doses return OrderedBunch({'dose':dict_organ_doses, 'name':name}) def load_Depos_organ_dose(self, name): # get seg and MU file_name = self.hparam.optimized_segments_MUs_file+'/optimized_segments_MUs.pickle' if not os.path.isfile(file_name): raise ValueError(f'file not exist: {file_name}') cprint(f'load segments and MUs from {file_name}', 'yellow') segments_and_MUs = unpickle_object(file_name) dict_segments, dict_MUs = OrderedBunch(), OrderedBunch() for beam_id, seg_MU in segments_and_MUs.items(): dict_segments[beam_id] = torch.tensor(seg_MU['Seg'], dtype=torch.float32, device=self.hparam.device) dict_MUs[beam_id] = torch.tensor(seg_MU['MU'], dtype=torch.float32, device=self.hparam.device, requires_grad=True) # compute fluence fluence, _ = computer_fluence(self.data, dict_segments, dict_MUs) fluence = fluence / self.hparam.dose_scale # * 1000 dict_FMs = self.data.project_to_fluenceMaps(to_np(fluence)) # compute dose doses = torch.matmul(self.deposition, fluence) # cal dose # split organ_doses to obtain individual organ doses dict_organ_doses = split_doses(doses, self.data.organ_inf) return OrderedBunch({'fluence':to_np(fluence), 'dose':dict_organ_doses, 'fluenceMaps': dict_FMs, 'name': name}) def load_fluence_dose_from_TPS(self, tps_ray_inten_file='./data/TPSray.txt'): # intensity fluence = np.loadtxt(tps_ray_inten_file) fluence = torch.tensor(fluence, dtype=torch.float32, device=self.hparam.device) dict_FMs = self.data.project_to_fluenceMaps(to_np(fluence)) # dose doses = torch.matmul(self.deposition, fluence) # cal dose # split organ_doses to obtain individual organ doses dict_organ_doses = split_doses(doses, self.data.organ_inf) return OrderedBunch({'fluence':to_np(fluence), 'dose':dict_organ_doses, 'fluenceMaps': dict_FMs, 'name': 'TPS'}) def comparison_plots(self, plans): ''' plans: list of plan ''' # pop unnessneary organ dose organ_filter = ['PTV1-nd2-nx2', 'PTV2', 'PGTVnd', 'PGTVnx', 'Temp.lobe_L', 'Temp.lobe_R', 'Parotid_L', 'Parotid_R', 'Mandible', 'Brainstem+2mmPRV', 'Cord+5mmPRV'] for plan in plans: for name in plan.dose.copy().keys(): if name not in organ_filter: plan.dose.pop(name) print(f'pop {name}') # print loss for plan in plans: dict_organ_doses = plan.dose.copy() for name, dose in dict_organ_doses.copy().items(): dict_organ_doses.update({name: dose*self.hparam.dose_scale}) # /1000 plan_loss, plan_breaking_points_nums = self.loss.loss_func(dict_organ_doses) print(f'{plan.name} breaking points #: ', end='') for organ_name, breaking_points_num in plan_breaking_points_nums.items(): print(f'{organ_name}: {breaking_points_num} ', end='') print(f'losses: loss={plan_loss}\n\n') # plot DVH fig, ax = plt.subplots(figsize=(20, 10)) max_dose = 12000 organ_names = list(plans[0].dose.keys()) colors = cm.jet(np.linspace(0,1,len(organ_names))) # linestyles = ['solid', 'dotted', 'dashed', 'dashdot'] linestyles = ['solid', 'dashed', 'dashdot'] if len(plans) > 4: raise NotImplementedError for i, organ_name in enumerate(organ_names): if self.data.organ_inf[organ_name] != 0: for pi, plan in enumerate(plans): n, bins, patches = ax.hist(to_np(plan.dose[organ_name]), bins=12000, linestyle=linestyles[pi], color=colors[i], range=(0, max_dose), density=True, histtype='step', cumulative=-1, label=f'{plan.name}_{organ_name}_maxDose{int(to_np(plan.dose[organ_name].max()))}') ax.grid(True) ax.legend(loc='upper left', bbox_to_anchor=(1.05,1.0)) ax.set_title('Dose volume Histograms') ax.set_xlabel('Absolute Dose cGy') ax.set_ylabel('Relative Volume %') plt.tight_layout() plt.savefig('./tmp.pdf') #plt.show() def run(self): # CG_Depos_plan = self.load_Depos_organ_dose('CG_depos') # tps_plan = self.load_fluence_dose_from_TPS() CG_MC_plan = self.load_MonteCarlo_organ_dose(self.mc.old_MUs, 'CG_MC') # mc095_plan = self.load_MonteCarlo_organ_dose(self.mc.old_MUs, 'CG_monteCarlo2') CG_MC_MURefined_plan = self.load_MonteCarlo_organ_dose(unpickle_object(os.path.join(self.hparam.refined_segments_MUs_file,'optimized_MUs.pickle')), 'CG_MC_MURefined') CG_MC_JY_plan = self.load_JYMonteCarlo_organ_dose('CG_JY_MC', '/mnt/win_share2/20200918_NPC_MCDOse_verify_by_JY_congliuReCal/dpm_result*Ave.dat') # JYMC095_plan = self.load_JYMonteCarlo_organ_dose('jymc0.95', '/mnt/win_share2/20200918_NPC_MCDOse_verify_by_JY_congliuReCal/dpm_result*Ave.dat', 0.95) # CLRecalJYMC_plan = self.load_JYMonteCarlo_organ_dose('jymc_clRecalc', '/mnt/win_share2/20200918_NPC_MCDOse_verify_by_JY/dpm_result*Ave.dat') # self.comparison_plots([mc095_plan, mc_plan]) # self.comparison_plots([JYMC_plan, CLRecalJYMC_plan]) # self.comparison_plots([mc_plan, JYMC_plan]) # self.comparison_plots([CG_Depos_plan, CG_MC_plan, CG_MC_JY_plan, CG_MC_MURefined_plan]) self.comparison_plots([CG_MC_plan, CG_MC_JY_plan, CG_MC_MURefined_plan])