changeset 4734:b51c08bd5c38

added ITagVisitor::Action_Remove
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 05 Jul 2021 16:12:10 +0200
parents 1db3b79d97bd
children e17fdc43ef6c
files OrthancFramework/Sources/DicomParsing/DicomModification.cpp OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.cpp OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.h OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp OrthancFramework/Sources/DicomParsing/ITagVisitor.h OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp
diffstat 6 files changed, 484 insertions(+), 156 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/DicomParsing/DicomModification.cpp	Mon Jun 28 14:25:37 2021 +0200
+++ b/OrthancFramework/Sources/DicomParsing/DicomModification.cpp	Mon Jul 05 16:12:10 2021 +0200
@@ -84,49 +84,55 @@
     {
     }
 
-    virtual void VisitNotSupported(const std::vector<DicomTag>& parentTags,
-                                   const std::vector<size_t>& parentIndexes,
-                                   const DicomTag& tag,
-                                   ValueRepresentation vr)
+    virtual Action VisitNotSupported(const std::vector<DicomTag>& parentTags,
+                                     const std::vector<size_t>& parentIndexes,
+                                     const DicomTag& tag,
+                                     ValueRepresentation vr) ORTHANC_OVERRIDE
     {
+      return Action_None;
     }
 
-    virtual void VisitEmptySequence(const std::vector<DicomTag>& parentTags,
-                                    const std::vector<size_t>& parentIndexes,
-                                    const DicomTag& tag)
+    virtual Action VisitEmptySequence(const std::vector<DicomTag>& parentTags,
+                                      const std::vector<size_t>& parentIndexes,
+                                      const DicomTag& tag) ORTHANC_OVERRIDE
     {
+      return Action_None;
     }
 
-    virtual void VisitBinary(const std::vector<DicomTag>& parentTags,
-                             const std::vector<size_t>& parentIndexes,
-                             const DicomTag& tag,
-                             ValueRepresentation vr,
-                             const void* data,
-                             size_t size)
-    {
-    }
-
-    virtual void VisitIntegers(const std::vector<DicomTag>& parentTags,
+    virtual Action VisitBinary(const std::vector<DicomTag>& parentTags,
                                const std::vector<size_t>& parentIndexes,
                                const DicomTag& tag,
                                ValueRepresentation vr,
-                               const std::vector<int64_t>& values)
+                               const void* data,
+                               size_t size) ORTHANC_OVERRIDE
     {
+      return Action_None;
+    }
+
+    virtual Action VisitIntegers(const std::vector<DicomTag>& parentTags,
+                                 const std::vector<size_t>& parentIndexes,
+                                 const DicomTag& tag,
+                                 ValueRepresentation vr,
+                                 const std::vector<int64_t>& values) ORTHANC_OVERRIDE
+    {
+      return Action_None;
     }
 
-    virtual void VisitDoubles(const std::vector<DicomTag>& parentTags,
-                              const std::vector<size_t>& parentIndexes,
-                              const DicomTag& tag,
-                              ValueRepresentation vr,
-                              const std::vector<double>& value)
+    virtual Action VisitDoubles(const std::vector<DicomTag>& parentTags,
+                                const std::vector<size_t>& parentIndexes,
+                                const DicomTag& tag,
+                                ValueRepresentation vr,
+                                const std::vector<double>& value) ORTHANC_OVERRIDE
     {
+      return Action_None;
     }
 
