Mercurial > hg > orthanc-stone
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"); |