changeset 1782:f053c80ea411

ImageBuffer3D::CommitSagittalSlice()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 14 May 2021 16:30:54 +0200
parents bf4b15b059ea
children 75d3e2ab1fe1
files OrthancStone/Sources/Volumes/ImageBuffer3D.cpp OrthancStone/Sources/Volumes/ImageBuffer3D.h UnitTestsSources/VolumeRenderingTests.cpp
diffstat 3 files changed, 101 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancStone/Sources/Volumes/ImageBuffer3D.cpp	Fri May 14 15:35:24 2021 +0200
+++ b/OrthancStone/Sources/Volumes/ImageBuffer3D.cpp	Fri May 14 16:30:54 2021 +0200
@@ -76,29 +76,29 @@
 
   Orthanc::Image*  ImageBuffer3D::ExtractSagittalSlice(unsigned int slice) const
   {
-    //LOG(TRACE) << "ImageBuffer3D::ExtractSagittalSlice this= " << std::hex << this << std::dec << " width_ = " << width_ << " height_ = " << height_ << " depth_ = " << depth_ << " slice = " << slice;
     if (slice >= width_)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
-
+    
     std::unique_ptr<Orthanc::Image> result(new Orthanc::Image(format_, height_, depth_, false));
-    //LOG(TRACE) << "ImageBuffer3D::ExtractSagittalSlice result will be an image of WIDTH = " << height_ << " and HEIGHT = " << depth_;
 
-    unsigned int bytesPerPixel = Orthanc::GetBytesPerPixel(format_);
+    const unsigned int bytesPerPixel = Orthanc::GetBytesPerPixel(format_);
 
     for (unsigned int z = 0; z < depth_; z++)
     {
-      //uint8_t* target = reinterpret_cast<uint8_t*>(result->GetRow(depth_ - 1 - z));
-      uint8_t* target = reinterpret_cast<uint8_t*>(result->GetRow(z));
+      uint8_t* q = reinterpret_cast<uint8_t*>(result->GetRow(z));
 
       for (unsigned int y = 0; y < height_; y++)
       {
-        const void* source = (reinterpret_cast<const uint8_t*>(image_.GetConstRow(y + z * height_)) + bytesPerPixel * slice);
-        const uint8_t* byteSrc = reinterpret_cast<const uint8_t*>(source);
-        for (size_t byte = 0; byte < bytesPerPixel; ++byte)
-          target[byte] = byteSrc[byte];
-        target += bytesPerPixel;
+        const uint8_t* p = reinterpret_cast<const uint8_t*>(image_.GetConstRow(y + z * height_)) + bytesPerPixel * slice;
+        
+        for (size_t x = 0; x < bytesPerPixel; ++x)
+        {
+          q[x] = p[x];
+        }
+        
+        q += bytesPerPixel;
       }
     }
 
@@ -106,6 +106,35 @@
   }
 
 