-    virtual void VisitAttributes(const std::vector<DicomTag>& parentTags,
-                                 const std::vector<size_t>& parentIndexes,
-                                 const DicomTag& tag,
-                                 const std::vector<DicomTag>& value)
+    virtual Action VisitAttributes(const std::vector<DicomTag>& parentTags,
+                                   const std::vector<size_t>& parentIndexes,
+                                   const DicomTag& tag,
+                                   const std::vector<DicomTag>& value) ORTHANC_OVERRIDE
     {
+      return Action_None;
     }
 
     virtual Action VisitString(std::string& newValue,
@@ -134,7 +140,7 @@
                                const std::vector<size_t>& parentIndexes,
                                const DicomTag& tag,
                                ValueRepresentation vr,
-                               const std::string& value)
+                               const std::string& value) ORTHANC_OVERRIDE
     {
       /**
        * Note that all the tags in "uids_" have the VR UI (unique
--- a/OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.cpp	Mon Jun 28 14:25:37 2021 +0200
+++ b/OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.cpp	Mon Jul 05 16:12:10 2021 +0200
@@ -371,32 +371,38 @@
 #endif
 
 
-  void DicomWebJsonVisitor::VisitNotSupported(const std::vector<DicomTag> &parentTags,
-                                              const std::vector<size_t> &parentIndexes,
-                                              const DicomTag &tag,
-                                              ValueRepresentation vr)
+  ITagVisitor::Action
+  DicomWebJsonVisitor::VisitNotSupported(const std::vector<DicomTag> &parentTags,
+                                         const std::vector<size_t> &parentIndexes,
+                                         const DicomTag &tag,
+                                         ValueRepresentation vr)
   {
+    return Action_None;
   }
 
 
-  void DicomWebJsonVisitor::VisitEmptySequence(const std::vector<DicomTag>& parentTags,
-                                               const std::vector<size_t>& parentIndexes,
-                                               const DicomTag& tag)
+  ITagVisitor::Action
+  DicomWebJsonVisitor::VisitEmptySequence(const std::vector<DicomTag>& parentTags,
+                                          const std::vector<size_t>& parentIndexes,
+                                          const DicomTag& tag)
   {
     if (tag.GetElement() != 0x0000)
     {
       Json::Value& node = CreateNode(parentTags, parentIndexes, tag);
       node[KEY_VR] = EnumerationToString(ValueRepresentation_Sequence);
     }
+
+    return Action_None;
   }
   
 
-  void DicomWebJsonVisitor::VisitBinary(const std::vector<DicomTag>& parentTags,
-                                        const std::vector<size_t>& parentIndexes,
-                                        const DicomTag& tag,
-                                        ValueRepresentation vr,
-                                        const void* data,
-                                        size_t size)
+  ITagVisitor::Action
+  DicomWebJsonVisitor::VisitBinary(const std::vector<DicomTag>& parentTags,
+                                   const std::vector<size_t>& parentIndexes,
+                                   const DicomTag& tag,
+                                   ValueRepresentation vr,
+                                   const void* data,
+                                   size_t size)
   {
     assert(vr == ValueRepresentation_OtherByte ||
            vr == ValueRepresentation_OtherDouble ||
@@ -456,14 +462,17 @@
         }
       }
     }
+
+    return Action_None;
   }
 
 
-  void DicomWebJsonVisitor::VisitIntegers(const std::vector<DicomTag>& parentTags,
-                                          const std::vector<size_t>& parentIndexes,
-                                          const DicomTag& tag,
-                                          ValueRepresentation vr,
-                                          const std::vector<int64_t>& values)
+  ITagVisitor::Action
+  DicomWebJsonVisitor::VisitIntegers(const std::vector<DicomTag>& parentTags,
+                                     const std::vector<size_t>& parentIndexes,
+                                     const DicomTag& tag,
+                                     ValueRepresentation vr,
+                                     const std::vector<int64_t>& values)
   {
     if (tag.GetElement() != 0x0000 &&
         vr != ValueRepresentation_NotSupported)
@@ -482,13 +491,16 @@
         node[KEY_VALUE] = content;
       }
     }
+
+    return Action_None;
   }
 
-  void DicomWebJsonVisitor::VisitDoubles(const std::vector<DicomTag>& parentTags,
-                                         const std::vector<size_t>& parentIndexes,
-                                         const DicomTag& tag,
-                                         ValueRepresentation vr,
-                                         const std::vector<double>& values)
+  ITagVisitor::Action
+  DicomWebJsonVisitor::VisitDoubles(const std::vector<DicomTag>& parentTags,
+                                    const std::vector<size_t>& parentIndexes,
+                                    const DicomTag& tag,
+                                    ValueRepresentation vr,
+                                    const std::vector<double>& values)
   {
     if (tag.GetElement() != 0x0000 &&
         vr != ValueRepresentation_NotSupported)
@@ -507,13 +519,16 @@
         node[KEY_VALUE] = content;
       }
     }
+
+    return Action_None;
   }
 
   
-  void DicomWebJsonVisitor::VisitAttributes(const std::vector<DicomTag>& parentTags,
-                                            const std::vector<size_t>& parentIndexes,
-                                            const DicomTag& tag,
-                                            const std::vector<DicomTag>& values)
+  ITagVisitor::Action
+  DicomWebJsonVisitor::VisitAttributes(const std::vector<DicomTag>& parentTags,
+                                       const std::vector<size_t>& parentIndexes,
+                                       const DicomTag& tag,
+                                       const std::vector<DicomTag>& values)
   {
     if (tag.GetElement() != 0x0000)
     {
@@ -531,6 +546,8 @@
         node[KEY_VALUE] = content;
       }
     }
+
+    return Action_None;
   }
 
   
--- a/OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.h	Mon Jun 28 14:25:37 2021 +0200
+++ b/OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.h	Mon Jul 05 16:12:10 2021 +0200
@@ -85,43 +85,43 @@
     void FormatXml(std::string& target) const;
 #endif
 
-    virtual void VisitNotSupported(const std::vector<DicomTag>& parentTags,
-                                   const std::vector<size_t>& parentIndexes,
-                                   const DicomTag& tag,
-                                   ValueRepresentation vr)
+    virtual Action VisitNotSupported(const std::vector<DicomTag>& parentTags,
+                                     const std::vector<size_t>& parentIndexes,
+                                     const DicomTag& tag,
+                                     ValueRepresentation vr)
       ORTHANC_OVERRIDE;
 
-    virtual void VisitEmptySequence(const std::vector<DicomTag>& parentTags,
-                                    const std::vector<size_t>& parentIndexes,
-                                    const DicomTag& tag)
+    virtual Action VisitEmptySequence(const std::vector<DicomTag>& parentTags,
+                                      const std::vector<size_t>& parentIndexes,
+                                      const DicomTag& tag)
       ORTHANC_OVERRIDE;
 
-    virtual void VisitBinary(const std::vector<DicomTag>& parentTags,
-                             const std::vector<size_t>& parentIndexes,
-                             const DicomTag& tag,
-                             ValueRepresentation vr,
-                             const void* data,
-                             size_t size)
-      ORTHANC_OVERRIDE;
-
-    virtual void VisitIntegers(const std::vector<DicomTag>& parentTags,
+    virtual Action VisitBinary(const std::vector<DicomTag>& parentTags,
                                const std::vector<size_t>& parentIndexes,
                                const DicomTag& tag,
                                ValueRepresentation vr,
-                               const std::vector<int64_t>& values)
+                               const void* data,
+                               size_t size)
+      ORTHANC_OVERRIDE;
+
+    virtual Action VisitIntegers(const std::vector<DicomTag>& parentTags,
+                                 const std::vector<size_t>& parentIndexes,
+                                 const DicomTag& tag,
+                                 ValueRepresentation vr,
+                                 const std::vector<int64_t>& values)
       ORTHANC_OVERRIDE;
 
-    virtual void VisitDoubles(const std::vector<DicomTag>& parentTags,
-                              const std::vector<size_t>& parentIndexes,
-                              const DicomTag& tag,
-                              ValueRepresentation vr,
-                              const std::vector<double>& values)
+    virtual Action VisitDoubles(const std::vector<DicomTag>& parentTags,
+                                const std::vector<size_t>& parentIndexes,
+                                const DicomTag& tag,
+                                ValueRepresentation vr,
+                                const std::vector<double>& values)
       ORTHANC_OVERRIDE;
 
-    virtual void VisitAttributes(const std::vector<DicomTag>& parentTags,
-                                 const std::vector<size_t>& parentIndexes,
-                                 const DicomTag& tag,
-                                 const std::vector<DicomTag>& values)
+    virtual Action VisitAttributes(const std::vector<DicomTag>& parentTags,
+                                   const std::vector<size_t>& parentIndexes,
+                                   const DicomTag& tag,
+                                   const std::vector<DicomTag>& values)
       ORTHANC_OVERRIDE;
 
     virtual Action VisitString(std::string& newValue,
--- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Mon Jun 28 14:25:37 2021 +0200
+++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Mon Jul 05 16:12:10 2021 +0200
@@ -2340,7 +2340,7 @@
 
 
   // Forward declaration
-  static void ApplyVisitorToElement(DcmElement& element,
+  static bool ApplyVisitorToElement(DcmElement& element,
                                     ITagVisitor& visitor,
                                     const std::vector<DicomTag>& parentTags,
                                     const std::vector<size_t>& parentIndexes,
@@ -2356,6 +2356,8 @@
   {
     assert(parentTags.size() == parentIndexes.size());
 
+    std::set<DcmTagKey> toRemove;
+    
     for (unsigned long i = 0; i < dataset.card(); i++)
     {
       DcmElement* element = dataset.getElement(i);
@@ -2365,13 +2367,25 @@
       }
       else
       {
-        ApplyVisitorToElement(*element, visitor, parentTags, parentIndexes, encoding, hasCodeExtensions);
+        if (!ApplyVisitorToElement(*element, visitor, parentTags, parentIndexes, encoding, hasCodeExtensions))
+        {
+          toRemove.insert(element->getTag());
+        }
       }      
     }
+
+    // Remove all the tags that were planned for removal (cf. ITagVisitor::Action_Remove)
+    for (std::set<DcmTagKey>::const_iterator
+           it = toRemove.begin(); it != toRemove.end(); ++it)
+    {
+      std::unique_ptr<DcmElement> tmp(dataset.remove(*it));
+    }
   }
 
 
-  static void ApplyVisitorToLeaf(DcmElement& element,
+  // Returns "true" iff the element must be kept. If "false" is
+  // returned, the element will be removed.
+  static bool ApplyVisitorToLeaf(DcmElement& element,
                                  ITagVisitor& visitor,
                                  const std::vector<DicomTag>& parentTags,
                                  const std::vector<size_t>& parentIndexes,
@@ -2415,11 +2429,13 @@
       Uint16* data16 = NULL;
       Uint8* data = NULL;
 
+      ITagVisitor::Action action;
+      
       if ((element.getTag() == DCM_PixelData ||  // (*) New in Orthanc 1.9.1
            evr == EVR_OW) &&
           element.getUint16Array(data16) == EC_Normal)
       {
-        visitor.VisitBinary(parentTags, parentIndexes, tag, vr, data16, element.getLength());
+        action = visitor.VisitBinary(parentTags, parentIndexes, tag, vr, data16, element.getLength());
       }
       else if (evr != EVR_OW &&
                element.getUint8Array(data) == EC_Normal)
@@ -2432,14 +2448,27 @@
          * reimplemented in derived class "DcmPixelData"). However,
          * "getUint16Array()" works correctly, hence (*).
          **/
-        visitor.VisitBinary(parentTags, parentIndexes, tag, vr, data, element.getLength());
+        action = visitor.VisitBinary(parentTags, parentIndexes, tag, vr, data, element.getLength());
       }
       else
       {
-        visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr);
+        action = visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr);
       }
 
