changeset 636:fb00a8be03e2

starting DecodeOrthancImageCommand
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 09 May 2019 16:58:35 +0200
parents 104c379f3f1b
children afc91cdc5128
files Framework/StoneEnumerations.h Samples/Sdl/Loader.cpp
diffstat 2 files changed, 307 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/StoneEnumerations.h	Thu May 09 15:02:44 2019 +0200
+++ b/Framework/StoneEnumerations.h	Thu May 09 16:58:35 2019 +0200
@@ -172,6 +172,10 @@
     MessageType_Test1,
     MessageType_Test2,
 
+
+    MessageType_ImageReady,
+
+
     MessageType_CustomMessage // Custom messages ids ust be greater than this (this one must remain in last position)
   };
 
--- a/Samples/Sdl/Loader.cpp	Thu May 09 15:02:44 2019 +0200
+++ b/Samples/Sdl/Loader.cpp	Thu May 09 16:58:35 2019 +0200
@@ -30,6 +30,8 @@
 // From Orthanc framework
 #include <Core/DicomFormat/DicomArray.h>
 #include <Core/DicomFormat/DicomImageInformation.h>
+#include <Core/Compression/ZlibCompressor.h>
+#include <Core/Compression/GzipCompressor.h>
 #include <Core/HttpClient.h>
 #include <Core/IDynamicObject.h>
 #include <Core/Images/Image.h>
@@ -56,7 +58,8 @@
   public:
     enum Type
     {
-      Type_OrthancApi
+      Type_OrthancRestApi,
+      Type_DecodeOrthancImage
     };
 
     virtual ~IOracleCommand()
@@ -133,18 +136,18 @@
 
   typedef std::map<std::string, std::string>  HttpHeaders;
 
