changeset 991:2f76b92addd4

keep private tags during anonymization
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 02 Jul 2014 11:56:08 +0200
parents 7cbcd580cd21
children af014624dac1 b3d4f8a30324 40e5255e7dc5
files OrthancServer/DicomModification.cpp OrthancServer/DicomModification.h OrthancServer/FromDcmtkBridge.cpp OrthancServer/FromDcmtkBridge.h OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp OrthancServer/ParsedDicomFile.cpp OrthancServer/ParsedDicomFile.h UnitTestsSources/FromDcmtkTests.cpp
diffstat 8 files changed, 108 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/DicomModification.cpp	Tue Jul 01 17:17:45 2014 +0200
+++ b/OrthancServer/DicomModification.cpp	Wed Jul 02 11:56:08 2014 +0200
@@ -96,12 +96,18 @@
   {
     removals_.erase(tag);
     replacements_.erase(tag);
+
+    if (FromDcmtkBridge::IsPrivateTag(tag))
+    {
+      privateTagsToKeep_.insert(tag);
+    }
   }
 
   void DicomModification::Remove(const DicomTag& tag)
   {
     removals_.insert(tag);
     replacements_.erase(tag);
+    privateTagsToKeep_.erase(tag);
   }
 
   bool DicomModification::IsRemoved(const DicomTag& tag) const
@@ -113,6 +119,7 @@
                                   const std::string& value)
   {
     removals_.erase(tag);
+    privateTagsToKeep_.erase(tag);
     replacements_[tag] = value;
   }
 
@@ -153,6 +160,7 @@
     removePrivateTags_ = true;
     level_ = ResourceType_Patient;
     uidMap_.clear();
+    privateTagsToKeep_.clear();
 
     // This is Table E.1-1 from PS 3.15-2008 - DICOM Part 15: Security and System Management Profiles
     removals_.insert(DicomTag(0x0008, 0x0014));  // Instance Creator UID
@@ -261,11 +269,11 @@
     // (1) Remove the private tags, if need be
     if (removePrivateTags_)
     {
-      toModify.RemovePrivateTags();
+      toModify.RemovePrivateTags(privateTagsToKeep_);
     }
 
     // (2) Remove the tags specified by the user
