changeset 3827:638906dcfe32 transcoding

integration mainline->transcoding
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 10 Apr 2020 16:18:17 +0200
parents 6762506ef4fb (diff) e82bd07c384e (current diff)
children 4fde7933e504
files Core/DicomNetworking/DicomStoreUserConnection.h Core/Enumerations.h UnitTestsSources/FromDcmtkTests.cpp
diffstat 22 files changed, 573 insertions(+), 149 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Fri Apr 10 16:12:10 2020 +0200
+++ b/CMakeLists.txt	Fri Apr 10 16:18:17 2020 +0200
@@ -26,7 +26,7 @@
 set(ENABLE_ZLIB ON)
 
 # To test transcoding
-#set(ENABLE_DCMTK_TRANSCODING ON)
+set(ENABLE_DCMTK_TRANSCODING ON)
 
 set(HAS_EMBEDDED_RESOURCES ON)
 
--- a/Core/DicomNetworking/DicomStoreUserConnection.h	Fri Apr 10 16:12:10 2020 +0200
+++ b/Core/DicomNetworking/DicomStoreUserConnection.h	Fri Apr 10 16:18:17 2020 +0200
@@ -79,10 +79,6 @@
     bool ProposeStorageClass(const std::string& sopClassUid,
                              const std::set<DicomTransferSyntax>& syntaxes);
     
-    bool LookupPresentationContext(uint8_t& presentationContextId,
-                                   const std::string& sopClassUid,
-                                   DicomTransferSyntax transferSyntax);
-        
   public:
     DicomStoreUserConnection(const DicomAssociationParameters& params);
     
@@ -124,6 +120,10 @@
     void PrepareStorageClass(const std::string& sopClassUid,
                              DicomTransferSyntax syntax);
 
+    bool LookupPresentationContext(uint8_t& presentationContextId,
+                                   const std::string& sopClassUid,
+                                   DicomTransferSyntax transferSyntax);
+        
     bool NegotiatePresentationContext(uint8_t& presentationContextId,
                                       const std::string& sopClassUid,
                                       DicomTransferSyntax transferSyntax);
--- a/Core/DicomNetworking/DicomUserConnection.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/Core/DicomNetworking/DicomUserConnection.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -240,6 +240,10 @@
                                       std::vector<const char*>& asFallback,
                                       const std::string& aet)
   {
+    // Presentation context IDs must be odd numbers, hence the
+    // increments by 2:
+    // http://dicom.nema.org/medical/dicom/2019e/output/chtml/part08/sect_9.3.2.2.html
+    
     Check(ASC_addPresentationContext(params, presentationContextId, 
                                      sopClass.c_str(), asPreferred, 1),
           aet, "initializing");
@@ -1177,6 +1181,28 @@
                              "Unable to negotiate a presentation context with AET " +
                              remoteAet_);
     }
+
+#if 0
+    // Manual loop over the accepted transfer syntaxes
+    LST_HEAD **l = &pimpl_->params_->DULparams.acceptedPresentationContext;
+    if (*l != NULL)
+    {
+      DUL_PRESENTATIONCONTEXT* pc = (DUL_PRESENTATIONCONTEXT*) LST_Head(l);
+      LST_Position(l, (LST_NODE*)pc);
+      while (pc)
+      {
+        if (pc->result == ASC_P_ACCEPTANCE)
+        {
+          printf("Accepted: %d [%s] [%s]\n", pc->presentationContextID, pc->abstractSyntax, pc->acceptedTransferSyntax);
+        }
+        else
+        {
+          printf("Rejected: %d [%s]\n", pc->presentationContextID, pc->abstractSyntax);
+        }
+        pc = (DUL_PRESENTATIONCONTEXT*) LST_Next(l);
+      }
+    }
+#endif
   }
 
   void DicomUserConnection::Close()
--- a/Core/DicomParsing/FromDcmtkBridge.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/Core/DicomParsing/FromDcmtkBridge.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -121,6 +121,13 @@
 #endif
 
 
+#include <dcmtk/dcmdata/dcrledrg.h>
+#if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
+#  include <dcmtk/dcmdata/dcrleerg.h>
+#  include <dcmtk/dcmimage/diregist.h>  // include to support color images
+#endif
+
+
 namespace Orthanc
 {
   static bool IsBinaryTag(const DcmTag& key)
@@ -1198,51 +1205,30 @@
     }
   }
 
-  bool FromDcmtkBridge::SaveToMemoryBuffer(std::string& buffer,
-                                           DcmDataset& dataSet)
+
+
+  static bool SaveToMemoryBufferInternal(std::string& buffer,
+                                         DcmFileFormat& dicom,
+                                         E_TransferSyntax xfer)
   {
-    // Determine the transfer syntax which shall be used to write the
-    // information to the file. We always switch to the Little Endian
-    // syntax, with explicit length.
-
-    // http://support.dcmtk.org/docs/dcxfer_8h-source.html
-
-
-    /**
-     * Note that up to Orthanc 0.7.1 (inclusive), the
-     * "EXS_LittleEndianExplicit" was always used to save the DICOM
-     * dataset into memory. We now keep the original transfer syntax
-     * (if available).
-     **/
-    E_TransferSyntax xfer = dataSet.getOriginalXfer();
-    if (xfer == EXS_Unknown)
-    {
-      // No information about the original transfer syntax: This is
-      // most probably a DICOM dataset that was read from memory.
-      xfer = EXS_LittleEndianExplicit;
-    }
-
     E_EncodingType encodingType = /*opt_sequenceType*/ EET_ExplicitLength;
 
-    // Create the meta-header information
-    DcmFileFormat ff(&dataSet);
-    ff.validateMetaInfo(xfer);
-    ff.removeInvalidGroups();
-
     // Create a memory buffer with the proper size
     {
-      const uint32_t estimatedSize = ff.calcElementLength(xfer, encodingType);  // (*)
+      const uint32_t estimatedSize = dicom.calcElementLength(xfer, encodingType);  // (*)
       buffer.resize(estimatedSize);
     }
 
     DcmOutputBufferStream ob(&buffer[0], buffer.size());
 
     // Fill the memory buffer with the meta-header and the dataset
-    ff.transferInit();
-    OFCondition c = ff.write(ob, xfer, encodingType, NULL,
-                             /*opt_groupLength*/ EGL_recalcGL,
-                             /*opt_paddingType*/ EPD_withoutPadding);
-    ff.transferEnd();
+    dicom.transferInit();
+    OFCondition c = dicom.write(ob, xfer, encodingType, NULL,
+                                /*opt_groupLength*/ EGL_recalcGL,
+                                /*opt_paddingType*/ EPD_noChange,
+                                /*padlen*/ 0, /*subPadlen*/ 0, /*instanceLength*/ 0,
+                                EWM_updateMeta /* creates new SOP instance UID on lossy */);
+    dicom.transferEnd();
 
     if (c.good())
     {
@@ -1265,6 +1251,86 @@
       return false;
     }
   }
