changeset 409:99c9b3238008

AffineTransform2D
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 12 Nov 2018 15:38:11 +0100
parents 6834c236b36d
children 6decc0ba9da5
files Framework/Radiography/RadiographyScene.cpp Framework/Radiography/RadiographyScene.h Framework/Toolbox/AffineTransform2D.cpp Framework/Toolbox/AffineTransform2D.h Resources/CMake/OrthancStoneConfiguration.cmake
diffstat 5 files changed, 305 insertions(+), 106 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Radiography/RadiographyScene.cpp	Mon Nov 12 14:52:10 2018 +0100
+++ b/Framework/Radiography/RadiographyScene.cpp	Mon Nov 12 15:38:11 2018 +0100
@@ -43,73 +43,20 @@
   }
 
 
-  static Matrix CreateOffsetMatrix(double dx,
-                                   double dy)
-  {
-    Matrix m = LinearAlgebra::IdentityMatrix(3);
-    m(0, 2) = dx;
-    m(1, 2) = dy;
-    return m;
-  }
-      
-
-  static Matrix CreateScalingMatrix(double sx,
-                                    double sy)
-  {
-    Matrix m = LinearAlgebra::IdentityMatrix(3);
-    m(0, 0) = sx;
-    m(1, 1) = sy;
-    return m;
-  }
-      
-
-  static Matrix CreateRotationMatrix(double angle)
-  {
-    Matrix m;
-    const double v[] = { cos(angle), -sin(angle), 0,
-                         sin(angle), cos(angle), 0,
-                         0, 0, 1 };
-    LinearAlgebra::FillMatrix(m, 3, 3, v);
-    return m;
-  }
-
-
-  static void ApplyTransform(double& x /* inout */,
-                             double& y /* inout */,
-                             const Matrix& transform)
-  {
-    Vector p;
-    LinearAlgebra::AssignVector(p, x, y, 1);
-
-    Vector q = LinearAlgebra::Product(transform, p);
-
-    if (!LinearAlgebra::IsNear(q[2], 1.0))
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-    else
-    {
-      x = q[0];
-      y = q[1];
-    }
-  }
-      
-      
-
   void RadiographyScene::Layer::UpdateTransform()
   {
-    transform_ = CreateScalingMatrix(pixelSpacingX_, pixelSpacingY_);
+    transform_ = AffineTransform2D::CreateScaling(pixelSpacingX_, pixelSpacingY_);
 
     double centerX, centerY;
     GetCenter(centerX, centerY);
 
-    transform_ = LinearAlgebra::Product(
-      CreateOffsetMatrix(panX_ + centerX, panY_ + centerY),
-      CreateRotationMatrix(angle_),
-      CreateOffsetMatrix(-centerX, -centerY),
+    transform_ = AffineTransform2D::Combine(
+      AffineTransform2D::CreateOffset(panX_ + centerX, panY_ + centerY),
+      AffineTransform2D::CreateRotation(angle_),
+      AffineTransform2D::CreateOffset(-centerX, -centerY),
       transform_);
 
-    LinearAlgebra::InvertMatrix(transformInverse_, transform_);
+    transformInverse_ = AffineTransform2D::Invert(transform_);
   }
 
 
@@ -117,7 +64,7 @@
                                             double x,
                                             double y) const
   {
-    ApplyTransform(x, y, transform_);
+    transform_.Apply(x, y);
     extent.AddPoint(x, y);
   }
 
@@ -161,14 +108,14 @@
         throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
 
