changeset 3912:7610af1532c3 transcoding

prototyping automated transcoding of incoming DICOM files
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 08 May 2020 13:43:50 +0200
parents 0ef7f4528be2
children 6ddad3e0b569
files Core/DicomParsing/IDicomTranscoder.cpp Core/DicomParsing/IDicomTranscoder.h Core/DicomParsing/ParsedDicomFile.cpp Core/DicomParsing/ParsedDicomFile.h OrthancServer/DicomInstanceToStore.cpp OrthancServer/DicomInstanceToStore.h OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp OrthancServer/OrthancRestApi/OrthancRestApi.cpp OrthancServer/OrthancRestApi/OrthancRestApi.h OrthancServer/ServerContext.cpp OrthancServer/ServerContext.h
diffstat 11 files changed, 148 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/Core/DicomParsing/IDicomTranscoder.cpp	Fri May 08 12:10:04 2020 +0200
+++ b/Core/DicomParsing/IDicomTranscoder.cpp	Fri May 08 13:43:50 2020 +0200
@@ -86,7 +86,26 @@
     }
     else
     {
-      throw OrthancException(ErrorCode_InternalError);
+      // Probably results from a call to "ReleaseDicom()"
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
     }
   }
+
+
+  DcmFileFormat* IDicomTranscoder::TranscodedDicom::ReleaseDicom()
+  {
+    if (internal_.get() != NULL)
+    {
+      return internal_.release();
+    }
+    else if (external_ != NULL)
+    {
+      return new DcmFileFormat(*external_);  // Clone
+    }
+    else
+    {
+      // Probably results from a call to "ReleaseDicom()"
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);      
+    }        
+  }
 }
--- a/Core/DicomParsing/IDicomTranscoder.h	Fri May 08 12:10:04 2020 +0200
+++ b/Core/DicomParsing/IDicomTranscoder.h	Fri May 08 13:43:50 2020 +0200
@@ -85,7 +85,9 @@
         return hasSopInstanceUidChanged_;
       }
       
