comparison OrthancStone/Sources/Loaders/SeriesOrderedFrames.cpp @ 1512:244ad1e4e76a

reorganization of folders
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 07 Jul 2020 16:21:02 +0200
parents Framework/Loaders/SeriesOrderedFrames.cpp@30deba7bc8e2
children 85e117739eca
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
22 #include "../Toolbox/SlicesSorter.h"
23 #include "SeriesOrderedFrames.h"
24
25 #include <OrthancException.h>
26
27 namespace OrthancStone
28 {
29 class SeriesOrderedFrames::Instance : public boost::noncopyable
30 {
31 private:
32 std::unique_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(),
220 static_cast<unsigned int>(j)));
221 }
222 }
223
224 isRegular_ = sorter.ComputeSpacingBetweenSlices(spacingBetweenSlices_);
225 return true;
226 }
227 }
228
229
230 void SeriesOrderedFrames::SortIndexInSeries()
231 {
232 std::vector<InstanceWithIndexInSeries> tmp;
233 tmp.reserve(instances_.size());
234
235 for (size_t i = 0; i < instances_.size(); i++)
236 {
237 assert(instances_[i] != NULL);
238 tmp.push_back(InstanceWithIndexInSeries(*instances_[i]));
239 }
240
241 std::sort(tmp.begin(), tmp.end());
242
243 for (size_t i = 0; i < tmp.size(); i++)
244 {
245 for (size_t j = 0; j < tmp[i].GetInstance().GetInstanceParameters().GetImageInformation().GetNumberOfFrames(); j++)
246 {
247 orderedFrames_.push_back(new Frame(tmp[i].GetInstance(),
248 static_cast<unsigned int>(j)));
249 }
250 }
251 }
252
253
254 const SeriesOrderedFrames::Frame& SeriesOrderedFrames::GetFrame(size_t seriesIndex) const
255 {
256 if (seriesIndex >= orderedFrames_.size())
257 {
258 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
259 }
260 else
261 {
262 assert(orderedFrames_[seriesIndex] != NULL);
263 return *(orderedFrames_[seriesIndex]);
264 }
265 }
266
267
268 SeriesOrderedFrames::SeriesOrderedFrames(LoadedDicomResources& instances) :
269 isVolume_(false),
270 isRegular_(false),
271 spacingBetweenSlices_(0)
272 {
273 instances_.reserve(instances.GetSize());
274
275 size_t numberOfFrames = 0;
276
277 for (size_t i = 0; i < instances.GetSize(); i++)
278 {
279 try
280 {
281 std::unique_ptr<Instance> instance(new Instance(instances.GetResource(i)));
282 numberOfFrames += instance->GetInstanceParameters().GetImageInformation().GetNumberOfFrames();
283 instances_.push_back(instance.release());
284 }
285 catch (Orthanc::OrthancException&)
286 {
287 // The instance has not all the required DICOM tags, skip it
288 }
289 }
290
291 orderedFrames_.reserve(numberOfFrames);
292
293 if (Sort3DVolume())
294 {
295 isVolume_ = true;
296
297 if (isRegular_)
298 {
299 LOG(INFO) << "Regular 3D volume detected";
300 }
301 else
302 {
303 LOG(INFO) << "Non-regular 3D volume detected";
304 }
305 }
306 else
307 {
308 LOG(INFO) << "Series is not a 3D volume, sorting by index";
309 SortIndexInSeries();
310 }
311
312 LOG(INFO) << "Number of frames: " << orderedFrames_.size();
313 }
314
315
316 unsigned int SeriesOrderedFrames::GetFrameIndex(size_t seriesIndex) const
317 {
318 return GetFrame(seriesIndex).GetFrameIndex();
319 }
320
321
322 const Orthanc::DicomMap& SeriesOrderedFrames::GetInstance(size_t seriesIndex) const
323 {
324 return GetFrame(seriesIndex).GetInstance();
325 }
326
327
328 const DicomInstanceParameters& SeriesOrderedFrames::GetInstanceParameters(size_t seriesIndex) const
329 {
330 return GetFrame(seriesIndex).GetInstanceParameters();
331 }
332
333
334 double SeriesOrderedFrames::GetSpacingBetweenSlices() const
335 {
336 if (IsRegular3DVolume())
337 {
338 return spacingBetweenSlices_;
339 }
340 else
341 {
342 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
343 }
344 }
345 }