changeset 3880:cdd0cb5ec4e4 transcoding

DicomStoreUserConnection::LookupTranscoding()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 04 May 2020 22:10:55 +0200
parents a18b34dec94a
children f23ab7829a8d
files Core/DicomNetworking/DicomStoreUserConnection.cpp Core/DicomNetworking/DicomStoreUserConnection.h UnitTestsSources/FromDcmtkTests.cpp
diffstat 3 files changed, 98 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/Core/DicomNetworking/DicomStoreUserConnection.cpp	Mon May 04 19:17:07 2020 +0200
+++ b/Core/DicomNetworking/DicomStoreUserConnection.cpp	Mon May 04 22:10:55 2020 +0200
@@ -84,13 +84,13 @@
              it = syntaxes.begin(); it != syntaxes.end(); ++it)
       {
         association_->ProposePresentationContext(sopClassUid, *it);
+        proposedOriginalClasses_.insert(std::make_pair(sopClassUid, *it));
       }
 
       if (addLittleEndianImplicit)
       {
-        std::set<DicomTransferSyntax> uncompressed;
-        uncompressed.insert(DicomTransferSyntax_LittleEndianImplicit);
-        association_->ProposePresentationContext(sopClassUid, uncompressed);
+        association_->ProposePresentationContext(sopClassUid, DicomTransferSyntax_LittleEndianImplicit);
+        proposedOriginalClasses_.insert(std::make_pair(sopClassUid, DicomTransferSyntax_LittleEndianImplicit));
       }
 
       if (addLittleEndianExplicit ||
@@ -109,6 +109,14 @@
         }
 
         association_->ProposePresentationContext(sopClassUid, uncompressed);
+
+        assert(!uncompressed.empty());
+        if (addLittleEndianExplicit ^ addBigEndianExplicit)
+        {
+          // Only one transfer syntax was proposed for this presentation context
+          assert(uncompressed.size() == 1);
+          proposedOriginalClasses_.insert(std::make_pair(sopClassUid, *uncompressed.begin()));
+        }
       }
 
       return true;
