diff OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp @ 4696:dd6274412ff4

new configuration option "ExternalDictionaries" to load external DICOM dictionaries
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 17 Jun 2021 15:47:21 +0200
parents 651f069c7f77
children 569d9ef165b1
line wrap: on
line diff
--- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Thu Jun 17 14:00:34 2021 +0200
+++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Thu Jun 17 15:47:21 2021 +0200
@@ -117,6 +117,9 @@
 #endif
 
 
+static bool hasExternalDictionaries_ = false;
+
+
 namespace Orthanc
 {
   static bool IsBinaryTag(const DcmTag& key)
@@ -226,22 +229,22 @@
     }
 
     
-#define DCMTK_TO_CTYPE_CONVERTER(converter, cType, dcmtkType, getter, toStringFunction)  \
+#define DCMTK_TO_CTYPE_CONVERTER(converter, cType, dcmtkType, getter, toStringFunction) \
                                                                         \
     struct converter                                                    \
     {                                                                   \
       typedef cType CType;                                              \
                                                                         \
       ORTHANC_FORCE_INLINE                                              \
-      static bool Apply(CType& result,                                  \
-                        DcmElement& element,                            \
-                        size_t i)                                       \
+        static bool Apply(CType& result,                                \
+                          DcmElement& element,                          \
+                          size_t i)                                     \
       {                                                                 \
         return dynamic_cast<dcmtkType&>(element).getter(result, i).good(); \
       }                                                                 \
                                                                         \
       ORTHANC_FORCE_INLINE                                              \
-      static std::string ToString(CType value)                          \
+        static std::string ToString(CType value)                        \
       {                                                                 \
         return toStringFunction(value);                                 \
       }                                                                 \
@@ -285,15 +288,15 @@
 
   void FromDcmtkBridge::InitializeDictionary(bool loadPrivateDictionary)
   {
-    LOG(INFO) << "Using DCTMK version: " << DCMTK_VERSION_NUMBER;
+    CLOG(INFO, DICOM) << "Using DCTMK version: " << DCMTK_VERSION_NUMBER;
     
+#if DCMTK_USE_EMBEDDED_DICTIONARIES == 1
     {
       DictionaryLocker locker;
 
       locker->clear();
 
-#if DCMTK_USE_EMBEDDED_DICTIONARIES == 1
-      LOG(INFO) << "Loading the embedded dictionaries";
+      CLOG(INFO, DICOM) << "Loading the embedded dictionaries";
       /**
        * Do not load DICONDE dictionary, it breaks the other tags. The
        * command "strace storescu 2>&1 |grep dic" shows that DICONDE
@@ -305,15 +308,16 @@
 
       if (loadPrivateDictionary)
       {
-        LOG(INFO) << "Loading the embedded dictionary of private tags";
+        CLOG(INFO, DICOM) << "Loading the embedded dictionary of private tags";
         LoadEmbeddedDictionary(*locker, FrameworkResources::DICTIONARY_PRIVATE);
       }
       else
       {
-        LOG(INFO) << "The dictionary of private tags has not been loaded";
+        CLOG(INFO, DICOM) << "The dictionary of private tags has not been loaded";
       }
-
+    }
 #else
+    {
       std::vector<std::string> dictionaries;
       
       const char* env = std::getenv(DCM_DICT_ENVIRONMENT_VARIABLE);
@@ -331,21 +335,17 @@
       {
         boost::filesystem::path base = DCMTK_DICTIONARY_DIR;
         dictionaries.push_back((base / "dicom.dic").string());
-        dictionaries.push_back((base / "private.dic").string());
-      }
-
-      for (size_t i = 0; i < dictionaries.size(); i++)
-      {
-        LOG(WARNING) << "Loading external DICOM dictionary: \"" << dictionaries[i] << "\"";
-        
-        if (!locker->loadDictionary(dictionaries[i].c_str()))
+
+        if (loadPrivateDictionary)
         {
-          throw OrthancException(ErrorCode_InexistentFile);
+          dictionaries.push_back((base / "private.dic").string());
         }
       }
 
+      LoadExternalDictionaries(dictionaries);
+      hasExternalDictionaries_ = false;  // Fix the side-effect of "LoadExternalDictionaries()"
+    }
 #endif
-    }
 
     /* make sure data dictionary is loaded */
     if (!dcmDataDict.isDictionaryLoaded())
@@ -367,6 +367,27 @@
   }
 
 
+  void FromDcmtkBridge::LoadExternalDictionaries(const std::vector<std::string>& dictionaries)
+  {
+    DictionaryLocker locker;
+
+    CLOG(INFO, DICOM) << "Clearing the DICOM dictionary";
+    locker->clear();
+
+    for (size_t i = 0; i < dictionaries.size(); i++)
+    {
+      LOG(WARNING) << "Loading external DICOM dictionary: \"" << dictionaries[i] << "\"";
+        
+      if (!locker->loadDictionary(dictionaries[i].c_str()))
+      {
+        throw OrthancException(ErrorCode_InexistentFile);
+      }
+    }    
+
+    hasExternalDictionaries_ = true;
+  }
+
+
   void FromDcmtkBridge::RegisterDictionaryTag(const DicomTag& tag,
                                               ValueRepresentation vr,
                                               const std::string& name,
@@ -392,10 +413,10 @@
     
     DcmEVR evr = ToDcmtkBridge::Convert(vr);
 
-    LOG(INFO) << "Registering tag in dictionary: (" << tag.Format() << ") "
-              << (DcmVR(evr).getValidVRName()) << " " 
-              << name << " (multiplicity: " << minMultiplicity << "-" 
-              << (arbitrary ? "n" : boost::lexical_cast<std::string>(maxMultiplicity)) << ")";
+    CLOG(INFO, DICOM) << "Registering tag in dictionary: (" << tag.Format() << ") "
+                      << (DcmVR(evr).getValidVRName()) << " " 
+                      << name << " (multiplicity: " << minMultiplicity << "-" 
+                      << (arbitrary ? "n" : boost::lexical_cast<std::string>(maxMultiplicity)) << ")";
 
     std::unique_ptr<DcmDictEntry>  entry;
     if (privateCreator.empty())
@@ -983,7 +1004,7 @@
     {
       // The "0" below lets "LeafValueToJson()" take care of "TooLong" values
       std::unique_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement
-                                  (element, flags, 0, encoding, hasCodeExtensions, ignoreTagLength));
+                                    (element, flags, 0, encoding, hasCodeExtensions, ignoreTagLength));
 
       if (ignoreTagLength.find(GetTag(element)) == ignoreTagLength.end())
       {
@@ -1113,18 +1134,69 @@
   }
 
 
-
   static std::string GetTagNameInternal(DcmTag& tag)
   {
+    if (!hasExternalDictionaries_)
     {
-      // Some patches for important tags because of different DICOM
-      // dictionaries between DCMTK versions
+      /**
+       * Some patches for important tags because of different DICOM
+       * dictionaries between DCMTK versions. Since Orthanc 1.9.4, we
+       * don't apply these patches if external dictionaries are
+       * loaded, notably for compatibility with DICONDE. In Orthanc <=
+       * 1.9.3, this was done by method "DicomTag::GetMainTagsName()".
+       **/
+      
       DicomTag tmp(tag.getGroup(), tag.getElement());
-      std::string n = tmp.GetMainTagsName();
-      if (n.size() != 0)
-      {
-        return n;
-      }
+
+      if (tmp == DICOM_TAG_ACCESSION_NUMBER)
+        return "AccessionNumber";
+
+      if (tmp == DICOM_TAG_SOP_INSTANCE_UID)
+        return "SOPInstanceUID";
+
+      if (tmp == DICOM_TAG_PATIENT_ID)
+        return "PatientID";
+
+      if (tmp == DICOM_TAG_SERIES_INSTANCE_UID)
+        return "SeriesInstanceUID";
+
+      if (tmp == DICOM_TAG_STUDY_INSTANCE_UID)
+        return "StudyInstanceUID"; 
+
+      if (tmp == DICOM_TAG_PIXEL_DATA)
+        return "PixelData";
+
+      if (tmp == DICOM_TAG_IMAGE_INDEX)
+        return "ImageIndex";
+
+      if (tmp == DICOM_TAG_INSTANCE_NUMBER)
+        return "InstanceNumber";
+
+      if (tmp == DICOM_TAG_NUMBER_OF_SLICES)
+        return "NumberOfSlices";
+
+      if (tmp == DICOM_TAG_NUMBER_OF_FRAMES)
+        return "NumberOfFrames";
+
+      if (tmp == DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES)
+        return "CardiacNumberOfImages";
+
+      if (tmp == DICOM_TAG_IMAGES_IN_ACQUISITION)
+        return "ImagesInAcquisition";
+
+      if (tmp == DICOM_TAG_PATIENT_NAME)
+        return "PatientName";
+
+      if (tmp == DICOM_TAG_IMAGE_POSITION_PATIENT)
+        return "ImagePositionPatient";
+
+      if (tmp == DICOM_TAG_IMAGE_ORIENTATION_PATIENT)
+        return "ImageOrientationPatient";
+
+      // New in Orthanc 1.6.0, as tagged as "RETIRED_" since DCMTK 3.6.4
+      if (tmp == DICOM_TAG_OTHER_PATIENT_IDS)
+        return "OtherPatientIDs";
+
       // End of patches
     }
 
@@ -1217,7 +1289,7 @@
     }
     else
     {
-      LOG(INFO) << "Unknown DICOM tag: \"" << name << "\"";
+      CLOG(INFO, DICOM) << "Unknown DICOM tag: \"" << name << "\"";
       throw OrthancException(ErrorCode_UnknownDicomTag);
     }
 #endif
@@ -1441,14 +1513,14 @@
 
         if (known)
         {
-          LOG(INFO) << "Transcoded an image from transfer syntax "
-                    << GetTransferSyntaxUid(sourceSyntax) << " to "
-                    << GetTransferSyntaxUid(syntax);
+          CLOG(INFO, DICOM) << "Transcoded an image from transfer syntax "
+                            << GetTransferSyntaxUid(sourceSyntax) << " to "
+                            << GetTransferSyntaxUid(syntax);
         }
         else
         {
-          LOG(INFO) << "Transcoded an image from unknown transfer syntax to "
-                    << GetTransferSyntaxUid(syntax);
+          CLOG(INFO, DICOM) << "Transcoded an image from unknown transfer syntax to "
+                            << GetTransferSyntaxUid(syntax);
         }
         
         return true;
@@ -1507,16 +1579,16 @@
         return ValueRepresentation_OtherByte;
 
 #if DCMTK_VERSION_NUMBER >= 361
-        case EVR_OD:
-          return ValueRepresentation_OtherDouble;
+      case EVR_OD:
+        return ValueRepresentation_OtherDouble;
 #endif
 
       case EVR_OF:
         return ValueRepresentation_OtherFloat;
 
 #if DCMTK_VERSION_NUMBER >= 362
-        case EVR_OL:
-          return ValueRepresentation_OtherLong;
+      case EVR_OL:
+        return ValueRepresentation_OtherLong;
 #endif
 
       case EVR_OW:
@@ -1695,9 +1767,9 @@
           throw OrthancException(ErrorCode_ParameterOutOfRange);
 
 
-        /**
-         * String types.
-         **/
+          /**
+           * String types.
+           **/
       
         case EVR_DS:  // decimal string
         case EVR_IS:  // integer string
@@ -2189,7 +2261,7 @@
   void FromDcmtkBridge::InitializeCodecs()
   {
 #if ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1
-    LOG(INFO) << "Registering JPEG Lossless codecs in DCMTK";
+    CLOG(INFO, DICOM) << "Registering JPEG Lossless codecs in DCMTK";
     DJLSDecoderRegistration::registerCodecs();
 # if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
     DJLSEncoderRegistration::registerCodecs();
@@ -2197,14 +2269,14 @@
 #endif
 
 #if ORTHANC_ENABLE_DCMTK_JPEG == 1
-    LOG(INFO) << "Registering JPEG codecs in DCMTK";
+    CLOG(INFO, DICOM) << "Registering JPEG codecs in DCMTK";
     DJDecoderRegistration::registerCodecs(); 
 # if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
     DJEncoderRegistration::registerCodecs();
 # endif
 #endif
 
-    LOG(INFO) << "Registering RLE codecs in DCMTK";
+    CLOG(INFO, DICOM) << "Registering RLE codecs in DCMTK";
     DcmRLEDecoderRegistration::registerCodecs(); 
 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
     DcmRLEEncoderRegistration::registerCodecs();