Isis 3 Programmer Reference
Affine.cpp
1 
6 /* SPDX-License-Identifier: CC0-1.0 */
7 
8 #include "Affine.h"
9 
10 #include <vector>
11 #include <iostream>
12 #include <sstream>
13 #include <string>
14 
15 #include <jama/jama_svd.h>
16 
17 #include "Constants.h"
18 #include "IException.h"
19 #include "IString.h"
20 #include "LeastSquares.h"
21 #include "PolynomialBivariate.h"
22 
23 using namespace std;
24 
25 namespace Isis {
30  Affine::Affine() {
31  Identity();
32  p_x = p_y = p_xp = p_yp = 0.0;
33  }
34 
46  Affine::Affine(const Affine::AMatrix &a) {
47  checkDims(a);
48  p_matrix = a.copy();
49  p_invmat = invert(p_matrix);
50  p_x = p_y = p_xp = p_yp = 0.0;
51  }
52 
54  Affine::~Affine() {}
55 
61  Affine::AMatrix Affine::getIdentity() {
62  AMatrix ident(3, 3, 0.0);
63  for(int i = 0 ; i < ident.dim2() ; i++) {
64  ident[i][i] = 1.0;
65  }
66  return (ident);
67  }
68 
73  void Affine::Identity() {
74  p_matrix = getIdentity();
75  p_invmat = getIdentity();
76  }
77 
92  void Affine::Solve(const double x[], const double y[],
93  const double xp[], const double yp[], int n) {
94  // We must solve two least squares equations
95  PolynomialBivariate xpFunc(1);
96  PolynomialBivariate ypFunc(1);
97  LeastSquares xpLSQ(xpFunc);
98  LeastSquares ypLSQ(ypFunc);
99 
100  // Push the knowns into the least squares class
101  for(int i = 0; i < n; i++) {
102  vector<double> coord(2);
103  coord[0] = x[i];
104  coord[1] = y[i];
105  xpLSQ.AddKnown(coord, xp[i]);
106  ypLSQ.AddKnown(coord, yp[i]);
107  }
108 
109  // Solve for A,B,C,D,E,F
110  xpLSQ.Solve();
111  ypLSQ.Solve();
112 
113  // Construct our affine matrix
114  p_matrix[0][0] = xpFunc.Coefficient(1); // A
115  p_matrix[0][1] = xpFunc.Coefficient(2); // B
116  p_matrix[0][2] = xpFunc.Coefficient(0); // C
117  p_matrix[1][0] = ypFunc.Coefficient(1); // D
118  p_matrix[1][1] = ypFunc.Coefficient(2); // E
119  p_matrix[1][2] = ypFunc.Coefficient(0); // F
120  p_matrix[2][0] = 0.0;
121  p_matrix[2][1] = 0.0;
122  p_matrix[2][2] = 1.0;
123 
124  // invert the matrix
125  p_invmat = invert(p_matrix);
126  }
127 
134  void Affine::Translate(double tx, double ty) {
135  AMatrix trans = getIdentity();
136 
137  trans[0][2] = tx;
138  trans[1][2] = ty;
139  p_matrix = TNT::matmult(trans, p_matrix);
140 
141  trans[0][2] = -tx;
142  trans[1][2] = -ty;
143  p_invmat = TNT::matmult(p_invmat, trans);
144  }
145 
151  void Affine::Rotate(double angle) {
152  AMatrix rot = getIdentity();
153 
154  double angleRadians = angle * Isis::PI / 180.0;
155  rot[0][0] = cos(angleRadians);
156  rot[0][1] = -sin(angleRadians);
157  rot[1][0] = sin(angleRadians);
158  rot[1][1] = cos(angleRadians);
159  p_matrix = TNT::matmult(rot, p_matrix);
160 
161  angleRadians = -angleRadians;
162  rot[0][0] = cos(angleRadians);
163  rot[0][1] = -sin(angleRadians);
164  rot[1][0] = sin(angleRadians);
165  rot[1][1] = cos(angleRadians);
166  p_invmat = TNT::matmult(p_invmat, rot);
167  }
168 
174  void Affine::Scale(double scaleFactor) {
175  AMatrix scale = getIdentity();
176  scale[0][0] = scaleFactor;
177  scale[1][1] = scaleFactor;
178  p_matrix = TNT::matmult(scale, p_matrix);
179 
180  // Invert for inverse translation
181  p_invmat = invert(p_matrix);
182  }
183 
191  void Affine::Compute(double x, double y) {
192  p_x = x;
193  p_y = y;
194  p_xp = p_matrix[0][0] * x + p_matrix[0][1] * y + p_matrix[0][2];
195  p_yp = p_matrix[1][0] * x + p_matrix[1][1] * y + p_matrix[1][2];
196  }
197 
205  void Affine::ComputeInverse(double xp, double yp) {
206  p_xp = xp;
207  p_yp = yp;
208  p_x = p_invmat[0][0] * xp + p_invmat[0][1] * yp + p_invmat[0][2];
209  p_y = p_invmat[1][0] * xp + p_invmat[1][1] * yp + p_invmat[1][2];
210  }
211 
220  vector<double> Affine::Coefficients(int var) {
221  int index = var - 1;
222  vector <double> coef;
223  coef.push_back(p_matrix[index][0]);
224  coef.push_back(p_matrix[index][1]);
225  coef.push_back(p_matrix[index][2]);
226  return coef;
227  }
228 
237  vector<double> Affine::InverseCoefficients(int var) {
238  int index = var - 1;
239  vector <double> coef;
240  coef.push_back(p_invmat[index][0]);
241  coef.push_back(p_invmat[index][1]);
242  coef.push_back(p_invmat[index][2]);
243  return coef;
244  }
245 
251  void Affine::checkDims(const AMatrix &am) const {
252  if((am.dim1() != 3) && (am.dim2() != 3)) {
253  ostringstream mess;
254  mess << "Affine matrices must be 3x3 - this one is " << am.dim1()
255  << "x" << am.dim2();
256  throw IException(IException::Programmer, mess.str(), _FILEINFO_);
257  }
258  return;
259  }
260 
271  Affine::AMatrix Affine::invert(const AMatrix &a) const {
272  // Now compute the inverse affine matrix using singular value
273  // decomposition A = USV'. So invA = V invS U'. Where ' represents
274  // the transpose of a matrix and invS is S with the recipricol of the
275  // diagonal elements
276  JAMA::SVD<double> svd(a);
277 
278  AMatrix V;
279  svd.getV(V);
280 
281  // The inverse of S is 1 over each diagonal element of S
282  AMatrix invS;
283  svd.getS(invS);
284  for(int i = 0; i < invS.dim1(); i++) {
285  if(invS[i][i] == 0.0) {
286  string msg = "Affine transform not invertible";
287  throw IException(IException::Unknown, msg, _FILEINFO_);
288  }
289  invS[i][i] = 1.0 / invS[i][i];
290  }
291 
292  // Transpose U
293  AMatrix U;
294  svd.getU(U);
295  AMatrix transU(U.dim2(), U.dim1());
296  for(int r = 0; r < U.dim1(); r++) {
297  for(int c = 0; c < U.dim2(); c++) {
298  transU[c][r] = U[r][c];
299  }
300  }
301 
302  // Multiply stuff together to get the inverse of the affine
303  AMatrix VinvS = TNT::matmult(V, invS);
304  return (TNT::matmult(VinvS, transU));
305  }
306 
307 } // end namespace isis
Isis::LeastSquares::Solve
int Solve(Isis::LeastSquares::SolveMethod method=SVD)
After all the data has been registered through AddKnown, invoke this method to solve the system of eq...
Definition: LeastSquares.cpp:205
Isis::Affine::AMatrix
TNT::Array2D< double > AMatrix
Affine Matrix.
Definition: Affine.h:67
Isis::PI
const double PI
The mathematical constant PI.
Definition: Constants.h:40
Isis::PolynomialBivariate
Nth degree Polynomial with two variables.
Definition: PolynomialBivariate.h:37
Isis::LeastSquares
Generic least square fitting class.
Definition: LeastSquares.h:99
Isis::IException
Isis exception class.
Definition: IException.h:91
std
Namespace for the standard library.
Isis::LeastSquares::AddKnown
void AddKnown(const std::vector< double > &input, double expected, double weight=1.0)
Invoke this method for each set of knowns.
Definition: LeastSquares.cpp:96
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16
Isis::BasisFunction::Coefficient
double Coefficient(int i) const
Returns the ith coefficient.
Definition: BasisFunction.h:107