changeset 1772:6c246f862b00

unit test VolumeRendering.Basic
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 12 May 2021 17:24:24 +0200
parents f302bbddf94d
children 34eb41352dff
files OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake OrthancStone/Sources/Toolbox/DicomInstanceParameters.cpp OrthancStone/Sources/Volumes/VolumeReslicer.cpp UnitTestsSources/VolumeRenderingTests.cpp
diffstat 4 files changed, 125 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake	Wed May 12 15:09:32 2021 +0200
+++ b/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake	Wed May 12 17:24:24 2021 +0200
@@ -265,6 +265,8 @@
   ${ORTHANC_STONE_ROOT}/Scene2D/ColorSceneLayer.h
   ${ORTHANC_STONE_ROOT}/Scene2D/ColorTextureSceneLayer.cpp
   ${ORTHANC_STONE_ROOT}/Scene2D/ColorTextureSceneLayer.h
+  ${ORTHANC_STONE_ROOT}/Scene2D/CopyStyleConfigurator.cpp
+  ${ORTHANC_STONE_ROOT}/Scene2D/CopyStyleConfigurator.h
   ${ORTHANC_STONE_ROOT}/Scene2D/FloatTextureSceneLayer.cpp
   ${ORTHANC_STONE_ROOT}/Scene2D/FloatTextureSceneLayer.h
   ${ORTHANC_STONE_ROOT}/Scene2D/GrayscaleStyleConfigurator.cpp
--- a/OrthancStone/Sources/Toolbox/DicomInstanceParameters.cpp	Wed May 12 15:09:32 2021 +0200
+++ b/OrthancStone/Sources/Toolbox/DicomInstanceParameters.cpp	Wed May 12 17:24:24 2021 +0200
@@ -108,13 +108,17 @@
         !dicom.GetValue(Orthanc::DICOM_TAG_ROWS).ParseFirstUnsignedInteger(height_))
     {
       height_ = 0;
-     }
+    }
 
 
     bool sliceThicknessPresent = true;
     if (!dicom.ParseDouble(sliceThickness_, Orthanc::DICOM_TAG_SLICE_THICKNESS))
     {
-      LOG(INFO) << "The (non-madatory) slice thickness information is missing in a multiframe image";
+      if (numberOfFrames_ > 1)
+      {
+        LOG(INFO) << "The (non-madatory) slice thickness information is missing in a multiframe image";
+      }
+      
       sliceThickness_ = 100.0 * std::numeric_limits<double>::epsilon();
       sliceThicknessPresent = false;
     }