-      DcmFileFormat& GetDicom() const;      
+      DcmFileFormat& GetDicom() const;
+
+      DcmFileFormat* ReleaseDicom();
     };
     
     /**
--- a/Core/DicomParsing/ParsedDicomFile.cpp	Fri May 08 12:10:04 2020 +0200
+++ b/Core/DicomParsing/ParsedDicomFile.cpp	Fri May 08 13:43:50 2020 +0200
@@ -1113,6 +1113,12 @@
   }
 
 
+  ParsedDicomFile::ParsedDicomFile(DcmFileFormat* dicom) : pimpl_(new PImpl)
+  {
+    pimpl_->file_.reset(dicom);  // No cloning
+  }
+
+
   DcmFileFormat& ParsedDicomFile::GetDcmtkObject() const
   {
     return *pimpl_->file_.get();
--- a/Core/DicomParsing/ParsedDicomFile.h	Fri May 08 12:10:04 2020 +0200
+++ b/Core/DicomParsing/ParsedDicomFile.h	Fri May 08 13:43:50 2020 +0200
@@ -102,6 +102,8 @@
 
     bool EmbedContentInternal(const std::string& dataUriScheme);
 
+    ParsedDicomFile(DcmFileFormat* dicom);  // This takes ownership (no clone)
+
   public:
     ParsedDicomFile(bool createIdentifiers);  // Create a minimal DICOM instance
 
@@ -114,9 +116,14 @@
 
     ParsedDicomFile(const std::string& content);
 
-    ParsedDicomFile(DcmDataset& dicom);
+    ParsedDicomFile(DcmDataset& dicom);  // This clones the DCMTK object
+
+    ParsedDicomFile(DcmFileFormat& dicom);  // This clones the DCMTK object
 
-    ParsedDicomFile(DcmFileFormat& dicom);
+    static ParsedDicomFile* AcquireDcmtkObject(DcmFileFormat* dicom)  // No clone here
+    {
+      return new ParsedDicomFile(dicom);
+    }
 
     DcmFileFormat& GetDcmtkObject() const;
 
--- a/OrthancServer/DicomInstanceToStore.cpp	Fri May 08 12:10:04 2020 +0200
+++ b/OrthancServer/DicomInstanceToStore.cpp	Fri May 08 13:43:50 2020 +0200
@@ -381,14 +381,14 @@
     }
 
 
-    bool HasPixelData()
+    ParsedDicomFile& GetParsedDicomFile()
     {
       ComputeMissingInformation();
       ParseDicomFile();
       
       if (parsed_.HasContent())
       {
-        return parsed_.GetContent().HasTag(DICOM_TAG_PIXEL_DATA);
+        return parsed_.GetContent();
       }
       else
       {
@@ -498,6 +498,11 @@
 
   bool DicomInstanceToStore::HasPixelData() const
   {
-    return const_cast<PImpl&>(*pimpl_).HasPixelData();
+    return const_cast<PImpl&>(*pimpl_).GetParsedDicomFile().HasTag(DICOM_TAG_PIXEL_DATA);
+  }
+
+  ParsedDicomFile& DicomInstanceToStore::GetParsedDicomFile() const
+  {
+    return const_cast<PImpl&>(*pimpl_).GetParsedDicomFile();
   }
 }
--- a/OrthancServer/DicomInstanceToStore.h	Fri May 08 12:10:04 2020 +0200
+++ b/OrthancServer/DicomInstanceToStore.h	Fri May 08 13:43:50 2020 +0200
@@ -92,5 +92,7 @@
     DicomInstanceHasher& GetHasher();
 
     bool HasPixelData() const;
+
+    ParsedDicomFile& GetParsedDicomFile() const;
   };
 }
--- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Fri May 08 12:10:04 2020 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Fri May 08 13:43:50 2020 +0200
@@ -280,7 +280,7 @@
 
     if (sendAnswer)
     {
-      OrthancRestApi::GetApi(call).AnswerStoredInstance(call, toStore, status);
+      OrthancRestApi::GetApi(call).AnswerStoredInstance(call, toStore, status, id);
     }
   }
 
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Fri May 08 12:10:04 2020 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Fri May 08 13:43:50 2020 +0200
@@ -63,11 +63,11 @@
 
   void OrthancRestApi::AnswerStoredInstance(RestApiPostCall& call,
                                             DicomInstanceToStore& instance,
-                                            StoreStatus status) const
+                                            StoreStatus status,
+                                            const std::string& instanceId) const
   {
     Json::Value result;
-    SetupResourceAnswer(result, instance.GetHasher().HashInstance(), 
-                        ResourceType_Instance, status);
+    SetupResourceAnswer(result, instanceId, ResourceType_Instance, status);
 
     result["ParentPatient"] = instance.GetHasher().HashPatient();
     result["ParentStudy"] = instance.GetHasher().HashStudy();
@@ -142,7 +142,7 @@
     std::string publicId;
     StoreStatus status = context.Store(publicId, toStore, StoreInstanceMode_Default);
 
-    OrthancRestApi::GetApi(call).AnswerStoredInstance(call, toStore, status);
+    OrthancRestApi::GetApi(call).AnswerStoredInstance(call, toStore, status, publicId);
   }
 
 
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.h	Fri May 08 12:10:04 2020 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.h	Fri May 08 13:43:50 2020 +0200
@@ -107,9 +107,12 @@
 
     static ServerIndex& GetIndex(RestApiCall& call);
 
+    // WARNING: "instanceId" can be different from
+    // "instance.GetHasher().HashInstance()" if transcoding is enabled
     void AnswerStoredInstance(RestApiPostCall& call,
                               DicomInstanceToStore& instance,
-                              StoreStatus status) const;
+                              StoreStatus status,
+                              const std::string& instanceId) const;
 
     void AnswerStoredResource(RestApiPostCall& call,
                               const std::string& publicId,
--- a/OrthancServer/ServerContext.cpp	Fri May 08 12:10:04 2020 +0200
+++ b/OrthancServer/ServerContext.cpp	Fri May 08 13:43:50 2020 +0200
@@ -344,9 +344,9 @@
   }
 
 
-  StoreStatus ServerContext::Store(std::string& resultPublicId,
-                                   DicomInstanceToStore& dicom,
-                                   StoreInstanceMode mode)
+  StoreStatus ServerContext::StoreAfterTranscoding(std::string& resultPublicId,
+                                                   DicomInstanceToStore& dicom,
+                                                   StoreInstanceMode mode)
   {
     bool overwrite;
     switch (mode)
@@ -502,6 +502,61 @@
   }
 
 
+  StoreStatus ServerContext::Store(std::string& resultPublicId,
+                                   DicomInstanceToStore& dicom,
+                                   StoreInstanceMode mode)
+  {
+    const DicomTransferSyntax option = DicomTransferSyntax_JPEGProcess1;
+    
+    if (1)
+    {
+      return StoreAfterTranscoding(resultPublicId, dicom, mode);
+    }
+    else
+    {
+      // TODO => Automated transcoding of incoming DICOM files
+      
+      DicomTransferSyntax sourceSyntax;
+      if (!FromDcmtkBridge::LookupOrthancTransferSyntax(
+            sourceSyntax, dicom.GetParsedDicomFile().GetDcmtkObject()) ||
+          sourceSyntax == option)
+      {
+        // No transcoding
+        return StoreAfterTranscoding(resultPublicId, dicom, mode);
+      }
+      else
+      {      
+        std::set<DicomTransferSyntax> syntaxes;
+        syntaxes.insert(option);
+
+        std::unique_ptr<IDicomTranscoder::TranscodedDicom> transcoded(
+          GetTranscoder().TranscodeToParsed(dicom.GetParsedDicomFile().GetDcmtkObject(),
+                                            dicom.GetBufferData(), dicom.GetBufferSize(),
+                                            syntaxes, true /* allow new SOP instance UID */));
+
+        if (transcoded.get() == NULL)
+        {
+          // Cannot transcode => store the original file
+          return StoreAfterTranscoding(resultPublicId, dicom, mode);
+        }
+        else
+        {
+          std::unique_ptr<ParsedDicomFile> tmp(
+            ParsedDicomFile::AcquireDcmtkObject(transcoded->ReleaseDicom()));
+      
+          DicomInstanceToStore toStore;
+          toStore.SetParsedDicomFile(*tmp);
+          toStore.SetOrigin(dicom.GetOrigin());
+
+          StoreStatus ok = StoreAfterTranscoding(resultPublicId, toStore, mode);
+          printf(">> %s\n", resultPublicId.c_str());
+          return ok;
+        }
+      }
+    }
+  }
+
+  
   void ServerContext::AnswerAttachment(RestApiOutput& output,
                                        const std::string& resourceId,
                                        FileContentType content)
