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