Mercurial > hg > orthanc-webviewer
comparison Plugin/Plugin.cpp @ 231:7097d0eaac76 transcoding
Move the GDCM decoder out of the Orthanc Web viewer plugin as a separate plugin
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 11 May 2020 15:34:12 +0200 |
parents | cce89307af28 |
children | 85b6a8bf8c7b |
comparison
equal
deleted
inserted
replaced
230:cce89307af28 | 231:7097d0eaac76 |
---|---|
27 #include <Core/DicomFormat/DicomMap.h> | 27 #include <Core/DicomFormat/DicomMap.h> |
28 #include <Core/OrthancException.h> | 28 #include <Core/OrthancException.h> |
29 #include <Core/SystemToolbox.h> | 29 #include <Core/SystemToolbox.h> |
30 #include <Core/Toolbox.h> | 30 #include <Core/Toolbox.h> |
31 #include <Plugins/Samples/Common/OrthancPluginCppWrapper.h> | 31 #include <Plugins/Samples/Common/OrthancPluginCppWrapper.h> |
32 #include <Plugins/Samples/GdcmDecoder/GdcmDecoderCache.h> | |
33 | 32 |
34 #include <boost/thread.hpp> | 33 #include <boost/thread.hpp> |
35 #include <boost/lexical_cast.hpp> | 34 #include <boost/lexical_cast.hpp> |
36 #include <EmbeddedResources.h> | 35 #include <EmbeddedResources.h> |
37 #include <boost/filesystem.hpp> | 36 #include <boost/filesystem.hpp> |
55 #endif | 54 #endif |
56 | 55 |
57 | 56 |
58 | 57 |
59 static OrthancPluginContext* context_ = NULL; | 58 static OrthancPluginContext* context_ = NULL; |
60 static bool restrictTransferSyntaxes_ = false; | |
61 static std::set<std::string> enabledTransferSyntaxes_; | |
62 | 59 |
63 | 60 |
64 class CacheContext | 61 class CacheContext |
65 { | 62 { |
66 private: | 63 private: |
87 std::unique_ptr<OrthancPlugins::CacheScheduler> scheduler_; | 84 std::unique_ptr<OrthancPlugins::CacheScheduler> scheduler_; |
88 | 85 |
89 Orthanc::SharedMessageQueue newInstances_; | 86 Orthanc::SharedMessageQueue newInstances_; |
90 bool stop_; | 87 bool stop_; |
91 boost::thread newInstancesThread_; | 88 boost::thread newInstancesThread_; |
92 OrthancPlugins::GdcmDecoderCache decoder_; | |
93 | 89 |
94 static void NewInstancesThread(CacheContext* cache) | 90 static void NewInstancesThread(CacheContext* cache) |
95 { | 91 { |
96 while (!cache->stop_) | 92 while (!cache->stop_) |
97 { | 93 { |
146 | 142 |
147 void SignalNewInstance(const char* instanceId) | 143 void SignalNewInstance(const char* instanceId) |
148 { | 144 { |
149 newInstances_.Enqueue(new DynamicString(instanceId)); | 145 newInstances_.Enqueue(new DynamicString(instanceId)); |
150 } | 146 } |
151 | |
152 OrthancPlugins::GdcmDecoderCache& GetDecoder() | |
153 { | |
154 return decoder_; | |
155 } | |
156 }; | 147 }; |
157 | 148 |
158 | 149 |
159 | 150 |
160 static CacheContext* cache_ = NULL; | 151 static CacheContext* cache_ = NULL; |
347 } | 338 } |
348 } | 339 } |
349 | 340 |
350 | 341 |
351 | 342 |
352 static bool ExtractTransferSyntax(std::string& transferSyntax, | 343 void ParseConfiguration(int& decodingThreads, |
353 const void* dicom, | |
354 const uint32_t size) | |
355 { | |
356 Orthanc::DicomMap header; | |
357 if (!Orthanc::DicomMap::ParseDicomMetaInformation(header, reinterpret_cast<const char*>(dicom), size)) | |
358 { | |
359 return false; | |
360 } | |
361 | |
362 const Orthanc::DicomValue* tag = header.TestAndGetValue(0x0002, 0x0010); | |
363 if (tag == NULL || | |
364 tag->IsNull() || | |
365 tag->IsBinary()) | |
366 { | |
367 return false; | |
368 } | |
369 else | |
370 { | |
371 // Stripping spaces should not be required, as this is a UI value | |
372 // representation whose stripping is supported by the Orthanc | |
373 // core, but let's be careful... | |
374 transferSyntax = Orthanc::Toolbox::StripSpaces(tag->GetContent()); | |
375 return true; | |
376 } | |
377 } | |
378 | |
379 | |
380 static bool IsTransferSyntaxEnabled(const void* dicom, | |
381 const uint32_t size) | |
382 { | |
383 std::string formattedSize; | |
384 | |
385 { | |
386 char tmp[16]; | |
387 sprintf(tmp, "%0.1fMB", static_cast<float>(size) / (1024.0f * 1024.0f)); | |
388 formattedSize.assign(tmp); | |
389 } | |
390 | |
391 if (!restrictTransferSyntaxes_) | |
392 { | |
393 std::string s = "Decoding one DICOM instance of " + formattedSize + " using GDCM"; | |
394 OrthancPluginLogInfo(context_, s.c_str()); | |
395 return true; | |
396 } | |
397 | |
398 std::string transferSyntax; | |
399 if (!ExtractTransferSyntax(transferSyntax, dicom, size)) | |
400 { | |
401 std::string s = ("Cannot extract the transfer syntax of this instance of " + | |
402 formattedSize + ", will use GDCM to decode it"); | |
403 OrthancPluginLogInfo(context_, s.c_str()); | |
404 return true; | |
405 } | |
406 | |
407 if (enabledTransferSyntaxes_.find(transferSyntax) != enabledTransferSyntaxes_.end()) | |
408 { | |
409 // Decoding for this transfer syntax is enabled | |
410 std::string s = ("Using GDCM to decode this instance of " + | |
411 formattedSize + " with transfer syntax " + transferSyntax); | |
412 OrthancPluginLogInfo(context_, s.c_str()); | |
413 return true; | |
414 } | |
415 else | |
416 { | |
417 std::string s = ("Won't use GDCM to decode this instance of " + | |
418 formattedSize + ", as its transfer syntax " + transferSyntax + " is disabled"); | |
419 OrthancPluginLogInfo(context_, s.c_str()); | |
420 return false; | |
421 } | |
422 } | |
423 | |
424 | |
425 static OrthancPluginErrorCode DecodeImageCallback(OrthancPluginImage** target, | |
426 const void* dicom, | |
427 const uint32_t size, | |
428 uint32_t frameIndex) | |
429 { | |
430 try | |
431 { | |
432 if (!IsTransferSyntaxEnabled(dicom, size)) | |
433 { | |
434 *target = NULL; | |
435 return OrthancPluginErrorCode_Success; | |
436 } | |
437 | |
438 std::unique_ptr<OrthancPlugins::OrthancImageWrapper> image; | |
439 | |
440 #if 0 | |
441 // Do not use the cache | |
442 OrthancPlugins::GdcmImageDecoder decoder(dicom, size); | |
443 image.reset(new OrthancPlugins::OrthancImageWrapper(context_, decoder, frameIndex)); | |
444 #else | |
445 using namespace OrthancPlugins; | |
446 image.reset(cache_->GetDecoder().Decode(context_, dicom, size, frameIndex)); | |
447 #endif | |
448 | |
449 *target = image->Release(); | |
450 | |
451 return OrthancPluginErrorCode_Success; | |
452 } | |
453 catch (Orthanc::OrthancException& e) | |
454 { | |
455 *target = NULL; | |
456 | |
457 std::string s = "Cannot decode image using GDCM: " + std::string(e.What()); | |
458 OrthancPluginLogWarning(context_, s.c_str()); | |
459 return OrthancPluginErrorCode_Plugin; | |
460 } | |
461 catch (std::runtime_error& e) | |
462 { | |
463 *target = NULL; | |
464 | |
465 std::string s = "Cannot decode image using GDCM: " + std::string(e.what()); | |
466 OrthancPluginLogWarning(context_, s.c_str()); | |
467 return OrthancPluginErrorCode_Plugin; | |
468 } | |
469 } | |
470 | |
471 | |
472 void ParseConfiguration(bool& enableGdcm, | |
473 int& decodingThreads, | |
474 boost::filesystem::path& cachePath, | 344 boost::filesystem::path& cachePath, |
475 int& cacheSize) | 345 int& cacheSize) |
476 { | 346 { |
477 /* Read the configuration of the Web viewer */ | 347 /* Read the configuration of the Web viewer */ |
478 Json::Value configuration; | 348 Json::Value configuration; |
498 } | 368 } |
499 | 369 |
500 cachePath = OrthancPlugins::GetStringValue(configuration[CONFIG_WEB_VIEWER], key, cachePath.string()); | 370 cachePath = OrthancPlugins::GetStringValue(configuration[CONFIG_WEB_VIEWER], key, cachePath.string()); |
501 cacheSize = OrthancPlugins::GetIntegerValue(configuration[CONFIG_WEB_VIEWER], "CacheSize", cacheSize); | 371 cacheSize = OrthancPlugins::GetIntegerValue(configuration[CONFIG_WEB_VIEWER], "CacheSize", cacheSize); |
502 decodingThreads = OrthancPlugins::GetIntegerValue(configuration[CONFIG_WEB_VIEWER], "Threads", decodingThreads); | 372 decodingThreads = OrthancPlugins::GetIntegerValue(configuration[CONFIG_WEB_VIEWER], "Threads", decodingThreads); |
503 | |
504 static const char* CONFIG_ENABLE_GDCM = "EnableGdcm"; | |
505 if (configuration[CONFIG_WEB_VIEWER].isMember(CONFIG_ENABLE_GDCM)) | |
506 { | |
507 if (configuration[CONFIG_WEB_VIEWER][CONFIG_ENABLE_GDCM].type() != Json::booleanValue) | |
508 { | |
509 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
510 } | |
511 else | |
512 { | |
513 enableGdcm = configuration[CONFIG_WEB_VIEWER][CONFIG_ENABLE_GDCM].asBool(); | |
514 } | |
515 } | |
516 | |
517 if (enableGdcm) | |
518 { | |
519 static const char* CONFIG_RESTRICT_TRANSFER_SYNTAXES = "RestrictTransferSyntaxes"; | |
520 | |
521 if (configuration[CONFIG_WEB_VIEWER].isMember(CONFIG_RESTRICT_TRANSFER_SYNTAXES)) | |
522 { | |
523 const Json::Value& config = configuration[CONFIG_WEB_VIEWER][CONFIG_RESTRICT_TRANSFER_SYNTAXES]; | |
524 | |
525 if (config.type() != Json::arrayValue) | |
526 { | |
527 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
528 } | |
529 | |
530 restrictTransferSyntaxes_ = true; | |
531 for (Json::Value::ArrayIndex i = 0; i < config.size(); i++) | |
532 { | |
533 if (config[i].type() != Json::stringValue) | |
534 { | |
535 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
536 } | |
537 else | |
538 { | |
539 std::string s = "Web viewer will use GDCM to decode transfer syntax " + config[i].asString(); | |
540 enabledTransferSyntaxes_.insert(config[i].asString()); | |
541 OrthancPluginLogWarning(context_, s.c_str()); | |
542 } | |
543 } | |
544 } | |
545 } | |
546 } | 373 } |
547 | 374 |
548 if (decodingThreads <= 0 || | 375 if (decodingThreads <= 0 || |
549 cacheSize <= 0) | 376 cacheSize <= 0) |
550 { | 377 { |
596 if (decodingThreads == 0) | 423 if (decodingThreads == 0) |
597 { | 424 { |
598 decodingThreads = 1; | 425 decodingThreads = 1; |
599 } | 426 } |
600 | 427 |
601 /* By default, use GDCM */ | |
602 bool enableGdcm = true; | |
603 | |
604 try | 428 try |
605 { | 429 { |
606 /* By default, a cache of 100 MB is used */ | 430 /* By default, a cache of 100 MB is used */ |
607 int cacheSize = 100; | 431 int cacheSize = 100; |
608 | 432 |
609 boost::filesystem::path cachePath; | 433 boost::filesystem::path cachePath; |
610 ParseConfiguration(enableGdcm, decodingThreads, cachePath, cacheSize); | 434 ParseConfiguration(decodingThreads, cachePath, cacheSize); |
611 | 435 |
612 std::string message = ("Web viewer using " + boost::lexical_cast<std::string>(decodingThreads) + | 436 std::string message = ("Web viewer using " + boost::lexical_cast<std::string>(decodingThreads) + |
613 " threads for the decoding of the DICOM images"); | 437 " threads for the decoding of the DICOM images"); |
614 OrthancPluginLogWarning(context_, message.c_str()); | 438 OrthancPluginLogWarning(context_, message.c_str()); |
615 | 439 |
691 } | 515 } |
692 return -1; | 516 return -1; |
693 } | 517 } |
694 | 518 |
695 | 519 |
696 /* Configure the DICOM decoder */ | |
697 if (enableGdcm) | |
698 { | |
699 // Replace the default decoder of DICOM images that is built in Orthanc | |
700 OrthancPluginLogWarning(context_, "Using GDCM instead of the DICOM decoder that is built in Orthanc"); | |
701 OrthancPluginRegisterDecodeImageCallback(context_, DecodeImageCallback); | |
702 } | |
703 else | |
704 { | |
705 OrthancPluginLogWarning(context_, "Using the DICOM decoder that is built in Orthanc (not using GDCM)"); | |
706 } | |
707 | |
708 | |
709 /* Install the callbacks */ | 520 /* Install the callbacks */ |
710 OrthancPluginRegisterRestCallbackNoLock(context_, "/web-viewer/series/(.*)", ServeCache<CacheBundle_SeriesInformation>); | 521 OrthancPluginRegisterRestCallbackNoLock(context_, "/web-viewer/series/(.*)", ServeCache<CacheBundle_SeriesInformation>); |
711 OrthancPluginRegisterRestCallbackNoLock(context_, "/web-viewer/is-stable-series/(.*)", IsStableSeries); | 522 OrthancPluginRegisterRestCallbackNoLock(context_, "/web-viewer/is-stable-series/(.*)", IsStableSeries); |
712 OrthancPluginRegisterRestCallbackNoLock(context_, "/web-viewer/instances/(.*)", ServeCache<CacheBundle_DecodedImage>); | 523 OrthancPluginRegisterRestCallbackNoLock(context_, "/web-viewer/instances/(.*)", ServeCache<CacheBundle_DecodedImage>); |
713 OrthancPluginRegisterRestCallbackNoLock(context, "/web-viewer/libs/(.*)", ServeEmbeddedFolder<Orthanc::EmbeddedResources::JAVASCRIPT_LIBS>); | 524 OrthancPluginRegisterRestCallbackNoLock(context, "/web-viewer/libs/(.*)", ServeEmbeddedFolder<Orthanc::EmbeddedResources::JAVASCRIPT_LIBS>); |