Skip to content

Commit 2bf0f66

Browse files
authored
Merge pull request e0404#758 from Raedlr/refactor/dicomImporter
Refactor dicom importer
2 parents a070165 + 70e3abd commit 2bf0f66

30 files changed

+1460
-1109
lines changed

AUTHORS.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* Emily Heath
2121
* Cindy Hermann
2222
* Noa Homolka
23+
* Raed Ibragim
2324
* Fabian Jäger
2425
* Fernando Hueso-González
2526
* Navid Khaledi

matRad/dicom/@matRad_DicomExporter/matRad_exportDicomCt.m

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,6 @@
6969
nSlices = ct.cubeDim(3);
7070
%Create X Y Z vectors if not present
7171
if ~any(isfield(ct,{'x','y','z'}))
72-
%positionOffset = transpose(ct.cubeDim ./ 2);
73-
%positionOffset = ct.cubeDim .* [ct.resolution.y, ct.resolution.x, ct.resolution.z] ./ 2;
74-
% positionOffset = [ct.resolution.y, ct.resolution.x, ct.resolution.z] ./ 2;
75-
% ct.x = ct.resolution.x*[0:ct.cubeDim(2)-1] - positionOffset(2);
76-
% ct.y = ct.resolution.y*[0:ct.cubeDim(1)-1] - positionOffset(1);
77-
% ct.z = ct.resolution.z*[0:ct.cubeDim(3)-1] - positionOffset(3);
7872
ct = matRad_getWorldAxes(ct);
7973
end
8074

matRad/dicom/@matRad_DicomExporter/matRad_exportDicomRTDoses.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
%Now image meta
8383
resolution = ct.resolution;
8484
meta.PixelSpacing = [resolution.y; resolution.x];
85-
meta.SliceThickness = resolution.z;
85+
meta.SliceThickness = num2str(resolution.z);
8686

8787
meta.ImagePositionPatient = [ct.x(1); ct.y(1); ct.z(1)];
8888
meta.ImageOrientationPatient = [1;0;0;0;1;0];

matRad/dicom/@matRad_DicomExporter/matRad_exportDicomRTStruct.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,9 @@
9696

9797

9898
%Since we are exporting HU directly --> no rescaling in any case
99-
%meta.SliceThickness = ct.resolution.z;
100-
%meta.PixelSpacing = [ct.resolution.y; ct.resolution.x];
101-
%meta.ImageOrientationPatient = [1;0;0;0;1;0]; %lps
99+
meta.SliceThickness = num2str(ct.resolution.z);
100+
meta.PixelSpacing = [ct.resolution.y; ct.resolution.x];
101+
meta.ImageOrientationPatient = [1;0;0;0;1;0]; %lps
102102