-    for (Removals::const_iterator it = removals_.begin(); 
+    for (SetOfTags::const_iterator it = removals_.begin(); 
          it != removals_.end(); ++it)
     {
       toModify.Remove(*it);
--- a/OrthancServer/DicomModification.h	Tue Jul 01 17:17:45 2014 +0200
+++ b/OrthancServer/DicomModification.h	Wed Jul 02 11:56:08 2014 +0200
@@ -46,15 +46,16 @@
      **/
 
   private:
-    typedef std::set<DicomTag> Removals;
+    typedef std::set<DicomTag> SetOfTags;
     typedef std::map<DicomTag, std::string> Replacements;
     typedef std::map< std::pair<ResourceType, std::string>, std::string>  UidMap;
 
-    Removals removals_;
+    SetOfTags removals_;
     Replacements replacements_;
     bool removePrivateTags_;
     ResourceType level_;
     UidMap uidMap_;
+    SetOfTags privateTagsToKeep_;
 
     void MapDicomIdentifier(ParsedDicomFile& dicom,
                             ResourceType level);
--- a/OrthancServer/FromDcmtkBridge.cpp	Tue Jul 01 17:17:45 2014 +0200
+++ b/OrthancServer/FromDcmtkBridge.cpp	Wed Jul 02 11:56:08 2014 +0200
@@ -168,12 +168,32 @@
   }
 
 
+  DicomTag FromDcmtkBridge::Convert(const DcmTag& tag)
+  {
+    return DicomTag(tag.getGTag(), tag.getETag());
+  }
+
+
   DicomTag FromDcmtkBridge::GetTag(const DcmElement& element)
   {
     return DicomTag(element.getGTag(), element.getETag());
   }
 
 
+  bool FromDcmtkBridge::IsPrivateTag(DcmTag& tag)
+  {
+    return (tag.getPrivateCreator() != NULL ||
+            !strcmp("PrivateCreator", tag.getTagName()));  // TODO - This may change with future versions of DCMTK
+  }
+
+
+  bool FromDcmtkBridge::IsPrivateTag(const DicomTag& tag)
+  {
+    DcmTag tmp(tag.GetGroup(), tag.GetElement());
+    return IsPrivateTag(tmp);
+  }
+
+
   DicomValue* FromDcmtkBridge::ConvertLeafElement(DcmElement& element,
                                                   Encoding encoding)
   {
@@ -516,7 +536,7 @@
         isxdigit(name[1]) &&
         isxdigit(name[2]) &&
         isxdigit(name[3]) &&
-        name[4] == '-' &&
+        (name[4] == '-' || name[4] == ',') &&
         isxdigit(name[5]) &&
         isxdigit(name[6]) &&
         isxdigit(name[7]) &&
--- a/OrthancServer/FromDcmtkBridge.h	Tue Jul 01 17:17:45 2014 +0200
+++ b/OrthancServer/FromDcmtkBridge.h	Wed Jul 02 11:56:08 2014 +0200
@@ -48,8 +48,14 @@
 
     static void Convert(DicomMap& target, DcmDataset& dataset);
 
+    static DicomTag Convert(const DcmTag& tag);
+
     static DicomTag GetTag(const DcmElement& element);
 
+    static bool IsPrivateTag(DcmTag& tag);
+
+    static bool IsPrivateTag(const DicomTag& tag);
+
     static DicomValue* ConvertLeafElement(DcmElement& element,
                                           Encoding encoding);
 
--- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Tue Jul 01 17:17:45 2014 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Wed Jul 02 11:56:08 2014 +0200
@@ -174,7 +174,8 @@
         ParseListOfTags(target, request["Keep"], TagOperation_Keep);
       }
 
-      if (target.GetReplacement(DICOM_TAG_PATIENT_NAME) == patientName)
+      if (target.IsReplaced(DICOM_TAG_PATIENT_NAME) &&
+          target.GetReplacement(DICOM_TAG_PATIENT_NAME) == patientName)
       {
         // Overwrite the random Patient's Name by one that is more
         // user-friendly (provided none was specified by the user)
--- a/OrthancServer/ParsedDicomFile.cpp	Tue Jul 01 17:17:45 2014 +0200
+++ b/OrthancServer/ParsedDicomFile.cpp	Wed Jul 02 11:56:08 2014 +0200
@@ -756,24 +756,42 @@
 
 
 
-  void ParsedDicomFile::RemovePrivateTags()
+  void ParsedDicomFile::RemovePrivateTagsInternal(const std::set<DicomTag>* toKeep)
   {
+    DcmDataset& dataset = *pimpl_->file_->getDataset();
+
+    // Loop over the dataset to detect its private tags
     typedef std::list<DcmElement*> Tags;
-
     Tags privateTags;
 
-    DcmDataset& dataset = *pimpl_->file_->getDataset();
     for (unsigned long i = 0; i < dataset.card(); i++)
     {
       DcmElement* element = dataset.getElement(i);
       DcmTag tag(element->getTag());
-      if (!strcmp("PrivateCreator", tag.getTagName()) ||  // TODO - This may change with future versions of DCMTK
-          tag.getPrivateCreator() != NULL)
+
+      // Is this a private tag?
+      if (FromDcmtkBridge::IsPrivateTag(tag))
       {
-        privateTags.push_back(element);
+        bool remove = true;
+
+        // Check whether this private tag is to be kept
+        if (toKeep != NULL)
+        {
+          DicomTag tmp = FromDcmtkBridge::Convert(tag);
+          if (toKeep->find(tmp) != toKeep->end())
+          {
+            remove = false;  // Keep it
+          }
+        }
+            
+        if (remove)
+        {
+          privateTags.push_back(element);
+        }
       }
     }
 
+    // Loop over the detected private tags to remove them
     for (Tags::iterator it = privateTags.begin(); 
          it != privateTags.end(); ++it)
     {
--- a/OrthancServer/ParsedDicomFile.h	Tue Jul 01 17:17:45 2014 +0200
+++ b/OrthancServer/ParsedDicomFile.h	Wed Jul 02 11:56:08 2014 +0200
@@ -51,6 +51,8 @@
     void Setup(const char* content,
                size_t size);
 
+    void RemovePrivateTagsInternal(const std::set<DicomTag>* toKeep);
+
   public:
     ParsedDicomFile();  // Create a minimal DICOM instance
 
@@ -79,7 +81,15 @@
                  const std::string& value,
                  DicomReplaceMode mode = DicomReplaceMode_InsertIfAbsent);
 
-    void RemovePrivateTags();
+    void RemovePrivateTags()
+    {
+      RemovePrivateTagsInternal(NULL);
+    }
+
+    void RemovePrivateTags(const std::set<DicomTag>& toKeep)
+    {
+      RemovePrivateTagsInternal(&toKeep);
+    }
 
     bool GetTagValue(std::string& value,
                      const DicomTag& tag);
--- a/UnitTestsSources/FromDcmtkTests.cpp	Tue Jul 01 17:17:45 2014 +0200
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Wed Jul 02 11:56:08 2014 +0200
@@ -40,6 +40,7 @@
 #include "../Core/ImageFormats/ImageBuffer.h"
 #include "../Core/ImageFormats/PngReader.h"
 #include "../Core/ImageFormats/PngWriter.h"
+#include "../Core/Uuid.h"
 
 using namespace Orthanc;
 
@@ -85,6 +86,36 @@
 }
 
 
+TEST(DicomModification, Anonymization)
+{
+  const DicomTag privateTag(0x0045, 0x0010);
+  ASSERT_TRUE(FromDcmtkBridge::IsPrivateTag(privateTag));
+
+  ParsedDicomFile o;
+  o.Replace(DICOM_TAG_PATIENT_NAME, "coucou");
+  o.Replace(privateTag, "private tag");
+
+  std::string s;
+  ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME));
+  ASSERT_FALSE(Toolbox::IsUuid(s));
+
+  DicomModification m;
+  m.SetupAnonymization();
+  m.Keep(privateTag);
+
+  m.Apply(o);
+
+  ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME));
+  ASSERT_TRUE(Toolbox::IsUuid(s));
+  ASSERT_TRUE(o.GetTagValue(s, privateTag));
+  ASSERT_EQ("private tag", s);
+  
+  m.SetupAnonymization();
+  m.Apply(o);
+  ASSERT_FALSE(o.GetTagValue(s, privateTag));
+}
+
+
 #include <dcmtk/dcmdata/dcuid.h>
 
 TEST(DicomModification, Png)