# HG changeset patch # User Sebastien Jodogne # Date 1620833064 -7200 # Node ID 6c246f862b009f65de6afd64468e9d694646ce78 # Parent f302bbddf94d4f2c00b6c6e8dd95cec3dde068d1 unit test VolumeRendering.Basic diff -r f302bbddf94d -r 6c246f862b00 OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake --- 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 diff -r f302bbddf94d -r 6c246f862b00 OrthancStone/Sources/Toolbox/DicomInstanceParameters.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::epsilon(); sliceThicknessPresent = false; } diff -r f302bbddf94d -r 6c246f862b00 OrthancStone/Sources/Volumes/VolumeReslicer.cpp --- 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 + (slice, extent, source, plane, box, interpolation, hasLinearFunction, scaling, offset); + } + else if (source.GetFormat() == Orthanc::PixelFormat_Grayscale16 && + slice.GetFormat() == Orthanc::PixelFormat_Grayscale8) + { + ProcessImage (slice, extent, source, plane, box, interpolation, hasLinearFunction, scaling, offset); diff -r f302bbddf94d -r 6c246f862b00 UnitTestsSources/VolumeRenderingTests.cpp --- 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 +#include + #include 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 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(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 slice(slicer.ExtractSlice(viewpoint)); + ASSERT_TRUE(slice->IsValid()); + + OrthancStone::CopyStyleConfigurator configurator; + std::unique_ptr layer(slice->CreateSceneLayer(&configurator, viewpoint)); + + { + const Orthanc::ImageAccessor& a = dynamic_cast(*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::GetFloatPixel(i, 0, 0)); + ASSERT_FLOAT_EQ(25, Orthanc::ImageTraits::GetFloatPixel(i, 1, 0)); + ASSERT_FLOAT_EQ(50, Orthanc::ImageTraits::GetFloatPixel(i, 2, 0)); + ASSERT_FLOAT_EQ(75, Orthanc::ImageTraits::GetFloatPixel(i, 0, 1)); + ASSERT_FLOAT_EQ(100, Orthanc::ImageTraits::GetFloatPixel(i, 1, 1)); + ASSERT_FLOAT_EQ(125, Orthanc::ImageTraits::GetFloatPixel(i, 2, 1)); + ASSERT_FLOAT_EQ(150, Orthanc::ImageTraits::GetFloatPixel(i, 0, 2)); + ASSERT_FLOAT_EQ(175, Orthanc::ImageTraits::GetFloatPixel(i, 1, 2)); + ASSERT_FLOAT_EQ(200, Orthanc::ImageTraits::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::PixelType pixel; + Orthanc::ImageTraits::GetPixel(pixel, j, 0, 0); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 1, 0); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 2, 0); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 3, 0); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 4, 0); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 0, 1); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 1, 1); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 2, 1); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 3, 1); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 4, 1); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 0, 2); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 1, 2); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 2, 2); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 3, 2); ASSERT_EQ(25, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 4, 2); ASSERT_EQ(50, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 0, 3); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 1, 3); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 2, 3); ASSERT_EQ(75, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 3, 3); ASSERT_EQ(100, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 4, 3); ASSERT_EQ(125, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 0, 4); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 1, 4); ASSERT_EQ(0, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 2, 4); ASSERT_EQ(150, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 3, 4); ASSERT_EQ(175, pixel.red_); + Orthanc::ImageTraits::GetPixel(pixel, j, 4, 4); ASSERT_EQ(200, pixel.red_); }