changeset 6475:3f855c1e43c4 pixel-anon

allow IDicomModifier to create a new ParsedDicomFile
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sat, 22 Nov 2025 12:25:29 +0100
parents 95652230a6b7
children a85d8643792e
files OrthancFramework/Sources/DicomParsing/DicomModification.cpp OrthancFramework/Sources/DicomParsing/DicomModification.h OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp OrthancFramework/UnitTestsSources/JobsTests.cpp OrthancServer/Sources/ServerContext.cpp
diffstat 5 files changed, 49 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/DicomParsing/DicomModification.cpp	Sat Nov 22 10:37:33 2025 +0100
+++ b/OrthancFramework/Sources/DicomParsing/DicomModification.cpp	Sat Nov 22 12:25:29 2025 +0100
@@ -958,7 +958,7 @@
     }        
   }
 
-  void DicomModification::Apply(ParsedDicomFile& toModify)
+  void DicomModification::Apply(std::unique_ptr<ParsedDicomFile>& toModify)
   {
     // Check the request
     assert(ResourceType_Patient + 1 == ResourceType_Study &&
@@ -1027,7 +1027,7 @@
     // is provided
     if (identifierGenerator_ != NULL)
     {
-      toModify.ExtractDicomSummary(currentSource_, ORTHANC_MAXIMUM_TAG_LENGTH);
+      toModify->ExtractDicomSummary(currentSource_, ORTHANC_MAXIMUM_TAG_LENGTH);
     }
 
     // (1) Make sure the relationships are updated with the ids that we force too
@@ -1038,7 +1038,7 @@
       {
         std::string original;
         std::string replacement = GetReplacementAsString(DICOM_TAG_STUDY_INSTANCE_UID);
-        const_cast<const ParsedDicomFile&>(toModify).GetTagValue(original, DICOM_TAG_STUDY_INSTANCE_UID);
+        const_cast<const ParsedDicomFile&>(*toModify).GetTagValue(original, DICOM_TAG_STUDY_INSTANCE_UID);
         RegisterMappedDicomIdentifier(original, replacement, ResourceType_Study);
       }
 
@@ -1046,7 +1046,7 @@
       {
         std::string original;
         std::string replacement = GetReplacementAsString(DICOM_TAG_SERIES_INSTANCE_UID);
-        const_cast<const ParsedDicomFile&>(toModify).GetTagValue(original, DICOM_TAG_SERIES_INSTANCE_UID);
+        const_cast<const ParsedDicomFile&>(*toModify).GetTagValue(original, DICOM_TAG_SERIES_INSTANCE_UID);
         RegisterMappedDicomIdentifier(original, replacement, ResourceType_Series);
       }
 
@@ -1054,7 +1054,7 @@
       {
         std::string original;
         std::string replacement = GetReplacementAsString(DICOM_TAG_SOP_INSTANCE_UID);
-        const_cast<const ParsedDicomFile&>(toModify).GetTagValue(original, DICOM_TAG_SOP_INSTANCE_UID);
+        const_cast<const ParsedDicomFile&>(*toModify).GetTagValue(original, DICOM_TAG_SOP_INSTANCE_UID);
         RegisterMappedDicomIdentifier(original, replacement, ResourceType_Instance);
       }
     }
@@ -1063,21 +1063,21 @@
     // (2) Remove the private tags, if need be
     if (removePrivateTags_)
     {
-      toModify.RemovePrivateTags(privateTagsToKeep_);
+      toModify->RemovePrivateTags(privateTagsToKeep_);
     }
 
     // (3) Clear the tags specified by the user
     for (SetOfTags::const_iterator it = clearings_.begin(); 
          it != clearings_.end(); ++it)
     {
-      toModify.Clear(*it, true /* only clear if the tag exists in the original file */);
+      toModify->Clear(*it, true /* only clear if the tag exists in the original file */);
     }
 
     // (4) Remove the tags specified by the user
     for (SetOfTags::const_iterator it = removals_.begin(); 
          it != removals_.end(); ++it)
     {
-      toModify.Remove(*it);
+      toModify->Remove(*it);
     }
 
     // (5) Replace the tags
@@ -1085,7 +1085,7 @@
          it != replacements_.end(); ++it)
     {
       assert(it->second != NULL);
-      toModify.Replace(it->first, *it->second, true /* decode data URI scheme */,
+      toModify->Replace(it->first, *it->second, true /* decode data URI scheme */,
                        DicomReplaceMode_InsertIfAbsent, privateCreator_);
     }
 
@@ -1099,7 +1099,7 @@
       }
       else
       {
-        MapDicomTags(toModify, ResourceType_Study);
+        MapDicomTags(*toModify, ResourceType_Study);
       }
     }
 
@@ -1112,7 +1112,7 @@
       }
       else
       {
-        MapDicomTags(toModify, ResourceType_Series);
+        MapDicomTags(*toModify, ResourceType_Series);
       }
     }
 
