changeset 765:f6438fdc447e

cont
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 23 May 2019 14:57:52 +0200
parents f36a6f7e7bdf
children d6cd7c5ca6ea
files Framework/Oracle/IOracle.h Framework/Oracle/ThreadedOracle.cpp Framework/Oracle/ThreadedOracle.h Framework/Volumes/ImageBuffer3D.h Samples/Sdl/Loader.cpp
diffstat 5 files changed, 167 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Oracle/IOracle.h	Thu May 23 12:54:10 2019 +0200
+++ b/Framework/Oracle/IOracle.h	Thu May 23 14:57:52 2019 +0200
@@ -32,6 +32,10 @@
     {
     }
 
+    virtual void Start() = 0;
+
+    virtual void Stop() = 0;
+
     virtual void Schedule(const IObserver& receiver,
                           IOracleCommand* command) = 0;  // Takes ownership
   };
--- a/Framework/Oracle/ThreadedOracle.cpp	Thu May 23 12:54:10 2019 +0200
+++ b/Framework/Oracle/ThreadedOracle.cpp	Thu May 23 14:57:52 2019 +0200
@@ -204,6 +204,9 @@
           
       Orthanc::GzipCompressor compressor;
       compressor.Uncompress(answer, compressed.c_str(), compressed.size());
+
+      LOG(INFO) << "Uncompressing gzip Encoding: from " << compressed.size()
+                << " to " << answer.size() << " bytes";
     }
   }
 
@@ -424,6 +427,29 @@
   }
 
 
+  ThreadedOracle::~ThreadedOracle()
+  {
+    if (state_ == State_Running)
+    {
+      LOG(ERROR) << "The threaded oracle is still running, explicit call to "
+                 << "Stop() is mandatory to avoid crashes";
+    }
+
+    try
+    {
+      StopInternal();
+    }
+    catch (Orthanc::OrthancException& e)
+    {
+      LOG(ERROR) << "Exception while stopping the threaded oracle: " << e.What();
+    }
+    catch (...)
+    {
+      LOG(ERROR) << "Native exception while stopping the threaded oracle";
+    }           
+  }
+
+  
   void ThreadedOracle::SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc)
   {
     boost::mutex::scoped_lock lock(mutex_);
--- a/Framework/Oracle/ThreadedOracle.h	Thu May 23 12:54:10 2019 +0200
+++ b/Framework/Oracle/ThreadedOracle.h	Thu May 23 14:57:52 2019 +0200
@@ -72,10 +72,7 @@
   public:
     ThreadedOracle(IMessageEmitter& emitter);
 
-    virtual ~ThreadedOracle()
-    {
-      StopInternal();
-    }
+    virtual ~ThreadedOracle();
 
     void SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc);
 
@@ -83,9 +80,9 @@
 
     void SetSleepingTimeResolution(unsigned int milliseconds);
 
-    void Start();
+    virtual void Start();
 
-    void Stop()
+    virtual void Stop()
     {
       StopInternal();
     }
--- a/Framework/Volumes/ImageBuffer3D.h	Thu May 23 12:54:10 2019 +0200
+++ b/Framework/Volumes/ImageBuffer3D.h	Thu May 23 14:57:52 2019 +0200
@@ -99,6 +99,11 @@
       return format_;
     }
 
+    unsigned int GetBytesPerPixel() const
+    {
+      return Orthanc::GetBytesPerPixel(format_);
+    }
+
     uint64_t GetEstimatedMemorySize() const;
 
     bool GetRange(float& minValue,
--- a/Samples/Sdl/Loader.cpp	Thu May 23 12:54:10 2019 +0200
+++ b/Samples/Sdl/Loader.cpp	Thu May 23 14:57:52 2019 +0200
@@ -871,11 +871,27 @@
     };
    
     
+    class LoadUncompressedPixelData : public State
+    {
+    public:
+      LoadUncompressedPixelData(OrthancMultiframeVolumeLoader& that) :
+        State(that)
+      {
+      }
+      
+      virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const
+      {
+        GetTarget().SetUncompressedPixelData(message.GetAnswer());
+      }
+    };
+   
+    
 
     IOracle&     oracle_;
     bool         active_;
     std::string  instanceId_;
     std::string  transferSyntaxUid_;
+    uint64_t     revision_;
 
     std::auto_ptr<DicomInstanceParameters>  dicom_;
     std::auto_ptr<VolumeImageGeometry>      geometry_;
@@ -903,9 +919,18 @@
         return;
       }
       
