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