103103
%meta.RescaleSlope = 1;
104104
%meta.RescaleIntercept = 0;
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
classdef matRad_DicomImporter < handle
2+
% matRad_DicomImporter matRad class to handle a dicom import.
3+
%
4+
% Example on how to use the matRad_DicomImport class
5+
%
6+
% dcmImpObj = matRad_DicomImporter('pathToFolder'); % create instance of matRad_DicomImporter
7+
% dcmImpObj.matRad_importDicom(dcmImpObj); % run the import
8+
%
9+
%
10+
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11+
%
12+
% Copyright 2020 the matRad development team.
13+
%
14+
% This file is part of the matRad project. It is subject to the license
15+
% terms in the LICENSE file found in the top-level directory of this
16+
% distribution and at https://github.com/e0404/matRad/LICENSE.md. No part
17+
% of the matRad project, including this file, may be copied, modified,
18+
% propagated, or distributed except according to the terms contained in the
19+
% LICENSE file.
20+
%
21+
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22+
properties
23+
24+
% path to DICOM file
25+
patDir;
26+
27+
% lists of all files
28+
allfiles;
29+
patient;
30+
importFiles; % all the names (directories) of files, that will be imported
31+
32+
% properties with data for import functions
33+
importCT;
34+
importRtss;
35+
importRTDose;
36+
37+
% structures for .mat file
38+
ct = [];
39+
cst = [];
40+
stf = [];
41+
pln = [];
42+
resultGUI = [];
43+
44+
ImportGrid;
45+
46+
% bools
47+
dicomMetaBool;
48+
visBool;
49+
50+
51+
52+
end
53+
54+
methods
55+
56+
function obj = matRad_DicomImporter(pathToFolder)
57+
58+
%matRad_DicomImporter Construct an instance of this class
59+
% Can be called with the structures. If no argument is given,
60+
% all structures will be read from the base workspace
61+
62+
obj.patDir = pathToFolder;
63+
matRad_cfg = MatRad_Config.instance();
64+
65+
if matRad_cfg.isOctave
66+
%Octave needs the DICOM package
67+
try
68+
pkg load dicom;
69+
catch
70+
matRad_cfg.dispError('The DICOM export requires the octave-forge package "dicom"!\n');
71+
end
72+
end
73+
74+
obj.patDir = pathToFolder;
75+
76+
obj.matRad_scanDicomImportFolder();
77+
78+
% matRad_DicomImporter imports only one structure, to select
79+
% patients and structures within a single patient the
80+
% matRad_importDicomWidget is used
81+
82+
ctFiles = strcmp(obj.allfiles(:,2),'CT');
83+
rtssFiles = strcmpi(obj.allfiles(:,2),'rtstruct'); %note we can have multiple RT structure sets, matRad will always import the firstit finds
84+
rtPlanFiles = strcmpi(obj.allfiles(:,2),'rtplan');
85+
rtDoseFiles = strcmpi(obj.allfiles(:,2),'rtdose');
86+
87+
obj.importFiles.ct = obj.allfiles(ctFiles,:);%All CT slice filepaths stored in a cell array like {'CTSlice1.dcm','CTSlice2.dcm'};
88+
obj.importFiles.rtss = obj.allfiles(rtssFiles,:);
89+
obj.importFiles.rtplan = obj.allfiles(rtPlanFiles,:);
90+
obj.importFiles.rtdose = obj.allfiles(rtDoseFiles,:);
91+
92+
for i = numel(obj.allfiles(:,1)):-1:1
93+
if strcmp(obj.allfiles(i,2),'CT')
94+
obj.importFiles.resx = obj.allfiles{i,9};
95+
obj.importFiles.resy = obj.allfiles{i,10};
96+
obj.importFiles.resz = obj.allfiles{i,11}; %some CT dicoms do not follow the standard and use SpacingBetweenSlices
97+
break
98+
end
99+
end
100+
101+
obj.importFiles.useImportGrid = false;
102+
103+
104+
end
105+
106+
matRad_importDicom(obj)
107+
108+
obj = matRad_importDicomCt(obj)
109+
110+
obj = matRad_importDicomRTDose(obj)
111+
112+
obj = matRad_importDicomRTPlan(obj)
113+
114+
obj = matRad_importDicomRtss(obj)
115+
116+
obj = matRad_importDicomSteeringPhotons(obj)
117+
118+
obj = matRad_importDicomSteeringParticles(obj)
119+
120+
obj = matRad_scanDicomImportFolder(obj)
121+
122+
obj = matRad_calcHU(obj)
123+
124+
obj = matRad_createCst(obj)
125+
126+
obj = matRad_dummyCst(obj)
127+
128+
% matRad_saveImport(obj);
129+
130+
end
131+
132+
end
133+
Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
function ct = matRad_calcHU(ct)
1+
function obj = matRad_calcHU(obj)
22
% matRad function to calculate Hounsfield units from a dicom ct
33
% that originally uses intensity values
44
%
5-
% call
6-
% ct = matRad_calcHU(ct)
5+
% In your object, there must be a property that contains unprocessed
6+
% dicom ct data which are stored as intensity values (IV)
7+
%
8+
% Output - ct structure with cube with HU
79
%
8-
% input
9-
% ct: unprocessed dicom ct data which are stored as intensity values (IV)
10+
% HU = IV * slope + intercept
1011
%
11-
% HU = IV * slope + intercept
12+
% call
13+
% obj = matRad_calcHU(obj)
1214
%
13-
% output
14-
% ct: ct struct with cube with HU
1515
%
1616
% References
1717
% -
@@ -29,10 +29,10 @@
2929
%
3030
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3131

32-
for i = 1:ct.numOfCtScen
33-
ct.cubeHU{i} = double(ct.cubeIV{i}) * double(ct.dicomInfo.RescaleSlope) + double(ct.dicomInfo.RescaleIntercept);
32+
for i = 1:obj.ct.numOfCtScen
33+
obj.ct.cubeHU{i} = double(obj.ct.cubeIV{i}) * double(obj.ct.dicomInfo.RescaleSlope) + double(obj.ct.dicomInfo.RescaleIntercept);
3434
end
3535

36-
ct = rmfield(ct,'cubeIV');
36+
obj.ct = rmfield(obj.ct,'cubeIV');
3737