-      return;  // We're done
+      switch (action)
+      {
+        case ITagVisitor::Action_None:
+          return true;  // We're done
+
+        case ITagVisitor::Action_Remove:
+          return false;
+
+        case ITagVisitor::Action_Replace:
+          throw OrthancException(ErrorCode_NotImplemented, "Iterator cannot replace binary data");
+
+        default:
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
     }
 
 
@@ -2473,7 +2502,10 @@
       switch (action)
       {
         case ITagVisitor::Action_None:
-          break;
+          return true;
+
+        case ITagVisitor::Action_Remove:
+          return false;
 
         case ITagVisitor::Action_Replace:
         {
@@ -2481,20 +2513,20 @@
           if (element.putString(s.c_str()) != EC_Normal)
           {
             throw OrthancException(ErrorCode_InternalError,
-                                   "Cannot replace value of tag: " + tag.Format());
+                                   "Iterator cannot replace value of tag: " + tag.Format());
           }
 
-          break;
+          return true;
         }
 
         default:
           throw OrthancException(ErrorCode_InternalError);
       }
-
-      return;  // We're done
     }
 
 
+    ITagVisitor::Action action;
+    
     try
     {
       // http://support.dcmtk.org/docs/dcvr_8h-source.html
@@ -2522,7 +2554,7 @@
         case EVR_UI:  // unique identifier
         {
           Uint8* data = NULL;
-
+          
           if (element.getUint8Array(data) == EC_Normal)
           {
             const Uint32 length = element.getLength();
@@ -2536,30 +2568,30 @@
             if (l == length)
             {
               // Not a null-terminated plain string
-              visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr);
+              action = visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr);
             }
             else
             {
               std::string ignored;
               std::string s(reinterpret_cast<const char*>(data), l);
-              ITagVisitor::Action action = visitor.VisitString
-                (ignored, parentTags, parentIndexes, tag, vr,
-                 Toolbox::ConvertToUtf8(s, encoding, hasCodeExtensions));
-
-              if (action != ITagVisitor::Action_None)
-              {
-                LOG(WARNING) << "Cannot replace this string tag: "
-                             << FromDcmtkBridge::GetTagName(element)
-                             << " (" << tag.Format() << ")";
-              }
+              action = visitor.VisitString(ignored, parentTags, parentIndexes, tag, vr,
+                                           Toolbox::ConvertToUtf8(s, encoding, hasCodeExtensions));
             }
           }
           else
           {
-            visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr);
+            action = visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr);
           }
 
