Mercurial > hg > orthanc-stone
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 } |