--- a/OrthancStone/Sources/Volumes/VolumeReslicer.cpp	Wed May 12 15:09:32 2021 +0200
+++ b/OrthancStone/Sources/Volumes/VolumeReslicer.cpp	Wed May 12 17:24:24 2021 +0200
@@ -459,10 +459,18 @@
                              float scaling,
                              float offset)
     {
-      if (source.GetFormat() == Orthanc::PixelFormat_Grayscale16 &&
+      if (source.GetFormat() == Orthanc::PixelFormat_Grayscale8 &&
           slice.GetFormat() == Orthanc::PixelFormat_Grayscale8)
       {
         ProcessImage<RowIterator,
+                     Orthanc::PixelFormat_Grayscale8,
+                     Orthanc::PixelFormat_Grayscale8>
+          (slice, extent, source, plane, box, interpolation, hasLinearFunction, scaling, offset);
+      }
+      else if (source.GetFormat() == Orthanc::PixelFormat_Grayscale16 &&
+               slice.GetFormat() == Orthanc::PixelFormat_Grayscale8)
+      {
+        ProcessImage<RowIterator,
                      Orthanc::PixelFormat_Grayscale16,
                      Orthanc::PixelFormat_Grayscale8>
           (slice, extent, source, plane, box, interpolation, hasLinearFunction, scaling, offset);
--- a/UnitTestsSources/VolumeRenderingTests.cpp	Wed May 12 15:09:32 2021 +0200
+++ b/UnitTestsSources/VolumeRenderingTests.cpp	Wed May 12 17:24:24 2021 +0200
@@ -19,9 +19,117 @@
  **/
 
 
+#include "../OrthancStone/Sources/Scene2D/CairoCompositor.h"
+#include "../OrthancStone/Sources/Scene2D/CopyStyleConfigurator.h"
+#include "../OrthancStone/Sources/Volumes/DicomVolumeImageMPRSlicer.h"
+#include "../OrthancStone/Sources/Volumes/DicomVolumeImageReslicer.h"
+
+#include <Images/ImageProcessing.h>
+#include <Images/ImageTraits.h>
+
 #include <gtest/gtest.h>
 
 TEST(VolumeRendering, Basic)
 {
+  Orthanc::DicomMap dicom;
+  dicom.SetValue(Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, "study", false);
+  dicom.SetValue(Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, "series", false);
+  dicom.SetValue(Orthanc::DICOM_TAG_SOP_INSTANCE_UID, "sop", false);
+  
+  OrthancStone::CoordinateSystem3D axial(OrthancStone::LinearAlgebra::CreateVector(-0.5, -0.5, 0),
+                                         OrthancStone::LinearAlgebra::CreateVector(1, 0, 0),
+                                         OrthancStone::LinearAlgebra::CreateVector(0, 1, 0));
+  
+  OrthancStone::VolumeImageGeometry geometry;
+  geometry.SetSizeInVoxels(3, 3, 1);
+  geometry.SetAxialGeometry(axial);
+  
+  boost::shared_ptr<OrthancStone::DicomVolumeImage> volume(new OrthancStone::DicomVolumeImage);
+  volume->Initialize(geometry, Orthanc::PixelFormat_Grayscale8, false);
+  volume->SetDicomParameters(OrthancStone::DicomInstanceParameters(dicom));
+
+  {
+    OrthancStone::ImageBuffer3D::SliceWriter writer(volume->GetPixelData(), OrthancStone::VolumeProjection_Axial, 0);
+    unsigned int v = 0;
+    for (unsigned int y = 0; y < writer.GetAccessor().GetHeight(); y++)
+    {
+      uint8_t *p = reinterpret_cast<uint8_t*>(writer.GetAccessor().GetRow(y));
+      for (unsigned int x = 0; x < writer.GetAccessor().GetWidth(); x++, p++)
+      {
+        *p = v;
+        v += 25;
+      }
+    }
+  }
+
+  OrthancStone::CoordinateSystem3D viewpoint;
+
+  //OrthancStone::DicomVolumeImageReslicer slicer(volume); slicer.SetInterpolation(OrthancStone::ImageInterpolation_Nearest);
+  OrthancStone::DicomVolumeImageMPRSlicer slicer(volume);
+  std::unique_ptr<OrthancStone::IVolumeSlicer::IExtractedSlice> slice(slicer.ExtractSlice(viewpoint));
+  ASSERT_TRUE(slice->IsValid());
+
+  OrthancStone::CopyStyleConfigurator configurator;
+  std::unique_ptr<OrthancStone::ISceneLayer> layer(slice->CreateSceneLayer(&configurator, viewpoint));
+
+  {
+    const Orthanc::ImageAccessor& a = dynamic_cast<OrthancStone::TextureBaseSceneLayer&>(*layer).GetTexture();
+    Orthanc::Image i(Orthanc::PixelFormat_Grayscale8, a.GetWidth(), a.GetHeight(), false);
+    Orthanc::ImageProcessing::Convert(i, a);
+
+    ASSERT_EQ(3u, i.GetWidth());
+    ASSERT_EQ(3u, i.GetHeight());
+
+    ASSERT_FLOAT_EQ(0, Orthanc::ImageTraits<Orthanc::PixelFormat_Grayscale8>::GetFloatPixel(i, 0, 0));
+    ASSERT_FLOAT_EQ(25, Orthanc::ImageTraits<Orthanc::PixelFormat_Grayscale8>::GetFloatPixel(i, 1, 0));
+    ASSERT_FLOAT_EQ(50, Orthanc::ImageTraits<Orthanc::PixelFormat_Grayscale8>::GetFloatPixel(i, 2, 0));
+    ASSERT_FLOAT_EQ(75, Orthanc::ImageTraits<Orthanc::PixelFormat_Grayscale8>::GetFloatPixel(i, 0, 1));
+    ASSERT_FLOAT_EQ(100, Orthanc::ImageTraits<Orthanc::PixelFormat_Grayscale8>::GetFloatPixel(i, 1, 1));
+    ASSERT_FLOAT_EQ(125, Orthanc::ImageTraits<Orthanc::PixelFormat_Grayscale8>::GetFloatPixel(i, 2, 1));
+    ASSERT_FLOAT_EQ(150, Orthanc::ImageTraits<Orthanc::PixelFormat_Grayscale8>::GetFloatPixel(i, 0, 2));
+    ASSERT_FLOAT_EQ(175, Orthanc::ImageTraits<Orthanc::PixelFormat_Grayscale8>::GetFloatPixel(i, 1, 2));
+    ASSERT_FLOAT_EQ(200, Orthanc::ImageTraits<Orthanc::PixelFormat_Grayscale8>::GetFloatPixel(i, 2, 2));
+  }
+
+  OrthancStone::Scene2D scene;  // Scene is initialized with the identity viewpoint
+  scene.SetLayer(0, layer.release());
+
+  OrthancStone::CairoCompositor compositor(5, 5);
+  compositor.Refresh(scene);
+
+  Orthanc::ImageAccessor i;
+  compositor.GetCanvas().GetReadOnlyAccessor(i);
+
+  Orthanc::Image j(Orthanc::PixelFormat_RGB24, i.GetWidth(), i.GetHeight(), false);
+  Orthanc::ImageProcessing::Convert(j, i);
+
+  ASSERT_EQ(5u, j.GetWidth());
+  ASSERT_EQ(5u, j.GetHeight());
+  Orthanc::PixelTraits<Orthanc::PixelFormat_RGB24>::PixelType pixel;
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 0, 0);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 1, 0);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 2, 0);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 3, 0);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 4, 0);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 0, 1);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 1, 1);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 2, 1);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 3, 1);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 4, 1);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 0, 2);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 1, 2);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 2, 2);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 3, 2);  ASSERT_EQ(25, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 4, 2);  ASSERT_EQ(50, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 0, 3);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 1, 3);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 2, 3);  ASSERT_EQ(75, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 3, 3);  ASSERT_EQ(100, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 4, 3);  ASSERT_EQ(125, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 0, 4);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 1, 4);  ASSERT_EQ(0, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 2, 4);  ASSERT_EQ(150, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 3, 4);  ASSERT_EQ(175, pixel.red_);
+  Orthanc::ImageTraits<Orthanc::PixelFormat_RGB24>::GetPixel(pixel, j, 4, 4);  ASSERT_EQ(200, pixel.red_);
 }