-          return;
+          if (action == ITagVisitor::Action_Replace)
+          {
+            LOG(WARNING) << "Iterator cannot replace this string tag: "
+                         << FromDcmtkBridge::GetTagName(element)
+                         << " (" << tag.Format() << ")";
+            return true;
+          }
+
+          break;
         }
     
         /**
@@ -2582,7 +2614,7 @@
             }
           }
 
-          visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values);
+          action = visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values);
           break;
         }
 
@@ -2602,7 +2634,7 @@
             }
           }
 
-          visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values);
+          action = visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values);
           break;
         }
 
@@ -2625,7 +2657,7 @@
             }
           }
 
-          visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values);
+          action = visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values);
           break;
         }
 
@@ -2645,7 +2677,7 @@
             }
           }
 
-          visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values);
+          action = visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values);
           break;
         }
 
@@ -2666,7 +2698,7 @@
             }
           }
 
-          visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values);
+          action = visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values);
           break;
         }
 
@@ -2689,7 +2721,7 @@
             }
           }
 
-          visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values);
+          action = visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values);
           break;
         }
 
@@ -2716,7 +2748,7 @@
           }
 
           assert(vr == ValueRepresentation_AttributeTag);
-          visitor.VisitAttributes(parentTags, parentIndexes, tag, values);
+          action = visitor.VisitAttributes(parentTags, parentIndexes, tag, values);
           break;
         }
 
@@ -2728,7 +2760,7 @@
 
         case EVR_SQ:  // sequence of items
         {
-          return;
+          return true;
         }
         
         
@@ -2751,8 +2783,8 @@
         case EVR_PixelData:  // used internally for uncompressed pixeld data
         case EVR_OverlayData:  // used internally for overlay data
         {
-          visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr);
-          return;
+          action = visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr);
+          break;
         }
         
 
@@ -2761,21 +2793,38 @@
          **/ 
 
         default:
