comparison Framework/Toolbox/SortedFrames.cpp @ 1478:fab6c6e795a3

Framework/Toolbox/SortedFrames.cpp
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 18 Jun 2020 16:01:00 +0200
parents
children 5e3cfe87a873
comparison
equal deleted inserted replaced
1477:5732edec7cbd 1478:fab6c6e795a3
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
22 #include "SortedFrames.h"
23
24 #include <OrthancException.h>
25
26 #include "GeometryToolbox.h"
27
28 namespace OrthancStone
29 {
30 SortedFrames::Instance::Instance(const Orthanc::DicomMap& tags)
31 {
32 tags_.Assign(tags);
33
34 if (!tags.LookupStringValue(sopInstanceUid_, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false))
35 {
36 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
37 }
38
39 uint32_t tmp;
40 if (tags.ParseUnsignedInteger32(tmp, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES))
41 {
42 numberOfFrames_ = tmp;
43 }
44 else
45 {
46 numberOfFrames_ = 1;
47 }
48
49 hasPosition_ = (
50 LinearAlgebra::ParseVector(position_, tags, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT) &&
51 position_.size() == 3 &&
52 GeometryToolbox::ComputeNormal(normal_, tags));
53 }
54
55
56 const Vector& SortedFrames::Instance::GetNormal() const
57 {
58 if (hasPosition_)
59 {
60 return normal_;
61 }
62 else
63 {
64 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
65 }
66 }
67
68
69 const Vector& SortedFrames::Instance::GetPosition() const
70 {
71 if (hasPosition_)
72 {
73 return position_;
74 }
75 else
76 {
77 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
78 }
79 }
80
81
82 SortedFrames::Frame::Frame(const Instance& instance,
83 unsigned int frameIndex) :
84 instance_(instance),
85 frameIndex_(frameIndex)
86 {
87 if (frameIndex >= instance.GetNumberOfFrames())
88 {
89 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
90 }
91 }
92
93
94 const SortedFrames::Instance& SortedFrames::GetInstance(size_t index) const
95 {
96 if (index >= instances_.size())
97 {
98 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
99 }
100 else
101 {
102 assert(instances_[index] != NULL);
103 return *instances_[index];
104 }
105 }
106
107
108 const SortedFrames::Frame& SortedFrames::GetFrame(size_t index) const
109 {
110 if (!sorted_)
111 {
112 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
113 "Sort() has not been called");
114 }
115 if (index >= frames_.size())
116 {
117 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
118 }
119 else
120 {
121 return frames_[index];
122 }
123 }
124
125
126 SortedFrames::~SortedFrames()
127 {
128 for (size_t i = 0; i < instances_.size(); i++)
129 {
130 assert(instances_[i] != NULL);
131 delete instances_[i];
132 }
133 }
134
135
136 void SortedFrames::AddInstance(const Orthanc::DicomMap& tags)
137 {
138 std::unique_ptr<Instance> instance(new Instance(tags));
139
140 std::string studyInstanceUid, seriesInstanceUid;
141 if (!tags.LookupStringValue(studyInstanceUid, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) ||
142 !tags.LookupStringValue(seriesInstanceUid, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false))
143 {
144 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
145 }
146
147 if (instances_.empty())
148 {
149 studyInstanceUid_ = studyInstanceUid;
150 seriesInstanceUid_ = seriesInstanceUid;
151 }
152 else
153 {
154 if (studyInstanceUid_ != studyInstanceUid ||
155 seriesInstanceUid_ != seriesInstanceUid)
156 {
157 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange,
158 "Mixing instances from different series");
159 }
160 }
161
162 instances_.push_back(instance.release());
163 sorted_ = false;
164 frames_.clear();
165 }
166
167
168 void SortedFrames::AddFramesOfInstance(std::set<size_t>& remainingInstances,
169 size_t index)
170 {
171 assert(instances_[index] != NULL);
172 const Instance& instance = *instances_[index];
173
174 for (unsigned int i = 0; i < instance.GetNumberOfFrames(); i++)
175 {
176 frames_.push_back(Frame(instance, i));
177 }
178
179 assert(remainingInstances.find(index) != remainingInstances.end());
180 remainingInstances.erase(index);
181 }
182
183
184 namespace
185 {
186 template<typename T>
187 class SortableItem
188 {
189 private:
190 T value_;
191 size_t instance_;
192 std::string sopInstanceUid_;
193
194 public:
195 SortableItem(const T& value,
196 size_t instance,
197 const std::string& sopInstanceUid) :
198 value_(value),
199 instance_(instance),
200 sopInstanceUid_(sopInstanceUid)
201 {
202 }
203
204 size_t GetInstanceIndex() const
205 {
206 return instance_;
207 }
208
209 bool operator< (const SortableItem& other) const
210 {
211 return (value_ < other.value_ ||
212 (value_ == other.value_ &&
213 sopInstanceUid_ < other.sopInstanceUid_));
214 }
215 };
216 }
217
218
219 void SortedFrames::SortUsingIntegerTag(std::set<size_t>& remainingInstances,
220 const Orthanc::DicomTag& tag)
221 {
222 std::vector< SortableItem<int32_t> > items;
223 items.reserve(remainingInstances.size());
224
225 for (std::set<size_t>::const_iterator it = remainingInstances.begin();
226 it != remainingInstances.end(); ++it)
227 {
228 assert(instances_[*it] != NULL);
229 const Instance& instance = *instances_[*it];
230
231 int32_t value;
232 std::string sopInstanceUid;
233 if (instance.GetTags().ParseInteger32(value, tag) &&
234 instance.GetTags().LookupStringValue(
235 sopInstanceUid, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false))
236 {
237 items.push_back(SortableItem<int32_t>(value, *it, sopInstanceUid));
238 }
239 }
240
241 std::sort(items.begin(), items.end());
242
243 for (size_t i = 0; i < items.size(); i++)
244 {
245 AddFramesOfInstance(remainingInstances, items[i].GetInstanceIndex());
246 }
247 }
248
249
250 void SortedFrames::SortUsingSopInstanceUid(std::set<size_t>& remainingInstances)
251 {
252 std::vector<SortableItem<int32_t> > items;
253 items.reserve(remainingInstances.size());
254
255 for (std::set<size_t>::const_iterator it = remainingInstances.begin();
256 it != remainingInstances.end(); ++it)
257 {
258 assert(instances_[*it] != NULL);
259 const Instance& instance = *instances_[*it];
260
261 std::string sopInstanceUid;
262 if (instance.GetTags().LookupStringValue(
263 sopInstanceUid, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false))
264 {
265 items.push_back(SortableItem<int32_t>(0 /* arbitrary value */, *it, sopInstanceUid));
266 }
267 }
268
269 std::sort(items.begin(), items.end());
270
271 for (size_t i = 0; i < items.size(); i++)
272 {
273 AddFramesOfInstance(remainingInstances, items[i].GetInstanceIndex());
274 }
275 }
276
277
278 void SortedFrames::SortUsing3DLocation(std::set<size_t>& remainingInstances)
279 {
280 /**
281 * Compute the mean of the normal vectors, using the recursive
282 * formula for arithmetic means for numerical stability.
283 * https://diego.assencio.com/?index=c34d06f4f4de2375658ed41f70177d59
284 **/
285
286 Vector meanNormal;
287 LinearAlgebra::AssignVector(meanNormal, 0, 0, 0);
288
289 unsigned int n = 0;
290
291 for (std::set<size_t>::const_iterator it = remainingInstances.begin();
292 it != remainingInstances.end(); ++it)
293 {
294 assert(instances_[*it] != NULL);
295 const Instance& instance = *instances_[*it];
296
297 if (instance.HasPosition())
298 {
299 n += 1;
300 meanNormal += (instance.GetNormal() - meanNormal) / static_cast<float>(n);
301 }
302 }
303
304 std::vector<SortableItem<float> > items;
305 items.reserve(n);
306
307 for (std::set<size_t>::const_iterator it = remainingInstances.begin();
308 it != remainingInstances.end(); ++it)
309 {
310 assert(instances_[*it] != NULL);
311 const Instance& instance = *instances_[*it];
312
313 std::string sopInstanceUid;
314 if (instance.HasPosition() &&
315 instance.GetTags().LookupStringValue(
316 sopInstanceUid, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false))
317 {
318 double p = LinearAlgebra::DotProduct(meanNormal, instance.GetPosition());
319 items.push_back(SortableItem<float>(p, *it, sopInstanceUid));
320 }
321 }
322
323 assert(items.size() <= n);
324
325 std::sort(items.begin(), items.end());
326
327 for (size_t i = 0; i < items.size(); i++)
328 {
329 AddFramesOfInstance(remainingInstances, items[i].GetInstanceIndex());
330 }
331 }
332
333
334 size_t SortedFrames::GetFramesCount() const
335 {
336 if (sorted_)
337 {
338 return frames_.size();
339 }
340 else
341 {
342 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
343 "Sort() has not been called");
344 }
345 }
346
347
348 void SortedFrames::Sort()
349 {
350 if (!sorted_)
351 {
352 size_t totalFrames = 0;
353 std::set<size_t> remainingInstances;
354
355 for (size_t i = 0; i < instances_.size(); i++)
356 {
357 assert(instances_[i] != NULL);
358 totalFrames += instances_[i]->GetNumberOfFrames();
359
360 remainingInstances.insert(i);
361 }
362
363 frames_.clear();
364 frames_.reserve(totalFrames);
365
366 SortUsingIntegerTag(remainingInstances, Orthanc::DICOM_TAG_INSTANCE_NUMBER); // VR is "IS"
367 SortUsingIntegerTag(remainingInstances, Orthanc::DICOM_TAG_IMAGE_INDEX); // VR is "US"
368 SortUsing3DLocation(remainingInstances);
369 SortUsingSopInstanceUid(remainingInstances);
370
371 // The following could in theory happen if several instances
372 // have the same SOPInstanceUID, no ordering is available
373 for (std::set<size_t>::const_iterator it = remainingInstances.begin();
374 it != remainingInstances.end(); it++)
375 {
376 AddFramesOfInstance(remainingInstances, *it);
377 }
378
379 if (frames_.size() != totalFrames ||
380 !remainingInstances.empty())
381 {
382 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
383 }
384
385 sorted_ = true;
386 }
387 }
388 }