Mercurial > hg > orthanc-wsi
comparison ViewerPlugin/Plugin.cpp @ 260:35c241231af2 iiif
reorganization
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 10 Jul 2023 08:31:50 +0200 |
parents | 3e511f10896c |
children | c72fbdecdc38 |
comparison
equal
deleted
inserted
replaced
259:3e511f10896c | 260:35c241231af2 |
---|---|
20 **/ | 20 **/ |
21 | 21 |
22 | 22 |
23 #include "../Framework/PrecompiledHeadersWSI.h" | 23 #include "../Framework/PrecompiledHeadersWSI.h" |
24 | 24 |
25 #include "../Framework/ImageToolbox.h" | |
26 #include "../Framework/Jpeg2000Reader.h" | |
27 #include "DicomPyramidCache.h" | 25 #include "DicomPyramidCache.h" |
28 #include "OrthancPluginConnection.h" | 26 #include "OrthancPluginConnection.h" |
27 #include "RawTile.h" | |
29 | 28 |
30 #include <Compatibility.h> // For std::unique_ptr | 29 #include <Compatibility.h> // For std::unique_ptr |
31 #include <Logging.h> | 30 #include <Logging.h> |
31 #include <Images/Image.h> | |
32 #include <Images/ImageProcessing.h> | 32 #include <Images/ImageProcessing.h> |
33 #include <Images/JpegReader.h> | |
34 #include <Images/JpegWriter.h> | |
35 #include <Images/PngWriter.h> | |
36 #include <MultiThreading/Semaphore.h> | |
37 #include <OrthancException.h> | 33 #include <OrthancException.h> |
38 #include <SystemToolbox.h> | 34 #include <SystemToolbox.h> |
39 | 35 |
40 #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" | 36 #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" |
41 | 37 |
43 | 39 |
44 #include <cassert> | 40 #include <cassert> |
45 #include <boost/regex.hpp> | 41 #include <boost/regex.hpp> |
46 #include <boost/math/special_functions/round.hpp> | 42 #include <boost/math/special_functions/round.hpp> |
47 | 43 |
48 std::unique_ptr<OrthancWSI::OrthancPluginConnection> orthanc_; | 44 static std::unique_ptr<OrthancWSI::OrthancPluginConnection> orthanc_; |
49 std::unique_ptr<OrthancWSI::DicomPyramidCache> cache_; | 45 static std::unique_ptr<OrthancWSI::DicomPyramidCache> cache_; |
50 std::unique_ptr<Orthanc::Semaphore> transcoderSemaphore_; | 46 static std::string publicIIIFUrl_; |
51 static std::string publicIIIFUrl_; | |
52 | 47 |
53 static void AnswerSparseTile(OrthancPluginRestOutput* output, | 48 static void AnswerSparseTile(OrthancPluginRestOutput* output, |
54 unsigned int tileWidth, | 49 unsigned int tileWidth, |
55 unsigned int tileHeight) | 50 unsigned int tileHeight) |
56 { | 51 { |
136 std::string s = result.toStyledString(); | 131 std::string s = result.toStyledString(); |
137 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), "application/json"); | 132 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), "application/json"); |
138 } | 133 } |
139 | 134 |
140 | 135 |
141 class RawTile : public boost::noncopyable | |
142 { | |
143 private: | |
144 Orthanc::PixelFormat format_; | |
145 unsigned int tileWidth_; | |
146 unsigned int tileHeight_; | |
147 Orthanc::PhotometricInterpretation photometric_; | |
148 std::string tile_; | |
149 OrthancWSI::ImageCompression compression_; | |
150 | |
151 Orthanc::ImageAccessor* DecodeInternal() | |
152 { | |
153 switch (compression_) | |
154 { | |
155 case OrthancWSI::ImageCompression_Jpeg: | |
156 { | |
157 std::unique_ptr<Orthanc::JpegReader> decoded(new Orthanc::JpegReader); | |
158 decoded->ReadFromMemory(tile_); | |
159 return decoded.release(); | |
160 } | |
161 | |
162 case OrthancWSI::ImageCompression_Jpeg2000: | |
163 { | |
164 std::unique_ptr<OrthancWSI::Jpeg2000Reader> decoded(new OrthancWSI::Jpeg2000Reader); | |
165 decoded->ReadFromMemory(tile_); | |
166 | |
167 if (photometric_ == Orthanc::PhotometricInterpretation_YBR_ICT) | |
168 { | |
169 OrthancWSI::ImageToolbox::ConvertJpegYCbCrToRgb(*decoded); | |
170 } | |
171 | |
172 return decoded.release(); | |
173 } | |
174 | |
175 case OrthancWSI::ImageCompression_None: | |
176 { | |
177 unsigned int bpp = Orthanc::GetBytesPerPixel(format_); | |
178 if (bpp * tileWidth_ * tileHeight_ != tile_.size()) | |
179 { | |
180 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
181 } | |
182 | |
183 std::unique_ptr<Orthanc::ImageAccessor> decoded(new Orthanc::ImageAccessor); | |
184 decoded->AssignReadOnly(format_, tileWidth_, tileHeight_, bpp * tileWidth_, tile_.c_str()); | |
185 | |
186 return decoded.release(); | |
187 } | |
188 | |
189 default: | |
190 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
191 } | |
192 } | |
193 | |
194 static void EncodeInternal(std::string& encoded, | |
195 const Orthanc::ImageAccessor& decoded, | |
196 Orthanc::MimeType transcodingType) | |
197 { | |
198 switch (transcodingType) | |
199 { | |
200 case Orthanc::MimeType_Png: | |
201 { | |
202 Orthanc::PngWriter writer; | |
203 Orthanc::IImageWriter::WriteToMemory(writer, encoded, decoded); | |
204 break; | |
205 } | |
206 | |
207 case Orthanc::MimeType_Jpeg: | |
208 { | |
209 Orthanc::JpegWriter writer; | |
210 Orthanc::IImageWriter::WriteToMemory(writer, encoded, decoded); | |
211 break; | |
212 } | |
213 | |
214 default: | |
215 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
216 } | |
217 } | |
218 | |
219 | |
220 public: | |
221 RawTile(OrthancWSI::ITiledPyramid& pyramid, | |
222 unsigned int level, | |
223 unsigned int tileX, | |
224 unsigned int tileY) : | |
225 format_(pyramid.GetPixelFormat()), | |
226 tileWidth_(pyramid.GetTileWidth(level)), | |
227 tileHeight_(pyramid.GetTileHeight(level)), | |
228 photometric_(pyramid.GetPhotometricInterpretation()) | |
229 { | |
230 if (!pyramid.ReadRawTile(tile_, compression_, level, tileX, tileY)) | |
231 { | |
232 // Handling of missing tile (for sparse tiling): TODO parameter? | |
233 // AnswerSparseTile(output, tileWidth, tileHeight); return; | |
234 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); | |
235 } | |
236 } | |
237 | |
238 void Answer(OrthancPluginRestOutput* output, | |
239 Orthanc::MimeType transcodingType) | |
240 { | |
241 if (compression_ == OrthancWSI::ImageCompression_Jpeg) | |
242 { | |
243 // The tile is already a JPEG image. In such a case, we can | |
244 // serve it as such, because any Web browser can handle JPEG. | |
245 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, tile_.c_str(), | |
246 tile_.size(), Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg)); | |
247 } | |
248 else | |
249 { | |
250 // This is a lossless frame (coming from a JPEG2000 or an | |
251 // uncompressed DICOM instance), which is not a DICOM-JPEG | |
252 // instance. We need to decompress the raw tile, then transcode | |
253 // it to the PNG/JPEG, depending on the "transcodingType". | |
254 | |
255 std::string transcoded; | |
256 | |
257 { | |
258 // The semaphore is used to throttle the number of simultaneous computations | |
259 Orthanc::Semaphore::Locker locker(*transcoderSemaphore_); | |
260 | |
261 std::unique_ptr<Orthanc::ImageAccessor> decoded(DecodeInternal()); | |
262 EncodeInternal(transcoded, *decoded, transcodingType); | |
263 } | |
264 | |
265 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, transcoded.c_str(), | |
266 transcoded.size(), Orthanc::EnumerationToString(transcodingType)); | |
267 } | |
268 } | |
269 | |
270 Orthanc::ImageAccessor* Decode() | |
271 { | |
272 Orthanc::Semaphore::Locker locker(*transcoderSemaphore_); | |
273 return DecodeInternal(); | |
274 } | |
275 | |
276 static void Encode(std::string& encoded, | |
277 const Orthanc::ImageAccessor& decoded, | |
278 Orthanc::MimeType transcodingType) | |
279 { | |
280 Orthanc::Semaphore::Locker locker(*transcoderSemaphore_); | |
281 EncodeInternal(encoded, decoded, transcodingType); | |
282 } | |
283 }; | |
284 | |
285 | |
286 void ServeTile(OrthancPluginRestOutput* output, | 136 void ServeTile(OrthancPluginRestOutput* output, |
287 const char* url, | 137 const char* url, |
288 const OrthancPluginHttpRequest* request) | 138 const OrthancPluginHttpRequest* request) |
289 { | 139 { |
290 std::string seriesId(request->groups[0]); | 140 std::string seriesId(request->groups[0]); |
795 | 645 |
796 // Limit the number of PNG transcoders to the number of available | 646 // Limit the number of PNG transcoders to the number of available |
797 // hardware threads (e.g. number of CPUs or cores or | 647 // hardware threads (e.g. number of CPUs or cores or |
798 // hyperthreading units) | 648 // hyperthreading units) |
799 unsigned int threads = Orthanc::SystemToolbox::GetHardwareConcurrency(); | 649 unsigned int threads = Orthanc::SystemToolbox::GetHardwareConcurrency(); |
800 transcoderSemaphore_.reset(new Orthanc::Semaphore(threads)); | 650 RawTile::InitializeTranscoderSemaphore(threads); |
801 | 651 |
802 char info[1024]; | 652 char info[1024]; |
803 sprintf(info, "The whole-slide imaging plugin will use at most %u threads to transcode the tiles", threads); | 653 sprintf(info, "The whole-slide imaging plugin will use at most %u threads to transcode the tiles", threads); |
804 OrthancPluginLogWarning(OrthancPlugins::GetGlobalContext(), info); | 654 OrthancPluginLogWarning(OrthancPlugins::GetGlobalContext(), info); |
805 | 655 |
843 | 693 |
844 ORTHANC_PLUGINS_API void OrthancPluginFinalize() | 694 ORTHANC_PLUGINS_API void OrthancPluginFinalize() |
845 { | 695 { |
846 cache_.reset(NULL); | 696 cache_.reset(NULL); |
847 orthanc_.reset(NULL); | 697 orthanc_.reset(NULL); |
848 transcoderSemaphore_.reset(NULL); | 698 RawTile::FinalizeTranscoderSemaphore(); |
849 } | 699 } |
850 | 700 |
851 | 701 |
852 ORTHANC_PLUGINS_API const char* OrthancPluginGetName() | 702 ORTHANC_PLUGINS_API const char* OrthancPluginGetName() |
853 { | 703 { |