comparison Framework/Toolbox/OrthancSlicesLoader.cpp @ 73:ffa6dded91bd wasm

reorganization
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 24 May 2017 11:59:24 +0200
parents
children f5f54ed8d307
comparison
equal deleted inserted replaced
72:c1cc3bdba18c 73:ffa6dded91bd
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 Osimis, 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 "OrthancSlicesLoader.h"
23
24 #include "MessagingToolbox.h"
25
26 #include "../../Resources/Orthanc/Core/Images/PngReader.h"
27 #include "../../Resources/Orthanc/Core/Logging.h"
28 #include "../../Resources/Orthanc/Core/OrthancException.h"
29 #include "../../Resources/Orthanc/Plugins/Samples/Common/DicomDatasetReader.h"
30 #include "../../Resources/Orthanc/Plugins/Samples/Common/FullOrthancDataset.h"
31
32 #include <boost/lexical_cast.hpp>
33
34 namespace OrthancStone
35 {
36 class OrthancSlicesLoader::Operation : public Orthanc::IDynamicObject
37 {
38 private:
39 Mode mode_;
40 unsigned int frame_;
41 unsigned int sliceIndex_;
42 const Slice* slice_;
43 std::string instanceId_;
44
45 Operation(Mode mode) :
46 mode_(mode)
47 {
48 }
49
50 public:
51 Mode GetMode() const
52 {
53 return mode_;
54 }
55
56 unsigned int GetSliceIndex() const
57 {
58 assert(mode_ == Mode_LoadImage);
59 return sliceIndex_;
60 }
61
62 const Slice& GetSlice() const
63 {
64 assert(mode_ == Mode_LoadImage && slice_ != NULL);
65 return *slice_;
66 }
67
68 unsigned int GetFrame() const
69 {
70 assert(mode_ == Mode_InstanceGeometry);
71 return frame_;
72 }
73
74 const std::string& GetInstanceId() const
75 {
76 assert(mode_ == Mode_InstanceGeometry);
77 return instanceId_;
78 }
79
80 static Operation* DownloadSeriesGeometry()
81 {
82 return new Operation(Mode_SeriesGeometry);
83 }
84
85 static Operation* DownloadInstanceGeometry(const std::string& instanceId,
86 unsigned int frame)
87 {
88 std::auto_ptr<Operation> operation(new Operation(Mode_InstanceGeometry));
89 operation->instanceId_ = instanceId;
90 operation->frame_ = frame;
91 return operation.release();
92 }
93
94 static Operation* DownloadSliceImage(unsigned int sliceIndex,
95 const Slice& slice)
96 {
97 std::auto_ptr<Operation> tmp(new Operation(Mode_LoadImage));
98 tmp->sliceIndex_ = sliceIndex;
99 tmp->slice_ = &slice;
100 return tmp.release();
101 }
102 };
103
104
105 class OrthancSlicesLoader::WebCallback : public IWebService::ICallback
106 {
107 private:
108 OrthancSlicesLoader& that_;
109
110 public:
111 WebCallback(OrthancSlicesLoader& that) :
112 that_(that)
113 {
114 }
115
116 virtual void NotifySuccess(const std::string& uri,
117 const void* answer,
118 size_t answerSize,
119 Orthanc::IDynamicObject* payload)
120 {
121 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload));
122
123 switch (operation->GetMode())
124 {
125 case Mode_SeriesGeometry:
126 that_.ParseSeriesGeometry(answer, answerSize);
127 break;
128
129 case Mode_InstanceGeometry:
130 that_.ParseInstanceGeometry(operation->GetInstanceId(),
131 operation->GetFrame(), answer, answerSize);
132 break;
133
134 case Mode_LoadImage:
135 that_.ParseSliceImage(*operation, answer, answerSize);
136 break;
137
138 default:
139 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
140 }
141 }
142
143 virtual void NotifyError(const std::string& uri,
144 Orthanc::IDynamicObject* payload)
145 {
146 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload));
147 LOG(ERROR) << "Cannot download " << uri;
148
149 switch (operation->GetMode())
150 {
151 case Mode_SeriesGeometry:
152 that_.userCallback_.NotifyGeometryError(that_);
153 that_.state_ = State_Error;
154 break;
155
156 case Mode_LoadImage:
157 that_.userCallback_.NotifySliceImageError(that_, operation->GetSliceIndex(),
158 operation->GetSlice());
159 break;
160
161 default:
162 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
163 }
164 }
165 };
166
167
168 void OrthancSlicesLoader::ParseSeriesGeometry(const void* answer,
169 size_t size)
170 {
171 Json::Value series;
172 if (!MessagingToolbox::ParseJson(series, answer, size) ||
173 series.type() != Json::objectValue)
174 {
175 userCallback_.NotifyGeometryError(*this);
176 return;
177 }
178
179 Json::Value::Members instances = series.getMemberNames();
180
181 slices_.Reserve(instances.size());
182
183 for (size_t i = 0; i < instances.size(); i++)
184 {
185 OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]);
186
187 Slice slice;
188 if (slice.ParseOrthancFrame(dataset, instances[i], 0 /* todo */))
189 {
190 slices_.AddSlice(slice);
191 }
192 else
193 {
194 LOG(WARNING) << "Skipping invalid instance " << instances[i];
195 }
196 }
197
198 bool ok = false;
199
200 if (slices_.GetSliceCount() > 0)
201 {
202 Vector normal;
203 if (slices_.SelectNormal(normal))
204 {
205 slices_.FilterNormal(normal);
206 slices_.SetNormal(normal);
207 slices_.Sort();
208 ok = true;
209 }
210 }
211
212 state_ = State_GeometryReady;
213
214 if (ok)
215 {
216 LOG(INFO) << "Loaded a series with " << slices_.GetSliceCount() << " slice(s)";
217 userCallback_.NotifyGeometryReady(*this);
218 }
219 else
220 {
221 LOG(ERROR) << "This series is empty";
222 userCallback_.NotifyGeometryError(*this);
223 }
224 }
225
226
227 void OrthancSlicesLoader::ParseInstanceGeometry(const std::string& instanceId,
228 unsigned int frame,
229 const void* answer,
230 size_t size)
231 {
232 Json::Value tags;
233 if (!MessagingToolbox::ParseJson(tags, answer, size) ||
234 tags.type() != Json::objectValue)
235 {
236 userCallback_.NotifyGeometryError(*this);
237 return;
238 }
239
240 OrthancPlugins::FullOrthancDataset dataset(tags);
241
242 state_ = State_GeometryReady;
243
244 Slice slice;
245 if (slice.ParseOrthancFrame(dataset, instanceId, frame))
246 {
247 LOG(INFO) << "Loaded instance " << instanceId;
248 slices_.AddSlice(slice);
249 userCallback_.NotifyGeometryReady(*this);
250 }
251 else
252 {
253 LOG(WARNING) << "Skipping invalid instance " << instanceId;
254 userCallback_.NotifyGeometryError(*this);
255 }
256 }
257
258
259 void OrthancSlicesLoader::ParseSliceImage(const Operation& operation,
260 const void* answer,
261 size_t size)
262 {
263 std::auto_ptr<Orthanc::PngReader> image(new Orthanc::PngReader);
264 image->ReadFromMemory(answer, size);
265
266 bool ok = (image->GetWidth() == operation.GetSlice().GetWidth() ||
267 image->GetHeight() == operation.GetSlice().GetHeight());
268
269 if (ok &&
270 operation.GetSlice().GetConverter().GetExpectedPixelFormat() ==
271 Orthanc::PixelFormat_SignedGrayscale16)
272 {
273 if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16)
274 {
275 image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16);
276 }
277 else
278 {
279 ok = false;
280 }
281 }
282
283 if (ok)
284 {
285 userCallback_.NotifySliceImageReady(*this, operation.GetSliceIndex(),
286 operation.GetSlice(), image.release());
287 }
288 else
289 {
290 userCallback_.NotifySliceImageError(*this, operation.GetSliceIndex(),
291 operation.GetSlice());
292 }
293 }
294
295
296 OrthancSlicesLoader::OrthancSlicesLoader(ICallback& callback,
297 IWebService& orthanc) :
298 webCallback_(new WebCallback(*this)),
299 userCallback_(callback),
300 orthanc_(orthanc),
301 state_(State_Initialization)
302 {
303 }
304
305
306 void OrthancSlicesLoader::ScheduleLoadSeries(const std::string& seriesId)
307 {
308 if (state_ != State_Initialization)
309 {
310 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
311 }
312 else
313 {
314 state_ = State_LoadingGeometry;
315 std::string uri = "/series/" + seriesId + "/instances-tags";
316 orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSeriesGeometry());
317 }
318 }
319
320
321 void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId,
322 unsigned int frame)
323 {
324 if (state_ != State_Initialization)
325 {
326 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
327 }
328 else
329 {
330 state_ = State_LoadingGeometry;
331 std::string uri = "/instances/" + instanceId + "/tags";
332 orthanc_.ScheduleGetRequest
333 (*webCallback_, uri, Operation::DownloadInstanceGeometry(instanceId, frame));
334 }
335 }
336
337
338 size_t OrthancSlicesLoader::GetSliceCount() const
339 {
340 if (state_ != State_GeometryReady)
341 {
342 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
343 }
344
345 return slices_.GetSliceCount();
346 }
347
348
349 const Slice& OrthancSlicesLoader::GetSlice(size_t index) const
350 {
351 if (state_ != State_GeometryReady)
352 {
353 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
354 }
355
356 return slices_.GetSlice(index);
357 }
358
359
360 void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index)
361 {
362 if (state_ != State_GeometryReady)
363 {
364 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
365 }
366 else
367 {
368 const Slice& slice = GetSlice(index);
369
370 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" +
371 boost::lexical_cast<std::string>(slice.GetFrame()));
372
373 switch (slice.GetConverter().GetExpectedPixelFormat())
374 {
375 case Orthanc::PixelFormat_RGB24:
376 uri += "/preview";
377 break;
378
379 case Orthanc::PixelFormat_Grayscale16:
380 uri += "/image-uint16";
381 break;
382
383 case Orthanc::PixelFormat_SignedGrayscale16:
384 uri += "/image-int16";
385 break;
386
387 default:
388 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
389 }
390
391 orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSliceImage(index, slice));
392 }
393 }
394 }