NLP_beamlet_optimizer.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. % colorwash(Geometry.data, D_full, [500, 1500], [0,70])
  2. % orthoslice(D_full, [0,70])
  3. function [D_full, w_fin, Geometry, optGoal] = NLP_beamlet_optimizer
  4. % This function performs the beamlet optimization
  5. % Inputs: none. Still needs to find "Geometry" and "beamlets" from Wiscplan
  6. % Outputs: full dose image dose: D_full, optimal beamlet weights: w_fin
  7. %
  8. % [D_full, w_fin, Geometry, optGoal] = NLP_beamlet_optimizer;
  9. %
  10. % Made by Peter Ferjancic 1. May 2018
  11. % Last updated: 14. August 2018
  12. % Inspired by Ana Barrigan's REGGUI optimization procedures
  13. N_fcallback1 = 5000;
  14. N_fcallback2 = 200000;
  15. patient = 'gbm_005';
  16. switch patient
  17. case 'gbm_005'
  18. patient_dir = 'C:\010-work\003_localGit\WiscPlan_v2\data\PatData_ausGli_005';
  19. DP_dir = '\\Mpufs5\data_wnx1\_Data\Glioma_aus\FET_FGL005\B1\Processed';
  20. otherwise
  21. error('invalid case')
  22. end
  23. % merge_beamlets(4, patient_dir);
  24. %% PROGRAM STARTS HERE
  25. % - no tocar lo que hay debajo -
  26. fprintf('starting NLP optimization process... \n')
  27. % % -- LOAD GEOMETRY AND BEAMLETS --
  28. load([patient_dir '\matlab_files\Geometry.mat'])
  29. % % beamlet_batch_filename = [patient_dir '\beamlet_batch_files\' 'beamletbatch0.bin'];
  30. % beamlet_batch_filename = [patient_dir '\' 'batch_dose.bin'];
  31. % beamlet_cell_array = read_ryan_beamlets(beamlet_batch_filename, 'ryan');
  32. % fprintf('\n loaded beamlets...')
  33. %
  34. % % -- SET INITIAL PARAMETERS --
  35. % numVox = numel(Geometry.data);
  36. % numBeamlet = size(beamlet_cell_array,2);
  37. %
  38. % %% -- BEAMLET DOSE DELIVERY --
  39. % beamlets = get_beamlets(beamlet_cell_array, numVox);
  40. % % show_joint_beamlets(beamlets, size(Geometry.data), 7:9)
  41. % fprintf('\n beamlet array constructed...')
  42. % % - merge beamlets into beams -
  43. % load([patient_dir '\all_beams.mat'])
  44. % beamletOrigin=[0 0 0];
  45. % beam_i=0;
  46. % beam_i_list=[];
  47. % for beamlet_i = 1:numel(all_beams)
  48. % newBeamletOrigin = all_beams{beamlet_i}.ip;
  49. % if any(newBeamletOrigin ~= beamletOrigin)
  50. % beam_i = beam_i+1;
  51. % beamletOrigin = newBeamletOrigin;
  52. % end
  53. % beam_i_list=[beam_i_list, beam_i];
  54. % end
  55. % beamlets_joined=[];
  56. % target_joined=[];
  57. % wbar2 = waitbar(0, 'merging beamlets into beams');
  58. % numBeam=numel(unique(beam_i_list));
  59. % for beam_i = unique(beam_i_list)
  60. % beam_full = sum(beamlets(:,beam_i_list == beam_i), 2);
  61. % beamlets_joined(:,end+1) = beam_full;
  62. % waitbar(beam_i/numBeam, wbar2)
  63. % end
  64. % close(wbar2)
  65. %% -- OPTIMIZATION TARGETS --
  66. % make_ROI_goals(Geometry, beamlets, beamlets_joined, patient);
  67. % [optGoal, optGoal_beam, optGoal_idx, targetMinMax_idx] = get_ROI_goals(patient);
  68. load([DP_dir '\RODP_files\ROI_goals.mat']);
  69. optGoal = ROI_goals.optGoal;
  70. optGoal_beam = ROI_goals.optGoal_beam;
  71. optGoal_idx = ROI_goals.optGoal_idx;
  72. targetMinMax_idx = ROI_goals.targetMinMax_idx;
  73. [beamlets, beamlets_joined, numBeamlet, numBeam, beam_i_list] = get_beam_lets(Geometry, patient_dir);
  74. % -- make them robust --
  75. RO_params=0;
  76. optGoal_beam = make_robust_optGoal(optGoal_beam, RO_params, beamlets_joined);
  77. optGoal = make_robust_optGoal(optGoal, RO_params, beamlets);
  78. %% -- INITIALIZE BEAMLET WEIGHTS --
  79. w0_beams = ones(numBeam,1);
  80. w0_beams = mean(optGoal_beam{1}.target ./ (optGoal_beam{1}.beamlets_pruned*w0_beams+0.1)) * w0_beams;
  81. w0_beams = w0_beams + (2*rand(size(w0_beams))-1) *0.1 .*w0_beams; % random perturbation
  82. w0_beams = double(w0_beams);
  83. % -- CALLBACK OPTIMIZATION FUNCTION --
  84. fun1 = @(x) get_penalty(x, optGoal_beam);
  85. fun2 = @(x) get_penalty(x, optGoal);
  86. % -- OPTIMIZATION PARAMETERS --
  87. % define optimization parameters
  88. A = [];
  89. b = [];
  90. Aeq = [];
  91. beq = [];
  92. lb = zeros(1, numBeamlet);
  93. lb_beam = zeros(1, numBeam);
  94. ub = [];
  95. nonlcon = [];
  96. % define opt limits, and make it fmincon progress
  97. options = optimoptions('fmincon');
  98. options.MaxFunctionEvaluations = N_fcallback1;
  99. options.Display = 'iter';
  100. options.PlotFcn = 'optimplotfval';
  101. % options.UseParallel = true;
  102. % options.OptimalityTolerance = 1e-9;
  103. fprintf('\n running initial optimizer:')
  104. %% Run the optimization
  105. % -- GET FULL BEAM WEIGHTS --
  106. tic
  107. w_beam = fmincon(fun1,w0_beams,A,b,Aeq,beq,lb_beam,ub,nonlcon,options);
  108. fprintf(' done!:')
  109. % t=toc;
  110. % disp(['Optimization time for beams = ',num2str(t)]);
  111. w_beamlets = ones(numBeamlet,1);
  112. numBeam=numel(unique(beam_i_list));
  113. for beam_i = 1:numBeam % assign weights to beamlets
  114. % beamlets from same beam get same initial weights
  115. w_beamlets(beam_i_list == beam_i) = w_beam(beam_i);
  116. end
  117. w_beamlets = w_beamlets + (2*rand(size(w_beamlets))-1) *0.1 .*w_beamlets; % small random perturbation
  118. w_beamlets = 1.1* w_beamlets; % this just kicks the beamlets up a bit to make it easier for the optimizer to start
  119. % -- GET FULL BEAMLET WEIGHTS --
  120. options.MaxFunctionEvaluations = N_fcallback2;
  121. % tic
  122. fprintf('\n running full optimizer:')
  123. w_fin = fmincon(fun2,w_beamlets,A,b,Aeq,beq,lb,ub,nonlcon,options);
  124. fprintf(' done!:')
  125. t=toc;
  126. disp(['Optimization time for beamlets = ',num2str(t)]);
  127. %% evaluate the results
  128. D_full = reshape(beamlets * w_fin, size(Geometry.data));
  129. %% save outputs
  130. NLP_result.dose = D_full;
  131. NLP_result.weights = w_fin;
  132. save([patient_dir '\matlab_files\NLP_result.mat'], 'NLP_result');
  133. plot_DVH(D_full, optGoal, optGoal_idx, targetMinMax_idx)
  134. colorwash(Geometry.data, D_full);
  135. % plot_DVH_robust(D_full, optGoal, optGoal_idx)
  136. end
  137. %% support functions
  138. % ---- PENALTY FUNCTION ----
  139. function penalty = get_penalty(x, optGoal)
  140. % this function gets called by the optimizer. It checks the penalty for
  141. % all the robust implementation and returns the worst result.
  142. NumScenarios = optGoal{1}.NbrRandScenarios * optGoal{1}.NbrSystSetUpScenarios * optGoal{1}.NbrRangeScenarios;
  143. fobj = zeros(NumScenarios,1);
  144. sc_i = 1;
  145. for nrs_i = 1:optGoal{1}.NbrRandScenarios
  146. for sss_i = 1 :optGoal{1}.NbrSystSetUpScenarios % syst. setup scenarios = sss
  147. for rrs_i = 1:optGoal{1}.NbrRangeScenarios % range scenario = rs
  148. fobj(sc_i)=eval_f(x, optGoal, nrs_i, sss_i, rrs_i);
  149. sc_i = sc_i + 1;
  150. end
  151. end
  152. end
  153. % take the worst case penalty of evaluated scenarios
  154. penalty=max(fobj);
  155. end
  156. % ------ supp: penalty for single scenario ------
  157. function penalty = eval_f(x, optGoal, nrs_i, sss_i, rrs_i)
  158. penalty = 0;
  159. % for each condition
  160. for goal_i = 1:numel(optGoal)
  161. switch optGoal{goal_i}.function
  162. % min, max, min_sq, max_sq, LeastSquare, min_perc_Volume, max_perc_Volume
  163. case 'min'
  164. % penalize if achieved dose is lower than target dose
  165. d_penalty = 1.0e0 * sum(max(0, ...
  166. (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.target) -...
  167. (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.beamlets_pruned * x)));
  168. case 'max'
  169. % penalize if achieved dose is higher than target dose
  170. d_penalty = 1.0e0 * sum(max(0, ...
  171. (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.beamlets_pruned * x)-...
  172. (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.target)));
  173. case 'min_sq'
  174. % penalize if achieved dose is higher than target dose
  175. temp1=min(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.beamlets_pruned * x)-...
  176. (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.target));
  177. d_penalty = 1.0e0 * sum(temp1.^2);
  178. case 'max_sq'
  179. % penalize if achieved dose is higher than target dose
  180. temp1=max(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.beamlets_pruned * x)-...
  181. (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.target));
  182. d_penalty = 1.0e0 * sum(temp1.^2);
  183. case 'LeastSquare'
  184. % penalize with sum of squares any deviation from target
  185. % dose
  186. d_penalty = 1.0e-1* sum(((...
  187. optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.beamlets_pruned * x) - ...
  188. optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.target).^2);
  189. case 'min_perc_Volume'
  190. % penalize by amount of volume under threshold
  191. perc_vox = numel(find((optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.target) -...
  192. (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.beamlets_pruned * x) > 0)) ...
  193. / numel(optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.target);
  194. d_penalty = 3.0e5 * min(perc_vox-0.05, 0)
  195. case 'max_perc_Volume'
  196. % penalize by amount of volume under threshold
  197. perc_vox = numel(find((optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.target) -...
  198. (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.beamlets_pruned * x) < 0)) ...
  199. / numel(optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.target);
  200. d_penalty = 3.0e4 * min(perc_vox-0.05, 0)
  201. end
  202. penalty = penalty + d_penalty * optGoal{goal_i}.opt_weight;
  203. end
  204. end
  205. % ---- MAKE ROI ROBUST ----
  206. function optGoal = make_robust_optGoal(optGoal, RO_params, beamlets);
  207. % take regular optimal goal and translate it into several robust cases
  208. % RO_params - should have the information below
  209. % nrs - random scenarios
  210. % sss - system setup scenarios
  211. % rrs - random range scenarios
  212. % X - X>0 moves image right
  213. % Y - Y>0 moves image down
  214. % Z - in/out.
  215. shift_mag = 3; % vox of shift
  216. nrs_scene_list={[0,0,0]};
  217. % sss_scene_list={[0,0,0]};
  218. sss_scene_list={[0,0,0], [-shift_mag,0,0], [shift_mag,0,0], [0,-shift_mag,0], [0,shift_mag,0]};
  219. % sss_scene_list={[0,0,0], [-shift_mag,0,0], [shift_mag,0,0], [0,-shift_mag,0], [0,shift_mag,0],...
  220. % [-shift_mag*2,0,0], [shift_mag*2,0,0], [0,-shift_mag*2,0], [0,shift_mag*2,0]};
  221. rrs_scene_list={[0,0,0]};
  222. [targetIn, meta] = nrrdread('C:\010-work\003_localGit\WiscPlan_v2\data\archive\CDP_data\CDP5_DP_target.nrrd');
  223. % [targetIn, meta] = nrrdread('C:\010-work\003_localGit\WiscPlan_v2\data\PD_HD_dicomPhantom\Tomo_DP_target.nrrd');
  224. % [targetIn, meta] = nrrdread('C:\010-work\003_localGit\WiscPlan_v2\data\archive\CDP_data\CDP5_DP_target.nrrd');
  225. for i = 1:numel(optGoal)
  226. optGoal{i}.NbrRandScenarios =numel(nrs_scene_list);
  227. optGoal{i}.NbrSystSetUpScenarios=numel(sss_scene_list);
  228. optGoal{i}.NbrRangeScenarios =numel(rrs_scene_list);
  229. end
  230. for goal_i = 1:numel(optGoal)
  231. % get target
  232. idx=optGoal{goal_i}.ROI_idx;
  233. targetImg1=zeros(optGoal{goal_i}.imgDim);
  234. targetImg1(idx)=1;
  235. % get beamlets
  236. for nrs_i = 1:optGoal{goal_i}.NbrRandScenarios % num. of random scenarios
  237. % modify target and beamlets
  238. targetImg2=targetImg1;
  239. % beamlets stay the same
  240. for sss_i = 1 :optGoal{goal_i}.NbrSystSetUpScenarios % syst. setup scenarios = sss
  241. % modify target and beamlets
  242. targetImg3=get_RO_sss(targetImg2, sss_scene_list{sss_i});
  243. % beamlets stay the same
  244. for rrs_i = 1:optGoal{goal_i}.NbrRangeScenarios % range scenario = rrs
  245. % modify target and beamlets
  246. targetImg4=targetImg3;
  247. % beamlets stay the same
  248. %% make new target and beamlets
  249. ROI_idx=[];
  250. ROI_idx=find(targetImg4>0);
  251. if isfield(optGoal{goal_i}, 'target_alpha')
  252. target = double(optGoal{goal_i}.target_alpha * targetIn(ROI_idx));
  253. % disp('exists')
  254. else
  255. target = ones(numel(ROI_idx), 1) .* optGoal{goal_i}.D_final;
  256. % disp('not exists')
  257. end
  258. beamlets_pruned = beamlets(ROI_idx, :);
  259. % save to optGoal output
  260. optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.ROI_idx = ROI_idx;
  261. optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.beamlets_pruned = beamlets_pruned;
  262. optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.target = target;
  263. end
  264. end
  265. end
  266. end
  267. end
  268. % ------ supp: RO case SSS ------
  269. function targetImg3=get_RO_sss(targetImg2, sss_scene_shift);
  270. % translate the target image
  271. targetImg3 = imtranslate(targetImg2,sss_scene_shift);
  272. end