+  
+
+  bool FromDcmtkBridge::SaveToMemoryBuffer(std::string& buffer,
+                                           DcmDataset& dataSet)
+  {
+    // Determine the transfer syntax which shall be used to write the
+    // information to the file. If not possible, switch to the Little
+    // Endian syntax, with explicit length.
+
+    // http://support.dcmtk.org/docs/dcxfer_8h-source.html
+
+
+    /**
+     * Note that up to Orthanc 0.7.1 (inclusive), the
+     * "EXS_LittleEndianExplicit" was always used to save the DICOM
+     * dataset into memory. We now keep the original transfer syntax
+     * (if available).
+     **/
+    E_TransferSyntax xfer = dataSet.getOriginalXfer();
+    if (xfer == EXS_Unknown)
+    {
+      // No information about the original transfer syntax: This is
+      // most probably a DICOM dataset that was read from memory.
+      xfer = EXS_LittleEndianExplicit;
+    }
+
+    // Create the meta-header information
+    DcmFileFormat ff(&dataSet);
+    ff.validateMetaInfo(xfer);
+    ff.removeInvalidGroups();
+
+    return SaveToMemoryBufferInternal(buffer, ff, xfer);
+  }
+
+
+  bool FromDcmtkBridge::SaveToMemoryBuffer(std::string& buffer,
+                                           DcmFileFormat& dicom)
+  {
+    E_TransferSyntax xfer = dicom.getDataset()->getOriginalXfer();
+    if (xfer == EXS_Unknown)
+    {
+      throw OrthancException(ErrorCode_InternalError,
+                             "Cannot write a DICOM instance with unknown transfer syntax");
+    }
+    else if (!dicom.validateMetaInfo(xfer).good())
+    {
+      throw OrthancException(ErrorCode_InternalError,
+                             "Cannot setup the transfer syntax to write a DICOM instance");
+    }
+    else
+    {
+      return SaveToMemoryBufferInternal(buffer, dicom, xfer);
+    }
+  }
+
+
+  bool FromDcmtkBridge::Transcode(std::string& buffer,
+                                  DcmFileFormat& dicom,
+                                  DicomTransferSyntax syntax,
+                                  const DcmRepresentationParameter* representation)
+  {
+    E_TransferSyntax xfer;
+    if (!LookupDcmtkTransferSyntax(xfer, syntax))
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+    else
+    {
+      if (!dicom.getDataset()->chooseRepresentation(xfer, representation).good() ||
+          !dicom.getDataset()->canWriteXfer(xfer) ||
+          !dicom.validateMetaInfo(xfer, EWM_updateMeta).good())
+      {
+        return false;
+      }
+
+      dicom.removeInvalidGroups();
+      
+      return SaveToMemoryBufferInternal(buffer, dicom, xfer);
+    }
+  }
 
 
   ValueRepresentation FromDcmtkBridge::LookupValueRepresentation(const DicomTag& tag)
@@ -2073,6 +2139,12 @@
     DJEncoderRegistration::registerCodecs();
 # endif
 #endif
+
+    LOG(INFO) << "Registering RLE codecs in DCMTK";
+    DcmRLEDecoderRegistration::registerCodecs(); 
+#if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
+    DcmRLEEncoderRegistration::registerCodecs();
+#endif
   }
 
 
@@ -2093,6 +2165,11 @@
     DJEncoderRegistration::cleanup();
 # endif
 #endif
+
+    DcmRLEDecoderRegistration::cleanup(); 
+#if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
+    DcmRLEEncoderRegistration::cleanup();
+#endif
   }
 
 
--- a/Core/DicomParsing/FromDcmtkBridge.h	Fri Apr 10 16:12:10 2020 +0200
+++ b/Core/DicomParsing/FromDcmtkBridge.h	Fri Apr 10 16:18:17 2020 +0200
@@ -205,6 +205,14 @@
     static bool SaveToMemoryBuffer(std::string& buffer,
                                    DcmDataset& dataSet);
 
+    static bool SaveToMemoryBuffer(std::string& buffer,
+                                   DcmFileFormat& dicom);
+
+    static bool Transcode(std::string& buffer,
+                          DcmFileFormat& dicom,
+                          DicomTransferSyntax syntax,
+                          const DcmRepresentationParameter* representation);
+
     static ValueRepresentation Convert(DcmEVR vr);
 
     static ValueRepresentation LookupValueRepresentation(const DicomTag& tag);