-          return;
+          return true;
+      }
+
+      switch (action)
+      {
+        case ITagVisitor::Action_None:
+          return true;  // We're done
+
+        case ITagVisitor::Action_Remove:
+          return false;
+
+        case ITagVisitor::Action_Replace:
+          throw OrthancException(ErrorCode_NotImplemented, "Iterator cannot replace non-string-like data");
+
+        default:
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
       }
     }
     catch (boost::bad_lexical_cast&)
     {
-      return;
+      return true;
     }
     catch (std::bad_cast&)
     {
-      return;
+      return true;
     }
   }
 
 
-  static void ApplyVisitorToElement(DcmElement& element,
+  // Returns "true" iff the element must be kept. If "false" is
+  // returned, the element will be removed.
+  static bool ApplyVisitorToElement(DcmElement& element,
                                     ITagVisitor& visitor,
                                     const std::vector<DicomTag>& parentTags,
                                     const std::vector<size_t>& parentIndexes,
@@ -2788,7 +2837,7 @@
 
     if (element.isLeaf())
     {
-      ApplyVisitorToLeaf(element, visitor, parentTags, parentIndexes, tag, encoding, hasCodeExtensions);
+      return ApplyVisitorToLeaf(element, visitor, parentTags, parentIndexes, tag, encoding, hasCodeExtensions);
     }
     else
     {
@@ -2799,7 +2848,22 @@
 
       if (sequence.card() == 0)
       {
-        visitor.VisitEmptySequence(parentTags, parentIndexes, tag);
+        ITagVisitor::Action action = visitor.VisitEmptySequence(parentTags, parentIndexes, tag);
+
+        switch (action)
+        {
+          case ITagVisitor::Action_None:
+            return true;
+
+          case ITagVisitor::Action_Remove:
+            return false;
+
+          case ITagVisitor::Action_Replace:
+            throw OrthancException(ErrorCode_NotImplemented, "Iterator cannot replace sequences");
+
+          default:
+            throw OrthancException(ErrorCode_ParameterOutOfRange);
+        }
       }
       else
       {
@@ -2814,6 +2878,8 @@
           DcmItem* child = sequence.getItem(i);
           ApplyVisitorToDataset(*child, visitor, tags, indexes, encoding, hasCodeExtensions);
         }
+
+        return true;  // Keep
       }
     }
   }
--- a/OrthancFramework/Sources/DicomParsing/ITagVisitor.h	Mon Jun 28 14:25:37 2021 +0200
+++ b/OrthancFramework/Sources/DicomParsing/ITagVisitor.h	Mon Jul 05 16:12:10 2021 +0200
@@ -35,6 +35,7 @@
     enum Action
     {
       Action_Replace,
+      Action_Remove,  // New in Orthanc 1.9.5
       Action_None
     };
 
