File indexing completed on 2025-03-09 03:55:00

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 16/08/2016
0007  * Description : point transform class and its utilities that models
0008  *               affine transformations between two sets of 2d-points.
0009  *
0010  * SPDX-FileCopyrightText: 2016      by Omar Amin <Omar dot moh dot amin at gmail dot com>
0011  * SPDX-FileCopyrightText: 2019      by Thanh Trung Dinh <dinhthanhtrung1996 at gmail dot com>
0012  * SPDX-FileCopyrightText: 2016-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0013  *
0014  * SPDX-License-Identifier: GPL-2.0-or-later
0015  *
0016  * ============================================================ */
0017 
0018 #include "pointtransformaffine.h"
0019 
0020 namespace Digikam
0021 {
0022 
0023 PointTransformAffine::PointTransformAffine()
0024     : m(std::vector<std::vector<float> >(2, std::vector<float>(2, 0))),
0025       b(std::vector<float>(2, 0))
0026 {
0027     m[0][0] = 1.0;
0028     m[1][1] = 1.0;
0029 }
0030 
0031 PointTransformAffine::PointTransformAffine(const std::vector<std::vector<float> >& m_,
0032                                            const std::vector<float>& b_)
0033     : m(m_),
0034       b(b_)
0035 {
0036 }
0037 
0038 PointTransformAffine::PointTransformAffine(const std::vector<std::vector<float> >& m_)
0039     : m(std::vector<std::vector<float> >(2, std::vector<float>(2, 0))),
0040       b(std::vector<float >(2, 0))
0041 {
0042 
0043     for (unsigned int i = 0 ; i < m_.size() ; ++i)
0044     {
0045         for (unsigned int j = 0 ; j < m_[0].size() ; ++j)
0046         {
0047             if (j == 2)
0048             {
0049                 b[i]    = m_[i][2];
0050             }
0051             else
0052             {
0053                 m[i][j] = m_[i][j];
0054             }
0055         }
0056     }
0057 }
0058 
0059 const std::vector<float> PointTransformAffine::operator() (const std::vector<float>& p) const
0060 {
0061     return (m*p + b);
0062 }
0063 
0064 
0065 const std::vector<std::vector<float> >& PointTransformAffine::get_m() const
0066 {
0067     return m;
0068 }
0069 
0070 const std::vector<float>& PointTransformAffine::get_b() const
0071 {
0072     return b;
0073 }
0074 
0075 // ----------------------------------------------------------------------------------------
0076 
0077 PointTransformAffine operator* (const PointTransformAffine& lhs,
0078                                 const PointTransformAffine& rhs)
0079 {
0080     return (PointTransformAffine(lhs.get_m() * rhs.get_m(),
0081                                  lhs.get_m() * rhs.get_b() + lhs.get_b()));
0082 }
0083 
0084 // ----------------------------------------------------------------------------------------
0085 
0086 PointTransformAffine inv(const PointTransformAffine& trans)
0087 {
0088     std::vector<std::vector<float> > im = MatrixOperations::inv2(trans.get_m());
0089 
0090     return (PointTransformAffine(im, -(im * trans.get_b())));
0091 }
0092 
0093 // ----------------------------------------------------------------------------------------
0094 
0095 PointTransformAffine findSimilarityTransform(const std::vector<std::vector<float> >& fromPoints,
0096                                              const std::vector<std::vector<float> >& toPoints)
0097 {
0098     // We use the formulas from the paper: Least-squares estimation of transformation
0099     // parameters between two point patterns by Umeyama. They are equations 34 through 43.
0100 
0101     std::vector<float> meanFrom(2, 0);
0102     std::vector<float> meanTo(2, 0);
0103     float sigmaFrom = 0.0F;
0104     float sigmaTo   = 0.0F;
0105     std::vector<std::vector<float> > cov(2, std::vector<float>(2, 0));
0106 
0107     for (unsigned long i = 0 ; i < fromPoints.size() ; ++i)
0108     {
0109         meanFrom = meanFrom + fromPoints[i];
0110         meanTo   = meanTo   + toPoints[i];
0111     }
0112 
0113     meanFrom = meanFrom / (int)fromPoints.size();
0114     meanTo   = meanTo   / (int)fromPoints.size();
0115 
0116     for (unsigned long i = 0 ; i < fromPoints.size() ; ++i)
0117     {
0118         sigmaFrom = sigmaFrom + length_squared(fromPoints[i] - meanFrom);
0119         sigmaTo   = sigmaTo   + length_squared(toPoints[i]   - meanTo);
0120         cov       = cov + (toPoints[i] - meanTo)*(fromPoints[i] - meanFrom);
0121     }
0122 
0123     sigmaFrom = sigmaFrom / (int)fromPoints.size();
0124     sigmaTo   = sigmaTo   / (int)fromPoints.size();
0125     cov       = cov       / (int)fromPoints.size();
0126     (void)sigmaTo; // to silent clang scan-build
0127 
0128     std::vector<std::vector<float> >  u(2,std::vector<float>(2));
0129     std::vector<std::vector<float> >  v(2,std::vector<float>(2));
0130     std::vector<std::vector<float> > vt(2,std::vector<float>(2));
0131     std::vector<std::vector<float> >  d(2,std::vector<float>(2));
0132     std::vector<std::vector<float> >  s(2,std::vector<float>(2,0));
0133 
0134     MatrixOperations::svd(cov, u, d, vt);
0135     s[0][0] = 1;
0136     s[1][1] = 1;
0137 
0138     if ((MatrixOperations::determinant(cov) < 0) ||
0139         ((MatrixOperations::determinant(cov) == 0) &&
0140          ((MatrixOperations::determinant(u) * MatrixOperations::determinant(v)) < 0)))
0141     {
0142         if (d[1][1] < d[0][0])
0143         {
0144             s[1][1] = -1;
0145         }
0146         else
0147         {
0148             s[0][0] = -1;
0149         }
0150     }
0151 
0152     MatrixOperations::transpose(vt, v);
0153     std::vector<std::vector<float> > r = u * s * v;
0154     float c                            = 1.0F;
0155 
0156     if (sigmaFrom != 0)
0157     {
0158         c = 1.0F / sigmaFrom * MatrixOperations::trace(d * s);
0159     }
0160 
0161     std::vector<float> t = meanTo - r * meanFrom * c;
0162 
0163     return (PointTransformAffine(r * c, t));
0164 }
0165 
0166 } // namespace Digikam