--- a/Core/Enumerations.h	Fri Apr 10 16:12:10 2020 +0200
+++ b/Core/Enumerations.h	Fri Apr 10 16:18:17 2020 +0200
@@ -753,7 +753,7 @@
     DicomAssociationRole_Scu,
     DicomAssociationRole_Scp
   };
-  
+
 
   /**
    * WARNING: Do not change the explicit values in the enumerations
--- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -227,7 +227,7 @@
     toStore.SetParsedDicomFile(dicom);
 
     ServerContext& context = OrthancRestApi::GetContext(call);
-    StoreStatus status = context.Store(id, toStore);
+    StoreStatus status = context.Store(id, toStore, StoreInstanceMode_Default);
 
     if (status == StoreStatus_Failure)
     {
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -139,7 +139,7 @@
     toStore.SetBuffer(dicom);
 
     std::string publicId;
-    StoreStatus status = context.Store(publicId, toStore);
+    StoreStatus status = context.Store(publicId, toStore, StoreInstanceMode_Default);
 
     OrthancRestApi::GetApi(call).AnswerStoredInstance(call, toStore, status);
   }
--- a/OrthancServer/ServerContext.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/OrthancServer/ServerContext.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -242,7 +242,8 @@
     isJobsEngineUnserialized_(false),
     metricsRegistry_(new MetricsRegistry),
     isHttpServerSecure_(true),
-    isExecuteLuaEnabled_(false)
+    isExecuteLuaEnabled_(false),
+    overwriteInstances_(false)
   {
     {
       OrthancConfiguration::ReaderLock lock;
@@ -339,8 +340,28 @@
 
 
   StoreStatus ServerContext::Store(std::string& resultPublicId,
-                                   DicomInstanceToStore& dicom)
+                                   DicomInstanceToStore& dicom,
+                                   StoreInstanceMode mode)
   {
+    bool overwrite;
+    switch (mode)
+    {
+      case StoreInstanceMode_Default:
+        overwrite = overwriteInstances_;
+        break;
+        
+      case StoreInstanceMode_OverwriteDuplicate:
+        overwrite = true;
+        break;
+        
+      case StoreInstanceMode_IgnoreDuplicate:
+        overwrite = false;
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }    
+    
     try
     {
       MetricsRegistry::Timer timer(GetMetricsRegistry(), "orthanc_store_dicom_duration_ms");
@@ -404,7 +425,8 @@
 
       typedef std::map<MetadataType, std::string>  InstanceMetadata;
       InstanceMetadata  instanceMetadata;
-      StoreStatus status = index_.Store(instanceMetadata, dicom, attachments);
+      StoreStatus status = index_.Store(
+        instanceMetadata, dicom, attachments, overwrite);
 
       // Only keep the metadata for the "instance" level
       dicom.GetMetadata().clear();
--- a/OrthancServer/ServerContext.h	Fri Apr 10 16:12:10 2020 +0200
+++ b/OrthancServer/ServerContext.h	Fri Apr 10 16:18:17 2020 +0200
@@ -221,6 +221,7 @@
     std::unique_ptr<MetricsRegistry>  metricsRegistry_;
     bool isHttpServerSecure_;
     bool isExecuteLuaEnabled_;
+    bool overwriteInstances_;
 
     std::unique_ptr<StorageCommitmentReports>  storageCommitmentReports_;
 
@@ -275,7 +276,8 @@
                        size_t size);
 
     StoreStatus Store(std::string& resultPublicId,
-                      DicomInstanceToStore& dicom);
+                      DicomInstanceToStore& dicom,
+                      StoreInstanceMode mode);
 
     void AnswerAttachment(RestApiOutput& output,
                           const std::string& resourceId,
@@ -426,6 +428,16 @@
       return isExecuteLuaEnabled_;
     }
 
+    void SetOverwriteInstances(bool overwrite)
+    {
+      overwriteInstances_ = overwrite;
+    }
+    
+    bool IsOverwriteInstances() const
+    {
+      return overwriteInstances_;
+    }
+    
     virtual IStorageCommitmentFactory::ILookupHandler*
     CreateStorageCommitment(const std::string& jobId,
                             const std::string& transactionUid,
--- a/OrthancServer/ServerEnumerations.h	Fri Apr 10 16:12:10 2020 +0200
+++ b/OrthancServer/ServerEnumerations.h	Fri Apr 10 16:18:17 2020 +0200
@@ -90,6 +90,13 @@
     FindStorageAccessMode_DiskOnLookupAndAnswer
   };
 
+  enum StoreInstanceMode
+  {
+    StoreInstanceMode_Default,
+    StoreInstanceMode_OverwriteDuplicate,
+    StoreInstanceMode_IgnoreDuplicate
+  };
+
 
   /**
    * WARNING: Do not change the explicit values in the enumerations
--- a/OrthancServer/ServerIndex.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/OrthancServer/ServerIndex.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -675,7 +675,6 @@
     db_(db),
     maximumStorageSize_(0),
     maximumPatients_(0),
-    overwrite_(false),
     mainDicomTagsRegistry_(new MainDicomTagsRegistry)
   {
     listener_.reset(new Listener(context));
@@ -753,7 +752,8 @@
   
   StoreStatus ServerIndex::Store(std::map<MetadataType, std::string>& instanceMetadata,
                                  DicomInstanceToStore& instanceToStore,
-                                 const Attachments& attachments)
+                                 const Attachments& attachments,
+                                 bool overwrite)
   {
     boost::mutex::scoped_lock lock(mutex_);
 
@@ -784,7 +784,7 @@
       {
         // The instance already exists
         
-        if (overwrite_)
+        if (overwrite)
         {
           // Overwrite the old instance
           LOG(INFO) << "Overwriting instance: " << hashInstance;
@@ -1660,12 +1660,6 @@
     StandaloneRecycling();
   }
 
-  void ServerIndex::SetOverwriteInstances(bool overwrite)
-  {
-    boost::mutex::scoped_lock lock(mutex_);
-    overwrite_ = overwrite;
-  }
-
 
   void ServerIndex::StandaloneRecycling()
   {
--- a/OrthancServer/ServerIndex.h	Fri Apr 10 16:12:10 2020 +0200
+++ b/OrthancServer/ServerIndex.h	Fri Apr 10 16:18:17 2020 +0200
@@ -71,7 +71,6 @@
 
     uint64_t     maximumStorageSize_;
     unsigned int maximumPatients_;
-    bool         overwrite_;
     std::unique_ptr<MainDicomTagsRegistry>  mainDicomTagsRegistry_;
 
     static void FlushThread(ServerIndex* that,
@@ -139,11 +138,10 @@
     // "count == 0" means no limit on the number of patients
     void SetMaximumPatientCount(unsigned int count);
 
-    void SetOverwriteInstances(bool overwrite);
-
     StoreStatus Store(std::map<MetadataType, std::string>& instanceMetadata,
                       DicomInstanceToStore& instance,
-                      const Attachments& attachments);
+                      const Attachments& attachments,
+                      bool overwrite);
 
     void GetGlobalStatistics(/* out */ uint64_t& diskSize,
                              /* out */ uint64_t& uncompressedSize,