@@ -1115,6 +1170,28 @@
   }
 
 
+  IDicomTranscoder& ServerContext::GetTranscoder()
+  {
+    IDicomTranscoder* transcoder = dcmtkTranscoder_.get();
+
+#if ORTHANC_ENABLE_PLUGINS == 1
+    if (HasPlugins())
+    {
+      transcoder = &GetPlugins();
+    }
+#endif
+
+    if (transcoder == NULL)
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+    else
+    {
+      return *transcoder;
+    }
+  }   
+
+
   void ServerContext::StoreWithTranscoding(std::string& sopClassUid,
                                            std::string& sopInstanceUid,
                                            DicomStoreUserConnection& connection,
@@ -1133,24 +1210,8 @@
     }
     else
     {
-      IDicomTranscoder* transcoder = dcmtkTranscoder_.get();
-
-#if ORTHANC_ENABLE_PLUGINS == 1
-      if (HasPlugins())
-      {
-        transcoder = &GetPlugins();
-      }
-#endif
-
-      if (transcoder == NULL)
-      {
-        throw OrthancException(ErrorCode_InternalError);
-      }
-      else
-      {
-        connection.Transcode(sopClassUid, sopInstanceUid, *transcoder, data, dicom.size(),
-                             hasMoveOriginator, moveOriginatorAet, moveOriginatorId);
-      }
+      connection.Transcode(sopClassUid, sopInstanceUid, GetTranscoder(), data, dicom.size(),
+                           hasMoveOriginator, moveOriginatorAet, moveOriginatorId);
     }
   }
 
@@ -1162,24 +1223,8 @@
                                 DicomTransferSyntax targetSyntax,
                                 bool allowNewSopInstanceUid)
   {
-    IDicomTranscoder* transcoder = dcmtkTranscoder_.get();
-    
-#if ORTHANC_ENABLE_PLUGINS == 1
-    if (HasPlugins())
-    {
-      transcoder = &GetPlugins();
-    }
-#endif
-
-    if (transcoder == NULL)
-    {
-      throw OrthancException(ErrorCode_InternalError);
-    }
-    else
-    {
-      return transcoder->TranscodeParsedToBuffer(
-        target, sourceSyntax, hasSopInstanceUidChanged,
-        dicom.GetDcmtkObject(), targetSyntax, allowNewSopInstanceUid);
-    }
+    return GetTranscoder().TranscodeParsedToBuffer(
+      target, sourceSyntax, hasSopInstanceUidChanged,
+      dicom.GetDcmtkObject(), targetSyntax, allowNewSopInstanceUid);
   }
 }
--- a/OrthancServer/ServerContext.h	Fri May 08 12:10:04 2020 +0200
+++ b/OrthancServer/ServerContext.h	Fri May 08 13:43:50 2020 +0200
@@ -229,6 +229,12 @@
     bool transcodeDicomProtocol_;
     std::unique_ptr<IDicomTranscoder>  dcmtkTranscoder_;
 
+    IDicomTranscoder& GetTranscoder();
+    
+    StoreStatus StoreAfterTranscoding(std::string& resultPublicId,
+                                      DicomInstanceToStore& dicom,
+                                      StoreInstanceMode mode);
+
   public:
     class DicomCacheLocker : public boost::noncopyable
     {