changeset 735:c3bbb130abc4

removing dependencies in ImageBuffer3D
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 21 May 2019 16:15:06 +0200
parents be3671662eec
children c0fcb2757b0a
files Framework/Toolbox/FiniteProjectiveCamera.cpp Framework/Toolbox/FiniteProjectiveCamera.h Framework/Toolbox/OrientedBoundingBox.cpp Framework/Toolbox/OrientedBoundingBox.h Framework/Toolbox/ParallelSlices.cpp Framework/Toolbox/ParallelSlices.h Framework/Toolbox/ShearWarpProjectiveTransform.cpp Framework/Toolbox/ShearWarpProjectiveTransform.h Framework/Volumes/ImageBuffer3D.cpp Framework/Volumes/ImageBuffer3D.h Framework/Volumes/VolumeReslicer.cpp Framework/Volumes/VolumeReslicer.h Samples/Sdl/Loader.cpp
diffstat 13 files changed, 140 insertions(+), 111 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Toolbox/FiniteProjectiveCamera.cpp	Tue May 21 15:20:04 2019 +0200
+++ b/Framework/Toolbox/FiniteProjectiveCamera.cpp	Tue May 21 16:15:06 2019 +0200
@@ -23,6 +23,7 @@
 
 #include "GeometryToolbox.h"
 #include "SubpixelReader.h"
+#include "ParallelSlices.h"
 
 #include <Core/Logging.h>
 #include <Core/OrthancException.h>