--- a/OrthancServer/ServerJobs/MergeStudyJob.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/OrthancServer/ServerJobs/MergeStudyJob.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -145,7 +145,8 @@
     toStore.SetParsedDicomFile(*modified);
 
     std::string modifiedInstance;
-    if (context_.Store(modifiedInstance, toStore) != StoreStatus_Success)
+    if (context_.Store(modifiedInstance, toStore,
+                       StoreInstanceMode_Default) != StoreStatus_Success)
     {
       LOG(ERROR) << "Error while storing a modified instance " << instance;
       return false;
--- a/OrthancServer/ServerJobs/Operations/ModifyInstanceOperation.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/OrthancServer/ServerJobs/Operations/ModifyInstanceOperation.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -114,7 +114,7 @@
       toStore.AddMetadata(ResourceType_Instance, MetadataType_ModifiedFrom, instance.GetId());
 
       std::string modifiedId;
-      context_.Store(modifiedId, toStore);
+      context_.Store(modifiedId, toStore, StoreInstanceMode_Default);
 
       // Only chain with other commands if this command succeeds
       outputs.Append(new DicomInstanceOperationValue(instance.GetServerContext(), modifiedId));
--- a/OrthancServer/ServerJobs/ResourceModificationJob.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/OrthancServer/ServerJobs/ResourceModificationJob.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -211,7 +211,8 @@
      **/
 
     std::string modifiedInstance;
-    if (context_.Store(modifiedInstance, toStore) != StoreStatus_Success)
+    if (context_.Store(modifiedInstance, toStore,
+                       StoreInstanceMode_Default) != StoreStatus_Success)
     {
       throw OrthancException(ErrorCode_CannotStoreInstance,
                              "Error while storing a modified instance " + instance);
--- a/OrthancServer/ServerJobs/SplitStudyJob.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/OrthancServer/ServerJobs/SplitStudyJob.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -138,7 +138,8 @@
     toStore.SetParsedDicomFile(*modified);
 
     std::string modifiedInstance;
-    if (context_.Store(modifiedInstance, toStore) != StoreStatus_Success)
+    if (context_.Store(modifiedInstance, toStore,
+                       StoreInstanceMode_Default) != StoreStatus_Success)
     {
       LOG(ERROR) << "Error while storing a modified instance " << instance;
       return false;
--- a/OrthancServer/main.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/OrthancServer/main.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -87,7 +87,7 @@
       toStore.SetJson(dicomJson);
 
       std::string id;
-      context_.Store(id, toStore);
+      context_.Store(id, toStore, StoreInstanceMode_Default);
     }
   }
 };
@@ -1307,7 +1307,7 @@
     context.SetStoreMD5ForAttachments(lock.GetConfiguration().GetBooleanParameter("StoreMD5ForAttachments", true));
 
     // New option in Orthanc 1.4.2
-    context.GetIndex().SetOverwriteInstances(lock.GetConfiguration().GetBooleanParameter("OverwriteInstances", false));
+    context.SetOverwriteInstances(lock.GetConfiguration().GetBooleanParameter("OverwriteInstances", false));
 
     try
     {
--- a/Resources/CMake/DcmtkConfiguration.cmake	Fri Apr 10 16:12:10 2020 +0200
+++ b/Resources/CMake/DcmtkConfiguration.cmake	Fri Apr 10 16:18:17 2020 +0200
@@ -36,6 +36,14 @@
       )
   endif()
 
+  if (ENABLE_DCMTK_TRANSCODING)
+    AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmimgle/libsrc DCMTK_SOURCES)
+    AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmimage/libsrc DCMTK_SOURCES)
+    include_directories(
+      ${DCMTK_SOURCES_DIR}/dcmimage/include
+      )
+  endif()
+  
   if (ENABLE_DCMTK_JPEG)
     AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc DCMTK_SOURCES)
     AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmjpeg/libijg8 DCMTK_SOURCES)
@@ -56,17 +64,21 @@
       ${DCMTK_SOURCES_DIR}/dcmjpeg/libijg8/jaricom.c
       ${DCMTK_SOURCES_DIR}/dcmjpeg/libijg12/jaricom.c
       ${DCMTK_SOURCES_DIR}/dcmjpeg/libijg24/jaricom.c
+      )
 