@@ -201,7 +209,7 @@
   {
     /**
      * Step 1: Check whether this presentation context is already
-     * available in the previously negociated assocation.
+     * available in the previously negotiated assocation.
      **/
 
     if (LookupPresentationContext(presentationContextId, sopClassUid, transferSyntax))
@@ -212,11 +220,21 @@
     // The association must be re-negotiated
     if (association_->IsOpen())
     {
-      LOG(INFO) << "Re-negociating DICOM association with "
+      LOG(INFO) << "Re-negotiating DICOM association with "
                 << parameters_.GetRemoteModality().GetApplicationEntityTitle();
+
+      if (proposedOriginalClasses_.find(std::make_pair(sopClassUid, transferSyntax)) !=
+          proposedOriginalClasses_.end())
+      {
+        LOG(INFO) << "The remote modality has already rejected SOP class UID \""
+                  << sopClassUid << "\" with transfer syntax \""
+                  << GetTransferSyntaxUid(transferSyntax) << "\", don't renegotiate";
+        return false;
+      }
     }
-    
+
     association_->ClearPresentationContexts();
+    proposedOriginalClasses_.clear();
     RegisterStorageClass(sopClassUid, transferSyntax);  // (*)
 
     
@@ -304,7 +322,7 @@
   {
     DicomTransferSyntax transferSyntax;
     LookupParameters(sopClassUid, sopInstanceUid, transferSyntax, dataset);
-    
+
     uint8_t presID;
     if (!NegotiatePresentationContext(presID, sopClassUid, transferSyntax))
     {
@@ -390,4 +408,34 @@
     StoreInternal(sopClassUid, sopInstanceUid, *dicom->getDataset(),
                   moveOriginatorAET, moveOriginatorID);
   }
+
+
+  bool DicomStoreUserConnection::LookupTranscoding(std::set<DicomTransferSyntax>& acceptedSyntaxes,
+                                                   const std::string& sopClassUid,
+                                                   DicomTransferSyntax sourceSyntax)
+  {
+    acceptedSyntaxes.clear();
+
+    // Make sure a negotiation has already occurred for this transfer
+    // syntax. We don't use the return code: Transcoding is possible
+    // even if the "sourceSyntax" is not supported.
+    uint8_t presID;
+    NegotiatePresentationContext(presID, sopClassUid, sourceSyntax);
+
+    std::map<DicomTransferSyntax, uint8_t> contexts;
+    if (association_->LookupAcceptedPresentationContext(contexts, sopClassUid))
+    {
+      for (std::map<DicomTransferSyntax, uint8_t>::const_iterator
+             it = contexts.begin(); it != contexts.end(); ++it)
+      {
+        acceptedSyntaxes.insert(it->first);
+      }
+      
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
 }
--- a/Core/DicomNetworking/DicomStoreUserConnection.h	Mon May 04 19:17:07 2020 +0200
+++ b/Core/DicomNetworking/DicomStoreUserConnection.h	Mon May 04 22:10:55 2020 +0200
@@ -69,10 +69,15 @@
   {
   private:
     typedef std::map<std::string, std::set<DicomTransferSyntax> > RegisteredClasses;
+
+    // "ProposedOriginalClasses" keeps track of the storage classes
+    // that were proposed with a single transfer syntax
+    typedef std::set< std::pair<std::string, DicomTransferSyntax> > ProposedOriginalClasses;
     
     DicomAssociationParameters           parameters_;
     boost::shared_ptr<DicomAssociation>  association_;  // "shared_ptr" is for PImpl
     RegisteredClasses                    registeredClasses_;
+    ProposedOriginalClasses              proposedOriginalClasses_;
     bool                                 proposeCommonClasses_;
     bool                                 proposeUncompressedSyntaxes_;
     bool                                 proposeRetiredBigEndian_;
@@ -86,6 +91,14 @@
                           DicomTransferSyntax& transferSyntax,
                           DcmDataset& dataset);
 
+    bool LookupPresentationContext(uint8_t& presentationContextId,
+                                   const std::string& sopClassUid,
+                                   DicomTransferSyntax transferSyntax);
+    
+    bool NegotiatePresentationContext(uint8_t& presentationContextId,
+                                      const std::string& sopClassUid,
+                                      DicomTransferSyntax transferSyntax);
+
     void StoreInternal(std::string& sopClassUid,
                        std::string& sopInstanceUid,
                        DcmDataset& dataset,
@@ -133,17 +146,6 @@
     void RegisterStorageClass(const std::string& sopClassUid,
                               DicomTransferSyntax syntax);
 
-    // Should only be used if transcoding
-    // TODO => to private
-    bool LookupPresentationContext(uint8_t& presentationContextId,
-                                   const std::string& sopClassUid,
-                                   DicomTransferSyntax transferSyntax);
-    
-    // TODO => to private
-    bool NegotiatePresentationContext(uint8_t& presentationContextId,
-                                      const std::string& sopClassUid,
-                                      DicomTransferSyntax transferSyntax);
-
     void Store(std::string& sopClassUid,
                std::string& sopInstanceUid,
                const void* buffer,
@@ -158,5 +160,9 @@
     {
       Store(sopClassUid, sopInstanceUid, buffer, size, "", 0);  // Not a C-Move
     }
+
+    bool LookupTranscoding(std::set<DicomTransferSyntax>& acceptedSyntaxes,
+                           const std::string& sopClassUid,
+                           DicomTransferSyntax sourceSyntax);
   };
 }
--- a/UnitTestsSources/FromDcmtkTests.cpp	Mon May 04 19:17:07 2020 +0200
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Mon May 04 22:10:55 2020 +0200
@@ -2875,11 +2875,27 @@
                           const std::string& sopClassUid,
                           DicomTransferSyntax transferSyntax)
 {
-  uint8_t id;
-      
-  if (scu.NegotiatePresentationContext(id, sopClassUid, transferSyntax))
+  std::set<DicomTransferSyntax> accepted;
+
+  if (!scu.LookupTranscoding(accepted, sopClassUid, transferSyntax))
+  {
+    throw OrthancException(ErrorCode_NetworkProtocol,
+                           "The SOP class is not supported by the remote modality");
+  }
+
   {
-    printf("**** OK, without transcoding !! %d\n", id);
+    unsigned int count = 0;
+    for (std::set<DicomTransferSyntax>::const_iterator
+           it = accepted.begin(); it != accepted.end(); ++it)
+    {
+      LOG(INFO) << "available for transcoding " << (count++) << ": " << sopClassUid
+                << " / " << GetTransferSyntaxUid(*it);
+    }
+  }
+  
+  if (accepted.find(transferSyntax) != accepted.end())
+  {
+    printf("**** OK, without transcoding !! [%s]\n", GetTransferSyntaxUid(transferSyntax));
   }
   else
   {
@@ -2894,10 +2910,9 @@
     bool found = false;
     for (size_t i = 0; i < 3; i++)
     {
-      if (scu.LookupPresentationContext(id, sopClassUid, uncompressed[i]))
+      if (accepted.find(uncompressed[i]) != accepted.end())
       {
-        printf("**** TRANSCODING to %s => %d\n",
-               GetTransferSyntaxUid(uncompressed[i]), id);
+        printf("**** TRANSCODING to %s\n", GetTransferSyntaxUid(uncompressed[i]));
         found = true;
         break;
       }
@@ -2924,9 +2939,11 @@
   //assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit);
 
   //assoc.SetUncompressedSyntaxesProposed(false);  // Necessary for transcoding
-  //assoc.SetCommonClassesProposed(false);
+  assoc.SetCommonClassesProposed(false);
+  assoc.SetRetiredBigEndianProposed(true);
+  TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit);
   TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000);
-  TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit);
+  TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000);
 }