+  void ImageBuffer3D::CommitSagittalSlice(unsigned int slice,
+                                          const Orthanc::ImageAccessor& source)
+  {
+    if (slice >= width_)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    
+    const unsigned int bytesPerPixel = Orthanc::GetBytesPerPixel(format_);
+
+    for (unsigned int z = 0; z < depth_; z++)
+    {
+      const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(z));
+
+      for (unsigned int y = 0; y < height_; y++)
+      {
+        uint8_t* q = reinterpret_cast<uint8_t*>(image_.GetRow(y + z * height_)) + bytesPerPixel * slice;
+
+        for (size_t x = 0; x < bytesPerPixel; ++x)
+        {
+          q[x] = p[x];
+        }
+        
+        p += bytesPerPixel;
+      }
+    }
+  }    
+
+
   ImageBuffer3D::ImageBuffer3D(Orthanc::PixelFormat format,
                                unsigned int width,
                                unsigned int height,
@@ -237,8 +266,11 @@
     {
       if (sagittal_.get() != NULL)
       {
-        // TODO
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+        assert(sagittal_->GetWidth() == that_.height_ &&
+               sagittal_->GetHeight() == that_.depth_ &&
+               sagittal_->GetFormat() == that_.format_);
+
+        that_.CommitSagittalSlice(slice_, *sagittal_);
       }
 
       // Update the dynamic range of the underlying image, if
@@ -252,7 +284,8 @@
                                           VolumeProjection projection,
                                           unsigned int slice) :
     that_(that),
-    modified_(false)
+    modified_(false),
+    slice_(slice)
   {
     switch (projection)
     {
--- a/OrthancStone/Sources/Volumes/ImageBuffer3D.h	Fri May 14 15:35:24 2021 +0200
+++ b/OrthancStone/Sources/Volumes/ImageBuffer3D.h	Fri May 14 16:30:54 2021 +0200
@@ -95,6 +95,9 @@
 
     Orthanc::Image*  ExtractSagittalSlice(unsigned int slice) const;
 
+    void CommitSagittalSlice(unsigned int slice,
+                             const Orthanc::ImageAccessor& source);
+
     template <typename T>
     T GetPixelUnchecked(unsigned int x,
                         unsigned int y,
@@ -204,6 +207,7 @@
       bool                           modified_;
       Orthanc::ImageAccessor         accessor_;
       std::unique_ptr<Orthanc::Image>  sagittal_;  // Unused for axial and coronal
+      unsigned int                   slice_;
 
       void Flush();
 
--- a/UnitTestsSources/VolumeRenderingTests.cpp	Fri May 14 15:35:24 2021 +0200
+++ b/UnitTestsSources/VolumeRenderingTests.cpp	Fri May 14 16:30:54 2021 +0200
@@ -188,10 +188,17 @@
 // Render the scene using the identity viewpoint (default)
 static Orthanc::ImageAccessor* Render(OrthancStone::ISceneLayer* layer,
                                       unsigned int width,
-                                      unsigned int height)
+                                      unsigned int height,
+                                      bool fitScene)
 {
   OrthancStone::Scene2D scene;
   scene.SetLayer(0, layer);
+
+  if (fitScene)
+  {
+    scene.FitContent(width, height);
+  }
+  
   return Render(scene, width, height);
 }
 
@@ -244,19 +251,38 @@
 }
 
 
-static OrthancStone::TextureBaseSceneLayer* Slice3x3x1Pattern(const OrthancStone::CoordinateSystem3D& volumeCoordinates,
+static OrthancStone::TextureBaseSceneLayer* Slice3x3x1Pattern(OrthancStone::VolumeProjection projection,
+                                                              const OrthancStone::CoordinateSystem3D& volumeCoordinates,
                                                               const OrthancStone::CoordinateSystem3D& cuttingPlane,
                                                               SlicerType type)
 {
   OrthancStone::VolumeImageGeometry geometry;
-  geometry.SetSizeInVoxels(3, 3, 1);
+
+  switch (projection)
+  {
+    case OrthancStone::VolumeProjection_Axial:
+      geometry.SetSizeInVoxels(3, 3, 1);
+      break;
+
+    case OrthancStone::VolumeProjection_Sagittal:
+      geometry.SetSizeInVoxels(1, 3, 3);
+      break;
+
+    case OrthancStone::VolumeProjection_Coronal:
+      geometry.SetSizeInVoxels(3, 1, 3);
+      break;
+
+    default:
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+  
   geometry.SetAxialGeometry(volumeCoordinates);
 
   boost::shared_ptr<OrthancStone::DicomVolumeImage> volume(new OrthancStone::DicomVolumeImage);
   volume->Initialize(geometry, Orthanc::PixelFormat_Grayscale8, false);
 
   {
-    OrthancStone::ImageBuffer3D::SliceWriter writer(volume->GetPixelData(), OrthancStone::VolumeProjection_Axial, 0);
+    OrthancStone::ImageBuffer3D::SliceWriter writer(volume->GetPixelData(), projection, 0);
     Assign3x3Pattern(writer.GetAccessor());
   }
 
@@ -282,7 +308,8 @@
   {
     OrthancStone::CoordinateSystem3D cuttingPlane;
     
-    std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer(Slice3x3x1Pattern(axial, cuttingPlane, static_cast<SlicerType>(mode)));
+    std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer(
+      Slice3x3x1Pattern(OrthancStone::VolumeProjection_Axial, axial, cuttingPlane, static_cast<SlicerType>(mode)));
 
     ASSERT_TRUE(layer.get() != NULL);
     ASSERT_EQ(OrthancStone::ISceneLayer::Type_FloatTexture, layer->GetType());
@@ -309,7 +336,7 @@
       ASSERT_FLOAT_EQ(200, GetPixelValue(texture, 2, 2));
     }
 
-    std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 5, 5));
+    std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 5, 5, false));
     ASSERT_EQ(5u, rendered->GetWidth());
     ASSERT_EQ(5u, rendered->GetHeight());
     ASSERT_FLOAT_EQ(0, GetPixelValue(*rendered, 0, 0));
@@ -354,7 +381,7 @@
     std::unique_ptr<OrthancStone::ColorTextureSceneLayer> layer(new OrthancStone::ColorTextureSceneLayer(pixel));
     layer->SetOrigin(0, 0);
 
-    std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 2, 2));
+    std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 2, 2, false));
     ASSERT_EQ(2u, rendered->GetWidth());
     ASSERT_EQ(2u, rendered->GetHeight());  
     ASSERT_FLOAT_EQ(0, GetPixelValue(*rendered, 0, 0));