-      # Disable support for encoding JPEG (modification in Orthanc 1.0.1)
-      ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djcodece.cc
-      ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencsv1.cc
-      ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencbas.cc
-      ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencpro.cc
-      ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djenclol.cc
-      ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencode.cc
-      ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencext.cc
-      ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencsps.cc
-      )
+    if (NOT ENABLE_DCMTK_TRANSCODING)
+      list(REMOVE_ITEM DCMTK_SOURCES 
+        # Disable support for encoding JPEG (modification in Orthanc 1.0.1)
+        ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djcodece.cc
+        ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencsv1.cc
+        ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencbas.cc
+        ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencpro.cc
+        ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djenclol.cc
+        ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencode.cc
+        ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencext.cc
+        ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencsps.cc
+        )
+    endif()
   endif()
 
 
@@ -78,15 +90,18 @@
       ${DCMTK_SOURCES_DIR}/dcmjpls/include
       ${DCMTK_SOURCES_DIR}/dcmjpls/libcharls
       )
-    list(REMOVE_ITEM DCMTK_SOURCES 
-      ${DCMTK_SOURCES_DIR}/dcmjpls/libsrc/djcodece.cc
-
-      # Disable support for encoding JPEG-LS (modification in Orthanc 1.0.1)
-      ${DCMTK_SOURCES_DIR}/dcmjpls/libsrc/djencode.cc
-      )
     list(APPEND DCMTK_SOURCES 
       ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djrplol.cc
       )
+
+    if (NOT ENABLE_DCMTK_TRANSCODING)
+      list(REMOVE_ITEM DCMTK_SOURCES 
+        ${DCMTK_SOURCES_DIR}/dcmjpls/libsrc/djcodece.cc
+
+        # Disable support for encoding JPEG-LS (modification in Orthanc 1.0.1)
+        ${DCMTK_SOURCES_DIR}/dcmjpls/libsrc/djencode.cc
+        )
+    endif()
   endif()
 
   
--- a/UnitTestsSources/FromDcmtkTests.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -1922,6 +1922,8 @@
 #include <dcmtk/dcmdata/dcostrmb.h>
 #include <dcmtk/dcmdata/dcpixel.h>
 #include <dcmtk/dcmdata/dcpxitem.h>
+#include <dcmtk/dcmjpeg/djrploss.h>   // for DJ_RPLossy
+#include <dcmtk/dcmjpeg/djrplol.h>    // for DJ_RPLossless
 
 
 namespace Orthanc
@@ -1933,6 +1935,8 @@
     {
     }
 
+    virtual DcmFileFormat& GetDicom() = 0;
+
     virtual DicomTransferSyntax GetTransferSyntax() = 0;
 
     virtual std::string GetSopClassUid() = 0;
@@ -1946,8 +1950,13 @@
     virtual void GetCompressedFrame(std::string& target,
                                     unsigned int frame) = 0;
 
-    virtual IDicomTranscoder* Transcode(std::set<DicomTransferSyntax> syntaxes,
-                                        bool allowNewSopInstanceUid) = 0;
+    // NB: Transcoding can change the value of "GetSopInstanceUid()"
+    // and "GetTransferSyntax()" if lossy compression is applied
+    virtual bool Transcode(std::string& target,
+                           std::set<DicomTransferSyntax> syntaxes,
+                           bool allowNewSopInstanceUid) = 0;
+
+    virtual void WriteToMemoryBuffer(std::string& target) = 0;
   };
 
 
@@ -1959,9 +1968,30 @@
     DicomTransferSyntax               transferSyntax_;
     std::string                       sopClassUid_;
     std::string                       sopInstanceUid_;
+    uint16_t                          bitsStored_;
+    unsigned int                      lossyQuality_;
+
+    static std::string GetStringTag(DcmDataset& dataset,
+                                    const DcmTagKey& tag)
+    {
+      const char* value = NULL;
+
+      if (!dataset.findAndGetString(tag, value).good() ||
+          value == NULL)
+      {
+        throw OrthancException(ErrorCode_BadFileFormat,
+                               "Missing SOP class/instance UID in DICOM instance");
+      }
+      else
+      {
+        return std::string(value);
+      }
+    }
 
     void Setup(DcmFileFormat* dicom)
     {
+      lossyQuality_ = 90;
+      
       dicom_.reset(dicom);
       
       if (dicom == NULL ||
@@ -1992,20 +2022,14 @@
           "Unsupported transfer syntax: " + boost::lexical_cast<std::string>(xfer));
       }
 
-      const char* a = NULL;
-      const char* b = NULL;
-
-      if (!dataset.findAndGetString(DCM_SOPClassUID, a).good() ||
-          !dataset.findAndGetString(DCM_SOPInstanceUID, b).good() ||
-          a == NULL ||
-          b == NULL)
+      if (!dataset.findAndGetUint16(DCM_BitsStored, bitsStored_).good())
       {
         throw OrthancException(ErrorCode_BadFileFormat,
-                               "Missing SOP class/instance UID in DICOM instance");
-      }
-
-      sopClassUid_.assign(a);
-      sopInstanceUid_.assign(b);
+                               "Missing \"Bits Stored\" tag in DICOM instance");
+      }      
+
+      sopClassUid_ = GetStringTag(dataset, DCM_SOPClassUID);
+      sopInstanceUid_ = GetStringTag(dataset, DCM_SOPInstanceUID);
     }
     
   public:
@@ -2020,6 +2044,35 @@
       Setup(FromDcmtkBridge::LoadFromMemoryBuffer(dicom, size));
     }
 
+    void SetLossyQuality(unsigned int quality)
+    {
+      if (quality <= 0 ||
+          quality > 100)
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+      else
+      {
+        lossyQuality_ = quality;
+      }
+    }
+
+    unsigned int GetLossyQuality() const
+    {
+      return lossyQuality_;
+    }
+
+    unsigned int GetBitsStored() const
+    {
+      return bitsStored_;
+    }
+
+    virtual DcmFileFormat& GetDicom()
+    {
+      assert(dicom_ != NULL);
+      return *dicom_;
+    }
+
     virtual DicomTransferSyntax GetTransferSyntax() ORTHANC_OVERRIDE
     {
       return transferSyntax_;
@@ -2040,6 +2093,15 @@
       return index_->GetFramesCount();
     }
 