-  class OrthancApiOracleCommand : public OracleCommandWithPayload
+  class OrthancRestApiCommand : public OracleCommandWithPayload
   {
   public:
     class SuccessMessage : public OrthancStone::OriginMessage<OrthancStone::MessageType_HttpRequestSuccess,   // TODO
-                                                              OrthancApiOracleCommand>
+                                                              OrthancRestApiCommand>
     {
     private:
       HttpHeaders   headers_;
       std::string   answer_;
 
     public:
-      SuccessMessage(const OrthancApiOracleCommand& command,
+      SuccessMessage(const OrthancRestApiCommand& command,
                      const HttpHeaders& answerHeaders,
                      std::string& answer  /* will be swapped to avoid a memcpy() */) :
         OriginMessage(command),
@@ -175,13 +178,13 @@
 
 
     class FailureMessage : public OrthancStone::OriginMessage<OrthancStone::MessageType_HttpRequestError,   // TODO
-                                                              OrthancApiOracleCommand>
+                                                              OrthancRestApiCommand>
     {
     private:
       Orthanc::HttpStatus  status_;
 
     public:
-      FailureMessage(const OrthancApiOracleCommand& command,
+      FailureMessage(const OrthancRestApiCommand& command,
                      Orthanc::HttpStatus status) :
         OriginMessage(command),
         status_(status)
@@ -206,7 +209,7 @@
     std::auto_ptr< OrthancStone::MessageHandler<FailureMessage> >  failureCallback_;
 
   public:
-    OrthancApiOracleCommand() :
+    OrthancRestApiCommand() :
       method_(Orthanc::HttpMethod_Get),
       uri_("/"),
       timeout_(10)
@@ -215,7 +218,7 @@
 
     virtual Type GetType() const
     {
-      return Type_OrthancApi;
+      return Type_OrthancRestApi;
     }
 
     void SetMethod(Orthanc::HttpMethod method)
@@ -239,6 +242,11 @@
       body_ = writer.write(json);
     }
 
+    void SetHttpHeaders(const HttpHeaders& headers)
+    {
+      headers_ = headers;
+    }
+
     void SetHttpHeader(const std::string& key,
                        const std::string& value)
     {
@@ -286,6 +294,128 @@
 
 
 
+
+  class DecodeOrthancImageCommand : public OracleCommandWithPayload
+  {
+  public:
+    class SuccessMessage : public OrthancStone::OriginMessage<OrthancStone::MessageType_ImageReady,   // TODO
+                                                              DecodeOrthancImageCommand>
+    {
+    private:
+      std::auto_ptr<Orthanc::ImageAccessor>  image_;
+      Orthanc::MimeType                      mime_;
+      unsigned int                           quality_;
+
+    public:
+      SuccessMessage(const DecodeOrthancImageCommand& command,
+                     Orthanc::ImageAccessor* image,   // Takes ownership
+                     Orthanc::MimeType mime,
+                     unsigned int quality) :
+        OriginMessage(command),
+        image_(image),
+        mime_(mime),
+        quality_(quality)
+      {
+        if (image == NULL)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+        }
+      }
+
+      const Orthanc::ImageAccessor& GetImage() const
+      {
+        return *image_;
+      }
+
+      Orthanc::MimeType GetMimeType() const
+      {
+        return mime_;
+      }
+
+      unsigned int GetQuality() const
+      {
+        return quality_;
+      }
+    };
+
+
+    class FailureMessage : public OrthancStone::OriginMessage<OrthancStone::MessageType_HttpRequestError,   // TODO
+                                                              DecodeOrthancImageCommand>
+    {
+    private:
+      Orthanc::HttpStatus  status_;
+
+    public:
+      FailureMessage(const DecodeOrthancImageCommand& command,
+                     Orthanc::HttpStatus status) :
+        OriginMessage(command),
+        status_(status)
+      {
+      }
+
+      Orthanc::HttpStatus GetHttpStatus() const
+      {
+        return status_;
+      }
+    };
+
+
+  private:
+    std::string    uri_;
+    HttpHeaders    headers_;
+    unsigned int   timeout_;
+
+    std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> >  successCallback_;
+    std::auto_ptr< OrthancStone::MessageHandler<FailureMessage> >  failureCallback_;
+
+  public:
+    DecodeOrthancImageCommand() :
+      uri_("/"),
+      timeout_(10)
+    {
+    }
+
+    virtual Type GetType() const
+    {
+      return Type_DecodeOrthancImage;
+    }
+
+    void SetUri(const std::string& uri)
+    {
+      uri_ = uri;
+    }
+
+    void SetHttpHeader(const std::string& key,
+                       const std::string& value)
+    {
+      headers_[key] = value;
+    }
+
+    const std::string& GetUri() const
+    {
+      return uri_;
+    }
+
+    const HttpHeaders& GetHttpHeaders() const
+    {
+      return headers_;
+    }
+
+    void SetTimeout(unsigned int seconds)
+    {
+      timeout_ = seconds;
+    }
+
+    unsigned int GetTimeout() const
+    {
+      return timeout_;
+    }
+  };
+
+
+
+
+
   class NativeOracle : public IOracle
   {
   private:
@@ -336,26 +466,30 @@
     std::vector<boost::thread*>    workers_;
 
 
+    void CopyHttpHeaders(Orthanc::HttpClient& client,
+                         const HttpHeaders& headers)
+    {
+      for (HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ )
+      {
+        client.AddHeader(it->first, it->second);
+      }
+    }
+
+
     void Execute(const OrthancStone::IObserver& receiver,
-                 const OrthancApiOracleCommand& command)
+                 const OrthancRestApiCommand& command)
     {
-      Orthanc::HttpClient  client(orthanc_, command.GetUri());
+      Orthanc::HttpClient client(orthanc_, command.GetUri());
       client.SetMethod(command.GetMethod());
       client.SetTimeout(command.GetTimeout());
 
+      CopyHttpHeaders(client, command.GetHttpHeaders());
+
       if (command.GetMethod() == Orthanc::HttpMethod_Post ||
           command.GetMethod() == Orthanc::HttpMethod_Put)
       {
         client.SetBody(command.GetBody());
       }
-      
-      {
-        const HttpHeaders& headers = command.GetHttpHeaders();
-        for (HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ )
-        {
-          client.AddHeader(it->first, it->second);
-        }
-      }
 
       std::string answer;
       HttpHeaders answerHeaders;
@@ -372,12 +506,91 @@
 
       if (success)
       {
-        OrthancApiOracleCommand::SuccessMessage message(command, answerHeaders, answer);
+        OrthancRestApiCommand::SuccessMessage message(command, answerHeaders, answer);
         emitter_.EmitMessage(receiver, message);
       }
       else
       {
-        OrthancApiOracleCommand::FailureMessage message(command, client.GetLastStatus());
+        OrthancRestApiCommand::FailureMessage message(command, client.GetLastStatus());
+        emitter_.EmitMessage(receiver, message);
+      }
+    }
+
+
+    void Execute(const OrthancStone::IObserver& receiver,
+                 const DecodeOrthancImageCommand& command)
+    {
+      Orthanc::HttpClient client(orthanc_, command.GetUri());
+      client.SetTimeout(command.GetTimeout());
+
+      CopyHttpHeaders(client, command.GetHttpHeaders());
+
+      std::string answer;
+      HttpHeaders answerHeaders;
+
+      bool success;
+      try
+      {
+        success = client.Apply(answer, answerHeaders);
+      }
+      catch (Orthanc::OrthancException& e)
+      {
+        success = false;
+      }
+
+      if (success)
+      {
+        printf("OK %d\n", answer.size());
+
+        Orthanc::MimeType contentType = Orthanc::MimeType_Binary;
+        Orthanc::HttpCompression contentEncoding = Orthanc::HttpCompression_None;
+
+        for (HttpHeaders::const_iterator it = answerHeaders.begin(); 
+             it != answerHeaders.end(); ++it)
+        {
+          std::string s;
+          Orthanc::Toolbox::ToLowerCase(s, it->first);
+
+          if (s == "content-encoding")
+          {
+            if (it->second == "gzip")
+            {
+              contentEncoding = Orthanc::HttpCompression_Gzip;
+            }
+            else 
+            {
+              throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol,
+                                              "Unsupported HTTP Content-Encoding: " + it->second);
+            }
+          }
+
+          if (s == "content-type")
+          {
+            contentType = Orthanc::StringToMimeType(it->second);
+          }
+
+          printf("  [%s] == [%s]\n", it->first.c_str(), it->second.c_str());
+        }
+
+        if (contentEncoding == Orthanc::HttpCompression_Gzip)
+        {
+          std::string compressed;
+          answer.swap(compressed);
+          
+          Orthanc::GzipCompressor compressor;
+          compressor.Uncompress(answer, compressed.c_str(), compressed.size());
+        }
+
+        printf("[%s] %d => %d\n", Orthanc::EnumerationToString(contentType), contentEncoding, answer.size());
+
+        
+
+        //DecodeOrthancImageCommand::SuccessMessage message(command, answerHeaders, answer);
+        //emitter_.EmitMessage(receiver, message);
+      }
+      else
+      {
+        DecodeOrthancImageCommand::FailureMessage message(command, client.GetLastStatus());
         emitter_.EmitMessage(receiver, message);
       }
     }
@@ -396,9 +609,14 @@
         {
           switch (item.GetCommand().GetType())
           {
-            case IOracleCommand::Type_OrthancApi:
+            case IOracleCommand::Type_OrthancRestApi:
               Execute(item.GetReceiver(), 
-                      dynamic_cast<const OrthancApiOracleCommand&>(item.GetCommand()));
+                      dynamic_cast<const OrthancRestApiCommand&>(item.GetCommand()));
+              break;
+
+            case IOracleCommand::Type_DecodeOrthancImage:
+              Execute(item.GetReceiver(), 
+                      dynamic_cast<const DecodeOrthancImageCommand&>(item.GetCommand()));
               break;
 
             default:
@@ -802,12 +1020,15 @@
 
     OrthancStone::CoordinateSystem3D  GetFrameGeometry(unsigned int frame) const
     {
-      if (frame >= imageInformation_.GetNumberOfFrames())
+      if (frame == 0)
+      {
+        return geometry_;
+      }
+      else if (frame >= imageInformation_.GetNumberOfFrames())
       {
         throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
       }
-
-      if (sopClassUid_ == OrthancStone::SopClassUid_RTDose)
+      else if (sopClassUid_ == OrthancStone::SopClassUid_RTDose)
       {
         if (frame >= frameOffsets_.size())
         {
@@ -819,6 +1040,10 @@
           geometry_.GetAxisX(),
           geometry_.GetAxisY());
       }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
     }
 
     bool FrameContainsPlane(unsigned int frame,
@@ -921,10 +1146,10 @@
     class MessageHandler : public Orthanc::IDynamicObject
     {
     public:
-      virtual void Handle(const OrthancApiOracleCommand::SuccessMessage& message) const = 0;
+      virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const = 0;
     };
 
-    void Handle(const OrthancApiOracleCommand::SuccessMessage& message)
+    void Handle(const OrthancRestApiCommand::SuccessMessage& message)
     {
       dynamic_cast<const MessageHandler&>(message.GetOrigin().GetPayload()).Handle(message);
     }
@@ -941,7 +1166,7 @@
       {
       }
 
-      virtual void Handle(const OrthancApiOracleCommand::SuccessMessage& message) const
+      virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const
       {
         Json::Value value;
         message.ParseJsonBody(value);
@@ -975,7 +1200,7 @@
       {
       }
 
-      virtual void Handle(const OrthancApiOracleCommand::SuccessMessage& message) const
+      virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const
       {
         Json::Value value;
         message.ParseJsonBody(value);
@@ -1003,7 +1228,7 @@
       active_(false)
     {
       oracle.RegisterObserverCallback(
-        new OrthancStone::Callable<AxialVolumeOrthancLoader, OrthancApiOracleCommand::SuccessMessage>
+        new OrthancStone::Callable<AxialVolumeOrthancLoader, OrthancRestApiCommand::SuccessMessage>
         (*this, &AxialVolumeOrthancLoader::Handle));
     }
 
@@ -1017,7 +1242,7 @@
 
       active_ = true;
 
-      std::auto_ptr<Refactoring::OrthancApiOracleCommand> command(new Refactoring::OrthancApiOracleCommand);
+      std::auto_ptr<Refactoring::OrthancRestApiCommand> command(new Refactoring::OrthancRestApiCommand);
       command->SetUri("/series/" + seriesId + "/instances-tags");
       command->SetPayload(new LoadSeriesGeometryHandler(*this));
 
@@ -1039,7 +1264,7 @@
 
       // TODO => Should be part of a second call if needed
 
-      std::auto_ptr<Refactoring::OrthancApiOracleCommand> command(new Refactoring::OrthancApiOracleCommand);
+      std::auto_ptr<Refactoring::OrthancRestApiCommand> command(new Refactoring::OrthancRestApiCommand);
       command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3004-000c");
       command->SetPayload(new LoadInstanceGeometryHandler(*this));
 
@@ -1054,7 +1279,7 @@
 class Toto : public OrthancStone::IObserver
 {
 private:
-  void Handle(const Refactoring::OrthancApiOracleCommand::SuccessMessage& message)
+  void Handle(const Refactoring::OrthancRestApiCommand::SuccessMessage& message)
   {
     Json::Value v;
     message.ParseJsonBody(v);
@@ -1062,7 +1287,7 @@
     printf("ICI [%s]\n", v.toStyledString().c_str());
   }
 
-  void Handle(const Refactoring::OrthancApiOracleCommand::FailureMessage& message)
+  void Handle(const Refactoring::OrthancRestApiCommand::FailureMessage& message)
   {
     printf("ERROR %d\n", message.GetHttpStatus());
   }
@@ -1073,7 +1298,7 @@
   {
     oracle.RegisterObserverCallback
       (new OrthancStone::Callable
-       <Toto, Refactoring::OrthancApiOracleCommand::SuccessMessage>(*this, &Toto::Handle));
+       <Toto, Refactoring::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle));
   }
 };
 
@@ -1101,12 +1326,13 @@
 
   oracle.Start();
 
+  if (0)
   {
     Json::Value v = Json::objectValue;
     v["Level"] = "Series";
     v["Query"] = Json::objectValue;
 
-    std::auto_ptr<Refactoring::OrthancApiOracleCommand>  command(new Refactoring::OrthancApiOracleCommand);
+    std::auto_ptr<Refactoring::OrthancRestApiCommand>  command(new Refactoring::OrthancRestApiCommand);
     command->SetMethod(Orthanc::HttpMethod_Post);
     command->SetUri("/tools/find");
     command->SetBody(v);
@@ -1114,6 +1340,47 @@
     oracle.Schedule(*toto, command.release());
   }
   
+  if (0)
+  {
+    std::auto_ptr<Refactoring::DecodeOrthancImageCommand>  command(new Refactoring::DecodeOrthancImageCommand);
+    command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg)));
+    command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview");
+    oracle.Schedule(*toto, command.release());
+  }
+  
+  if (0)
+  {
+    std::auto_ptr<Refactoring::DecodeOrthancImageCommand>  command(new Refactoring::DecodeOrthancImageCommand);
+    command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png)));
+    command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview");
+    oracle.Schedule(*toto, command.release());
+  }
+  
+  if (1)
+  {
+    std::auto_ptr<Refactoring::DecodeOrthancImageCommand>  command(new Refactoring::DecodeOrthancImageCommand);
+    command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png)));
+    command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
+    oracle.Schedule(*toto, command.release());
+  }
+  
+  if (1)
+  {
+    std::auto_ptr<Refactoring::DecodeOrthancImageCommand>  command(new Refactoring::DecodeOrthancImageCommand);
+    command->SetHttpHeader("Accept-Encoding", "gzip");
+    command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam)));
+    command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
+    oracle.Schedule(*toto, command.release());
+  }
+  
+  if (1)
+  {
+    std::auto_ptr<Refactoring::DecodeOrthancImageCommand>  command(new Refactoring::DecodeOrthancImageCommand);
+    command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam)));
+    command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
+    oracle.Schedule(*toto, command.release());
+  }
+  
   // 2017-11-17-Anonymized
   loader1->LoadSeries(oracle, "cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618");  // CT
   loader2->LoadInstance(oracle, "41029085-71718346-811efac4-420e2c15-d39f99b6");  // RT-DOSE