@@ -367,7 +394,7 @@
     std::unique_ptr<OrthancStone::ColorTextureSceneLayer> layer(new OrthancStone::ColorTextureSceneLayer(pixel));
     layer->SetOrigin(-0.01, 0);
 
-    std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 2, 2));
+    std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 2, 2, false));
     ASSERT_FLOAT_EQ(0, GetPixelValue(*rendered, 0, 0));
     ASSERT_FLOAT_EQ(0, GetPixelValue(*rendered, 1, 0));
     ASSERT_FLOAT_EQ(255, GetPixelValue(*rendered, 0, 1));
@@ -378,7 +405,7 @@
     std::unique_ptr<OrthancStone::ColorTextureSceneLayer> layer(new OrthancStone::ColorTextureSceneLayer(pixel));
     layer->SetOrigin(-0.01, -0.01);
 
-    std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 2, 2));
+    std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 2, 2, false));
     ASSERT_FLOAT_EQ(255, GetPixelValue(*rendered, 0, 0));
     ASSERT_FLOAT_EQ(0, GetPixelValue(*rendered, 1, 0));
     ASSERT_FLOAT_EQ(0, GetPixelValue(*rendered, 0, 1));
@@ -389,7 +416,7 @@
     std::unique_ptr<OrthancStone::ColorTextureSceneLayer> layer(new OrthancStone::ColorTextureSceneLayer(pixel));
     layer->SetOrigin(0, -0.01);
 
-    std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 2, 2));
+    std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 2, 2, false));
     ASSERT_FLOAT_EQ(0, GetPixelValue(*rendered, 0, 0));
     ASSERT_FLOAT_EQ(255, GetPixelValue(*rendered, 1, 0));
     ASSERT_FLOAT_EQ(0, GetPixelValue(*rendered, 0, 1));
@@ -482,6 +509,7 @@
 }
 
 
+
 TEST(VolumeRendering, FlipAxial)
 {
   double x = 2;
@@ -513,7 +541,8 @@
                                                     OrthancStone::LinearAlgebra::CreateVector(1, 0, 0),
                                                     OrthancStone::LinearAlgebra::CreateVector(0, 1, 0));
 