+    virtual void WriteToMemoryBuffer(std::string& target) ORTHANC_OVERRIDE
+    {
+      if (!FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_))
+      {
+        throw OrthancException(ErrorCode_InternalError,
+                               "Cannot write the DICOM instance to a memory buffer");
+      }
+    }
+
     virtual ImageAccessor* DecodeFrame(unsigned int frame) ORTHANC_OVERRIDE
     {
       assert(dicom_->getDataset() != NULL);
@@ -2049,48 +2111,69 @@
     virtual void GetCompressedFrame(std::string& target,
                                     unsigned int frame) ORTHANC_OVERRIDE
     {
-#if 1
       index_->GetRawFrame(target, frame);
-      printf("%d: %d\n", frame, target.size());
-#endif
-
-#if 1
-      assert(dicom_->getDataset() != NULL);
-      DcmDataset& dataset = *dicom_->getDataset();
+    }
+
+    virtual bool Transcode(std::string& target,
+                           std::set<DicomTransferSyntax> syntaxes,
+                           bool allowNewSopInstanceUid) ORTHANC_OVERRIDE
+    {
+      assert(dicom_ != NULL &&
+             dicom_->getDataset() != NULL);
       
-      DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dataset);
-
-      if (pixelSequence != NULL &&
-          frame == 0 &&
-          pixelSequence->card() != GetFramesCount() + 1)
+      if (syntaxes.find(GetTransferSyntax()) != syntaxes.end())
       {
-        printf("COMPRESSED\n");
-        
-        // Check out "djcodecd.cc"
-        
-        printf("%d fragments\n", pixelSequence->card());
+        printf("NO TRANSCODING\n");
         
-        // Skip the first fragment, that is the offset table
-        for (unsigned long i = 1; ;i++)
-        {
-          DcmPixelItem *fragment = NULL;
-          if (pixelSequence->getItem(fragment, i).good())
-          {
-            printf("fragment %d %d\n", i, fragment->getLength());
-          }
-          else
-          {
-            break;
-          }
-        }
+        // No change in the transfer syntax => simply serialize the current dataset
+        WriteToMemoryBuffer(target);
+        return true;
+      }
+      
+      printf(">> %d\n", bitsStored_);
+
+      DJ_RPLossy rpLossy(lossyQuality_);
+
+      if (syntaxes.find(DicomTransferSyntax_LittleEndianImplicit) != syntaxes.end() &&
+          FromDcmtkBridge::Transcode(target, *dicom_, DicomTransferSyntax_LittleEndianImplicit, NULL))
+      {
+        transferSyntax_ = DicomTransferSyntax_LittleEndianImplicit;
+        return true;
+      }
+      else if (syntaxes.find(DicomTransferSyntax_LittleEndianExplicit) != syntaxes.end() &&
+               FromDcmtkBridge::Transcode(target, *dicom_, DicomTransferSyntax_LittleEndianExplicit, NULL))
+      {
+        transferSyntax_ = DicomTransferSyntax_LittleEndianExplicit;
+        return true;
       }
-#endif
-    }
-
-    virtual IDicomTranscoder* Transcode(std::set<DicomTransferSyntax> syntaxes,
-                                        bool allowNewSopInstanceUid) ORTHANC_OVERRIDE
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
+      else if (syntaxes.find(DicomTransferSyntax_BigEndianExplicit) != syntaxes.end() &&
+               FromDcmtkBridge::Transcode(target, *dicom_, DicomTransferSyntax_BigEndianExplicit, NULL))
+      {
+        transferSyntax_ = DicomTransferSyntax_BigEndianExplicit;
+        return true;
+      }
+      else if (syntaxes.find(DicomTransferSyntax_JPEGProcess1) != syntaxes.end() &&
+               allowNewSopInstanceUid &&
+               GetBitsStored() == 8 &&
+               FromDcmtkBridge::Transcode(target, *dicom_, DicomTransferSyntax_JPEGProcess1, &rpLossy))
+      {
+        transferSyntax_ = DicomTransferSyntax_JPEGProcess1;
+        sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID);
+        return true;
+      }
+      else if (syntaxes.find(DicomTransferSyntax_JPEGProcess2_4) != syntaxes.end() &&
+               allowNewSopInstanceUid &&
+               GetBitsStored() <= 12 &&
+               FromDcmtkBridge::Transcode(target, *dicom_, DicomTransferSyntax_JPEGProcess2_4, &rpLossy))
+      {
+        transferSyntax_ = DicomTransferSyntax_JPEGProcess2_4;
+        sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID);
+        return true;
+      }
+      else
+      {
+        return false;
+      }
     }
   };
 }
@@ -2167,14 +2250,16 @@
   }
 }
 
