123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 |
- function [D_full, w_fin] = NLP_beamlet_optimizer
- % This function performs the beamlet optimization
- % Inputs: none. Still needs to find "Geometry" and "beamlets" from Wiscplan
- % Outputs: full dose image dose: D_full, optimal beamlet weights: w_fin
- %
- % Made by Peter Ferjancic 1. May 2018
- % Last updated: 1. May 2018
- % Inspired by Ana Barrigan's REGGUI optimization procedures
- % To-do:
- % - Add robusness aspect (+take worst case scenario, see REGGUI)
- N_fcallback1 = 10000;
- N_fcallback2 = 50000;
- patient = 'phantom';
- switch patient
- case 'phantom'
- patient_dir = 'C:\010-work\003_localGit\WiscPlan_v2\data\PatientData';
- blet_in_beam=5; % this is the number of beamlets in a beam. Called "Mxp" in helicalDosecalcSetup
- case 'phantom_HD'
- patient_dir = 'C:\010-work\003_localGit\WiscPlan_v2\data\PD_HD_dicomPhantom';
- blet_in_beam=20; % ???
- case 'doggo'
- patient_dir = 'C:\010-work\003_localGit\WiscPlan_v2\data\PatientData_dog5_2';
- blet_in_beam=5; % ???
- otherwise
- error('invalid case')
- end
- %% PROGRAM STARTS HERE
- % - no tocar lo que hay debajo -
- fprintf('starting NLP optimization process... ')
- % -- LOAD GEOMETRY AND BEAMLETS --
- load([patient_dir '\matlab_files\Geometry.mat'])
- % beamlet_batch_filename = [patient_dir '\beamlet_batch_files\' 'beamletbatch0.bin'];
- beamlet_batch_filename = [patient_dir '\' 'batch_dose.bin'];
- beamlet_cell_array = read_ryan_beamlets(beamlet_batch_filename, 'ryan');
- fprintf('\n loaded beamlets...')
- % -- SET INITIAL PARAMETERS --
- numVox = numel(Geometry.data);
- numBeamlet = size(beamlet_cell_array,2);
- %% -- BEAMLET DOSE DELIVERY --
- beamlets = get_beamlets(beamlet_cell_array, numVox);
- % show_joint_beamlets(beamlets, size(Geometry.data), 7:9)
- fprintf('\n beamlet array constructed...')
- % - merge beamlets into beams -
- load([patient_dir '\all_beams.mat'])
- beamletOrigin=[0 0 0];
- beam_i=0;
- beam_i_list=[];
- for beamlet_i = 1:numel(all_beams)
- newBeamletOrigin = all_beams{beamlet_i}.ip;
- if any(newBeamletOrigin ~= beamletOrigin)
- beam_i = beam_i+1;
- beamletOrigin = newBeamletOrigin;
- end
- beam_i_list=[beam_i_list, beam_i];
- end
- beamlets_joined=[];
- target_joined=[];
- wbar2 = waitbar(0, 'merging beamlets into beams');
- numBeam=numel(unique(beam_i_list));
- for beam_i = unique(beam_i_list)
- beam_full = sum(beamlets(:,beam_i_list == beam_i), 2);
- beamlets_joined(:,end+1) = beam_full;
- waitbar(beam_i/numBeam, wbar2)
- end
- close(wbar2)
- %% -- OPTIMIZATION TARGETS --
- switch patient
- case 'phantom'
- optGoal = make_ROI_goals(Geometry, beamlets);
- optGoal_beam = make_ROI_goals(Geometry, beamlets_joined);
- N_beamlets_in_beam = 10;
- case 'phantom_HD'
- optGoal = make_ROI_goals(Geometry, beamlets);
- optGoal_beam = make_ROI_goals(Geometry, beamlets_joined);
- case 'doggo'
- optGoal = make_ROI_goals_DOG(Geometry, beamlets);
- optGoal_beam = make_ROI_goals_DOG(Geometry, beamlets_joined);
- otherwise
- error('invalid case')
- end
- % -- make them robust --
- RO_params=0;
- optGoal_beam = make_robust_optGoal(optGoal_beam, RO_params, beamlets_joined);
- optGoal = make_robust_optGoal(optGoal, RO_params, beamlets);
- %% -- INITIALIZE BEAMLET WEIGHTS --
- w0_beams = ones(numBeam,1);
- w0_beams = mean(optGoal_beam{1}.target ./ (optGoal_beam{1}.beamlets_pruned*w0_beams+0.1)) * w0_beams;
- w0_beams = w0_beams + (2*rand(size(w0_beams))-1) *0.1 .*w0_beams; % random perturbation
- % -- CALLBACK OPTIMIZATION FUNCTION --
- fun1 = @(x) get_penalty(x, optGoal_beam);
- fun2 = @(x) get_penalty(x, optGoal);
- % -- OPTIMIZATION PARAMETERS --
- % define optimization parameters
- A = [];
- b = [];
- Aeq = [];
- beq = [];
- lb = zeros(1, numBeamlet);
- lb_beam = zeros(1, numBeamlet);
- ub = [];
- nonlcon = [];
- % define opt limits, and make it fmincon progress
- options = optimoptions('fmincon');
- options.MaxFunctionEvaluations = N_fcallback1;
- options.Display = 'iter';
- options.PlotFcn = 'optimplotfval';
- fprintf('\n running initial optimizer:')
- %% Run the optimization
- % -- GET FULL BEAM WEIGHTS --
- tic
- w_beam = fmincon(fun1,w0_beams,A,b,Aeq,beq,lb_beam,ub,nonlcon,options);
- 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
- w_beamlets = w_beamlets + (2*rand(size(w_beamlets))-1) *0.1 .*w_beamlets; % small random perturbation
- % -- GET FULL BEAMLET WEIGHTS --
- options.MaxFunctionEvaluations = N_fcallback2;
- tic
- w_fin = fmincon(fun2,w_beamlets,A,b,Aeq,beq,lb,ub,nonlcon,options);
- 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;
- % save('C:\010-work\003_localGit\WiscPlan_v2\data\PatientData\matlab_files\NLP_result.mat', 'NLP_result');
- optGoal_idx=[1,3];
- plot_DVH(D_full, optGoal, optGoal_idx)
- % plot_DVH_robust(D_full, optGoal, optGoal_idx)
- end
- % orthoslice(D_full, [0,70])
- %% support functions
- % ---- PENALTY FUNCTION ----
- function penalty = get_penalty(x, optGoal)
- % 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,1);
- sc_i = 1;
-
- for nrs_i = 1:optGoal{1}.NbrRandScenarios
- for sss_i = 1 :optGoal{1}.NbrSystSetUpScenarios % syst. setup scenarios = ss
- for rrs_i = 1:optGoal{1}.NbrRangeScenarios % range scenario = rs
- fobj(sc_i)=eval_f(x, optGoal, nrs_i, sss_i, rrs_i);
- sc_i = sc_i + 1;
- end
- end
- end
- % take the worst case penalty of evaluated scenarios
- penalty=max(fobj);
- end
- % ------ supp: penalty for single scenario ------
- function penalty = eval_f(x, optGoal, nrs_i, sss_i, rrs_i)
- penalty = 0;
- % for each condition
- for goal_i = 1:numel(optGoal)
- switch optGoal{goal_i}.function
- 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}.rrs{rrs_i}.target) -...
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_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}.rrs{rrs_i}.beamlets_pruned * x)-...
- (optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.target)));
- case 'LeastSquare'
- % penalize with sum of squares any deviation from target
- % dose
- d_penalty = 1.0e-1* sum(((...
- optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.beamlets_pruned * x) - ...
- optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.target).^2);
- end
- penalty = penalty + d_penalty * optGoal{goal_i}.opt_weight;
- end
- end
- % ---- GET BEAMLETS ----
- function beamlets = get_beamlets(beamlet_cell_array, numVox);
- wbar1 = waitbar(0, 'Creating beamlet array');
- numBeam = size(beamlet_cell_array,2);
- batchSize=100;
- beamlets = sparse(0, 0);
- for beam_i=1:numBeam
- % for each beam define how much dose it delivers on each voxel
- idx=beamlet_cell_array{1, beam_i}.non_zero_indices;
- % break the beamlets into multiple batches
- if rem(beam_i, batchSize)==1;
- beamlet_batch = sparse(numVox, batchSize);
- beam_i_temp=1;
- end
- beamlet_batch(idx, beam_i_temp) = 1000*beamlet_cell_array{1, beam_i}.non_zero_values;
- waitbar(beam_i/numBeam, wbar1, ['Adding beamlet array: #', num2str(beam_i)])
- % add the batch to full set when filled
- if rem(beam_i, batchSize)==0;
- beamlets =[beamlets, beamlet_batch];
- end
- % crop and add the batch to full set when completed
- if beam_i==numBeam;
- beamlet_batch=beamlet_batch(:, 1:beam_i_temp);
- beamlets =[beamlets, beamlet_batch];
- end
- beam_i_temp=beam_i_temp+1;
- end
- close(wbar1)
- end
- function show_joint_beamlets(beamlets, IMGsize, Beam_list)
- % this function overlays and plots multiple beamlets. The goal is to
- % check whether some beamlets are part of the same beam manually.
-
- beam=sum(beamlets(:, Beam_list), 2);
-
- beamImg=reshape(full(beam), IMGsize);
-
- orthoslice(beamImg)
-
- end
- % ---- MAKE ROI GOALS ----
- function optGoal = make_ROI_goals(Geometry, beamlets)
- optGoal={};
-
- % -- START DEFINITION OF GOAL --
- goal_1.name = 'Target_min';
- goal_1.ROI_name = Geometry.ROIS{1, 2}.name;
- ROI_idx = Geometry.ROIS{1, 2}.ind;
- goal_1.ROI_idx = ROI_idx;
- goal_1.imgDim = size(Geometry.data);
- goal_1.D_final = 60;
- goal_1.function = 'min';
- goal_1.beamlets_pruned = beamlets(ROI_idx, :);
- goal_1.target = ones(numel(ROI_idx), 1) * goal_1.D_final;
- goal_1.opt_weight = 1 / numel(ROI_idx); % normalize to volume of target area
- goal_1.dvh_col = [0.9, 0.2, 0.2]; % color of the final DVH plot
- % assign target
- optGoal{end+1}=goal_1;
- % -- END DEFINITION OF GOAL --
-
- % -- START DEFINITION OF GOAL --
- goal_2.name = 'Target_max';
- goal_2.ROI_name = Geometry.ROIS{1, 2}.name;
- ROI_idx = Geometry.ROIS{1, 2}.ind;
- goal_2.ROI_idx = ROI_idx;
- goal_2.imgDim = size(Geometry.data);
- goal_2.D_final = 65;
- goal_2.function = 'max';
- goal_2.beamlets_pruned = beamlets(ROI_idx, :);
- goal_2.target = ones(numel(ROI_idx), 1) * goal_2.D_final;
- goal_2.opt_weight = 0.8 / numel(ROI_idx); % normalize to volume of target area
- goal_2.dvh_col = [0.9, 0.2, 0.2]; % color of the final DVH plot
- % assign target
- optGoal{end+1}=goal_2;
- % -- END DEFINITION OF GOAL --
-
- % -- START DEFINITION OF GOAL --
- goal_3.name = 'ROI 1_max';
- goal_3.ROI_name = Geometry.ROIS{1, 3}.name;
- ROI_idx = Geometry.ROIS{1, 3}.ind;
- goal_3.ROI_idx = ROI_idx;
- goal_3.imgDim = size(Geometry.data);
- goal_3.D_final = 10;
- goal_3.function = 'max';
- goal_3.beamlets_pruned = beamlets(ROI_idx, :);
- goal_3.target = ones(numel(ROI_idx), 1) * goal_3.D_final;
- goal_3.opt_weight = 2 / numel(ROI_idx); % normalize to volume of target area
- goal_3.dvh_col = [0.2, 0.9, 0.2]; % color of the final DVH plot
- % assign target
- optGoal{end+1}=goal_3;
- % -- END DEFINITION OF GOAL --
-
- end
- function optGoal = make_ROI_goals_DOG(Geometry, beamlets)
- optGoal={};
-
- % -- START DEFINITION OF GOAL --
- goal_1.name = 'Target_min';
- goal_1.ROI_name = Geometry.ROIS{1, 1}.name;
- ROI_idx = Geometry.ROIS{1, 1}.ind;
- goal_1.ROI_idx = ROI_idx;
- goal_1.imgDim = size(Geometry.data);
- goal_1.D_final = 60;
- goal_1.function = 'min';
- goal_1.beamlets_pruned = beamlets(ROI_idx, :);
- goal_1.target = ones(numel(ROI_idx), 1) * goal_1.D_final;
- goal_1.opt_weight = 1 / numel(ROI_idx); % normalize to volume of target area
- goal_1.dvh_col = [0.9, 0.2, 0.2]; % color of the final DVH plot
- % assign target
- optGoal{end+1}=goal_1;
- % -- END DEFINITION OF GOAL --
-
- % -- START DEFINITION OF GOAL --
- goal_2.name = 'Doggo_max';
- goal_2.ROI_name = Geometry.ROIS{1, 4}.name;
- ROI_idx = Geometry.ROIS{1, 1}.ind;
- goal_2.ROI_idx = ROI_idx;
- goal_2.imgDim = size(Geometry.data);
- goal_2.D_final = 20;
- goal_2.function = 'max';
- goal_2.beamlets_pruned = beamlets(ROI_idx, :);
- goal_2.target = ones(numel(ROI_idx), 1) * goal_2.D_final;
- goal_2.opt_weight = 0.2 / numel(ROI_idx); % normalize to volume of target area
- goal_2.dvh_col = [0.9, 0.2, 0.2]; % color of the final DVH plot
- % assign target
- optGoal{end+1}=goal_2;
- % -- END DEFINITION OF GOAL --
-
- 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
- % rrs - random range scenarios
-
- % X - X>0 moves image right
- % Y - Y>0 moves image down
- % Z - in/out.
- nrs_scene_list={[0,0,0]};
- sss_scene_list={[0,0,0]};
- rrs_scene_list={[0,0,0]};
-
- % nrs_scene_list={[0,0,0]};
- sss_scene_list={[0,0,0], [-15,0,0], [-10,0,0], [-5,0,0], [5,0,0], [10,0,0], [15,0,0]};
- % rrs_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(rrs_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=get_RO_sss(targetImg2, sss_scene_list{sss_i});
- % beamlets stay the same
-
- for rrs_i = 1:optGoal{goal_i}.NbrRangeScenarios % range scenario = rrs
- % modify target and beamlets
- targetImg4=targetImg3;
- % beamlets stay the same
-
- %% make new target and beamlets
- ROI_idx=[];
- ROI_idx=find(targetImg4>0);
- target = ones(numel(ROI_idx), 1) * optGoal{goal_i}.D_final;
- beamlets_pruned = beamlets(ROI_idx, :);
-
- % save to optGoal output
- optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.ROI_idx = ROI_idx;
- optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.beamlets_pruned = beamlets_pruned;
- optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.target = target;
- end
- end
- end
- end
- end
- % ------ supp: RO case SSS ------
- function targetImg3=get_RO_sss(targetImg2, sss_scene_shift);
- % translate the target image
- targetImg3 = imtranslate(targetImg2,sss_scene_shift);
- end
- % ---- DVH PLOT FUNCTION ----
- function plot_DVH(dose, optGoal, optGoal_idx)
- % this function plots the DVHs of the given dose
- nfrac=1;
-
- figure
- hold on
- for roi_idx=optGoal_idx
- % plot the histogram
-
- [dvh dosebins] = get_dvh_data(dose,optGoal{roi_idx}.ROI_idx,nfrac);
- plot(dosebins, dvh,'Color', optGoal{roi_idx}.dvh_col,'LineStyle', '-','DisplayName', optGoal{roi_idx}.ROI_name);
- end
- hold off
-
- end
- function plot_DVH_robust(dose, optGoal, optGoal_idx)
- % this function plots the DVHs of the given dose
- nfrac=1;
-
- figure
- hold on
- for goal_i=optGoal_idx
- % for all the perturbations
- for nrs_i = 1:optGoal{1}.NbrRandScenarios
- for sss_i = 1:optGoal{1}.NbrSystSetUpScenarios % syst. setup scenarios = ss
- for rrs_i = 1:optGoal{1}.NbrRangeScenarios % range scenario = rs
- % plot the histogram
- ROI_idx = optGoal{goal_i}.nrs{nrs_i}.sss{sss_i}.rrs{rrs_i}.ROI_idx;
-
- [dvh dosebins] = get_dvh_data(dose,ROI_idx,nfrac);
- plot(dosebins, dvh,'Color', optGoal{goal_i}.dvh_col,'LineStyle', '-','DisplayName', optGoal{goal_i}.ROI_name);
- end
- end
- end
- end
- hold off
-
- end
- function [dvh dosebins] = get_dvh_data(dose,roi_idx,nfrac);
- % this function calculates the data for the DVH
- dosevec = dose(roi_idx);
-
-
- [pdf dosebins] = hist(dosevec, 999);
- % clip negative bins
- pdf = pdf(dosebins >= 0);
- dosebins = dosebins(dosebins >= 0);
- dvh = fliplr(cumsum(fliplr(pdf))) / numel(dosevec) * 100;
- % append the last bin
- dosebins = [dosebins dosebins(end)+0.1];
- dvh = [dvh 0];
-
- end
|