Mercurial > hg > orthanc-stone
comparison Framework/Toolbox/OrthancSlicesLoader.cpp @ 252:40b21c1f8b8d am-2
more usage of IObservable/IObserver
author | am@osimis.io |
---|---|
date | Tue, 03 Jul 2018 10:26:56 +0200 |
parents | 192e6e349e69 |
children | 9afafb192180 |
comparison
equal
deleted
inserted
replaced
251:192e6e349e69 | 252:40b21c1f8b8d |
---|---|
11 * | 11 * |
12 * This program is distributed in the hope that it will be useful, but | 12 * This program is distributed in the hope that it will be useful, but |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | 13 * WITHOUT ANY WARRANTY; without even the implied warranty of |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 * Affero General Public License for more details. | 15 * Affero General Public License for more details. |
16 * | 16 * |
17 * You should have received a copy of the GNU Affero General Public License | 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/>. | 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 **/ | 19 **/ |
20 | 20 |
21 | 21 |
45 * https://stackoverflow.com/a/34571089/881731 | 45 * https://stackoverflow.com/a/34571089/881731 |
46 **/ | 46 **/ |
47 static std::string base64_decode(const std::string &in) | 47 static std::string base64_decode(const std::string &in) |
48 { | 48 { |
49 std::string out; | 49 std::string out; |
50 | 50 |
51 std::vector<int> T(256,-1); | 51 std::vector<int> T(256,-1); |
52 for (int i=0; i<64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; | 52 for (int i=0; i<64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; |
53 | 53 |
54 int val=0, valb=-8; | 54 int val=0, valb=-8; |
55 for (size_t i = 0; i < in.size(); i++) { | 55 for (size_t i = 0; i < in.size(); i++) { |
56 unsigned char c = in[i]; | 56 unsigned char c = in[i]; |
57 if (T[c] == -1) break; | 57 if (T[c] == -1) break; |
58 val = (val<<6) + T[c]; | 58 val = (val<<6) + T[c]; |
76 unsigned int frame_; | 76 unsigned int frame_; |
77 unsigned int sliceIndex_; | 77 unsigned int sliceIndex_; |
78 const Slice* slice_; | 78 const Slice* slice_; |
79 std::string instanceId_; | 79 std::string instanceId_; |
80 SliceImageQuality quality_; | 80 SliceImageQuality quality_; |
81 | 81 |
82 Operation(Mode mode) : | 82 Operation(Mode mode) : |
83 mode_(mode) | 83 mode_(mode) |
84 { | 84 { |
85 } | 85 } |
86 | 86 |
87 public: | 87 public: |
88 Mode GetMode() const | 88 Mode GetMode() const |
89 { | 89 { |
90 return mode_; | 90 return mode_; |
91 } | 91 } |
92 | 92 |
93 SliceImageQuality GetQuality() const | 93 SliceImageQuality GetQuality() const |
94 { | 94 { |
95 assert(mode_ == Mode_LoadImage || | 95 assert(mode_ == Mode_LoadImage || |
96 mode_ == Mode_LoadRawImage); | 96 mode_ == Mode_LoadRawImage); |
97 return quality_; | 97 return quality_; |
98 } | 98 } |
99 | 99 |
100 unsigned int GetSliceIndex() const | 100 unsigned int GetSliceIndex() const |
101 { | 101 { |
102 assert(mode_ == Mode_LoadImage || | 102 assert(mode_ == Mode_LoadImage || |
103 mode_ == Mode_LoadRawImage); | 103 mode_ == Mode_LoadRawImage); |
104 return sliceIndex_; | 104 return sliceIndex_; |
105 } | 105 } |
106 | 106 |
107 const Slice& GetSlice() const | 107 const Slice& GetSlice() const |
108 { | 108 { |
109 assert(mode_ == Mode_LoadImage || | 109 assert(mode_ == Mode_LoadImage || |
110 mode_ == Mode_LoadRawImage); | 110 mode_ == Mode_LoadRawImage); |
111 assert(slice_ != NULL); | 111 assert(slice_ != NULL); |
112 return *slice_; | 112 return *slice_; |
113 } | 113 } |
114 | 114 |
115 unsigned int GetFrame() const | 115 unsigned int GetFrame() const |
116 { | 116 { |
117 assert(mode_ == Mode_FrameGeometry); | 117 assert(mode_ == Mode_FrameGeometry); |
118 return frame_; | 118 return frame_; |
119 } | 119 } |
120 | 120 |
121 const std::string& GetInstanceId() const | 121 const std::string& GetInstanceId() const |
122 { | 122 { |
123 assert(mode_ == Mode_FrameGeometry || | 123 assert(mode_ == Mode_FrameGeometry || |
124 mode_ == Mode_InstanceGeometry); | 124 mode_ == Mode_InstanceGeometry); |
125 return instanceId_; | 125 return instanceId_; |
126 } | 126 } |
127 | 127 |
128 static Operation* DownloadSeriesGeometry() | 128 static Operation* DownloadSeriesGeometry() |
129 { | 129 { |
130 return new Operation(Mode_SeriesGeometry); | 130 return new Operation(Mode_SeriesGeometry); |
131 } | 131 } |
132 | 132 |
133 static Operation* DownloadInstanceGeometry(const std::string& instanceId) | 133 static Operation* DownloadInstanceGeometry(const std::string& instanceId) |
134 { | 134 { |
135 std::auto_ptr<Operation> operation(new Operation(Mode_InstanceGeometry)); | 135 std::auto_ptr<Operation> operation(new Operation(Mode_InstanceGeometry)); |
136 operation->instanceId_ = instanceId; | 136 operation->instanceId_ = instanceId; |
137 return operation.release(); | 137 return operation.release(); |
138 } | 138 } |
139 | 139 |
140 static Operation* DownloadFrameGeometry(const std::string& instanceId, | 140 static Operation* DownloadFrameGeometry(const std::string& instanceId, |
141 unsigned int frame) | 141 unsigned int frame) |
142 { | 142 { |
143 std::auto_ptr<Operation> operation(new Operation(Mode_FrameGeometry)); | 143 std::auto_ptr<Operation> operation(new Operation(Mode_FrameGeometry)); |
144 operation->instanceId_ = instanceId; | 144 operation->instanceId_ = instanceId; |
145 operation->frame_ = frame; | 145 operation->frame_ = frame; |
146 return operation.release(); | 146 return operation.release(); |
147 } | 147 } |
148 | 148 |
149 static Operation* DownloadSliceImage(unsigned int sliceIndex, | 149 static Operation* DownloadSliceImage(unsigned int sliceIndex, |
150 const Slice& slice, | 150 const Slice& slice, |
151 SliceImageQuality quality) | 151 SliceImageQuality quality) |
152 { | 152 { |
153 std::auto_ptr<Operation> tmp(new Operation(Mode_LoadImage)); | 153 std::auto_ptr<Operation> tmp(new Operation(Mode_LoadImage)); |
154 tmp->sliceIndex_ = sliceIndex; | 154 tmp->sliceIndex_ = sliceIndex; |
155 tmp->slice_ = &slice; | 155 tmp->slice_ = &slice; |
156 tmp->quality_ = quality; | 156 tmp->quality_ = quality; |
157 return tmp.release(); | 157 return tmp.release(); |
158 } | 158 } |
159 | 159 |
160 static Operation* DownloadSliceRawImage(unsigned int sliceIndex, | 160 static Operation* DownloadSliceRawImage(unsigned int sliceIndex, |
161 const Slice& slice) | 161 const Slice& slice) |
162 { | 162 { |
163 std::auto_ptr<Operation> tmp(new Operation(Mode_LoadRawImage)); | 163 std::auto_ptr<Operation> tmp(new Operation(Mode_LoadRawImage)); |
164 tmp->sliceIndex_ = sliceIndex; | 164 tmp->sliceIndex_ = sliceIndex; |
165 tmp->slice_ = &slice; | 165 tmp->slice_ = &slice; |
166 tmp->quality_ = SliceImageQuality_Full; | 166 tmp->quality_ = SliceImageQuality_Full; |
167 return tmp.release(); | 167 return tmp.release(); |
168 } | 168 } |
169 }; | 169 }; |
170 | 170 |
171 | 171 |
172 class OrthancSlicesLoader::WebCallback : public IWebService::ICallback | 172 class OrthancSlicesLoader::WebCallback : public IWebService::ICallback |
173 { | 173 { |
174 private: | 174 private: |
175 OrthancSlicesLoader& that_; | 175 OrthancSlicesLoader& that_; |
176 | 176 |
177 public: | 177 public: |
178 WebCallback(MessageBroker& broker, OrthancSlicesLoader& that) : | 178 WebCallback(MessageBroker& broker, OrthancSlicesLoader& that) : |
179 IWebService::ICallback(broker), | 179 IWebService::ICallback(broker), |
180 that_(that) | 180 that_(that) |
181 { | 181 { |
182 } | 182 } |
183 | 183 |
184 virtual void OnHttpRequestSuccess(const std::string& uri, | 184 virtual void OnHttpRequestSuccess(const std::string& uri, |
185 const void* answer, | 185 const void* answer, |
186 size_t answerSize, | 186 size_t answerSize, |
187 Orthanc::IDynamicObject* payload) | 187 Orthanc::IDynamicObject* payload) |
188 { | 188 { |
189 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload)); | 189 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload)); |
190 | 190 |
191 switch (operation->GetMode()) | 191 switch (operation->GetMode()) |
192 { | 192 { |
193 case Mode_SeriesGeometry: | 193 case Mode_SeriesGeometry: |
194 that_.ParseSeriesGeometry(answer, answerSize); | 194 that_.ParseSeriesGeometry(answer, answerSize); |
195 break; | |
196 | |
197 case Mode_InstanceGeometry: | |
198 that_.ParseInstanceGeometry(operation->GetInstanceId(), answer, answerSize); | |
199 break; | |
200 | |
201 case Mode_FrameGeometry: | |
202 that_.ParseFrameGeometry(operation->GetInstanceId(), | |
203 operation->GetFrame(), answer, answerSize); | |
204 break; | |
205 | |
206 case Mode_LoadImage: | |
207 switch (operation->GetQuality()) | |
208 { | |
209 case SliceImageQuality_Full: | |
210 that_.ParseSliceImagePng(*operation, answer, answerSize); | |
195 break; | 211 break; |
196 | 212 |
197 case Mode_InstanceGeometry: | 213 case SliceImageQuality_Jpeg50: |
198 that_.ParseInstanceGeometry(operation->GetInstanceId(), answer, answerSize); | 214 case SliceImageQuality_Jpeg90: |
199 break; | 215 case SliceImageQuality_Jpeg95: |
200 | 216 that_.ParseSliceImageJpeg(*operation, answer, answerSize); |
201 case Mode_FrameGeometry: | |
202 that_.ParseFrameGeometry(operation->GetInstanceId(), | |
203 operation->GetFrame(), answer, answerSize); | |
204 break; | |
205 | |
206 case Mode_LoadImage: | |
207 switch (operation->GetQuality()) | |
208 { | |
209 case SliceImageQuality_Full: | |
210 that_.ParseSliceImagePng(*operation, answer, answerSize); | |
211 break; | |
212 | |
213 case SliceImageQuality_Jpeg50: | |
214 case SliceImageQuality_Jpeg90: | |
215 case SliceImageQuality_Jpeg95: | |
216 that_.ParseSliceImageJpeg(*operation, answer, answerSize); | |
217 break; | |
218 | |
219 default: | |
220 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
221 } | |
222 | |
223 break; | |
224 | |
225 case Mode_LoadRawImage: | |
226 that_.ParseSliceRawImage(*operation, answer, answerSize); | |
227 break; | |
228 | |
229 default: | |
230 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
231 } | |
232 } | |
233 | |
234 virtual void OnHttpRequestError(const std::string& uri, | |
235 Orthanc::IDynamicObject* payload) | |
236 { | |
237 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload)); | |
238 LOG(ERROR) << "Cannot download " << uri; | |
239 | |
240 switch (operation->GetMode()) | |
241 { | |
242 case Mode_FrameGeometry: | |
243 case Mode_SeriesGeometry: | |
244 that_.userCallback_.NotifyGeometryError(that_); | |
245 that_.state_ = State_Error; | |
246 break; | |
247 | |
248 case Mode_LoadImage: | |
249 that_.userCallback_.NotifySliceImageError(that_, operation->GetSliceIndex(), | |
250 operation->GetSlice(), | |
251 operation->GetQuality()); | |
252 break; | 217 break; |
253 | 218 |
254 default: | 219 default: |
255 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | 220 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); |
256 } | 221 } |
257 } | 222 |
223 break; | |
224 | |
225 case Mode_LoadRawImage: | |
226 that_.ParseSliceRawImage(*operation, answer, answerSize); | |
227 break; | |
228 | |
229 default: | |
230 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
231 } | |
232 } | |
233 | |
234 virtual void OnHttpRequestError(const std::string& uri, | |
235 Orthanc::IDynamicObject* payload) | |
236 { | |
237 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload)); | |
238 LOG(ERROR) << "Cannot download " << uri; | |
239 | |
240 switch (operation->GetMode()) | |
241 { | |
242 case Mode_FrameGeometry: | |
243 case Mode_SeriesGeometry: | |
244 that_.userCallback_.OnSliceGeometryError(that_); | |
245 that_.state_ = State_Error; | |
246 break; | |
247 | |
248 case Mode_LoadImage: | |
249 that_.userCallback_.OnSliceImageError(that_, operation->GetSliceIndex(), | |
250 operation->GetSlice(), | |
251 operation->GetQuality()); | |
252 break; | |
253 | |
254 default: | |
255 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
256 } | |
257 } | |
258 }; | 258 }; |
259 | 259 |
260 void OrthancSlicesLoader::ISliceLoaderObserver::HandleMessage(IObservable& from, const IMessage& message) | |
261 { | |
262 switch (message.GetType()) | |
263 { | |
264 case MessageType_SliceGeometryReady: | |
265 OnSliceGeometryReady(dynamic_cast<OrthancSlicesLoader&>(from)); | |
266 break; | |
267 case MessageType_SliceGeometryError: | |
268 OnSliceGeometryError(dynamic_cast<OrthancSlicesLoader&>(from)); | |
269 break; | |
270 case MessageType_SliceImageReady: | |
271 { | |
272 const SliceImageReadyMessage& msg = dynamic_cast<const SliceImageReadyMessage&>(message); | |
273 OnSliceImageReady(dynamic_cast<OrthancSlicesLoader&>(from), | |
274 msg.sliceIndex_, | |
275 msg.slice_, | |
276 msg.image_, | |
277 msg.effectiveQuality_); | |
278 }; break; | |
279 case MessageType_SliceImageError: | |
280 { | |
281 const SliceImageErrorMessage& msg = dynamic_cast<const SliceImageErrorMessage&>(message); | |
282 OnSliceImageError(dynamic_cast<OrthancSlicesLoader&>(from), | |
283 msg.sliceIndex_, | |
284 msg.slice_, | |
285 msg.effectiveQuality_); | |
286 }; break; | |
287 default: | |
288 VLOG("unhandled message type" << message.GetType()); | |
289 } | |
290 } | |
260 | 291 |
261 | 292 |
262 void OrthancSlicesLoader::NotifySliceImageSuccess(const Operation& operation, | 293 void OrthancSlicesLoader::NotifySliceImageSuccess(const Operation& operation, |
263 std::auto_ptr<Orthanc::ImageAccessor>& image) const | 294 std::auto_ptr<Orthanc::ImageAccessor>& image) const |
264 { | 295 { |
266 { | 297 { |
267 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | 298 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); |
268 } | 299 } |
269 else | 300 else |
270 { | 301 { |
271 userCallback_.NotifySliceImageReady | 302 userCallback_.OnSliceImageReady |
272 (*this, operation.GetSliceIndex(), operation.GetSlice(), image, operation.GetQuality()); | 303 (*this, operation.GetSliceIndex(), operation.GetSlice(), image, operation.GetQuality()); |
273 } | 304 } |
274 } | 305 } |
275 | 306 |
276 | 307 |
277 void OrthancSlicesLoader::NotifySliceImageError(const Operation& operation) const | 308 void OrthancSlicesLoader::NotifySliceImageError(const Operation& operation) const |
278 { | 309 { |
279 userCallback_.NotifySliceImageError | 310 userCallback_.OnSliceImageError |
280 (*this, operation.GetSliceIndex(), operation.GetSlice(), operation.GetQuality()); | 311 (*this, operation.GetSliceIndex(), operation.GetSlice(), operation.GetQuality()); |
281 } | 312 } |
282 | 313 |
283 | 314 |
284 void OrthancSlicesLoader::SortAndFinalizeSlices() | 315 void OrthancSlicesLoader::SortAndFinalizeSlices() |
285 { | 316 { |
286 bool ok = false; | 317 bool ok = false; |
287 | 318 |
288 if (slices_.GetSliceCount() > 0) | 319 if (slices_.GetSliceCount() > 0) |
294 slices_.SetNormal(normal); | 325 slices_.SetNormal(normal); |
295 slices_.Sort(); | 326 slices_.Sort(); |
296 ok = true; | 327 ok = true; |
297 } | 328 } |
298 } | 329 } |
299 | 330 |
300 state_ = State_GeometryReady; | 331 state_ = State_GeometryReady; |
301 | 332 |
302 if (ok) | 333 if (ok) |
303 { | 334 { |
304 LOG(INFO) << "Loaded a series with " << slices_.GetSliceCount() << " slice(s)"; | 335 LOG(INFO) << "Loaded a series with " << slices_.GetSliceCount() << " slice(s)"; |
305 userCallback_.NotifyGeometryReady(*this); | 336 userCallback_.OnSliceGeometryReady(*this); |
306 } | 337 } |
307 else | 338 else |
308 { | 339 { |
309 LOG(ERROR) << "This series is empty"; | 340 LOG(ERROR) << "This series is empty"; |
310 userCallback_.NotifyGeometryError(*this); | 341 userCallback_.OnSliceGeometryError(*this); |
311 } | 342 } |
312 } | 343 } |
313 | 344 |
314 | 345 |
315 void OrthancSlicesLoader::ParseSeriesGeometry(const void* answer, | 346 void OrthancSlicesLoader::ParseSeriesGeometry(const void* answer, |
316 size_t size) | 347 size_t size) |
317 { | 348 { |
318 Json::Value series; | 349 Json::Value series; |
319 if (!MessagingToolbox::ParseJson(series, answer, size) || | 350 if (!MessagingToolbox::ParseJson(series, answer, size) || |
320 series.type() != Json::objectValue) | 351 series.type() != Json::objectValue) |
321 { | 352 { |
322 userCallback_.NotifyGeometryError(*this); | 353 userCallback_.OnSliceGeometryError(*this); |
323 return; | 354 return; |
324 } | 355 } |
325 | 356 |
326 Json::Value::Members instances = series.getMemberNames(); | 357 Json::Value::Members instances = series.getMemberNames(); |
327 | 358 |
328 slices_.Reserve(instances.size()); | 359 slices_.Reserve(instances.size()); |
329 | 360 |
330 for (size_t i = 0; i < instances.size(); i++) | 361 for (size_t i = 0; i < instances.size(); i++) |
331 { | 362 { |
332 OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]); | 363 OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]); |
333 | 364 |
334 Orthanc::DicomMap dicom; | 365 Orthanc::DicomMap dicom; |
335 MessagingToolbox::ConvertDataset(dicom, dataset); | 366 MessagingToolbox::ConvertDataset(dicom, dataset); |
336 | 367 |
337 unsigned int frames; | 368 unsigned int frames; |
338 if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES)) | 369 if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES)) |
339 { | 370 { |
340 frames = 1; | 371 frames = 1; |
341 } | 372 } |
342 | 373 |
343 for (unsigned int frame = 0; frame < frames; frame++) | 374 for (unsigned int frame = 0; frame < frames; frame++) |
344 { | 375 { |
345 std::auto_ptr<Slice> slice(new Slice); | 376 std::auto_ptr<Slice> slice(new Slice); |
346 if (slice->ParseOrthancFrame(dicom, instances[i], frame)) | 377 if (slice->ParseOrthancFrame(dicom, instances[i], frame)) |
347 { | 378 { |
351 { | 382 { |
352 LOG(WARNING) << "Skipping invalid frame " << frame << " within instance " << instances[i]; | 383 LOG(WARNING) << "Skipping invalid frame " << frame << " within instance " << instances[i]; |
353 } | 384 } |
354 } | 385 } |
355 } | 386 } |
356 | 387 |
357 SortAndFinalizeSlices(); | 388 SortAndFinalizeSlices(); |
358 } | 389 } |
359 | 390 |
360 | 391 |
361 void OrthancSlicesLoader::ParseInstanceGeometry(const std::string& instanceId, | 392 void OrthancSlicesLoader::ParseInstanceGeometry(const std::string& instanceId, |
362 const void* answer, | 393 const void* answer, |
363 size_t size) | 394 size_t size) |
364 { | 395 { |
365 Json::Value tags; | 396 Json::Value tags; |
366 if (!MessagingToolbox::ParseJson(tags, answer, size) || | 397 if (!MessagingToolbox::ParseJson(tags, answer, size) || |
367 tags.type() != Json::objectValue) | 398 tags.type() != Json::objectValue) |
368 { | 399 { |
369 userCallback_.NotifyGeometryError(*this); | 400 userCallback_.OnSliceGeometryError(*this); |
370 return; | 401 return; |
371 } | 402 } |
372 | 403 |
373 OrthancPlugins::FullOrthancDataset dataset(tags); | 404 OrthancPlugins::FullOrthancDataset dataset(tags); |
374 | 405 |
375 Orthanc::DicomMap dicom; | 406 Orthanc::DicomMap dicom; |
376 MessagingToolbox::ConvertDataset(dicom, dataset); | 407 MessagingToolbox::ConvertDataset(dicom, dataset); |
377 | 408 |
378 unsigned int frames; | 409 unsigned int frames; |
379 if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES)) | 410 if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES)) |
380 { | 411 { |
381 frames = 1; | 412 frames = 1; |
382 } | 413 } |
383 | 414 |
384 LOG(INFO) << "Instance " << instanceId << " contains " << frames << " frame(s)"; | 415 LOG(INFO) << "Instance " << instanceId << " contains " << frames << " frame(s)"; |
385 | 416 |
386 for (unsigned int frame = 0; frame < frames; frame++) | 417 for (unsigned int frame = 0; frame < frames; frame++) |
387 { | 418 { |
388 std::auto_ptr<Slice> slice(new Slice); | 419 std::auto_ptr<Slice> slice(new Slice); |
389 if (slice->ParseOrthancFrame(dicom, instanceId, frame)) | 420 if (slice->ParseOrthancFrame(dicom, instanceId, frame)) |
390 { | 421 { |
391 slices_.AddSlice(slice.release()); | 422 slices_.AddSlice(slice.release()); |
392 } | 423 } |
393 else | 424 else |
394 { | 425 { |
395 LOG(WARNING) << "Skipping invalid multi-frame instance " << instanceId; | 426 LOG(WARNING) << "Skipping invalid multi-frame instance " << instanceId; |
396 userCallback_.NotifyGeometryError(*this); | 427 userCallback_.OnSliceGeometryError(*this); |
397 return; | 428 return; |
398 } | 429 } |
399 } | 430 } |
400 | 431 |
401 SortAndFinalizeSlices(); | 432 SortAndFinalizeSlices(); |
402 } | 433 } |
403 | 434 |
404 | 435 |
405 void OrthancSlicesLoader::ParseFrameGeometry(const std::string& instanceId, | 436 void OrthancSlicesLoader::ParseFrameGeometry(const std::string& instanceId, |
406 unsigned int frame, | 437 unsigned int frame, |
407 const void* answer, | 438 const void* answer, |
408 size_t size) | 439 size_t size) |
409 { | 440 { |
410 Json::Value tags; | 441 Json::Value tags; |
411 if (!MessagingToolbox::ParseJson(tags, answer, size) || | 442 if (!MessagingToolbox::ParseJson(tags, answer, size) || |
412 tags.type() != Json::objectValue) | 443 tags.type() != Json::objectValue) |
413 { | 444 { |
414 userCallback_.NotifyGeometryError(*this); | 445 userCallback_.OnSliceGeometryError(*this); |
415 return; | 446 return; |
416 } | 447 } |
417 | 448 |
418 OrthancPlugins::FullOrthancDataset dataset(tags); | 449 OrthancPlugins::FullOrthancDataset dataset(tags); |
419 | 450 |
420 state_ = State_GeometryReady; | 451 state_ = State_GeometryReady; |
421 | 452 |
422 Orthanc::DicomMap dicom; | 453 Orthanc::DicomMap dicom; |
423 MessagingToolbox::ConvertDataset(dicom, dataset); | 454 MessagingToolbox::ConvertDataset(dicom, dataset); |
424 | 455 |
425 std::auto_ptr<Slice> slice(new Slice); | 456 std::auto_ptr<Slice> slice(new Slice); |
426 if (slice->ParseOrthancFrame(dicom, instanceId, frame)) | 457 if (slice->ParseOrthancFrame(dicom, instanceId, frame)) |
427 { | 458 { |
428 LOG(INFO) << "Loaded instance " << instanceId; | 459 LOG(INFO) << "Loaded instance " << instanceId; |
429 slices_.AddSlice(slice.release()); | 460 slices_.AddSlice(slice.release()); |
430 userCallback_.NotifyGeometryReady(*this); | 461 userCallback_.OnSliceGeometryReady(*this); |
431 } | 462 } |
432 else | 463 else |
433 { | 464 { |
434 LOG(WARNING) << "Skipping invalid instance " << instanceId; | 465 LOG(WARNING) << "Skipping invalid instance " << instanceId; |
435 userCallback_.NotifyGeometryError(*this); | 466 userCallback_.OnSliceGeometryError(*this); |
436 } | 467 } |
437 } | 468 } |
438 | 469 |
439 | 470 |
440 void OrthancSlicesLoader::ParseSliceImagePng(const Operation& operation, | 471 void OrthancSlicesLoader::ParseSliceImagePng(const Operation& operation, |
441 const void* answer, | 472 const void* answer, |
442 size_t size) | 473 size_t size) |
443 { | 474 { |
444 std::auto_ptr<Orthanc::ImageAccessor> image; | 475 std::auto_ptr<Orthanc::ImageAccessor> image; |
445 | 476 |
446 try | 477 try |
447 { | 478 { |
448 image.reset(new Orthanc::PngReader); | 479 image.reset(new Orthanc::PngReader); |
449 dynamic_cast<Orthanc::PngReader&>(*image).ReadFromMemory(answer, size); | 480 dynamic_cast<Orthanc::PngReader&>(*image).ReadFromMemory(answer, size); |
450 } | 481 } |
451 catch (Orthanc::OrthancException&) | 482 catch (Orthanc::OrthancException&) |
452 { | 483 { |
453 NotifySliceImageError(operation); | 484 NotifySliceImageError(operation); |
454 return; | 485 return; |
455 } | 486 } |
456 | 487 |
457 if (image->GetWidth() != operation.GetSlice().GetWidth() || | 488 if (image->GetWidth() != operation.GetSlice().GetWidth() || |
458 image->GetHeight() != operation.GetSlice().GetHeight()) | 489 image->GetHeight() != operation.GetSlice().GetHeight()) |
459 { | 490 { |
460 NotifySliceImageError(operation); | 491 NotifySliceImageError(operation); |
461 return; | 492 return; |
462 } | 493 } |
463 | 494 |
464 if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() == | 495 if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() == |
465 Orthanc::PixelFormat_SignedGrayscale16) | 496 Orthanc::PixelFormat_SignedGrayscale16) |
466 { | 497 { |
467 if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16) | 498 if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16) |
468 { | 499 { |
472 { | 503 { |
473 NotifySliceImageError(operation); | 504 NotifySliceImageError(operation); |
474 return; | 505 return; |
475 } | 506 } |
476 } | 507 } |
477 | 508 |
478 NotifySliceImageSuccess(operation, image); | 509 NotifySliceImageSuccess(operation, image); |
479 } | 510 } |
480 | 511 |
481 | 512 |
482 void OrthancSlicesLoader::ParseSliceImageJpeg(const Operation& operation, | 513 void OrthancSlicesLoader::ParseSliceImageJpeg(const Operation& operation, |
483 const void* answer, | 514 const void* answer, |
484 size_t size) | 515 size_t size) |
485 { | 516 { |
490 encoded["Orthanc"].type() != Json::objectValue) | 521 encoded["Orthanc"].type() != Json::objectValue) |
491 { | 522 { |
492 NotifySliceImageError(operation); | 523 NotifySliceImageError(operation); |
493 return; | 524 return; |
494 } | 525 } |
495 | 526 |
496 Json::Value& info = encoded["Orthanc"]; | 527 Json::Value& info = encoded["Orthanc"]; |
497 if (!info.isMember("PixelData") || | 528 if (!info.isMember("PixelData") || |
498 !info.isMember("Stretched") || | 529 !info.isMember("Stretched") || |
499 !info.isMember("Compression") || | 530 !info.isMember("Compression") || |
500 info["Compression"].type() != Json::stringValue || | 531 info["Compression"].type() != Json::stringValue || |
503 info["Compression"].asString() != "Jpeg") | 534 info["Compression"].asString() != "Jpeg") |
504 { | 535 { |
505 NotifySliceImageError(operation); | 536 NotifySliceImageError(operation); |
506 return; | 537 return; |
507 } | 538 } |
508 | 539 |
509 bool isSigned = false; | 540 bool isSigned = false; |
510 bool isStretched = info["Stretched"].asBool(); | 541 bool isStretched = info["Stretched"].asBool(); |
511 | 542 |
512 if (info.isMember("IsSigned")) | 543 if (info.isMember("IsSigned")) |
513 { | 544 { |
514 if (info["IsSigned"].type() != Json::booleanValue) | 545 if (info["IsSigned"].type() != Json::booleanValue) |
515 { | 546 { |
516 NotifySliceImageError(operation); | 547 NotifySliceImageError(operation); |
517 return; | 548 return; |
518 } | 549 } |
519 else | 550 else |
520 { | 551 { |
521 isSigned = info["IsSigned"].asBool(); | 552 isSigned = info["IsSigned"].asBool(); |
522 } | 553 } |
523 } | 554 } |
524 | 555 |
525 std::auto_ptr<Orthanc::ImageAccessor> reader; | 556 std::auto_ptr<Orthanc::ImageAccessor> reader; |
526 | 557 |
527 { | 558 { |
528 std::string jpeg; | 559 std::string jpeg; |
529 //Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString()); | 560 //Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString()); |
530 jpeg = base64_decode(info["PixelData"].asString()); | 561 jpeg = base64_decode(info["PixelData"].asString()); |
531 | 562 |
532 try | 563 try |
533 { | 564 { |
534 reader.reset(new Orthanc::JpegReader); | 565 reader.reset(new Orthanc::JpegReader); |
535 dynamic_cast<Orthanc::JpegReader&>(*reader).ReadFromMemory(jpeg); | 566 dynamic_cast<Orthanc::JpegReader&>(*reader).ReadFromMemory(jpeg); |
536 } | 567 } |
538 { | 569 { |
539 NotifySliceImageError(operation); | 570 NotifySliceImageError(operation); |
540 return; | 571 return; |
541 } | 572 } |
542 } | 573 } |
543 | 574 |
544 Orthanc::PixelFormat expectedFormat = | 575 Orthanc::PixelFormat expectedFormat = |
545 operation.GetSlice().GetConverter().GetExpectedPixelFormat(); | 576 operation.GetSlice().GetConverter().GetExpectedPixelFormat(); |
546 | 577 |
547 if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image | 578 if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image |
548 { | 579 { |
549 if (expectedFormat != Orthanc::PixelFormat_RGB24) | 580 if (expectedFormat != Orthanc::PixelFormat_RGB24) |
550 { | 581 { |
551 NotifySliceImageError(operation); | 582 NotifySliceImageError(operation); |
552 return; | 583 return; |
553 } | 584 } |
554 | 585 |
555 if (isSigned || isStretched) | 586 if (isSigned || isStretched) |
556 { | 587 { |
557 NotifySliceImageError(operation); | 588 NotifySliceImageError(operation); |
558 return; | 589 return; |
559 } | 590 } |
561 { | 592 { |
562 NotifySliceImageSuccess(operation, reader); | 593 NotifySliceImageSuccess(operation, reader); |
563 return; | 594 return; |
564 } | 595 } |
565 } | 596 } |
566 | 597 |
567 if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8) | 598 if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8) |
568 { | 599 { |
569 NotifySliceImageError(operation); | 600 NotifySliceImageError(operation); |
570 return; | 601 return; |
571 } | 602 } |
572 | 603 |
573 if (!isStretched) | 604 if (!isStretched) |
574 { | 605 { |
575 if (expectedFormat != reader->GetFormat()) | 606 if (expectedFormat != reader->GetFormat()) |
576 { | 607 { |
577 NotifySliceImageError(operation); | 608 NotifySliceImageError(operation); |
581 { | 612 { |
582 NotifySliceImageSuccess(operation, reader); | 613 NotifySliceImageSuccess(operation, reader); |
583 return; | 614 return; |
584 } | 615 } |
585 } | 616 } |
586 | 617 |
587 int32_t stretchLow = 0; | 618 int32_t stretchLow = 0; |
588 int32_t stretchHigh = 0; | 619 int32_t stretchHigh = 0; |
589 | 620 |
590 if (!info.isMember("StretchLow") || | 621 if (!info.isMember("StretchLow") || |
591 !info.isMember("StretchHigh") || | 622 !info.isMember("StretchHigh") || |
592 info["StretchLow"].type() != Json::intValue || | 623 info["StretchLow"].type() != Json::intValue || |
593 info["StretchHigh"].type() != Json::intValue) | 624 info["StretchHigh"].type() != Json::intValue) |
594 { | 625 { |
595 NotifySliceImageError(operation); | 626 NotifySliceImageError(operation); |
596 return; | 627 return; |
597 } | 628 } |
598 | 629 |
599 stretchLow = info["StretchLow"].asInt(); | 630 stretchLow = info["StretchLow"].asInt(); |
600 stretchHigh = info["StretchHigh"].asInt(); | 631 stretchHigh = info["StretchHigh"].asInt(); |
601 | 632 |
602 if (stretchLow < -32768 || | 633 if (stretchLow < -32768 || |
603 stretchHigh > 65535 || | 634 stretchHigh > 65535 || |
604 (stretchLow < 0 && stretchHigh > 32767)) | 635 (stretchLow < 0 && stretchHigh > 32767)) |
605 { | 636 { |
606 // This range cannot be represented with a uint16_t or an int16_t | 637 // This range cannot be represented with a uint16_t or an int16_t |
607 NotifySliceImageError(operation); | 638 NotifySliceImageError(operation); |
608 return; | 639 return; |
609 } | 640 } |
610 | 641 |
611 // Decode a grayscale JPEG 8bpp image coming from the Web viewer | 642 // Decode a grayscale JPEG 8bpp image coming from the Web viewer |
612 std::auto_ptr<Orthanc::ImageAccessor> image | 643 std::auto_ptr<Orthanc::ImageAccessor> image |
613 (new Orthanc::Image(expectedFormat, reader->GetWidth(), reader->GetHeight(), false)); | 644 (new Orthanc::Image(expectedFormat, reader->GetWidth(), reader->GetHeight(), false)); |
614 | 645 |
615 Orthanc::ImageProcessing::Convert(*image, *reader); | 646 Orthanc::ImageProcessing::Convert(*image, *reader); |
616 reader.reset(NULL); | 647 reader.reset(NULL); |
617 | 648 |
618 float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f; | 649 float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f; |
619 | 650 |
620 if (!LinearAlgebra::IsCloseToZero(scaling)) | 651 if (!LinearAlgebra::IsCloseToZero(scaling)) |
621 { | 652 { |
622 float offset = static_cast<float>(stretchLow) / scaling; | 653 float offset = static_cast<float>(stretchLow) / scaling; |
623 Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true); | 654 Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true); |
624 } | 655 } |
625 | 656 |
626 NotifySliceImageSuccess(operation, image); | 657 NotifySliceImageSuccess(operation, image); |
627 } | 658 } |
628 | 659 |
629 | 660 |
630 class StringImage : | 661 class StringImage : |
631 public Orthanc::ImageAccessor, | 662 public Orthanc::ImageAccessor, |
632 public boost::noncopyable | 663 public boost::noncopyable |
633 { | 664 { |
634 private: | 665 private: |
635 std::string buffer_; | 666 std::string buffer_; |
636 | 667 |
637 public: | 668 public: |
642 { | 673 { |
643 if (buffer.size() != Orthanc::GetBytesPerPixel(format) * width * height) | 674 if (buffer.size() != Orthanc::GetBytesPerPixel(format) * width * height) |
644 { | 675 { |
645 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | 676 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); |
646 } | 677 } |
647 | 678 |
648 buffer_.swap(buffer); // The source buffer is now empty | 679 buffer_.swap(buffer); // The source buffer is now empty |
649 | 680 |
650 void* data = (buffer_.empty() ? NULL : &buffer_[0]); | 681 void* data = (buffer_.empty() ? NULL : &buffer_[0]); |
651 | 682 |
652 AssignWritable(format, width, height, | 683 AssignWritable(format, width, height, |
653 Orthanc::GetBytesPerPixel(format) * width, data); | 684 Orthanc::GetBytesPerPixel(format) * width, data); |
654 } | 685 } |
655 }; | 686 }; |
656 | 687 |
657 | 688 |
658 void OrthancSlicesLoader::ParseSliceRawImage(const Operation& operation, | 689 void OrthancSlicesLoader::ParseSliceRawImage(const Operation& operation, |
659 const void* answer, | 690 const void* answer, |
660 size_t size) | 691 size_t size) |
661 { | 692 { |
662 Orthanc::GzipCompressor compressor; | 693 Orthanc::GzipCompressor compressor; |
663 | 694 |
664 std::string raw; | 695 std::string raw; |
665 compressor.Uncompress(raw, answer, size); | 696 compressor.Uncompress(raw, answer, size); |
666 | 697 |
667 const Orthanc::DicomImageInformation& info = operation.GetSlice().GetImageInformation(); | 698 const Orthanc::DicomImageInformation& info = operation.GetSlice().GetImageInformation(); |
668 | 699 |
675 raw.size() == info.GetWidth() * info.GetHeight() * 4) | 706 raw.size() == info.GetWidth() * info.GetHeight() * 4) |
676 { | 707 { |
677 // This is the case of RT-DOSE (uint32_t values) | 708 // This is the case of RT-DOSE (uint32_t values) |
678 | 709 |
679 std::auto_ptr<Orthanc::ImageAccessor> image | 710 std::auto_ptr<Orthanc::ImageAccessor> image |
680 (new StringImage(Orthanc::PixelFormat_Grayscale32, info.GetWidth(), | 711 (new StringImage(Orthanc::PixelFormat_Grayscale32, info.GetWidth(), |
681 info.GetHeight(), raw)); | 712 info.GetHeight(), raw)); |
682 | 713 |
683 // TODO - Only for big endian | 714 // TODO - Only for big endian |
684 for (unsigned int y = 0; y < image->GetHeight(); y++) | 715 for (unsigned int y = 0; y < image->GetHeight(); y++) |
685 { | 716 { |
686 uint32_t *p = reinterpret_cast<uint32_t*>(image->GetRow(y)); | 717 uint32_t *p = reinterpret_cast<uint32_t*>(image->GetRow(y)); |
687 for (unsigned int x = 0; x < image->GetWidth(); x++, p++) | 718 for (unsigned int x = 0; x < image->GetWidth(); x++, p++) |
688 { | 719 { |
689 *p = le32toh(*p); | 720 *p = le32toh(*p); |
690 } | 721 } |
691 } | 722 } |
692 | 723 |
693 NotifySliceImageSuccess(operation, image); | 724 NotifySliceImageSuccess(operation, image); |
694 } | 725 } |
695 else if (info.GetBitsAllocated() == 16 && | 726 else if (info.GetBitsAllocated() == 16 && |
696 info.GetBitsStored() == 16 && | 727 info.GetBitsStored() == 16 && |
697 info.GetHighBit() == 15 && | 728 info.GetHighBit() == 15 && |
699 !info.IsSigned() && | 730 !info.IsSigned() && |
700 info.GetPhotometricInterpretation() == Orthanc::PhotometricInterpretation_Monochrome2 && | 731 info.GetPhotometricInterpretation() == Orthanc::PhotometricInterpretation_Monochrome2 && |
701 raw.size() == info.GetWidth() * info.GetHeight() * 2) | 732 raw.size() == info.GetWidth() * info.GetHeight() * 2) |
702 { | 733 { |
703 std::auto_ptr<Orthanc::ImageAccessor> image | 734 std::auto_ptr<Orthanc::ImageAccessor> image |
704 (new StringImage(Orthanc::PixelFormat_Grayscale16, info.GetWidth(), | 735 (new StringImage(Orthanc::PixelFormat_Grayscale16, info.GetWidth(), |
705 info.GetHeight(), raw)); | 736 info.GetHeight(), raw)); |
706 | 737 |
707 // TODO - Big endian ? | 738 // TODO - Big endian ? |
708 | 739 |
709 NotifySliceImageSuccess(operation, image); | 740 NotifySliceImageSuccess(operation, image); |
710 } | 741 } |
711 else | 742 else |
712 { | 743 { |
713 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | 744 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); |
714 } | 745 } |
715 | 746 |
716 } | 747 } |
717 | 748 |
718 | 749 |
719 OrthancSlicesLoader::OrthancSlicesLoader(MessageBroker& broker, | 750 OrthancSlicesLoader::OrthancSlicesLoader(MessageBroker& broker, |
720 ICallback& callback, | 751 ISliceLoaderObserver& callback, |
721 IWebService& orthanc) : | 752 IWebService& orthanc) : |
722 webCallback_(new WebCallback(broker, *this)), | 753 webCallback_(new WebCallback(broker, *this)), |
723 userCallback_(callback), | 754 userCallback_(callback), |
724 orthanc_(orthanc), | 755 orthanc_(orthanc), |
725 state_(State_Initialization) | 756 state_(State_Initialization) |
726 { | 757 { |
727 } | 758 } |
728 | 759 |
729 | 760 |
730 void OrthancSlicesLoader::ScheduleLoadSeries(const std::string& seriesId) | 761 void OrthancSlicesLoader::ScheduleLoadSeries(const std::string& seriesId) |
731 { | 762 { |
732 if (state_ != State_Initialization) | 763 if (state_ != State_Initialization) |
733 { | 764 { |
738 state_ = State_LoadingGeometry; | 769 state_ = State_LoadingGeometry; |
739 std::string uri = "/series/" + seriesId + "/instances-tags"; | 770 std::string uri = "/series/" + seriesId + "/instances-tags"; |
740 orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSeriesGeometry()); | 771 orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSeriesGeometry()); |
741 } | 772 } |
742 } | 773 } |
743 | 774 |
744 | 775 |
745 void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId) | 776 void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId) |
746 { | 777 { |
747 if (state_ != State_Initialization) | 778 if (state_ != State_Initialization) |
748 { | 779 { |
749 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 780 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
750 } | 781 } |
751 else | 782 else |
752 { | 783 { |
753 state_ = State_LoadingGeometry; | 784 state_ = State_LoadingGeometry; |
754 | 785 |
755 // Tag "3004-000c" is "Grid Frame Offset Vector", which is | 786 // Tag "3004-000c" is "Grid Frame Offset Vector", which is |
756 // mandatory to read RT DOSE, but is too long to be returned by default | 787 // mandatory to read RT DOSE, but is too long to be returned by default |
757 std::string uri = "/instances/" + instanceId + "/tags?ignore-length=3004-000c"; | 788 std::string uri = "/instances/" + instanceId + "/tags?ignore-length=3004-000c"; |
758 orthanc_.ScheduleGetRequest | 789 orthanc_.ScheduleGetRequest |
759 (*webCallback_, uri, Operation::DownloadInstanceGeometry(instanceId)); | 790 (*webCallback_, uri, Operation::DownloadInstanceGeometry(instanceId)); |
760 } | 791 } |
761 } | 792 } |
762 | 793 |
763 | 794 |
764 void OrthancSlicesLoader::ScheduleLoadFrame(const std::string& instanceId, | 795 void OrthancSlicesLoader::ScheduleLoadFrame(const std::string& instanceId, |
765 unsigned int frame) | 796 unsigned int frame) |
766 { | 797 { |
767 if (state_ != State_Initialization) | 798 if (state_ != State_Initialization) |
768 { | 799 { |
771 else | 802 else |
772 { | 803 { |
773 state_ = State_LoadingGeometry; | 804 state_ = State_LoadingGeometry; |
774 std::string uri = "/instances/" + instanceId + "/tags"; | 805 std::string uri = "/instances/" + instanceId + "/tags"; |
775 orthanc_.ScheduleGetRequest | 806 orthanc_.ScheduleGetRequest |
776 (*webCallback_, uri, Operation::DownloadFrameGeometry(instanceId, frame)); | 807 (*webCallback_, uri, Operation::DownloadFrameGeometry(instanceId, frame)); |
777 } | 808 } |
778 } | 809 } |
779 | 810 |
780 | 811 |
781 bool OrthancSlicesLoader::IsGeometryReady() const | 812 bool OrthancSlicesLoader::IsGeometryReady() const |
782 { | 813 { |
783 return state_ == State_GeometryReady; | 814 return state_ == State_GeometryReady; |
784 } | 815 } |
785 | 816 |
786 | 817 |
787 size_t OrthancSlicesLoader::GetSliceCount() const | 818 size_t OrthancSlicesLoader::GetSliceCount() const |
788 { | 819 { |
789 if (state_ != State_GeometryReady) | 820 if (state_ != State_GeometryReady) |
790 { | 821 { |
791 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 822 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
792 } | 823 } |
793 | 824 |
794 return slices_.GetSliceCount(); | 825 return slices_.GetSliceCount(); |
795 } | 826 } |
796 | 827 |
797 | 828 |
798 const Slice& OrthancSlicesLoader::GetSlice(size_t index) const | 829 const Slice& OrthancSlicesLoader::GetSlice(size_t index) const |
799 { | 830 { |
800 if (state_ != State_GeometryReady) | 831 if (state_ != State_GeometryReady) |
801 { | 832 { |
802 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 833 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
803 } | 834 } |
804 | 835 |
805 return slices_.GetSlice(index); | 836 return slices_.GetSlice(index); |
806 } | 837 } |
807 | 838 |
808 | 839 |
809 bool OrthancSlicesLoader::LookupSlice(size_t& index, | 840 bool OrthancSlicesLoader::LookupSlice(size_t& index, |
810 const CoordinateSystem3D& plane) const | 841 const CoordinateSystem3D& plane) const |
811 { | 842 { |
812 if (state_ != State_GeometryReady) | 843 if (state_ != State_GeometryReady) |
813 { | 844 { |
814 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 845 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
815 } | 846 } |
816 | 847 |
817 return slices_.LookupSlice(index, plane); | 848 return slices_.LookupSlice(index, plane); |
818 } | 849 } |
819 | 850 |
820 | 851 |
821 void OrthancSlicesLoader::ScheduleSliceImagePng(const Slice& slice, | 852 void OrthancSlicesLoader::ScheduleSliceImagePng(const Slice& slice, |
822 size_t index) | 853 size_t index) |
823 { | 854 { |
824 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + | 855 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + |
825 boost::lexical_cast<std::string>(slice.GetFrame())); | 856 boost::lexical_cast<std::string>(slice.GetFrame())); |
826 | 857 |
827 switch (slice.GetConverter().GetExpectedPixelFormat()) | 858 switch (slice.GetConverter().GetExpectedPixelFormat()) |
828 { | 859 { |
829 case Orthanc::PixelFormat_RGB24: | 860 case Orthanc::PixelFormat_RGB24: |
830 uri += "/preview"; | 861 uri += "/preview"; |
831 break; | 862 break; |
832 | 863 |
833 case Orthanc::PixelFormat_Grayscale16: | 864 case Orthanc::PixelFormat_Grayscale16: |
834 uri += "/image-uint16"; | 865 uri += "/image-uint16"; |
835 break; | 866 break; |
836 | 867 |
837 case Orthanc::PixelFormat_SignedGrayscale16: | 868 case Orthanc::PixelFormat_SignedGrayscale16: |
838 uri += "/image-int16"; | 869 uri += "/image-int16"; |
839 break; | 870 break; |
840 | 871 |
841 default: | 872 default: |
842 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 873 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
843 } | 874 } |
844 | 875 |
845 orthanc_.ScheduleGetRequest(*webCallback_, uri, | 876 orthanc_.ScheduleGetRequest(*webCallback_, uri, |
846 Operation::DownloadSliceImage(index, slice, SliceImageQuality_Full)); | 877 Operation::DownloadSliceImage(index, slice, SliceImageQuality_Full)); |
847 } | 878 } |
848 | 879 |
849 | 880 |
850 void OrthancSlicesLoader::ScheduleSliceImageJpeg(const Slice& slice, | 881 void OrthancSlicesLoader::ScheduleSliceImageJpeg(const Slice& slice, |
851 size_t index, | 882 size_t index, |
852 SliceImageQuality quality) | 883 SliceImageQuality quality) |
853 { | 884 { |
854 unsigned int value; | 885 unsigned int value; |
855 | 886 |
856 switch (quality) | 887 switch (quality) |
857 { | 888 { |
858 case SliceImageQuality_Jpeg50: | 889 case SliceImageQuality_Jpeg50: |
859 value = 50; | 890 value = 50; |
860 break; | 891 break; |
861 | 892 |
862 case SliceImageQuality_Jpeg90: | 893 case SliceImageQuality_Jpeg90: |
863 value = 90; | 894 value = 90; |
864 break; | 895 break; |
865 | 896 |
866 case SliceImageQuality_Jpeg95: | 897 case SliceImageQuality_Jpeg95: |
867 value = 95; | 898 value = 95; |
868 break; | 899 break; |
869 | 900 |
870 default: | 901 default: |
871 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 902 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
872 } | 903 } |
873 | 904 |
874 // This requires the official Web viewer plugin to be installed! | 905 // This requires the official Web viewer plugin to be installed! |
875 std::string uri = ("/web-viewer/instances/jpeg" + | 906 std::string uri = ("/web-viewer/instances/jpeg" + |
876 boost::lexical_cast<std::string>(value) + | 907 boost::lexical_cast<std::string>(value) + |
877 "-" + slice.GetOrthancInstanceId() + "_" + | 908 "-" + slice.GetOrthancInstanceId() + "_" + |
878 boost::lexical_cast<std::string>(slice.GetFrame())); | 909 boost::lexical_cast<std::string>(slice.GetFrame())); |
879 | 910 |
880 orthanc_.ScheduleGetRequest(*webCallback_, uri, | 911 orthanc_.ScheduleGetRequest(*webCallback_, uri, |
881 Operation::DownloadSliceImage(index, slice, quality)); | 912 Operation::DownloadSliceImage(index, slice, quality)); |
882 } | 913 } |
883 | 914 |
884 | 915 |
885 | 916 |
886 void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index, | 917 void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index, |
887 SliceImageQuality quality) | 918 SliceImageQuality quality) |
888 { | 919 { |
889 if (state_ != State_GeometryReady) | 920 if (state_ != State_GeometryReady) |
890 { | 921 { |
891 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 922 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
892 } | 923 } |
893 | 924 |
894 const Slice& slice = GetSlice(index); | 925 const Slice& slice = GetSlice(index); |
895 | 926 |
896 if (slice.HasOrthancDecoding()) | 927 if (slice.HasOrthancDecoding()) |
897 { | 928 { |
898 if (quality == SliceImageQuality_Full) | 929 if (quality == SliceImageQuality_Full) |
899 { | 930 { |
900 ScheduleSliceImagePng(slice, index); | 931 ScheduleSliceImagePng(slice, index); |
904 ScheduleSliceImageJpeg(slice, index, quality); | 935 ScheduleSliceImageJpeg(slice, index, quality); |
905 } | 936 } |
906 } | 937 } |
907 else | 938 else |
908 { | 939 { |
909 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + | 940 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + |
910 boost::lexical_cast<std::string>(slice.GetFrame()) + "/raw.gz"); | 941 boost::lexical_cast<std::string>(slice.GetFrame()) + "/raw.gz"); |
911 orthanc_.ScheduleGetRequest(*webCallback_, uri, | 942 orthanc_.ScheduleGetRequest(*webCallback_, uri, |
912 Operation::DownloadSliceRawImage(index, slice)); | 943 Operation::DownloadSliceRawImage(index, slice)); |
913 } | 944 } |
914 } | 945 } |