-#include "dcmtk/dcmjpeg/djrploss.h"  /* for DJ_RPLossy */
-#include "dcmtk/dcmjpeg/djrplol.h"   /* for DJ_RPLossless */
 
 #include <boost/filesystem.hpp>
 
 
 static void TestFile(const std::string& path)
 {
+  static unsigned int count = 0;
+  count++;
+  
+
   printf("** %s\n", path.c_str());
 
   std::string s;
@@ -2182,9 +2267,19 @@
 
   Orthanc::DcmtkTranscoder transcoder(s.c_str(), s.size());
 
-  printf("[%s] [%s] [%s] %d\n", GetTransferSyntaxUid(transcoder.GetTransferSyntax()),
+  /*if (transcoder.GetBitsStored() != 8)  // TODO
+    return; */
+
+  {
+    char buf[1024];
+    sprintf(buf, "/tmp/source-%06d.dcm", count);
+    printf(">> %s\n", buf);
+    Orthanc::SystemToolbox::WriteFile(s, buf);
+  }
+
+  printf("[%s] [%s] [%s] %d %d\n", GetTransferSyntaxUid(transcoder.GetTransferSyntax()),
          transcoder.GetSopClassUid().c_str(), transcoder.GetSopInstanceUid().c_str(),
-         transcoder.GetFramesCount());
+         transcoder.GetFramesCount(), transcoder.GetTransferSyntax());
 
   for (size_t i = 0; i < transcoder.GetFramesCount(); i++)
   {
@@ -2193,27 +2288,65 @@
 
     if (i == 0)
     {
-      static unsigned int i = 0;
       char buf[1024];
-      sprintf(buf, "/tmp/frame-%06d.dcm", i++);
+      sprintf(buf, "/tmp/frame-%06d.raw", count);
       printf(">> %s\n", buf);
       Orthanc::SystemToolbox::WriteFile(f, buf);
     }
   }
 
+  {
+    std::string t;
+    transcoder.WriteToMemoryBuffer(t);
+
+    Orthanc::DcmtkTranscoder transcoder2(t.c_str(), t.size());
+    printf(">> %d %d ; %lu bytes\n", transcoder.GetTransferSyntax(), transcoder2.GetTransferSyntax(), t.size());
+  }
+
+  {
+    std::string a = transcoder.GetSopInstanceUid();
+    DicomTransferSyntax b = transcoder.GetTransferSyntax();
+    
+    std::set<DicomTransferSyntax> syntaxes;
+    syntaxes.insert(DicomTransferSyntax_JPEGProcess2_4);
+    //syntaxes.insert(DicomTransferSyntax_LittleEndianExplicit);
+
+    std::string t;
+    bool ok = transcoder.Transcode(t, syntaxes, true);
+    printf("Transcoding: %d\n", ok);
+
+    if (ok)
+    {
+      printf("[%s] => [%s]\n", a.c_str(), transcoder.GetSopInstanceUid().c_str());
+      printf("[%s] => [%s]\n", GetTransferSyntaxUid(b),
+             GetTransferSyntaxUid(transcoder.GetTransferSyntax()));
+      
+      {
+        char buf[1024];
+        sprintf(buf, "/tmp/transcoded-%06d.dcm", count);
+        printf(">> %s\n", buf);
+        Orthanc::SystemToolbox::WriteFile(t, buf);
+      }
+
+      Orthanc::DcmtkTranscoder transcoder2(t.c_str(), t.size());
+      printf("  => transcoded transfer syntax %d ; %lu bytes\n", transcoder2.GetTransferSyntax(), t.size());
+    }
+  }
+  
   printf("\n");
 }
 
-TEST(Toto, Transcode)
+TEST(Toto, DISABLED_Transcode)
 {
+  //OFLog::configure(OFLogger::DEBUG_LOG_LEVEL);
+
   if (0)
   {
-    OFLog::configure(OFLogger::DEBUG_LOG_LEVEL);
-
     std::string s;
     //SystemToolbox::ReadFile(s, "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/1.2.840.10008.1.2.4.50.dcm");
     //SystemToolbox::ReadFile(s, "/home/jodogne/DICOM/Alain.dcm");
-    SystemToolbox::ReadFile(s, "/home/jodogne/Subversion/orthanc-tests/Database/Brainix/Epi/IM-0001-0002.dcm");
+    //SystemToolbox::ReadFile(s, "/home/jodogne/Subversion/orthanc-tests/Database/Brainix/Epi/IM-0001-0002.dcm");
+    SystemToolbox::ReadFile(s, "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/1.2.840.10008.1.2.1.dcm");
 
     std::unique_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(s.c_str(), s.size()));
 
@@ -2225,7 +2358,7 @@
 #if 0
     E_TransferSyntax target = EXS_LittleEndianExplicit;
     p = NULL;
-#elif 1
+#elif 0
     E_TransferSyntax target = EXS_JPEGProcess14SV1;  
     DJ_RPLossless rp_lossless(6, 0);
     p = &rp_lossless;
@@ -2257,10 +2390,137 @@
         TestFile(it->path().string());
       }
     }
-
+  }
+
+  if (0)
+  {
     TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Multiframe.dcm");
     TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Issue44/Monochrome1-Jpeg.dcm");
   }
