comparison ViewerPlugin/Plugin.cpp @ 73:a8c90aa32ca6

LRU caching of pyramids, OrthancWSIClearCache script
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 28 Nov 2016 16:51:19 +0100
parents d529d9ce3c7e
children ff0ef01c332c
comparison
equal deleted inserted replaced
72:ea6309f70f1f 73:a8c90aa32ca6
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 **/ 18 **/
19 19
20 20
21 #include "../Framework/PrecompiledHeadersWSI.h" 21 #include "../Framework/PrecompiledHeadersWSI.h"
22 #include "../Framework/Inputs/DicomPyramid.h" 22
23 #include "DicomPyramidCache.h"
23 #include "../Framework/Jpeg2000Reader.h" 24 #include "../Framework/Jpeg2000Reader.h"
24 25
25 #include "../Resources/Orthanc/Core/Images/ImageProcessing.h" 26 #include "../Resources/Orthanc/Core/Images/ImageProcessing.h"
26 #include "../Resources/Orthanc/Core/Images/PngWriter.h" 27 #include "../Resources/Orthanc/Core/Images/PngWriter.h"
27 #include "../Resources/Orthanc/Core/MultiThreading/Semaphore.h" 28 #include "../Resources/Orthanc/Core/MultiThreading/Semaphore.h"
31 32
32 #include <EmbeddedResources.h> 33 #include <EmbeddedResources.h>
33 34
34 #include <cassert> 35 #include <cassert>
35 36
36
37
38 namespace OrthancWSI
39 {
40 // TODO Add LRU recycling policy
41 class DicomPyramidCache : public boost::noncopyable
42 {
43 private:
44 boost::mutex mutex_;
45 OrthancPlugins::IOrthancConnection& orthanc_;
46 std::auto_ptr<DicomPyramid> pyramid_;
47
48 DicomPyramid& GetPyramid(const std::string& seriesId)
49 {
50 // Mutex is assumed to be locked
51
52 if (pyramid_.get() == NULL ||
53 pyramid_->GetSeriesId() != seriesId)
54 {
55 pyramid_.reset(new DicomPyramid(orthanc_, seriesId, true /* use metadata cache */));
56 }
57
58 if (pyramid_.get() == NULL)
59 {
60 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
61 }
62
63 return *pyramid_;
64 }
65
66 public:
67 DicomPyramidCache(OrthancPlugins::IOrthancConnection& orthanc) :
68 orthanc_(orthanc)
69 {
70 }
71
72 void Invalidate(const std::string& seriesId)
73 {
74 boost::mutex::scoped_lock lock(mutex_);
75
76 if (pyramid_.get() != NULL &&
77 pyramid_->GetSeriesId() == seriesId)
78 {
79 pyramid_.reset(NULL);
80 }
81 }
82
83 class Locker : public boost::noncopyable
84 {
85 private:
86 boost::mutex::scoped_lock lock_;
87 DicomPyramid& pyramid_;
88
89 public:
90 Locker(DicomPyramidCache& cache,
91 const std::string& seriesId) :
92 lock_(cache.mutex_),
93 pyramid_(cache.GetPyramid(seriesId))
94 {
95 }
96
97 DicomPyramid& GetPyramid() const
98 {
99 return pyramid_;
100 }
101 };
102 };
103 }
104
105
106 OrthancPluginContext* context_ = NULL; 37 OrthancPluginContext* context_ = NULL;
107 38
108 std::auto_ptr<OrthancPlugins::OrthancPluginConnection> orthanc_; 39 std::auto_ptr<OrthancPlugins::OrthancPluginConnection> orthanc_;
109 std::auto_ptr<OrthancWSI::DicomPyramidCache> cache_; 40 std::auto_ptr<OrthancWSI::DicomPyramidCache> cache_;
110 std::auto_ptr<Orthanc::Semaphore> transcoderSemaphore_; 41 std::auto_ptr<Orthanc::Semaphore> transcoderSemaphore_;
149 OrthancPluginLogInfo(context_, tmp); 80 OrthancPluginLogInfo(context_, tmp);
150 81
151 82
152 OrthancWSI::DicomPyramidCache::Locker locker(*cache_, seriesId); 83 OrthancWSI::DicomPyramidCache::Locker locker(*cache_, seriesId);
153 84
85 unsigned int tileWidth = locker.GetPyramid().GetTileWidth();
86 unsigned int tileHeight = locker.GetPyramid().GetTileHeight();
154 unsigned int totalWidth = locker.GetPyramid().GetLevelWidth(0); 87 unsigned int totalWidth = locker.GetPyramid().GetLevelWidth(0);
155 unsigned int totalHeight = locker.GetPyramid().GetLevelHeight(0); 88 unsigned int totalHeight = locker.GetPyramid().GetLevelHeight(0);
156 89
90 Json::Value sizes = Json::arrayValue;
157 Json::Value resolutions = Json::arrayValue; 91 Json::Value resolutions = Json::arrayValue;
92 Json::Value tilesCount = Json::arrayValue;
158 for (unsigned int i = 0; i < locker.GetPyramid().GetLevelCount(); i++) 93 for (unsigned int i = 0; i < locker.GetPyramid().GetLevelCount(); i++)
159 { 94 {
160 resolutions.append(static_cast<float>(totalWidth) / 95 unsigned int levelWidth = locker.GetPyramid().GetLevelWidth(i);
161 static_cast<float>(locker.GetPyramid().GetLevelWidth(i))); 96 unsigned int levelHeight = locker.GetPyramid().GetLevelHeight(i);
97
98 resolutions.append(static_cast<float>(totalWidth) / static_cast<float>(levelWidth));
99
100 Json::Value s = Json::arrayValue;
101 s.append(levelWidth);
102 s.append(levelHeight);
103 sizes.append(s);
104
105 s = Json::arrayValue;
106 s.append(OrthancWSI::CeilingDivision(levelWidth, tileWidth));
107 s.append(OrthancWSI::CeilingDivision(levelHeight, tileHeight));
108 tilesCount.append(s);
162 } 109 }
163 110
164 Json::Value result; 111 Json::Value result;
165 result["ID"] = seriesId; 112 result["ID"] = seriesId;
113 result["Resolutions"] = resolutions;
114 result["Sizes"] = sizes;
115 result["TileHeight"] = tileHeight;
116 result["TileWidth"] = tileWidth;
117 result["TilesCount"] = tilesCount;
118 result["TotalHeight"] = totalHeight;
166 result["TotalWidth"] = totalWidth; 119 result["TotalWidth"] = totalWidth;
167 result["TotalHeight"] = totalHeight;
168 result["TileWidth"] = locker.GetPyramid().GetTileWidth();
169 result["TileHeight"] = locker.GetPyramid().GetTileHeight();
170 result["Resolutions"] = resolutions;
171 120
172 std::string s = result.toStyledString(); 121 std::string s = result.toStyledString();
173 OrthancPluginAnswerBuffer(context_, output, s.c_str(), s.size(), "application/json"); 122 OrthancPluginAnswerBuffer(context_, output, s.c_str(), s.size(), "application/json");
174 } 123 }
175 124
376 OrthancPluginLogWarning(context_, info); 325 OrthancPluginLogWarning(context_, info);
377 326
378 OrthancPluginSetDescription(context, "Provides a Web viewer of whole-slide microscopic images within Orthanc."); 327 OrthancPluginSetDescription(context, "Provides a Web viewer of whole-slide microscopic images within Orthanc.");
379 328
380 orthanc_.reset(new OrthancPlugins::OrthancPluginConnection(context)); 329 orthanc_.reset(new OrthancPlugins::OrthancPluginConnection(context));
381 cache_.reset(new OrthancWSI::DicomPyramidCache(*orthanc_)); 330 cache_.reset(new OrthancWSI::DicomPyramidCache(*orthanc_, 10 /* Number of pyramids to be cached - TODO parameter */));
382 331
383 OrthancPluginRegisterOnChangeCallback(context_, OnChangeCallback); 332 OrthancPluginRegisterOnChangeCallback(context_, OnChangeCallback);
384 333
385 OrthancPlugins::RegisterRestCallback<ServeFile>(context, "/wsi/app/(ol.css)", true); 334 OrthancPlugins::RegisterRestCallback<ServeFile>(context, "/wsi/app/(ol.css)", true);
386 OrthancPlugins::RegisterRestCallback<ServeFile>(context, "/wsi/app/(ol.js)", true); 335 OrthancPlugins::RegisterRestCallback<ServeFile>(context, "/wsi/app/(ol.js)", true);