comparison Framework/Deprecated/Toolbox/Slice.cpp @ 732:c35e98d22764

move Deprecated classes to a separate folder
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 21 May 2019 14:27:35 +0200
parents Framework/Toolbox/Slice.cpp@d2c0e347ddc2
children 1f74bc3459ba
comparison
equal deleted inserted replaced
729:529189f399ec 732:c35e98d22764
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-2019 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
22 #include "Slice.h"
23
24 #include "../../StoneEnumerations.h"
25 #include "../../Toolbox/GeometryToolbox.h"
26
27 #include <Core/Logging.h>
28 #include <Core/OrthancException.h>
29 #include <Core/Toolbox.h>
30
31 #include <boost/lexical_cast.hpp>
32
33 namespace Deprecated
34 {
35 static bool ParseDouble(double& target,
36 const std::string& source)
37 {
38 try
39 {
40 target = boost::lexical_cast<double>(source);
41 return true;
42 }
43 catch (boost::bad_lexical_cast&)
44 {
45 return false;
46 }
47 }
48
49 Slice* Slice::Clone() const
50 {
51 std::auto_ptr<Slice> target(new Slice());
52
53 target->type_ = type_;
54 target->orthancInstanceId_ = orthancInstanceId_;
55 target->sopClassUid_ = sopClassUid_;
56 target->frame_ = frame_;
57 target->frameCount_ = frameCount_;
58 target->geometry_ = geometry_;
59 target->pixelSpacingX_ = pixelSpacingX_;
60 target->pixelSpacingY_ = pixelSpacingY_;
61 target->thickness_ = thickness_;
62 target->width_ = width_;
63 target->height_ = height_;
64 target->converter_ = converter_;
65 if (imageInformation_.get() != NULL)
66 target->imageInformation_.reset(imageInformation_->Clone());
67
68 return target.release();
69 }
70
71 bool Slice::ComputeRTDoseGeometry(const Orthanc::DicomMap& dataset,
72 unsigned int frame)
73 {
74 // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part03/sect_C.8.8.3.2.html
75
76 {
77 std::string increment;
78
79 if (dataset.CopyToString(increment, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER, false))
80 {
81 Orthanc::Toolbox::ToUpperCase(increment);
82 if (increment != "3004,000C") // This is the "Grid Frame Offset Vector" tag
83 {
84 LOG(ERROR) << "Bad value for the \"FrameIncrementPointer\" tag";
85 return false;
86 }
87 }
88 }
89
90 std::string offsetTag;
91
92 if (!dataset.CopyToString(offsetTag, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR, false) ||
93 offsetTag.empty())
94 {
95 LOG(ERROR) << "Cannot read the \"GridFrameOffsetVector\" tag, check you are using Orthanc >= 1.3.1";
96 return false;
97 }
98
99 std::vector<std::string> offsets;
100 Orthanc::Toolbox::TokenizeString(offsets, offsetTag, '\\');
101
102 if (frameCount_ <= 1 ||
103 offsets.size() < frameCount_ ||
104 offsets.size() < 2 ||
105 frame >= frameCount_)
106 {
107 LOG(ERROR) << "No information about the 3D location of some slice(s) in a RT DOSE";
108 return false;
109 }
110
111 double offset0, offset1, z;
112
113 if (!ParseDouble(offset0, offsets[0]) ||
114 !ParseDouble(offset1, offsets[1]) ||
115 !ParseDouble(z, offsets[frame]))
116 {
117 LOG(ERROR) << "Invalid syntax";
118 return false;
119 }
120
121 if (!OrthancStone::LinearAlgebra::IsCloseToZero(offset0))
122 {
123 LOG(ERROR) << "Invalid syntax";
124 return false;
125 }
126
127 geometry_ = OrthancStone::CoordinateSystem3D(geometry_.GetOrigin() + z * geometry_.GetNormal(),
128 //+ 650 * geometry_.GetAxisX(),
129 geometry_.GetAxisX(),
130 geometry_.GetAxisY());
131
132 thickness_ = offset1 - offset0;
133 if (thickness_ < 0)
134 {
135 thickness_ = -thickness_;
136 }
137
138 return true;
139 }
140
141
142 bool Slice::ParseOrthancFrame(const Orthanc::DicomMap& dataset,
143 const std::string& instanceId,
144 unsigned int frame)
145 {
146 orthancInstanceId_ = instanceId;
147 frame_ = frame;
148 type_ = Type_OrthancDecodableFrame;
149 imageInformation_.reset(new Orthanc::DicomImageInformation(dataset));
150
151 if (!dataset.CopyToString(sopClassUid_, Orthanc::DICOM_TAG_SOP_CLASS_UID, false) ||
152 sopClassUid_.empty())
153 {
154 LOG(ERROR) << "Instance without a SOP class UID";
155 return false;
156 }
157
158 if (!dataset.ParseUnsignedInteger32(frameCount_, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES))
159 {
160 frameCount_ = 1; // Assume instance with one frame
161 }
162
163 if (frame >= frameCount_)
164 {
165 return false;
166 }
167
168 if (!dataset.ParseUnsignedInteger32(width_, Orthanc::DICOM_TAG_COLUMNS) ||
169 !dataset.ParseUnsignedInteger32(height_, Orthanc::DICOM_TAG_ROWS))
170 {
171 return false;
172 }
173
174 thickness_ = 100.0 * std::numeric_limits<double>::epsilon();
175
176 std::string tmp;
177 if (dataset.CopyToString(tmp, Orthanc::DICOM_TAG_SLICE_THICKNESS, false))
178 {
179 if (!tmp.empty() &&
180 !ParseDouble(thickness_, tmp))
181 {
182 return false; // Syntax error
183 }
184 }
185
186 converter_.ReadParameters(dataset);
187
188 OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dataset);
189
190 std::string position, orientation;
191 if (dataset.CopyToString(position, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) &&
192 dataset.CopyToString(orientation, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false))
193 {
194 geometry_ = OrthancStone::CoordinateSystem3D(position, orientation);
195
196 bool ok = true;
197
198 switch (OrthancStone::StringToSopClassUid(sopClassUid_))
199 {
200 case OrthancStone::SopClassUid_RTDose:
201 type_ = Type_OrthancRawFrame;
202 ok = ComputeRTDoseGeometry(dataset, frame);
203 break;
204
205 default:
206 break;
207 }
208
209 if (!ok)
210 {
211 LOG(ERROR) << "Cannot deduce the 3D location of frame " << frame
212 << " in instance " << instanceId << ", whose SOP class UID is: " << sopClassUid_;
213 return false;
214 }
215 }
216
217 return true;
218 }
219
220
221 const std::string Slice::GetOrthancInstanceId() const
222 {
223 if (type_ == Type_OrthancDecodableFrame ||
224 type_ == Type_OrthancRawFrame)
225 {
226 return orthancInstanceId_;
227 }
228 else
229 {
230 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
231 }
232 }
233
234
235 unsigned int Slice::GetFrame() const
236 {
237 if (type_ == Type_Invalid)
238 {
239 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
240 }
241
242 return frame_;
243 }
244
245
246 const OrthancStone::CoordinateSystem3D& Slice::GetGeometry() const
247 {
248 if (type_ == Type_Invalid)
249 {
250 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
251 }
252
253 return geometry_;
254 }
255
256
257 double Slice::GetThickness() const
258 {
259 if (type_ == Type_Invalid)
260 {
261 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
262 }
263
264 return thickness_;
265 }
266
267
268 double Slice::GetPixelSpacingX() const
269 {
270 if (type_ == Type_Invalid)
271 {
272 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
273 }
274
275 return pixelSpacingX_;
276 }
277
278
279 double Slice::GetPixelSpacingY() const
280 {
281 if (type_ == Type_Invalid)
282 {
283 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
284 }
285
286 return pixelSpacingY_;
287 }
288
289
290 unsigned int Slice::GetWidth() const
291 {
292 if (type_ == Type_Invalid)
293 {
294 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
295 }
296
297 return width_;
298 }
299
300
301 unsigned int Slice::GetHeight() const
302 {
303 if (type_ == Type_Invalid)
304 {
305 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
306 }
307
308 return height_;
309 }
310
311
312 const DicomFrameConverter& Slice::GetConverter() const
313 {
314 if (type_ == Type_Invalid)
315 {
316 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
317 }
318
319 return converter_;
320 }
321
322
323 bool Slice::ContainsPlane(const OrthancStone::CoordinateSystem3D& plane) const
324 {
325 if (type_ == Type_Invalid)
326 {
327 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
328 }
329
330 bool opposite;
331 return (OrthancStone::GeometryToolbox::IsParallelOrOpposite(opposite,
332 GetGeometry().GetNormal(),
333 plane.GetNormal()) &&
334 OrthancStone::LinearAlgebra::IsNear(GetGeometry().ProjectAlongNormal(GetGeometry().GetOrigin()),
335 GetGeometry().ProjectAlongNormal(plane.GetOrigin()),
336 thickness_ / 2.0));
337 }
338
339
340 void Slice::GetExtent(std::vector<OrthancStone::Vector>& points) const
341 {
342 double sx = GetPixelSpacingX();
343 double sy = GetPixelSpacingY();
344 double w = static_cast<double>(GetWidth());
345 double h = static_cast<double>(GetHeight());
346
347 points.clear();
348 points.push_back(GetGeometry().MapSliceToWorldCoordinates(-0.5 * sx, -0.5 * sy));
349 points.push_back(GetGeometry().MapSliceToWorldCoordinates((w - 0.5) * sx, -0.5 * sy));
350 points.push_back(GetGeometry().MapSliceToWorldCoordinates(-0.5 * sx, (h - 0.5) * sy));
351 points.push_back(GetGeometry().MapSliceToWorldCoordinates((w - 0.5) * sx, (h - 0.5) * sy));
352 }
353
354
355 const Orthanc::DicomImageInformation& Slice::GetImageInformation() const
356 {
357 if (imageInformation_.get() == NULL)
358 {
359 // Only available if constructing the "Slice" object with a DICOM map
360 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
361 }
362 else
363 {
364 return *imageInformation_;
365 }
366 }
367 }