-
Notifications
You must be signed in to change notification settings - Fork 199
Pareto navigation #669
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev_varRBErobOpt
Are you sure you want to change the base?
Pareto navigation #669
Changes from 18 commits
fe06707
823794f
4cba6b2
2e806a9
bc7f21d
0faf883
6b8aa22
21347fe
22bb7e6
fed3c63
778b4cc
8cb4398
d134d85
23eee6a
b9009b1
46ec9b3
267d2b8
823f44d
5ddce71
e8c6218
b38a859
f19635f
8004b74
13e4615
ece7b7f
75f7513
c7dc07e
74ae379
4ceb2bf
db5d715
b8df93d
5baac7a
41234fe
94d2d46
a0336fe
130bcc7
f0fd213
2210d7f
e93381b
aa90d6a
c0b9594
7e1653f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| classdef matRad_UIData < handle | ||
| % matRad_UIData implements a class that allows easy storing of | ||
| % variables related to the pareto Navigation | ||
| % | ||
| % References | ||
| % - | ||
| % | ||
| % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | ||
| % | ||
| % Copyright 2020 the matRad development team. | ||
| % | ||
| % This file is part of the matRad project. It is subject to the license | ||
| % terms in the LICENSE file found in the top-level directory of this | ||
| % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part | ||
| % of the matRad project, including this file, may be copied, modified, | ||
| % propagated, or distributed except according to the terms contained in the | ||
| % LICENSE file. | ||
| % | ||
| % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | ||
|
|
||
| properties | ||
| wRef %weight vector of last calculated plan | ||
| fRef %objective function values of last calculated plan | ||
| fIndsAll % stores all objective function values | ||
| fIndsRed % stores only the "reduced points" | ||
| upboundInit | ||
| upboundRed | ||
| upboundSlider | ||
| lowboundSliderInit | ||
| lowboundSlider | ||
| linestyle | ||
| end | ||
|
|
||
| methods | ||
| function obj = matRad_UIData(wRef,fRef,fInds) | ||
| obj.wRef = wRef; | ||
| obj.fRef = fRef; | ||
| obj.fIndsAll = fInds; | ||
| obj.fIndsRed = fInds; | ||
| obj.upboundInit = max(fInds,[],1); | ||
| obj.upboundRed = max(fInds,[],1); | ||
| obj.upboundSlider= max(fInds,[],1); | ||
| obj.lowboundSliderInit = min(fInds,[],1); | ||
| obj.lowboundSlider = min(fInds,[],1); | ||
| obj.linestyle = 2; | ||
| end | ||
|
|
||
| function [sliderLowBound,sliderUpBound] = restrictObjective(obj,i,bound) | ||
| obj.upboundRed(i) = bound; | ||
| obj.fIndsRed = obj.fIndsRed(obj.fIndsRed(:,i) <= bound,:); | ||
| obj.upboundSlider= max([obj.fIndsRed;obj.fRef],[],1); | ||
| obj.upboundSlider(i) = bound; | ||
| obj.lowboundSlider = min([obj.fIndsRed;obj.fRef],[],1); | ||
| sliderLowBound = obj.lowboundSlider; | ||
| sliderUpBound = obj.upboundSlider; | ||
| end | ||
|
|
||
| function releaseObjectiveBounds(obj) | ||
| obj.upboundRed = obj.upboundInit; | ||
| obj.fIndsRed = obj.fIndsAll; | ||
| obj.upboundSlider = obj.upboundInit; | ||
| obj.lowboundSlider = obj.lowboundSliderInit; | ||
| end | ||
|
|
||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,227 @@ | ||
| function matRad_UIInterpolation(data,dij,pln,ct,cst,optiProb) | ||
|
|
||
|
|
||
| fInds = data.finds; | ||
|
|
||
|
|
||
| %sort function values according to each dimension - still necessary? | ||
| [A,I] = sort(fInds(:,1),1,'ascend'); | ||
| fIndsSorted = fInds(I,:); | ||
| weights = data.weights(:,I); | ||
| %idx = round(size(fInds,1)/2); | ||
| idx = 4; | ||
| %% | ||
|
|
||
| slice = round(pln.propStf.isoCenter(1,3)./ct.resolution.z); | ||
| %% | ||
|
|
||
| %% | ||
| f = figure('units','normalized','outerposition',[0 0 1 1]); | ||
| DosePlot = axes('Position',[.1 .5 .4 .4]); | ||
| title('Dose Slice') | ||
| DVHPlot = axes('Position',[.6 .5 .35 .4]); | ||
| title('DVH'); | ||
| % | ||
|
|
||
| %Create uipanels | ||
| p = uipanel(f,'Position',[0.05 0.1 0.3 0.35]); | ||
| p2 = uipanel(f,'Position',[0.35 0.3 0.15 0.15]); | ||
|
|
||
| %extract the names of the objectives and corresponding VOI | ||
| names = {}; | ||
| for i = (1:numel(optiProb.objectives)) | ||
| names{end + 1} = optiProb.objectives{i}.name; | ||
| end | ||
|
|
||
| VOIs = {}; | ||
| for i = 1:size(cst,1) | ||
| if ~isempty(cst{i,6}) | ||
| VOIs{end + 1} = cst{i,2}; | ||
| end | ||
| end | ||
| %Generate reference point (should lie somewhere in the middle of | ||
| %objective 1) | ||
| fRef = fIndsSorted(idx,:); | ||
| wRef = weights(:,idx); | ||
| refObj = matRad_UIData(wRef,fRef,fIndsSorted); | ||
|
|
||
| %initial UI elements | ||
| sliders = {}; | ||
| namesui = {}; | ||
| sliderValues = {}; | ||
| fixButtons = {}; | ||
| VOIui = {}; | ||
|
|
||
| %%Create interactive elements | ||
| for i = 1:size(fInds,2) | ||
| VOIui{i} = uicontrol(p,'Style','text',... | ||
| 'Units','normalized',... | ||
| 'Position',[0.02,0.9-(i-1)*0.13, 0.15,0.1],... | ||
| 'String',VOIs{i}); | ||
|
|
||
| namesui{i} = uicontrol(p,'Style','text',... | ||
| 'Units','normalized',... | ||
| 'Position',[0.17,0.9-(i-1)*0.13, 0.22,0.1],... | ||
| 'String',names{i}); | ||
|
|
||
| sliderValues{i} = uicontrol(p,'Style','text',... | ||
| 'Units','normalized',... | ||
| 'Position',[0.9,0.9-(i-1)*0.13, 0.1,0.05],... | ||
| 'String',fIndsSorted(idx,i)); | ||
|
|
||
| sliders{i} = uicontrol(p,'Style','slider',... | ||
| 'Units','normalized',... | ||
| 'Min',min(fIndsSorted(:,i)),'Max',max(fIndsSorted(:,i)),... | ||
| 'Position',[0.4,0.9-(i-1)*0.13, 0.35,0.05]); | ||
|
|
||
|
|
||
| fixButtons{i} = uicontrol(p,'Style','pushbutton',... | ||
| 'Units','normalized',... | ||
| 'Position',[0.78,0.9-(i-1)*0.13, 0.1,0.05],... | ||
| 'String','Fix'); | ||
|
|
||
| sliders{i}.Value = fIndsSorted(idx,i); | ||
|
|
||
| end | ||
|
|
||
| DVHButton = uicontrol(p2,'Style','pushbutton',... | ||
| 'Units','normalized',... | ||
| 'Position',[0.1 0.6 0.35 0.3],... | ||
| 'String','Show DVH'); | ||
|
|
||
| ExportButton = uicontrol(p2,'Style','pushbutton',... | ||
| 'Units','normalized',... | ||
| 'Position',[0.55 0.6 0.35 0.3],... | ||
| 'String','Export to GUI'); | ||
|
|
||
| ResetConstraintButton = uicontrol(p2,'Style','pushbutton',... | ||
| 'Units','normalized',... | ||
| 'Position',[0.2 0.3 0.6 0.2],... | ||
| 'String','Reset Constraints'); | ||
|
|
||
| ParetoSurfaceButton = uicontrol(p2,'Style','pushbutton',... | ||
| 'Units','normalized',... | ||
| 'Position',[0.2 0.05 0.6 0.2],... | ||
| 'String','Show PS'); | ||
|
|
||
|
|
||
| %%Set callback for buttons | ||
| for i = 1:size(fInds,2) | ||
| set(sliders{i},'Callback',{@slider_callback,sliderValues,sliders,i,weights,dij,slice,DosePlot,refObj,optiProb,data.modcst,cst,pln,ct}) | ||
| end | ||
|
|
||
| for i = 1:numel(fixButtons) | ||
| set(fixButtons{i},'Callback',{@FixButton_callback,refObj,sliders,i}) | ||
| end | ||
|
|
||
| set(DVHButton,'Callback',{@DVHButton_callback,cst,refObj,dij,pln}) | ||
| set(ExportButton,'Callback',{@ExportButton_callback,refObj,dij}) | ||
| set(ResetConstraintButton,'Callback',{@ResetConstraintButton_callback,refObj,sliders}) | ||
| set(ParetoSurfaceButton,'Callback',{@ParetoSurfaceButton_callback,refObj}) | ||
|
|
||
|
|
||
| %initial plot of first point | ||
|
|
||
| cubes = reshape(dij.physicalDose{1}*wRef,dij.doseGrid.dimensions); | ||
|
|
||
| cubes = matRad_interp3(dij.doseGrid.x,dij.doseGrid.y',dij.doseGrid.z, ... | ||
| cubes, ... | ||
| dij.ctGrid.x,dij.ctGrid.y',dij.ctGrid.z,'linear',0); | ||
|
|
||
| matRad_plotSliceWrapper(DosePlot,ct,cst,1,cubes,3,slice); | ||
| %zoom(DosePlot,1.1); | ||
| dvh = matRad_calcDVH(cst,cubes,'cum'); | ||
| matRad_showDVH(dvh,cst,pln); | ||
|
|
||
| end | ||
| % End of main file | ||
| function DVHButton_callback(~,~,cst,refObj,dij,pln) | ||
| %Shows the DVH for the current plan | ||
| resultGUI = matRad_calcCubes(refObj.wRef,dij); | ||
| %dvh = matRad_calcDVH(cst,doseCube,'cum'); | ||
| dvh = matRad_calcDVH(cst,resultGUI.physicalDose,'cum'); | ||
|
|
||
| matRad_showDVH(dvh,cst,pln,refObj.linestyle); | ||
| if refObj.linestyle < 4 | ||
| refObj.linestyle = refObj.linestyle + 1; | ||
| else | ||
| refObj.linestyle = 1; | ||
| end | ||
| % | ||
|
|
||
| end | ||
|
|
||
| function ExportButton_callback(~,~,refObj,dij) | ||
| %Export the current plan to the matRadGUi for better inspection | ||
| resultGUI = matRad_calcCubes(refObj.wRef,dij); | ||
| assignin('base',"resultGUI",resultGUI); | ||
| matRadGUI; | ||
|
|
||
| end | ||
|
|
||
|
|
||
| function FixButton_callback(~,~,refObj,sliders,i) | ||
|
|
||
| [lb,ub] = refObj.restrictObjective(i,sliders{i}.Value); %update refObjects bounds | ||
|
|
||
|
|
||
| for i = 1:numel(sliders) | ||
| set(sliders{i},'Min',lb(i)); | ||
| set(sliders{i},'Max',ub(i)); | ||
| end | ||
| end | ||
|
|
||
|
|
||
| function ResetConstraintButton_callback(~,~,refObj,sliders) | ||
| refObj.releaseObjectiveBounds(); | ||
|
|
||
| for i = 1:numel(sliders) | ||
| set(sliders{i},'Min',refObj.lowboundSliderInit(i)); | ||
| set(sliders{i},'Max',refObj.upboundInit(i)); | ||
| end | ||
| end | ||
|
|
||
| function ParetoSurfaceButton_callback(~,~,refObj) | ||
| %TODO: Should only show up in low dimensions | ||
| matRad_plotPS(refObj); | ||
|
|
||
| end | ||
|
|
||
|
|
||
| % Callback subfunctions to support UI actions | ||
| function slider_callback(slider,~,textFields,sliders,idx,weights,dij,slice,DosePlot,refObj,optiProb,cstMod,cst,pln,ct) | ||
| % Update the text for the moved slider | ||
| %Calculate combination weights | ||
|
|
||
|
|
||
| v = matRad_navigationProblem(refObj.fIndsAll',refObj.fRef,slider.Value,idx,refObj.upboundRed); | ||
| %%need to check actual dimensions | ||
| if numel(v) == 0 | ||
| %navigation algorithm didnt find a new point | ||
| for i = 1:numel(sliders) | ||
| set(sliders{i},'Value',refObj.fRef(i)); | ||
| set(textFields{i},'String',refObj.fRef(i)); | ||
| end | ||
|
|
||
| else | ||
|
|
||
| wnew = weights*v;%new weights | ||
|
|
||
| fNew = matRad_objectiveFunctions(optiProb,wnew,dij,cstMod); | ||
| fNew = optiProb.normalizeObjectives(fNew'); | ||
| refObj.fRef = fNew; | ||
| refObj.wRef = wnew; | ||
|
|
||
| %% should be moved to seperate function | ||
| cubes = matRad_calcFastCubes(wnew,dij,pln); | ||
| %#Update the slider and text values for objective functions | ||
|
|
||
| for i = 1:numel(sliders) | ||
| set(sliders{i},'Value',fNew(i)); | ||
| set(textFields{i},'String',fNew(i)); | ||
| end | ||
|
|
||
| %Plot updated plan | ||
| matRad_plotSliceWrapper(DosePlot,ct,cst,1,cubes,3,slice); | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| function cubes = matRad_calcFastCubes(w,dij,pln) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the speedup you gain by this?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could also alternatively cycle through the projections stored in OptimizationProblem? Are they not fast enough?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First option would calculate too many quantities if we have something like RBExD etc. Second option might be possible, but I thought the backprojections are mainly meant for usage in the optimization problem |
||
| % matRad computation of cube for plan optimization quantity | ||
| % | ||
| % call | ||
| % resultGUI = matRad_calcCubes(w,dij,pln | ||
| % | ||
| % input | ||
| % w: bixel weight vector | ||
| % dij: dose influence matrix | ||
| % pln: matRad pln struct | ||
| % | ||
| % output | ||
| % cubes: Array storing cubes of optimization quantity | ||
| % | ||
| % References | ||
| % - | ||
| % | ||
| % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | ||
| % | ||
| % Copyright 2015 the matRad development team. | ||
| % | ||
| % This file is part of the matRad project. It is subject to the license | ||
| % terms in the LICENSE file found in the top-level directory of this | ||
| % distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part | ||
| % of the matRad project, including this file, may be copied, modified, | ||
| % propagated, or distributed except according to the terms contained in the | ||
| % LICENSE file. | ||
| % | ||
| % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | ||
| switch pln.bioParam.quantityOpt | ||
| case 'physicalDose' | ||
| cubes = reshape(dij.physicalDose{1}*w,dij.doseGrid.dimensions); | ||
| case 'RBExD' | ||
| if isfield(dij,'mAlphaDose') && isfield(dij,'mSqrtBetaDose') | ||
| %TODO | ||
| matRad_cfg.dispError('TODO'); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this not implemented? |
||
| else | ||
| cubes = reshape(dij.physicalDose{1}*w,dij.doseGrid.dimensions)*dij.RBE; | ||
| end | ||
| case 'effect' | ||
| cubes = reshape(full(dij.mAlphaDose{1} * wBeam + (dij.mSqrtBetaDose{1} * wBeam).^2),dij.doseGrid.dimensions); | ||
| end | ||
| cubes = matRad_interp3(dij.doseGrid.x,dij.doseGrid.y',dij.doseGrid.z, ... | ||
| cubes, ... | ||
| dij.ctGrid.x,dij.ctGrid.y',dij.ctGrid.z,'linear',0); | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should follow the Widget mechanism (check the gui folder). Create a widget derived from matRad_Widget here, you may ask @amitantony for help with that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes :D