Mercurial > hg > orthanc-stone
comparison RenderingPlugin/Sources/Plugin.cpp @ 1944:3daecfa5791c
rendering plugin: caching multiple rt-struct in memory
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 02 Jun 2022 11:45:28 +0200 |
parents | a601b8abc1cb |
children | 98952be6fb97 |
comparison
equal
deleted
inserted
replaced
1943:a601b8abc1cb | 1944:3daecfa5791c |
---|---|
25 | 25 |
26 #include "../../OrthancStone/Sources/Toolbox/AffineTransform2D.h" | 26 #include "../../OrthancStone/Sources/Toolbox/AffineTransform2D.h" |
27 #include "../../OrthancStone/Sources/Toolbox/DicomInstanceParameters.h" | 27 #include "../../OrthancStone/Sources/Toolbox/DicomInstanceParameters.h" |
28 #include "../../OrthancStone/Sources/Toolbox/DicomStructureSet.h" | 28 #include "../../OrthancStone/Sources/Toolbox/DicomStructureSet.h" |
29 | 29 |
30 #include <EmbeddedResources.h> | 30 #include <Cache/MemoryObjectCache.h> |
31 | |
32 #include <Images/Image.h> | 31 #include <Images/Image.h> |
33 #include <Images/ImageProcessing.h> | 32 #include <Images/ImageProcessing.h> |
34 #include <Images/PngWriter.h> | |
35 #include <Images/NumpyWriter.h> | 33 #include <Images/NumpyWriter.h> |
36 #include <Logging.h> | 34 #include <Logging.h> |
37 #include <SerializationToolbox.h> | 35 #include <SerializationToolbox.h> |
38 #include <Toolbox.h> | 36 #include <Toolbox.h> |
39 | 37 |
47 | 45 |
48 | 46 |
49 class DicomStructureCache : public boost::noncopyable | 47 class DicomStructureCache : public boost::noncopyable |
50 { | 48 { |
51 private: | 49 private: |
52 boost::mutex mutex_; | 50 class Item : public Orthanc::ICacheable |
53 std::string instanceId_; | 51 { |
54 std::unique_ptr<OrthancStone::DicomStructureSet> rtstruct_; | 52 private: |
53 std::unique_ptr<OrthancStone::DicomStructureSet> rtstruct_; | |
54 | |
55 public: | |
56 Item(OrthancStone::DicomStructureSet* rtstruct) : | |
57 rtstruct_(rtstruct) | |
58 { | |
59 if (rtstruct == NULL) | |
60 { | |
61 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
62 } | |
63 } | |
64 | |
65 virtual size_t GetMemoryUsage() const | |
66 { | |
67 return 1; | |
68 } | |
69 | |
70 OrthancStone::DicomStructureSet& GetRtStruct() const | |
71 { | |
72 return *rtstruct_; | |
73 } | |
74 }; | |
75 | |
76 Orthanc::MemoryObjectCache cache_; | |
55 | 77 |
56 DicomStructureCache() // Singleton design pattern | 78 DicomStructureCache() // Singleton design pattern |
57 { | 79 { |
58 } | 80 } |
59 | 81 |
60 public: | 82 public: |
61 void Invalidate(const std::string& instanceId) | 83 void Invalidate(const std::string& instanceId) |
62 { | 84 { |
63 boost::mutex::scoped_lock lock(mutex_); | 85 cache_.Invalidate(instanceId); |
64 | 86 } |
65 if (instanceId_ == instanceId) | 87 |
66 { | 88 void SetMaximumNumberOfItems(size_t items) |
67 rtstruct_.reset(NULL); | 89 { |
68 } | 90 cache_.SetMaximumSize(items); |
69 } | 91 } |
70 | 92 |
71 static DicomStructureCache& GetSingleton() | 93 static DicomStructureCache& GetSingleton() |
72 { | 94 { |
73 static DicomStructureCache instance; | 95 static DicomStructureCache instance; |
75 } | 97 } |
76 | 98 |
77 class Accessor : public boost::noncopyable | 99 class Accessor : public boost::noncopyable |
78 { | 100 { |
79 private: | 101 private: |
80 boost::mutex::scoped_lock lock_; | 102 DicomStructureCache& that_; |
81 std::string instanceId_; | 103 std::string instanceId_; |
82 OrthancStone::DicomStructureSet* rtstruct_; | 104 Orthanc::MemoryObjectCache::Accessor lock_; |
105 std::unique_ptr<OrthancStone::DicomStructureSet> notCached_; | |
83 | 106 |
84 public: | 107 public: |
85 Accessor(DicomStructureCache& that, | 108 Accessor(DicomStructureCache& that, |
86 const std::string& instanceId) : | 109 const std::string& instanceId) : |
87 lock_(that.mutex_), | 110 that_(that), |
88 instanceId_(instanceId), | 111 instanceId_(instanceId), |
89 rtstruct_(NULL) | 112 lock_(that.cache_, instanceId, true /* unique, as "GetRtStruct()" is mutable */) |
90 { | 113 { |
91 if (that.instanceId_ == instanceId && | 114 if (!lock_.IsValid()) |
92 that.rtstruct_.get() != NULL) | 115 { |
93 { | 116 OrthancStone::OrthancPluginConnection connection; |
94 rtstruct_ = that.rtstruct_.get(); | 117 OrthancStone::FullOrthancDataset dataset(connection, "/instances/" + instanceId + "/tags?ignore-length=3006-0050"); |
95 } | 118 notCached_.reset(new OrthancStone::DicomStructureSet(dataset)); |
96 else | 119 } |
97 { | 120 } |
121 | |
122 ~Accessor() | |
123 { | |
124 if (!lock_.IsValid()) | |
125 { | |
126 assert(notCached_.get() != NULL); | |
127 | |
98 try | 128 try |
99 { | 129 { |
100 OrthancStone::OrthancPluginConnection connection; | 130 that_.cache_.Acquire(instanceId_, new Item(notCached_.release())); |
101 OrthancStone::FullOrthancDataset dataset(connection, "/instances/" + instanceId + "/tags?ignore-length=3006-0050"); | |
102 that.rtstruct_.reset(new OrthancStone::DicomStructureSet(dataset)); | |
103 that.instanceId_ = instanceId; | |
104 rtstruct_ = that.rtstruct_.get(); | |
105 } | 131 } |
106 catch (Orthanc::OrthancException&) | 132 catch (Orthanc::OrthancException& e) |
107 { | 133 { |
134 LOG(ERROR) << "Cannot insert RT-STRUCT into cache: " << e.What(); | |
108 } | 135 } |
109 } | 136 } |
110 } | 137 } |
111 | 138 |
112 const std::string& GetInstanceId() const | 139 const std::string& GetInstanceId() const |
113 { | 140 { |
114 return instanceId_; | 141 return instanceId_; |
115 } | 142 } |
116 | 143 |
117 bool IsValid() const | |
118 { | |
119 return rtstruct_ != NULL; | |
120 } | |
121 | |
122 OrthancStone::DicomStructureSet& GetRtStruct() const | 144 OrthancStone::DicomStructureSet& GetRtStruct() const |
123 { | 145 { |
124 if (IsValid()) | 146 if (lock_.IsValid()) |
125 { | 147 { |
126 return *rtstruct_; | 148 return dynamic_cast<Item&>(lock_.GetValue()).GetRtStruct(); |
127 } | 149 } |
128 else | 150 else |
129 { | 151 { |
130 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 152 assert(notCached_.get() != NULL); |
153 return *notCached_; | |
131 } | 154 } |
132 } | 155 } |
133 }; | 156 }; |
134 }; | 157 }; |
135 | 158 |
296 flipY_ = false; | 319 flipY_ = false; |
297 hasResize_ = false; | 320 hasResize_ = false; |
298 targetWidth_ = 0; | 321 targetWidth_ = 0; |
299 targetHeight_ = 0; | 322 targetHeight_ = 0; |
300 hasInterpolation_ = false; | 323 hasInterpolation_ = false; |
324 interpolation_ = OrthancStone::ImageInterpolation_Nearest; | |
301 } | 325 } |
302 | 326 |
303 | 327 |
304 OrthancStone::AffineTransform2D ComputeTransform(unsigned int sourceWidth, | 328 OrthancStone::AffineTransform2D ComputeTransform(unsigned int sourceWidth, |
305 unsigned int sourceHeight) const | 329 unsigned int sourceHeight) const |
633 const char* url, | 657 const char* url, |
634 const OrthancPluginHttpRequest* request) | 658 const OrthancPluginHttpRequest* request) |
635 { | 659 { |
636 DicomStructureCache::Accessor accessor(DicomStructureCache::GetSingleton(), request->groups[0]); | 660 DicomStructureCache::Accessor accessor(DicomStructureCache::GetSingleton(), request->groups[0]); |
637 | 661 |
638 if (!accessor.IsValid()) | |
639 { | |
640 throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem); | |
641 } | |
642 | |
643 Json::Value answer; | 662 Json::Value answer; |
644 answer[STRUCTURES] = Json::arrayValue; | 663 answer[STRUCTURES] = Json::arrayValue; |
645 | 664 |
646 for (size_t i = 0; i < accessor.GetRtStruct().GetStructuresCount(); i++) | 665 for (size_t i = 0; i < accessor.GetRtStruct().GetStructuresCount(); i++) |
647 { | 666 { |
669 | 688 |
670 std::string t; | 689 std::string t; |
671 s.ToString(t); | 690 s.ToString(t); |
672 | 691 |
673 answer[INSTANCES].append(t); | 692 answer[INSTANCES].append(t); |
674 } | 693 } |
675 | 694 |
676 std::string s = answer.toStyledString(); | 695 std::string s = answer.toStyledString(); |
677 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), "application/json"); | 696 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), "application/json"); |
678 } | 697 } |
679 | 698 |
770 | 789 |
771 std::list< std::vector<OrthancStone::Vector> > polygons; | 790 std::list< std::vector<OrthancStone::Vector> > polygons; |
772 | 791 |
773 { | 792 { |
774 DicomStructureCache::Accessor accessor(DicomStructureCache::GetSingleton(), request->groups[0]); | 793 DicomStructureCache::Accessor accessor(DicomStructureCache::GetSingleton(), request->groups[0]); |
775 | |
776 if (!accessor.IsValid()) | |
777 { | |
778 throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem); | |
779 } | |
780 | 794 |
781 size_t structureIndex; | 795 size_t structureIndex; |
782 bool found = false; | 796 bool found = false; |
783 for (size_t i = 0; i < accessor.GetRtStruct().GetStructuresCount(); i++) | 797 for (size_t i = 0; i < accessor.GetRtStruct().GetStructuresCount(); i++) |
784 { | 798 { |
887 return -1; | 901 return -1; |
888 } | 902 } |
889 | 903 |
890 try | 904 try |
891 { | 905 { |
906 DicomStructureCache::GetSingleton().SetMaximumNumberOfItems(1024); // Cache up to 1024 RT-STRUCT instances | |
907 | |
892 OrthancPlugins::RegisterRestCallback<RenderNumpyFrame>("/stone/instances/([^/]+)/frames/([0-9]+)/numpy", true); | 908 OrthancPlugins::RegisterRestCallback<RenderNumpyFrame>("/stone/instances/([^/]+)/frames/([0-9]+)/numpy", true); |
893 OrthancPlugins::RegisterRestCallback<ListRtStruct>("/stone/rt-struct", true); | 909 OrthancPlugins::RegisterRestCallback<ListRtStruct>("/stone/rt-struct", true); |
894 OrthancPlugins::RegisterRestCallback<GetRtStruct>("/stone/rt-struct/([^/]+)/info", true); | 910 OrthancPlugins::RegisterRestCallback<GetRtStruct>("/stone/rt-struct/([^/]+)/info", true); |
895 OrthancPlugins::RegisterRestCallback<RenderRtStruct>("/stone/rt-struct/([^/]+)/numpy", true); | 911 OrthancPlugins::RegisterRestCallback<RenderRtStruct>("/stone/rt-struct/([^/]+)/numpy", true); |
896 OrthancPluginRegisterOnChangeCallback(context, OnChangeCallback); | 912 OrthancPluginRegisterOnChangeCallback(context, OnChangeCallback); |