-    ApplyTransform(x, y, transform_);
+    transform_.Apply(x, y);
   }
 
 
   bool RadiographyScene::Layer::Contains(double x,
                                          double y) const
   {
-    ApplyTransform(x, y, transformInverse_);
+    transformInverse_.Apply(x, y);
         
     unsigned int cropX, cropY, cropWidth, cropHeight;
     GetCrop(cropX, cropY, cropWidth, cropHeight);
@@ -195,27 +142,27 @@
     double x, y;
     x = dx;
     y = dy;
-    ApplyTransform(x, y, transform_);
+    transform_.Apply(x, y);
     cairo_move_to(cr, x, y);
 
     x = dx + dwidth;
     y = dy;
-    ApplyTransform(x, y, transform_);
+    transform_.Apply(x, y);
     cairo_line_to(cr, x, y);
 
     x = dx + dwidth;
     y = dy + dheight;
-    ApplyTransform(x, y, transform_);
+    transform_.Apply(x, y);
     cairo_line_to(cr, x, y);
 
     x = dx;
     y = dy + dheight;
-    ApplyTransform(x, y, transform_);
+    transform_.Apply(x, y);
     cairo_line_to(cr, x, y);
 
     x = dx;
     y = dy;
-    ApplyTransform(x, y, transform_);
+    transform_.Apply(x, y);
     cairo_line_to(cr, x, y);
 
     cairo_stroke(cr);
@@ -345,7 +292,7 @@
     }
     else
     {
-      ApplyTransform(sceneX, sceneY, transformInverse_);
+      transformInverse_.Apply(sceneX, sceneY);
         
       int x = static_cast<int>(std::floor(sceneX));
       int y = static_cast<int>(std::floor(sceneY));
@@ -404,7 +351,7 @@
   {
     centerX = static_cast<double>(width_) / 2.0;
     centerY = static_cast<double>(height_) / 2.0;
-    ApplyTransform(centerX, centerY, transform_);
+    transform_.Apply(centerX, centerY);
   }
 
 
@@ -598,7 +545,7 @@
       
 
     virtual void Render(Orthanc::ImageAccessor& buffer,
-                        const Matrix& viewTransform,
+                        const AffineTransform2D& viewTransform,
                         ImageInterpolation interpolation) const
     {
       if (alpha_.get() == NULL)
@@ -614,15 +561,16 @@
       unsigned int cropX, cropY, cropWidth, cropHeight;
       GetCrop(cropX, cropY, cropWidth, cropHeight);
 
-      Matrix m = LinearAlgebra::Product(viewTransform,
-                                        GetTransform(),
-                                        CreateOffsetMatrix(cropX, cropY));
+      const AffineTransform2D t = AffineTransform2D::Combine(
+        viewTransform, GetTransform(),
+        AffineTransform2D::CreateOffset(cropX, cropY));
 
       Orthanc::ImageAccessor cropped;
       alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
         
       Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false);
-      ApplyProjectiveTransform(tmp, cropped, m, interpolation, true /* clear */);
+      
+      t.Apply(tmp, cropped, interpolation, true /* clear */);
 
       // Blit
       const unsigned int width = buffer.GetWidth();
@@ -756,7 +704,7 @@
 
       
     virtual void Render(Orthanc::ImageAccessor& buffer,
-                        const Matrix& viewTransform,
+                        const AffineTransform2D& viewTransform,
                         ImageInterpolation interpolation) const
     {
       if (converted_.get() != NULL)
@@ -769,14 +717,14 @@
         unsigned int cropX, cropY, cropWidth, cropHeight;
         GetCrop(cropX, cropY, cropWidth, cropHeight);
 
-        Matrix m = LinearAlgebra::Product(viewTransform,
-                                          GetTransform(),
-                                          CreateOffsetMatrix(cropX, cropY));
+        AffineTransform2D t = AffineTransform2D::Combine(
+          viewTransform, GetTransform(),
+          AffineTransform2D::CreateOffset(cropX, cropY));
 
         Orthanc::ImageAccessor cropped;
         converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
         
-        ApplyProjectiveTransform(buffer, cropped, m, interpolation, false);
+        t.Apply(buffer, cropped, interpolation, false);
       }
     }
 
@@ -1052,7 +1000,7 @@
     
 
   void RadiographyScene::Render(Orthanc::ImageAccessor& buffer,
-                                const Matrix& viewTransform,
+                                const AffineTransform2D& viewTransform,
                                 ImageInterpolation interpolation) const
   {
     Orthanc::ImageProcessing::Set(buffer, 0);
@@ -1173,9 +1121,9 @@
                           static_cast<unsigned int>(w),
                           static_cast<unsigned int>(h), false);
 
-    Matrix view = LinearAlgebra::Product(
-      CreateScalingMatrix(1.0 / pixelSpacingX, 1.0 / pixelSpacingY),
-      CreateOffsetMatrix(-extent.GetX1(), -extent.GetY1()));
+    AffineTransform2D view = AffineTransform2D::Combine(
+      AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY),
+      AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1()));
       
     Render(layers, view, interpolation);
 
--- a/Framework/Radiography/RadiographyScene.h	Mon Nov 12 14:52:10 2018 +0100
+++ b/Framework/Radiography/RadiographyScene.h	Mon Nov 12 15:38:11 2018 +0100
@@ -21,8 +21,8 @@
 
 #pragma once
 
+#include "../Toolbox/AffineTransform2D.h"
 #include "../Toolbox/Extent2D.h"
-#include "../Toolbox/LinearAlgebra.h"
 #include "../Toolbox/OrthancApiClient.h"
 #include "../Viewport/CairoContext.h"
 
@@ -45,32 +45,33 @@
       Corner_BottomRight
     };
 