@@ -1125,7 +1125,7 @@
       }
       else
       {
-        MapDicomTags(toModify, ResourceType_Instance);
+        MapDicomTags(*toModify, ResourceType_Instance);
       }
     }
 
@@ -1136,11 +1136,11 @@
 
       if (updateReferencedRelationships_)
       {
-        const_cast<const ParsedDicomFile&>(toModify).Apply(visitor);
+        const_cast<const ParsedDicomFile&>(*toModify).Apply(visitor);
       }
       else
       {
-        visitor.RemoveRelationships(toModify);
+        visitor.RemoveRelationships(*toModify);
       }
     }
 
@@ -1149,7 +1149,7 @@
          it != removeSequences_.end(); ++it)
     {
       assert(it->GetPrefixLength() > 0);
-      toModify.RemovePath(*it);
+      toModify->RemovePath(*it);
     }
 
     for (SequenceReplacements::const_iterator it = sequenceReplacements_.begin();
@@ -1157,8 +1157,8 @@
     {
       assert(*it != NULL);
       assert((*it)->GetPath().GetPrefixLength() > 0);
-      toModify.ReplacePath((*it)->GetPath(), (*it)->GetValue(), true /* decode data URI scheme */,
-                           DicomReplaceMode_InsertIfAbsent, privateCreator_);
+      toModify->ReplacePath((*it)->GetPath(), (*it)->GetValue(), true /* decode data URI scheme */,
+                            DicomReplaceMode_InsertIfAbsent, privateCreator_);
     }
 
   }
--- a/OrthancFramework/Sources/DicomParsing/DicomModification.h	Sat Nov 22 10:37:33 2025 +0100
+++ b/OrthancFramework/Sources/DicomParsing/DicomModification.h	Sat Nov 22 12:25:29 2025 +0100
@@ -62,7 +62,9 @@
       {
       }
 
-      virtual bool Apply(ParsedDicomFile& dicom) = 0;                       
+      // It is allowed for the implementation to replace "dicom" by a
+      // brand new ParsedDicomFile instance
+      virtual void Apply(std::unique_ptr<ParsedDicomFile>& dicom) = 0;
     };
 
   private:
@@ -249,7 +251,8 @@
 
     void SetupAnonymization(DicomVersion version);
 
-    void Apply(ParsedDicomFile& toModify);
+    // The "toModify" might be replaced by a new object
+    void Apply(std::unique_ptr<ParsedDicomFile>& toModify);
 
     void SetAllowManualIdentifiers(bool check);
 
--- a/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp	Sat Nov 22 10:37:33 2025 +0100
+++ b/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp	Sat Nov 22 12:25:29 2025 +0100
@@ -116,7 +116,7 @@
     std::unique_ptr<ParsedDicomFile> f(o.Clone(false));
     if (i > 4)
       o.ReplacePlainString(DICOM_TAG_SERIES_INSTANCE_UID, "coucou");
-    m.Apply(*f);
+    m.Apply(f);
     f->SaveToFile(b);
   }
 }
@@ -135,26 +135,26 @@
   ASSERT_EQ(0x1020, privateTag2.GetElement());
 
   std::string s;
-  ParsedDicomFile o(true);
-  o.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "coucou");
-  ASSERT_FALSE(o.GetTagValue(s, privateTag));
-  o.Insert(privateTag, "private tag", false, "OrthancCreator");
-  ASSERT_TRUE(o.GetTagValue(s, privateTag));
+  std::unique_ptr<ParsedDicomFile> o(new ParsedDicomFile(true));
+  o->ReplacePlainString(DICOM_TAG_PATIENT_NAME, "coucou");
+  ASSERT_FALSE(o->GetTagValue(s, privateTag));
+  o->Insert(privateTag, "private tag", false, "OrthancCreator");
+  ASSERT_TRUE(o->GetTagValue(s, privateTag));
   ASSERT_STREQ("private tag", s.c_str());
 
-  ASSERT_FALSE(o.GetTagValue(s, privateTag2));
-  ASSERT_THROW(o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_ThrowIfAbsent, "OrthancCreator"), OrthancException);
-  ASSERT_FALSE(o.GetTagValue(s, privateTag2));
-  o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_IgnoreIfAbsent, "OrthancCreator");
-  ASSERT_FALSE(o.GetTagValue(s, privateTag2));
-  o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_InsertIfAbsent, "OrthancCreator");
-  ASSERT_TRUE(o.GetTagValue(s, privateTag2));
+  ASSERT_FALSE(o->GetTagValue(s, privateTag2));
+  ASSERT_THROW(o->Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_ThrowIfAbsent, "OrthancCreator"), OrthancException);
+  ASSERT_FALSE(o->GetTagValue(s, privateTag2));
+  o->Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_IgnoreIfAbsent, "OrthancCreator");
+  ASSERT_FALSE(o->GetTagValue(s, privateTag2));
+  o->Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_InsertIfAbsent, "OrthancCreator");
+  ASSERT_TRUE(o->GetTagValue(s, privateTag2));
   ASSERT_STREQ("hello", s.c_str());
