comparison Framework/Loaders/SeriesOrderedFrames.cpp @ 1228:c471a0aa137b broker

adding the next generation of loaders
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 09 Dec 2019 13:58:37 +0100
parents
children 0ca50d275b9a
comparison
equal deleted inserted replaced
1227:a1c0c9c9f9af 1228:c471a0aa137b
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 "../Toolbox/SlicesSorter.h"
23 #include "SeriesOrderedFrames.h"
24
25 #include <Core/OrthancException.h>
26
27 namespace OrthancStone
28 {
29 class SeriesOrderedFrames::Instance : public boost::noncopyable
30 {
31 private:
32 std::auto_ptr<Orthanc::DicomMap> dicom_;
33 DicomInstanceParameters parameters_;
34
35 public:
36 Instance(const Orthanc::DicomMap& dicom) :
37 dicom_(dicom.Clone()),
38 parameters_(dicom)
39 {
40 }
41
42 const Orthanc::DicomMap& GetInstance() const
43 {
44 return *dicom_;
45 }
46
47 const DicomInstanceParameters& GetInstanceParameters() const
48 {
49 return parameters_;
50 }
51
52 bool Lookup3DGeometry(CoordinateSystem3D& target) const
53 {
54 try
55 {
56 std::string imagePositionPatient, imageOrientationPatient;
57 if (dicom_->LookupStringValue(imagePositionPatient, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) &&
58 dicom_->LookupStringValue(imageOrientationPatient, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false))
59 {
60 target = CoordinateSystem3D(imagePositionPatient, imageOrientationPatient);
61 return true;
62 }
63 }
64 catch (Orthanc::OrthancException&)
65 {
66 }
67
68 return false;
69 }
70
71 bool LookupIndexInSeries(int& target) const
72 {
73 std::string value;
74
75 if (dicom_->LookupStringValue(value, Orthanc::DICOM_TAG_INSTANCE_NUMBER, false) ||
76 dicom_->LookupStringValue(value, Orthanc::DICOM_TAG_IMAGE_INDEX, false))
77 {
78 try
79 {
80 target = boost::lexical_cast<int>(value);
81 return true;
82 }
83 catch (boost::bad_lexical_cast&)
84 {
85 }
86 }
87
88 return false;
89 }
90 };
91
92
93 class SeriesOrderedFrames::Frame : public boost::noncopyable
94 {
95 private:
96 const Instance* instance_;
97 unsigned int frameIndex_;
98
99 public:
100 Frame(const Instance& instance,
101 unsigned int frameIndex) :
102 instance_(&instance),
103 frameIndex_(frameIndex)
104 {
105 if (frameIndex_ >= instance.GetInstanceParameters().GetImageInformation().GetNumberOfFrames())
106 {
107 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
108 }
109 }
110
111 const Orthanc::DicomMap& GetInstance() const
112 {
113 assert(instance_ != NULL);
114 return instance_->GetInstance();
115 }
116
117 const DicomInstanceParameters& GetInstanceParameters() const
118 {
119 assert(instance_ != NULL);
120 return instance_->GetInstanceParameters();
121 }
122
123 unsigned int GetFrameIndex() const
124 {
125 return frameIndex_;
126 }
127 };
128
129
130 class SeriesOrderedFrames::InstanceWithIndexInSeries
131 {
132 private:
133 const Instance* instance_; // Don't use a reference to make "std::sort()" happy
134 int index_;
135
136 public:
137 InstanceWithIndexInSeries(const Instance& instance) :
138 instance_(&instance)
139 {
140 if (!instance_->LookupIndexInSeries(index_))
141 {
142 index_ = std::numeric_limits<int>::max();
143 }
144 }
145
146 const Instance& GetInstance() const
147 {
148 return *instance_;
149 }
150
151 int GetIndexInSeries() const
152 {
153 return index_;
154 }
155
156 bool operator< (const InstanceWithIndexInSeries& other) const
157 {
158 return (index_ < other.index_);
159 }
160 };
161
162
163 void SeriesOrderedFrames::Clear()
164 {
165 for (size_t i = 0; i < instances_.size(); i++)
166 {
167 assert(instances_[i] != NULL);
168 delete instances_[i];
169 }
170
171 for (size_t i = 0; i < orderedFrames_.size(); i++)
172 {
173 assert(orderedFrames_[i] != NULL);
174 delete orderedFrames_[i];
175 }
176
177 instances_.clear();
178 orderedFrames_.clear();
179 }
180
181
182 bool SeriesOrderedFrames::Sort3DVolume()
183 {
184 SlicesSorter sorter;
185 sorter.Reserve(instances_.size());
186
187 for (size_t i = 0; i < instances_.size(); i++)
188 {
189 CoordinateSystem3D geometry;
190 if (instances_[i]->Lookup3DGeometry(geometry))
191 {
192 sorter.AddSlice(geometry, new Orthanc::SingleValueObject<Instance*>(instances_[i]));
193 }
194 else
195 {
196 return false; // Not a 3D volume
197 }
198 }
199
200 if (!sorter.Sort() ||
201 sorter.GetSlicesCount() != instances_.size() ||
202 !sorter.AreAllSlicesDistinct())
203 {
204 return false;
205 }
206 else
207 {
208 for (size_t i = 0; i < sorter.GetSlicesCount(); i++)
209 {
210 assert(sorter.HasSlicePayload(i));
211
212 const Orthanc::SingleValueObject<Instance*>& payload =
213 dynamic_cast<const Orthanc::SingleValueObject<Instance*>&>(sorter.GetSlicePayload(i));
214
215 assert(payload.GetValue() != NULL);
216
217 for (size_t j = 0; j < payload.GetValue()->GetInstanceParameters().GetImageInformation().GetNumberOfFrames(); j++)
218 {
219 orderedFrames_.push_back(new Frame(*payload.GetValue(), j));
220 }
221 }
222
223 isRegular_ = sorter.ComputeSpacingBetweenSlices(spacingBetweenSlices_);
224 return true;
225 }
226 }
227
228
229 void SeriesOrderedFrames::SortIndexInSeries()
230 {
231 std::vector<InstanceWithIndexInSeries> tmp;
232 tmp.reserve(instances_.size());
233
234 for (size_t i = 0; i < instances_.size(); i++)
235 {
236 assert(instances_[i] != NULL);
237 tmp.push_back(InstanceWithIndexInSeries(*instances_[i]));
238 }
239
240 std::sort(tmp.begin(), tmp.end());
241
242 for (size_t i = 0; i < tmp.size(); i++)
243 {
244 for (size_t j = 0; j < tmp[i].GetInstance().GetInstanceParameters().GetImageInformation().GetNumberOfFrames(); j++)
245 {
246 orderedFrames_.push_back(new Frame(tmp[i].GetInstance(), j));
247 }
248 }
249 }
250
251
252 const SeriesOrderedFrames::Frame& SeriesOrderedFrames::GetFrame(size_t seriesIndex) const
253 {
254 if (seriesIndex >= orderedFrames_.size())
255 {
256 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
257 }
258 else
259 {
260 assert(orderedFrames_[seriesIndex] != NULL);
261 return *(orderedFrames_[seriesIndex]);
262 }
263 }
264
265
266 SeriesOrderedFrames::SeriesOrderedFrames(LoadedDicomResources& instances) :
267 isVolume_(false),
268 isRegular_(false),
269 spacingBetweenSlices_(0)
270 {
271 instances_.reserve(instances.GetSize());
272
273 size_t numberOfFrames = 0;
274
275 for (size_t i = 0; i < instances.GetSize(); i++)
276 {
277 try
278 {
279 std::auto_ptr<Instance> instance(new Instance(instances.GetResource(i)));
280 numberOfFrames += instance->GetInstanceParameters().GetImageInformation().GetNumberOfFrames();
281 instances_.push_back(instance.release());
282 }
283 catch (Orthanc::OrthancException&)
284 {
285 // The instance has not all the required DICOM tags, skip it
286 }
287 }
288
289 orderedFrames_.reserve(numberOfFrames);
290
291 if (Sort3DVolume())
292 {
293 isVolume_ = true;
294
295 if (isRegular_)
296 {
297 LOG(INFO) << "Regular 3D volume detected";
298 }
299 else
300 {
301 LOG(INFO) << "Non-regular 3D volume detected";
302 }
303 }
304 else
305 {
306 LOG(INFO) << "Series is not a 3D volume, sorting by index";
307 SortIndexInSeries();
308 }
309
310 LOG(INFO) << "Number of frames: " << orderedFrames_.size();
311 }
312
313
314 unsigned int SeriesOrderedFrames::GetFrameIndex(size_t seriesIndex) const
315 {
316 return GetFrame(seriesIndex).GetFrameIndex();
317 }
318
319
320 const Orthanc::DicomMap& SeriesOrderedFrames::GetInstance(size_t seriesIndex) const
321 {
322 return GetFrame(seriesIndex).GetInstance();
323 }
324
325
326 const DicomInstanceParameters& SeriesOrderedFrames::GetInstanceParameters(size_t seriesIndex) const
327 {
328 return GetFrame(seriesIndex).GetInstanceParameters();
329 }
330
331
332 double SeriesOrderedFrames::GetSpacingBetweenSlices() const
333 {
334 if (IsRegular3DVolume())
335 {
336 return spacingBetweenSlices_;
337 }
338 else
339 {
340 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
341 }
342 }
343 }