Mercurial > hg > orthanc-stone
comparison UnitTestsSources/UnitTestsMain.cpp @ 73:ffa6dded91bd wasm
reorganization
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 24 May 2017 11:59:24 +0200 |
parents | c1cc3bdba18c |
children | f5f54ed8d307 |
comparison
equal
deleted
inserted
replaced
72:c1cc3bdba18c | 73:ffa6dded91bd |
---|---|
20 | 20 |
21 | 21 |
22 #include "gtest/gtest.h" | 22 #include "gtest/gtest.h" |
23 | 23 |
24 #include "../Framework/Toolbox/OrthancAsynchronousWebService.h" | 24 #include "../Framework/Toolbox/OrthancAsynchronousWebService.h" |
25 #include "../Framework/Toolbox/OrthancSlicesLoader.h" | |
25 #include "../Resources/Orthanc/Core/Logging.h" | 26 #include "../Resources/Orthanc/Core/Logging.h" |
26 #include "../Framework/Toolbox/OrthancSynchronousWebService.h" | |
27 #include "../Framework/Layers/OrthancFrameLayerSource.h" | |
28 #include "../Framework/Widgets/LayerWidget.h" | |
29 | |
30 | |
31 #include "../Resources/Orthanc/Core/Images/PngReader.h" | |
32 #include "../Framework/Toolbox/MessagingToolbox.h" | |
33 #include "../Framework/Toolbox/DicomFrameConverter.h" | |
34 | 27 |
35 #include <boost/lexical_cast.hpp> | 28 #include <boost/lexical_cast.hpp> |
36 #include <boost/date_time/posix_time/posix_time.hpp> | 29 #include <boost/date_time/posix_time/posix_time.hpp> |
37 #include <boost/thread/thread.hpp> | 30 #include <boost/thread/thread.hpp> |
38 | 31 |
39 namespace OrthancStone | 32 namespace OrthancStone |
40 { | 33 { |
41 class Slice | |
42 { | |
43 private: | |
44 enum Type | |
45 { | |
46 Type_Invalid, | |
47 Type_OrthancInstance | |
48 // TODO A slice could come from some DICOM file (URL) | |
49 }; | |
50 | |
51 Type type_; | |
52 std::string orthancInstanceId_; | |
53 unsigned int frame_; | |
54 SliceGeometry geometry_; | |
55 double pixelSpacingX_; | |
56 double pixelSpacingY_; | |
57 double thickness_; | |
58 unsigned int width_; | |
59 unsigned int height_; | |
60 DicomFrameConverter converter_; | |
61 | |
62 public: | |
63 Slice() : type_(Type_Invalid) | |
64 { | |
65 } | |
66 | |
67 bool ParseOrthancFrame(const OrthancPlugins::IDicomDataset& dataset, | |
68 const std::string& instanceId, | |
69 unsigned int frame) | |
70 { | |
71 OrthancPlugins::DicomDatasetReader reader(dataset); | |
72 | |
73 unsigned int frameCount; | |
74 if (!reader.GetUnsignedIntegerValue(frameCount, OrthancPlugins::DICOM_TAG_NUMBER_OF_FRAMES)) | |
75 { | |
76 frameCount = 1; // Assume instance with one frame | |
77 } | |
78 | |
79 if (frame >= frameCount) | |
80 { | |
81 return false; | |
82 } | |
83 | |
84 if (!reader.GetDoubleValue(thickness_, OrthancPlugins::DICOM_TAG_SLICE_THICKNESS)) | |
85 { | |
86 thickness_ = 100.0 * std::numeric_limits<double>::epsilon(); | |
87 } | |
88 | |
89 GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dataset); | |
90 | |
91 std::string position, orientation; | |
92 if (dataset.GetStringValue(position, OrthancPlugins::DICOM_TAG_IMAGE_POSITION_PATIENT) && | |
93 dataset.GetStringValue(orientation, OrthancPlugins::DICOM_TAG_IMAGE_ORIENTATION_PATIENT)) | |
94 { | |
95 geometry_ = SliceGeometry(position, orientation); | |
96 } | |
97 | |
98 if (reader.GetUnsignedIntegerValue(width_, OrthancPlugins::DICOM_TAG_COLUMNS) && | |
99 reader.GetUnsignedIntegerValue(height_, OrthancPlugins::DICOM_TAG_ROWS)) | |
100 { | |
101 orthancInstanceId_ = instanceId; | |
102 frame_ = frame; | |
103 converter_.ReadParameters(dataset); | |
104 | |
105 type_ = Type_OrthancInstance; | |
106 return true; | |
107 } | |
108 else | |
109 { | |
110 return false; | |
111 } | |
112 } | |
113 | |
114 bool IsOrthancInstance() const | |
115 { | |
116 return type_ == Type_OrthancInstance; | |
117 } | |
118 | |
119 const std::string GetOrthancInstanceId() const | |
120 { | |
121 if (type_ != Type_OrthancInstance) | |
122 { | |
123 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
124 } | |
125 | |
126 return orthancInstanceId_; | |
127 } | |
128 | |
129 unsigned int GetFrame() const | |
130 { | |
131 if (type_ == Type_Invalid) | |
132 { | |
133 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
134 } | |
135 | |
136 return frame_; | |
137 } | |
138 | |
139 const SliceGeometry& GetGeometry() const | |
140 { | |
141 if (type_ == Type_Invalid) | |
142 { | |
143 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
144 } | |
145 | |
146 return geometry_; | |
147 } | |
148 | |
149 double GetThickness() const | |
150 { | |
151 if (type_ == Type_Invalid) | |
152 { | |
153 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
154 } | |
155 | |
156 return thickness_; | |
157 } | |
158 | |
159 double GetPixelSpacingX() const | |
160 { | |
161 if (type_ == Type_Invalid) | |
162 { | |
163 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
164 } | |
165 | |
166 return pixelSpacingX_; | |
167 } | |
168 | |
169 double GetPixelSpacingY() const | |
170 { | |
171 if (type_ == Type_Invalid) | |
172 { | |
173 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
174 } | |
175 | |
176 return pixelSpacingY_; | |
177 } | |
178 | |
179 unsigned int GetWidth() const | |
180 { | |
181 if (type_ == Type_Invalid) | |
182 { | |
183 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
184 } | |
185 | |
186 return width_; | |
187 } | |
188 | |
189 unsigned int GetHeight() const | |
190 { | |
191 if (type_ == Type_Invalid) | |
192 { | |
193 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
194 } | |
195 | |
196 return height_; | |
197 } | |
198 | |
199 const DicomFrameConverter& GetConverter() const | |
200 { | |
201 if (type_ == Type_Invalid) | |
202 { | |
203 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
204 } | |
205 | |
206 return converter_; | |
207 } | |
208 }; | |
209 | |
210 | |
211 class SliceSorter : public boost::noncopyable | |
212 { | |
213 private: | |
214 class SliceWithDepth : public boost::noncopyable | |
215 { | |
216 private: | |
217 Slice slice_; | |
218 double depth_; | |
219 | |
220 public: | |
221 SliceWithDepth(const Slice& slice) : | |
222 slice_(slice), | |
223 depth_(0) | |
224 { | |
225 } | |
226 | |
227 void SetNormal(const Vector& normal) | |
228 { | |
229 depth_ = boost::numeric::ublas::inner_prod | |
230 (slice_.GetGeometry().GetOrigin(), normal); | |
231 } | |
232 | |
233 double GetDepth() const | |
234 { | |
235 return depth_; | |
236 } | |
237 | |
238 const Slice& GetSlice() const | |
239 { | |
240 return slice_; | |
241 } | |
242 }; | |
243 | |
244 struct Comparator | |
245 { | |
246 bool operator() (const SliceWithDepth* const& a, | |
247 const SliceWithDepth* const& b) const | |
248 { | |
249 return a->GetDepth() < b->GetDepth(); | |
250 } | |
251 }; | |
252 | |
253 typedef std::vector<SliceWithDepth*> Slices; | |
254 | |
255 Slices slices_; | |
256 bool hasNormal_; | |
257 | |
258 public: | |
259 SliceSorter() : hasNormal_(false) | |
260 { | |
261 } | |
262 | |
263 ~SliceSorter() | |
264 { | |
265 for (size_t i = 0; i < slices_.size(); i++) | |
266 { | |
267 assert(slices_[i] != NULL); | |
268 delete slices_[i]; | |
269 } | |
270 } | |
271 | |
272 void Reserve(size_t count) | |
273 { | |
274 slices_.reserve(count); | |
275 } | |
276 | |
277 void AddSlice(const Slice& slice) | |
278 { | |
279 slices_.push_back(new SliceWithDepth(slice)); | |
280 } | |
281 | |
282 size_t GetSliceCount() const | |
283 { | |
284 return slices_.size(); | |
285 } | |
286 | |
287 const Slice& GetSlice(size_t i) const | |
288 { | |
289 if (i >= slices_.size()) | |
290 { | |
291 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
292 } | |
293 | |
294 assert(slices_[i] != NULL); | |
295 return slices_[i]->GetSlice(); | |
296 } | |
297 | |
298 void SetNormal(const Vector& normal) | |
299 { | |
300 for (size_t i = 0; i < slices_.size(); i++) | |
301 { | |
302 slices_[i]->SetNormal(normal); | |
303 } | |
304 | |
305 hasNormal_ = true; | |
306 } | |
307 | |
308 void Sort() | |
309 { | |
310 if (!hasNormal_) | |
311 { | |
312 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
313 } | |
314 | |
315 Comparator comparator; | |
316 std::sort(slices_.begin(), slices_.end(), comparator); | |
317 } | |
318 | |
319 void FilterNormal(const Vector& normal) | |
320 { | |
321 size_t pos = 0; | |
322 | |
323 for (size_t i = 0; i < slices_.size(); i++) | |
324 { | |
325 if (GeometryToolbox::IsParallel(normal, slices_[i]->GetSlice().GetGeometry().GetNormal())) | |
326 { | |
327 // This slice is compatible with the selected normal | |
328 slices_[pos] = slices_[i]; | |
329 pos += 1; | |
330 } | |
331 else | |
332 { | |
333 delete slices_[i]; | |
334 slices_[i] = NULL; | |
335 } | |
336 } | |
337 | |
338 slices_.resize(pos); | |
339 } | |
340 | |
341 bool SelectNormal(Vector& normal) const | |
342 { | |
343 std::vector<Vector> normalCandidates; | |
344 std::vector<unsigned int> normalCount; | |
345 | |
346 bool found = false; | |
347 | |
348 for (size_t i = 0; !found && i < GetSliceCount(); i++) | |
349 { | |
350 const Vector& normal = GetSlice(i).GetGeometry().GetNormal(); | |
351 | |
352 bool add = true; | |
353 for (size_t j = 0; add && j < normalCandidates.size(); j++) // (*) | |
354 { | |
355 if (GeometryToolbox::IsParallel(normal, normalCandidates[j])) | |
356 { | |
357 normalCount[j] += 1; | |
358 add = false; | |
359 } | |
360 } | |
361 | |
362 if (add) | |
363 { | |
364 if (normalCount.size() > 2) | |
365 { | |
366 // To get linear-time complexity in (*). This heuristics | |
367 // allows the series to have one single frame that is | |
368 // not parallel to the others (such a frame could be a | |
369 // generated preview) | |
370 found = false; | |
371 } | |
372 else | |
373 { | |
374 normalCandidates.push_back(normal); | |
375 normalCount.push_back(1); | |
376 } | |
377 } | |
378 } | |
379 | |
380 for (size_t i = 0; !found && i < normalCandidates.size(); i++) | |
381 { | |
382 unsigned int count = normalCount[i]; | |
383 if (count == GetSliceCount() || | |
384 count + 1 == GetSliceCount()) | |
385 { | |
386 normal = normalCandidates[i]; | |
387 found = true; | |
388 } | |
389 } | |
390 | |
391 return found; | |
392 } | |
393 }; | |
394 | |
395 | |
396 | |
397 class OrthancSlicesLoader : public boost::noncopyable | |
398 { | |
399 public: | |
400 class ICallback : public boost::noncopyable | |
401 { | |
402 public: | |
403 virtual ~ICallback() | |
404 { | |
405 } | |
406 | |
407 virtual void NotifyGeometryReady(const OrthancSlicesLoader& loader) = 0; | |
408 | |
409 virtual void NotifyGeometryError(const OrthancSlicesLoader& loader) = 0; | |
410 | |
411 virtual void NotifySliceImageReady(const OrthancSlicesLoader& loader, | |
412 unsigned int sliceIndex, | |
413 const Slice& slice, | |
414 Orthanc::ImageAccessor* image) = 0; | |
415 | |
416 virtual void NotifySliceImageError(const OrthancSlicesLoader& loader, | |
417 unsigned int sliceIndex, | |
418 const Slice& slice) = 0; | |
419 }; | |
420 | |
421 private: | |
422 enum State | |
423 { | |
424 State_Error, | |
425 State_Initialization, | |
426 State_LoadingGeometry, | |
427 State_GeometryReady | |
428 }; | |
429 | |
430 enum Mode | |
431 { | |
432 Mode_SeriesGeometry, | |
433 Mode_InstanceGeometry, | |
434 Mode_LoadImage | |
435 }; | |
436 | |
437 class Operation : public Orthanc::IDynamicObject | |
438 { | |
439 private: | |
440 Mode mode_; | |
441 unsigned int frame_; | |
442 unsigned int sliceIndex_; | |
443 const Slice* slice_; | |
444 std::string instanceId_; | |
445 | |
446 Operation(Mode mode) : | |
447 mode_(mode) | |
448 { | |
449 } | |
450 | |
451 public: | |
452 Mode GetMode() const | |
453 { | |
454 return mode_; | |
455 } | |
456 | |
457 unsigned int GetSliceIndex() const | |
458 { | |
459 assert(mode_ == Mode_LoadImage); | |
460 return sliceIndex_; | |
461 } | |
462 | |
463 const Slice& GetSlice() const | |
464 { | |
465 assert(mode_ == Mode_LoadImage && slice_ != NULL); | |
466 return *slice_; | |
467 } | |
468 | |
469 unsigned int GetFrame() const | |
470 { | |
471 assert(mode_ == Mode_InstanceGeometry); | |
472 return frame_; | |
473 } | |
474 | |
475 const std::string& GetInstanceId() const | |
476 { | |
477 assert(mode_ == Mode_InstanceGeometry); | |
478 return instanceId_; | |
479 } | |
480 | |
481 static Operation* DownloadSeriesGeometry() | |
482 { | |
483 return new Operation(Mode_SeriesGeometry); | |
484 } | |
485 | |
486 static Operation* DownloadInstanceGeometry(const std::string& instanceId, | |
487 unsigned int frame) | |
488 { | |
489 std::auto_ptr<Operation> operation(new Operation(Mode_InstanceGeometry)); | |
490 operation->instanceId_ = instanceId; | |
491 operation->frame_ = frame; | |
492 return operation.release(); | |
493 } | |
494 | |
495 static Operation* DownloadSliceImage(unsigned int sliceIndex, | |
496 const Slice& slice) | |
497 { | |
498 std::auto_ptr<Operation> tmp(new Operation(Mode_LoadImage)); | |
499 tmp->sliceIndex_ = sliceIndex; | |
500 tmp->slice_ = &slice; | |
501 return tmp.release(); | |
502 } | |
503 }; | |
504 | |
505 | |
506 class WebCallback : public IWebService::ICallback | |
507 { | |
508 private: | |
509 OrthancSlicesLoader& that_; | |
510 | |
511 public: | |
512 WebCallback(OrthancSlicesLoader& that) : | |
513 that_(that) | |
514 { | |
515 } | |
516 | |
517 virtual void NotifySuccess(const std::string& uri, | |
518 const void* answer, | |
519 size_t answerSize, | |
520 Orthanc::IDynamicObject* payload) | |
521 { | |
522 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload)); | |
523 | |
524 switch (operation->GetMode()) | |
525 { | |
526 case Mode_SeriesGeometry: | |
527 that_.ParseSeriesGeometry(answer, answerSize); | |
528 break; | |
529 | |
530 case Mode_InstanceGeometry: | |
531 that_.ParseInstanceGeometry(operation->GetInstanceId(), | |
532 operation->GetFrame(), answer, answerSize); | |
533 break; | |
534 | |
535 case Mode_LoadImage: | |
536 that_.ParseSliceImage(*operation, answer, answerSize); | |
537 break; | |
538 | |
539 default: | |
540 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
541 } | |
542 } | |
543 | |
544 virtual void NotifyError(const std::string& uri, | |
545 Orthanc::IDynamicObject* payload) | |
546 { | |
547 std::auto_ptr<Operation> operation(dynamic_cast<Operation*>(payload)); | |
548 LOG(ERROR) << "Cannot download " << uri; | |
549 | |
550 switch (operation->GetMode()) | |
551 { | |
552 case Mode_SeriesGeometry: | |
553 that_.userCallback_.NotifyGeometryError(that_); | |
554 that_.state_ = State_Error; | |
555 break; | |
556 | |
557 case Mode_LoadImage: | |
558 that_.userCallback_.NotifySliceImageError(that_, operation->GetSliceIndex(), | |
559 operation->GetSlice()); | |
560 break; | |
561 | |
562 default: | |
563 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
564 } | |
565 } | |
566 }; | |
567 | |
568 | |
569 WebCallback webCallback_; | |
570 ICallback& userCallback_; | |
571 IWebService& orthanc_; | |
572 State state_; | |
573 SliceSorter slices_; | |
574 | |
575 | |
576 void ParseSeriesGeometry(const void* answer, | |
577 size_t size) | |
578 { | |
579 Json::Value series; | |
580 if (!MessagingToolbox::ParseJson(series, answer, size) || | |
581 series.type() != Json::objectValue) | |
582 { | |
583 userCallback_.NotifyGeometryError(*this); | |
584 return; | |
585 } | |
586 | |
587 Json::Value::Members instances = series.getMemberNames(); | |
588 | |
589 slices_.Reserve(instances.size()); | |
590 | |
591 for (size_t i = 0; i < instances.size(); i++) | |
592 { | |
593 OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]); | |
594 | |
595 Slice slice; | |
596 if (slice.ParseOrthancFrame(dataset, instances[i], 0 /* todo */)) | |
597 { | |
598 slices_.AddSlice(slice); | |
599 } | |
600 else | |
601 { | |
602 LOG(WARNING) << "Skipping invalid instance " << instances[i]; | |
603 } | |
604 } | |
605 | |
606 bool ok = false; | |
607 | |
608 if (slices_.GetSliceCount() > 0) | |
609 { | |
610 Vector normal; | |
611 if (slices_.SelectNormal(normal)) | |
612 { | |
613 slices_.FilterNormal(normal); | |
614 slices_.SetNormal(normal); | |
615 slices_.Sort(); | |
616 ok = true; | |
617 } | |
618 } | |
619 | |
620 state_ = State_GeometryReady; | |
621 | |
622 if (ok) | |
623 { | |
624 LOG(INFO) << "Loaded a series with " << slices_.GetSliceCount() << " slice(s)"; | |
625 userCallback_.NotifyGeometryReady(*this); | |
626 } | |
627 else | |
628 { | |
629 LOG(ERROR) << "This series is empty"; | |
630 userCallback_.NotifyGeometryError(*this); | |
631 } | |
632 } | |
633 | |
634 | |
635 void ParseInstanceGeometry(const std::string& instanceId, | |
636 unsigned int frame, | |
637 const void* answer, | |
638 size_t size) | |
639 { | |
640 Json::Value tags; | |
641 if (!MessagingToolbox::ParseJson(tags, answer, size) || | |
642 tags.type() != Json::objectValue) | |
643 { | |
644 userCallback_.NotifyGeometryError(*this); | |
645 return; | |
646 } | |
647 | |
648 OrthancPlugins::FullOrthancDataset dataset(tags); | |
649 | |
650 state_ = State_GeometryReady; | |
651 | |
652 Slice slice; | |
653 if (slice.ParseOrthancFrame(dataset, instanceId, frame)) | |
654 { | |
655 LOG(INFO) << "Loaded instance " << instanceId; | |
656 slices_.AddSlice(slice); | |
657 userCallback_.NotifyGeometryReady(*this); | |
658 } | |
659 else | |
660 { | |
661 LOG(WARNING) << "Skipping invalid instance " << instanceId; | |
662 userCallback_.NotifyGeometryError(*this); | |
663 } | |
664 } | |
665 | |
666 | |
667 void ParseSliceImage(const Operation& operation, | |
668 const void* answer, | |
669 size_t size) | |
670 { | |
671 std::auto_ptr<Orthanc::PngReader> image(new Orthanc::PngReader); | |
672 image->ReadFromMemory(answer, size); | |
673 | |
674 bool ok = (image->GetWidth() == operation.GetSlice().GetWidth() || | |
675 image->GetHeight() == operation.GetSlice().GetHeight()); | |
676 | |
677 if (ok && | |
678 operation.GetSlice().GetConverter().GetExpectedPixelFormat() == | |
679 Orthanc::PixelFormat_SignedGrayscale16) | |
680 { | |
681 if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16) | |
682 { | |
683 image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16); | |
684 } | |
685 else | |
686 { | |
687 ok = false; | |
688 } | |
689 } | |
690 | |
691 if (ok) | |
692 { | |
693 userCallback_.NotifySliceImageReady(*this, operation.GetSliceIndex(), | |
694 operation.GetSlice(), image.release()); | |
695 } | |
696 else | |
697 { | |
698 userCallback_.NotifySliceImageError(*this, operation.GetSliceIndex(), | |
699 operation.GetSlice()); | |
700 } | |
701 } | |
702 | |
703 | |
704 public: | |
705 OrthancSlicesLoader(ICallback& callback, | |
706 IWebService& orthanc) : | |
707 webCallback_(*this), | |
708 userCallback_(callback), | |
709 orthanc_(orthanc), | |
710 state_(State_Initialization) | |
711 { | |
712 } | |
713 | |
714 void ScheduleLoadSeries(const std::string& seriesId) | |
715 { | |
716 if (state_ != State_Initialization) | |
717 { | |
718 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
719 } | |
720 else | |
721 { | |
722 state_ = State_LoadingGeometry; | |
723 std::string uri = "/series/" + seriesId + "/instances-tags"; | |
724 orthanc_.ScheduleGetRequest(webCallback_, uri, Operation::DownloadSeriesGeometry()); | |
725 } | |
726 } | |
727 | |
728 void ScheduleLoadInstance(const std::string& instanceId, | |
729 unsigned int frame) | |
730 { | |
731 if (state_ != State_Initialization) | |
732 { | |
733 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
734 } | |
735 else | |
736 { | |
737 state_ = State_LoadingGeometry; | |
738 std::string uri = "/instances/" + instanceId + "/tags"; | |
739 orthanc_.ScheduleGetRequest | |
740 (webCallback_, uri, Operation::DownloadInstanceGeometry(instanceId, frame)); | |
741 } | |
742 } | |
743 | |
744 size_t GetSliceCount() const | |
745 { | |
746 if (state_ != State_GeometryReady) | |
747 { | |
748 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
749 } | |
750 | |
751 return slices_.GetSliceCount(); | |
752 } | |
753 | |
754 const Slice& GetSlice(size_t index) const | |
755 { | |
756 if (state_ != State_GeometryReady) | |
757 { | |
758 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
759 } | |
760 | |
761 return slices_.GetSlice(index); | |
762 } | |
763 | |
764 void ScheduleLoadSliceImage(size_t index) | |
765 { | |
766 if (state_ != State_GeometryReady) | |
767 { | |
768 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
769 } | |
770 else | |
771 { | |
772 const Slice& slice = GetSlice(index); | |
773 | |
774 std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + | |
775 boost::lexical_cast<std::string>(slice.GetFrame())); | |
776 | |
777 switch (slice.GetConverter().GetExpectedPixelFormat()) | |
778 { | |
779 case Orthanc::PixelFormat_RGB24: | |
780 uri += "/preview"; | |
781 break; | |
782 | |
783 case Orthanc::PixelFormat_Grayscale16: | |
784 uri += "/image-uint16"; | |
785 break; | |
786 | |
787 case Orthanc::PixelFormat_SignedGrayscale16: | |
788 uri += "/image-int16"; | |
789 break; | |
790 | |
791 default: | |
792 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
793 } | |
794 | |
795 orthanc_.ScheduleGetRequest(webCallback_, uri, Operation::DownloadSliceImage(index, slice)); | |
796 } | |
797 } | |
798 }; | |
799 | |
800 | |
801 class Tata : public OrthancSlicesLoader::ICallback | 34 class Tata : public OrthancSlicesLoader::ICallback |
802 { | 35 { |
803 public: | 36 public: |
804 virtual void NotifyGeometryReady(const OrthancSlicesLoader& loader) | 37 virtual void NotifyGeometryReady(const OrthancSlicesLoader& loader) |
805 { | 38 { |