Skip to content

Commit 1308623

Browse files
Tissue selection from GUI bug fixing (#852)
* Add getTissueParameters function to bioModel class * Update PlanWidget to allow biological tissue definiiton * clean up matRad_bioModel by calling the validate function from the model itself * add biological model tests for getting available tissue parameters * sanitize tissue btn callback * update the tissue table * test tissue selection button --------- Co-authored-by: Niklas Wahl <[email protected]>
1 parent 3b0e262 commit 1308623

File tree

6 files changed

+148
-61
lines changed

6 files changed

+148
-61
lines changed

matRad/bioModels/LQbasedModels/kernelBasedModels/matRad_LQKernelBasedModel.m

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,22 @@
8080

8181
end
8282
end
83+
84+
methods (Static)
85+
86+
87+
function [alphaX, betaX] = getAvailableTissueParameters(pln)
88+
89+
% load machine
90+
machine = matRad_loadMachine(pln);
91+
if isfield(machine.data,'alphaX') && isfield(machine.data,'betaX')
92+
alphaX = machine.data(1).alphaX;
93+
betaX = machine.data(1).betaX;
94+
else
95+
matRad_cfg = MatRad_Config.instance();
96+
matRad_cfg.dispError('The selected biological model requires AlphaX and BetaX to be set in the machine file but none was found.');
97+
end
98+
99+
end
100+
end
83101
end

matRad/bioModels/matRad_BiologicalModel.m

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,13 @@
251251
end
252252
end
253253

254+
function [alphaX, betaX] = getAvailableTissueParameters(pln)
255+
% empty values in standard implementation, needs to be
256+
% overwritten in subclasses
257+
alphaX = [];
258+
betaX = [];
259+
end
260+
254261

255262
end
256263

matRad/bioModels/matRad_bioModel.m

Lines changed: 10 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
1-
function model = matRad_bioModel(sRadiationMode, sModel)
1+
function model = matRad_bioModel(radiationMode, model, providedQuantities)
22
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
33
% matRad_bioModel
44
% This is a helper function to instantiate a matRad_BiologicalModel. This
55
% function currently exists for downwards compatability, as the new
66
% Biological Models will follow a polymorphic software architecture
77
%
88
% call
9-
% matRad_bioModel(sRadiationMode, sModel)
9+
% matRad_bioModel(radiationMode, model)
1010
%
1111
% e.g. pln.bioModel = matRad_bioModel('protons','MCN')
1212
%
1313
% input
14-
% sRadiationMode: radiation modality 'photons' 'protons' 'helium' 'carbon' 'brachy'
14+
% radiationMode: radiation modality 'photons' 'protons' 'helium' 'carbon' 'brachy'
1515
%
16-
% sModel: string to denote which biological model is used
16+
% model: string to denote which biological model is used
1717
% 'none': for photons, protons, carbon 'constRBE': constant RBE for photons and protons
1818
% 'MCN': McNamara-variable RBE model for protons 'WED': Wedenberg-variable RBE model for protons
1919
% 'LEM': Local Effect Model for carbon ions
2020
%
21+
% providedQuantities: optional cell string of provided quantities to
22+
% check if the model can be evaluated
23+
%
2124
% output
2225
% model: instance of a biological model
2326
%
@@ -36,51 +39,10 @@
3639
%
3740
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3841

39-
matRad_cfg = MatRad_Config.instance();
40-
41-
% Look for the correct inputs
42-
p = inputParser;
43-
addRequired(p, 'sRadiationMode', @ischar);
44-
addRequired(p, 'sModel',@ischar);
45-
46-
p.KeepUnmatched = true;
47-
48-
%Check for the available models
49-
mainFolder = fullfile(matRad_cfg.matRadSrcRoot,'bioModels');
50-
userDefinedFolder = fullfile(matRad_cfg.primaryUserFolder, 'bioModels');
51-
52-
if ~exist(userDefinedFolder,"dir")
53-
folders = {mainFolder};
42+
if nargin < 3
43+
model = matRad_BiologicalModel.validate(model,radiationMode);
5444
else
55-
folders = {mainFolder,userDefinedFolder};
56-
end
57-
58-
availableBioModelsClassList = matRad_findSubclasses('matRad_BiologicalModel', 'folders', folders , 'includeSubfolders',true);
59-
modelInfos = matRad_identifyClassesByConstantProperties(availableBioModelsClassList,'model');
60-
modelNames = {modelInfos.model};
61-
62-
if numel(unique({modelInfos.model})) ~= numel(modelInfos)
63-
matRad_cfg.dispError('Multiple biological models with the same name available.');
45+
model = matRad_BiologicalModel.validate(model,radiationMode,providedQuantities);
6446
end
65-
66-
selectedModelIdx = find(strcmp(sModel, modelNames));
67-
68-
% Create first instance of the selected model
69-
if ~isempty(selectedModelIdx)
70-
tmpBioParam = modelInfos(selectedModelIdx).handle();
71-
else
72-
matRad_cfg.dispError('Unrecognized biological model: %s', sModel);
73-
end
74-
75-
% For the time being I do not assigne the model specific parameters, they
76-
% can be assigned by the user later
77-
78-
correctRadiationModality = any(strcmp(tmpBioParam.possibleRadiationModes, sRadiationMode));
79-
80-
if ~correctRadiationModality
81-
matRad_cfg.dispError('Incorrect radiation modality for the required biological model');
82-
end
83-
84-
model = tmpBioParam;
8547

8648
end % end function

matRad/gui/widgets/matRad_PlanWidget.m

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,7 @@ function btnSetTissue_Callback(this, hObject, eventdata)
14681468

14691469
if evalin('base','exist(''cst'')') && evalin('base','exist(''pln'')')
14701470
try
1471+
matRad_cfg = MatRad_Config.instance();
14711472
%parse variables from base-workspace
14721473
cst = evalin('base','cst');
14731474
pln = evalin('base','pln');
@@ -1476,9 +1477,27 @@ function btnSetTissue_Callback(this, hObject, eventdata)
14761477
fileName = [pln.radiationMode '_' pln.machine];
14771478
load(fileName);
14781479

1479-
% check for available cell types characterized by alphaX and betaX
1480-
for i = 1:size(machine.data(1).alphaX,2)
1481-
CellType{i} = [num2str(machine.data(1).alphaX(i)) ' ' num2str(machine.data(1).betaX(i))];
1480+
%biological model
1481+
if isfield(matRad_cfg.defaults.bioModel,pln.radiationMode)
1482+
defaultModel = matRad_cfg.defaults.bioModel.(pln.radiationMode);
1483+
else
1484+
defaultModel = matRad_cfg.defaults.bioModel.fallback;
1485+
end
1486+
if ~isfield(pln,'bioModel')
1487+
pln.bioModel = defaultModel;
1488+
end
1489+
1490+
bioModel = matRad_BiologicalModel.validate(pln.bioModel,pln.radiationMode);
1491+
1492+
[availableAlphaX, availableBetaX] = bioModel.getAvailableTissueParameters(pln);
1493+
1494+
if ~isempty(availableAlphaX) && ~isempty(availableBetaX)
1495+
for i = 1:size(availableAlphaX,2)
1496+
CellType{i} = [num2str(availableAlphaX(i)) ' ' num2str(availableBetaX(i))];
1497+
columnformat = {'char',CellType,'numeric'};
1498+
end
1499+
else
1500+
columnformat = {'char','numeric','numeric'};
14821501
end
14831502

14841503
%fill table data array
@@ -1507,16 +1526,42 @@ function btnSetTissue_Callback(this, hObject, eventdata)
15071526
%set focus
15081527
figure(figTissue);
15091528
else
1510-
figTissue = figure('Name','Set Tissue Parameters','Color',[.5 .5 .5],'NumberTitle','off','OuterPosition',...
1511-
[ceil(ScreenSize(3)/2) 100 Width Height]);
1529+
figTissue = figure('Name','Set Tissue Parameters', ...
1530+
'NumberTitle','off', ...
1531+
'OuterPosition',[ceil(ScreenSize(3)/2) 100 Width Height],...
1532+
'Color',matRad_cfg.gui.backgroundColor);
15121533
end
15131534

15141535
% define the tissue parameter table
15151536
cNames = {'VOI','alphaX betaX','alpha beta ratio'};
1516-
columnformat = {'char',CellType,'numeric'};
1537+
% columnformat = {'char',CellType,'numeric'};
1538+
1539+
%design table colors
1540+
colorMatrix = repmat(matRad_cfg.gui.elementColor,size(data,1),1);
1541+
ix2 = 2:2:size(data,1);
1542+
if ~isempty(ix2)
1543+
shadeColor = rgb2hsv(matRad_cfg.gui.elementColor);
1544+
if shadeColor(3) < 0.5
1545+
shadeColor(3) = shadeColor(3)*1.5+0.1;
1546+
else
1547+
shadeColor(3) = shadeColor(3)*0.5-0.1;
1548+
end
1549+
1550+
colorMatrix(ix2,:) = repmat(hsv2rgb(shadeColor),numel(ix2),1);
1551+
end
1552+
1553+
1554+
% Create the uitable
1555+
tissueTable = uitable('Parent', figTissue, ...
1556+
'Data', data, ...
1557+
'ColumnEditable',[false true false],...
1558+
'ColumnName',cNames, ...
1559+
'ColumnFormat',columnformat, ...
1560+
'Position',[50 150 10 10], ...
1561+
'ForegroundColor',matRad_cfg.gui.textColor,...
1562+
'BackgroundColor',colorMatrix,...
1563+
'RowStriping','on');
15171564

1518-
tissueTable = uitable('Parent', figTissue,'Data', data,'ColumnEditable',[false true false],...
1519-
'ColumnName',cNames, 'ColumnFormat',columnformat,'Position',[50 150 10 10]);
15201565
set(tissueTable,'CellEditCallback',@(hObject,eventdata) tissueTable_CellEditCallback(this,hObject,eventdata));
15211566
% set width and height
15221567
currTablePos = get(tissueTable,'Position');
@@ -1525,14 +1570,27 @@ function btnSetTissue_Callback(this, hObject, eventdata)
15251570
currTablePos(4) = currTableExt(4);
15261571
set(tissueTable,'Position',currTablePos);
15271572

1573+
themeParams = {'BackgroundColor', matRad_cfg.gui.backgroundColor,...
1574+
'ForegroundColor',matRad_cfg.gui.textColor,...
1575+
'FontSize',matRad_cfg.gui.fontSize,...
1576+
'FontName',matRad_cfg.gui.fontName,...
1577+
'FontWeight',matRad_cfg.gui.fontWeight};
1578+
15281579
% define two buttons with callbacks
1529-
uicontrol('Parent', figTissue,'Style', 'pushbutton', 'String', 'Save&Close',...
1580+
uicontrol('Parent', figTissue, ...
1581+
'Style', 'pushbutton', ...
1582+
'String', 'Save&Close',...
15301583
'Position', [Width-(0.25*Width) 0.1 * Height 70 30],...
1531-
'Callback', @(hpb,eventdata)SaveTissueParameters(this,hpb,eventdata));
1584+
'Callback', @(hpb,eventdata)SaveTissueParameters(this,hpb,eventdata),...
1585+
themeParams{:});
15321586

1533-
uicontrol('Parent', figTissue,'Style', 'pushbutton', 'String', 'Cancel&Close',...
1587+
uicontrol('Parent', ...
1588+
figTissue,'Style', ...
1589+
'pushbutton', ...
1590+
'String', 'Cancel&Close',...
15341591
'Position', [Width-(0.5*Width) 0.1 * Height 80 30],...
1535-
'Callback', 'close');
1592+
'Callback', 'close', ...
1593+
themeParams{:});
15361594
catch ME
15371595
this.showWarning('Could not set Tissue parameter update! Reason: %s\n',ME.message)
15381596
end

test/bioModel/test_biologicalModel.m

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,39 @@
2323
if moxunit_util_platform_is_octave()
2424
assertExceptionThrown(@(model) matRad_bioModel('photons', 'MCN'));
2525
assertExceptionThrown(@(model) matRad_bioModel('protons', 'HEL'));
26-
2726
else
2827
assertExceptionThrown(@(model) matRad_bioModel('photons', 'MCN'), 'matRad:Error');
2928
assertExceptionThrown(@(model) matRad_bioModel('protons', 'HEL'),'matRad:Error');
3029
end
30+
31+
function test_setBiologicalModelProvidedQuantities
32+
bioModel = matRad_bioModel('protons', 'MCN', {'physicalDose','LET'});
33+
assertTrue(isa(bioModel, 'matRad_MCNamara'));
34+
35+
if moxunit_util_platform_is_octave()
36+
assertExceptionThrown(@(model) matRad_bioModel('protons', 'MCN', {'physicalDose'}));
37+
else
38+
assertExceptionThrown(@(model) matRad_bioModel('photons', 'MCN', {'physicalDose'}), 'matRad:Error');
39+
end
40+
41+
function test_tissueParameters_emptyModel
42+
bioModel = matRad_EmptyBiologicalModel();
43+
abx = bioModel.getAvailableTissueParameters(struct());
44+
assertTrue(isempty(abx));
45+
46+
function test_tissueParameters_kernelModel
47+
bioModel = matRad_KernelBasedLEM();
48+
abx = bioModel.getAvailableTissueParameters(struct('machine','Generic','radiationMode','carbon'));
49+
assertTrue(isnumeric(abx));
50+
assertEqual(size(abx,2),2);
51+
assertTrue(size(abx,1) >= 1);
3152

53+
if moxunit_util_platform_is_octave()
54+
assertExceptionThrown(@() bioModel.getAvailableTissueParameters(struct('machine','Generic','radiationMode','photons')));
55+
else
56+
assertExceptionThrown(@() bioModel.getAvailableTissueParameters(struct('machine','Generic','radiationMode','photons')), 'matRad:Error');
57+
end
58+
3259

3360
function test_calcBiologicalQuantitiesForBixel_MCN
3461
bioModel = matRad_bioModel('protons','MCN');

test/gui/test_gui_PlanWidget.m

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,21 @@
138138
evalin('base','clear ct cst pln stf dij resultGUI');
139139
delete(h);
140140

141+
function test_PlanWidget_tissuetable
142+
evalin('base','load carbon_testData.mat');
143+
144+
%Modify to have multiple isocenters
145+
h = matRad_PlanWidget();
146+
147+
cb = get(h.handles.btnSetTissue,'Callback');
148+
cb(h.handles.btnSetTissue,[]);
149+
150+
figHandles = get(0,'Children');
151+
assertTrue(strcmp(get(figHandles,'Name'),'Set Tissue Parameters'));
152+
153+
close(figHandles);
154+
delete(h);
155+
141156

142157

143158
%TODO: Test Buttons

0 commit comments

Comments
 (0)