-  o.Replace(privateTag2, std::string("hello world"), false, DicomReplaceMode_InsertIfAbsent, "OrthancCreator");
-  ASSERT_TRUE(o.GetTagValue(s, privateTag2));
+  o->Replace(privateTag2, std::string("hello world"), false, DicomReplaceMode_InsertIfAbsent, "OrthancCreator");
+  ASSERT_TRUE(o->GetTagValue(s, privateTag2));
   ASSERT_STREQ("hello world", s.c_str());
 
-  ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME));
+  ASSERT_TRUE(o->GetTagValue(s, DICOM_TAG_PATIENT_NAME));
   ASSERT_FALSE(Toolbox::IsUuid(s));
 
   DicomModification m;
@@ -163,14 +163,14 @@
 
   m.Apply(o);
 
-  ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME));
+  ASSERT_TRUE(o->GetTagValue(s, DICOM_TAG_PATIENT_NAME));
   ASSERT_TRUE(Toolbox::IsUuid(s));
-  ASSERT_TRUE(o.GetTagValue(s, privateTag));
+  ASSERT_TRUE(o->GetTagValue(s, privateTag));
   ASSERT_STREQ("private tag", s.c_str());
   
   m.SetupAnonymization(DicomVersion_2008);
   m.Apply(o);
-  ASSERT_FALSE(o.GetTagValue(s, privateTag));
+  ASSERT_FALSE(o->GetTagValue(s, privateTag));
 }
 
 
@@ -2772,7 +2772,7 @@
       modif.Replace(DicomPath(DICOM_TAG_PATIENT_NAME), "Hello1", false);
       modif.Replace(DicomPath::Parse("ReferencedImageSequence[1].ReferencedSOPClassUID"), "Hello2", false);
       modif.Replace(DicomPath::Parse("RelatedSeriesSequence[0].PurposeOfReferenceCodeSequence[0].CodeValue"), "Hello3", false);
-      modif.Apply(*dicom);
+      modif.Apply(dicom);
     }
     
     Json::Value vv;
@@ -2794,7 +2794,7 @@
       modif.Remove(DicomPath(DICOM_TAG_PATIENT_NAME));
       modif.Remove(DicomPath::Parse("ReferencedImageSequence[1].ReferencedSOPClassUID"));
       modif.Remove(DicomPath::Parse("RelatedSeriesSequence[0].PurposeOfReferenceCodeSequence"));
-      modif.Apply(*dicom);
+      modif.Apply(dicom);
     }
     
     Json::Value vv;
@@ -2815,8 +2815,8 @@
     {
       DicomModification modif;
       modif.SetupAnonymization(DicomVersion_2023b);
-      modif.Apply(*dicom1);
-      modif.Apply(*dicom2);
+      modif.Apply(dicom1);
+      modif.Apply(dicom2);
     }
 
     // Same anonymization context and same input DICOM => hence, same output DICOM    
@@ -2844,7 +2844,7 @@
       modif.SetupAnonymization(DicomVersion_2023b);
       modif.Keep(DicomPath::Parse("ReferencedImageSequence[1].ReferencedSOPInstanceUID"));
       modif.Keep(DicomPath::Parse("RelatedSeriesSequence"));
-      modif.Apply(*dicom);
+      modif.Apply(dicom);
     }
     
     Json::Value vv;
--- a/OrthancFramework/UnitTestsSources/JobsTests.cpp	Sat Nov 22 10:37:33 2025 +0100
+++ b/OrthancFramework/UnitTestsSources/JobsTests.cpp	Sat Nov 22 12:25:29 2025 +0100
@@ -1084,7 +1084,7 @@
     modification.Remove(DICOM_TAG_SERIES_DESCRIPTION);
     modification.Replace(DICOM_TAG_PATIENT_NAME, "Test 4", true);
 
-    modification.Apply(*modified);
+    modification.Apply(modified);
 
     s = 42;
     modification.Serialize(s);
@@ -1095,7 +1095,7 @@
     ASSERT_EQ(ResourceType_Series, modification.GetLevel());
     
     std::unique_ptr<ParsedDicomFile> second(source.Clone(true));
-    modification.Apply(*second);
+    modification.Apply(second);
 
     std::string t;
     ASSERT_TRUE(second->GetTagValue(t, DICOM_TAG_STUDY_DESCRIPTION));
--- a/OrthancServer/Sources/ServerContext.cpp	Sat Nov 22 10:37:33 2025 +0100
+++ b/OrthancServer/Sources/ServerContext.cpp	Sat Nov 22 12:25:29 2025 +0100
@@ -2140,7 +2140,7 @@
       }
     }
 
-    modification.Apply(*dicomFile);
+    modification.Apply(dicomFile);
 
     if (transcode)
     {