BaxterInterface/ASIFT_tests/demo_ASIFT_src/libNumerics/computeH.cpp
2018-07-23 17:30:57 +02:00

651 lines
17 KiB
C++
Executable file

// Authors: Unknown. Please, if you are the author of this file, or if you
// know who are the authors of this file, let us know, so we can give the
// adequate credits and/or get the adequate authorizations.
#include "homography.h"
#include "numerics.h"
#include <algorithm>
#include <math.h> /* For sqrt */
#include <string.h>
static const float minEigenValue = 1e-3f; // For regular matrix
namespace libNumerics {
/// Constructor. Field `b' used only for error computation.
ComputeH::ComputeH(Type type)
: _type(type), n( size(type) ), b(0)
{
clear();
}
// Destructor
ComputeH::~ComputeH()
{}
// Dimension of matrix w.r.t. type
int ComputeH::size(Type type)
{
switch(type) {
case Translation:
return 2;
case Zoom:
return 3;
case Rotation: // In fact 3, but nonlinear system
case GeneralZoom:
case Similarity:
return 4;
case Affine:
return 6;
case Projective:
return 8;
}
return 8;
}
// Return less general motion
ComputeH::Type ComputeH::restrict(Type t)
{
switch(t) {
case Translation:
return Translation; // Should return identity
case Rotation:
case Zoom:
return Translation;
case Similarity:
return Zoom; // Rotation also correct. Arbitrary choice.
case GeneralZoom:
return Zoom;
case Affine:
return Similarity;
case Projective:
return Affine;
}
return Affine;
}
// Reinitialize
void ComputeH::clear()
{
memset(Ann, 0, n*n*sizeof(double));
memset(Bn, 0, n*sizeof(double));
b = 0;
}
// Add two corresponding points
void ComputeH::add(float x, float y, float X, float Y, float w)
{
if(_type <= Similarity) { // Separate for readability
add_4parameters(x, y, X, Y, w);
return;
}
double x2 = x*x, y2 = y*y, xy = x*y;
double xX = x*X, yX = y*X, xY = x*Y, yY = y*Y;
double *A = Ann, *B = Bn;
*A++ += w* x2; // Equation 1
*A++ += w* xy;
A += 2;
*A++ += w* x;
A++;
if(_type == Projective) {
*A++ -= w* x*xX;
*A++ -= w* x*yX;
}
*B++ += w* x*X;
A++; // Equation 2
*A++ += w* y2;
A += 2;
*A++ += w* y;
A++;
if(_type == Projective) {
*A++ -= w* y*xX;
*A++ -= w* y*yX;
}
*B++ += w* y*X;
A +=2; // Equation 3
*A++ += w* x2;
*A++ += w* xy;
A++;
*A++ += w* x;
if(_type == Projective) {
*A++ -= w* x*xY;
*A++ -= w* x*yY;
}
*B++ += w* x*Y;
A +=3; // Equation 4
*A++ += w* y2;
A++;
*A++ += w* y;
if(_type == Projective) {
*A++ -= w* y*xY;
*A++ -= w* y*yY;
}
*B++ += w* y*Y;
A+= 4; // Equation 5
*A++ += w;
A++;
if(_type == Projective) {
*A++ -= w* xX;
*A++ -= w* yX;
}
*B++ += w* X;
A += 5; // Equation 6
*A++ += w;
*B++ += w* Y;
if(_type == Projective) {
*A++ -= w* xY;
*A++ -= w* yY;
A += 6; // Equation 7
*A++ += w* (xX*xX + xY*xY);
*A++ += w* (xX*yX + xY*yY);
*B++ -= w* (xX*X + xY*Y);
A+= 7; // Equation 8
*A++ += w* (yX*yX + yY*yY);
*B++ -= w* (yX*X + yY*Y);
}
b += w* (X*X + Y*Y);
}
// Add two corresponding points, type involving at most 4 parameters
void ComputeH::add_4parameters(float x, float y, float X, float Y, float w)
{
double *A = Ann, *B = Bn;
if(_type == Translation) {
A[0] += w;
A[3] += w;
B[0] += w* (X - x);
B[1] += w* (Y - y);
b += w* ((X-x)*(X-x) + (Y-y)*(Y-y));
return;
}
b += w* (X*X + Y*Y);
if(_type == GeneralZoom) {
A[0] += w* x*x;
A[2] += w* x;
B[0] += w* x*X;
A[5] += w* y*y;
A[7] += w* y;
B[1] += w* y*Y;
A[10]+= w;
B[2] += w* X;
A[15]+= w;
B[3] += w* Y;
return;
}
*A++ += w* (x*x + y*y); // Equation 1
if(_type != Zoom) // Similarity or Rotation
A++;
*A++ += w* x;
*A++ += w* y;
*B++ += w* (x*X + y*Y);
if(_type != Zoom) { // Similarity or Rotation
A++; // Equation 2
*A++ += w* (x*x + y*y);
*A++ += w* y;
*A++ -= w* x;
*B++ += w* (y*X - x*Y);
A++; // Prepare for next line
}
A++; // Equation 3
*A++ += w;
A++;
*B++ += w* X;
A += n-1; // Equation 4
*A++ += w;
*B++ += w* Y;
}
// Add corresponding lines of equation ux + by + x = 0
void ComputeH::add(float x, float y, float z, float X, float Y, float Z,
float w)
{
float s = 1.0f / (float)sqrt(x*x + y*y);
x *= s;
y *= s;
z *= s;
s = 1.0f / (float)sqrt(X*X + Y*Y);
X *= s;
Y *= s;
Z *= s;
if(_type <= Similarity) { // Separate for readability
add_4parameters(x, y, z, X, Y, Z, w);
return;
}
double x2 = x*x, y2 = y*y, z2 = z*z, xy = x*y, xz = x*z, yz = y*z;
double X2 = X*X, Y2 = Y*Y, Z2 = Z*Z, XY = X*Y, XZ = X*Z, YZ = Y*Z;
double *A = Ann, *B = Bn;
*A++ += w* (y2+z2) * X2; // Equation 1
*A++ -= w* xy * X2;
*A++ += w* (y2+z2) * XY;
*A++ -= w* xy * XY;
*A++ -= w* xz * X2;
*A++ -= w* xz * XY;
if(_type == Projective) {
*A++ += w* (y2+z2) * XZ;
*A++ -= w* xy * XZ;
}
*B++ += w* xz * XZ;
A++; // Equation 2
*A++ += w* (x2+z2) * X2;
*A++ -= w* xy * XY;
*A++ += w* (x2+z2) * XY;
*A++ -= w* yz * X2;
*A++ -= w* yz * XY;
if(_type == Projective) {
*A++ -= w* xy * XZ;
*A++ += w* (x2+z2) * XZ;
}
*B++ -= w* yz * XZ;
A += 2; // Equation 3
*A++ += w* (y2+z2) * Y2;
*A++ -= w* xy * Y2;
*A++ -= w* xz * XY;
*A++ -= w* xz * Y2;
if(_type == Projective) {
*A++ += w* (y2+z2) * YZ;
*A++ -= w* xy * YZ;
}
*B++ += w* xz * YZ;
A += 3; // Equation 4
*A++ += w* (x2+z2) * Y2;
*A++ -= w* yz * XY;
*A++ -= w* yz * Y2;
if(_type == Projective) {
*A++ -= w* xy * YZ;
*A++ += w* (x2+z2) * YZ;
}
*B++ += w* yz * YZ;
A += 4; // Equation 5
*A++ += w* X2; // *(x2+y2=1)
*A++ += w* XY; // *(x2+y2=1)
if(_type == Projective) {
*A++ -= w* xz * XZ;
*A++ -= w* yz * XZ;
}
*B++ -= w* XZ; // *(x2+y2=1)
A += 5; // Equation 6
*A++ += w* Y2; // *(x2+y2=1)
*B++ -= w* YZ; // *(x2+y2=1)
if(_type == Projective) {
*A++ -= w* xz * YZ;
*A++ -= w* yz * YZ;
A += 6; // Equation 7
*A++ += w* (y2+z2) * Y2;
*A++ -= w* xy * Z2;
*B++ += w* xz * Z2;
A += 7; // Equation 8
*A++ += w* (x2+z2) * Z2;
*B++ += w* yz * Z2;
}
b += w* Z2; // *(x2+y2=1)
}
// Add two corresponding lines, type involving at most 4 parameters
void ComputeH::add_4parameters(float x, float y, float z,
float X, float Y, float Z, float w)
{
double x2 = x*x, y2 = y*y, z2 = z*z, xy = x*y, xz = x*z, yz = y*z;
double X2 = X*X, Y2 = Y*Y, Z2 = Z*Z, XY = X*Y, XZ = X*Z, YZ = Y*Z;
double *A = Ann, *B = Bn;
if(_type == Translation) {
*A++ += w* X2; // *(x2+y2=1)
*A++ += w* XY; // *(x2+y2=1)
*B++ += w* (yz*XY + xz*X2 - XZ/* *(x2+y2=1) */);
A++;
*A++ += w* Y2; // *(x2+y2=1)
*B++ += w* (xz*XY + yz*Y2 - YZ/* *(x2+y2=1) */);
b += w* (z2 + Z2 + y2*X2 + x2*Z2 - 2*(xz*XZ + yz*YZ + xy*XZ));
return;
}
b += w* Z2; // *(x2+y2=1)
if(_type == GeneralZoom) {
*A++ += w* (y2+z2) * X2;
*A++ -= w* xy * XY;
*A++ -= w* xz * X2;
*A++ -= w* xz * XY;
*B++ += w* xz * XZ;
A++;
*A++ += w* (x2+z2) * Y2;
*A++ -= w* yz * XY;
*A++ -= w* yz * Y2;
*B++ += w* yz * YZ;
A += 2;
*A++ += w* X2; // *(x2+y2=1)
*A++ += w* XY; // *(x2+y2=1)
*B++ -= w* XZ; // *(x2+y2=1)
A += 3;
*A++ += w* Y2; // *(x2+y2=1)
*B++ -= w* YZ; // *(x2+y2=1)
return;
}
if(_type == Zoom) {
*A++ += w* (z2/* *(X2+Y2=1)*/ + y2*X2 + x2*Y2 - 2*xy*XY);
*A++ -= w* (yz*XY + xz*X2);
*A++ -= w* (yz*Y2 + xz*XY);
*B++ += w* (yz*YZ + xz*X2);
} else { // Similarity or Rotation
*A++ += w* (1 /* =x2+y2*/+ 2*(z2 - xy)) * X2;
*A++ += w* (x2 - y2) * XY;
*A++ -= w* (xz + yz) * X2;
*A++ -= w* (xz + yz) * XY;
*B++ += w* (xz + yz) * XZ;
A++;
*A++ += w* (1 /* =x2+y2*/+ 2*(z2 + xy)) * Y2;
*A++ += w* (xz - yz) * XY;
*A++ += w* (xz - yz) * Y2;
*B++ += w* (yz - xz) * YZ;
A++; // Prepare for next line
}
A++;
*A++ += w* X2; // *(x2+y2=1)
*A++ += w* XY; // *(x2+y2=1)
*B++ -= w* XZ; // *(x2+y2=1)
A += n-1;
*A++ += w* Y2; // *(x2+y2=1)
*B++ -= w* YZ; // *(x2+y2=1)
}
// Wrap vector of unknowns `v' into structure `map'
void ComputeH::wrap(Homography& h, const vector<double>& v) const
{
int i = 0;
h.mat()(0,0) = (_type==Translation)? 1.0f: v(i++);
h.mat()(0,1) = (_type==Translation || _type==Zoom || _type==GeneralZoom) ?
0: v(i++);
if(n >= 6) {
h.mat()(1,0) = v(i++);
h.mat()(1,1) = v(i++);
} else {
h.mat()(1,0) = -h.mat()(0,1);
h.mat()(1,1) = (_type==GeneralZoom)? v(i++): h.mat()(0,0);
}
h.mat()(0,2) = v(i++);
h.mat()(1,2) = v(i++);
if(_type == Projective) {
h.mat()(2,0) = v(i++);
h.mat()(2,1) = v(i++);
} else
h.mat()(2,0) = h.mat()(2,1) = 0;
h.mat()(2,2) = 1.0;
}
/// Unwrap parameters in \a h into vector of unknowns \a v.
void ComputeH::unwrap(const Homography& h, vector<double>& v) const
{
int i = 0;
if(_type != Translation) {
v(i++) = h.mat()(0,0);
if(_type != Zoom) {
if(_type != GeneralZoom) {
v(i++) = h.mat()(0,1); // Rotation or Similarity or...
if(n >= 6) // Affine or Projective
v(i++) = h.mat()(1,0);
}
if(_type==GeneralZoom || _type==Affine || _type==Projective)
v(i++) = h.mat()(1,1);
}
}
v(i++) = h.mat()(0,2);
v(i++) = h.mat()(1,2);
if(_type == Projective) {
v(i++) = h.mat()(2,0);
v(i++) = h.mat()(2,1);
}
}
// Sum of weights (=#correspondences)
float ComputeH::weight() const
{
// Diagonal coefficient affecting translation
int i = (_type == Projective) ? 6 : n;
return static_cast<float>(Ann[(i-1)*(n+1)]); // Element (i-1,i-1)
}
// Return quadratic error when mapping with `motion'
float ComputeH::q_error(const Homography& map) const
{
vector<double> v(n);
unwrap(map, v);
return q_error(v);
}
// Idem, with arguments in a vector
float ComputeH::q_error(const vector<double>& v) const
{
double e = b;
// Diagonal terms
const double* A = Ann + n*n-1;
for(int i = n-1; i >= 0; i--, A -= n+1)
e += *A * v(i) * v(i);
// Cross terms
A = Ann + (n-1)*n; // Last row
for(int i = n-1; i >= 0; i--, A -= n) {
double vi = v(i);
e -= 2.0 * Bn[i] * vi;
for(int j = n-1; j > i; j--)
e += 2.0 * A[j] * vi * v(j);
}
return static_cast<float>(e);
}
// LSE for rotation: solve linear system under quadratic constraint
bool ComputeH::compute_rotation(vector<double>& B) const
{
if(Ann[15] <= 0) // No point added or absurd value
return false;
B(0) = Ann[15] * Bn[0] - Ann[2] * Bn[2] - Ann[3] * Bn[3];
B(1) = Ann[15] * Bn[1] - Ann[3] * Bn[2] + Ann[2] * Bn[3];
double root = sqrt(B(0)*B(0) + B(1)*B(1));
if(root < minEigenValue)
return false;
// Test first solution
double lambda1 = (Ann[2]*Ann[2] + Ann[3]*Ann[3] + root) / Ann[15];
B(0) /= root;
B(1) /= root;
B(2) = (-Ann[2]*Bn[0] - Ann[3]*Bn[1] + lambda1 * Bn[2]) / root;
B(3) = (-Ann[3]*Bn[0] + Ann[2]*Bn[1] + lambda1 * Bn[3]) / root;
float v1 = q_error(B);
// Test second solution
vector<double> C(4);
double lambda2 = (Ann[2]*Ann[2] + Ann[3]*Ann[3] - root) / Ann[15];
C(0) = -B(0);
C(1) = -B(1);
C(2) = -(-Ann[2]*Bn[0] - Ann[3]*Bn[1] + lambda2 * Bn[2]) / root;
C(3) = -(-Ann[3]*Bn[0] + Ann[2]*Bn[1] + lambda2 * Bn[3]) / root;
if(v1 > q_error(C)) // Keep second solution
B = C;
return true;
}
// Return LSE motion and the sum of weights
float ComputeH::compute(Homography& map) const
{
vector<double> B(n);
B.read(Bn);
if(_type == Rotation) {
if(! compute_rotation(B))
return 0;
} else {
matrix<double> A(n,n);
A.read(Ann);
Normalization left, right;
if(_type == Projective && !normalize(left, A, B, right))
return 0;
A.symUpper();
vector<double> oldB(B);
if(! solveLU(A, B))
return 0;
if(_type == Projective && ! de_normalize(left, B, right))
return 0;
}
wrap(map, B);
return weight();
}
// Normalize independently original and final points so that the new
// origin is their centroid and their mean square distance (to it) is 2
bool ComputeH::normalize(Normalization& left,
matrix<double>& A, vector<double>& B,
Normalization& right) const
{
double w = A(5,5); // Total weight
if(w < minEigenValue)
return false;
double invW = 1.0 / w;
// Find normalizations (zoom-translation)
right.s = (A(0,0) + A(1,1)) - (A(0,4)*A(0,4) + A(1,4)*A(1,4))*invW;
if(right.s < minEigenValue)
return false;
right.s = sqrt(2.0*w / right.s);
right.x = - invW * right.s * A(0,4);
right.y = - invW * right.s * A(1,4);
left.s = b - (B(4)*B(4) + B(5)*B(5))*invW;
if(left.s < minEigenValue)
return false;
left.s = sqrt(2.0*w / left.s);
left.x = - invW * left.s * B(4);
left.y = - invW * left.s * B(5);
double norm = left.x*left.x + left.y*left.y;
double s2 = right.s*right.s, sS = right.s*left.s, S2 = left.s*left.s;
// Normalization of vector B
double b0 = B(0), b1 = B(1), b2 = B(2), b3 = B(3);
B(0) = sS * B(0) - w*right.x*left.x;
B(1) = sS * B(1) - w*right.y*left.x;
B(2) = sS * B(2) - w*right.x*left.y;
B(3) = sS * B(3) - w*right.y*left.y;
B(4) = B(5) = 0;
B(6) = sS*(left.s*B(6) - 2*(left.x*b0 + left.y*b2)) +
w*right.x*(norm - 2.0);
B(7) = sS*(left.s*B(7) - 2*(left.x*b1 + left.y*b3)) +
w*right.y*(norm - 2.0);
// Normalization of matrix A
double a0 = A(0,0), a1 = A(0,1), a6 = A(0,6), a7 = A(0,7), a9 = A(1,1);
double a15 = A(1,7), a22 = A(2,6), a23 = A(2,7), a31 = A(3,7);
A(0,0) = s2 * A(0,0) - w*right.x*right.x;
A(0,1) = s2 * A(0,1) - w*right.x*right.y;
A(0,4) = 0;
A(0,6) = right.s*(sS*A(0,6) - right.s*left.x*a0 - left.s*right.x*b0) +
w*right.x*left.x*right.x - right.x * B(0);
A(0,7) = right.s*(sS*A(0,7) - right.s*left.x*a1 - left.s*right.x*b1) +
w*right.x*left.x*right.y - right.y * B(0);
A(1,1) = s2 * A(1,1) - w*right.y*right.y;
A(1,4) = 0;
A(1,6) = A(0,7);
A(1,7) = right.s*(sS*A(1,7) - right.s*left.x*a9 - left.s*right.y*b1) +
w*right.y*left.x*right.y - right.y * B(1);
A(2,2) = A(0,0);
A(2,3) = A(0,1);
A(2,5) = 0;
A(2,6) = right.s*(sS*A(2,6) - right.s*left.y*a0 - left.s*right.x*b2) +
w*right.x*left.y*right.x - right.x * B(2);
A(2,7) = right.s*(sS*A(2,7) - right.s*left.y*a1 - left.s*right.x*b3) +
w*right.x*left.y*right.y - right.y * B(2);
A(3,3) = A(1,1);
A(3,5) = 0;
A(3,6) = A(2,7);
A(3,7) = right.s*(sS*A(3,7) - right.s*left.y*a9 - left.s*right.y*b3) +
w*right.y*left.y*right.y - right.y * B(3);
A(4,6) = -B(0);
A(4,7) = -B(1);
A(5,6) = -B(2);
A(5,7) = -B(3);
A(6,6) = s2*(S2*A(6,6) - 2*left.s*(left.x*a6+left.y*a22) + a0*norm) -
2*right.x*(B(6) + w*right.x);
A(6,7) = s2*(S2*A(6,7) - 2*left.s*(left.x*a7+left.y*a23) + a1*norm) -
right.x*B(7) - right.y*B(6) - 2*w*right.x*right.y;
A(7,7) = s2*(S2*A(7,7) - 2*left.s*(left.x*a15+left.y*a31) + a9*norm) -
2*right.y*(B(7) + w*right.y);
return true;
}
// `l' (left) and 'r' (right) representing zoom-translation normalizations,
// and `B' the parameters of a projective motion,
// compute l^-1 B r
bool ComputeH::de_normalize(const Normalization& l,
vector<double>& B,
const Normalization& r)
{
// B := B r
B(4) += r.x * B(0) + r.y * B(1); // Line 1
B(0) *= r.s;
B(1) *= r.s;
B(5) += r.x * B(2) + r.y * B(3); // Line 2
B(2) *= r.s;
B(3) *= r.s;
double f = r.x * B(6) + r.y * B(7) + 1.0; // Line 3
if(-minEigenValue < f && f < minEigenValue)
return false; // Origin of right normalization on line at infinity
B(6) *= r.s;
B(7) *= r.s;
// B := l^-1 B
double s = 1.0 / (l.s * f);
B(0) = (B(0) - l.x*B(6)) * s; // Line 1
B(1) = (B(1) - l.x*B(7)) * s;
B(4) = (B(4) - l.x* f ) * s;
B(2) = (B(2) - l.y*B(6)) * s; // Line 2
B(3) = (B(3) - l.y*B(7)) * s;
B(5) = (B(5) - l.y* f ) * s;
B(6) /= f; // Line 3
B(7) /= f;
return true;
}
} // libNumerics