+
+  if (0)
+  {
+    TestFile("/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/1.2.840.10008.1.2.1.dcm");
+  }
+}
+
+
+
+#include "../Core/DicomNetworking/DicomAssociation.h"
+#include "../Core/DicomNetworking/DicomControlUserConnection.h"
+#include "../Core/DicomNetworking/DicomStoreUserConnection.h"
+
+TEST(Toto, DISABLED_DicomAssociation)
+{
+  DicomAssociationParameters params;
+  params.SetLocalApplicationEntityTitle("ORTHANC");
+  params.SetRemoteApplicationEntityTitle("PACS");
+  params.SetRemotePort(2001);
+
+#if 0
+  DicomAssociation assoc;
+  assoc.ProposeGenericPresentationContext(UID_StorageCommitmentPushModelSOPClass);
+  assoc.ProposeGenericPresentationContext(UID_VerificationSOPClass);
+  assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage,
+                                   DicomTransferSyntax_JPEGProcess1);
+  assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage,
+                                   DicomTransferSyntax_JPEGProcess2_4);
+  assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage,
+                                   DicomTransferSyntax_JPEG2000);
+  
+  assoc.Open(params);
+
+  int presID = ASC_findAcceptedPresentationContextID(&assoc.GetDcmtkAssociation(), UID_ComputedRadiographyImageStorage);
+  printf(">> %d\n", presID);
+    
+  std::map<DicomTransferSyntax, uint8_t> pc;
+  printf(">> %d\n", assoc.LookupAcceptedPresentationContext(pc, UID_ComputedRadiographyImageStorage));
+  
+  for (std::map<DicomTransferSyntax, uint8_t>::const_iterator
+         it = pc.begin(); it != pc.end(); ++it)
+  {
+    printf("[%s] => %d\n", GetTransferSyntaxUid(it->first), it->second);
+  }
+#else
+  {
+    DicomControlUserConnection assoc(params);
+
+    try
+    {
+      printf(">> %d\n", assoc.Echo());
+    }
+    catch (OrthancException&)
+    {
+    }
+  }
+    
+  params.SetRemoteApplicationEntityTitle("PACS");
+  params.SetRemotePort(2000);
+
+  {
+    DicomControlUserConnection assoc(params);
+    printf(">> %d\n", assoc.Echo());
+  }
+
+#endif
+}
+
+static void TestTranscode(DicomStoreUserConnection& scu,
+                          const std::string& sopClassUid,
+                          DicomTransferSyntax transferSyntax)
+{
+  uint8_t id;
+      
+  if (scu.NegotiatePresentationContext(id, sopClassUid, transferSyntax))
+  {
+    printf("**** OK, without transcoding !! %d\n", id);
+  }
+  else
+  {
+    // Transcoding - only in Orthanc >= 1.7.0
+
+    const DicomTransferSyntax uncompressed[] = {
+      DicomTransferSyntax_LittleEndianImplicit,  // Default transfer syntax
+      DicomTransferSyntax_LittleEndianExplicit,
+      DicomTransferSyntax_BigEndianExplicit
+    };
+
+    bool found = false;
+    for (size_t i = 0; i < 3; i++)
+    {
+      if (scu.LookupPresentationContext(id, sopClassUid, uncompressed[i]))
+      {
+        printf("**** TRANSCODING to %s => %d\n",
+               GetTransferSyntaxUid(uncompressed[i]), id);
+        found = true;
+        break;
+      }
+    }
+
+    if (!found)
+    {
+      printf("**** KO KO KO\n");
+    }
+  }
+}
+
+
+TEST(Toto, DISABLED_Store)
+{
+  DicomAssociationParameters params;
+  params.SetLocalApplicationEntityTitle("ORTHANC");
+  params.SetRemoteApplicationEntityTitle("PACS");
+  params.SetRemotePort(2000);
+
+  DicomStoreUserConnection assoc(params);
+  assoc.PrepareStorageClass(UID_MRImageStorage, DicomTransferSyntax_JPEGProcess1);
+  assoc.PrepareStorageClass(UID_MRImageStorage, DicomTransferSyntax_JPEGProcess2_4);
+  //assoc.PrepareStorageClass(UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit);
+
+  //assoc.SetUncompressedSyntaxesProposed(false);
+  //assoc.SetCommonClassesProposed(false);
+  TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000);
+  //TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit);
 }
 
 #endif
--- a/UnitTestsSources/MultiThreadingTests.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/UnitTestsSources/MultiThreadingTests.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -1316,7 +1316,7 @@
       DicomInstanceToStore toStore;
       toStore.SetParsedDicomFile(dicom);
 
-      return (context_->Store(id, toStore) == StoreStatus_Success);
+      return (context_->Store(id, toStore, StoreInstanceMode_Default) == StoreStatus_Success);
     }
   };
 }
--- a/UnitTestsSources/ServerIndexTests.cpp	Fri Apr 10 16:12:10 2020 +0200
+++ b/UnitTestsSources/ServerIndexTests.cpp	Fri Apr 10 16:18:17 2020 +0200
@@ -726,7 +726,8 @@
     std::map<MetadataType, std::string> instanceMetadata;
     DicomInstanceToStore toStore;
     toStore.SetSummary(instance);
-    ASSERT_EQ(StoreStatus_Success, index.Store(instanceMetadata, toStore, attachments));
+    ASSERT_EQ(StoreStatus_Success, index.Store(instanceMetadata, toStore, attachments,
+                                               false /* don't overwrite */));
     ASSERT_EQ(5u, instanceMetadata.size());
     ASSERT_TRUE(instanceMetadata.find(MetadataType_Instance_RemoteAet) != instanceMetadata.end());
     ASSERT_TRUE(instanceMetadata.find(MetadataType_Instance_ReceptionDate) != instanceMetadata.end());
@@ -803,7 +804,7 @@
 
     DicomInstanceHasher hasher(instance);
     std::string id = hasher.HashInstance();
-    context.GetIndex().SetOverwriteInstances(overwrite);
+    context.SetOverwriteInstances(overwrite);
 
     uint64_t diskSize, uncompressedSize, countPatients, countStudies, countSeries, countInstances;
     context.GetIndex().GetGlobalStatistics(diskSize, uncompressedSize, countPatients, 
@@ -819,7 +820,7 @@
       ASSERT_EQ(id, toStore.GetHasher().HashInstance());
 
       std::string id2;
-      ASSERT_EQ(StoreStatus_Success, context.Store(id2, toStore));
+      ASSERT_EQ(StoreStatus_Success, context.Store(id2, toStore, StoreInstanceMode_Default));
       ASSERT_EQ(id, id2);
     }
 
@@ -854,7 +855,8 @@
       toStore.SetOrigin(DicomInstanceOrigin::FromPlugins());
 
       std::string id2;
-      ASSERT_EQ(overwrite ? StoreStatus_Success : StoreStatus_AlreadyStored, context.Store(id2, toStore));
+      ASSERT_EQ(overwrite ? StoreStatus_Success : StoreStatus_AlreadyStored,
+                context.Store(id2, toStore, StoreInstanceMode_Default));
       ASSERT_EQ(id, id2);
     }