-      if (transferSyntaxUid_ != "1.2.840.10008.1.2" &&
-          transferSyntaxUid_ != "1.2.840.10008.1.2.1" &&
-          transferSyntaxUid_ != "1.2.840.10008.1.2.2")
+      if (transferSyntaxUid_ == "1.2.840.10008.1.2" ||
+          transferSyntaxUid_ == "1.2.840.10008.1.2.1" ||
+          transferSyntaxUid_ == "1.2.840.10008.1.2.2")
+      {
+        std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand);
+        command->SetHttpHeader("Accept-Encoding", "gzip");
+        command->SetUri("/instances/" + instanceId_ + "/content/" +
+                        Orthanc::DICOM_TAG_PIXEL_DATA.Format() + "/0");
+        command->SetPayload(new LoadUncompressedPixelData(*this));
+        oracle_.Schedule(*this, command.release());
+      }
+      else
       {
         throw Orthanc::OrthancException(
           Orthanc::ErrorCode_NotImplemented,
@@ -961,6 +986,78 @@
       ScheduleFrameDownloads();
     }
 
+
+    ORTHANC_FORCE_INLINE
+    static void CopyPixel(uint32_t& target,
+                          const void* source)
+    {
+      // TODO - check alignement?
+      target = le32toh(*reinterpret_cast<const uint32_t*>(source));
+    }
+      
+
+    template <typename T>
+    void CopyPixelData(const std::string& pixelData)
+    {
+      const Orthanc::PixelFormat format = image_->GetFormat();
+      const unsigned int bpp = image_->GetBytesPerPixel();
+      const unsigned int width = image_->GetWidth();
+      const unsigned int height = image_->GetHeight();
+      const unsigned int depth = image_->GetDepth();
+
+      if (pixelData.size() != bpp * width * height * depth)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
+                                        "The pixel data has not the proper size");
+      }
+
+      if (pixelData.empty())
+      {
+        return;
+      }
+
+      const uint8_t* source = reinterpret_cast<const uint8_t*>(pixelData.c_str());
+
+      for (unsigned int z = 0; z < depth; z++)
+      {
+        ImageBuffer3D::SliceWriter writer(*image_, VolumeProjection_Axial, z);
+
+        assert (writer.GetAccessor().GetWidth() == width &&
+                writer.GetAccessor().GetHeight() == height);
+
+        for (unsigned int y = 0; y < height; y++)
+        {
+          assert(sizeof(T) == Orthanc::GetBytesPerPixel(format));
+
+          T* target = reinterpret_cast<T*>(writer.GetAccessor().GetRow(y));
+
+          for (unsigned int x = 0; x < width; x++)
+          {
+            CopyPixel(*target, source);
+            
+            target ++;
+            source += bpp;
+          }
+        }
+      }
+    }
+    
+
+    void SetUncompressedPixelData(const std::string& pixelData)
+    {
+      switch (image_->GetFormat())
+      {
+        case Orthanc::PixelFormat_Grayscale32:
+          CopyPixelData<uint32_t>(pixelData);
+          break;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+
+      revision_ ++;
+    }
+
     
   public:
     OrthancMultiframeVolumeLoader(IOracle& oracle,
@@ -996,6 +1093,7 @@
 
         {
           std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand);
+          command->SetHttpHeader("Accept-Encoding", "gzip");
           command->SetUri("/instances/" + instanceId + "/tags");
           command->SetPayload(new LoadGeometry(*this));
           oracle_.Schedule(*this, command.release());
@@ -1232,6 +1330,13 @@
         }
       }
 
+      /**
+       * The sleep() leads to a crash if the oracle is still running,
+       * while this object is destroyed. Always stop the oracle before
+       * destroying active objects.  (*)
+       **/
+      // boost::this_thread::sleep(boost::posix_time::seconds(2));
+
       oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(message.GetOrigin().GetDelay()));
     }
   }
@@ -1306,7 +1411,7 @@
 
 
 void Run(OrthancStone::NativeApplicationContext& context,
-         OrthancStone::IOracle& oracle)
+         OrthancStone::ThreadedOracle& oracle)
 {
   boost::shared_ptr<Toto> toto;
   boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> loader1, loader2;
@@ -1411,9 +1516,23 @@
 
   toto->SetVolume(0, new OrthancStone::OrthancSeriesVolumeProgressiveLoader::MPRSlicer(loader1));
 
-  LOG(WARNING) << "...Waiting for Ctrl-C...";
-  Orthanc::SystemToolbox::ServerBarrier();
-  //boost::this_thread::sleep(boost::posix_time::seconds(1));
+  {
+    oracle.Start();
+
+    LOG(WARNING) << "...Waiting for Ctrl-C...";
+    Orthanc::SystemToolbox::ServerBarrier();
+
+    /**
+     * WARNING => The oracle must be stopped BEFORE the objects using
+     * it are destroyed!!! This forces to wait for the completion of
+     * the running callback methods. Otherwise, the callbacks methods
+     * might still be running while their parent object is destroyed,
+     * resulting in crashes. This is very visible if adding a sleep(),
+     * as in (*).
+     **/
+
+    oracle.Stop();
+  }
 }
 
 
@@ -1442,11 +1561,11 @@
       oracle.SetOrthancParameters(p);
     }
 
-    oracle.Start();
+    //oracle.Start();
 
     Run(context, oracle);
-
-    oracle.Stop();
+    
+    //oracle.Stop();
   }
   catch (Orthanc::OrthancException& e)
   {