comparison OrthancStone/Sources/Toolbox/DicomStructureSet2.cpp @ 1512:244ad1e4e76a

reorganization of folders
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 07 Jul 2020 16:21:02 +0200
parents Framework/Toolbox/DicomStructureSet2.cpp@30deba7bc8e2
children 4fb8fdf03314
comparison
equal deleted inserted replaced
1511:9dfeee74c1e6 1512:244ad1e4e76a
1 /**
2 * Stone of Orthanc
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Affero General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/
20
21 #ifdef BGO_ENABLE_DICOMSTRUCTURESETLOADER2
22
23 #include "DicomStructureSet2.h"
24
25 #include "../Toolbox/LinearAlgebra.h"
26 #include "../StoneException.h"
27
28 #include <Logging.h>
29 #include <OrthancException.h>
30 #include <Toolbox.h>
31 #include <DicomFormat/DicomTag.h>
32
33 #include <FullOrthancDataset.h>
34 #include <DicomDatasetReader.h>
35
36 namespace OrthancStone
37 {
38 static const OrthancPlugins::DicomTag DICOM_TAG_CONTOUR_GEOMETRIC_TYPE(0x3006, 0x0042);
39 static const OrthancPlugins::DicomTag DICOM_TAG_CONTOUR_IMAGE_SEQUENCE(0x3006, 0x0016);
40 static const OrthancPlugins::DicomTag DICOM_TAG_CONTOUR_SEQUENCE(0x3006, 0x0040);
41 static const OrthancPlugins::DicomTag DICOM_TAG_CONTOUR_DATA(0x3006, 0x0050);
42 static const OrthancPlugins::DicomTag DICOM_TAG_NUMBER_OF_CONTOUR_POINTS(0x3006, 0x0046);
43 static const OrthancPlugins::DicomTag DICOM_TAG_REFERENCED_SOP_INSTANCE_UID(0x0008, 0x1155);
44 static const OrthancPlugins::DicomTag DICOM_TAG_ROI_CONTOUR_SEQUENCE(0x3006, 0x0039);
45 static const OrthancPlugins::DicomTag DICOM_TAG_ROI_DISPLAY_COLOR(0x3006, 0x002a);
46 static const OrthancPlugins::DicomTag DICOM_TAG_ROI_NAME(0x3006, 0x0026);
47 static const OrthancPlugins::DicomTag DICOM_TAG_RT_ROI_INTERPRETED_TYPE(0x3006, 0x00a4);
48 static const OrthancPlugins::DicomTag DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE(0x3006, 0x0080);
49 static const OrthancPlugins::DicomTag DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE(0x3006, 0x0020);
50
51 static inline uint8_t ConvertAndClipToByte(double v)
52 {
53 if (v < 0)
54 {
55 return 0;
56 }
57 else if (v >= 255)
58 {
59 return 255;
60 }
61 else
62 {
63 return static_cast<uint8_t>(v);
64 }
65 }
66
67 static bool ReadDicomToVector(Vector& target,
68 const OrthancPlugins::IDicomDataset& dataset,
69 const OrthancPlugins::DicomPath& tag)
70 {
71 std::string value;
72 return (dataset.GetStringValue(value, tag) &&
73 LinearAlgebra::ParseVector(target, value));
74 }
75
76
77 void DicomPathToString(std::string& s, const OrthancPlugins::DicomPath& dicomPath)
78 {
79 std::stringstream tmp;
80 for (size_t i = 0; i < dicomPath.GetPrefixLength(); ++i)
81 {
82 OrthancPlugins::DicomTag tag = dicomPath.GetPrefixTag(i);
83
84 // We use this other object to be able to use GetMainTagsName
85 // and Format
86 Orthanc::DicomTag tag2(tag.GetGroup(), tag.GetElement());
87 size_t index = dicomPath.GetPrefixIndex(i);
88 tmp << tag2.GetMainTagsName() << " (" << tag2.Format() << ") [" << index << "] / ";
89 }
90 const OrthancPlugins::DicomTag& tag = dicomPath.GetFinalTag();
91 Orthanc::DicomTag tag2(tag.GetGroup(), tag.GetElement());
92 tmp << tag2.GetMainTagsName() << " (" << tag2.Format() << ")";
93 s = tmp.str();
94 }
95
96 std::ostream& operator<<(std::ostream& s, const OrthancPlugins::DicomPath& dicomPath)
97 {
98 std::string tmp;
99 DicomPathToString(tmp, dicomPath);
100 s << tmp;
101 return s;
102 }
103
104
105 DicomStructureSet2::DicomStructureSet2()
106 {
107
108 }
109
110
111 DicomStructureSet2::~DicomStructureSet2()
112 {
113
114 }
115
116 void DicomStructureSet2::SetContents(const OrthancPlugins::FullOrthancDataset& tags)
117 {
118 FillStructuresFromDataset(tags);
119 ComputeDependentProperties();
120 }
121
122 void DicomStructureSet2::ComputeDependentProperties()
123 {
124 for (size_t i = 0; i < structures_.size(); ++i)
125 {
126 structures_[i].ComputeDependentProperties();
127 }
128 }
129
130 void DicomStructureSet2::FillStructuresFromDataset(const OrthancPlugins::FullOrthancDataset& tags)
131 {
132 OrthancPlugins::DicomDatasetReader reader(tags);
133
134 // a few sanity checks
135 size_t count = 0, tmp = 0;
136
137 // DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE (0x3006, 0x0080);
138 // DICOM_TAG_ROI_CONTOUR_SEQUENCE (0x3006, 0x0039);
139 // DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE (0x3006, 0x0020);
140 if (!tags.GetSequenceSize(count, DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE) ||
141 !tags.GetSequenceSize(tmp, DICOM_TAG_ROI_CONTOUR_SEQUENCE) ||
142 tmp != count ||
143 !tags.GetSequenceSize(tmp, DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE) ||
144 tmp != count)
145 {
146 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
147 }
148
149 // let's now parse the structures stored in the dicom file
150 // DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE (0x3006, 0x0080)
151 // DICOM_TAG_RT_ROI_INTERPRETED_TYPE (0x3006, 0x00a4)
152 // DICOM_TAG_ROI_DISPLAY_COLOR (0x3006, 0x002a)
153 // DICOM_TAG_ROI_NAME (0x3006, 0x0026)
154 structures_.resize(count);
155 for (size_t i = 0; i < count; i++)
156 {
157 // (0x3006, 0x0080)[i]/(0x3006, 0x00a4)
158 structures_[i].interpretation_ = reader.GetStringValue
159 (OrthancPlugins::DicomPath(DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE, i,
160 DICOM_TAG_RT_ROI_INTERPRETED_TYPE),
161 "No interpretation");
162
163 // (0x3006, 0x0020)[i]/(0x3006, 0x0026)
164 structures_[i].name_ = reader.GetStringValue
165 (OrthancPlugins::DicomPath(DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE, i,
166 DICOM_TAG_ROI_NAME),
167 "No name");
168
169 Vector color;
170 // (0x3006, 0x0039)[i]/(0x3006, 0x002a)
171 if (ReadDicomToVector(color, tags, OrthancPlugins::DicomPath(
172 DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, DICOM_TAG_ROI_DISPLAY_COLOR))
173 && color.size() == 3)
174 {
175 structures_[i].red_ = ConvertAndClipToByte(color[0]);
176 structures_[i].green_ = ConvertAndClipToByte(color[1]);
177 structures_[i].blue_ = ConvertAndClipToByte(color[2]);
178 }
179 else
180 {
181 structures_[i].red_ = 255;
182 structures_[i].green_ = 0;
183 structures_[i].blue_ = 0;
184 }
185
186 size_t countSlices;
187 // DICOM_TAG_ROI_CONTOUR_SEQUENCE (0x3006, 0x0039);
188 // DICOM_TAG_CONTOUR_SEQUENCE (0x3006, 0x0040);
189 if (!tags.GetSequenceSize(countSlices, OrthancPlugins::DicomPath(
190 DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, DICOM_TAG_CONTOUR_SEQUENCE)))
191 {
192 LOG(WARNING) << "DicomStructureSet2::SetContents | structure \"" << structures_[i].name_ << "\" has no slices!";
193 countSlices = 0;
194 }
195
196 LOG(INFO) << "New RT structure: \"" << structures_[i].name_
197 << "\" with interpretation \"" << structures_[i].interpretation_
198 << "\" containing " << countSlices << " slices (color: "
199 << static_cast<int>(structures_[i].red_) << ","
200 << static_cast<int>(structures_[i].green_) << ","
201 << static_cast<int>(structures_[i].blue_) << ")";
202
203 // These temporary variables avoid allocating many vectors in the loop below
204
205 // (0x3006, 0x0039)[i]/(0x3006, 0x0040)[0]/(0x3006, 0x0046)
206 OrthancPlugins::DicomPath countPointsPath(
207 DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
208 DICOM_TAG_CONTOUR_SEQUENCE, 0,
209 DICOM_TAG_NUMBER_OF_CONTOUR_POINTS);
210
211 OrthancPlugins::DicomPath geometricTypePath(
212 DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
213 DICOM_TAG_CONTOUR_SEQUENCE, 0,
214 DICOM_TAG_CONTOUR_GEOMETRIC_TYPE);
215
216 OrthancPlugins::DicomPath imageSequencePath(
217 DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
218 DICOM_TAG_CONTOUR_SEQUENCE, 0,
219 DICOM_TAG_CONTOUR_IMAGE_SEQUENCE);
220
221 // (3006,0039)[i] / (0x3006, 0x0040)[0] / (0x3006, 0x0016)[0] / (0x0008, 0x1155)
222 OrthancPlugins::DicomPath referencedInstancePath(
223 DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
224 DICOM_TAG_CONTOUR_SEQUENCE, 0,
225 DICOM_TAG_CONTOUR_IMAGE_SEQUENCE, 0,
226 DICOM_TAG_REFERENCED_SOP_INSTANCE_UID);
227
228 OrthancPlugins::DicomPath contourDataPath(
229 DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
230 DICOM_TAG_CONTOUR_SEQUENCE, 0,
231 DICOM_TAG_CONTOUR_DATA);
232
233 for (size_t j = 0; j < countSlices; j++)
234 {
235 unsigned int countPoints = 0;
236
237 countPointsPath.SetPrefixIndex(1, j);
238 if (!reader.GetUnsignedIntegerValue(countPoints, countPointsPath))
239 {
240 std::string s;
241 DicomPathToString(s, countPointsPath);
242 LOG(ERROR) << "Dicom path " << s << " is not valid (should contain an unsigned integer)";
243 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
244 }
245
246 //LOG(INFO) << "Parsing slice containing " << countPoints << " vertices";
247
248 geometricTypePath.SetPrefixIndex(1, j);
249 std::string type = reader.GetMandatoryStringValue(geometricTypePath);
250 if (type != "CLOSED_PLANAR")
251 {
252 // TODO: support points!!
253 LOG(WARNING) << "Ignoring contour with geometry type: " << type;
254 continue;
255 }
256
257 size_t size = 0;
258
259 imageSequencePath.SetPrefixIndex(1, j);
260 if (!tags.GetSequenceSize(size, imageSequencePath) || size != 1)
261 {
262 LOG(ERROR) << "The ContourImageSequence sequence (tag 3006,0016) must be present and contain one entry.";
263 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
264 }
265
266 referencedInstancePath.SetPrefixIndex(1, j);
267 std::string sopInstanceUid = reader.GetMandatoryStringValue(referencedInstancePath);
268
269 contourDataPath.SetPrefixIndex(1, j);
270 std::string slicesData = reader.GetMandatoryStringValue(contourDataPath);
271
272 Vector points;
273 if (!LinearAlgebra::ParseVector(points, slicesData) ||
274 points.size() != 3 * countPoints)
275 {
276 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
277 }
278
279 // seen in real world
280 if (Orthanc::Toolbox::StripSpaces(sopInstanceUid) == "")
281 {
282 LOG(ERROR) << "WARNING. The following Dicom tag (Referenced SOP Instance UID) contains an empty value : // (3006,0039)[" << i << "] / (0x3006, 0x0040)[0] / (0x3006, 0x0016)[0] / (0x0008, 0x1155)";
283 }
284
285 DicomStructurePolygon2 polygon(sopInstanceUid,type);
286 polygon.Reserve(countPoints);
287
288 for (size_t k = 0; k < countPoints; k++)
289 {
290 Vector v(3);
291 v[0] = points[3 * k];
292 v[1] = points[3 * k + 1];
293 v[2] = points[3 * k + 2];
294 polygon.AddPoint(v);
295 }
296 structures_[i].AddPolygon(polygon);
297 }
298 }
299 }
300
301
302 void DicomStructureSet2::Clear()
303 {
304 structures_.clear();
305 }
306
307 }
308
309 #endif
310 // BGO_ENABLE_DICOMSTRUCTURESETLOADER2
311