|
@@ -1,543 +1,543 @@
|
|
|
-
|
|
|
-
|
|
|
-% paths.patient_dir
|
|
|
-% paths.Goal_dir (previously called DP_dir)
|
|
|
-% paths.patient
|
|
|
-% paths.goalsName
|
|
|
-
|
|
|
-% colorwash(Geometry.data, D_full, [500, 1500], [0,70])
|
|
|
-% orthoslice(D_full, [0,70])
|
|
|
-
|
|
|
-function [D_full, w_fin, Geometry, optGoal] = NLP_optimizer_v3(varargin)
|
|
|
-% This function performs the beamlet optimization
|
|
|
-% [D_full, w_fin, Geometry, optGoal] = NLP_beamlet_optimizer;
|
|
|
-%
|
|
|
-% Inputs:
|
|
|
-% () OR
|
|
|
-% (Pat_path, path2goal) OR
|
|
|
-% (Pat_path, path2goal, beamlet_weights)
|
|
|
-% Pat_path, path2goal = strings to patient folder and optimal goals
|
|
|
-% beamlet_weights = initial beamlet weights
|
|
|
-%
|
|
|
-% Outputs:
|
|
|
-% full dose image dose: [D_full, w_fin, Geometry, optGoal]
|
|
|
-%
|
|
|
-% Made by Peter Ferjancic 1. May 2018
|
|
|
-% Last updated: 1. April 2019
|
|
|
-
|
|
|
-if nargin<2
|
|
|
- load('WiscPlan_preferences.mat')
|
|
|
- [Pat_path] = uigetdir([WiscPlan_preferences.patientDataPath ], 'Select Patient folder');
|
|
|
- [Goal_file,Goal_path,indx] = uigetfile([Pat_path '\matlab_files\*.mat'], 'Select OptGoal file');
|
|
|
-
|
|
|
- path2geometry = [Pat_path, '\matlab_files\Geometry.mat'];
|
|
|
- path2goal = [Goal_path, Goal_file];
|
|
|
-else
|
|
|
- Pat_path = varargin{1};
|
|
|
- path2geometry = [Pat_path, '\matlab_files\Geometry.mat'];
|
|
|
- path2goal = varargin{2};
|
|
|
- [Goal_path,Goal_file,ext] = fileparts(path2goal);
|
|
|
-end
|
|
|
-
|
|
|
-dialogue_box = 'yes'
|
|
|
-switch dialogue_box
|
|
|
- case 'yes'
|
|
|
- str = inputdlg({'N of iterations for initial calc', 'N of iterations for full calc', ...
|
|
|
- 'Use pre-existing NLP_result to initiate? (y/n)'}, 'input', [1,35], {'100000', '500000', 'n'});
|
|
|
- N_fcallback1 = str2double(str{1}); % 100000 is a good guesstimate
|
|
|
- N_fcallback2 = str2double(str{2}); % 500000 is a good guesstimate
|
|
|
- pre_beamWeights = str{3};
|
|
|
- case 'no'
|
|
|
- disp('dialogue box skipped')
|
|
|
- N_fcallback1 = 1e5;
|
|
|
- N_fcallback2 = 2e6; % 500000;
|
|
|
- pre_beamWeights = 'n';
|
|
|
-end
|
|
|
-
|
|
|
-
|
|
|
-switch pre_beamWeights
|
|
|
- case 'y'
|
|
|
- [NLP_file,NLP_path,indx] = uigetfile([Pat_path '\matlab_files\*.mat'], 'Select NLP_result file');
|
|
|
- load([NLP_path, NLP_file])
|
|
|
- w_beamlets = NLP_result.weights;
|
|
|
-
|
|
|
- load([Pat_path, '\all_beams.mat'])
|
|
|
- if numel(all_beams) ~= numel(w_beamlets)
|
|
|
- error('Provided weight number does not match beamlet number!')
|
|
|
- end
|
|
|
- case 'n'
|
|
|
- disp('Initial beam weights will be calculated.')
|
|
|
-end
|
|
|
-
|
|
|
-%% PROGRAM STARTS HERE
|
|
|
-% - no tocar lo que hay debajo -
|
|
|
-fprintf('starting NLP optimization process... \n')
|
|
|
-
|
|
|
-% % -- LOAD GEOMETRY, GOALS, BEAMLETS --
|
|
|
-load(path2geometry)
|
|
|
-load(path2goal)
|
|
|
-[beamlets, numBeamlet] = get_beamlets(Geometry, Pat_path, OptGoals);
|
|
|
-% [beamlets, beamlets_joined, numBeamlet, numBeam, beam_i_list] = get_beam_lets(Geometry, Pat_path);
|
|
|
-
|
|
|
-
|
|
|
-%% -- OPTIMIZATION TARGETS --
|
|
|
-% -- make the optimization optGoal structure --
|
|
|
-for i_goal = 1:size(OptGoals.goals,1)
|
|
|
-
|
|
|
- if isfield(OptGoals.data{i_goal}, 'SupVox_num')
|
|
|
- SupVox_num = OptGoals.data{i_goal}.SupVox_num;
|
|
|
- else
|
|
|
- answer = inputdlg(['# of supervoxels for "' OptGoals.data{i_goal}.name '" with ' num2str(numel(OptGoals.data{i_goal}.ROI_idx)) ' vox: ("0" to skip)'])
|
|
|
- SupVox_num = str2double(answer{1})
|
|
|
- end
|
|
|
- optGoal{i_goal} = OptGoals.data{i_goal};
|
|
|
- optGoal{i_goal}.sss_scene_list = OptGoals.sss_scene_list;
|
|
|
- optGoal{i_goal}.maxModulation = OptGoals.maxModulation;
|
|
|
- optGoal{i_goal}.BeamSmoothMax = OptGoals.BeamSmoothMax;
|
|
|
-
|
|
|
- optGoal{i_goal}.optFuncNum = OptGoals.optFuncNum;
|
|
|
-
|
|
|
- % modulation
|
|
|
- switch SupVox_num
|
|
|
- case 0
|
|
|
- % if not supervoxel, just select provided ROI_idx
|
|
|
-
|
|
|
- optGoal{i_goal}.beamlets_pruned = sparse(beamlets(optGoal{i_goal}.ROI_idx, :));
|
|
|
-
|
|
|
- otherwise
|
|
|
- % -- if supervoxel, merge given columns
|
|
|
- % - make supervoxel map
|
|
|
- switch OptGoals.goals{i_goal, 3}
|
|
|
- case 'Fixed dose'
|
|
|
- mask = zeros(OptGoals.data{i_goal}.imgDim);
|
|
|
- mask(OptGoals.data{i_goal}.ROI_idx) = 1;
|
|
|
- % group superpixels
|
|
|
- superMask = superpix_group(mask, SupVox_num, 'no');
|
|
|
- case 'Dose map'
|
|
|
- mask = zeros(OptGoals.data{i_goal}.imgDim);
|
|
|
- mask(OptGoals.data{i_goal}.ROI_idx) = OptGoals.data{i_goal}.D_final;
|
|
|
- mask2 = round(mask/3)*3;
|
|
|
- superMask = superpix_group(mask, SupVox_num, 'no');
|
|
|
- orthoslice(superMask)
|
|
|
- end
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- superVoxList = unique(superMask);
|
|
|
- superVoxList = superVoxList(superVoxList>0);
|
|
|
-
|
|
|
- optGoal{i_goal}.ROI_idx_old = optGoal{i_goal}.ROI_idx; % copy old index data
|
|
|
- optGoal{i_goal}.ROI_idx = zeros(numel(superVoxList), 1);
|
|
|
- optGoal{i_goal}.opt_weight = optGoal{i_goal}.opt_weight * numel(optGoal{i_goal}.ROI_idx_old)/numel(optGoal{i_goal}.ROI_idx);
|
|
|
-
|
|
|
- if isfield(OptGoals.data{i_goal}, 'wgt_map')
|
|
|
- tabula_wgtmap = zeros(size(superMask));
|
|
|
- tabula_wgtmap(OptGoals.data{i_goal}.ROI_idx) = OptGoals.data{i_goal}.wgt_map;
|
|
|
- end
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- h_w1 = waitbar(0, 'merging superboxels');
|
|
|
- for i_supVox = 1:numel(superVoxList)
|
|
|
- waitbar(i_supVox/numel(superVoxList), h_w1)
|
|
|
- supVox_idx = superVoxList(i_supVox);
|
|
|
- idxList = find(superMask == supVox_idx);
|
|
|
- optGoal{i_goal}.beamlets_pruned(i_supVox,:) = sparse(mean(beamlets(idxList, :),1));
|
|
|
-
|
|
|
- if isfield(OptGoals.data{i_goal}, 'wgt_map')
|
|
|
- optGoal{i_goal}.vox_wgt(i_supVox) = sum(tabula_wgtmap(idxList));
|
|
|
- end
|
|
|
-
|
|
|
- % -- make new indeces
|
|
|
- optGoal{i_goal}.ROI_idx(i_supVox) = idxList(1);
|
|
|
- end
|
|
|
- close(h_w1)
|
|
|
- end
|
|
|
-end
|
|
|
-
|
|
|
-% -- make them robust --
|
|
|
-RO_params=0;
|
|
|
-optGoal = make_robust_optGoal(optGoal, RO_params, beamlets);
|
|
|
-% save([Goal_path, Goal_file '_robust.mat'], 'optGoal')
|
|
|
-
|
|
|
-% -- get beamlet indeces --
|
|
|
-load([Pat_path, '\all_beams.mat'])
|
|
|
-Nbeamlets = all_beams{1}.Mxp; % number of beamlets in a beam - usually 64 or 32
|
|
|
-weightTable = zeros(100,Nbeamlets);
|
|
|
-for ind_bmlt = 1:numel(all_beams)
|
|
|
- bLet_idx.y(ind_bmlt) = floor(all_beams{1, ind_bmlt}.num/Nbeamlets)+1;
|
|
|
- bLet_idx.x(ind_bmlt) = rem(all_beams{1, ind_bmlt}.num, Nbeamlets)+1;
|
|
|
- weightTable(bLet_idx.y(ind_bmlt),bLet_idx.x(ind_bmlt)) = 1;
|
|
|
-end
|
|
|
-bLet_idx.idx = find(weightTable>0);
|
|
|
-bLet_idx.Nbeamlets = Nbeamlets;
|
|
|
-disp('.')
|
|
|
-
|
|
|
-% -- CALLBACK OPTIMIZATION FUNCTION --
|
|
|
-fun1 = @(x) get_penalty(x, optGoal_beam, bLet_idx);
|
|
|
-fun2 = @(x) get_penalty(x, optGoal, bLet_idx);
|
|
|
-
|
|
|
-% -- OPTIMIZATION PARAMETERS --
|
|
|
-% define optimization parameters
|
|
|
-A = [];
|
|
|
-b = [];
|
|
|
-Aeq = [];
|
|
|
-beq = [];
|
|
|
-lb = zeros(1, numBeamlet);
|
|
|
-% lb_beam = zeros(1, numBeam);
|
|
|
-ub = [];
|
|
|
-nonlcon = [];
|
|
|
-
|
|
|
-% define opt limits, and make it fmincon progress
|
|
|
-options = optimoptions('fmincon');
|
|
|
-options.MaxFunctionEvaluations = N_fcallback1;
|
|
|
-options.Display = 'iter';
|
|
|
-options.PlotFcn = 'optimplotfval';
|
|
|
-% options.UseParallel = true;
|
|
|
-options.UseParallel = false;
|
|
|
-% options.OptimalityTolerance = 1e-9;
|
|
|
-
|
|
|
-%% -- INITIALIZE BEAMLET WEIGHTS --
|
|
|
-switch pre_beamWeights
|
|
|
- case 'y'
|
|
|
- % should have been assigned previously.
|
|
|
- disp('Provided beamlet weights used for initial comparison')
|
|
|
- case 'n'
|
|
|
- % if initial beamlet weights are not provided, get quick estimate
|
|
|
-% fprintf('\n running initial optimizer:')
|
|
|
- % initialize beamlet weights, OR
|
|
|
- w0 = ones(numBeamlet,1);
|
|
|
-% w0 = mean(optGoal{1}.D_final(optGoal{1}.ROI_idx) ./ (optGoal{1}.beamlets_pruned*w0+0.1)) * w0; % old
|
|
|
- w0 = mean(optGoal{1}.D_final(:)) ./ mean(optGoal{1}.beamlets_pruned*w0+0.05) * w0;
|
|
|
- w_beamlets = double(w0);
|
|
|
-
|
|
|
- % -- GET BEAM WEIGHTS --
|
|
|
-% tic
|
|
|
-% w_beam = fmincon(fun1,w0_beams,A,b,Aeq,beq,lb,ub,nonlcon,options);
|
|
|
-% fprintf(' done!:')
|
|
|
-% t=toc;
|
|
|
-% disp(['Optimization time for beams = ',num2str(t)]);
|
|
|
-%
|
|
|
-% w_beamlets = ones(numBeamlet,1);
|
|
|
-% numBeam=numel(unique(beam_i_list));
|
|
|
-% for beam_i = 1:numBeam % assign weights to beamlets
|
|
|
-% % beamlets from same beam get same initial weights
|
|
|
-% w_beamlets(beam_i_list == beam_i) = w_beam(beam_i);
|
|
|
-% end
|
|
|
-end
|
|
|
-
|
|
|
-%% FULL OPTIMIZATION
|
|
|
-
|
|
|
-% -- GET FULL BEAMLET WEIGHTS --
|
|
|
-options.MaxFunctionEvaluations = N_fcallback2;
|
|
|
-tic
|
|
|
-fprintf('\n running full optimizer:')
|
|
|
-w_fin = fmincon(fun2,w_beamlets,A,b,Aeq,beq,lb,ub,nonlcon,options);
|
|
|
-fprintf(' done!:')
|
|
|
-t=toc;
|
|
|
-disp(['Optimization time for beamlets = ',num2str(t)]);
|
|
|
-
|
|
|
-
|
|
|
-%% evaluate the results
|
|
|
-D_full = reshape(beamlets * w_fin, size(Geometry.data));
|
|
|
-
|
|
|
-%% save outputs
|
|
|
-NLP_result.dose = D_full;
|
|
|
-NLP_result.weights = w_fin;
|
|
|
-NLP_result.sss_scene_list = optGoal{1}.sss_scene_list;
|
|
|
-NLP_result.maxModulation = OptGoals.maxModulation;
|
|
|
-NLP_result.BeamSmoothMax = OptGoals.BeamSmoothMax;
|
|
|
-save([Pat_path, '\matlab_files\NLP_result_' Goal_file '.mat'], 'NLP_result');
|
|
|
-
|
|
|
-
|
|
|
-plot_DVH(Geometry, D_full)
|
|
|
-colorwash(Geometry.data, D_full, [500, 1500], [0, 90]);
|
|
|
-
|
|
|
-
|
|
|
-end
|
|
|
-
|
|
|
-%% support functions
|
|
|
-% ---- PENALTY FUNCTION ----
|
|
|
-function penalty = get_penalty(x, optGoal, bLet_idx)
|
|
|
- % this function gets called by the optimizer. It checks the penalty for
|
|
|
- % all the robust implementation and returns the worst result.
|
|
|
-
|
|
|
- NumScenarios = optGoal{1}.NbrRandScenarios * optGoal{1}.NbrSystSetUpScenarios * optGoal{1}.NbrRangeScenarios;
|
|
|
- fobj = zeros(NumScenarios,numel(optGoal)+2);
|
|
|
- sc_i = 1;
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- for nrs_i = 1:optGoal{1}.NbrRandScenarios
|
|
|
- for sss_i = 1 :optGoal{1}.NbrSystSetUpScenarios % syst. setup scenarios = sss
|
|
|
- for rgs_i = 1:optGoal{1}.NbrRangeScenarios % range scenario = rs
|
|
|
-% fobj(sc_i)=eval_f(x, optGoal, nrs_i, sss_i, rgs_i, bLet_idx);
|
|
|
- fobj(sc_i,:) = eval_f(x, optGoal, nrs_i, sss_i, rgs_i, bLet_idx);
|
|
|
- sc_i = sc_i + 1;
|
|
|
- end
|
|
|
- end
|
|
|
- end
|
|
|
-
|
|
|
-
|
|
|
- switch optGoal{1}.optFuncNum
|
|
|
- case 1 % "RO max"
|
|
|
- penalty=max(sum(fobj,2));
|
|
|
- case 2 % "RO mean"
|
|
|
- penalty=mean(sum(fobj,2));
|
|
|
- case 3 % "RO objective-based"
|
|
|
- penalty=sum(max(fobj,[],1));
|
|
|
- case 4 % "Classical"
|
|
|
- penalty=sum(fobj(1,:));
|
|
|
- end
|
|
|
- % take the worst case penalty of evaluated scenarios
|
|
|
-% penalty=max(fobj);
|
|
|
-
|
|
|
- % take the median worst case (stochastic robust)
|
|
|
-% penalty=median(fobj);
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-end
|
|
|
-% ------ supp: penalty for single scenario ------
|
|
|
-function penArray = eval_f(x, optGoal, nrs_i, sss_i, rgs_i, bLet_idx)
|
|
|
-% penalty = 0;
|
|
|
- penArray = zeros(1,numel(optGoal)+2); % all the optGoals, plus modulation plus smoothing
|
|
|
- % for each condition
|
|
|
- for goal_i = 1:numel(optGoal)
|
|
|
- switch optGoal{goal_i}.function
|
|
|
- % min, max, min_sq, max_sq, LeastSquare, min_perc_Volume, max_perc_Volume
|
|
|
- case 'min'
|
|
|
- % penalize if achieved dose is lower than target dose
|
|
|
- d_penalty = 1.0e0 * sum(max(0, ...
|
|
|
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target) -...
|
|
|
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)));
|
|
|
- case 'max'
|
|
|
- % penalize if achieved dose is higher than target dose
|
|
|
- d_penalty = 1.0e0 * sum(max(0, ...
|
|
|
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target)));
|
|
|
- case 'min_sq'
|
|
|
- % penalize if achieved dose is lower than target dose
|
|
|
- temp1=min(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target));
|
|
|
- d_penalty = 1.0e0 * sum(temp1.*temp1);
|
|
|
- case 'max_sq'
|
|
|
- % penalize if achieved dose is higher than target dose
|
|
|
- temp1=max(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target));
|
|
|
- d_penalty = 1.0e0 * sum(temp1.*temp1);
|
|
|
- case 'min_step'
|
|
|
- % penalize if achieved dose is lower than target dose
|
|
|
- temp1=-min(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target));
|
|
|
- d_penalty = 1.0e1 * (sum(temp1.*temp1) + 1.0e3* sum(temp1>0)/numel(temp1));
|
|
|
- case 'max_step'
|
|
|
- % penalize if achieved dose is higher than target dose
|
|
|
- temp1=max(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target));
|
|
|
- d_penalty = 1.0e1 * (sum(temp1.*temp1) + 1.0e3* sum(temp1>0)/numel(temp1));
|
|
|
- case 'LeastSquare'
|
|
|
- % penalize with sum of squares any deviation from target
|
|
|
- % dose
|
|
|
- temp1 = (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x) - ...
|
|
|
- optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target;
|
|
|
- d_penalty = 1.0e0* sum(temp1.^2);
|
|
|
- case 'min_perc_Volume'
|
|
|
- % penalize by amount of volume under threshold
|
|
|
- perc_vox = numel(find((optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target) -...
|
|
|
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x) > 0)) ...
|
|
|
- / numel(optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target);
|
|
|
- d_penalty = 3.0e4 * min(perc_vox-0.05, 0)
|
|
|
-
|
|
|
- case 'max_perc_Volume'
|
|
|
- % penalize by amount of volume under threshold
|
|
|
- perc_vox = numel(find((optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target) -...
|
|
|
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x) < 0)) ...
|
|
|
- / numel(optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target);
|
|
|
- d_penalty = 3.0e4 * min(perc_vox-0.05, 0)
|
|
|
- case 'min_sq_voxwgt'
|
|
|
- % penalize if achieved dose is lower than target dose
|
|
|
- temp1=min(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target));
|
|
|
- d_penalty = 1.0e0 * sum(temp1.*temp1.* optGoal{goal_i}.vox_wgt');
|
|
|
- case 'max_sq_voxwgt'
|
|
|
- % penalize if achieved dose is lower than target dose
|
|
|
- temp1=min(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target));
|
|
|
- d_penalty = 1.0e0 * sum(temp1.*temp1.* optGoal{goal_i}.vox_wgt');
|
|
|
- end
|
|
|
-% penalty = penalty + d_penalty * optGoal{goal_i}.opt_weight;
|
|
|
- penArray(goal_i) = d_penalty * optGoal{goal_i}.opt_weight;
|
|
|
- end
|
|
|
-
|
|
|
- %% add modulation penalty
|
|
|
- if true
|
|
|
- if isfield(optGoal{goal_i}, 'maxModulation')
|
|
|
- max_modulation = optGoal{goal_i}.maxModulation;
|
|
|
- else
|
|
|
- error('no Max modulation parameter enterd!')
|
|
|
- max_modulation = 5;
|
|
|
- end
|
|
|
-
|
|
|
- mod_pen_weight = 1.0e10;
|
|
|
- % calc the penalty
|
|
|
- mod_excess = max(0, x-max_modulation*mean(x));
|
|
|
-% mod_excess = max(0, x-max_modulation*mean(x(x>1)));
|
|
|
- mod_pen1 = mod_pen_weight*(sum(mod_excess) + numel(mod_excess)* any(mod_excess));
|
|
|
-% penalty = penalty + mod_pen1;
|
|
|
- penArray(end-1) = mod_pen1;
|
|
|
- end
|
|
|
-
|
|
|
- %% add penlty for single off beamlets - version 2
|
|
|
- if false
|
|
|
- if isfield(optGoal{goal_i}, 'BeamSmoothMax')
|
|
|
- BeamSmoothMax = optGoal{goal_i}.BeamSmoothMax;
|
|
|
- else
|
|
|
- error('no Max beam smooth parameter enterd!')
|
|
|
- BeamSmoothMax = 1e6;
|
|
|
- end
|
|
|
- mod_pen_weight = BeamSmoothMax; %1.0e6
|
|
|
- x_down = zeros(size(x));
|
|
|
- x_down(2:end) = x(1:end-1);
|
|
|
- x_down2 = zeros(size(x));
|
|
|
- x_down2(3:end) = x(1:end-2);
|
|
|
- x_up = zeros(size(x));
|
|
|
- x_up(1:end-1) = x(2:end);
|
|
|
- x_up2 = zeros(size(x));
|
|
|
- x_up2(1:end-2) = x(3:end);
|
|
|
- mod_pen2 = mod_pen_weight*sum((x_down+x_up-2*x).^2 + 0.5*(x_down2+x_up2-2*x).^2)/(mean(x)^(2)*numel(x));
|
|
|
-% penalty = penalty + mod_pen2;
|
|
|
- penArray(end) = mod_pen2;
|
|
|
- end
|
|
|
- %% add penlty for single off beamlets - version 3
|
|
|
- if true
|
|
|
- if isfield(optGoal{goal_i}, 'BeamSmoothMax')
|
|
|
- BeamSmoothMax = optGoal{goal_i}.BeamSmoothMax;
|
|
|
- else
|
|
|
- error('no Max beam smooth parameter enterd!')
|
|
|
- BeamSmoothMax = 1e6;
|
|
|
- end
|
|
|
- mod_pen_weight = BeamSmoothMax; %1.0e6
|
|
|
-
|
|
|
- % make 2D beamletWeight map
|
|
|
- maxY = max(bLet_idx.y);
|
|
|
- tabula = zeros(maxY, bLet_idx.Nbeamlets);
|
|
|
- tabula(bLet_idx.idx) = x;
|
|
|
- % for each index calculate laplace
|
|
|
- myWeights = 1/12*[1,3,1; 1,-12,1; 1,3,1];
|
|
|
- i=2:maxY-1;
|
|
|
- j=2:bLet_idx.Nbeamlets-1;
|
|
|
- tabula2(i,j) = ...
|
|
|
- myWeights(1)*tabula(i-1,j-1) + myWeights(4)*tabula(i-1,j) + myWeights(7)*tabula(i-1,j+1)...
|
|
|
- +myWeights(2)*tabula(i,j-1) + myWeights(8)*tabula(i,j+1) ...
|
|
|
- +myWeights(3)*tabula(i+1,j-1) + myWeights(6)*tabula(i+1,j) + myWeights(9)*tabula(i+1,j+1)...
|
|
|
- +myWeights(5)*tabula(i,j);
|
|
|
- tabula2(1,j) = ...
|
|
|
- myWeights(2)*tabula(1,j-1) + myWeights(8)*tabula(1,j+1)...
|
|
|
- +myWeights(3)*tabula(2,j-1) + myWeights(6)*tabula(2,j) + myWeights(9)*tabula(2,j+1)...
|
|
|
- +myWeights(5)*tabula(1,j);
|
|
|
- tabula2(maxY,j) =...
|
|
|
- myWeights(1)*tabula(maxY-1,j-1) + myWeights(4)*tabula(maxY-1,j) + myWeights(7)*tabula(maxY-1,j+1)...
|
|
|
- +myWeights(2)*tabula(maxY,j-1) + myWeights(8)*tabula(maxY,j+1) ...
|
|
|
- +myWeights(5)*tabula(maxY,j);
|
|
|
- % make sum of squares
|
|
|
- mod_pen2 = mod_pen_weight*sum((tabula2(:)).^2)/(mean(x.^2)*numel(x));
|
|
|
-% penalty = penalty + mod_pen2;
|
|
|
- penArray(end) = mod_pen2;
|
|
|
- end
|
|
|
-end
|
|
|
-
|
|
|
-% ---- MAKE ROI ROBUST ----
|
|
|
-function optGoal = make_robust_optGoal(optGoal, RO_params, beamlets);
|
|
|
- % take regular optimal goal and translate it into several robust cases
|
|
|
-
|
|
|
- % RO_params - should have the information below
|
|
|
- % nrs - random scenarios
|
|
|
- % sss - system setup scenarios
|
|
|
- % rgs - random range scenarios
|
|
|
-
|
|
|
- % X - X>0 moves image right
|
|
|
- % Y - Y>0 moves image down
|
|
|
- % Z - in/out.
|
|
|
-
|
|
|
- shift_X = 2; % vox of shift
|
|
|
- shift_Y = 2; % vox of shift
|
|
|
- shift_Z = 1; % vox of shift
|
|
|
- nrs_scene_list={[0,0,0]};
|
|
|
-
|
|
|
-
|
|
|
- % ----====#### CHANGE ROBUSTNESS HERE ####====----
|
|
|
- if isfield(optGoal{1}, 'sss_scene_list')
|
|
|
- sss_scene_list = optGoal{1}.sss_scene_list;
|
|
|
- else
|
|
|
- error('New OptGoals should no longer reach this code!')
|
|
|
- sss_scene_list={[0,0,0], [-shift_Y,0,0], [shift_Y,0,0], [0,-shift_X,0], [0,shift_X,0], [0,0,-shift_Z], [0,0,shift_Z]};
|
|
|
- optGoal{1}.sss_scene_list = sss_scene_list;
|
|
|
- end
|
|
|
-% sss_scene_list={[0,0,0]};
|
|
|
-
|
|
|
- % ----====#### CHANGE ROBUSTNESS HERE ####====----
|
|
|
-% [targetIn, meta] = nrrdread('C:\010-work\003_localGit\WiscPlan_v2\data\archive\CDP_data\CDP5_DP_target.nrrd');
|
|
|
-% [targetIn, meta] = nrrdread('C:\010-work\003_localGit\WiscPlan_v2\data\PD_HD_dicomPhantom\Tomo_DP_target.nrrd');
|
|
|
-% [targetIn, meta] = nrrdread('C:\010-work\003_localGit\WiscPlan_v2\data\archive\CDP_data\CDP5_DP_target.nrrd');
|
|
|
- rgs_scene_list={[0,0,0]};
|
|
|
- for i = 1:numel(optGoal)
|
|
|
- optGoal{i}.NbrRandScenarios =numel(nrs_scene_list);
|
|
|
- optGoal{i}.NbrSystSetUpScenarios=numel(sss_scene_list);
|
|
|
- optGoal{i}.NbrRangeScenarios =numel(rgs_scene_list);
|
|
|
- end
|
|
|
- for goal_i = 1:numel(optGoal)
|
|
|
- % get target
|
|
|
- idx=optGoal{goal_i}.ROI_idx;
|
|
|
- targetImg1=zeros(optGoal{goal_i}.imgDim);
|
|
|
- targetImg1(idx)=1;
|
|
|
- % get beamlets
|
|
|
- for nrs_i = 1:optGoal{goal_i}.NbrRandScenarios % num. of random scenarios
|
|
|
- % modify target and beamlets
|
|
|
- targetImg2=targetImg1;
|
|
|
- % beamlets stay the same
|
|
|
- for sss_i = 1 :optGoal{goal_i}.NbrSystSetUpScenarios % syst. setup scenarios = sss
|
|
|
- % modify target and beamlets
|
|
|
- [targetImg3 idxValid]=get_RO_sss(targetImg2, sss_scene_list{sss_i});
|
|
|
- % beamlets stay the same
|
|
|
- for rgs_i = 1:optGoal{goal_i}.NbrRangeScenarios % range scenario = rgs
|
|
|
- % modify target and beamlets
|
|
|
- targetImg4=targetImg3;
|
|
|
- % beamlets stay the same
|
|
|
- %% make new target and beamlets
|
|
|
- ROI_idx=[];
|
|
|
- ROI_idx=find(targetImg4>0);
|
|
|
- target = optGoal{goal_i}.D_final(idxValid);
|
|
|
- beamlets_pruned = beamlets(ROI_idx, :);
|
|
|
- % save to optGoal output
|
|
|
- optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.ROI_idx = ROI_idx;
|
|
|
- optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned = beamlets_pruned;
|
|
|
- optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target = target;
|
|
|
- end
|
|
|
- end
|
|
|
- end
|
|
|
- end
|
|
|
-end
|
|
|
-%% ------ supp: RO case SSS ------
|
|
|
-function [targetImg3 ia]=get_RO_sss(targetImg2, sss_scene_shift);
|
|
|
- % translate the target image
|
|
|
-
|
|
|
- targetImg3 = imtranslate(targetImg2,sss_scene_shift);
|
|
|
-
|
|
|
- % now we need to figure out if any target voxels fell out during the
|
|
|
- % shift
|
|
|
- imgValid = imtranslate(targetImg3,-sss_scene_shift);
|
|
|
- imgInvalid = (targetImg2-imgValid);
|
|
|
-
|
|
|
- idx_1 = find(targetImg2);
|
|
|
- idx_2 = find(imgInvalid);
|
|
|
-
|
|
|
- [idxValid,ia] = setdiff(idx_1,idx_2);
|
|
|
-
|
|
|
- [C,ia, ib] = intersect(idx_1,idxValid);
|
|
|
-
|
|
|
-end
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
+% paths.patient_dir
|
|
|
+% paths.Goal_dir (previously called DP_dir)
|
|
|
+% paths.patient
|
|
|
+% paths.goalsName
|
|
|
+
|
|
|
+% colorwash(Geometry.data, D_full, [500, 1500], [0,70])
|
|
|
+% orthoslice(D_full, [0,70])
|
|
|
+
|
|
|
+function [D_full, w_fin, Geometry, optGoal] = NLP_optimizer_v3(varargin)
|
|
|
+% This function performs the beamlet optimization
|
|
|
+% [D_full, w_fin, Geometry, optGoal] = NLP_beamlet_optimizer;
|
|
|
+%
|
|
|
+% Inputs:
|
|
|
+% () OR
|
|
|
+% (Pat_path, path2goal) OR
|
|
|
+% (Pat_path, path2goal, beamlet_weights)
|
|
|
+% Pat_path, path2goal = strings to patient folder and optimal goals
|
|
|
+% beamlet_weights = initial beamlet weights
|
|
|
+%
|
|
|
+% Outputs:
|
|
|
+% full dose image dose: [D_full, w_fin, Geometry, optGoal]
|
|
|
+%
|
|
|
+% Made by Peter Ferjancic 1. May 2018
|
|
|
+% Last updated: 1. April 2019
|
|
|
+
|
|
|
+if nargin<2
|
|
|
+ load('WiscPlan_preferences.mat')
|
|
|
+ [Pat_path] = uigetdir([WiscPlan_preferences.patientDataPath ], 'Select Patient folder');
|
|
|
+ [Goal_file,Goal_path,indx] = uigetfile([Pat_path '\matlab_files\*.mat'], 'Select OptGoal file');
|
|
|
+
|
|
|
+ path2geometry = [Pat_path, '\matlab_files\Geometry.mat'];
|
|
|
+ path2goal = [Goal_path, Goal_file];
|
|
|
+else
|
|
|
+ Pat_path = varargin{1};
|
|
|
+ path2geometry = [Pat_path, '\matlab_files\Geometry.mat'];
|
|
|
+ path2goal = varargin{2};
|
|
|
+ [Goal_path,Goal_file,ext] = fileparts(path2goal);
|
|
|
+end
|
|
|
+
|
|
|
+dialogue_box = 'yes'
|
|
|
+switch dialogue_box
|
|
|
+ case 'yes'
|
|
|
+ str = inputdlg({'N of iterations for initial calc', 'N of iterations for full calc', ...
|
|
|
+ 'Use pre-existing NLP_result to initiate? (y/n)'}, 'input', [1,35], {'100000', '500000', 'n'});
|
|
|
+ N_fcallback1 = str2double(str{1}); % 100000 is a good guesstimate
|
|
|
+ N_fcallback2 = str2double(str{2}); % 500000 is a good guesstimate
|
|
|
+ pre_beamWeights = str{3};
|
|
|
+ case 'no'
|
|
|
+ disp('dialogue box skipped')
|
|
|
+ N_fcallback1 = 1e5;
|
|
|
+ N_fcallback2 = 2e6; % 500000;
|
|
|
+ pre_beamWeights = 'n';
|
|
|
+end
|
|
|
+
|
|
|
+
|
|
|
+switch pre_beamWeights
|
|
|
+ case 'y'
|
|
|
+ [NLP_file,NLP_path,indx] = uigetfile([Pat_path '\matlab_files\*.mat'], 'Select NLP_result file');
|
|
|
+ load([NLP_path, NLP_file])
|
|
|
+ w_beamlets = NLP_result.weights;
|
|
|
+
|
|
|
+ load([Pat_path, '\all_beams.mat'])
|
|
|
+ if numel(all_beams) ~= numel(w_beamlets)
|
|
|
+ error('Provided weight number does not match beamlet number!')
|
|
|
+ end
|
|
|
+ case 'n'
|
|
|
+ disp('Initial beam weights will be calculated.')
|
|
|
+end
|
|
|
+
|
|
|
+%% PROGRAM STARTS HERE
|
|
|
+% - no tocar lo que hay debajo -
|
|
|
+fprintf('starting NLP optimization process... \n')
|
|
|
+
|
|
|
+% % -- LOAD GEOMETRY, GOALS, BEAMLETS --
|
|
|
+load(path2geometry)
|
|
|
+load(path2goal)
|
|
|
+[beamlets, numBeamlet] = get_beamlets(Geometry, Pat_path, OptGoals);
|
|
|
+% [beamlets, beamlets_joined, numBeamlet, numBeam, beam_i_list] = get_beam_lets(Geometry, Pat_path);
|
|
|
+
|
|
|
+
|
|
|
+%% -- OPTIMIZATION TARGETS --
|
|
|
+% -- make the optimization optGoal structure --
|
|
|
+for i_goal = 1:size(OptGoals.goals,1)
|
|
|
+
|
|
|
+ if isfield(OptGoals.data{i_goal}, 'SupVox_num')
|
|
|
+ SupVox_num = OptGoals.data{i_goal}.SupVox_num;
|
|
|
+ else
|
|
|
+ answer = inputdlg(['# of supervoxels for "' OptGoals.data{i_goal}.name '" with ' num2str(numel(OptGoals.data{i_goal}.ROI_idx)) ' vox: ("0" to skip)'])
|
|
|
+ SupVox_num = str2double(answer{1})
|
|
|
+ end
|
|
|
+ optGoal{i_goal} = OptGoals.data{i_goal};
|
|
|
+ optGoal{i_goal}.sss_scene_list = OptGoals.sss_scene_list;
|
|
|
+ optGoal{i_goal}.maxModulation = OptGoals.maxModulation;
|
|
|
+ optGoal{i_goal}.BeamSmoothMax = OptGoals.BeamSmoothMax;
|
|
|
+
|
|
|
+ optGoal{i_goal}.optFuncNum = OptGoals.optFuncNum;
|
|
|
+
|
|
|
+ % modulation
|
|
|
+ switch SupVox_num
|
|
|
+ case 0
|
|
|
+ % if not supervoxel, just select provided ROI_idx
|
|
|
+
|
|
|
+ optGoal{i_goal}.beamlets_pruned = sparse(beamlets(optGoal{i_goal}.ROI_idx, :));
|
|
|
+
|
|
|
+ otherwise
|
|
|
+ % -- if supervoxel, merge given columns
|
|
|
+ % - make supervoxel map
|
|
|
+ switch OptGoals.goals{i_goal, 3}
|
|
|
+ case 'Fixed dose'
|
|
|
+ mask = zeros(OptGoals.data{i_goal}.imgDim);
|
|
|
+ mask(OptGoals.data{i_goal}.ROI_idx) = 1;
|
|
|
+ % group superpixels
|
|
|
+ superMask = superpix_group(mask, SupVox_num, 'no');
|
|
|
+ case 'Dose map'
|
|
|
+ mask = zeros(OptGoals.data{i_goal}.imgDim);
|
|
|
+ mask(OptGoals.data{i_goal}.ROI_idx) = OptGoals.data{i_goal}.D_final;
|
|
|
+ mask2 = round(mask/3)*3;
|
|
|
+ superMask = superpix_group(mask, SupVox_num, 'no');
|
|
|
+ orthoslice(superMask)
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ superVoxList = unique(superMask);
|
|
|
+ superVoxList = superVoxList(superVoxList>0);
|
|
|
+
|
|
|
+ optGoal{i_goal}.ROI_idx_old = optGoal{i_goal}.ROI_idx; % copy old index data
|
|
|
+ optGoal{i_goal}.ROI_idx = zeros(numel(superVoxList), 1);
|
|
|
+ optGoal{i_goal}.opt_weight = optGoal{i_goal}.opt_weight * numel(optGoal{i_goal}.ROI_idx_old)/numel(optGoal{i_goal}.ROI_idx);
|
|
|
+
|
|
|
+ if isfield(OptGoals.data{i_goal}, 'wgt_map')
|
|
|
+ tabula_wgtmap = zeros(size(superMask));
|
|
|
+ tabula_wgtmap(OptGoals.data{i_goal}.ROI_idx) = OptGoals.data{i_goal}.wgt_map;
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ h_w1 = waitbar(0, 'merging superboxels');
|
|
|
+ for i_supVox = 1:numel(superVoxList)
|
|
|
+ waitbar(i_supVox/numel(superVoxList), h_w1)
|
|
|
+ supVox_idx = superVoxList(i_supVox);
|
|
|
+ idxList = find(superMask == supVox_idx);
|
|
|
+ optGoal{i_goal}.beamlets_pruned(i_supVox,:) = sparse(mean(beamlets(idxList, :),1));
|
|
|
+
|
|
|
+ if isfield(OptGoals.data{i_goal}, 'wgt_map')
|
|
|
+ optGoal{i_goal}.vox_wgt(i_supVox) = sum(tabula_wgtmap(idxList));
|
|
|
+ end
|
|
|
+
|
|
|
+ % -- make new indeces
|
|
|
+ optGoal{i_goal}.ROI_idx(i_supVox) = idxList(1);
|
|
|
+ end
|
|
|
+ close(h_w1)
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+% -- make them robust --
|
|
|
+RO_params=0;
|
|
|
+optGoal = make_robust_optGoal(optGoal, RO_params, beamlets);
|
|
|
+% save([Goal_path, Goal_file '_robust.mat'], 'optGoal')
|
|
|
+
|
|
|
+% -- get beamlet indeces --
|
|
|
+load([Pat_path, '\all_beams.mat'])
|
|
|
+Nbeamlets = all_beams{1}.Mxp; % number of beamlets in a beam - usually 64 or 32
|
|
|
+weightTable = zeros(100,Nbeamlets);
|
|
|
+for ind_bmlt = 1:numel(all_beams)
|
|
|
+ bLet_idx.y(ind_bmlt) = floor(all_beams{1, ind_bmlt}.num/Nbeamlets)+1;
|
|
|
+ bLet_idx.x(ind_bmlt) = rem(all_beams{1, ind_bmlt}.num, Nbeamlets)+1;
|
|
|
+ weightTable(bLet_idx.y(ind_bmlt),bLet_idx.x(ind_bmlt)) = 1;
|
|
|
+end
|
|
|
+bLet_idx.idx = find(weightTable>0);
|
|
|
+bLet_idx.Nbeamlets = Nbeamlets;
|
|
|
+disp('.')
|
|
|
+
|
|
|
+% -- CALLBACK OPTIMIZATION FUNCTION --
|
|
|
+fun1 = @(x) get_penalty(x, optGoal_beam, bLet_idx);
|
|
|
+fun2 = @(x) get_penalty(x, optGoal, bLet_idx);
|
|
|
+
|
|
|
+% -- OPTIMIZATION PARAMETERS --
|
|
|
+% define optimization parameters
|
|
|
+A = [];
|
|
|
+b = [];
|
|
|
+Aeq = [];
|
|
|
+beq = [];
|
|
|
+lb = zeros(1, numBeamlet);
|
|
|
+% lb_beam = zeros(1, numBeam);
|
|
|
+ub = [];
|
|
|
+nonlcon = [];
|
|
|
+
|
|
|
+% define opt limits, and make it fmincon progress
|
|
|
+options = optimoptions('fmincon');
|
|
|
+options.MaxFunctionEvaluations = N_fcallback1;
|
|
|
+options.Display = 'iter';
|
|
|
+options.PlotFcn = 'optimplotfval';
|
|
|
+% options.UseParallel = true;
|
|
|
+options.UseParallel = false;
|
|
|
+% options.OptimalityTolerance = 1e-9;
|
|
|
+
|
|
|
+%% -- INITIALIZE BEAMLET WEIGHTS --
|
|
|
+switch pre_beamWeights
|
|
|
+ case 'y'
|
|
|
+ % should have been assigned previously.
|
|
|
+ disp('Provided beamlet weights used for initial comparison')
|
|
|
+ case 'n'
|
|
|
+ % if initial beamlet weights are not provided, get quick estimate
|
|
|
+% fprintf('\n running initial optimizer:')
|
|
|
+ % initialize beamlet weights, OR
|
|
|
+ w0 = ones(numBeamlet,1);
|
|
|
+% w0 = mean(optGoal{1}.D_final(optGoal{1}.ROI_idx) ./ (optGoal{1}.beamlets_pruned*w0+0.1)) * w0; % old
|
|
|
+ w0 = mean(optGoal{1}.D_final(:)) ./ mean(optGoal{1}.beamlets_pruned*w0+0.05) * w0;
|
|
|
+ w_beamlets = double(w0);
|
|
|
+
|
|
|
+ % -- GET BEAM WEIGHTS --
|
|
|
+% tic
|
|
|
+% w_beam = fmincon(fun1,w0_beams,A,b,Aeq,beq,lb,ub,nonlcon,options);
|
|
|
+% fprintf(' done!:')
|
|
|
+% t=toc;
|
|
|
+% disp(['Optimization time for beams = ',num2str(t)]);
|
|
|
+%
|
|
|
+% w_beamlets = ones(numBeamlet,1);
|
|
|
+% numBeam=numel(unique(beam_i_list));
|
|
|
+% for beam_i = 1:numBeam % assign weights to beamlets
|
|
|
+% % beamlets from same beam get same initial weights
|
|
|
+% w_beamlets(beam_i_list == beam_i) = w_beam(beam_i);
|
|
|
+% end
|
|
|
+end
|
|
|
+
|
|
|
+%% FULL OPTIMIZATION
|
|
|
+
|
|
|
+% -- GET FULL BEAMLET WEIGHTS --
|
|
|
+options.MaxFunctionEvaluations = N_fcallback2;
|
|
|
+tic
|
|
|
+fprintf('\n running full optimizer:')
|
|
|
+w_fin = fmincon(fun2,w_beamlets,A,b,Aeq,beq,lb,ub,nonlcon,options);
|
|
|
+fprintf(' done!:')
|
|
|
+t=toc;
|
|
|
+disp(['Optimization time for beamlets = ',num2str(t)]);
|
|
|
+
|
|
|
+
|
|
|
+%% evaluate the results
|
|
|
+D_full = reshape(beamlets * w_fin, size(Geometry.data));
|
|
|
+
|
|
|
+%% save outputs
|
|
|
+NLP_result.dose = D_full;
|
|
|
+NLP_result.weights = w_fin;
|
|
|
+NLP_result.sss_scene_list = optGoal{1}.sss_scene_list;
|
|
|
+NLP_result.maxModulation = OptGoals.maxModulation;
|
|
|
+NLP_result.BeamSmoothMax = OptGoals.BeamSmoothMax;
|
|
|
+save([Pat_path, '\matlab_files\NLP_result_' Goal_file '.mat'], 'NLP_result');
|
|
|
+
|
|
|
+
|
|
|
+plot_DVH(Geometry, D_full)
|
|
|
+colorwash(Geometry.data, D_full, [500, 1500], [0, 90]);
|
|
|
+
|
|
|
+
|
|
|
+end
|
|
|
+
|
|
|
+%% support functions
|
|
|
+% ---- PENALTY FUNCTION ----
|
|
|
+function penalty = get_penalty(x, optGoal, bLet_idx)
|
|
|
+ % this function gets called by the optimizer. It checks the penalty for
|
|
|
+ % all the robust implementation and returns the worst result.
|
|
|
+
|
|
|
+ NumScenarios = optGoal{1}.NbrRandScenarios * optGoal{1}.NbrSystSetUpScenarios * optGoal{1}.NbrRangeScenarios;
|
|
|
+ fobj = zeros(NumScenarios,numel(optGoal)+2);
|
|
|
+ sc_i = 1;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ for nrs_i = 1:optGoal{1}.NbrRandScenarios
|
|
|
+ for sss_i = 1 :optGoal{1}.NbrSystSetUpScenarios % syst. setup scenarios = sss
|
|
|
+ for rgs_i = 1:optGoal{1}.NbrRangeScenarios % range scenario = rs
|
|
|
+% fobj(sc_i)=eval_f(x, optGoal, nrs_i, sss_i, rgs_i, bLet_idx);
|
|
|
+ fobj(sc_i,:) = eval_f(x, optGoal, nrs_i, sss_i, rgs_i, bLet_idx);
|
|
|
+ sc_i = sc_i + 1;
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+ switch optGoal{1}.optFuncNum
|
|
|
+ case 1 % "RO max"
|
|
|
+ penalty=max(sum(fobj,2));
|
|
|
+ case 2 % "RO mean"
|
|
|
+ penalty=mean(sum(fobj,2));
|
|
|
+ case 3 % "RO objective-based"
|
|
|
+ penalty=sum(max(fobj,[],1));
|
|
|
+ case 4 % "Classical"
|
|
|
+ penalty=sum(fobj(1,:));
|
|
|
+ end
|
|
|
+ % take the worst case penalty of evaluated scenarios
|
|
|
+% penalty=max(fobj);
|
|
|
+
|
|
|
+ % take the median worst case (stochastic robust)
|
|
|
+% penalty=median(fobj);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+end
|
|
|
+% ------ supp: penalty for single scenario ------
|
|
|
+function penArray = eval_f(x, optGoal, nrs_i, sss_i, rgs_i, bLet_idx)
|
|
|
+% penalty = 0;
|
|
|
+ penArray = zeros(1,numel(optGoal)+2); % all the optGoals, plus modulation plus smoothing
|
|
|
+ % for each condition
|
|
|
+ for goal_i = 1:numel(optGoal)
|
|
|
+ switch optGoal{goal_i}.function
|
|
|
+ % min, max, min_sq, max_sq, LeastSquare, min_perc_Volume, max_perc_Volume
|
|
|
+ case 'min'
|
|
|
+ % penalize if achieved dose is lower than target dose
|
|
|
+ d_penalty = 1.0e0 * sum(max(0, ...
|
|
|
+ (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target) -...
|
|
|
+ (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)));
|
|
|
+ case 'max'
|
|
|
+ % penalize if achieved dose is higher than target dose
|
|
|
+ d_penalty = 1.0e0 * sum(max(0, ...
|
|
|
+ (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
+ (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target)));
|
|
|
+ case 'min_sq'
|
|
|
+ % penalize if achieved dose is lower than target dose
|
|
|
+ temp1=min(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
+ (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target));
|
|
|
+ d_penalty = 1.0e0 * sum(temp1.*temp1);
|
|
|
+ case 'max_sq'
|
|
|
+ % penalize if achieved dose is higher than target dose
|
|
|
+ temp1=max(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
+ (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target));
|
|
|
+ d_penalty = 1.0e0 * sum(temp1.*temp1);
|
|
|
+ case 'min_step'
|
|
|
+ % penalize if achieved dose is lower than target dose
|
|
|
+ temp1=-min(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
+ (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target));
|
|
|
+ d_penalty = 1.0e1 * (sum(temp1.*temp1) + 1.0e3* sum(temp1>0)/numel(temp1));
|
|
|
+ case 'max_step'
|
|
|
+ % penalize if achieved dose is higher than target dose
|
|
|
+ temp1=max(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
+ (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target));
|
|
|
+ d_penalty = 1.0e1 * (sum(temp1.*temp1) + 1.0e3* sum(temp1>0)/numel(temp1));
|
|
|
+ case 'LeastSquare'
|
|
|
+ % penalize with sum of squares any deviation from target
|
|
|
+ % dose
|
|
|
+ temp1 = (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x) - ...
|
|
|
+ optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target;
|
|
|
+ d_penalty = 1.0e0* sum(temp1.^2);
|
|
|
+ case 'min_perc_Volume'
|
|
|
+ % penalize by amount of volume under threshold
|
|
|
+ perc_vox = numel(find((optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target) -...
|
|
|
+ (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x) > 0)) ...
|
|
|
+ / numel(optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target);
|
|
|
+ d_penalty = 3.0e4 * min(perc_vox-0.05, 0)
|
|
|
+
|
|
|
+ case 'max_perc_Volume'
|
|
|
+ % penalize by amount of volume under threshold
|
|
|
+ perc_vox = numel(find((optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target) -...
|
|
|
+ (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x) < 0)) ...
|
|
|
+ / numel(optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target);
|
|
|
+ d_penalty = 3.0e4 * min(perc_vox-0.05, 0)
|
|
|
+ case 'min_sq_voxwgt'
|
|
|
+ % penalize if achieved dose is lower than target dose
|
|
|
+ temp1=min(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
+ (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target));
|
|
|
+ d_penalty = 1.0e0 * sum(temp1.*temp1.* optGoal{goal_i}.vox_wgt');
|
|
|
+ case 'max_sq_voxwgt'
|
|
|
+ % penalize if achieved dose is lower than target dose
|
|
|
+ temp1=min(0, (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned * x)-...
|
|
|
+ (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target));
|
|
|
+ d_penalty = 1.0e0 * sum(temp1.*temp1.* optGoal{goal_i}.vox_wgt');
|
|
|
+ end
|
|
|
+% penalty = penalty + d_penalty * optGoal{goal_i}.opt_weight;
|
|
|
+ penArray(goal_i) = d_penalty * optGoal{goal_i}.opt_weight;
|
|
|
+ end
|
|
|
+
|
|
|
+ %% add modulation penalty
|
|
|
+ if true
|
|
|
+ if isfield(optGoal{goal_i}, 'maxModulation')
|
|
|
+ max_modulation = optGoal{goal_i}.maxModulation;
|
|
|
+ else
|
|
|
+ error('no Max modulation parameter enterd!')
|
|
|
+ max_modulation = 5;
|
|
|
+ end
|
|
|
+
|
|
|
+ mod_pen_weight = 1.0e10;
|
|
|
+ % calc the penalty
|
|
|
+ mod_excess = max(0, x-max_modulation*mean(x));
|
|
|
+% mod_excess = max(0, x-max_modulation*mean(x(x>1)));
|
|
|
+ mod_pen1 = mod_pen_weight*(sum(mod_excess) + numel(mod_excess)* any(mod_excess));
|
|
|
+% penalty = penalty + mod_pen1;
|
|
|
+ penArray(end-1) = mod_pen1;
|
|
|
+ end
|
|
|
+
|
|
|
+ %% add penlty for single off beamlets - version 2
|
|
|
+ if false
|
|
|
+ if isfield(optGoal{goal_i}, 'BeamSmoothMax')
|
|
|
+ BeamSmoothMax = optGoal{goal_i}.BeamSmoothMax;
|
|
|
+ else
|
|
|
+ error('no Max beam smooth parameter enterd!')
|
|
|
+ BeamSmoothMax = 1e6;
|
|
|
+ end
|
|
|
+ mod_pen_weight = BeamSmoothMax; %1.0e6
|
|
|
+ x_down = zeros(size(x));
|
|
|
+ x_down(2:end) = x(1:end-1);
|
|
|
+ x_down2 = zeros(size(x));
|
|
|
+ x_down2(3:end) = x(1:end-2);
|
|
|
+ x_up = zeros(size(x));
|
|
|
+ x_up(1:end-1) = x(2:end);
|
|
|
+ x_up2 = zeros(size(x));
|
|
|
+ x_up2(1:end-2) = x(3:end);
|
|
|
+ mod_pen2 = mod_pen_weight*sum((x_down+x_up-2*x).^2 + 0.5*(x_down2+x_up2-2*x).^2)/(mean(x)^(2)*numel(x));
|
|
|
+% penalty = penalty + mod_pen2;
|
|
|
+ penArray(end) = mod_pen2;
|
|
|
+ end
|
|
|
+ %% add penlty for single off beamlets - version 3
|
|
|
+ if true
|
|
|
+ if isfield(optGoal{goal_i}, 'BeamSmoothMax')
|
|
|
+ BeamSmoothMax = optGoal{goal_i}.BeamSmoothMax;
|
|
|
+ else
|
|
|
+ error('no Max beam smooth parameter enterd!')
|
|
|
+ BeamSmoothMax = 1e6;
|
|
|
+ end
|
|
|
+ mod_pen_weight = BeamSmoothMax; %1.0e6
|
|
|
+
|
|
|
+ % make 2D beamletWeight map
|
|
|
+ maxY = max(bLet_idx.y);
|
|
|
+ tabula = zeros(maxY, bLet_idx.Nbeamlets);
|
|
|
+ tabula(bLet_idx.idx) = x;
|
|
|
+ % for each index calculate laplace
|
|
|
+ myWeights = 1/12*[1,3,1; 1,-12,1; 1,3,1];
|
|
|
+ i=2:maxY-1;
|
|
|
+ j=2:bLet_idx.Nbeamlets-1;
|
|
|
+ tabula2(i,j) = ...
|
|
|
+ myWeights(1)*tabula(i-1,j-1) + myWeights(4)*tabula(i-1,j) + myWeights(7)*tabula(i-1,j+1)...
|
|
|
+ +myWeights(2)*tabula(i,j-1) + myWeights(8)*tabula(i,j+1) ...
|
|
|
+ +myWeights(3)*tabula(i+1,j-1) + myWeights(6)*tabula(i+1,j) + myWeights(9)*tabula(i+1,j+1)...
|
|
|
+ +myWeights(5)*tabula(i,j);
|
|
|
+ tabula2(1,j) = ...
|
|
|
+ myWeights(2)*tabula(1,j-1) + myWeights(8)*tabula(1,j+1)...
|
|
|
+ +myWeights(3)*tabula(2,j-1) + myWeights(6)*tabula(2,j) + myWeights(9)*tabula(2,j+1)...
|
|
|
+ +myWeights(5)*tabula(1,j);
|
|
|
+ tabula2(maxY,j) =...
|
|
|
+ myWeights(1)*tabula(maxY-1,j-1) + myWeights(4)*tabula(maxY-1,j) + myWeights(7)*tabula(maxY-1,j+1)...
|
|
|
+ +myWeights(2)*tabula(maxY,j-1) + myWeights(8)*tabula(maxY,j+1) ...
|
|
|
+ +myWeights(5)*tabula(maxY,j);
|
|
|
+ % make sum of squares
|
|
|
+ mod_pen2 = mod_pen_weight*sum((tabula2(:)).^2)/(mean(x.^2)*numel(x));
|
|
|
+% penalty = penalty + mod_pen2;
|
|
|
+ penArray(end) = mod_pen2;
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+% ---- MAKE ROI ROBUST ----
|
|
|
+function optGoal = make_robust_optGoal(optGoal, RO_params, beamlets);
|
|
|
+ % take regular optimal goal and translate it into several robust cases
|
|
|
+
|
|
|
+ % RO_params - should have the information below
|
|
|
+ % nrs - random scenarios
|
|
|
+ % sss - system setup scenarios
|
|
|
+ % rgs - random range scenarios
|
|
|
+
|
|
|
+ % X - X>0 moves image right
|
|
|
+ % Y - Y>0 moves image down
|
|
|
+ % Z - in/out.
|
|
|
+
|
|
|
+ shift_X = 2; % vox of shift
|
|
|
+ shift_Y = 2; % vox of shift
|
|
|
+ shift_Z = 1; % vox of shift
|
|
|
+ nrs_scene_list={[0,0,0]};
|
|
|
+
|
|
|
+
|
|
|
+ % ----====#### CHANGE ROBUSTNESS HERE ####====----
|
|
|
+ if isfield(optGoal{1}, 'sss_scene_list')
|
|
|
+ sss_scene_list = optGoal{1}.sss_scene_list;
|
|
|
+ else
|
|
|
+ error('New OptGoals should no longer reach this code!')
|
|
|
+ sss_scene_list={[0,0,0], [-shift_Y,0,0], [shift_Y,0,0], [0,-shift_X,0], [0,shift_X,0], [0,0,-shift_Z], [0,0,shift_Z]};
|
|
|
+ optGoal{1}.sss_scene_list = sss_scene_list;
|
|
|
+ end
|
|
|
+% sss_scene_list={[0,0,0]};
|
|
|
+
|
|
|
+ % ----====#### CHANGE ROBUSTNESS HERE ####====----
|
|
|
+% [targetIn, meta] = nrrdread('C:\010-work\003_localGit\WiscPlan_v2\data\archive\CDP_data\CDP5_DP_target.nrrd');
|
|
|
+% [targetIn, meta] = nrrdread('C:\010-work\003_localGit\WiscPlan_v2\data\PD_HD_dicomPhantom\Tomo_DP_target.nrrd');
|
|
|
+% [targetIn, meta] = nrrdread('C:\010-work\003_localGit\WiscPlan_v2\data\archive\CDP_data\CDP5_DP_target.nrrd');
|
|
|
+ rgs_scene_list={[0,0,0]};
|
|
|
+ for i = 1:numel(optGoal)
|
|
|
+ optGoal{i}.NbrRandScenarios =numel(nrs_scene_list);
|
|
|
+ optGoal{i}.NbrSystSetUpScenarios=numel(sss_scene_list);
|
|
|
+ optGoal{i}.NbrRangeScenarios =numel(rgs_scene_list);
|
|
|
+ end
|
|
|
+ for goal_i = 1:numel(optGoal)
|
|
|
+ % get target
|
|
|
+ idx=optGoal{goal_i}.ROI_idx;
|
|
|
+ targetImg1=zeros(optGoal{goal_i}.imgDim);
|
|
|
+ targetImg1(idx)=1;
|
|
|
+ % get beamlets
|
|
|
+ for nrs_i = 1:optGoal{goal_i}.NbrRandScenarios % num. of random scenarios
|
|
|
+ % modify target and beamlets
|
|
|
+ targetImg2=targetImg1;
|
|
|
+ % beamlets stay the same
|
|
|
+ for sss_i = 1 :optGoal{goal_i}.NbrSystSetUpScenarios % syst. setup scenarios = sss
|
|
|
+ % modify target and beamlets
|
|
|
+ [targetImg3 idxValid]=get_RO_sss(targetImg2, sss_scene_list{sss_i});
|
|
|
+ % beamlets stay the same
|
|
|
+ for rgs_i = 1:optGoal{goal_i}.NbrRangeScenarios % range scenario = rgs
|
|
|
+ % modify target and beamlets
|
|
|
+ targetImg4=targetImg3;
|
|
|
+ % beamlets stay the same
|
|
|
+ %% make new target and beamlets
|
|
|
+ ROI_idx=[];
|
|
|
+ ROI_idx=find(targetImg4>0);
|
|
|
+ target = optGoal{goal_i}.D_final(idxValid);
|
|
|
+ beamlets_pruned = beamlets(ROI_idx, :);
|
|
|
+ % save to optGoal output
|
|
|
+ optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.ROI_idx = ROI_idx;
|
|
|
+ optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.beamlets_pruned = beamlets_pruned;
|
|
|
+ optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rgs{rgs_i}.target = target;
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+end
|
|
|
+%% ------ supp: RO case SSS ------
|
|
|
+function [targetImg3 ia]=get_RO_sss(targetImg2, sss_scene_shift);
|
|
|
+ % translate the target image
|
|
|
+
|
|
|
+ targetImg3 = imtranslate(targetImg2,sss_scene_shift);
|
|
|
+
|
|
|
+ % now we need to figure out if any target voxels fell out during the
|
|
|
+ % shift
|
|
|
+ imgValid = imtranslate(targetImg3,-sss_scene_shift);
|
|
|
+ imgInvalid = (targetImg2-imgValid);
|
|
|
+
|
|
|
+ idx_1 = find(targetImg2);
|
|
|
+ idx_2 = find(imgInvalid);
|
|
|
+
|
|
|
+ [idxValid,ia] = setdiff(idx_1,idx_2);
|
|
|
+
|
|
|
+ [C,ia, ib] = intersect(idx_1,idxValid);
|
|
|
+
|
|
|
+end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|