@@ -42,46 +43,47 @@
     {
     }
 
-    // Visiting a DICOM element that is internal to DCMTK
-    virtual void VisitNotSupported(const std::vector<DicomTag>& parentTags,
+    // Visiting a DICOM element that is internal to DCMTK. Can return
+    // "Remove" or "None".
+    virtual Action VisitNotSupported(const std::vector<DicomTag>& parentTags,
+                                     const std::vector<size_t>& parentIndexes,
+                                     const DicomTag& tag,
+                                     ValueRepresentation vr) = 0;
+
+    // SQ - can return "Remove" or "None"
+    virtual Action VisitEmptySequence(const std::vector<DicomTag>& parentTags,
+                                      const std::vector<size_t>& parentIndexes,
+                                      const DicomTag& tag) = 0;
+
+    // SL, SS, UL, US - can return "Remove" or "None"
+    virtual Action VisitIntegers(const std::vector<DicomTag>& parentTags,
+                                 const std::vector<size_t>& parentIndexes,
+                                 const DicomTag& tag,
+                                 ValueRepresentation vr,
+                                 const std::vector<int64_t>& values) = 0;
+
+    // FL, FD, OD, OF - can return "Remove" or "None"
+    virtual Action VisitDoubles(const std::vector<DicomTag>& parentTags,
+                                const std::vector<size_t>& parentIndexes,
+                                const DicomTag& tag,
+                                ValueRepresentation vr,
+                                const std::vector<double>& values) = 0;
+
+    // AT - can return "Remove" or "None"
+    virtual Action VisitAttributes(const std::vector<DicomTag>& parentTags,
                                    const std::vector<size_t>& parentIndexes,
                                    const DicomTag& tag,
-                                   ValueRepresentation vr) = 0;
+                                   const std::vector<DicomTag>& values) = 0;
 
-    // SQ
-    virtual void VisitEmptySequence(const std::vector<DicomTag>& parentTags,
-                                    const std::vector<size_t>& parentIndexes,
-                                    const DicomTag& tag) = 0;
-
-    // SL, SS, UL, US
-    virtual void VisitIntegers(const std::vector<DicomTag>& parentTags,
+    // OB, OL, OW, UN - can return "Remove" or "None"
+    virtual Action VisitBinary(const std::vector<DicomTag>& parentTags,
                                const std::vector<size_t>& parentIndexes,
                                const DicomTag& tag,
                                ValueRepresentation vr,
-                               const std::vector<int64_t>& values) = 0;
-
-    // FL, FD, OD, OF
-    virtual void VisitDoubles(const std::vector<DicomTag>& parentTags,
-                              const std::vector<size_t>& parentIndexes,
-                              const DicomTag& tag,
-                              ValueRepresentation vr,
-                              const std::vector<double>& values) = 0;
+                               const void* data,
+                               size_t size) = 0;
 
-    // AT
-    virtual void VisitAttributes(const std::vector<DicomTag>& parentTags,
-                                 const std::vector<size_t>& parentIndexes,
-                                 const DicomTag& tag,
-                                 const std::vector<DicomTag>& values) = 0;
-
-    // OB, OL, OW, UN
-    virtual void VisitBinary(const std::vector<DicomTag>& parentTags,
-                             const std::vector<size_t>& parentIndexes,
-                             const DicomTag& tag,
-                             ValueRepresentation vr,
-                             const void* data,
-                             size_t size) = 0;
-
-    // Visiting an UTF-8 string
+    // Visiting an UTF-8 string - can return "Replace", "Remove" or "None"
     virtual Action VisitString(std::string& newValue,
                                const std::vector<DicomTag>& parentTags,
                                const std::vector<size_t>& parentIndexes,
--- a/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp	Mon Jun 28 14:25:37 2021 +0200
+++ b/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp	Mon Jul 05 16:12:10 2021 +0200
@@ -61,6 +61,9 @@
 #include <dcmtk/dcmdata/dcdeftag.h>
 #include <dcmtk/dcmdata/dcelem.h>
 #include <dcmtk/dcmdata/dcvrat.h>
+#include <dcmtk/dcmdata/dcbytstr.h>
+#include <dcmtk/dcmdata/dcvrss.h>
+#include <dcmtk/dcmdata/dcvrfl.h>
 
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/lexical_cast.hpp>
@@ -2710,6 +2713,240 @@
 }
 
 
