core/indigo-core/common/math/best_fit.cpp (256 lines of code) (raw):
/****************************************************************************
* Copyright (C) from 2009 to Present EPAM Systems.
*
* This file is part of Indigo toolkit.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
#include "base_cpp/array.h"
#include "base_cpp/tlscont.h"
#include "math/algebra.h"
using namespace indigo;
bool Transform3f::bestFit(int npoints, const Vec3f points[], const Vec3f goals[], float* sqsum_out)
{
QS_DEF(Array<double>, X); // set of points
QS_DEF(Array<double>, Y); // set of goals
Matr3x3d R, RT, RTR, evectors_matrix;
//
Matr3x3d rotation;
double scale;
Vec3f translation;
//
bool res = 1;
Vec3f vec, tmp;
double cpoints[3] = {0.0}, cgoals[3] = {0.0}; // centroid of points, of goals
int i, j, k;
for (i = 0; i < npoints; i++)
{
cpoints[0] += points[i].x;
cpoints[1] += points[i].y;
cpoints[2] += points[i].z;
cgoals[0] += goals[i].x;
cgoals[1] += goals[i].y;
cgoals[2] += goals[i].z;
}
for (i = 0; i < 3; i++)
{
cpoints[i] /= npoints;
cgoals[i] /= npoints;
}
X.resize(npoints * 3);
Y.resize(npoints * 3);
// move each set to origin
for (i = 0; i < npoints; i++)
{
X[i * 3 + 0] = points[i].x - cpoints[0];
X[i * 3 + 1] = points[i].y - cpoints[1];
X[i * 3 + 2] = points[i].z - cpoints[2];
Y[i * 3 + 0] = goals[i].x - cgoals[0];
Y[i * 3 + 1] = goals[i].y - cgoals[1];
Y[i * 3 + 2] = goals[i].z - cgoals[2];
}
if (npoints > 1)
{
/* compute R */
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
R.elements[i * 3 + j] = 0.0;
for (k = 0; k < npoints; k++)
{
R.elements[i * 3 + j] += Y[k * 3 + i] * X[k * 3 + j];
}
}
}
// Compute R^T * R
R.getTransposed(RT);
RT.matrixMatrixMultiply(R, RTR);
RTR.eigenSystem(evectors_matrix);
if (RTR.elements[0] > 2 * EPSILON)
{
// float norm_b0,norm_b1,norm_b2;
Vec3f a0, a1, a2;
Vec3f b0, b1, b2;
a0.set((float)evectors_matrix.elements[0], (float)evectors_matrix.elements[3], (float)evectors_matrix.elements[6]);
a1.set((float)evectors_matrix.elements[1], (float)evectors_matrix.elements[4], (float)evectors_matrix.elements[7]);
a2.cross(a0, a1);
R.matrixVectorMultiply(a0, b0);
R.matrixVectorMultiply(a1, b1);
// norm_b0 = b0.length();
// norm_b1 = b1.length();
Line3f l1, l2;
float sqs1, sqs2;
l1.bestFit(npoints, points, &sqs1);
l2.bestFit(npoints, goals, &sqs2);
if (sqs1 < 2 * EPSILON && sqs2 < 2 * EPSILON)
{
Transform3f temp;
temp.rotationVecVec(l1.dir, l2.dir);
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
rotation.elements[i * 3 + j] = temp.elements[j * 4 + i];
}
else
{
b0.normalize();
b1.normalize();
b2.cross(b0, b1);
// norm_b2 = b2.length();
evectors_matrix.elements[2] = a2.x;
evectors_matrix.elements[5] = a2.y;
evectors_matrix.elements[8] = a2.z;
evectors_matrix.transpose();
RTR.elements[0] = b0.x;
RTR.elements[1] = b1.x;
RTR.elements[2] = b2.x;
RTR.elements[3] = b0.y;
RTR.elements[4] = b1.y;
RTR.elements[5] = b2.y;
RTR.elements[6] = b0.z;
RTR.elements[7] = b1.z;
RTR.elements[8] = b2.z;
RTR.matrixMatrixMultiply(evectors_matrix, rotation);
}
}
else
{
res = 0;
}
}
else
{
res = 0;
}
if (!res)
{
rotation.identity();
}
// Calc scale
scale = 1.0;
if (res && npoints > 1)
{
float l1 = 0.0;
float l2 = 0.0;
Vec3f vx, vy;
for (i = 0; i < npoints; i++)
{
Vec3f vx((float)X[i * 3 + 0], (float)X[i * 3 + 1], (float)X[i * 3 + 2]);
Vec3f vy((float)Y[i * 3 + 0], (float)Y[i * 3 + 1], (float)Y[i * 3 + 2]);
rotation.matrixVectorMultiply(vx, vec);
l1 += Vec3f::dot(vy, vec);
l2 += Vec3f::dot(vec, vec);
}
scale = l1 / l2;
}
X.clear();
Y.clear();
// Calc translation
translation.set((float)cgoals[0], (float)cgoals[1], (float)cgoals[2]);
tmp = Vec3f((float)cpoints[0], (float)cpoints[1], (float)cpoints[2]);
rotation.matrixVectorMultiply(tmp, vec);
vec.scale((float)scale);
translation.sub(vec);
identity();
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
elements[i * 4 + j] = (float)rotation.elements[j * 3 + i];
}
}
elements[15] = 1.0f;
translate(translation);
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
elements[i * 4 + j] *= (float)scale;
}
}
// Deviation
if (sqsum_out)
{
*sqsum_out = 0;
float d = .0f;
for (i = 0; i < npoints; i++)
{
vec.pointTransformation(points[i], *this);
d = Vec3f::dist(vec, goals[i]);
*sqsum_out += d * d;
}
}
return true;
}
bool Plane3f::bestFit(int npoints, const Vec3f points[], float* sqsum_out)
{
QS_DEF(Array<double>, m);
m.clear_resize(npoints * 3);
int i, j, k;
Matr3x3d A, evec;
Vec3f c;
for (i = 0; i < npoints; i++)
{
c.add(points[i]);
}
c.scale(1.0f / npoints);
for (i = 0; i < npoints; i++)
{
m[3 * i + 0] = points[i].x - c.x;
m[3 * i + 1] = points[i].y - c.y;
m[3 * i + 2] = points[i].z - c.z;
}
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
A.elements[i * 3 + j] = 0;
for (k = 0; k < npoints; k++)
{
A.elements[i * 3 + j] += m[k * 3 + i] * m[k * 3 + j];
}
}
}
A.eigenSystem(evec);
_norm.x = (float)evec.elements[2];
_norm.y = (float)evec.elements[5];
_norm.z = (float)evec.elements[8];
_d = -Vec3f::dot(_norm, c);
if (sqsum_out != 0)
{
*sqsum_out = 0;
for (i = 0; i < npoints; i++)
{
float d = distFromPoint(points[i]);
*sqsum_out += d * d;
}
}
return true;
}
bool Line3f::bestFit(int npoints, const Vec3f points[], float* sqsum_out)
{
QS_DEF(Array<double>, m);
Matr3x3d A;
Matr3x3d evec;
int i, j, k;
m.clear_resize(npoints * 3);
org = Vec3f(0, 0, 0);
for (i = 0; i < npoints; i++)
{
org.add(points[i]);
}
org.scale(1.0f / npoints);
for (i = 0; i < npoints; i++)
{
m[3 * i + 0] = points[i].x - org.x;
m[3 * i + 1] = points[i].y - org.y;
m[3 * i + 2] = points[i].z - org.z;
}
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
A.elements[i * 3 + j] = 0;
for (k = 0; k < npoints; k++)
{
A.elements[i * 3 + j] += m[k * 3 + i] * m[k * 3 + j];
}
}
}
A.eigenSystem(evec);
dir.x = (float)evec.elements[0];
dir.y = (float)evec.elements[3];
dir.z = (float)evec.elements[6];
dir.normalize();
if (sqsum_out != 0)
{
*sqsum_out = 0;
for (int i = 0; i < npoints; i++)
{
float d = distFromPoint(points[i]);
*sqsum_out += d * d;
}
}
return true;
}