Mercurial > hg > orthanc-stone
comparison Framework/dev.h @ 318:3a4ca166fafa am-2
ImageAccessor refactoring + implemented Image Cache in SmartLoader
author | am@osimis.io |
---|---|
date | Mon, 08 Oct 2018 17:10:08 +0200 |
parents | b4abaeb783b1 |
children | 8716176ff7f0 |
comparison
equal
deleted
inserted
replaced
317:b66d13708f40 | 318:3a4ca166fafa |
---|---|
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 |
39 | 39 |
40 | 40 |
41 namespace OrthancStone | 41 namespace OrthancStone |
42 { | 42 { |
43 // TODO: Handle errors while loading | 43 // TODO: Handle errors while loading |
44 class OrthancVolumeImage : | 44 class OrthancVolumeImage : |
45 public SlicedVolumeBase, | 45 public SlicedVolumeBase, |
46 public OrthancStone::IObserver | 46 public OrthancStone::IObserver |
47 { | 47 { |
48 private: | 48 private: |
49 OrthancSlicesLoader loader_; | 49 OrthancSlicesLoader loader_; |
50 std::auto_ptr<ImageBuffer3D> image_; | 50 std::auto_ptr<ImageBuffer3D> image_; |
51 std::auto_ptr<DownloadStack> downloadStack_; | 51 std::auto_ptr<DownloadStack> downloadStack_; |
52 bool computeRange_; | 52 bool computeRange_; |
62 loader_.ScheduleLoadSliceImage(slice, SliceImageQuality_Jpeg90); | 62 loader_.ScheduleLoadSliceImage(slice, SliceImageQuality_Jpeg90); |
63 } | 63 } |
64 } | 64 } |
65 | 65 |
66 | 66 |
67 static bool IsCompatible(const Slice& a, | 67 static bool IsCompatible(const Slice& a, |
68 const Slice& b) | 68 const Slice& b) |
69 { | 69 { |
70 if (!GeometryToolbox::IsParallel(a.GetGeometry().GetNormal(), | 70 if (!GeometryToolbox::IsParallel(a.GetGeometry().GetNormal(), |
71 b.GetGeometry().GetNormal())) | 71 b.GetGeometry().GetNormal())) |
72 { | 72 { |
96 | 96 |
97 return true; | 97 return true; |
98 } | 98 } |
99 | 99 |
100 | 100 |
101 static double GetDistance(const Slice& a, | 101 static double GetDistance(const Slice& a, |
102 const Slice& b) | 102 const Slice& b) |
103 { | 103 { |
104 return fabs(a.GetGeometry().ProjectAlongNormal(a.GetGeometry().GetOrigin()) - | 104 return fabs(a.GetGeometry().ProjectAlongNormal(a.GetGeometry().GetOrigin()) - |
105 a.GetGeometry().ProjectAlongNormal(b.GetGeometry().GetOrigin())); | 105 a.GetGeometry().ProjectAlongNormal(b.GetGeometry().GetOrigin())); |
106 } | 106 } |
107 | 107 |
108 | 108 |
109 void OnSliceGeometryReady(const OrthancSlicesLoader& loader) | 109 void OnSliceGeometryReady(const OrthancSlicesLoader& loader) |
172 | 172 |
173 SlicedVolumeBase::NotifyGeometryReady(); | 173 SlicedVolumeBase::NotifyGeometryReady(); |
174 } | 174 } |
175 | 175 |
176 virtual void OnSliceImageReady(const OrthancSlicesLoader& loader, | 176 virtual void OnSliceImageReady(const OrthancSlicesLoader& loader, |
177 unsigned int sliceIndex, | 177 unsigned int sliceIndex, |
178 const Slice& slice, | 178 const Slice& slice, |
179 std::auto_ptr<Orthanc::ImageAccessor>& image, | 179 const boost::shared_ptr<Orthanc::ImageAccessor>& image, |
180 SliceImageQuality quality) | 180 SliceImageQuality quality) |
181 { | 181 { |
182 { | 182 { |
183 ImageBuffer3D::SliceWriter writer(*image_, VolumeProjection_Axial, sliceIndex); | 183 ImageBuffer3D::SliceWriter writer(*image_, VolumeProjection_Axial, sliceIndex); |
184 Orthanc::ImageProcessing::Copy(writer.GetAccessor(), *image); | 184 Orthanc::ImageProcessing::Copy(writer.GetAccessor(), *image); |
185 } | 185 } |
219 msg.image_, | 219 msg.image_, |
220 msg.effectiveQuality_); | 220 msg.effectiveQuality_); |
221 }; break; | 221 }; break; |
222 case MessageType_SliceLoader_ImageError: | 222 case MessageType_SliceLoader_ImageError: |
223 { | 223 { |
224 const OrthancSlicesLoader::SliceImageErrorMessage& msg = dynamic_cast<const OrthancSlicesLoader::SliceImageErrorMessage&>(message); | 224 const OrthancSlicesLoader::SliceImageErrorMessage& msg = dynamic_cast<const OrthancSlicesLoader::SliceImageErrorMessage&>(message); |
225 LOG(ERROR) << "Cannot download slice " << msg.sliceIndex_ << " in a volume image"; | 225 LOG(ERROR) << "Cannot download slice " << msg.sliceIndex_ << " in a volume image"; |
226 ScheduleSliceDownload(); | 226 ScheduleSliceDownload(); |
227 }; break; | 227 }; break; |
228 default: | 228 default: |
229 VLOG("unhandled message type" << message.GetType()); | 229 VLOG("unhandled message type" << message.GetType()); |
230 } | 230 } |
231 } | 231 } |
232 | 232 |
233 public: | 233 public: |
234 OrthancVolumeImage(MessageBroker& broker, | 234 OrthancVolumeImage(MessageBroker& broker, |
235 OrthancApiClient& orthanc, | 235 OrthancApiClient& orthanc, |
236 bool computeRange) : | 236 bool computeRange) : |
237 OrthancStone::IObserver(broker), | 237 OrthancStone::IObserver(broker), |
238 loader_(broker, orthanc), | 238 loader_(broker, orthanc), |
239 computeRange_(computeRange), | 239 computeRange_(computeRange), |
240 pendingSlices_(0) | 240 pendingSlices_(0) |
241 { | 241 { |
242 // TODO: replace with new callables loader_.RegisterObserver(*this); | 242 // TODO: replace with new callables loader_.RegisterObserver(*this); |
243 } | 243 } |
244 | 244 |
245 void ScheduleLoadSeries(const std::string& seriesId) | 245 void ScheduleLoadSeries(const std::string& seriesId) |
246 { | 246 { |
247 loader_.ScheduleLoadSeries(seriesId); | 247 loader_.ScheduleLoadSeries(seriesId); |
369 Vector origin = axial.GetGeometry().GetOrigin(); | 369 Vector origin = axial.GetGeometry().GetOrigin(); |
370 origin += (static_cast<double>(volume.GetSliceCount() - 1) * | 370 origin += (static_cast<double>(volume.GetSliceCount() - 1) * |
371 axialThickness * axial.GetGeometry().GetNormal()); | 371 axialThickness * axial.GetGeometry().GetNormal()); |
372 | 372 |
373 reference_ = CoordinateSystem3D(origin, | 373 reference_ = CoordinateSystem3D(origin, |
374 axial.GetGeometry().GetAxisX(), | 374 axial.GetGeometry().GetAxisX(), |
375 -axial.GetGeometry().GetNormal()); | 375 -axial.GetGeometry().GetNormal()); |
376 } | 376 } |
377 | 377 |
378 void SetupSagittal(const OrthancVolumeImage& volume) | 378 void SetupSagittal(const OrthancVolumeImage& volume) |
379 { | 379 { |
391 Vector origin = axial.GetGeometry().GetOrigin(); | 391 Vector origin = axial.GetGeometry().GetOrigin(); |
392 origin += (static_cast<double>(volume.GetSliceCount() - 1) * | 392 origin += (static_cast<double>(volume.GetSliceCount() - 1) * |
393 axialThickness * axial.GetGeometry().GetNormal()); | 393 axialThickness * axial.GetGeometry().GetNormal()); |
394 | 394 |
395 reference_ = CoordinateSystem3D(origin, | 395 reference_ = CoordinateSystem3D(origin, |
396 axial.GetGeometry().GetAxisY(), | 396 axial.GetGeometry().GetAxisY(), |
397 axial.GetGeometry().GetNormal()); | 397 axial.GetGeometry().GetNormal()); |
398 } | 398 } |
399 | 399 |
400 public: | 400 public: |
401 VolumeImageGeometry(const OrthancVolumeImage& volume, | 401 VolumeImageGeometry(const OrthancVolumeImage& volume, |
408 | 408 |
409 converter_ = volume.GetSlice(0).GetConverter(); | 409 converter_ = volume.GetSlice(0).GetConverter(); |
410 | 410 |
411 switch (projection) | 411 switch (projection) |
412 { | 412 { |
413 case VolumeProjection_Axial: | 413 case VolumeProjection_Axial: |
414 SetupAxial(volume); | 414 SetupAxial(volume); |
415 break; | 415 break; |
416 | 416 |
417 case VolumeProjection_Coronal: | 417 case VolumeProjection_Coronal: |
418 SetupCoronal(volume); | 418 SetupCoronal(volume); |
419 break; | 419 break; |
420 | 420 |
421 case VolumeProjection_Sagittal: | 421 case VolumeProjection_Sagittal: |
422 SetupSagittal(volume); | 422 SetupSagittal(volume); |
423 break; | 423 break; |
424 | 424 |
425 default: | 425 default: |
426 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 426 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
427 } | 427 } |
428 } | 428 } |
429 | 429 |
430 size_t GetSliceCount() const | 430 size_t GetSliceCount() const |
431 { | 431 { |
485 }; | 485 }; |
486 | 486 |
487 | 487 |
488 | 488 |
489 class VolumeImageSource : | 489 class VolumeImageSource : |
490 public LayerSourceBase, | 490 public LayerSourceBase, |
491 private ISlicedVolume::IObserver | 491 private ISlicedVolume::IObserver |
492 { | 492 { |
493 private: | 493 private: |
494 OrthancVolumeImage& volume_; | 494 OrthancVolumeImage& volume_; |
495 std::auto_ptr<VolumeImageGeometry> axialGeometry_; | 495 std::auto_ptr<VolumeImageGeometry> axialGeometry_; |
496 std::auto_ptr<VolumeImageGeometry> coronalGeometry_; | 496 std::auto_ptr<VolumeImageGeometry> coronalGeometry_; |
510 coronalGeometry_.reset(new VolumeImageGeometry(volume_, VolumeProjection_Coronal)); | 510 coronalGeometry_.reset(new VolumeImageGeometry(volume_, VolumeProjection_Coronal)); |
511 sagittalGeometry_.reset(new VolumeImageGeometry(volume_, VolumeProjection_Sagittal)); | 511 sagittalGeometry_.reset(new VolumeImageGeometry(volume_, VolumeProjection_Sagittal)); |
512 | 512 |
513 LayerSourceBase::NotifyGeometryReady(); | 513 LayerSourceBase::NotifyGeometryReady(); |
514 } | 514 } |
515 | 515 |
516 virtual void NotifyGeometryError(const ISlicedVolume& volume) | 516 virtual void NotifyGeometryError(const ISlicedVolume& volume) |
517 { | 517 { |
518 LayerSourceBase::NotifyGeometryError(); | 518 LayerSourceBase::NotifyGeometryError(); |
519 } | 519 } |
520 | 520 |
521 virtual void NotifyContentChange(const ISlicedVolume& volume) | 521 virtual void NotifyContentChange(const ISlicedVolume& volume) |
522 { | 522 { |
523 LayerSourceBase::NotifyContentChange(); | 523 LayerSourceBase::NotifyContentChange(); |
524 } | 524 } |
525 | 525 |
544 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 544 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
545 } | 545 } |
546 | 546 |
547 switch (projection) | 547 switch (projection) |
548 { | 548 { |
549 case VolumeProjection_Axial: | 549 case VolumeProjection_Axial: |
550 return *axialGeometry_; | 550 return *axialGeometry_; |
551 | 551 |
552 case VolumeProjection_Sagittal: | 552 case VolumeProjection_Sagittal: |
553 return *sagittalGeometry_; | 553 return *sagittalGeometry_; |
554 | 554 |
555 case VolumeProjection_Coronal: | 555 case VolumeProjection_Coronal: |
556 return *coronalGeometry_; | 556 return *coronalGeometry_; |
557 | 557 |
558 default: | 558 default: |
559 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | 559 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); |
560 } | 560 } |
561 } | 561 } |
562 | 562 |
563 | 563 |
564 bool DetectProjection(VolumeProjection& projection, | 564 bool DetectProjection(VolumeProjection& projection, |
611 !DetectProjection(projection, viewportSlice)) | 611 !DetectProjection(projection, viewportSlice)) |
612 { | 612 { |
613 return false; | 613 return false; |
614 } | 614 } |
615 else | 615 else |
616 { | 616 { |
617 // As the slices of the volumic image are arranged in a box, | 617 // As the slices of the volumic image are arranged in a box, |
618 // we only consider one single reference slice (the one with index 0). | 618 // we only consider one single reference slice (the one with index 0). |
619 std::auto_ptr<Slice> slice(GetProjectionGeometry(projection).GetSlice(0)); | 619 std::auto_ptr<Slice> slice(GetProjectionGeometry(projection).GetSlice(0)); |
620 slice->GetExtent(points); | 620 slice->GetExtent(points); |
621 | 621 |
648 frame.reset(Orthanc::Image::Clone(reader.GetAccessor())); | 648 frame.reset(Orthanc::Image::Clone(reader.GetAccessor())); |
649 } | 649 } |
650 | 650 |
651 std::auto_ptr<Slice> slice(geometry.GetSlice(closest)); | 651 std::auto_ptr<Slice> slice(geometry.GetSlice(closest)); |
652 LayerSourceBase::NotifyLayerReady( | 652 LayerSourceBase::NotifyLayerReady( |
653 FrameRenderer::CreateRenderer(frame.release(), *slice, isFullQuality), | 653 FrameRenderer::CreateRenderer(frame.release(), *slice, isFullQuality), |
654 //new SliceOutlineRenderer(slice), | 654 //new SliceOutlineRenderer(slice), |
655 slice->GetGeometry(), false); | 655 slice->GetGeometry(), false); |
656 return; | 656 return; |
657 } | 657 } |
658 } | 658 } |
659 | 659 |
660 // Error | 660 // Error |
663 } | 663 } |
664 }; | 664 }; |
665 | 665 |
666 | 666 |
667 class VolumeImageInteractor : | 667 class VolumeImageInteractor : |
668 public IWorldSceneInteractor, | 668 public IWorldSceneInteractor, |
669 protected ISlicedVolume::IObserver | 669 protected ISlicedVolume::IObserver |
670 { | 670 { |
671 private: | 671 private: |
672 LayerWidget& widget_; | 672 LayerWidget& widget_; |
673 VolumeProjection projection_; | 673 VolumeProjection projection_; |
674 std::auto_ptr<VolumeImageGeometry> slices_; | 674 std::auto_ptr<VolumeImageGeometry> slices_; |
685 SetSlice(slices_->GetSliceCount() / 2); | 685 SetSlice(slices_->GetSliceCount() / 2); |
686 | 686 |
687 widget_.SetDefaultView(); | 687 widget_.SetDefaultView(); |
688 } | 688 } |
689 } | 689 } |
690 | 690 |
691 virtual void NotifyGeometryError(const ISlicedVolume& volume) | 691 virtual void NotifyGeometryError(const ISlicedVolume& volume) |
692 { | 692 { |
693 } | 693 } |
694 | 694 |
695 virtual void NotifyContentChange(const ISlicedVolume& volume) | 695 virtual void NotifyContentChange(const ISlicedVolume& volume) |
696 { | 696 { |
697 } | 697 } |
698 | 698 |
699 virtual void NotifySliceChange(const ISlicedVolume& volume, | 699 virtual void NotifySliceChange(const ISlicedVolume& volume, |
729 MouseWheelDirection direction, | 729 MouseWheelDirection direction, |
730 KeyboardModifiers modifiers, | 730 KeyboardModifiers modifiers, |
731 IStatusBar* statusBar) | 731 IStatusBar* statusBar) |
732 { | 732 { |
733 int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1); | 733 int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1); |
734 | 734 |
735 switch (direction) | 735 switch (direction) |
736 { | 736 { |
737 case MouseWheelDirection_Up: | 737 case MouseWheelDirection_Up: |
738 OffsetSlice(-scale); | 738 OffsetSlice(-scale); |
739 break; | 739 break; |
740 | 740 |
741 case MouseWheelDirection_Down: | 741 case MouseWheelDirection_Down: |
742 OffsetSlice(scale); | 742 OffsetSlice(scale); |
743 break; | 743 break; |
744 | 744 |
745 default: | 745 default: |
746 break; | 746 break; |
747 } | 747 } |
748 } | 748 } |
749 | 749 |
750 virtual void KeyPressed(WorldSceneWidget& widget, | 750 virtual void KeyPressed(WorldSceneWidget& widget, |
751 char key, | 751 char key, |
752 KeyboardModifiers modifiers, | 752 KeyboardModifiers modifiers, |
753 IStatusBar* statusBar) | 753 IStatusBar* statusBar) |
754 { | 754 { |
755 switch (key) | 755 switch (key) |
756 { | 756 { |
757 case 's': | 757 case 's': |
758 widget.SetDefaultView(); | 758 widget.SetDefaultView(); |
759 break; | 759 break; |
760 | 760 |
761 default: | 761 default: |
762 break; | 762 break; |
763 } | 763 } |
764 } | 764 } |
765 | 765 |
766 public: | 766 public: |
767 VolumeImageInteractor(OrthancVolumeImage& volume, | 767 VolumeImageInteractor(OrthancVolumeImage& volume, |
768 LayerWidget& widget, | 768 LayerWidget& widget, |
769 VolumeProjection projection) : | 769 VolumeProjection projection) : |
770 widget_(widget), | 770 widget_(widget), |
805 if (slice >= static_cast<int>(slices_->GetSliceCount())) | 805 if (slice >= static_cast<int>(slices_->GetSliceCount())) |
806 { | 806 { |
807 slice = slices_->GetSliceCount() - 1; | 807 slice = slices_->GetSliceCount() - 1; |
808 } | 808 } |
809 | 809 |
810 if (slice != static_cast<int>(slice_)) | 810 if (slice != static_cast<int>(slice_)) |
811 { | 811 { |
812 SetSlice(slice); | 812 SetSlice(slice); |
813 } | 813 } |
814 } | 814 } |
815 } | 815 } |
816 | 816 |
817 void SetSlice(size_t slice) | 817 void SetSlice(size_t slice) |
818 { | 818 { |
819 if (slices_.get() != NULL) | 819 if (slices_.get() != NULL) |
820 { | 820 { |
821 slice_ = slice; | 821 slice_ = slice; |
854 Vector p, d; | 854 Vector p, d; |
855 | 855 |
856 const CoordinateSystem3D& slice = otherPlane_.GetSlice(); | 856 const CoordinateSystem3D& slice = otherPlane_.GetSlice(); |
857 | 857 |
858 // Compute the line of intersection between the two slices | 858 // Compute the line of intersection between the two slices |
859 if (!GeometryToolbox::IntersectTwoPlanes(p, d, | 859 if (!GeometryToolbox::IntersectTwoPlanes(p, d, |
860 slice.GetOrigin(), slice.GetNormal(), | 860 slice.GetOrigin(), slice.GetNormal(), |
861 viewportSlice.GetOrigin(), viewportSlice.GetNormal())) | 861 viewportSlice.GetOrigin(), viewportSlice.GetNormal())) |
862 { | 862 { |
863 // The two slice are parallel, don't try and display the intersection | 863 // The two slice are parallel, don't try and display the intersection |
864 NotifyLayerReady(NULL, reference.GetGeometry(), false); | 864 NotifyLayerReady(NULL, reference.GetGeometry(), false); |
869 viewportSlice.ProjectPoint(x1, y1, p); | 869 viewportSlice.ProjectPoint(x1, y1, p); |
870 viewportSlice.ProjectPoint(x2, y2, p + 1000.0 * d); | 870 viewportSlice.ProjectPoint(x2, y2, p + 1000.0 * d); |
871 | 871 |
872 const Extent2D extent = otherPlane_.GetSceneExtent(); | 872 const Extent2D extent = otherPlane_.GetSceneExtent(); |
873 | 873 |
874 if (GeometryToolbox::ClipLineToRectangle(x1, y1, x2, y2, | 874 if (GeometryToolbox::ClipLineToRectangle(x1, y1, x2, y2, |
875 x1, y1, x2, y2, | 875 x1, y1, x2, y2, |
876 extent.GetX1(), extent.GetY1(), | 876 extent.GetX1(), extent.GetY1(), |
877 extent.GetX2(), extent.GetY2())) | 877 extent.GetX2(), extent.GetY2())) |
878 { | 878 { |
879 NotifyLayerReady(new LineLayerRenderer(x1, y1, x2, y2, slice), reference.GetGeometry(), false); | 879 NotifyLayerReady(new LineLayerRenderer(x1, y1, x2, y2, slice), reference.GetGeometry(), false); |
882 { | 882 { |
883 // Parallel slices | 883 // Parallel slices |
884 NotifyLayerReady(NULL, reference.GetGeometry(), false); | 884 NotifyLayerReady(NULL, reference.GetGeometry(), false); |
885 } | 885 } |
886 } | 886 } |
887 } | 887 } |
888 }; | 888 }; |
889 } | 889 } |