+TEST(FromDcmtkBridge, VisitorRemoveTag)
+{
+  class V : public ITagVisitor
+  {
+  private:
+    uint32_t seen_;
+    
+  public:
+    V() : seen_(0)
+    {
+    }
+
+    unsigned int GetSeen() const
+    {
+      return seen_;
+    }
+    
+    virtual Action VisitNotSupported(const std::vector<DicomTag>& parentTags,
+                                     const std::vector<size_t>& parentIndexes,
+                                     const DicomTag& tag,
+                                     ValueRepresentation vr) ORTHANC_OVERRIDE
+    {
+      seen_ |= (1 << 0);
+      
+      if (parentTags.size() == 0u &&
+          parentIndexes.size() == 0u &&
+          DcmTagKey(tag.GetGroup(), tag.GetElement()) == DCM_PixelData)
+      {
+        return Action_Remove;
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }        
+    }
+
+    virtual Action VisitEmptySequence(const std::vector<DicomTag>& parentTags,
+                                      const std::vector<size_t>& parentIndexes,
+                                      const DicomTag& tag) ORTHANC_OVERRIDE
+    {
+      seen_ |= (1 << 1);
+      
+      if (parentTags.size() == 1u &&
+          parentIndexes.size() == 1u &&
+          parentTags[0] == DICOM_TAG_REFERENCED_IMAGE_SEQUENCE &&
+          parentIndexes[0] == 0u &&
+          DcmTagKey(tag.GetGroup(), tag.GetElement()) == DCM_ReferencedPatientSequence)
+      {
+        return Action_Remove;
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }        
+    }
+
+    virtual Action VisitIntegers(const std::vector<DicomTag>& parentTags,
+                                 const std::vector<size_t>& parentIndexes,
+                                 const DicomTag& tag,
+                                 ValueRepresentation vr,
+                                 const std::vector<int64_t>& values) ORTHANC_OVERRIDE
+    {
+      seen_ |= (1 << 2);
+      
+      if (parentTags.size() == 0u &&
+          parentIndexes.size() == 0u &&
+          DcmTagKey(tag.GetGroup(), tag.GetElement()) == DCM_TagAngleSecondAxis &&
+          values.size() == 2 &&
+          values[0] == 12 &&
+          values[1] == 13)
+      {
+        return Action_Remove;
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+    }
+
+    virtual Action VisitDoubles(const std::vector<DicomTag>& parentTags,
+                                const std::vector<size_t>& parentIndexes,
+                                const DicomTag& tag,
+                                ValueRepresentation vr,
+                                const std::vector<double>& values) ORTHANC_OVERRIDE
+    {
+      seen_ |= (1 << 3);
+      
+      if (parentTags.size() == 1u &&
+          parentIndexes.size() == 1u &&
+          parentTags[0] == DICOM_TAG_REFERENCED_IMAGE_SEQUENCE &&
+          parentIndexes[0] == 0u &&
+          DcmTagKey(tag.GetGroup(), tag.GetElement()) == DCM_ExaminedBodyThickness &&
+          values.size() == 3 &&
+          std::abs(values[0] - 42.0f) <= 0.001f &&
+          std::abs(values[1] - 43.0f) <= 0.001f &&
+          std::abs(values[2] - 47.0f) <= 0.001f)
+      {
+        return Action_Remove;
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+    }
+
+    virtual Action VisitAttributes(const std::vector<DicomTag>& parentTags,
+                                   const std::vector<size_t>& parentIndexes,
+                                   const DicomTag& tag,
+                                   const std::vector<DicomTag>& values) ORTHANC_OVERRIDE
+    {
+      seen_ |= (1 << 4);
+      
+      if (parentTags.size() == 1u &&
+          parentIndexes.size() == 1u &&
+          parentTags[0] == DICOM_TAG_REFERENCED_IMAGE_SEQUENCE &&
+          parentIndexes[0] == 0u &&
+          DcmTagKey(tag.GetGroup(), tag.GetElement()) == DCM_DimensionIndexPointer &&
+          values.size() == 2 &&
+          values[0] == DICOM_TAG_STUDY_DATE &&
+          values[1] == DICOM_TAG_STUDY_TIME)
+      {
+        return Action_Remove;
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+    }
+
+    virtual Action VisitBinary(const std::vector<DicomTag>& parentTags,
+                               const std::vector<size_t>& parentIndexes,
+                               const DicomTag& tag,
+                               ValueRepresentation vr,
+                               const void* data,
+                               size_t size) ORTHANC_OVERRIDE
+    {
+      seen_ |= (1 << 5);
+      
+      if (parentTags.size() == 1u &&
+          parentIndexes.size() == 1u &&
+          parentTags[0] == DICOM_TAG_REFERENCED_IMAGE_SEQUENCE &&
+          parentIndexes[0] == 0u &&
+          tag.GetGroup() == 0x0011 &&
+          tag.GetElement() == 0x1311 &&
+          size == 4u &&
+          memcmp(data, "abcd", 4) == 0)
+      {
+        return Action_Remove;
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+    }
+
+    virtual Action VisitString(std::string& newValue,
+                               const std::vector<DicomTag>& parentTags,
+                               const std::vector<size_t>& parentIndexes,
+                               const DicomTag& tag,
+                               ValueRepresentation vr,
+                               const std::string& value) ORTHANC_OVERRIDE
+    {
+      seen_ |= (1 << 6);
+      return Action_Remove;
+    }
+  };
+
+
+  std::unique_ptr<ParsedDicomFile> dicom;
+
+  {
+    Json::Value v = Json::objectValue;
+    v["PatientName"] = "Hello";
+    v["ReferencedSOPClassUID"] = "1.2.840.10008.5.1.4.1.1.4";
+    v["ReferencedImageSequence"][0]["ReferencedSOPClassUID"] = "1.2.840.10008.5.1.4.1.1.4";
+    v["ReferencedImageSequence"][0]["ReferencedSOPInstanceUID"] = "1.2.840.113619.2.176.2025.1499492.7040.1171286241.719";
+    v["ReferencedImageSequence"][0]["ReferencedPatientSequence"] = Json::arrayValue;  // Empty sequence
+    v["ReferencedImageSequence"][0]["0011,1311"] = "abcd";  // Binary
+
+    dicom.reset(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "PrivateCreator"));
+
+    {
+      // Test value multiplicity (cannot be done using "ParsedDicomFile::CreateFromJson()")
+      const int16_t a[] = { 12, 13 };
+      std::unique_ptr<DcmSignedShort> s(new DcmSignedShort(DCM_TagAngleSecondAxis));  // VisitIntegers()
+      ASSERT_TRUE(s->putSint16Array(a, 2).good());
+      dicom->GetDcmtkObject().getDataset()->insert(s.release());
+    }
+  
+    {
+      const float a[] = { 42, 43, 47 };
+      std::unique_ptr<DcmFloatingPointSingle> s(new DcmFloatingPointSingle(DCM_ExaminedBodyThickness));  // VisitDoubles()
+      ASSERT_TRUE(s->putFloat32Array(a, 3).good());
+      DcmItem *item = NULL;
+      ASSERT_TRUE(dicom->GetDcmtkObject().getDataset()->findAndGetSequenceItem(DCM_ReferencedImageSequence, item, 0).good());
+      item->insert(s.release());
+    }
+  
+    {
+      const uint16_t a[] = { 0x0008, 0x0020, 0x0008, 0x0030 };
+      std::unique_ptr<DcmAttributeTag> s(new DcmAttributeTag(DCM_DimensionIndexPointer));  // VisitAttributes()
+      ASSERT_TRUE(s->putUint16Array(a, 2).good());
+      DcmItem *item = NULL;
+      ASSERT_TRUE(dicom->GetDcmtkObject().getDataset()->findAndGetSequenceItem(DCM_ReferencedImageSequence, item, 0).good());
+      item->insert(s.release());
+    }
+  
+    ASSERT_TRUE(dicom->GetDcmtkObject().getDataset()->insert(new DcmByteString(DCM_PixelData)).good());  // VisitNotSupported()
+  }
+
+  {
+    V visitor;
+    dicom->Apply(visitor);
+    ASSERT_EQ(127u, visitor.GetSeen());  // Make sure all the methods have been applied
+  }
+
+  {
+    Json::Value b;
+    dicom->DatasetToJson(b, DicomToJsonFormat_Short, DicomToJsonFlags_Default, 0);
+    ASSERT_EQ(Json::objectValue, b.type());
+
+    Json::Value::Members members = b.getMemberNames();
+    ASSERT_EQ(1u, members.size());
+    ASSERT_EQ("0008,1140", members[0]);
+
+    // Check that "b["0008,1140"]" is a sequence with one single empty object
+    ASSERT_EQ(Json::arrayValue, b["0008,1140"].type());
+    ASSERT_EQ(1u, b["0008,1140"].size());
+    ASSERT_EQ(Json::objectValue, b["0008,1140"][0].type());
+    ASSERT_EQ(0u, b["0008,1140"][0].size());
+  }
+}
+
+
 
 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1