3838
end
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
function obj = matRad_createCst(obj)
2+
% matRad function to create a cst struct upon dicom import
3+
%
4+
% In your object, there must be a property that contains matlab structure
5+
% containing information about rt structure set (generated with
6+
% matRad_importDicomRtss and matRad_convRtssContours2Indices)
7+
%
8+
% Output - matRad cst structure
9+
%
10+
% call
11+
% obj = matRad_createCst(obj)
12+
%
13+
%
14+
% References
15+
% -
16+
%
17+
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
18+
%
19+
% Copyright 2015 the matRad development team.
20+
%
21+
% This file is part of the matRad project. It is subject to the license
22+
% terms in the LICENSE file found in the top-level directory of this
23+
% distribution and at https://github.com/e0404/matRad/LICENSE.md. No part
24+
% of the matRad project, including this file, may be copied, modified,
25+
% propagated, or distributed except according to the terms contained in the
26+
% LICENSE file.
27+
%
28+
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
29+
30+
matRad_cfg = MatRad_Config.instance();
31+
32+
nStructures = size(obj.importRtss.structures,2);
33+
obj.cst = cell(nStructures,6);
34+
35+
%Create set of default colors
36+
defaultColors = colorcube(nStructures);
37+
38+
for i = 1:size(obj.importRtss.structures,2)
39+
obj.cst{i,1} = i - 1; % first organ has number 0
40+
obj.cst{i,2} = obj.importRtss.structures(i).structName;
41+
42+
if ~isempty(regexpi(obj.cst{i,2},'tv', 'once')) || ...
43+
~isempty(regexpi(obj.cst{i,2},'target', 'once')) || ...
44+
~isempty(regexpi(obj.cst{i,2},'gtv', 'once')) || ...
45+
~isempty(regexpi(obj.cst{i,2},'ctv', 'once')) || ...
46+
~isempty(regexpi(obj.cst{i,2},'ptv', 'once')) || ...
47+
~isempty(regexpi(obj.cst{i,2},'boost', 'once')) || ...
48+
~isempty(regexpi(obj.cst{i,2},'tumor', 'once'))
49+
50+
obj.cst{i,3} = 'TARGET';
51+
52+
obj.cst{i,5}.Priority = 1;
53+
54+
% default objectives for targets
55+
objective = DoseObjectives.matRad_SquaredDeviation;
56+
objective.penalty = 800;
57+
objective.parameters = {30}; %Default reference Dose
58+
obj.cst{i,6}{1} = struct(objective);
59+
60+
else
61+
62+
obj.cst{i,3} = 'OAR';
63+
64+
obj.cst{i,5}.Priority = 2;
65+
66+
obj.cst{i,6} = []; % define no OAR dummy objcetives
67+
68+
end
69+
70+
obj.cst{i,4}{1} = obj.importRtss.structures(i).indices;
71+
72+
% set default parameter for biological planning
73+
obj.cst{i,5}.alphaX = 0.1;
74+
obj.cst{i,5}.betaX = 0.05;
75+
obj.cst{i,5}.Visible = 1;
76+
if isfield(obj.importRtss.structures(i),'structColor') && ~isempty(obj.importRtss.structures(i).structColor)
77+
obj.cst{i,5}.visibleColor = obj.importRtss.structures(i).structColor' ./ 255;
78+
else
79+
obj.cst{i,5}.visibleColor = defaultColors(i,:);
80+
matRad_cfg.dispInfo('No color information for structure %d "%s". Assigned default color [%f %f %f]\n',i,obj.cst{i,2},defaultColors(i,1),defaultColors(i,2),defaultColors(i,3));
81+
end
82+
end
Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
function cst = matRad_dummyCst(ct)
1+
function obj = matRad_dummyCst(obj)
22
% matRad function to create a dummy cst struct for a ct
3-
%
4-
% call
5-
% cst = matRad_dummyCst(ct)
63
%
7-
% input
8-
% ct: matRad ct struct
4+
% In your object, there should be properties that contain:
5+
% - ct structure;
6+
% - cst structure.
97
%
10-
% output
11-
% cst: matRad cst struct
8+
% Output - matRad cst structure
9+
%
10+
% call
11+
% obj = matRad_dummyCst(obj)
1212
%
1313
% References
1414
% -
@@ -29,21 +29,21 @@
2929
warning('Did not find RTSS. Creating dummy segmentation for matRad.');
3030

3131
% allocate
32-
cst = cell(1,6);
32+
obj.cst = cell(1,6);
3333

3434
% fill
35-
cst{1,1} = 0; % first organ has number 0
36-
cst{1,2} = 'dummyContour';
37-
cst{1,3} = 'OAR';
38-
cst{1,4}{1} = find(ct.cubeHU{1}>0.1);
39-
cst{1,5}.Priority = 1;
35+
obj.cst{1,1} = 0; % first organ has number 0
36+
obj.cst{1,2} = 'dummyContour';
37+
obj.cst{1,3} = 'OAR';
38+
obj.cst{1,4}{1} = find(obj.ct.cubeHU{1}>0.1);
39+
obj.cst{1,5}.Priority = 1;
4040

4141
% set default parameter for biological planning
42-
cst{1,5}.alphaX = 0.1;
43-
cst{1,5}.betaX = 0.05;
44-
cst{1,5}.Visible = 1;
45-
cst{1,5}.visibleColor = [0 0 0];
42+
obj.cst{1,5}.alphaX = 0.1;
43+
obj.cst{1,5}.betaX = 0.05;
44+
obj.cst{1,5}.Visible = 1;
45+
obj.cst{1,5}.visibleColor = [0 0 0];
4646

4747
% define no objcetives
48-
cst{1,6} = [];
48+
obj.cst{1,6} = [];
4949

0 commit comments

Comments
 (0)