+
     class Layer : public boost::noncopyable
     {
       friend class RadiographyScene;
       
     private:
-      size_t        index_;
-      bool          hasSize_;
-      unsigned int  width_;
-      unsigned int  height_;
-      bool          hasCrop_;
-      unsigned int  cropX_;
-      unsigned int  cropY_;
-      unsigned int  cropWidth_;
-      unsigned int  cropHeight_;
-      Matrix        transform_;
-      Matrix        transformInverse_;
-      double        pixelSpacingX_;
-      double        pixelSpacingY_;
-      double        panX_;
-      double        panY_;
-      double        angle_;
-      bool          resizeable_;
+      size_t             index_;
+      bool               hasSize_;
+      unsigned int       width_;
+      unsigned int       height_;
+      bool               hasCrop_;
+      unsigned int       cropX_;
+      unsigned int       cropY_;
+      unsigned int       cropWidth_;
+      unsigned int       cropHeight_;
+      AffineTransform2D  transform_;
+      AffineTransform2D  transformInverse_;
+      double             pixelSpacingX_;
+      double             pixelSpacingY_;
+      double             panX_;
+      double             panY_;
+      double             angle_;
+      bool               resizeable_;
 
 
     protected:
-      const Matrix& GetTransform() const
+      const AffineTransform2D& GetTransform() const
       {
         return transform_;
       }
@@ -209,7 +210,7 @@
                                        float& width) const = 0;
 
       virtual void Render(Orthanc::ImageAccessor& buffer,
-                          const Matrix& viewTransform,
+                          const AffineTransform2D& viewTransform,
                           ImageInterpolation interpolation) const = 0;
 
       virtual bool GetRange(float& minValue,
@@ -299,7 +300,7 @@
     Extent2D GetSceneExtent() const;
 
     void Render(Orthanc::ImageAccessor& buffer,
-                const Matrix& viewTransform,
+                const AffineTransform2D& viewTransform,
                 ImageInterpolation interpolation) const;
 
     bool LookupLayer(size_t& index /* out */,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Toolbox/AffineTransform2D.cpp	Mon Nov 12 15:38:11 2018 +0100
@@ -0,0 +1,166 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "AffineTransform2D.h"
+
+#include "ImageGeometry.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+namespace OrthancStone
+{
+  AffineTransform2D::AffineTransform2D() :
+    matrix_(LinearAlgebra::IdentityMatrix(3))
+  {
+  }
+
+  
+  AffineTransform2D::AffineTransform2D(const Matrix& m)
+  {
+    if (m.size1() != 3 ||
+        m.size2() != 3)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
+    }
+
+    if (!LinearAlgebra::IsCloseToZero(m(2, 0)) ||
+        !LinearAlgebra::IsCloseToZero(m(2, 1)) ||
+        LinearAlgebra::IsCloseToZero(m(2, 2)))
+    {
+      LOG(ERROR) << "Cannot setup an AffineTransform2D with perspective effects";
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    matrix_ = m / m(2, 2);
+  }
+    
+
+  void AffineTransform2D::Apply(double& x /* inout */,
+                                double& y /* inout */) const
+  {
+    Vector p;
+    LinearAlgebra::AssignVector(p, x, y, 1);
+
+    Vector q = LinearAlgebra::Product(matrix_, p);
+
+    if (!LinearAlgebra::IsNear(q[2], 1.0))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+    else
+    {
+      x = q[0];
+      y = q[1];
+    }
+  }
+
+
+  void AffineTransform2D::Apply(Orthanc::ImageAccessor& target,
+                                const Orthanc::ImageAccessor& source,
+                                ImageInterpolation interpolation,
+                                bool clear) const
+  {
+    assert(LinearAlgebra::IsNear(matrix_(2, 0), 0) &&
+           LinearAlgebra::IsNear(matrix_(2, 1), 0) &&
+           LinearAlgebra::IsNear(matrix_(2, 2), 1));
+
+    ApplyAffineTransform(target, source,
+                         matrix_(0, 0), matrix_(0, 1), matrix_(0, 2),
+                         matrix_(1, 0), matrix_(1, 1), matrix_(1, 2),
+                         interpolation, clear);
+  }
+
+
+  AffineTransform2D AffineTransform2D::Invert(const AffineTransform2D& a)
+  {
+    AffineTransform2D t;
+    LinearAlgebra::InvertMatrix(t.matrix_, a.matrix_);
+    return t;
+  }
+
+
+  AffineTransform2D AffineTransform2D::Combine(const AffineTransform2D& a,
+                                               const AffineTransform2D& b)
+  {
+    return AffineTransform2D(LinearAlgebra::Product(a.GetHomogeneousMatrix(),
+                                                    b.GetHomogeneousMatrix()));
+  }
+  
+      
+  AffineTransform2D AffineTransform2D::Combine(const AffineTransform2D& a,
+                                               const AffineTransform2D& b,
+                                               const AffineTransform2D& c)
+  {
+    return AffineTransform2D(LinearAlgebra::Product(a.GetHomogeneousMatrix(),
+                                                    b.GetHomogeneousMatrix(),
+                                                    c.GetHomogeneousMatrix()));
+  }
+  
+      
+  AffineTransform2D AffineTransform2D::Combine(const AffineTransform2D& a,
+                                               const AffineTransform2D& b,
+                                               const AffineTransform2D& c,
+                                               const AffineTransform2D& d)
+  {
+    return AffineTransform2D(LinearAlgebra::Product(a.GetHomogeneousMatrix(),
+                                                    b.GetHomogeneousMatrix(),
+                                                    c.GetHomogeneousMatrix(),
+                                                    d.GetHomogeneousMatrix()));
+  }
+  
+
+  AffineTransform2D AffineTransform2D::CreateOffset(double dx,
+                                                    double dy)
+  {
+    AffineTransform2D t;
+    t.matrix_(0, 2) = dx;
+    t.matrix_(1, 2) = dy;
+      
+    return t;
+  }
+  
+
+  AffineTransform2D AffineTransform2D::CreateScaling(double sx,
+                                                     double sy)
+  {
+    AffineTransform2D t;
+    t.matrix_(0, 0) = sx;
+    t.matrix_(1, 1) = sy;
+      
+    return t;
+  }
+  
+
+  AffineTransform2D AffineTransform2D::CreateRotation(double angle)
+  {
+    double cosine = cos(angle);
+    double sine = sin(angle);
+      
+    AffineTransform2D t;
+    t.matrix_(0, 0) = cosine;
+    t.matrix_(0, 1) = -sine;
+    t.matrix_(1, 0) = sine;
+    t.matrix_(1, 1) = cosine;
+
+    return t;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Toolbox/AffineTransform2D.h	Mon Nov 12 15:38:11 2018 +0100
@@ -0,0 +1,82 @@
+/**
+ * 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 "../StoneEnumerations.h"
+#include "LinearAlgebra.h"
+
+#include <Core/Images/ImageAccessor.h>
+
+namespace OrthancStone
+{
+  class AffineTransform2D
+  {
+  private:
+    Matrix  matrix_;
+
+  public:
+    AffineTransform2D();
+
+    // The matrix must be 3x3, without perspective effects
+    AffineTransform2D(const Matrix& m);
+
+    AffineTransform2D(const AffineTransform2D& other) :
+      matrix_(other.matrix_)
+    {
+    }
+    
+    const Matrix& GetHomogeneousMatrix() const
+    {
+      return matrix_;
+    }
+
+    void Apply(double& x /* inout */,
+               double& y /* inout */) const;
+
+    void Apply(Orthanc::ImageAccessor& target,
+               const Orthanc::ImageAccessor& source,
+               ImageInterpolation interpolation,
+               bool clear) const;
+    
+    static AffineTransform2D Invert(const AffineTransform2D& a);
+
+    static AffineTransform2D Combine(const AffineTransform2D& a,
+                                     const AffineTransform2D& b);
+      
+    static AffineTransform2D Combine(const AffineTransform2D& a,
+                                     const AffineTransform2D& b,
+                                     const AffineTransform2D& c);
+      
+    static AffineTransform2D Combine(const AffineTransform2D& a,
+                                     const AffineTransform2D& b,
+                                     const AffineTransform2D& c,
+                                     const AffineTransform2D& d);
+
+    static AffineTransform2D CreateOffset(double dx,
+                                          double dy);
+
+    static AffineTransform2D CreateScaling(double sx,
+                                           double sy);
+    
+    static AffineTransform2D CreateRotation(double angle);
+  };
+}
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Mon Nov 12 14:52:10 2018 +0100
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Mon Nov 12 15:38:11 2018 +0100
@@ -251,8 +251,8 @@
   ${ORTHANC_STONE_ROOT}/Framework/SmartLoader.cpp
   ${ORTHANC_STONE_ROOT}/Framework/StoneEnumerations.cpp
   ${ORTHANC_STONE_ROOT}/Framework/StoneException.h
-  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/UndoRedoStack.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/UndoRedoStack.h
+  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/AffineTransform2D.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/AffineTransform2D.h
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/CoordinateSystem3D.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DicomFrameConverter.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DicomStructureSet.cpp
@@ -273,6 +273,8 @@
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/ShearWarpProjectiveTransform.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/Slice.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/SlicesSorter.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/UndoRedoStack.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/UndoRedoStack.h
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/ViewportGeometry.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Viewport/CairoContext.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Viewport/CairoSurface.cpp