changeset 183:98da3a8d4820 wasm

SubvoxelReader
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 15 Mar 2018 15:19:24 +0100
parents 2cbfb08f3a95
children 9523ce4f44cc
files Framework/Toolbox/SubpixelReader.h Framework/Toolbox/SubvoxelReader.h Framework/Volumes/VolumeReslicer.cpp
diffstat 3 files changed, 519 insertions(+), 296 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Toolbox/SubpixelReader.h	Thu Mar 15 12:12:01 2018 +0100
+++ b/Framework/Toolbox/SubpixelReader.h	Thu Mar 15 15:19:24 2018 +0100
@@ -90,7 +90,12 @@
     inline bool GetValue(PixelType& target,
                          float x,
                          float y) const;
+
+    inline bool GetFloatValue(float& target,
+                              float x,
+                              float y) const;
   };
+
     
     
   template <Orthanc::PixelFormat Format>
@@ -106,9 +111,13 @@
     {
     }
 
+    inline bool GetFloatValue(float& target,
+                              float x,
+                              float y) const;
+
     inline bool GetValue(PixelType& target,
                          float x,
-                         float y);
+                         float y) const;
   };
 
 
@@ -144,9 +153,49 @@
 
 
   template <Orthanc::PixelFormat Format>
+  bool SubpixelReader<Format, ImageInterpolation_Nearest>::GetFloatValue(float& target,
+                                                                         float x,
+                                                                         float y) const
+  {
+    PixelType value;
+    
+    if (GetValue(value, x, y))
+    {
+      target = Traits::PixelToFloat(value);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+
+  template <Orthanc::PixelFormat Format>
   bool SubpixelReader<Format, ImageInterpolation_Bilinear>::GetValue(PixelType& target,
                                                                      float x,
-                                                                     float y)
+                                                                     float y) const
+  {
+    float value;
+
+    if (GetFloatValue(value, x, y))
+    {
+      Traits::FloatToPixel(target, value);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+
+  template <Orthanc::PixelFormat Format>
+  bool SubpixelReader<Format, ImageInterpolation_Bilinear>::GetFloatValue(float& target,
+                                                                          float x,
+                                                                          float y) const
   {
     x -= 0.5f;
     y -= 0.5f;
@@ -203,9 +252,8 @@
 
       float ax = x - static_cast<float>(ux);
       float ay = y - static_cast<float>(uy);
-      float value = GeometryToolbox::ComputeBilinearInterpolationUnitSquare(ax, ay, f00, f01, f10, f11);
+      target = GeometryToolbox::ComputeBilinearInterpolationUnitSquare(ax, ay, f00, f01, f10, f11);
           
-      Traits::FloatToPixel(target, value);
       return true;
     }
   }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Toolbox/SubvoxelReader.h	Thu Mar 15 15:19:24 2018 +0100
@@ -0,0 +1,419 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Volumes/ImageBuffer3D.h"
+#include "GeometryToolbox.h"
+
+#include <Core/Images/ImageTraits.h>
+
+#include <boost/noncopyable.hpp>
+#include <cmath>
+
+namespace OrthancStone
+{
+  namespace Internals
+  {
+    class SubvoxelReaderBase : public boost::noncopyable
+    {
+    private:
+      const ImageBuffer3D& source_;
+      unsigned int         width_;
+      unsigned int         height_;
+      unsigned int         depth_;
+
+    public:
+      SubvoxelReaderBase(const ImageBuffer3D& source) :
+        source_(source),
+        width_(source.GetWidth()),
+        height_(source.GetHeight()),
+        depth_(source.GetDepth())
+      {
+      }
+
+      ORTHANC_FORCE_INLINE
+      const Orthanc::ImageAccessor& GetSource() const
+      {
+        return source_.GetInternalImage();
+      }
+      
+      ORTHANC_FORCE_INLINE
+      unsigned int GetWidth() const
+      {
+        return width_;
+      }
+      
+      ORTHANC_FORCE_INLINE
+      unsigned int GetHeight() const
+      {
+        return height_;
+      }
+      
+      ORTHANC_FORCE_INLINE
+      unsigned int GetDepth() const
+      {
+        return depth_;
+      }
+      
+      ORTHANC_FORCE_INLINE
+      unsigned int ComputeRow(unsigned int y,
+                              unsigned int z) const
+      {
+        return z * height_ + y;
+      }
+    };
+  }
+
+    
+  template <Orthanc::PixelFormat Format,
+            ImageInterpolation Interpolation>
+  class SubvoxelReader;
+
+    
+  template <Orthanc::PixelFormat Format>
+  class SubvoxelReader<Format, ImageInterpolation_Nearest> : 
+    public Internals::SubvoxelReaderBase
+  {
+  public:
+    typedef Orthanc::PixelTraits<Format>  Traits;
+    typedef typename Traits::PixelType    PixelType;
+
+    SubvoxelReader(const ImageBuffer3D& source) :
+      SubvoxelReaderBase(source)
+    {
+    }
+
+    inline bool GetValue(PixelType& target,
+                         float x,
+                         float y,
+                         float z) const;
+    
+    inline bool GetFloatValue(float& target,
+                              float x,
+                              float y,
+                              float z) const;
+  };
+    
+    
+  template <Orthanc::PixelFormat Format>
+  class SubvoxelReader<Format, ImageInterpolation_Bilinear> : 
+    public Internals::SubvoxelReaderBase
+  {
+  public:
+    typedef Orthanc::PixelTraits<Format>  Traits;
+    typedef typename Traits::PixelType    PixelType;
+
+    SubvoxelReader(const ImageBuffer3D& source) :
+      SubvoxelReaderBase(source)
+    {
+    }
+
+    inline bool Sample(float& f00,
+                       float& f01,
+                       float& f10,
+                       float& f11,
+                       unsigned int ux,
+                       unsigned int uy,
+                       unsigned int uz) const;
+
+    inline bool GetValue(PixelType& target,
+                         float x,
+                         float y,
+                         float z) const;
+
+    inline bool GetFloatValue(float& target,
+                              float x,
+                              float y,
+                              float z) const;
+  };
+    
+
+  template <Orthanc::PixelFormat Format>
+  class SubvoxelReader<Format, ImageInterpolation_Trilinear> : 
+    public Internals::SubvoxelReaderBase
+  {
+  private:
+    SubvoxelReader<Format, ImageInterpolation_Bilinear>   bilinear_;
+
+  public:
+    typedef Orthanc::PixelTraits<Format>  Traits;
+    typedef typename Traits::PixelType    PixelType;
+
+    SubvoxelReader(const ImageBuffer3D& source) :
+      SubvoxelReaderBase(source),
+      bilinear_(source)
+    {
+    }
+
+    inline bool GetValue(PixelType& target,
+                         float x,
+                         float y,
+                         float z) const;
+
+    inline bool GetFloatValue(float& target,
+                              float x,
+                              float y,
+                              float z) const;
+  };
+
+
+
+  template <Orthanc::PixelFormat Format>
+  bool SubvoxelReader<Format, ImageInterpolation_Nearest>::GetValue(PixelType& target,
+                                                                    float x,
+                                                                    float y,
+                                                                    float z) const
+  {
+    if (x < 0 ||
+        y < 0 ||
+        z < 0)
+    {
+      return false;
+    }
+    else
+    {
+      unsigned int ux = static_cast<unsigned int>(std::floor(x));
+      unsigned int uy = static_cast<unsigned int>(std::floor(y));
+      unsigned int uz = static_cast<unsigned int>(std::floor(z));
+
+      if (ux < GetWidth() &&
+          uy < GetHeight() &&
+          uz < GetDepth())
+      {
+        Orthanc::ImageTraits<Format>::GetPixel(target, GetSource(), ux, ComputeRow(uy, uz));
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+    }
+  }
+
+
+  template <Orthanc::PixelFormat Format>
+  bool SubvoxelReader<Format, ImageInterpolation_Nearest>::GetFloatValue(float& target,
+                                                                         float x,
+                                                                         float y,
+                                                                         float z) const
+  {
+    PixelType value;
+    
+    if (GetValue(value, x, y, z))
+    {
+      target = Traits::PixelToFloat(value);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+
+  template <Orthanc::PixelFormat Format>
+  bool SubvoxelReader<Format, ImageInterpolation_Bilinear>::Sample(float& f00,
+                                                                   float& f01,
+                                                                   float& f10,
+                                                                   float& f11,
+                                                                   unsigned int ux,
+                                                                   unsigned int uy,
+                                                                   unsigned int uz) const
+  {
+    if (ux < GetWidth() &&
+        uy < GetHeight() &&
+        uz < GetDepth())
+    {
+      f00 = Orthanc::ImageTraits<Format>::GetFloatPixel(GetSource(), ux, ComputeRow(uy, uz));
+    }
+    else
+    {
+      // Pixel is out of the volume
+      return false;
+    }
+
+    if (ux + 1 < GetWidth())
+    {
+      f01 = Orthanc::ImageTraits<Format>::GetFloatPixel(GetSource(), ux + 1, ComputeRow(uy, uz));
+    }
+    else
+    {
+      f01 = f00;
+    }
+
+    if (uy + 1 < GetHeight())
+    {
+      f10 = Orthanc::ImageTraits<Format>::GetFloatPixel(GetSource(), ux, ComputeRow(uy + 1, uz));
+    }
+    else
+    {
+      f10 = f00;
+    }
+
+    if (ux + 1 < GetWidth() &&
+        uy + 1 < GetHeight())
+    {
+      f11 = Orthanc::ImageTraits<Format>::GetFloatPixel(GetSource(), ux + 1, ComputeRow(uy + 1, uz));
+    }
+    else
+    {
+      f11 = f00;
+    }
+
+    return true;
+  }
+
+
+
+  template <Orthanc::PixelFormat Format>
+  bool SubvoxelReader<Format, ImageInterpolation_Bilinear>::GetFloatValue(float& target,
+                                                                          float x,
+                                                                          float y,
+                                                                          float z) const
+  {
+    x -= 0.5f;
+    y -= 0.5f;
+        
+    if (x < 0 ||
+        y < 0 ||
+        z < 0)
+    {
+      return false;
+    }
+    else
+    {
+      unsigned int ux = static_cast<unsigned int>(std::floor(x));
+      unsigned int uy = static_cast<unsigned int>(std::floor(y));
+      unsigned int uz = static_cast<unsigned int>(std::floor(z));
+
+      float f00, f01, f10, f11;
+      if (Sample(f00, f01, f10, f11, ux, uy, uz))
+      {
+        float ax = x - static_cast<float>(ux);
+        float ay = y - static_cast<float>(uy);
+
+        target = GeometryToolbox::ComputeBilinearInterpolationUnitSquare(ax, ay, f00, f01, f10, f11);
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+    }
+  }
+
+
+
+  template <Orthanc::PixelFormat Format>
+  bool SubvoxelReader<Format, ImageInterpolation_Bilinear>::GetValue(PixelType& target,
+                                                                     float x,
+                                                                     float y,
+                                                                     float z) const
+  {
+    float value;
+
+    if (GetFloatValue(value, x, y, z))
+    {
+      Traits::FloatToPixel(target, value);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+
+  template <Orthanc::PixelFormat Format>
+  bool SubvoxelReader<Format, ImageInterpolation_Trilinear>::GetFloatValue(float& target,
+                                                                           float x,
+                                                                           float y,
+                                                                           float z) const
+  {
+    x -= 0.5f;
+    y -= 0.5f;
+    z -= 0.5f;
+        
+    if (x < 0 ||
+        y < 0 ||
+        z < 0)
+    {
+      return false;
+    }
+    else
+    {
+      unsigned int ux = static_cast<unsigned int>(std::floor(x));
+      unsigned int uy = static_cast<unsigned int>(std::floor(y));
+      unsigned int uz = static_cast<unsigned int>(std::floor(z));
+
+      float f000, f001, f010, f011;
+      if (bilinear_.Sample(f000, f001, f010, f011, ux, uy, uz))
+      {
+        const float ax = x - static_cast<float>(ux);
+        const float ay = y - static_cast<float>(uy);
+
+        float f100, f101, f110, f111;
+
+        if (bilinear_.Sample(f100, f101, f110, f111, ux, uy, uz + 1))
+        {
+          const float az = z - static_cast<float>(uz);
+          target = GeometryToolbox::ComputeTrilinearInterpolationUnitSquare
+            (ax, ay, az, f000, f001, f010, f011, f100, f101, f110, f111);
+        }
+        else
+        {
+          target = GeometryToolbox::ComputeBilinearInterpolationUnitSquare
+            (ax, ay, f000, f001, f010, f011);
+        }
+
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+    }
+  }
+
+
+
+  template <Orthanc::PixelFormat Format>
+  bool SubvoxelReader<Format, ImageInterpolation_Trilinear>::GetValue(PixelType& target,
+                                                                      float x,
+                                                                      float y,
+                                                                      float z) const
+  {
+    float value;
+
+    if (GetFloatValue(value, x, y, z))
+    {
+      Traits::FloatToPixel(target, value);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+}
--- a/Framework/Volumes/VolumeReslicer.cpp	Thu Mar 15 12:12:01 2018 +0100
+++ b/Framework/Volumes/VolumeReslicer.cpp	Thu Mar 15 15:19:24 2018 +0100
@@ -1,6 +1,7 @@
 #include "VolumeReslicer.h"
 
 #include "../Toolbox/GeometryToolbox.h"
+#include "../Toolbox/SubvoxelReader.h"
 
 #include <Core/Images/ImageTraits.h>
 #include <Core/Logging.h>
@@ -116,286 +117,6 @@
     };
 
 
-
-    class VoxelReaderBase : public boost::noncopyable
-    {
-    private:
-      const Orthanc::ImageAccessor&  image_;
-      unsigned int                   width_;
-      unsigned int                   height_;
-      unsigned int                   depth_;
-      float                          widthFloat_;
-      float                          heightFloat_;
-      float                          depthFloat_;
-
-    public:
-      VoxelReaderBase(const ImageBuffer3D& image) :
-        image_(image.GetInternalImage()),
-        width_(image.GetWidth()),
-        height_(image.GetHeight()),
-        depth_(image.GetDepth()),
-        widthFloat_(static_cast<float>(image.GetWidth())),
-        heightFloat_(static_cast<float>(image.GetHeight())),
-        depthFloat_(static_cast<float>(image.GetDepth()))
-      {
-      }
-
-      const Orthanc::ImageAccessor& GetImage() const
-      {
-        return image_;
-      }
-
-      const unsigned int GetImageWidth() const
-      {
-        return width_;
-      }
-
-      const unsigned int GetImageHeight() const
-      {
-        return height_;
-      }
-
-      const unsigned int GetImageDepth() const
-      {
-        return depth_;
-      }
-
-      bool GetNearestCoordinates(unsigned int& imageX,
-                                 unsigned int& imageY,
-                                 unsigned int& imageZ,
-                                 float& fractionalX,
-                                 float& fractionalY,
-                                 float& fractionalZ,
-                                 float volumeX,
-                                 float volumeY,
-                                 float volumeZ) const
-      {
-        if (volumeX >= 0 &&
-            volumeY >= 0 &&
-            volumeZ >= 0)
-        {
-          const float x = volumeX * widthFloat_;
-          const float y = volumeY * heightFloat_;
-          const float z = volumeZ * depthFloat_;
-          
-          imageX = static_cast<unsigned int>(std::floor(x));
-          imageY = static_cast<unsigned int>(std::floor(y));
-          imageZ = static_cast<unsigned int>(std::floor(z));
-
-          if (imageX < width_ &&
-              imageY < height_ &&
-              imageZ < depth_)
-          {
-            fractionalX = x - static_cast<float>(imageX);
-            fractionalY = y - static_cast<float>(imageY);
-            fractionalZ = z - static_cast<float>(imageZ);
-            return true;
-          }
-          else
-          {
-            return false;
-          }
-        }
-        else
-        {
-          return false;
-        }
-      }
-    };
-
-
-    template <Orthanc::PixelFormat InputFormat,
-              ImageInterpolation Interpolation>
-    class VoxelReader;
-
-    
-    template <Orthanc::PixelFormat InputFormat>
-    class VoxelReader<InputFormat, ImageInterpolation_Nearest> :
-      public VoxelReaderBase
-    {
-    public:
-      typedef typename Orthanc::PixelTraits<InputFormat>::PixelType   InputPixelType;
-
-      VoxelReader(const ImageBuffer3D& image) :
-        VoxelReaderBase(image)
-      {
-      }
-
-      ORTHANC_FORCE_INLINE
-      float GetFloatValue(float volumeX,
-                          float volumeY,
-                          float volumeZ) const
-      {
-        InputPixelType value;
-        GetValue(value, volumeX, volumeY, volumeZ);
-        return static_cast<float>(value);
-      }
-
-      ORTHANC_FORCE_INLINE
-      void GetValue(InputPixelType& target,
-                    float volumeX,
-                    float volumeY,
-                    float volumeZ) const
-      {
-        unsigned int imageX, imageY, imageZ;
-        float fractionalX, fractionalY, fractionalZ;  // unused
-
-        if (GetNearestCoordinates(imageX, imageY, imageZ,
-                                  fractionalX, fractionalY, fractionalZ,
-                                  volumeX, volumeY, volumeZ))
-        {
-          Orthanc::ImageTraits<InputFormat>::GetPixel(target, GetImage(), imageX, 
-                                                      imageY + imageZ * GetImageHeight());
-        }
-        else
-        {
-          target = std::numeric_limits<InputPixelType>::min();
-        }
-      }
-    };
-    
-    
-    template <Orthanc::PixelFormat InputFormat>
-    class VoxelReader<InputFormat, ImageInterpolation_Bilinear> :
-      public VoxelReaderBase
-    {
-    private:
-      float outOfVolume_;
-      
-    public:
-      VoxelReader(const ImageBuffer3D& image) :
-        VoxelReaderBase(image)
-      {
-        typedef typename Orthanc::PixelTraits<InputFormat>::PixelType Pixel;
-        outOfVolume_ = static_cast<float>(std::numeric_limits<Pixel>::min());
-      }
-
-      void SampleVoxels(float& f00,
-                        float& f01,
-                        float& f10,
-                        float& f11,
-                        unsigned int imageX,
-                        unsigned int imageY,
-                        unsigned int imageZ) const
-      {
-        f00 = Orthanc::ImageTraits<InputFormat>::GetFloatPixel
-          (GetImage(), imageX, imageY + imageZ * GetImageHeight());
-
-        if (imageX + 1 < GetImageWidth())
-        {
-          f01 = Orthanc::ImageTraits<InputFormat>::GetFloatPixel
-            (GetImage(), imageX + 1, imageY + imageZ * GetImageHeight());
-        }
-        else
-        {
-          f01 = f00;
-        }
-
-        if (imageY + 1 < GetImageWidth())
-        {
-          f10 = Orthanc::ImageTraits<InputFormat>::GetFloatPixel
-            (GetImage(), imageX, imageY + 1 + imageZ * GetImageHeight());
-        }
-        else
-        {
-          f10 = f00;
-        }
-
-        if (imageX + 1 < GetImageWidth() &&
-            imageY + 1 < GetImageHeight())
-        {
-          f11 = Orthanc::ImageTraits<InputFormat>::GetFloatPixel
-            (GetImage(), imageX + 1, imageY + 1 + imageZ * GetImageHeight());
-        }
-        else
-        {
-          f11 = f00;
-        }
-      }
-
-      float GetOutOfVolume() const
-      {
-        return outOfVolume_;
-      }
-      
-      float GetFloatValue(float volumeX,
-                          float volumeY,
-                          float volumeZ) const
-      {
-        unsigned int imageX, imageY, imageZ;
-        float fractionalX, fractionalY, fractionalZ;
-
-        if (GetNearestCoordinates(imageX, imageY, imageZ,
-                                  fractionalX, fractionalY, fractionalZ,
-                                  volumeX, volumeY, volumeZ))
-        {
-          float f00, f01, f10, f11;
-          SampleVoxels(f00, f01, f10, f11, imageX, imageY, imageZ);
-          return GeometryToolbox::ComputeBilinearInterpolationUnitSquare
-            (fractionalX, fractionalY, f00, f01, f10, f11);
-        }
-        else
-        {
-          return outOfVolume_;
-        }
-      }
-    };
-
-
-    template <Orthanc::PixelFormat InputFormat>
-    class VoxelReader<InputFormat, ImageInterpolation_Trilinear> :
-      public VoxelReaderBase
-    {
-    private:
-      typedef VoxelReader<InputFormat, ImageInterpolation_Bilinear>  Bilinear;
-
-      Bilinear      bilinear_;
-      unsigned int  imageDepth_;
-
-    public:
-      VoxelReader(const ImageBuffer3D& image) :
-        VoxelReaderBase(image),
-        bilinear_(image),
-        imageDepth_(image.GetDepth())
-      {
-      }
-
-      float GetFloatValue(float volumeX,
-                          float volumeY,
-                          float volumeZ) const
-      {
-        unsigned int imageX, imageY, imageZ;
-        float fractionalX, fractionalY, fractionalZ;
-
-        if (GetNearestCoordinates(imageX, imageY, imageZ,
-                                  fractionalX, fractionalY, fractionalZ,
-                                  volumeX, volumeY, volumeZ))
-        {
-          float f000, f001, f010, f011;
-          bilinear_.SampleVoxels(f000, f001, f010, f011, imageX, imageY, imageZ);
-
-          if (imageZ + 1 < imageDepth_)
-          {
-            float f100, f101, f110, f111;
-            bilinear_.SampleVoxels(f100, f101, f110, f111, imageX, imageY, imageZ + 1);
-            return GeometryToolbox::ComputeTrilinearInterpolationUnitSquare
-              (fractionalX, fractionalY, fractionalZ,
-               f000, f001, f010, f011, f100, f101, f110, f111);
-          }
-          else
-          {
-            return GeometryToolbox::ComputeBilinearInterpolationUnitSquare
-              (fractionalX, fractionalY, f000, f001, f010, f011);
-          }
-        }
-        else
-        {
-          return bilinear_.GetOutOfVolume();
-        }
-      }
-    };
-
-
     template <typename VoxelReader,
               typename PixelWriter,
               TransferFunction Function>
@@ -424,9 +145,14 @@
                  float volumeY,
                  float volumeZ)
       {
-        typename VoxelReader::InputPixelType image;
-        reader_.GetValue(image, volumeX, volumeY, volumeZ);
-        writer_.SetValue(pixel, image);
+        typename VoxelReader::PixelType value;
+
+        if (!reader_.GetValue(value, volumeX, volumeY, volumeZ))
+        {
+          VoxelReader::Traits::SetMinValue(value);
+        }
+
+        writer_.SetValue(pixel, value);
       }        
     };
 
@@ -438,12 +164,14 @@
     private:
       VoxelReader  reader_;
       PixelWriter  writer_;
+      float        outOfVolume_;
       
     public:
       PixelShader(const ImageBuffer3D& image,
                   float /* scaling */,
                   float /* offset */) :
-        reader_(image)
+        reader_(image),
+        outOfVolume_(static_cast<float>(std::numeric_limits<typename VoxelReader::PixelType>::min()))
       {
       }
       
@@ -453,8 +181,15 @@
                  float volumeY,
                  float volumeZ)
       {
-        writer_.SetFloatValue(pixel, reader_.GetFloatValue(volumeX, volumeY, volumeZ));
-      }        
+        float value;
+
+        if (!reader_.GetFloatValue(value, volumeX, volumeY, volumeZ))
+        {
+          value = outOfVolume_;
+        }
+
+        writer_.SetFloatValue(pixel, value);
+      }
     };
 
     
@@ -467,6 +202,7 @@
       PixelWriter  writer_;
       float        scaling_;
       float        offset_;
+      float        outOfVolume_;
       
     public:
       PixelShader(const ImageBuffer3D& image,
@@ -474,7 +210,8 @@
                   float offset) :
         reader_(image),
         scaling_(scaling),
-        offset_(offset)
+        offset_(offset),
+        outOfVolume_(static_cast<float>(std::numeric_limits<typename VoxelReader::PixelType>::min()))
       {
       }
       
@@ -484,7 +221,18 @@
                  float volumeY,
                  float volumeZ)
       {
-        writer_.SetFloatValue(pixel, scaling_ * reader_.GetFloatValue(volumeX, volumeY, volumeZ) + offset_);
+        float value;
+
+        if (reader_.GetFloatValue(value, volumeX, volumeY, volumeZ))
+        {
+          value = scaling_ * value + offset_;
+        }
+        else
+        {
+          value = outOfVolume_;
+        }
+
+        writer_.SetFloatValue(pixel, value);
       }        
     };
 
@@ -616,13 +364,17 @@
                              float scaling,
                              float offset)
     {
-      typedef VoxelReader<InputFormat, Interpolation>   Reader;
-      typedef PixelWriter<InputFormat, OutputFormat>    Writer;
-      typedef PixelShader<Reader, Writer, Function>     Shader;
+      typedef SubvoxelReader<InputFormat, Interpolation>   Reader;
+      typedef PixelWriter<InputFormat, OutputFormat>       Writer;
+      typedef PixelShader<Reader, Writer, Function>        Shader;
 
       const unsigned int outputWidth = slice.GetWidth();
       const unsigned int outputHeight = slice.GetHeight();
 
+      const float sourceWidth = static_cast<float>(source.GetWidth());
+      const float sourceHeight = static_cast<float>(source.GetHeight());
+      const float sourceDepth = static_cast<float>(source.GetDepth());
+
       Shader shader(source, scaling, offset);
 
       for (unsigned int y = 0; y < outputHeight; y++)
@@ -636,7 +388,11 @@
         {
           float volumeX, volumeY, volumeZ;
           it.GetVolumeCoordinates(volumeX, volumeY, volumeZ);
-          shader.Apply(p, volumeX, volumeY, volumeZ);
+
+          shader.Apply(p, 
+                       volumeX * sourceWidth, 
+                       volumeY * sourceHeight, 
+                       volumeZ * sourceDepth);
           it.Next();
         }
       }