comparison Samples/Sdl/Loader.cpp @ 760:1181e1ad98ec

progressive loading working
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 22 May 2019 18:34:06 +0200
parents 774681b2c77c
children 07adcffba38c 26f4345e771e
comparison
equal deleted inserted replaced
759:774681b2c77c 760:1181e1ad98ec
28 #include "../../Framework/Oracle/OracleCommandExceptionMessage.h" 28 #include "../../Framework/Oracle/OracleCommandExceptionMessage.h"
29 29
30 // From Stone 30 // From Stone
31 #include "../../Framework/Loaders/BasicFetchingItemsSorter.h" 31 #include "../../Framework/Loaders/BasicFetchingItemsSorter.h"
32 #include "../../Framework/Loaders/BasicFetchingStrategy.h" 32 #include "../../Framework/Loaders/BasicFetchingStrategy.h"
33 #include "../../Framework/Scene2D/CairoCompositor.h"
33 #include "../../Framework/Scene2D/Scene2D.h" 34 #include "../../Framework/Scene2D/Scene2D.h"
34 #include "../../Framework/StoneInitialization.h" 35 #include "../../Framework/StoneInitialization.h"
35 #include "../../Framework/Toolbox/GeometryToolbox.h" 36 #include "../../Framework/Toolbox/GeometryToolbox.h"
36 #include "../../Framework/Toolbox/SlicesSorter.h" 37 #include "../../Framework/Toolbox/SlicesSorter.h"
37 #include "../../Framework/Volumes/ImageBuffer3D.h" 38 #include "../../Framework/Volumes/ImageBuffer3D.h"
38 #include "../../Framework/Volumes/VolumeImageGeometry.h" 39 #include "../../Framework/Volumes/VolumeImageGeometry.h"
39 40
40 // From Orthanc framework 41 // From Orthanc framework
42 #include <Core/Images/Image.h>
41 #include <Core/Images/ImageProcessing.h> 43 #include <Core/Images/ImageProcessing.h>
44 #include <Core/Images/PngWriter.h>
42 #include <Core/Logging.h> 45 #include <Core/Logging.h>
43 #include <Core/OrthancException.h> 46 #include <Core/OrthancException.h>
44 #include <Core/SystemToolbox.h> 47 #include <Core/SystemToolbox.h>
45 48
46 49
47 namespace OrthancStone 50 namespace OrthancStone
48 { 51 {
49 static bool IsSameCuttingPlane(const CoordinateSystem3D& a,
50 const CoordinateSystem3D& b)
51 {
52 double distance;
53 return (CoordinateSystem3D::ComputeDistance(distance, a, b) &&
54 LinearAlgebra::IsCloseToZero(distance));
55 }
56
57
58 class IVolumeSlicer : public boost::noncopyable 52 class IVolumeSlicer : public boost::noncopyable
59 { 53 {
60 public: 54 public:
61 class ExtractedSlice : public boost::noncopyable 55 class ExtractedSlice : public boost::noncopyable
62 { 56 {
109 const VolumeImageGeometry& geometry_; 103 const VolumeImageGeometry& geometry_;
110 bool valid_; 104 bool valid_;
111 VolumeProjection projection_; 105 VolumeProjection projection_;
112 unsigned int sliceIndex_; 106 unsigned int sliceIndex_;
113 107
108 void CheckValid() const
109 {
110 if (!valid_)
111 {
112 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
113 }
114 }
115
114 protected: 116 protected:
115 virtual uint64_t GetRevisionInternal(VolumeProjection projection, 117 virtual uint64_t GetRevisionInternal(VolumeProjection projection,
116 unsigned int sliceIndex) const = 0; 118 unsigned int sliceIndex) const = 0;
117 119
118 virtual const DicomInstanceParameters& GetDicomParameters(VolumeProjection projection, 120 virtual const DicomInstanceParameters& GetDicomParameters(VolumeProjection projection,
126 geometry_(geometry) 128 geometry_(geometry)
127 { 129 {
128 valid_ = geometry_.DetectSlice(projection_, sliceIndex_, cuttingPlane); 130 valid_ = geometry_.DetectSlice(projection_, sliceIndex_, cuttingPlane);
129 } 131 }
130 132
133 VolumeProjection GetProjection() const
134 {
135 CheckValid();
136 return projection_;
137 }
138
139 unsigned int GetSliceIndex() const
140 {
141 CheckValid();
142 return sliceIndex_;
143 }
144
131 virtual bool IsValid() 145 virtual bool IsValid()
132 { 146 {
133 return valid_; 147 return valid_;
134 } 148 }
135 149
136 virtual uint64_t GetRevision() 150 virtual uint64_t GetRevision()
137 { 151 {
138 if (!valid_) 152 CheckValid();
139 { 153 return GetRevisionInternal(projection_, sliceIndex_);
140 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
141 }
142 else
143 {
144 return GetRevisionInternal(projection_, sliceIndex_);
145 }
146 } 154 }
147 155
148 virtual ISceneLayer* CreateSceneLayer() 156 virtual ISceneLayer* CreateSceneLayer()
149 { 157 {
150 if (!valid_) 158 CheckValid();
151 { 159
152 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 160 std::auto_ptr<TextureBaseSceneLayer> texture;
153 }
154 else
155 {
156 std::auto_ptr<TextureBaseSceneLayer> texture;
157 161
158 { 162 {
159 const DicomInstanceParameters& parameters = GetDicomParameters(projection_, sliceIndex_); 163 const DicomInstanceParameters& parameters = GetDicomParameters(projection_, sliceIndex_);
160 ImageBuffer3D::SliceReader reader(image_, projection_, sliceIndex_); 164 ImageBuffer3D::SliceReader reader(image_, projection_, sliceIndex_);
161 texture.reset(parameters.CreateTexture(reader.GetAccessor())); 165 texture.reset(parameters.CreateTexture(reader.GetAccessor()));
162 } 166 }
163 167
164 const CoordinateSystem3D& system = geometry_.GetProjectionGeometry(projection_); 168 const CoordinateSystem3D& system = geometry_.GetProjectionGeometry(projection_);
165 169
166 double x0, y0, x1, y1; 170 double x0, y0, x1, y1;
167 system.ProjectPoint(x0, y0, system.GetOrigin()); 171 system.ProjectPoint(x0, y0, system.GetOrigin());
168 system.ProjectPoint(x1, y1, system.GetOrigin() + system.GetAxisX()); 172 system.ProjectPoint(x1, y1, system.GetOrigin() + system.GetAxisX());
169 texture->SetOrigin(x0, y0); 173 texture->SetOrigin(x0, y0);
170 174
171 double dx = x1 - x0; 175 double dx = x1 - x0;
172 double dy = y1 - y0; 176 double dy = y1 - y0;
173 if (!LinearAlgebra::IsCloseToZero(dx) || 177 if (!LinearAlgebra::IsCloseToZero(dx) ||
174 !LinearAlgebra::IsCloseToZero(dy)) 178 !LinearAlgebra::IsCloseToZero(dy))
175 { 179 {
176 texture->SetAngle(atan2(dy, dx)); 180 texture->SetAngle(atan2(dy, dx));
177 } 181 }
178 182
179 Vector tmp; 183 Vector tmp = geometry_.GetVoxelDimensions(projection_);
180 geometry_.GetVoxelDimensions(projection_); 184 texture->SetPixelSpacing(tmp[0], tmp[1]);
181 texture->SetPixelSpacing(tmp[0], tmp[1]); 185
182 186 return texture.release();
183 // texture->SetLinearInterpolation(linearInterpolation_); // TODO
184
185 return texture.release();
186 }
187 } 187 }
188 }; 188 };
189 189
190 190
191 // This class combines a 3D image buffer, a 3D volume geometry and 191 // This class combines a 3D image buffer, a 3D volume geometry and
192 // information about the DICOM parameters of each slice. 192 // information about the DICOM parameters of each slice.
193 class DicomSeriesVolumeImage : public IVolumeSlicer 193 class DicomSeriesVolumeImage : public boost::noncopyable
194 { 194 {
195 private: 195 public:
196 class Slice : public DicomVolumeImageOrthogonalSlice 196 class ExtractedSlice : public DicomVolumeImageOrthogonalSlice
197 { 197 {
198 private: 198 private:
199 const DicomSeriesVolumeImage& that_; 199 const DicomSeriesVolumeImage& that_;
200 200
201 protected: 201 protected:
219 { 219 {
220 return that_.GetSliceParameters(projection == VolumeProjection_Axial ? sliceIndex : 0); 220 return that_.GetSliceParameters(projection == VolumeProjection_Axial ? sliceIndex : 0);
221 } 221 }
222 222
223 public: 223 public:
224 Slice(const DicomSeriesVolumeImage& that, 224 ExtractedSlice(const DicomSeriesVolumeImage& that,
225 const CoordinateSystem3D& plane) : 225 const CoordinateSystem3D& plane) :
226 DicomVolumeImageOrthogonalSlice(that.GetImage(), that.GetGeometry(), plane), 226 DicomVolumeImageOrthogonalSlice(that.GetImage(), that.GetGeometry(), plane),
227 that_(that) 227 that_(that)
228 { 228 {
229 } 229 }
230 }; 230 };
231 231
232 232
233 private:
233 std::auto_ptr<ImageBuffer3D> image_; 234 std::auto_ptr<ImageBuffer3D> image_;
234 std::auto_ptr<VolumeImageGeometry> geometry_; 235 std::auto_ptr<VolumeImageGeometry> geometry_;
235 std::vector<DicomInstanceParameters*> slices_; 236 std::vector<DicomInstanceParameters*> slices_;
236 uint64_t revision_; 237 uint64_t revision_;
237 std::vector<uint64_t> slicesRevision_; 238 std::vector<uint64_t> slicesRevision_;
470 471
471 revision_ ++; 472 revision_ ++;
472 slicesRevision_[index] += 1; 473 slicesRevision_[index] += 1;
473 } 474 }
474 } 475 }
475
476 virtual IVolumeSlicer::ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const
477 {
478 if (HasGeometry())
479 {
480 return new Slice(*this, cuttingPlane);
481 }
482 else
483 {
484 return new InvalidExtractedSlice;
485 }
486 }
487 }; 476 };
488 477
489 478
490 479
491 // This class combines a 3D DICOM volume together with its loader 480 class OrthancSeriesVolumeProgressiveLoader :
492 class IDicomVolumeImageSource : public boost::noncopyable 481 public IObserver,
493 { 482 public IVolumeSlicer
494 public:
495 virtual ~IDicomVolumeImageSource()
496 {
497 }
498
499 virtual const DicomSeriesVolumeImage& GetVolume() const = 0;
500
501 virtual void NotifyAxialSliceAccessed(unsigned int sliceIndex) = 0;
502 };
503
504
505
506 class OrthancSeriesVolumeProgressiveLoader :
507 public IObserver,
508 public IDicomVolumeImageSource
509 { 483 {
510 private: 484 private:
511 static const unsigned int LOW_QUALITY = 0; 485 static const unsigned int LOW_QUALITY = 0;
512 static const unsigned int MIDDLE_QUALITY = 1; 486 static const unsigned int MIDDLE_QUALITY = 1;
513 static const unsigned int BEST_QUALITY = 2; 487 static const unsigned int BEST_QUALITY = 2;
639 613
640 ScheduleNextSliceDownload(); 614 ScheduleNextSliceDownload();
641 } 615 }
642 616
643 617
644 IOracle& oracle_; 618 IOracle& oracle_;
645 bool active_; 619 bool active_;
646 DicomSeriesVolumeImage volume_; 620 DicomSeriesVolumeImage volume_;
647 unsigned int simultaneousDownloads_; 621 unsigned int simultaneousDownloads_;
648 622
649 std::auto_ptr<IFetchingItemsSorter::IFactory> sorter_; 623 std::auto_ptr<IFetchingItemsSorter::IFactory> sorter_;
650 std::auto_ptr<IFetchingStrategy> strategy_; 624 std::auto_ptr<IFetchingStrategy> strategy_;
651 625
652 public: 626 public:
703 oracle_.Schedule(*this, command.release()); 677 oracle_.Schedule(*this, command.release());
704 } 678 }
705 } 679 }
706 680
707 681
708 virtual const DicomSeriesVolumeImage& GetVolume() const 682 const DicomSeriesVolumeImage& GetVolume() const
709 { 683 {
710 return volume_; 684 return volume_;
711 } 685 }
712 686
713 687
714 virtual void NotifyAxialSliceAccessed(unsigned int sliceIndex) 688 virtual IVolumeSlicer::ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const
715 { 689 {
716 if (strategy_.get() == NULL) 690 if (volume_.HasGeometry() &&
717 { 691 volume_.GetSlicesCount() != 0)
718 // Should have called GetVolume().HasGeometry() before 692 {
719 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 693 std::auto_ptr<DicomVolumeImageOrthogonalSlice> slice
694 (new DicomSeriesVolumeImage::ExtractedSlice(volume_, cuttingPlane));
695
696 assert(slice.get() != NULL &&
697 strategy_.get() != NULL);
698
699 if (slice->GetProjection() == VolumeProjection_Axial)
700 {
701 strategy_->SetCurrent(slice->GetSliceIndex());
702 }
703
704 return slice.release();
720 } 705 }
721 else 706 else
722 { 707 {
723 strategy_->SetCurrent(sliceIndex); 708 return new InvalidExtractedSlice;
724 } 709 }
725 } 710 }
726 }; 711 };
727 712
728 713
749 oracle_.Schedule(*this, command.release()); 734 oracle_.Schedule(*this, command.release());
750 } 735 }
751 #endif 736 #endif
752 737
753 738
754 /* class VolumeSlicerBase : public OLD_IVolumeSlicer 739 class SceneVolumeSlicer : public boost::noncopyable
755 { 740 {
756 private: 741 private:
757 Scene2D& scene_; 742 int layerDepth_;
758 int layerDepth_; 743 std::auto_ptr<IVolumeSlicer> volume_;
759 bool first_; 744 bool linearInterpolation_;
760 CoordinateSystem3D lastPlane_; 745 std::auto_ptr<CoordinateSystem3D> lastPlane_;
761 746 uint64_t lastRevision_;
762 protected: 747
763 bool HasCuttingPlaneChanged(const CoordinateSystem3D& plane) const 748 static bool IsSameCuttingPlane(const CoordinateSystem3D& a,
764 { 749 const CoordinateSystem3D& b)
765 if (first_ || 750 {
766 !LinearAlgebra::IsCloseToZero( 751 double distance;
767 boost::numeric::ublas::norm_2(lastPlane_.GetNormal() - plane.GetNormal()))) 752 return (CoordinateSystem3D::ComputeDistance(distance, a, b) &&
768 { 753 LinearAlgebra::IsCloseToZero(distance));
769 // This is the first rendering, or the plane has not the same orientation 754 }
770 return false; 755
756 public:
757 SceneVolumeSlicer(int layerDepth,
758 IVolumeSlicer* volume) : // Takes ownership
759 layerDepth_(layerDepth),
760 volume_(volume),
761 linearInterpolation_(false)
762 {
763 if (volume == NULL)
764 {
765 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
766 }
767 }
768
769 const IVolumeSlicer& GetSlicer() const
770 {
771 return *volume_;
772 }
773
774 void SetLinearInterpolation(bool enabled)
775 {
776 linearInterpolation_ = enabled;
777 }
778
779 bool IsLinearInterpolation() const
780 {
781 return linearInterpolation_;
782 }
783
784 void Update(Scene2D& scene,
785 const CoordinateSystem3D& plane)
786 {
787 assert(volume_.get() != NULL);
788 std::auto_ptr<IVolumeSlicer::ExtractedSlice> slice(volume_->ExtractSlice(plane));
789
790 if (slice.get() == NULL)
791 {
792 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
793 }
794
795 if (!slice->IsValid())
796 {
797 // The slicer cannot handle this cutting plane: Clear the layer
798 scene.DeleteLayer(layerDepth_);
799 lastPlane_.reset(NULL);
800 }
801 else if (lastPlane_.get() != NULL &&
802 IsSameCuttingPlane(*lastPlane_, plane) &&
803 lastRevision_ == slice->GetRevision())
804 {
805 // The content of the slice has not changed: Do nothing
771 } 806 }
772 else 807 else
773 { 808 {
774 double offset1 = lastPlane_.ProjectAlongNormal(plane.GetOrigin()); 809 // Content has changed: An update is needed
775 double offset2 = lastPlane_.ProjectAlongNormal(lastPlane_.GetOrigin()); 810 lastPlane_.reset(new CoordinateSystem3D(plane));
776 return LinearAlgebra::IsCloseToZero(offset2 - offset1); 811 lastRevision_ = slice->GetRevision();
777 } 812
778 } 813 std::auto_ptr<ISceneLayer> layer(slice->CreateSceneLayer());
779 814 if (layer.get() == NULL)
780 void SetLastCuttingPlane(const CoordinateSystem3D& plane) 815 {
781 { 816 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
782 first_ = false; 817 }
783 lastPlane_ = plane; 818
784 } 819 if (layer->GetType() == ISceneLayer::Type_ColorTexture ||
785 820 layer->GetType() == ISceneLayer::Type_FloatTexture)
786 void SetLayer(ISceneLayer* layer) 821 {
787 { 822 dynamic_cast<TextureBaseSceneLayer&>(*layer).SetLinearInterpolation(linearInterpolation_);
788 scene_.SetLayer(layerDepth_, layer); 823 }
789 } 824
790 825 scene.SetLayer(layerDepth_, layer.release());
791 void DeleteLayer() 826 }
792 { 827 }
793 scene_.DeleteLayer(layerDepth_);
794 }
795
796 public:
797 VolumeSlicerBase(Scene2D& scene,
798 int layerDepth) :
799 scene_(scene),
800 layerDepth_(layerDepth),
801 first_(true)
802 {
803 }
804 };*/
805
806
807
808 class OLD_IVolumeSlicer : public boost::noncopyable
809 {
810 public:
811 virtual ~OLD_IVolumeSlicer()
812 {
813 }
814
815 virtual void SetCuttingPlane(Scene2D& scene,
816 const CoordinateSystem3D& plane) = 0;
817 }; 828 };
818 829
819 830
820 class DicomVolumeMPRSlicer : public OLD_IVolumeSlicer 831
821 {
822 private:
823 bool linearInterpolation_;
824 int layerDepth_;
825 IDicomVolumeImageSource& source_;
826 bool first_;
827 VolumeProjection lastProjection_;
828 unsigned int lastSliceIndex_;
829 uint64_t lastSliceRevision_;
830
831 public:
832 DicomVolumeMPRSlicer(Scene2D& scene,
833 int layerDepth,
834 IDicomVolumeImageSource& source) :
835 linearInterpolation_(false),
836 layerDepth_(layerDepth),
837 source_(source),
838 first_(true)
839 {
840 }
841
842 void SetLinearInterpolation(bool enabled)
843 {
844 linearInterpolation_ = enabled;
845 }
846
847 bool IsLinearInterpolation() const
848 {
849 return linearInterpolation_;
850 }
851
852 virtual void SetCuttingPlane(Scene2D& scene,
853 const CoordinateSystem3D& plane)
854 {
855 if (!source_.GetVolume().HasGeometry() ||
856 source_.GetVolume().GetSlicesCount() == 0)
857 {
858 scene.DeleteLayer(layerDepth_);
859 return;
860 }
861
862 const VolumeImageGeometry& geometry = source_.GetVolume().GetGeometry();
863
864 VolumeProjection projection;
865 unsigned int sliceIndex;
866 if (!geometry.DetectSlice(projection, sliceIndex, plane))
867 {
868 // The cutting plane is neither axial, nor coronal, nor
869 // sagittal. Could use "VolumeReslicer" here.
870 scene.DeleteLayer(layerDepth_);
871 return;
872 }
873
874 uint64_t sliceRevision;
875 if (projection == VolumeProjection_Axial)
876 {
877 sliceRevision = source_.GetVolume().GetSliceRevision(sliceIndex);
878
879 if (first_ ||
880 lastSliceIndex_ != sliceIndex)
881 {
882 // Reorder the prefetching queue
883 source_.NotifyAxialSliceAccessed(sliceIndex);
884 }
885 }
886 else
887 {
888 // For coronal and sagittal projections, we take the global
889 // revision of the volume
890 sliceRevision = source_.GetVolume().GetRevision();
891 }
892
893 if (first_ ||
894 lastProjection_ != projection ||
895 lastSliceIndex_ != sliceIndex ||
896 lastSliceRevision_ != sliceRevision)
897 {
898 // Either the cutting plane, or the content of the slice have not
899 // changed since the last time the layer was set: Update is needed
900
901 first_ = false;
902 lastProjection_ = projection;
903 lastSliceIndex_ = sliceIndex;
904 lastSliceRevision_ = sliceRevision;
905
906 std::auto_ptr<TextureBaseSceneLayer> texture;
907
908 {
909 const DicomInstanceParameters& parameters = source_.GetVolume().GetSliceParameters
910 (projection == VolumeProjection_Axial ? sliceIndex : 0);
911
912 ImageBuffer3D::SliceReader reader(source_.GetVolume().GetImage(), projection, sliceIndex);
913 texture.reset(parameters.CreateTexture(reader.GetAccessor()));
914 }
915
916 const CoordinateSystem3D& system = geometry.GetProjectionGeometry(projection);
917
918 double x0, y0, x1, y1;
919 system.ProjectPoint(x0, y0, system.GetOrigin());
920 system.ProjectPoint(x0, y0, system.GetOrigin() + system.GetAxisX());
921 texture->SetOrigin(x0, y0);
922
923 double dx = x1 - x0;
924 double dy = y1 - y0;
925 if (!LinearAlgebra::IsCloseToZero(dx) ||
926 !LinearAlgebra::IsCloseToZero(dy))
927 {
928 texture->SetAngle(atan2(dy, dx));
929 }
930
931 Vector tmp;
932 geometry.GetVoxelDimensions(projection);
933 texture->SetPixelSpacing(tmp[0], tmp[1]);
934
935 texture->SetLinearInterpolation(linearInterpolation_);
936
937 scene.SetLayer(layerDepth_, texture.release());
938 }
939 }
940 };
941 832
942 833
943 class NativeApplicationContext : public IMessageEmitter 834 class NativeApplicationContext : public IMessageEmitter
944 { 835 {
945 private: 836 private:
1013 904
1014 905
1015 class Toto : public OrthancStone::IObserver 906 class Toto : public OrthancStone::IObserver
1016 { 907 {
1017 private: 908 private:
909 OrthancStone::IOracle& oracle_;
910 OrthancStone::Scene2D scene_;
911 std::auto_ptr<OrthancStone::SceneVolumeSlicer> slicer_;
912
1018 void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message) 913 void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message)
1019 { 914 {
1020 printf("TIMEOUT! %d\n", dynamic_cast<const Orthanc::SingleValueObject<unsigned int>& >(message.GetOrigin().GetPayload()).GetValue()); 915 if (message.GetOrigin().HasPayload())
916 {
917 printf("TIMEOUT! %d\n", dynamic_cast<const Orthanc::SingleValueObject<unsigned int>& >(message.GetOrigin().GetPayload()).GetValue());
918 }
919 else
920 {
921 printf("TIMEOUT\n");
922
923 if (slicer_.get() != NULL)
924 {
925 OrthancStone::CoordinateSystem3D plane;
926
927 const OrthancStone::OrthancSeriesVolumeProgressiveLoader& loader =
928 dynamic_cast<const OrthancStone::OrthancSeriesVolumeProgressiveLoader&>(slicer_->GetSlicer());
929
930 if (loader.GetVolume().HasGeometry())
931 {
932 plane = loader.GetVolume().GetGeometry().GetSagittalGeometry();
933 plane.SetOrigin(loader.GetVolume().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f));
934 }
935
936 slicer_->Update(scene_, plane);
937 scene_.FitContent(1024, 768);
938
939 {
940 OrthancStone::CairoCompositor compositor(scene_, 1024, 768);
941 compositor.Refresh();
942
943 Orthanc::ImageAccessor accessor;
944 compositor.GetCanvas().GetReadOnlyAccessor(accessor);
945
946 Orthanc::Image tmp(Orthanc::PixelFormat_RGB24, accessor.GetWidth(), accessor.GetHeight(), false);
947 Orthanc::ImageProcessing::Convert(tmp, accessor);
948
949 static unsigned int count = 0;
950 char buf[64];
951 sprintf(buf, "scene-%06d.png", count++);
952
953 Orthanc::PngWriter writer;
954 writer.WriteToFile(buf, tmp);
955 }
956 }
957
958 oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(message.GetOrigin().GetDelay()));
959 }
1021 } 960 }
1022 961
1023 void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) 962 void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message)
1024 { 963 {
1025 Json::Value v; 964 Json::Value v;
1053 break; 992 break;
1054 } 993 }
1055 } 994 }
1056 995
1057 public: 996 public:
1058 Toto(OrthancStone::IObservable& oracle) : 997 Toto(OrthancStone::IOracle& oracle,
1059 IObserver(oracle.GetBroker()) 998 OrthancStone::IObservable& oracleObservable) :
1060 { 999 IObserver(oracleObservable.GetBroker()),
1061 oracle.RegisterObserverCallback 1000 oracle_(oracle)
1001 {
1002 oracleObservable.RegisterObserverCallback
1062 (new OrthancStone::Callable 1003 (new OrthancStone::Callable
1063 <Toto, OrthancStone::SleepOracleCommand::TimeoutMessage>(*this, &Toto::Handle)); 1004 <Toto, OrthancStone::SleepOracleCommand::TimeoutMessage>(*this, &Toto::Handle));
1064 1005
1065 oracle.RegisterObserverCallback 1006 oracleObservable.RegisterObserverCallback
1066 (new OrthancStone::Callable 1007 (new OrthancStone::Callable
1067 <Toto, OrthancStone::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle)); 1008 <Toto, OrthancStone::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle));
1068 1009
1069 oracle.RegisterObserverCallback 1010 oracleObservable.RegisterObserverCallback
1070 (new OrthancStone::Callable 1011 (new OrthancStone::Callable
1071 <Toto, OrthancStone::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle)); 1012 <Toto, OrthancStone::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle));
1072 1013
1073 oracle.RegisterObserverCallback 1014 oracleObservable.RegisterObserverCallback
1074 (new OrthancStone::Callable 1015 (new OrthancStone::Callable
1075 <Toto, OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle)); 1016 <Toto, OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle));
1076 1017
1077 oracle.RegisterObserverCallback 1018 oracleObservable.RegisterObserverCallback
1078 (new OrthancStone::Callable 1019 (new OrthancStone::Callable
1079 <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle)); 1020 <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle));
1021 }
1022
1023 void SetVolume(int depth,
1024 OrthancStone::IVolumeSlicer* volume)
1025 {
1026 slicer_.reset(new OrthancStone::SceneVolumeSlicer(0, volume));
1080 } 1027 }
1081 }; 1028 };
1082 1029
1083 1030
1084 void Run(OrthancStone::NativeApplicationContext& context, 1031 void Run(OrthancStone::NativeApplicationContext& context,
1087 std::auto_ptr<Toto> toto; 1034 std::auto_ptr<Toto> toto;
1088 std::auto_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> loader1, loader2; 1035 std::auto_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> loader1, loader2;
1089 1036
1090 { 1037 {
1091 OrthancStone::NativeApplicationContext::WriterLock lock(context); 1038 OrthancStone::NativeApplicationContext::WriterLock lock(context);
1092 toto.reset(new Toto(lock.GetOracleObservable())); 1039 toto.reset(new Toto(oracle, lock.GetOracleObservable()));
1093 loader1.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(oracle, lock.GetOracleObservable())); 1040 loader1.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(oracle, lock.GetOracleObservable()));
1094 loader2.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(oracle, lock.GetOracleObservable())); 1041 loader2.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(oracle, lock.GetOracleObservable()));
1095 } 1042 }
1043
1044 oracle.Schedule(*toto, new OrthancStone::SleepOracleCommand(100));
1096 1045
1097 if (0) 1046 if (0)
1098 { 1047 {
1099 Json::Value v = Json::objectValue; 1048 Json::Value v = Json::objectValue;
1100 v["Level"] = "Series"; 1049 v["Level"] = "Series";
1174 //loader2->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE 1123 //loader2->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE
1175 1124
1176 // Delphine 1125 // Delphine
1177 //loader1->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT 1126 //loader1->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT
1178 loader1->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5"); // Lung 1/10mm 1127 loader1->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5"); // Lung 1/10mm
1128
1129
1130 toto->SetVolume(0, loader1.release());
1179 1131
1180 LOG(WARNING) << "...Waiting for Ctrl-C..."; 1132 LOG(WARNING) << "...Waiting for Ctrl-C...";
1181 Orthanc::SystemToolbox::ServerBarrier(); 1133 Orthanc::SystemToolbox::ServerBarrier();
1182 //boost::this_thread::sleep(boost::posix_time::seconds(1)); 1134 //boost::this_thread::sleep(boost::posix_time::seconds(1));
1183 } 1135 }
1197 try 1149 try
1198 { 1150 {
1199 OrthancStone::NativeApplicationContext context; 1151 OrthancStone::NativeApplicationContext context;
1200 1152
1201 OrthancStone::ThreadedOracle oracle(context); 1153 OrthancStone::ThreadedOracle oracle(context);
1154 oracle.SetThreadsCount(1);
1202 1155
1203 { 1156 {
1204 Orthanc::WebServiceParameters p; 1157 Orthanc::WebServiceParameters p;
1205 //p.SetUrl("http://localhost:8043/"); 1158 //p.SetUrl("http://localhost:8043/");
1206 p.SetCredentials("orthanc", "orthanc"); 1159 p.SetCredentials("orthanc", "orthanc");