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);