@@ -299,6 +300,7 @@
   static void ApplyRaytracerInternal(Orthanc::ImageAccessor& target,
                                      const FiniteProjectiveCamera& camera,
                                      const ImageBuffer3D& source,
+                                     const VolumeImageGeometry& geometry,
                                      VolumeProjection projection)
   {
     if (source.GetFormat() != SourceFormat ||
@@ -315,8 +317,8 @@
     LOG(WARNING) << "Output image size: " << target.GetWidth() << "x" << target.GetHeight();
     LOG(WARNING) << "Output pixel format: " << Orthanc::EnumerationToString(target.GetFormat());
 
-    std::auto_ptr<OrthancStone::ParallelSlices> slices(source.GetGeometry(projection));
-    const OrthancStone::Vector pixelSpacing = source.GetGeometry().GetVoxelDimensions(projection);
+    std::auto_ptr<OrthancStone::ParallelSlices> slices(OrthancStone::ParallelSlices::FromVolumeImage(geometry, projection));
+    const OrthancStone::Vector pixelSpacing = geometry.GetVoxelDimensions(projection);
     const unsigned int targetWidth = target.GetWidth();
     const unsigned int targetHeight = target.GetHeight();
 
@@ -422,6 +424,7 @@
 
   Orthanc::ImageAccessor*
   FiniteProjectiveCamera::ApplyRaytracer(const ImageBuffer3D& source,
+                                         const VolumeImageGeometry& geometry,
                                          Orthanc::PixelFormat targetFormat,
                                          unsigned int targetWidth,
                                          unsigned int targetHeight,
@@ -440,14 +443,14 @@
     {
       ApplyRaytracerInternal<Orthanc::PixelFormat_Grayscale16,
                              Orthanc::PixelFormat_Grayscale16, true>
-        (*target, *this, source, projection);
+        (*target, *this, source, geometry, projection);
     }
     else if (targetFormat == Orthanc::PixelFormat_Grayscale16 &&
              source.GetFormat() == Orthanc::PixelFormat_Grayscale16 && !mip)
     {
       ApplyRaytracerInternal<Orthanc::PixelFormat_Grayscale16,
                              Orthanc::PixelFormat_Grayscale16, false>
-        (*target, *this, source, projection);
+        (*target, *this, source, geometry, projection);
     }
     else
     {
--- a/Framework/Toolbox/FiniteProjectiveCamera.h	Tue May 21 15:20:04 2019 +0200
+++ b/Framework/Toolbox/FiniteProjectiveCamera.h	Tue May 21 16:15:06 2019 +0200
@@ -23,6 +23,7 @@
 
 #include "LinearAlgebra.h"
 #include "../Volumes/ImageBuffer3D.h"
+#include "VolumeImageGeometry.h"
 
 namespace OrthancStone
 {
@@ -109,6 +110,7 @@
     Vector ApplyGeneral(const Vector& v) const;
 
     Orthanc::ImageAccessor* ApplyRaytracer(const ImageBuffer3D& source,
+                                           const VolumeImageGeometry& geometry,
                                            Orthanc::PixelFormat targetFormat,
                                            unsigned int targetWidth,
                                            unsigned int targetHeight,
--- a/Framework/Toolbox/OrientedBoundingBox.cpp	Tue May 21 15:20:04 2019 +0200
+++ b/Framework/Toolbox/OrientedBoundingBox.cpp	Tue May 21 16:15:06 2019 +0200
@@ -22,6 +22,7 @@
 #include "OrientedBoundingBox.h"
 
 #include "GeometryToolbox.h"
+#include "../Volumes/ImageBuffer3D.h"
 
 #include <Core/OrthancException.h>
 
@@ -29,26 +30,25 @@
 
 namespace OrthancStone
 {
-  OrientedBoundingBox::OrientedBoundingBox(const ImageBuffer3D& image)
+  OrientedBoundingBox::OrientedBoundingBox(const VolumeImageGeometry& geometry)
   {
-    unsigned int n = image.GetDepth();
+    unsigned int n = geometry.GetDepth();
     if (n < 1)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);      
     }
 
-    const CoordinateSystem3D& geometry = image.GetGeometry().GetAxialGeometry();
-    Vector dim = image.GetGeometry().GetVoxelDimensions(VolumeProjection_Axial);
+    Vector dim = geometry.GetVoxelDimensions(VolumeProjection_Axial);
+
+    u_ = geometry.GetAxialGeometry().GetAxisX();
+    v_ = geometry.GetAxialGeometry().GetAxisY();
+    w_ = geometry.GetAxialGeometry().GetNormal();
 
-    u_ = geometry.GetAxisX();
-    v_ = geometry.GetAxisY();
-    w_ = geometry.GetNormal();
-
-    hu_ = static_cast<double>(image.GetWidth() * dim[0] / 2.0);
-    hv_ = static_cast<double>(image.GetHeight() * dim[1] / 2.0);
-    hw_ = static_cast<double>(image.GetDepth() * dim[2] / 2.0);
+    hu_ = static_cast<double>(geometry.GetWidth() * dim[0] / 2.0);
+    hv_ = static_cast<double>(geometry.GetHeight() * dim[1] / 2.0);
+    hw_ = static_cast<double>(geometry.GetDepth() * dim[2] / 2.0);
       
-    c_ = (geometry.GetOrigin() + 
+    c_ = (geometry.GetAxialGeometry().GetOrigin() + 
           (hu_ - dim[0] / 2.0) * u_ +
           (hv_ - dim[1] / 2.0) * v_ +
           (hw_ - dim[2] / 2.0) * w_);
--- a/Framework/Toolbox/OrientedBoundingBox.h	Tue May 21 15:20:04 2019 +0200
+++ b/Framework/Toolbox/OrientedBoundingBox.h	Tue May 21 16:15:06 2019 +0200
@@ -21,9 +21,10 @@
 
 #pragma once
 
+#include "CoordinateSystem3D.h"
 #include "Extent2D.h"
 #include "LinearAlgebra.h"
-#include "../Volumes/ImageBuffer3D.h"
+#include "VolumeImageGeometry.h"
 
 namespace OrthancStone
 {
@@ -39,7 +40,7 @@
     double  hw_;  // half depth
 
   public:
-    OrientedBoundingBox(const ImageBuffer3D& image);
+    OrientedBoundingBox(const VolumeImageGeometry& geometry);
 
     const Vector& GetCenter() const
     {
--- a/Framework/Toolbox/ParallelSlices.cpp	Tue May 21 15:20:04 2019 +0200
+++ b/Framework/Toolbox/ParallelSlices.cpp	Tue May 21 16:15:06 2019 +0200
@@ -22,6 +22,7 @@
 #include "ParallelSlices.h"
 
 #include "GeometryToolbox.h"
+#include "../Volumes/ImageBuffer3D.h"
 
 #include <Core/Logging.h>
 #include <Core/OrthancException.h>
@@ -30,7 +31,7 @@
 {
   ParallelSlices::ParallelSlices()
   {
-    LinearAlgebra::AssignVector(normal_, 0, 0, 1);
+    Clear();
   }
 
 
@@ -48,7 +49,7 @@
   }
 
 
-  ParallelSlices::~ParallelSlices()
+  void ParallelSlices::Clear()
   {
     for (size_t i = 0; i < slices_.size(); i++)
     {
@@ -58,6 +59,15 @@
         slices_[i] = NULL;
       }
     }
+
+    slices_.clear();
+    LinearAlgebra::AssignVector(normal_, 0, 0, 1);
+  }
+  
+
+  ParallelSlices::~ParallelSlices()
+  {
+    Clear();
   }
 
 
@@ -146,4 +156,60 @@
 
     return reversed.release();
   }
+
+
+  ParallelSlices* ParallelSlices::FromVolumeImage(const VolumeImageGeometry& geometry,
+                                                  VolumeProjection projection)
+  {
+    const Vector dimensions = geometry.GetVoxelDimensions(VolumeProjection_Axial);
+    const CoordinateSystem3D& axial = geometry.GetAxialGeometry();
+    
+    std::auto_ptr<ParallelSlices> result(new ParallelSlices);
+
+    switch (projection)
+    {
+      case VolumeProjection_Axial:
+        for (unsigned int z = 0; z < geometry.GetDepth(); z++)
+        {
+          Vector origin = axial.GetOrigin();
+          origin += static_cast<double>(z) * dimensions[2] * axial.GetNormal();
+
+          result->AddSlice(origin,
+                           axial.GetAxisX(),
+                           axial.GetAxisY());
+        }
+        break;
+
+      case VolumeProjection_Coronal:
+        for (unsigned int y = 0; y < geometry.GetHeight(); y++)
+        {
+          Vector origin = axial.GetOrigin();
+          origin += static_cast<double>(y) * dimensions[1] * axial.GetAxisY();
+          origin += static_cast<double>(geometry.GetDepth() - 1) * dimensions[2] * axial.GetNormal();
+
+          result->AddSlice(origin,
+                           axial.GetAxisX(),
+                           -axial.GetNormal());
+        }
+        break;
+
+      case VolumeProjection_Sagittal:
+        for (unsigned int x = 0; x < geometry.GetWidth(); x++)
+        {
+          Vector origin = axial.GetOrigin();
+          origin += static_cast<double>(x) * dimensions[0] * axial.GetAxisX();
+          origin += static_cast<double>(geometry.GetDepth() - 1) * dimensions[2] * axial.GetNormal();
+
+          result->AddSlice(origin,
+                           axial.GetAxisY(),
+                           -axial.GetNormal());
+        }
+        break;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    return result.release();
+  }
 }
--- a/Framework/Toolbox/ParallelSlices.h	Tue May 21 15:20:04 2019 +0200
+++ b/Framework/Toolbox/ParallelSlices.h	Tue May 21 16:15:06 2019 +0200
@@ -22,6 +22,7 @@
 #pragma once
 
 #include "CoordinateSystem3D.h"
+#include "VolumeImageGeometry.h"
 
 namespace OrthancStone
 {
@@ -33,6 +34,8 @@
     
     ParallelSlices& operator= (const ParallelSlices& other);  // Forbidden
 
+    void Clear();
+
   public:
     ParallelSlices();
 
@@ -63,5 +66,8 @@
                              const Vector& origin) const;
 
     ParallelSlices* Reverse() const;
+
+    static ParallelSlices* FromVolumeImage(const VolumeImageGeometry& geometry,
+                                           VolumeProjection projection);
   };
 }
--- a/Framework/Toolbox/ShearWarpProjectiveTransform.cpp	Tue May 21 15:20:04 2019 +0200
+++ b/Framework/Toolbox/ShearWarpProjectiveTransform.cpp	Tue May 21 16:15:06 2019 +0200
@@ -343,6 +343,7 @@
                                  float& maxValue,
                                  const Matrix& M_view,
                                  const ImageBuffer3D& source,
+                                 const VolumeImageGeometry& geometry,
                                  double pixelSpacing,
                                  unsigned int countSlices,
                                  ImageInterpolation shearInterpolation,
@@ -385,8 +386,8 @@
     
     // Compute the "world" matrix that maps the source volume to the
     // (0,0,0)->(1,1,1) unit cube
-    Vector origin = source.GetGeometry().GetCoordinates(0, 0, 0);
-    Vector ps = source.GetGeometry().GetVoxelDimensions(VolumeProjection_Axial);
+    Vector origin = geometry.GetCoordinates(0, 0, 0);
+    Vector ps = geometry.GetVoxelDimensions(VolumeProjection_Axial);
     Matrix world = LinearAlgebra::Product(
       GeometryToolbox::CreateScalingMatrix(1.0 / ps[0], 1.0 / ps[1], 1.0 / ps[2]),
       GeometryToolbox::CreateTranslationMatrix(-origin[0], -origin[1], -origin[2]));
@@ -598,6 +599,7 @@
                                   float& maxValue,
                                   const Matrix& M_view,
                                   const ImageBuffer3D& source,
+                                  const VolumeImageGeometry& geometry,
                                   bool mip,
                                   double pixelSpacing,
                                   unsigned int countSlices,
@@ -607,13 +609,13 @@
     if (mip)
     {
       ApplyAxialInternal<SourceFormat, TargetFormat, true>
-        (target, maxValue, M_view, source, pixelSpacing,
+        (target, maxValue, M_view, source, geometry, pixelSpacing,
          countSlices, shearInterpolation, warpInterpolation);
     }
     else
     {
       ApplyAxialInternal<SourceFormat, TargetFormat, false>
-        (target, maxValue, M_view, source, pixelSpacing,
+        (target, maxValue, M_view, source, geometry, pixelSpacing,
          countSlices, shearInterpolation, warpInterpolation);
     } 
   }
@@ -623,6 +625,7 @@
   ShearWarpProjectiveTransform::ApplyAxial(float& maxValue,
                                            const Matrix& M_view,
                                            const ImageBuffer3D& source,
+                                           const VolumeImageGeometry& geometry,
                                            Orthanc::PixelFormat targetFormat,
                                            unsigned int targetWidth,
                                            unsigned int targetHeight,
@@ -640,7 +643,7 @@
     {
       ApplyAxialInternal2<Orthanc::PixelFormat_Grayscale16,
                           Orthanc::PixelFormat_Grayscale16>
-        (*target, maxValue, M_view, source, mip, pixelSpacing,
+        (*target, maxValue, M_view, source, geometry, mip, pixelSpacing,
          countSlices, shearInterpolation, warpInterpolation);
     }
     else if (source.GetFormat() == Orthanc::PixelFormat_SignedGrayscale16 &&
@@ -648,7 +651,7 @@
     {
       ApplyAxialInternal2<Orthanc::PixelFormat_SignedGrayscale16,
                           Orthanc::PixelFormat_SignedGrayscale16>
-        (*target, maxValue, M_view, source, mip, pixelSpacing,
+        (*target, maxValue, M_view, source, geometry, mip, pixelSpacing,
          countSlices, shearInterpolation, warpInterpolation);
     }
     else
--- a/Framework/Toolbox/ShearWarpProjectiveTransform.h	Tue May 21 15:20:04 2019 +0200
+++ b/Framework/Toolbox/ShearWarpProjectiveTransform.h	Tue May 21 16:15:06 2019 +0200
@@ -92,6 +92,7 @@
     static Orthanc::ImageAccessor* ApplyAxial(float& maxValue,
                                               const Matrix& M_view,  // cf. "CalibrateView()"
                                               const ImageBuffer3D& source,
+                                              const VolumeImageGeometry& geometry,
                                               Orthanc::PixelFormat targetFormat,
                                               unsigned int targetWidth,
                                               unsigned int targetHeight,
--- a/Framework/Volumes/ImageBuffer3D.cpp	Tue May 21 15:20:04 2019 +0200
+++ b/Framework/Volumes/ImageBuffer3D.cpp	Tue May 21 16:15:06 2019 +0200
@@ -21,8 +21,6 @@
 
 #include "ImageBuffer3D.h"
 
-#include "../Toolbox/GeometryToolbox.h"
-
 #include <Core/Images/ImageProcessing.h>
 #include <Core/Logging.h>
 #include <Core/OrthancException.h>
@@ -118,8 +116,6 @@
     computeRange_(computeRange),
     hasRange_(false)
   {
-    geometry_.SetSize(width, height, depth);
-
     LOG(INFO) << "Created a 3D image of size " << width << "x" << height
               << "x" << depth << " in " << Orthanc::EnumerationToString(format)
               << " (" << (GetEstimatedMemorySize() / (1024ll * 1024ll)) << "MB)";
@@ -132,63 +128,6 @@
   }
 
 
-
-
-  ParallelSlices* ImageBuffer3D::GetGeometry(VolumeProjection projection) const
-  {
-    const Vector dimensions = geometry_.GetVoxelDimensions(VolumeProjection_Axial);
-    const CoordinateSystem3D& axial = geometry_.GetAxialGeometry();
-    
-    std::auto_ptr<ParallelSlices> result(new ParallelSlices);
-
-    switch (projection)
-    {
-      case VolumeProjection_Axial:
-        for (unsigned int z = 0; z < depth_; z++)
-        {
-          Vector origin = axial.GetOrigin();
-          origin += static_cast<double>(z) * dimensions[2] * axial.GetNormal();
-
-          result->AddSlice(origin,
-                           axial.GetAxisX(),
-                           axial.GetAxisY());
-        }
-        break;
-
-      case VolumeProjection_Coronal:
-        for (unsigned int y = 0; y < height_; y++)
-        {
-          Vector origin = axial.GetOrigin();
-          origin += static_cast<double>(y) * dimensions[1] * axial.GetAxisY();
-          origin += static_cast<double>(depth_ - 1) * dimensions[2] * axial.GetNormal();
-
-          result->AddSlice(origin,
-                           axial.GetAxisX(),
-                           -axial.GetNormal());
-        }
-        break;
-
-      case VolumeProjection_Sagittal:
-        for (unsigned int x = 0; x < width_; x++)
-        {
-          Vector origin = axial.GetOrigin();
-          origin += static_cast<double>(x) * dimensions[0] * axial.GetAxisX();
-          origin += static_cast<double>(depth_ - 1) * dimensions[2] * axial.GetNormal();
-
-          result->AddSlice(origin,
-                           axial.GetAxisY(),
-                           -axial.GetNormal());
-        }
-        break;
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    return result.release();
-  }
-
-
   uint64_t ImageBuffer3D::GetEstimatedMemorySize() const
   {
     return image_.GetPitch() * image_.GetHeight() * Orthanc::GetBytesPerPixel(format_);
--- a/Framework/Volumes/ImageBuffer3D.h	Tue May 21 15:20:04 2019 +0200
+++ b/Framework/Volumes/ImageBuffer3D.h	Tue May 21 16:15:06 2019 +0200
@@ -22,8 +22,7 @@
 #pragma once
 
 #include "../StoneEnumerations.h"
-#include "../Toolbox/ParallelSlices.h"
-#include "../Toolbox/VolumeImageGeometry.h"
+#include "../Toolbox/LinearAlgebra.h"
 
 #include <Core/Images/Image.h>
 
@@ -32,7 +31,6 @@
   class ImageBuffer3D : public boost::noncopyable
   {
   private:
-    VolumeImageGeometry    geometry_;  // TODO => Move this out of this class
     Orthanc::Image         image_;
     Orthanc::PixelFormat   format_;
     unsigned int           width_;
@@ -76,16 +74,6 @@
 
     void Clear();
 
-    VolumeImageGeometry& GetGeometry()
-    {
-      return geometry_;
-    }
-
-    const VolumeImageGeometry& GetGeometry() const
-    {
-      return geometry_;
-    }
-
     const Orthanc::ImageAccessor& GetInternalImage() const
     {
       return image_;
@@ -111,9 +99,6 @@
       return format_;
     }
 
-    // TODO - Remove
-    ParallelSlices* GetGeometry(VolumeProjection projection) const;
-    
     uint64_t GetEstimatedMemorySize() const;
 
     bool GetRange(float& minValue,
--- a/Framework/Volumes/VolumeReslicer.cpp	Tue May 21 15:20:04 2019 +0200
+++ b/Framework/Volumes/VolumeReslicer.cpp	Tue May 21 16:15:06 2019 +0200
@@ -745,12 +745,13 @@
 
   
   void VolumeReslicer::Apply(const ImageBuffer3D& source,
+                             const VolumeImageGeometry& geometry,
                              const CoordinateSystem3D& plane)
   {
     // Choose the default voxel size as the finest voxel dimension
     // of the source volumetric image
     const OrthancStone::Vector dim =
-      source.GetGeometry().GetVoxelDimensions(OrthancStone::VolumeProjection_Axial);
+      geometry.GetVoxelDimensions(OrthancStone::VolumeProjection_Axial);
     double voxelSize = dim[0];
     
     if (dim[1] < voxelSize)
@@ -768,11 +769,12 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
     }
 
-    Apply(source, plane, voxelSize);
+    Apply(source, geometry, plane, voxelSize);
   }
 
   
   void VolumeReslicer::Apply(const ImageBuffer3D& source,
+                             const VolumeImageGeometry& geometry,
                              const CoordinateSystem3D& plane,
                              double voxelSize)
   {
@@ -783,7 +785,7 @@
     // to 6 vertices. We compute the extent of the intersection
     // polygon, with respect to the coordinate system of the reslicing
     // plane.
-    OrientedBoundingBox box(source);
+    OrientedBoundingBox box(geometry);
 
     if (!box.ComputeExtent(extent_, plane))
     {
--- a/Framework/Volumes/VolumeReslicer.h	Tue May 21 15:20:04 2019 +0200
+++ b/Framework/Volumes/VolumeReslicer.h	Tue May 21 16:15:06 2019 +0200
@@ -111,9 +111,11 @@
     Orthanc::ImageAccessor* ReleaseOutputSlice();
 
     void Apply(const ImageBuffer3D& source,
+               const VolumeImageGeometry& geometry,
                const CoordinateSystem3D& plane);
 
     void Apply(const ImageBuffer3D& source,
+               const VolumeImageGeometry& geometry,
                const CoordinateSystem3D& plane,
                double voxelSize);
   };
--- a/Samples/Sdl/Loader.cpp	Tue May 21 15:20:04 2019 +0200
+++ b/Samples/Sdl/Loader.cpp	Tue May 21 16:15:06 2019 +0200
@@ -31,6 +31,7 @@
 #include "../../Framework/StoneInitialization.h"
 #include "../../Framework/Toolbox/GeometryToolbox.h"
 #include "../../Framework/Toolbox/SlicesSorter.h"
+#include "../../Framework/Toolbox/VolumeImageGeometry.h"
 #include "../../Framework/Volumes/ImageBuffer3D.h"
 
 // From Orthanc framework
@@ -1269,6 +1270,7 @@
   {
   private:
     std::auto_ptr<OrthancStone::ImageBuffer3D>  image_;
+    std::auto_ptr<OrthancStone::VolumeImageGeometry>  geometry_;
     std::vector<DicomInstanceParameters*>       slices_;
     uint64_t                                    revision_;
     std::vector<uint64_t>                       slicesRevision_;
@@ -1336,6 +1338,7 @@
     void Clear()
     {
       image_.reset();
+      geometry_.reset();
       
       for (size_t i = 0; i < slices_.size(); i++)
       {
@@ -1386,6 +1389,8 @@
                                         "Cannot sort the 3D slices of a DICOM series");          
       }
 
+      geometry_.reset(new OrthancStone::VolumeImageGeometry);
+
       if (slices.GetSlicesCount() == 0)
       {
         // Empty volume
@@ -1417,9 +1422,10 @@
                                                      parameters.GetImageInformation().GetHeight(),
                                                      slices.GetSlicesCount(), false /* don't compute range */));      
 
-        image_->GetGeometry().SetAxialGeometry(slices.GetSliceGeometry(0));
-        image_->GetGeometry().SetVoxelDimensions(parameters.GetPixelSpacingX(),
-                                                 parameters.GetPixelSpacingY(), spacingZ);
+        geometry_->SetSize(image_->GetWidth(), image_->GetHeight(), image_->GetDepth());
+        geometry_->SetAxialGeometry(slices.GetSliceGeometry(0));
+        geometry_->SetVoxelDimensions(parameters.GetPixelSpacingX(),
+                                      parameters.GetPixelSpacingY(), spacingZ);
       }
       
       image_->Clear();
@@ -1434,7 +1440,8 @@
 
     bool HasGeometry() const
     {
-      return (image_.get() != NULL);
+      return (image_.get() != NULL &&
+              geometry_.get() != NULL);
     }
 
     const OrthancStone::ImageBuffer3D& GetImage() const
@@ -1449,6 +1456,18 @@
       }
     }
 
+    const OrthancStone::VolumeImageGeometry& GetGeometry() const
+    {
+      if (!HasGeometry())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+      else
+      {
+        return *geometry_;
+      }
+    }
+
     size_t GetSlicesCount() const
     {
       if (!HasGeometry())
@@ -1834,7 +1853,7 @@
         return;
       }
 
-      const OrthancStone::VolumeImageGeometry& geometry = source_.GetVolume().GetImage().GetGeometry();
+      const OrthancStone::VolumeImageGeometry& geometry = source_.GetVolume().GetGeometry();
 
       OrthancStone::VolumeProjection projection;
       unsigned int sliceIndex;