-      std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer(Slice3x3x1Pattern(axial, cuttingPlane, static_cast<SlicerType>(mode)));
+      std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer(
+        Slice3x3x1Pattern(OrthancStone::VolumeProjection_Axial, axial, cuttingPlane, static_cast<SlicerType>(mode)));
       ASSERT_TRUE(AreSameImages(layer->GetTexture(), pattern));
 
       OrthancStone::Extent2D extent;
@@ -523,7 +552,7 @@
       ASSERT_FLOAT_EQ(x + 2.5, extent.GetX2());
       ASSERT_FLOAT_EQ(y + 2.5, extent.GetY2());
 
-      std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 15, 15));
+      std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 15, 15, false));
       ASSERT_TRUE(IsConstImageWithExclusion(0.0f, *rendered, 9, 8, 3, 3));
 
       Orthanc::ImageAccessor p;
@@ -536,7 +565,8 @@
                                                     OrthancStone::LinearAlgebra::CreateVector(-1, 0, 0),
                                                     OrthancStone::LinearAlgebra::CreateVector(0, 1, 0));
 
-      std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer(Slice3x3x1Pattern(axial, cuttingPlane, static_cast<SlicerType>(mode)));
+      std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer(
+        Slice3x3x1Pattern(OrthancStone::VolumeProjection_Axial, axial, cuttingPlane, static_cast<SlicerType>(mode)));
       if (mode == 1)
       {
         // Reslicer directly flips the pixels of the texture
@@ -555,7 +585,7 @@
       ASSERT_FLOAT_EQ(-(x - 0.5), extent.GetX2());
       ASSERT_FLOAT_EQ(y + 2.5, extent.GetY2());
 
-      std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 15, 15));
+      std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 15, 15, false));
       ASSERT_TRUE(IsConstImageWithExclusion(0.0f, *rendered, 3, 8, 3, 3));
 
       Orthanc::ImageAccessor p;
@@ -568,7 +598,8 @@
                                                     OrthancStone::LinearAlgebra::CreateVector(1, 0, 0),
                                                     OrthancStone::LinearAlgebra::CreateVector(0, -1, 0));
 
-      std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer(Slice3x3x1Pattern(axial, cuttingPlane, static_cast<SlicerType>(mode)));
+      std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer(
+        Slice3x3x1Pattern(OrthancStone::VolumeProjection_Axial, axial, cuttingPlane, static_cast<SlicerType>(mode)));
       if (mode == 1)
       {
         ASSERT_TRUE(AreSameImages(layer->GetTexture(), patternY));
@@ -585,7 +616,7 @@
       ASSERT_FLOAT_EQ(x + 2.5, extent.GetX2());
       ASSERT_FLOAT_EQ(-(y - 0.5), extent.GetY2());
 
-      std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 15, 15));
+      std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 15, 15, false));
       ASSERT_TRUE(IsConstImageWithExclusion(0.0f, *rendered, 9, 4, 3, 3));
 
       Orthanc::ImageAccessor p;
@@ -598,7 +629,8 @@
                                                     OrthancStone::LinearAlgebra::CreateVector(-1, 0, 0),
                                                     OrthancStone::LinearAlgebra::CreateVector(0, -1, 0));
 
-      std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer(Slice3x3x1Pattern(axial, cuttingPlane, static_cast<SlicerType>(mode)));
+      std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer(
+        Slice3x3x1Pattern(OrthancStone::VolumeProjection_Axial, axial, cuttingPlane, static_cast<SlicerType>(mode)));
       if (mode == 1)
       {
         ASSERT_TRUE(AreSameImages(layer->GetTexture(), patternXY));
@@ -615,7 +647,7 @@
       ASSERT_FLOAT_EQ(-(x - 0.5), extent.GetX2());
       ASSERT_FLOAT_EQ(-(y - 0.5), extent.GetY2());
 
-      std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 15, 15));
+      std::unique_ptr<Orthanc::ImageAccessor> rendered(Render(layer.release(), 15, 15, false));
       ASSERT_TRUE(IsConstImageWithExclusion(0.0f, *rendered, 3, 4, 3, 3));
 
       Orthanc::ImageAccessor p;