init
This commit is contained in:
+2557
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,212 @@
|
||||
#include "geometry/Basics.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions> // SQRT of Matrix
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//************************************************
|
||||
//******************** Matrix ********************
|
||||
//************************************************
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
Eigen::MatrixXd AffineTransformation(const Eigen::MatrixXd& ref, const Eigen::MatrixXd& matrix)
|
||||
{
|
||||
const Eigen::MatrixXd isR = ref.sqrt().inverse(); // Inverse Square root of Reference matrix => isR
|
||||
return isR * matrix * isR.transpose(); // Affine transformation : isR * sample * isR^T
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixStandardization(Eigen::MatrixXd& matrix, const EStandardization standard)
|
||||
{
|
||||
if (standard == EStandardization::Center) { return MatrixCenter(matrix); }
|
||||
if (standard == EStandardization::StandardScale) { return MatrixStandardization(matrix); }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixStandardization(const Eigen::MatrixXd& in, Eigen::MatrixXd& out, const EStandardization standard)
|
||||
{
|
||||
out = in;
|
||||
return MatrixStandardization(out, standard);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixCenter(Eigen::MatrixXd& matrix)
|
||||
{
|
||||
for (size_t i = 0, r = matrix.rows(), c = matrix.cols(); i < r; ++i)
|
||||
{
|
||||
const double mu = matrix.row(i).mean();
|
||||
for (size_t j = 0; j < c; ++j) { matrix(i, j) -= mu; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixCenter(const Eigen::MatrixXd& in, Eigen::MatrixXd& out)
|
||||
{
|
||||
out = in;
|
||||
return MatrixCenter(out);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixStandardScaler(Eigen::MatrixXd& matrix)
|
||||
{
|
||||
Eigen::RowVectorXd dummyScale;
|
||||
return MatrixStandardScaler(matrix, dummyScale);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixStandardScaler(Eigen::MatrixXd& matrix, Eigen::RowVectorXd& scale)
|
||||
{
|
||||
const size_t r = matrix.rows(), c = matrix.cols();
|
||||
std::vector<double> mu(r, 0), sigma(r, 0);
|
||||
scale.resize(r);
|
||||
|
||||
for (size_t i = 0; i < r; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < c; ++j)
|
||||
{
|
||||
const double value = matrix(i, j);
|
||||
mu[i] += value;
|
||||
sigma[i] += value * value;
|
||||
}
|
||||
|
||||
mu[i] /= double(c);
|
||||
sigma[i] = sigma[i] / double(c) - mu[i] * mu[i];
|
||||
scale[i] = sigma[i] == 0 ? 1 : sqrt(sigma[i]);
|
||||
|
||||
for (size_t j = 0; j < c; ++j) { matrix(i, j) = (matrix(i, j) - mu[i]) / scale[i]; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixStandardScaler(const Eigen::MatrixXd& in, Eigen::MatrixXd& out, Eigen::RowVectorXd& scale)
|
||||
{
|
||||
out = in;
|
||||
return MatrixStandardScaler(out, scale);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixStandardScaler(const Eigen::MatrixXd& in, Eigen::MatrixXd& out)
|
||||
{
|
||||
Eigen::RowVectorXd dummyScale;
|
||||
return MatrixStandardScaler(in, out, dummyScale);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
std::string MatrixPrint(const Eigen::MatrixXd& matrix)
|
||||
{
|
||||
std::stringstream sstream;
|
||||
sstream << matrix;
|
||||
return sstream.str();
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool AreEquals(const Eigen::MatrixXd& matrix1, const Eigen::MatrixXd& matrix2, const double precision)
|
||||
{
|
||||
return matrix1.size() == matrix2.size() && (matrix1.size() == 0 || matrix1.isApprox(matrix2, precision));
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//************************************************
|
||||
//************************************************
|
||||
//************************************************
|
||||
|
||||
//*************************************************************
|
||||
//******************** Index Manipulations ********************
|
||||
//*************************************************************
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
Eigen::RowVectorXd GetElements(const Eigen::RowVectorXd& row, const std::vector<size_t>& index)
|
||||
{
|
||||
const size_t k = index.size();
|
||||
Eigen::RowVectorXd result(k);
|
||||
for (size_t i = 0; i < k; ++i) { result[i] = row[index[i]]; }
|
||||
return result;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
//*************************************************************
|
||||
//*************************************************************
|
||||
//*************************************************************
|
||||
|
||||
//***************************************************
|
||||
//******************** Validates ********************
|
||||
//***************************************************
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool InRange(const double value, const double min, const double max) { return (min <= value && value <= max); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool AreNotEmpty(const std::vector<Eigen::MatrixXd>& matrices)
|
||||
{
|
||||
if (matrices.empty()) { return false; }
|
||||
for (const auto& m : matrices) { if (!IsNotEmpty(m)) { return false; } }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool IsNotEmpty(const Eigen::MatrixXd& matrix) { return (matrix.size() != 0); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool HaveSameSize(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b) { return (IsNotEmpty(a) && a.rows() == b.rows() && a.cols() == b.cols()); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
bool IsSquare(const Eigen::MatrixXd& matrix) { return (IsNotEmpty(matrix) && matrix.rows() == matrix.cols()); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool AreSquare(const std::vector<Eigen::MatrixXd>& matrices)
|
||||
{
|
||||
if (matrices.empty()) { return false; }
|
||||
for (const auto& m : matrices) { if (!IsSquare(m)) { return false; } }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool HaveSameSize(const std::vector<Eigen::MatrixXd>& matrices)
|
||||
{
|
||||
if (matrices.empty()) { return false; }
|
||||
const size_t r = matrices[0].rows(), c = matrices[0].cols();
|
||||
for (const auto& m : matrices) { if (size_t(m.rows()) != r || size_t(m.cols()) != c) { return false; } }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
//***************************************************
|
||||
//***************************************************
|
||||
//***************************************************
|
||||
|
||||
//********************************************************
|
||||
//******************** CSV MANAGEMENT ********************
|
||||
//********************************************************
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
std::vector<std::string> Split(const std::string& s, const std::string& sep)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
std::string::size_type i = 0, j;
|
||||
const std::string::size_type n = sep.size();
|
||||
|
||||
while ((j = s.find(sep, i)) != std::string::npos)
|
||||
{
|
||||
result.emplace_back(s, i, j - i); // Add part
|
||||
i = j + n; // Update pos
|
||||
}
|
||||
result.emplace_back(s, i, s.size() - 1 - i); // Last without \n
|
||||
return result;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
//********************************************************
|
||||
//********************************************************
|
||||
//********************************************************
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "geometry/Classification.hpp"
|
||||
#include "geometry/Covariance.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool LSQR(const std::vector<std::vector<Eigen::RowVectorXd>>& dataset, Eigen::MatrixXd& weight)
|
||||
{
|
||||
// Precomputation
|
||||
if (dataset.empty()) { return false; }
|
||||
const size_t nbClass = dataset.size(), nbFeatures = dataset[0][0].size();
|
||||
std::vector<size_t> nbSample(nbClass);
|
||||
size_t totalSample = 0;
|
||||
for (size_t k = 0; k < nbClass; ++k)
|
||||
{
|
||||
if (dataset[k].empty()) { return false; }
|
||||
nbSample[k] = dataset[k].size();
|
||||
totalSample += nbSample[k];
|
||||
}
|
||||
|
||||
// Compute Class Euclidian mean
|
||||
Eigen::MatrixXd mean = Eigen::MatrixXd::Zero(nbClass, nbFeatures);
|
||||
for (size_t k = 0; k < nbClass; ++k)
|
||||
{
|
||||
for (size_t i = 0; i < nbSample[k]; ++i) { mean.row(k) += dataset[k][i]; }
|
||||
mean.row(k) /= double(nbSample[k]);
|
||||
}
|
||||
|
||||
// Compute Class Covariance
|
||||
Eigen::MatrixXd cov = Eigen::MatrixXd::Zero(nbFeatures, nbFeatures);
|
||||
for (size_t k = 0; k < nbClass; ++k)
|
||||
{
|
||||
//Fit Data to existing covariance matrix method
|
||||
Eigen::MatrixXd classData(nbFeatures, nbSample[k]);
|
||||
for (size_t i = 0; i < nbSample[k]; ++i) { classData.col(i) = dataset[k][i]; }
|
||||
|
||||
// Standardize Features
|
||||
Eigen::RowVectorXd scale;
|
||||
MatrixStandardScaler(classData, scale);
|
||||
|
||||
//Compute Covariance of this class
|
||||
Eigen::MatrixXd classCov;
|
||||
if (!CovarianceMatrix(classData, classCov, EEstimator::LWF)) { return false; }
|
||||
|
||||
// Rescale
|
||||
for (size_t i = 0; i < nbFeatures; ++i) { for (size_t j = 0; j < nbFeatures; ++j) { classCov(i, j) *= scale[i] * scale[j]; } }
|
||||
|
||||
//Add to cov with good weight
|
||||
cov += (double(nbSample[k]) / double(totalSample)) * classCov;
|
||||
}
|
||||
|
||||
// linear least squares systems solver
|
||||
// Chosen solver with the performance table of this page : https://eigen.tuxfamily.org/dox/group__TutorialLinearAlgebra.html
|
||||
weight = cov.colPivHouseholderQr().solve(mean.transpose()).transpose();
|
||||
//weight = cov.completeOrthogonalDecomposition().solve(mean.transpose()).transpose();
|
||||
//weight = cov.bdcSvd(ComputeThinU | ComputeThinV).solve(mean.transpose()).transpose();
|
||||
|
||||
// Treat binary case as a special case
|
||||
if (nbClass == 2)
|
||||
{
|
||||
const Eigen::MatrixXd tmp = weight.row(1) - weight.row(0); // Need to use a tmp variable otherwise sometimes error
|
||||
weight = tmp;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool FgDACompute(const std::vector<std::vector<Eigen::RowVectorXd>>& dataset, Eigen::MatrixXd& weight)
|
||||
{
|
||||
// Compute LSQR Weight
|
||||
Eigen::MatrixXd w;
|
||||
if (!LSQR(dataset, w)) { return false; }
|
||||
const size_t nbClass = w.rows();
|
||||
|
||||
// Transform to FgDA Weight
|
||||
const Eigen::MatrixXd wT = w.transpose();
|
||||
weight = (wT * (w * wT).colPivHouseholderQr().solve(Eigen::MatrixXd::Identity(nbClass, nbClass))) * w;
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool FgDAApply(const Eigen::RowVectorXd& in, Eigen::RowVectorXd& out, const Eigen::MatrixXd& weight)
|
||||
{
|
||||
if (in.cols() != weight.rows()) { return false; }
|
||||
out = in * weight;
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,172 @@
|
||||
# include "geometry/Covariance.hpp"
|
||||
#include <algorithm> // std::min/max
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//***********************************************************
|
||||
//******************** COVARIANCES BASES ********************
|
||||
//***********************************************************
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double Variance(const Eigen::RowVectorXd& x)
|
||||
{
|
||||
const size_t S = x.cols(); // Number of Samples => S
|
||||
if (S == 0) { return 0; } // If false input
|
||||
|
||||
const double mu = x.mean();
|
||||
return x.cwiseProduct(x).sum() / S - mu * mu;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double Covariance(const Eigen::RowVectorXd& x, const Eigen::RowVectorXd& y)
|
||||
{
|
||||
const size_t xS = x.cols(), yS = y.cols(); // Number of Samples => S
|
||||
if (xS == 0 || xS != yS) { return 0; } // If false input
|
||||
return (x.cwiseProduct(y).sum() - x.sum() * y.sum() / xS) / xS;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool ShrunkCovariance(Eigen::MatrixXd& cov, const double shrinkage)
|
||||
{
|
||||
if (!InRange(shrinkage, 0, 1)) { return false; } // Verification
|
||||
const size_t n = cov.rows(); // Number of Features => N
|
||||
|
||||
const double coef = shrinkage * cov.trace() / n; // Diagonal Coefficient
|
||||
cov = (1 - shrinkage) * cov; // Shrinkage
|
||||
for (size_t i = 0; i < n; ++i) { cov(i, i) += coef; } // Add Diagonal Coefficient
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool ShrunkCovariance(const Eigen::MatrixXd& in, Eigen::MatrixXd& out, const double shrinkage)
|
||||
{
|
||||
out = in;
|
||||
return ShrunkCovariance(out, shrinkage);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrix(const Eigen::MatrixXd& in, Eigen::MatrixXd& out, const EEstimator estimator, const EStandardization standard)
|
||||
{
|
||||
if (!IsNotEmpty(in)) { return false; } // Verification
|
||||
Eigen::MatrixXd sample;
|
||||
MatrixStandardization(in, sample, standard); // Standardization
|
||||
switch (estimator) // Switch Method
|
||||
{
|
||||
case EEstimator::COV: return CovarianceMatrixCOV(sample, out);
|
||||
case EEstimator::SCM: return CovarianceMatrixSCM(sample, out);
|
||||
case EEstimator::LWF: return CovarianceMatrixLWF(sample, out);
|
||||
case EEstimator::OAS: return CovarianceMatrixOAS(sample, out);
|
||||
case EEstimator::MCD: return CovarianceMatrixMCD(sample, out);
|
||||
case EEstimator::COR: return CovarianceMatrixCOR(sample, out);
|
||||
default: return CovarianceMatrixIDE(sample, out);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//***********************************************************
|
||||
//***********************************************************
|
||||
//***********************************************************
|
||||
|
||||
//***********************************************************
|
||||
//******************** COVARIANCES TYPES ********************
|
||||
//***********************************************************
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixCOV(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov)
|
||||
{
|
||||
const size_t n = samples.rows(); // Number of Features => N
|
||||
|
||||
cov.resize(n, n); // Init size of matrix
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
const Eigen::RowVectorXd ri = samples.row(i);
|
||||
cov(i, i) = Variance(ri); // Diagonal Value
|
||||
|
||||
for (size_t j = i + 1; j < n; ++j)
|
||||
{
|
||||
const Eigen::RowVectorXd rj = samples.row(j);
|
||||
cov(i, j) = cov(j, i) = Covariance(ri, rj); // Symetric covariance
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixSCM(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov)
|
||||
{
|
||||
cov = samples * samples.transpose(); // X*X^T
|
||||
cov /= cov.trace(); // X*X^T / trace(X*X^T)
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixLWF(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov)
|
||||
{
|
||||
const size_t n = samples.rows(), S = samples.cols(); // Number of Features & Samples => N & S
|
||||
|
||||
CovarianceMatrixCOV(samples, cov); // Initial Covariance Matrix => Cov
|
||||
const double mu = cov.trace() / n;
|
||||
Eigen::MatrixXd mDelta = cov; // mDelta = cov - mu * I_n
|
||||
for (size_t i = 0; i < n; ++i) { mDelta(i, i) -= mu; }
|
||||
const Eigen::MatrixXd x2 = samples.cwiseProduct(samples), // Squared each sample => X^2
|
||||
cov2 = cov.cwiseProduct(cov); // Squared each element of Cov => Cov^2
|
||||
|
||||
const double delta = mDelta.cwiseProduct(mDelta).sum() / n,
|
||||
beta = 1. / double(n * S) * (x2 * x2.transpose() / double(S) - cov2).sum(),
|
||||
shrinkage = std::min(beta, delta) / delta; // Assure shrinkage <= 1
|
||||
|
||||
return ShrunkCovariance(cov, shrinkage); // Shrinkage of the matrix
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixOAS(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov)
|
||||
{
|
||||
const size_t n = samples.rows(), S = samples.cols(); // Number of Features & Samples => N & S
|
||||
CovarianceMatrixCOV(samples, cov); // Initial Covariance Matrix => Cov
|
||||
|
||||
// Compute Shrinkage : Formula from Chen et al.'s
|
||||
const double mu = cov.trace() / n,
|
||||
mu2 = mu * mu,
|
||||
alpha = cov.cwiseProduct(cov).mean(),
|
||||
num = alpha + mu2,
|
||||
den = (S + 1) * (alpha - mu2 / n),
|
||||
shrinkage = (den == 0) ? 1.0 : std::min(num / den, 1.0);
|
||||
|
||||
return ShrunkCovariance(cov, shrinkage); // Shrinkage of the matrix
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixMCD(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov) { return CovarianceMatrixIDE(samples, cov); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixCOR(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov)
|
||||
{
|
||||
const size_t n = samples.rows(); // Number of Features => N
|
||||
CovarianceMatrixCOV(samples, cov); // Initial Covariance Matrix => Cov
|
||||
const Eigen::MatrixXd d = cov.diagonal().cwiseSqrt(); // Squared root of diagonal
|
||||
|
||||
for (size_t i = 0; i < n; ++i) { for (size_t j = 0; j < n; ++j) { cov(i, j) /= d(i) * d(j); } }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixIDE(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov)
|
||||
{
|
||||
cov = Eigen::MatrixXd::Identity(samples.rows(), samples.rows());
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
//***********************************************************
|
||||
//***********************************************************
|
||||
//***********************************************************
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "geometry/Distance.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double Distance(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, const EMetric metric)
|
||||
{
|
||||
if (!HaveSameSize(a, b)) { return 0; }
|
||||
switch (metric)
|
||||
{
|
||||
case EMetric::Riemann: return DistanceRiemann(a, b);
|
||||
case EMetric::Euclidian: return DistanceEuclidian(a, b);
|
||||
case EMetric::LogEuclidian: return DistanceLogEuclidian(a, b);
|
||||
case EMetric::LogDet: return DistanceLogDet(a, b);
|
||||
case EMetric::Kullback: return DistanceKullbackSym(a, b);
|
||||
case EMetric::Wasserstein: return DistanceWasserstein(a, b);
|
||||
case EMetric::Identity:
|
||||
default: return 1.0;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceRiemann(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b)
|
||||
{
|
||||
const Eigen::GeneralizedSelfAdjointEigenSolver<Eigen::MatrixXd> es(a, b);
|
||||
const Eigen::ArrayXd result = es.eigenvalues();
|
||||
return sqrt(result.log().square().sum());
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceEuclidian(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b) { return (b - a).norm(); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceLogEuclidian(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b) { return DistanceEuclidian(a.log(), b.log()); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceLogDet(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b)
|
||||
{
|
||||
return sqrt(log((0.5 * (a + b)).determinant()) - 0.5 * log(a.determinant() * b.determinant()));
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceKullback(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b)
|
||||
{
|
||||
return 0.5 * ((b.inverse() * a).trace() - a.rows() + log(b.determinant() / a.determinant()));
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceKullbackSym(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b) { return DistanceKullback(a, b) + DistanceKullback(b, a); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceWasserstein(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b)
|
||||
{
|
||||
const Eigen::MatrixXd sB = b.sqrt();
|
||||
return sqrt((a + b - 2 * (sB * a * sB).sqrt()).trace());
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "geometry/Featurization.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions>
|
||||
#include "geometry/Basics.hpp"
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
#ifndef M_SQRT2
|
||||
#define M_SQRT2 1.4142135623730950488016887242097
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool Featurization(const Eigen::MatrixXd& in, Eigen::RowVectorXd& out, const bool tangent, const Eigen::MatrixXd& ref)
|
||||
{
|
||||
if (tangent) { return TangentSpace(in, out, ref); }
|
||||
return SqueezeUpperTriangle(in, out, true);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool UnFeaturization(const Eigen::RowVectorXd& in, Eigen::MatrixXd& out, const bool tangent, const Eigen::MatrixXd& ref)
|
||||
{
|
||||
if (tangent) { return UnTangentSpace(in, out, ref); }
|
||||
return UnSqueezeUpperTriangle(in, out, true);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool SqueezeUpperTriangle(const Eigen::MatrixXd& in, Eigen::RowVectorXd& out, const bool rowMajor)
|
||||
{
|
||||
if (!IsSquare(in)) { return false; } // Verification
|
||||
const size_t n = in.rows(); // Number of Features => N
|
||||
out.resize(n * (n + 1) / 2); // Resize
|
||||
|
||||
size_t idx = 0; // Row Index => idx
|
||||
// Row Major or Diagonal Method
|
||||
if (rowMajor) { for (size_t i = 0; i < n; ++i) { for (size_t j = i; j < n; ++j) { out[idx++] = in(i, j); } } }
|
||||
else { for (size_t i = 0; i < n; ++i) { for (size_t j = i; j < n; ++j) { out[idx++] = in(j, j - i); } } }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool UnSqueezeUpperTriangle(const Eigen::RowVectorXd& in, Eigen::MatrixXd& out, const bool rowMajor)
|
||||
{
|
||||
const size_t nR = in.size(), // Size of Row => Nr
|
||||
n = int((sqrt(1 + 8 * nR) - 1) / 2); // Number of Features => N
|
||||
if (n == 0) { return false; } // Verification
|
||||
out.setZero(n, n); // Init
|
||||
|
||||
size_t idx = 0; // Row Index => idx
|
||||
// Row Major or Diagonal Method
|
||||
if (rowMajor) { for (size_t i = 0; i < n; ++i) { for (size_t j = i; j < n; ++j) { out(j, i) = out(i, j) = in[idx++]; } } }
|
||||
else { for (size_t i = 0; i < n; ++i) { for (size_t j = i; j < n; ++j) { out(j - i, j) = out(j, j - i) = in[idx++]; } } }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool TangentSpace(const Eigen::MatrixXd& in, Eigen::RowVectorXd& out, const Eigen::MatrixXd& ref)
|
||||
{
|
||||
if (!IsSquare(in)) { return false; } // Verification
|
||||
const size_t n = in.rows(); // Number of Features => N
|
||||
|
||||
const Eigen::MatrixXd sC = (ref.size() == 0) ? Eigen::MatrixXd::Identity(n, n) : Eigen::MatrixXd(ref.sqrt()),
|
||||
isC = sC.inverse(), // Inverse Square root of ref => isC
|
||||
mJ = (isC * in * isC).log(), // Transformation Matrix => mJ
|
||||
mCoeffs = M_SQRT2 * Eigen::MatrixXd(Eigen::MatrixXd::Ones(n, n).triangularView<Eigen::StrictlyUpper>())
|
||||
+ Eigen::MatrixXd::Identity(n, n);
|
||||
|
||||
Eigen::RowVectorXd vJ, vCoeffs;
|
||||
if (!SqueezeUpperTriangle(mJ, vJ, true)) { return false; } // Get upper triangle of J => vJ
|
||||
if (!SqueezeUpperTriangle(mCoeffs, vCoeffs, true)) { return false; } // ... of Coefs => vCoeffs
|
||||
out = vCoeffs.cwiseProduct(vJ); // element-wise multiplication
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool UnTangentSpace(const Eigen::RowVectorXd& in, Eigen::MatrixXd& out, const Eigen::MatrixXd& ref)
|
||||
{
|
||||
const size_t n = out.rows(); // Number of Features => N
|
||||
if (!UnSqueezeUpperTriangle(in, out)) { return false; }
|
||||
|
||||
const Eigen::MatrixXd sC = (ref.size() == 0) ? Eigen::MatrixXd::Identity(n, n) : Eigen::MatrixXd(ref.sqrt()),
|
||||
coeffs = Eigen::MatrixXd(out.triangularView<Eigen::StrictlyUpper>()) / M_SQRT2;
|
||||
|
||||
out = sC * (Eigen::MatrixXd(out.diagonal().asDiagonal()) + coeffs + coeffs.transpose()).exp() * sC;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "geometry/Geodesic.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool Geodesic(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, Eigen::MatrixXd& g, const EMetric metric, const double alpha)
|
||||
{
|
||||
if (!HaveSameSize(a, b)) { return false; } // Verification same size
|
||||
if (!IsSquare(a)) { return false; } // Verification square matrix
|
||||
if (!InRange(alpha, 0, 1)) { return false; } // Verification alpha in [0;1]
|
||||
switch (metric) // Switch metric
|
||||
{
|
||||
case EMetric::Riemann: return GeodesicRiemann(a, b, g, alpha);
|
||||
case EMetric::Euclidian: return GeodesicEuclidian(a, b, g, alpha);
|
||||
case EMetric::LogEuclidian: return GeodesicLogEuclidian(a, b, g, alpha);
|
||||
case EMetric::Identity:
|
||||
default: return GeodesicIdentity(a, b, g, alpha);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool GeodesicRiemann(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, Eigen::MatrixXd& g, const double alpha)
|
||||
{
|
||||
const Eigen::MatrixXd sA = a.sqrt(), isA = sA.inverse();
|
||||
g = sA * (isA * b * isA).pow(alpha) * sA;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool GeodesicEuclidian(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, Eigen::MatrixXd& g, const double alpha)
|
||||
{
|
||||
g = (1 - alpha) * a + alpha * b;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool GeodesicLogEuclidian(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, Eigen::MatrixXd& g, const double alpha)
|
||||
{
|
||||
g = ((1 - alpha) * a.log() + alpha * b.log()).exp();
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool GeodesicIdentity(const Eigen::MatrixXd& a, const Eigen::MatrixXd& /*b*/, Eigen::MatrixXd& g, const double /*alpha*/)
|
||||
{
|
||||
g = Eigen::MatrixXd::Identity(a.rows(), a.rows());
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,227 @@
|
||||
#include "geometry/Metrics.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include "geometry/Geodesic.hpp"
|
||||
#include "geometry/Distance.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions>
|
||||
#include <iostream>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//static const double EPSILON = 0.000000001; // 10^{-9}
|
||||
static const double EPSILON = 0.0001; // 10^{-4}
|
||||
static const size_t ITER_MAX = 50;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool Mean(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean, const EMetric metric)
|
||||
{
|
||||
if (covs.empty()) { return false; } // If no matrix in vector
|
||||
if (covs.size() == 1) // If just one matrix in vector
|
||||
{
|
||||
mean = covs[0];
|
||||
return true;
|
||||
}
|
||||
if (!HaveSameSize(covs))
|
||||
{
|
||||
std::cout << "Matrices haven't same size." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Force Square Matrix for non Euclidian and non Identity metric
|
||||
if (!IsSquare(covs[0]) && (metric != EMetric::Euclidian && metric != EMetric::Identity))
|
||||
{
|
||||
std::cout << "Non Square Matrix is invalid with " << toString(metric) << " metric." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (metric) // Switch method
|
||||
{
|
||||
case EMetric::Riemann: return MeanRiemann(covs, mean);
|
||||
case EMetric::Euclidian: return MeanEuclidian(covs, mean);
|
||||
case EMetric::LogEuclidian: return MeanLogEuclidian(covs, mean);
|
||||
case EMetric::LogDet: return MeanLogDet(covs, mean);
|
||||
case EMetric::Kullback: return MeanKullback(covs, mean);
|
||||
case EMetric::ALE: return MeanALE(covs, mean);
|
||||
case EMetric::Harmonic: return MeanHarmonic(covs, mean);
|
||||
case EMetric::Wasserstein: return MeanWasserstein(covs, mean);
|
||||
case EMetric::Identity:
|
||||
default: return MeanIdentity(covs, mean);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool AJDPham(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& ajd, double /*epsilon*/, const int /*maxIter*/)
|
||||
{
|
||||
MeanIdentity(covs, ajd);
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanRiemann(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
size_t i = 0; // Index of Covariance Matrix => i
|
||||
double nu = 1.0, // Coefficient change => nu
|
||||
tau = std::numeric_limits<double>::max(), // Coefficient change criterion => tau
|
||||
crit = std::numeric_limits<double>::max(); // Current change => crit
|
||||
if (!MeanEuclidian(covs, mean)) { return false; } // Initial Mean
|
||||
|
||||
while (i < ITER_MAX && EPSILON < crit && EPSILON < nu) // Stopping criterion
|
||||
{
|
||||
i++; // Iteration Criterion
|
||||
const Eigen::MatrixXd sC = mean.sqrt(), isC = sC.inverse(); // Square root & Inverse Square root of Mean => sC & isC
|
||||
Eigen::MatrixXd mJ = Eigen::MatrixXd::Zero(n, n); // Change => J
|
||||
for (const auto& cov : covs) { mJ += (isC * cov * isC).log(); } // Sum of log(isC*Ci*isC)
|
||||
mJ /= double(k); // Normalization
|
||||
crit = mJ.norm(); // Current change criterion
|
||||
mean = sC * (nu * mJ).exp() * sC; // Update Mean => M = sC * exp(nu*J) * sC
|
||||
|
||||
const double h = nu * crit; // Update Coefficient change
|
||||
if (h < tau)
|
||||
{
|
||||
nu *= 0.95;
|
||||
tau = h;
|
||||
}
|
||||
else { nu *= 0.5; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanEuclidian(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
mean = Eigen::MatrixXd::Zero(n, n); // Initial Mean
|
||||
for (const auto& cov : covs) { mean += cov; } // Sum of Ci
|
||||
mean /= double(k); // Normalization
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanLogEuclidian(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
mean = Eigen::MatrixXd::Zero(n, n); // Initial Mean
|
||||
for (const auto& cov : covs) { mean += cov.log(); } // Sum of log(Ci)
|
||||
mean = (mean / double(k)).exp(); // Normalization
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanLogDet(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
size_t i = 0; // Index of Covariance Matrix => i
|
||||
double crit = std::numeric_limits<double>::max(); // Current change => crit
|
||||
if (!MeanEuclidian(covs, mean)) { return false; } // Initial Mean
|
||||
|
||||
while (i < ITER_MAX && EPSILON < crit) // Stopping criterion
|
||||
{
|
||||
i++; // Iteration Criterion
|
||||
Eigen::MatrixXd mJ = Eigen::MatrixXd::Zero(n, n); // Change => J
|
||||
|
||||
for (const auto& cov : covs) { mJ += (0.5 * (cov + mean)).inverse(); } // Sum of ((Ci+M)/2)^{-1}
|
||||
mJ = (mJ / double(k)).inverse(); // Normalization
|
||||
crit = (mJ - mean).norm(); // Current change criterion
|
||||
mean = mJ; // Update mean
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanKullback(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
Eigen::MatrixXd m1, m2;
|
||||
if (!MeanEuclidian(covs, m1)) { return false; }
|
||||
if (!MeanHarmonic(covs, m2)) { return false; }
|
||||
if (!GeodesicRiemann(m1, m2, mean, 0.5)) { return false; }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanWasserstein(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
size_t i = 0; // Index of Covariance Matrix => i
|
||||
double crit = std::numeric_limits<double>::max(); // Current change => crit
|
||||
|
||||
if (!MeanEuclidian(covs, mean)) { return false; } // Initial Mean
|
||||
Eigen::MatrixXd sC = mean.sqrt(); // Square root of Mean => sC
|
||||
|
||||
while (i < ITER_MAX && EPSILON < crit) // Stopping criterion
|
||||
{
|
||||
i++; // Iteration Criterion
|
||||
Eigen::MatrixXd mJ = Eigen::MatrixXd::Zero(n, n); // Change => J
|
||||
|
||||
for (const auto& cov : covs) { mJ += (sC * cov * sC).sqrt(); } // Sum of sqrt(sC*Ci*sC)
|
||||
mJ /= double(k); // Normalization
|
||||
|
||||
const Eigen::MatrixXd sJ = mJ.sqrt(); // Square root of change => sJ
|
||||
crit = (sJ - sC).norm(); // Current change criterion
|
||||
sC = sJ; // Update sC
|
||||
}
|
||||
mean = sC * sC; // Un-square root
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanALE(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
size_t i = 0; // Index of Covariance Matrix => i
|
||||
double crit = std::numeric_limits<double>::max(); // Change criterion => crit
|
||||
if (!AJDPham(covs, mean)) { return false; } // Initial Mean
|
||||
Eigen::MatrixXd mJ; // Change
|
||||
|
||||
while (i < ITER_MAX && EPSILON < crit) // Stopping criterion
|
||||
{
|
||||
i++; // Iteration Criterion
|
||||
mJ = Eigen::MatrixXd::Zero(n, n); // Change => J
|
||||
|
||||
for (const auto& cov : covs) { mJ += (mean.transpose() * cov * mean).log(); } // Sum of log(C^T*Ci*C)
|
||||
mJ /= double(k); // Normalization
|
||||
|
||||
Eigen::MatrixXd update = mJ.exp().diagonal().asDiagonal(); // Update Form => U
|
||||
mean = mean * update.sqrt().inverse(); // Update Mean M = M * U^{-1/2}
|
||||
|
||||
crit = DistanceRiemann(Eigen::MatrixXd::Identity(n, n), update);
|
||||
}
|
||||
|
||||
mJ = Eigen::MatrixXd::Zero(n, n); // Last Change => J
|
||||
for (const auto& cov : covs) { mJ += (mean.transpose() * cov * mean).log(); } // Sum of log(C^T*Ci*C)
|
||||
mJ /= double(k); // Normalization
|
||||
|
||||
Eigen::MatrixXd mA = mean.inverse();
|
||||
mean = mA.transpose() * mJ.exp() * mA;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanHarmonic(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
mean = Eigen::MatrixXd::Zero(n, n); // Initial Mean
|
||||
for (const auto& cov : covs) { mean += cov.inverse(); } // Sum of Inverse
|
||||
mean = (mean / double(k)).inverse(); // Normalization and inverse
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanIdentity(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
mean = Eigen::MatrixXd::Identity(covs[0].rows(), covs[0].cols());
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,151 @@
|
||||
#include "geometry/Median.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "geometry/Basics.hpp"
|
||||
#include "geometry/Featurization.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double Median(const Eigen::MatrixXd& m)
|
||||
{
|
||||
const std::vector<double> v(m.data(), m.data() + m.rows() * m.cols());
|
||||
return Median(v);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool Median(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon, const size_t maxIter, const EMetric& metric)
|
||||
{
|
||||
if (matrices.empty()) { return false; } // If no matrix in vector
|
||||
if (matrices.size() == 1) // If just one matrix in vector
|
||||
{
|
||||
median = matrices[0];
|
||||
return true;
|
||||
}
|
||||
if (!HaveSameSize(matrices)) // If different sizes
|
||||
{
|
||||
std::cout << "Matrices have different sizes." << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!IsSquare(matrices[0]) && metric == EMetric::Riemann) // If non square for Riemann metric
|
||||
{
|
||||
std::cout << "Non Square Matrix is invalid with " << toString(metric) << " metric." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (metric)
|
||||
{
|
||||
case EMetric::Riemann: return MedianRiemann(matrices, median, epsilon, maxIter);
|
||||
case EMetric::Euclidian: return MedianEuclidian(matrices, median, epsilon, maxIter);
|
||||
case EMetric::Identity: return MedianIdentity(matrices, median);
|
||||
case EMetric::LogEuclidian:
|
||||
case EMetric::LogDet:
|
||||
case EMetric::Kullback:
|
||||
case EMetric::ALE:
|
||||
case EMetric::Harmonic:
|
||||
case EMetric::Wasserstein:
|
||||
std::cout << toString(metric) << " metric not implemented." << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MedianEuclidian(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon, const size_t maxIter)
|
||||
{
|
||||
if (matrices.empty() || matrices[0].size() == 0) { return false; }
|
||||
const size_t n = matrices.size(); // Number of sample
|
||||
|
||||
// Initial Median is the median of each channel in all matrix of dataset
|
||||
median = matrices[0]; // to copy size
|
||||
for (size_t i = 0; i < size_t(median.size()); ++i)
|
||||
{
|
||||
std::vector<double> tmp;
|
||||
tmp.reserve(n); // Reserve to optimize (a little) the pushback memory access.
|
||||
for (const auto& cov : matrices) { tmp.push_back(cov.data()[i]); } // Stack value number i of all matrix
|
||||
median.data()[i] = Median(tmp);
|
||||
}
|
||||
|
||||
size_t iter = 0; // number of iteration
|
||||
double gain = epsilon; // Gain since last compute
|
||||
while (iter < maxIter && gain >= epsilon)
|
||||
{
|
||||
Eigen::MatrixXd prev = median; // Keep old median
|
||||
median.setZero(); // Reset median
|
||||
double sumCoefs = 0; // Sum of Coefficient
|
||||
for (const auto& cov : matrices)
|
||||
{
|
||||
//Eigen::MatrixXd difference = cov - prev;
|
||||
//double coef = sqrt(difference.cwiseProduct(difference).sum());
|
||||
if (cov.isApprox(prev)) { continue; } // In this case, Median is exactly this current matrix so we don't consider this matrix
|
||||
double coef = (cov - prev).norm();
|
||||
// Personnal hack and security
|
||||
coef = 1.0 / coef;
|
||||
sumCoefs += coef; // Sum for normalization
|
||||
median += coef * cov; // Add to the new median
|
||||
}
|
||||
if (sumCoefs > 0.0) { median /= sumCoefs; } // Normalize
|
||||
|
||||
gain = (median - prev).norm() / median.norm(); // It's the Frobenius norm
|
||||
iter++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MedianRiemann(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon, const size_t maxIter)
|
||||
{
|
||||
if (matrices.empty() || !IsSquare(matrices[0])) { return false; }
|
||||
const size_t n = matrices.size(); // Number of sample
|
||||
const size_t nf = matrices[0].rows() * (matrices[0].rows() + 1) / 2; // Number of Features in tangent space
|
||||
size_t iter = 0; // number of iteration
|
||||
if (!MeanEuclidian(matrices, median)) { return false; } // Initialize Median
|
||||
|
||||
double gain = epsilon; // Gain since last compute
|
||||
std::vector<Eigen::MatrixXd> mats;
|
||||
mats.reserve(n);
|
||||
for (const auto& m : matrices) { mats.push_back(m); }
|
||||
while (iter < maxIter)
|
||||
{
|
||||
// Compute Tangent space of all matrices & sum of euclidian distance of each transposed matrix
|
||||
std::vector<Eigen::RowVectorXd> ts(n);
|
||||
double sum = 0.0;
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
if (!TangentSpace(mats[i], ts[i], median)) { return false; }
|
||||
sum += sqrt(ts[i].cwiseAbs2().sum());
|
||||
}
|
||||
if (std::abs((sum - gain) / gain) < epsilon) { break; } // std::abs call fabs to keep type
|
||||
|
||||
// Arithmetic median in tangent space
|
||||
std::vector<std::vector<double>> transposeTs(nf, std::vector<double>(n));
|
||||
Eigen::RowVectorXd featureMedian(nf);
|
||||
for (size_t i = 0; i < n; ++i) { for (size_t j = 0; j < nf; ++j) { transposeTs[j][i] = ts[i][j]; } }
|
||||
for (size_t j = 0; j < nf; ++j) { featureMedian[j] = Median(transposeTs[j]); }
|
||||
|
||||
// back to the manifold
|
||||
Eigen::MatrixXd tmp;
|
||||
if (!UnTangentSpace(featureMedian, tmp, median)) { return false; }
|
||||
gain = sum; // Update gain
|
||||
median = tmp; // Update Median
|
||||
iter++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MedianIdentity(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median)
|
||||
{
|
||||
median = Eigen::MatrixXd::Identity(matrices[0].rows(), matrices[0].cols());
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,243 @@
|
||||
#include "geometry/Misc.hpp"
|
||||
#include "geometry/Featurization.hpp"
|
||||
|
||||
#include <boost/math/special_functions/gamma.hpp>
|
||||
#include <numeric> // std::iota
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
/// <summary> Get the sign of the specified value. </summary>
|
||||
/// <param name="x"> The value. </param>
|
||||
/// <returns> <c>1</c> if <c>x > 0</c>, <c>0</c> if <c>x == 0</c>, <c>-1</c> if <c>x < 0</c>. </returns>
|
||||
template <typename T>
|
||||
int sgn(T x) { return (T(0) < x) - (x < T(0)); }
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
/// <summary> Structure used for iota function for range of double. </summary>
|
||||
struct SDoubleIota
|
||||
{
|
||||
explicit SDoubleIota(const double init = 0.0, const double inc = 1.0) : v(init), inc(inc) {}
|
||||
|
||||
operator double() const { return v; } // don't add explicit qualifier for iota functions (were template cast is used)
|
||||
SDoubleIota& operator++()
|
||||
{
|
||||
v += inc;
|
||||
return *this;
|
||||
}
|
||||
double v;
|
||||
double inc;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
/// <summary> Structure used for iota function for range of round index. </summary>
|
||||
struct SRoundIndex
|
||||
{
|
||||
explicit SRoundIndex(const double init = 0.0, const double inc = 1.0) : v(init), inc(inc) {}
|
||||
|
||||
operator size_t() const { return size_t(std::round(v)); } // don't add explicit qualifier for iota functions (were template cast is used)
|
||||
SRoundIndex& operator++()
|
||||
{
|
||||
v += inc;
|
||||
return *this;
|
||||
}
|
||||
double v;
|
||||
double inc;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
std::vector<double> doubleRange(const double begin, const double end, const double step, const bool closed)
|
||||
{
|
||||
std::vector<double> res;
|
||||
if (end < begin) { return res; }
|
||||
const double size = (end - begin) / step;
|
||||
// check if the end is inclued in range for size, we ceil for no modulo values and add the last value to the range
|
||||
res.resize(size_t(std::ceil((closed && std::trunc(size) == size) ? size + 1 : size)));
|
||||
std::iota(res.begin(), res.end(), SDoubleIota(begin, step));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> RoundIndexRange(const double begin, const double end, const double step, const bool closed, const bool unique)
|
||||
{
|
||||
std::vector<size_t> res;
|
||||
if (end < begin) { return res; }
|
||||
const double size = (end - begin) / step;
|
||||
// check if the end is inclued in range for size, we ceil for no modulo values and add the last value to the range
|
||||
res.resize(size_t(std::ceil((closed && std::trunc(size) == size) ? size + 1 : size)));
|
||||
std::iota(res.begin(), res.end(), SRoundIndex(begin, step));
|
||||
|
||||
if (unique)
|
||||
{
|
||||
const auto last = std::unique(res.begin(), res.end()); // Remove duplicate values (but after last, we have undefined value)
|
||||
res.erase(last, res.end()); // Resize Vector (we erase after last)
|
||||
}
|
||||
return res;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> BinHist(const std::vector<double>& dataset, const size_t n)
|
||||
{
|
||||
std::vector<size_t> res(n, 0);
|
||||
const double max = *std::max_element(dataset.begin(), dataset.end());
|
||||
if (max == 0) { return res; } // if max is 0, coef can't be compute
|
||||
const double coef = n / max;
|
||||
for (const auto& data : dataset)
|
||||
{
|
||||
const size_t bin = size_t(std::floor(data * coef));
|
||||
if (bin < n) { res[bin]++; }
|
||||
else if (bin == n) { res[n - 1]++; } // if this data is equal to max
|
||||
}
|
||||
return res;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool FitDistribution(const std::vector<double>& values, double& mu, double& sigma, const std::vector<double>& betas, const double minQuant,
|
||||
const double maxQuant, const double minClean, const double maxDropout, const double stepBound, const double stepScale)
|
||||
{
|
||||
if (values.empty() || betas.empty() || minQuant < 0 || minQuant > 1 || maxQuant < 0 || maxQuant > 1 || minClean < 0 || maxDropout < 0
|
||||
|| stepBound < 0.0001 || stepBound > 0.1 || stepScale < 0.0001 || stepScale > 0.1) { return false; }
|
||||
|
||||
//========== Scales ==========
|
||||
const size_t nBeta = betas.size();
|
||||
// Scales is a vector for each beta as :
|
||||
// scale = beta/(2*gamma(1/beta)) with gamma the function as gamma(n) = (n-1)! for all integer greater than 0
|
||||
std::vector<double> scales;
|
||||
scales.reserve(nBeta);
|
||||
std::transform(betas.begin(), betas.end(), std::back_inserter(scales), [](const double beta) -> double { return beta / (2 * tgamma(1 / beta)); });
|
||||
|
||||
//========== zBounds ==========
|
||||
// zBounds is a vector of lower and upper bounds for each beta as : sign(quants-1/2) * gammaincinv(sign(quants-1/2) * (2*quants-1), 1/beta)^(1/beta);
|
||||
// with gammaincinv the Inverse incomplete gamma function, here quants are the quantiles limit (by default [0.022 0.6])
|
||||
std::vector<std::vector<double>> zBounds(nBeta);
|
||||
const int signMin = sgn(minQuant - 0.5), signMax = sgn(maxQuant - 0.5);
|
||||
const double coefMin = signMin * (2 * minQuant - 1), coefMax = signMax * (2 * maxQuant - 1);
|
||||
|
||||
for (size_t i = 0; i < nBeta; ++i)
|
||||
{
|
||||
if (betas[i] == 0) { zBounds[i] = { 0, 0 }; }
|
||||
else
|
||||
{
|
||||
const double beta = 1 / betas[i];
|
||||
zBounds[i] = {
|
||||
signMin * pow(boost::math::gamma_p_inv(beta, coefMin), beta),
|
||||
signMax * pow(boost::math::gamma_p_inv(beta, coefMax), beta)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//========== Sort Values ==========
|
||||
// We sort values to access quantiles directly
|
||||
const size_t n = values.size();
|
||||
std::vector<double> newValues = values;
|
||||
std::sort(newValues.begin(), newValues.end());
|
||||
|
||||
//========== Compute Index range ==========
|
||||
// Width are the limit if all data is clean or artifacted. It's usefull for the for loop limit and step for each width possible
|
||||
// Bounds are the range of begining value used to compute mu and sigma. It's usefull for the for loop limit and step for first index of value to take
|
||||
// We create Vector for widths and bounds to precompute all round and avoid duplicate indexes in widths or bounds
|
||||
std::vector<size_t> widths = RoundIndexRange(n * (maxQuant - minQuant) * minClean, n * (maxQuant - minQuant), n * stepScale, true, false);
|
||||
std::reverse(widths.begin(), widths.end());
|
||||
const std::vector<size_t> bounds = RoundIndexRange(n * minQuant, n * (minQuant + maxDropout), n * stepBound, true, false);
|
||||
const size_t maxWidth = std::max(widths.front(), widths.back()); // to prevent if widths is in descending or ascending order
|
||||
const size_t nBound = bounds.size();
|
||||
|
||||
//========== Compute Grid (with index range) ==========
|
||||
// Create the Biggest table of data with width in column and bound in row
|
||||
std::vector<std::vector<double>> grid(nBound);
|
||||
std::vector<double> firsts(nBound);
|
||||
for (size_t i = 0; i < nBound; ++i)
|
||||
{
|
||||
grid[i].reserve(maxWidth);
|
||||
const auto first = newValues.begin() + bounds[i];
|
||||
std::copy_n(first, maxWidth, std::back_inserter(grid[i]));
|
||||
firsts[i] = grid[i][0];
|
||||
for (auto& e : grid[i]) { e -= firsts[i]; } // Substract first value on all element
|
||||
}
|
||||
|
||||
//========== Width Loop ==========
|
||||
double bestKl = std::numeric_limits<double>::max();
|
||||
size_t bestBeta = 0, bestId = 0, bestWidth = 0;
|
||||
// for each interval width...
|
||||
for (const auto& w : widths)
|
||||
{
|
||||
const size_t nbins = size_t(std::round(3 * log2(1 + (double(w) / 2))));
|
||||
|
||||
//========== Compute Histogramm ==========
|
||||
std::vector<std::vector<double>> hist(nBound);
|
||||
for (size_t i = 0; i < nBound; ++i)
|
||||
{
|
||||
hist[i].reserve(nbins);
|
||||
std::vector<size_t> tmp = BinHist(std::vector<double>(grid[i].begin(), grid[i].begin() + w), nbins);
|
||||
std::transform(tmp.begin(), tmp.end(), std::back_inserter(hist[i]), [](const size_t e) -> double { return log(e + 0.01); });
|
||||
}
|
||||
|
||||
//========== Beta Loop ==========
|
||||
for (size_t b = 0; b < nBeta; ++b)
|
||||
{
|
||||
//========== Compute Probability ==========
|
||||
std::vector<double> prob(nbins);
|
||||
double sumprob = 0.0;
|
||||
for (size_t i = 0; i < nbins; ++i)
|
||||
{
|
||||
prob[i] = std::exp(-std::pow(std::abs(zBounds[b][0] + (((i + 0.5) / nbins) * (zBounds[b][1] - zBounds[b][0]))), betas[b])) * scales[b];
|
||||
sumprob += prob[i];
|
||||
}
|
||||
if (sumprob != 0) { for (auto& p : prob) { p /= sumprob; } }
|
||||
|
||||
//========== Compute the Kullback-Leibler divergences ==========
|
||||
//kl = sum(prob * (log(prob) - hist)) + log(w));
|
||||
std::vector<double> kl(nBound, log(w));
|
||||
for (size_t i = 0; i < nBound; ++i) { for (size_t j = 0; j < nbins; ++j) { kl[i] += prob[j] * (log(prob[j]) - hist[i][j]); } }
|
||||
|
||||
// Update Parameters
|
||||
auto minIt = std::min_element(kl.begin(), kl.end());
|
||||
if (*minIt < bestKl)
|
||||
{
|
||||
bestKl = *minIt;
|
||||
bestBeta = b;
|
||||
bestId = minIt - kl.begin();
|
||||
bestWidth = w - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double alpha = grid[bestId][bestWidth] / (zBounds[bestBeta][1] - zBounds[bestBeta][0]);
|
||||
double beta = betas[bestBeta];
|
||||
|
||||
mu = firsts[bestId] - zBounds[bestBeta][0] * alpha;
|
||||
sigma = sqrt(alpha * alpha * std::tgamma(3 / beta) / std::tgamma(1 / beta));
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
void sortedEigenVector(const Eigen::MatrixXd& matrix, Eigen::MatrixXd& vectors, std::vector<double>& values, const EMetric /*metric*/)
|
||||
{
|
||||
// Compute Eigen Vector/Values
|
||||
const Eigen::EigenSolver<Eigen::MatrixXd> es(matrix);
|
||||
const Eigen::MatrixXd tmpVec = es.eigenvectors().real(); // It's complex by default but all imaginary part are 0
|
||||
const Eigen::MatrixXd tmpVal = es.eigenvalues().real(); // It's complex by default but all imaginary part are 0
|
||||
values = std::vector<double>(tmpVal.data(), tmpVal.data() + tmpVal.size());
|
||||
|
||||
// Get order of eigen values.
|
||||
std::vector<size_t> idx(values.size());
|
||||
std::iota(idx.begin(), idx.end(), 0);
|
||||
std::stable_sort(idx.begin(), idx.end(), [&values](const size_t i1, const size_t i2) { return values[i1] < values[i2]; });
|
||||
// Sort Eigen Values
|
||||
std::stable_sort(values.begin(), values.end());
|
||||
|
||||
// Sort Eigen Vector
|
||||
vectors = tmpVec; // copy matrix to set size easily
|
||||
for (size_t i = 0; i < size_t(tmpVec.cols()); ++i) { vectors.col(i) = tmpVec.col(idx[i]); }
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,278 @@
|
||||
#include "geometry/artifacts/CASR.hpp"
|
||||
|
||||
#include "geometry/Misc.hpp"
|
||||
#include "geometry/Median.hpp"
|
||||
#include "geometry/Covariance.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
#include "geometry/classifier/IMatrixClassifier.hpp"
|
||||
|
||||
#include <boost/math/special_functions/detail/igamma_inverse.hpp>
|
||||
#include <unsupported/Eigen/MatrixFunctions>
|
||||
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
#include <iostream>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CASR::train(const std::vector<Eigen::MatrixXd>& dataset, const double rejectionLimit)
|
||||
{
|
||||
if (dataset.empty() || dataset[0].size() == 0) { return false; }
|
||||
const size_t n = dataset.size(); // Number of samples
|
||||
m_nChannel = dataset[0].rows(); // Number of channels
|
||||
|
||||
//========== Compute the covariance matrix ==========
|
||||
std::vector<Eigen::MatrixXd> covs(n);
|
||||
//for (size_t i = 0; i < n; ++i) { if (!CovarianceMatrixLWF(dataset[i], covs[i])) { return false; } } // We assume data is centered
|
||||
for (size_t i = 0; i < n; ++i) { if (!CovarianceMatrix(dataset[i], covs[i], EEstimator::LWF, EStandardization::Center)) { return false; } }
|
||||
|
||||
//========== Compute Square Root of Median ==========
|
||||
if (!Median(covs, m_median)) { return false; } // Geometric median independant of metric
|
||||
m_median = m_median.sqrt();
|
||||
|
||||
//========== Compute Eigen vectors ==========
|
||||
Eigen::MatrixXd eigVector;
|
||||
std::vector<double> eigValues;
|
||||
sortedEigenVector(m_median, eigVector, eigValues, m_metric); //Actually only Euclidian metric is implemented
|
||||
|
||||
//========== Compute the ponderate dataset ==========
|
||||
std::vector<Eigen::MatrixXd> newDataset;
|
||||
newDataset.reserve(n);
|
||||
for (const auto& m : dataset) { newDataset.push_back((m.transpose() * eigVector)); } // Multiply by eigen vector (we transpose to have channels in column
|
||||
for (auto& m : newDataset) { m = m.cwiseProduct(m); } // Square new signal
|
||||
|
||||
//========== Compute the "fit" distribution ==========
|
||||
// Compute the RMS of each channel for each sample
|
||||
std::vector<std::vector<double>> rms(m_nChannel, std::vector<double>(n));
|
||||
for (size_t i = 0; i < n; ++i) { for (size_t j = 0; j < m_nChannel; ++j) { rms[j][i] = sqrt(newDataset[i].col(j).mean()); } }
|
||||
|
||||
// Compute the "fit" distribution
|
||||
std::vector<double> mu(m_nChannel, 0.0), sigma(m_nChannel, 0.0);
|
||||
for (size_t i = 0; i < m_nChannel; ++i) { FitDistribution(rms[i], mu[i], sigma[i]); }
|
||||
|
||||
// Compute the threshold Matrix
|
||||
m_threshold = Eigen::MatrixXd::Zero(m_nChannel, m_nChannel);
|
||||
for (size_t i = 0; i < m_nChannel; ++i) { m_threshold(i, i) = mu[i] + rejectionLimit * sigma[i]; }
|
||||
m_threshold *= eigVector.transpose();
|
||||
|
||||
// Initialize Reconstruction matrix and trivial
|
||||
m_r = Eigen::MatrixXd::Identity(m_nChannel, m_nChannel);
|
||||
m_trivial = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CASR::process(const Eigen::MatrixXd& in, Eigen::MatrixXd& out)
|
||||
{
|
||||
// Check if input data is compatible with training data and if we don't limit so much the reconstruction
|
||||
out = in;
|
||||
if (size_t(out.rows()) != m_nChannel) { return false; }
|
||||
const size_t begin = size_t((1.0 - m_maxChannel) * double(m_nChannel)); // We define the number of channels to non reconstruct
|
||||
if (begin == m_nChannel) { return true; }
|
||||
if (m_r.size() == 0) { m_r = Eigen::MatrixXd::Identity(m_nChannel, m_nChannel); }
|
||||
|
||||
// Compute Covariance matrix
|
||||
Eigen::MatrixXd cov;
|
||||
if (!CovarianceMatrix(in, cov, EEstimator::LWF, EStandardization::Center)) { return false; }
|
||||
if (m_cov.size() == 0) { m_cov = cov; } // if first time
|
||||
else { if (!Mean({ m_cov, cov }, m_cov, m_metric)) { return false; } } // else mean of the both
|
||||
|
||||
// Compute Eigen vector & values
|
||||
Eigen::MatrixXd eigVector;
|
||||
std::vector<double> eigValues;
|
||||
sortedEigenVector(m_cov, eigVector, eigValues, m_metric);
|
||||
|
||||
// Check if eigen values is over threshold computed during train (ponderated by eigen vector)
|
||||
Eigen::MatrixXd threshold = (m_threshold * eigVector).cwiseAbs2();
|
||||
bool trivial = true;
|
||||
std::vector<bool> keep(m_nChannel, true);
|
||||
for (size_t i = begin; i < m_nChannel; ++i)
|
||||
{
|
||||
if (eigValues[i] >= threshold.col(i).sum())
|
||||
{
|
||||
keep[i] = false;
|
||||
trivial = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if All channels are clean
|
||||
if (trivial) { m_r = Eigen::MatrixXd::Identity(m_nChannel, m_nChannel); }
|
||||
else // if not...
|
||||
{
|
||||
// Compute the reconstruction matrix with bad channels
|
||||
Eigen::MatrixXd tmp = eigVector.transpose() * m_median;
|
||||
for (size_t i = begin; i < m_nChannel; ++i) { if (!keep[i]) { tmp.row(i).setZero(); } }
|
||||
const Eigen::MatrixXd newR = m_median * tmp.completeOrthogonalDecomposition().pseudoInverse() * eigVector.transpose();
|
||||
|
||||
if (!m_trivial)
|
||||
{
|
||||
// Compute blend values for the samples
|
||||
const size_t nSample = in.cols();
|
||||
std::vector<double> blend(nSample);
|
||||
std::iota(blend.begin(), blend.end(), 1); // Range 1 to nSample (inclued)
|
||||
for (auto& b : blend) { b = (1 - cos(M_PI * (b / double(nSample)))) / 2.0; }
|
||||
|
||||
// Apply reconstruction ponderate by the blend (we considere the old reconstruction matrix for the second part)
|
||||
Eigen::MatrixXd t1 = newR * in;
|
||||
Eigen::MatrixXd t2 = m_r * in;
|
||||
for (size_t i = 0; i < nSample; ++i) { out.col(i) = (blend[i] * t1.col(i)) + ((1 - blend[i]) * t2.col(i)); }
|
||||
}
|
||||
m_r = newR; // Update the reconstruction matrix
|
||||
}
|
||||
m_trivial = trivial;
|
||||
return true;
|
||||
}
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
bool CASR::setMatrices(const Eigen::MatrixXd& median, const Eigen::MatrixXd& threshold, const Eigen::MatrixXd& reconstruct,
|
||||
const Eigen::MatrixXd& covariance)
|
||||
{
|
||||
if (!IsSquare(median) || !HaveSameSize(median, threshold)
|
||||
|| (reconstruct.size() != 0 && !HaveSameSize(median, reconstruct))
|
||||
|| (covariance.size() != 0 && !HaveSameSize(median, covariance)))
|
||||
{
|
||||
std::cout << "All matrices must be square with same size (or empty for reconstruct and covariance matrix" << std::endl;
|
||||
return false;
|
||||
}
|
||||
m_nChannel = median.rows();
|
||||
m_median = median;
|
||||
m_threshold = threshold;
|
||||
m_r = reconstruct.size() != 0 ? reconstruct : Eigen::MatrixXd::Identity(m_nChannel, m_nChannel);
|
||||
m_cov = covariance;
|
||||
m_trivial = true;
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//***********************
|
||||
//***** XML Manager *****
|
||||
//***********************
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CASR::saveXML(const std::string& filename) const
|
||||
{
|
||||
tinyxml2::XMLDocument doc;
|
||||
// Create Root
|
||||
tinyxml2::XMLNode* root = doc.NewElement("ASR"); // Create root node
|
||||
doc.InsertFirstChild(root); // Add root to XML
|
||||
|
||||
tinyxml2::XMLElement* data = doc.NewElement("ASR-data"); // Create data node
|
||||
data->SetAttribute("metric", toString(m_metric).c_str()); // Set attribute metric
|
||||
data->SetAttribute("nChannel", int(m_nChannel)); // Set attribute nCHannel
|
||||
data->SetAttribute("maxChannel", int(m_maxChannel)); // Set attribute nCHannel
|
||||
data->SetAttribute("trivial", m_trivial); // Set attribute nCHannel
|
||||
|
||||
tinyxml2::XMLElement* median = doc.NewElement("Median"); // Create Median node
|
||||
if (!IMatrixClassifier::saveMatrix(median, m_median)) { return false; } // Save Median Matrix
|
||||
data->InsertEndChild(median); // Add Median node to data node
|
||||
|
||||
tinyxml2::XMLElement* threshold = doc.NewElement("Threshold"); // Create Median node
|
||||
if (!IMatrixClassifier::saveMatrix(threshold, m_threshold)) { return false; } // Save Median Matrix
|
||||
data->InsertEndChild(threshold); // Add Median node to data node
|
||||
|
||||
tinyxml2::XMLElement* r = doc.NewElement("R"); // Create Median node
|
||||
if (!IMatrixClassifier::saveMatrix(r, m_r)) { return false; } // Save Median Matrix
|
||||
data->InsertEndChild(r); // Add Median node to data node
|
||||
|
||||
tinyxml2::XMLElement* cov = doc.NewElement("Cov"); // Create Median node
|
||||
if (!IMatrixClassifier::saveMatrix(cov, m_cov)) { return false; } // Save Median Matrix
|
||||
data->InsertEndChild(cov); // Add Median node to data node
|
||||
|
||||
root->InsertEndChild(data); // Add data to root
|
||||
return doc.SaveFile(filename.c_str()) == 0; // save XML (if != 0 it means error)
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CASR::loadXML(const std::string& filename)
|
||||
{
|
||||
// Load File
|
||||
tinyxml2::XMLDocument xmlDoc;
|
||||
if (xmlDoc.LoadFile(filename.c_str()) != 0) { return false; } // Check File Exist and Loading
|
||||
|
||||
// Load Root
|
||||
tinyxml2::XMLNode* root = xmlDoc.FirstChild(); // Get Root Node
|
||||
if (root == nullptr) { return false; } // Check Root Node Exist
|
||||
|
||||
// Load Data
|
||||
tinyxml2::XMLElement* data = root->FirstChildElement("ASR-data"); // Get Data Node
|
||||
if (data == nullptr) { return false; } // Check Root Node Exist
|
||||
m_metric = StringToMetric(std::string(data->Attribute("metric")));
|
||||
m_nChannel = data->IntAttribute("nChannel");
|
||||
m_maxChannel = data->IntAttribute("maxChannel");
|
||||
m_trivial = data->BoolAttribute("trivial");
|
||||
|
||||
tinyxml2::XMLElement* element = data->FirstChildElement("Median"); // Get Median Node
|
||||
if (element == nullptr) { return false; } // Check if Node Exist
|
||||
if (!IMatrixClassifier::loadMatrix(element, m_median)) { return false; } // Load Median Matrix
|
||||
|
||||
element = data->FirstChildElement("Threshold"); // Get Threshold Node
|
||||
if (element == nullptr) { return false; } // Check if Node Exist
|
||||
if (!IMatrixClassifier::loadMatrix(element, m_threshold)) { return false; } // Load Threshold Matrix
|
||||
|
||||
element = data->FirstChildElement("R"); // Get R Node
|
||||
if (element == nullptr) { return false; } // Check if Node Exist
|
||||
if (!IMatrixClassifier::loadMatrix(element, m_r)) { return false; } // Load R Matrix
|
||||
|
||||
element = data->FirstChildElement("Cov"); // Get Cov Node
|
||||
if (element == nullptr) { return false; } // Check if Node Exist
|
||||
if (!IMatrixClassifier::loadMatrix(element, m_cov)) { return false; } // Load Cov Matrix
|
||||
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//*****************************
|
||||
//***** Override Operator *****
|
||||
//*****************************
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CASR::isEqual(const CASR& obj, const double precision) const
|
||||
{
|
||||
return m_metric == obj.m_metric && m_nChannel == obj.m_nChannel
|
||||
&& abs(m_maxChannel - obj.m_maxChannel) < precision && m_trivial == obj.m_trivial
|
||||
&& AreEquals(m_median, obj.m_median, precision) && AreEquals(m_threshold, obj.m_threshold, precision)
|
||||
&& AreEquals(m_r, obj.m_r, precision) && AreEquals(m_cov, obj.m_cov, precision);
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CASR::copy(const CASR& obj)
|
||||
{
|
||||
m_metric = obj.m_metric;
|
||||
m_nChannel = obj.m_nChannel;
|
||||
m_maxChannel = obj.m_maxChannel;
|
||||
m_trivial = obj.m_trivial;
|
||||
m_median = obj.m_median;
|
||||
m_threshold = obj.m_threshold;
|
||||
m_r = obj.m_r;
|
||||
m_cov = obj.m_cov;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
std::stringstream CASR::print() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Metric : " << toString(m_metric) << std::endl;
|
||||
if (m_nChannel == 0) { ss << "Training not done" << std::endl; }
|
||||
else
|
||||
{
|
||||
ss << "Training done." << std::endl;
|
||||
ss << size_t(m_maxChannel * double(m_nChannel)) << "/" << m_nChannel << " channels can be reconstruted." << std::endl;
|
||||
ss << "Median matrix is : " << std::endl << m_median << std::endl;
|
||||
ss << "Threshold matrix is : " << std::endl << m_threshold << std::endl;
|
||||
if (m_cov.size() == 0) { ss << "No process launched yet." << std::endl; }
|
||||
else
|
||||
{
|
||||
ss << "Last sample " << (m_trivial ? "was" : "wasn't") << " trivial." << std::endl;
|
||||
ss << "Last Reconstruction Matrix : " << std::endl << m_r << std::endl;
|
||||
ss << "Last Covariance Matrix : " << std::endl << m_cov << std::endl;
|
||||
}
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,146 @@
|
||||
#include "geometry/classifier/CBias.hpp"
|
||||
#include "geometry/classifier/IMatrixClassifier.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include "geometry/Geodesic.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions> // SQRT of Matrix
|
||||
#include <iostream>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::computeBias(const std::vector<std::vector<Eigen::MatrixXd>>& dataset, const EMetric metric) { return computeBias(Vector2DTo1D(dataset), metric); }
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::computeBias(const std::vector<Eigen::MatrixXd>& dataset, const EMetric metric)
|
||||
{
|
||||
if (!Mean(dataset, m_bias, metric)) { return false; } // Compute Bias reference
|
||||
m_biasIS = m_bias.sqrt().inverse(); // Inverse Square root of Bias matrix => isR
|
||||
m_n = 0;
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CBias::applyBias(const std::vector<std::vector<Eigen::MatrixXd>>& in, std::vector<std::vector<Eigen::MatrixXd>>& out)
|
||||
{
|
||||
const size_t n = in.size();
|
||||
out.resize(n);
|
||||
for (size_t i = 0; i < n; ++i) { applyBias(in[i], out[i]); }
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CBias::applyBias(const std::vector<Eigen::MatrixXd>& in, std::vector<Eigen::MatrixXd>& out)
|
||||
{
|
||||
const size_t n = in.size();
|
||||
out.resize(n);
|
||||
for (size_t i = 0; i < n; ++i) { applyBias(in[i], out[i]); }
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CBias::applyBias(const Eigen::MatrixXd& in, Eigen::MatrixXd& out) { out = m_biasIS * in * m_biasIS.transpose(); }
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CBias::updateBias(const Eigen::MatrixXd& sample, const EMetric metric)
|
||||
{
|
||||
m_n++; // Update number of classify
|
||||
if (m_n == 1) { m_bias = sample; } // At the first pass we reinitialize the Bias
|
||||
else { Geodesic(m_bias, sample, m_bias, metric, 1.0 / m_n); }
|
||||
m_biasIS = m_bias.sqrt().inverse(); // Inverse Square root of Bias matrix => isR
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CBias::setBias(const Eigen::MatrixXd& bias)
|
||||
{
|
||||
m_bias = bias;
|
||||
m_biasIS = m_bias.sqrt().inverse();
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::saveXML(const std::string& filename) const
|
||||
{
|
||||
tinyxml2::XMLDocument xmlDoc;
|
||||
// Create Root
|
||||
tinyxml2::XMLNode* root = xmlDoc.NewElement("Bias"); // Create root node
|
||||
xmlDoc.InsertFirstChild(root); // Add root to XML
|
||||
|
||||
tinyxml2::XMLElement* data = xmlDoc.NewElement("Bias-data"); // Create data node
|
||||
if (!saveAdditional(xmlDoc, data)) { return false; } // Save Optionnal Informations
|
||||
|
||||
root->InsertEndChild(data); // Add data to root
|
||||
return xmlDoc.SaveFile(filename.c_str()) == 0; // save XML (if != 0 it means error)
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::loadXML(const std::string& filename)
|
||||
{
|
||||
// Load File
|
||||
tinyxml2::XMLDocument xmlDoc;
|
||||
if (xmlDoc.LoadFile(filename.c_str()) != 0) { return false; } // Check File Exist and Loading
|
||||
|
||||
// Load Root
|
||||
tinyxml2::XMLNode* root = xmlDoc.FirstChild(); // Get Root Node
|
||||
if (root == nullptr) { return false; } // Check Root Node Exist
|
||||
|
||||
// Load Data
|
||||
tinyxml2::XMLElement* data = root->FirstChildElement("Bias-data"); // Get Data Node
|
||||
if (!loadAdditional(data)) { return false; } // Load Optionnal Informations
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::saveAdditional(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const
|
||||
{
|
||||
tinyxml2::XMLElement* bias = doc.NewElement("Bias"); // Create Bias node
|
||||
bias->SetAttribute("n", int(m_n)); // Set attribute class number of trials
|
||||
if (!IMatrixClassifier::saveMatrix(bias, m_bias)) { return false; } // Save class
|
||||
data->InsertEndChild(bias); // Add class node to data node
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::loadAdditional(tinyxml2::XMLElement* data)
|
||||
{
|
||||
tinyxml2::XMLElement* bias = data->FirstChildElement("Bias"); // Get LDA Weight Node
|
||||
m_n = bias->IntAttribute("n"); // Get the number of Trials for this class
|
||||
if (!IMatrixClassifier::loadMatrix(bias, m_bias)) { return false; } // Load Reference Matrix
|
||||
m_biasIS = m_bias.sqrt().inverse();
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::isEqual(const CBias& obj, const double precision) const { return AreEquals(m_bias, obj.m_bias, precision) && m_n == obj.m_n; }
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CBias::copy(const CBias& obj)
|
||||
{
|
||||
m_bias = obj.m_bias;
|
||||
m_biasIS = obj.m_biasIS;
|
||||
m_n = obj.m_n;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
std::stringstream CBias::print() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Number of Classification : " << m_n << std::endl;
|
||||
ss << "Bias Matrix : ";
|
||||
if (m_bias.size() != 0) { ss << std::endl << m_bias.format(MATRIX_FORMAT) << std::endl; }
|
||||
else { ss << "Not Computed" << std::endl; }
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
#include "geometry/classifier/CMatrixClassifierFgMDM.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
CMatrixClassifierFgMDM::~CMatrixClassifierFgMDM()
|
||||
{
|
||||
for (auto& v : m_dataset) { v.clear(); }
|
||||
m_dataset.clear();
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDM::train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset)
|
||||
{
|
||||
m_dataset = dataset;
|
||||
return train();
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDM::classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance,
|
||||
std::vector<double>& probability, const EAdaptations adaptation, const size_t& realClassId)
|
||||
{
|
||||
if (!CMatrixClassifierFgMDMRT::classify(sample, classId, distance, probability, EAdaptations::None)) { return false; }
|
||||
|
||||
// Adaptation
|
||||
if (adaptation == EAdaptations::None) { return true; }
|
||||
// Get class id for adaptation and increase number of trials, expected if supervised, predicted if unsupervised
|
||||
const size_t id = adaptation == EAdaptations::Supervised ? realClassId : classId;
|
||||
if (id >= m_nbClass) { return false; } // Check id (if supervised and bad input)
|
||||
m_nbTrials[id]++; // Update number of trials for the class id
|
||||
m_dataset[id].push_back(sample); // Update the dataset
|
||||
|
||||
// Retrain
|
||||
return train();
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
#include "geometry/classifier/CMatrixClassifierFgMDMRT.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include "geometry/Featurization.hpp"
|
||||
#include "geometry/Classification.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRT::train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset)
|
||||
{
|
||||
if (dataset.empty()) { return false; }
|
||||
if (!Mean(Vector2DTo1D(dataset), m_ref, EMetric::Riemann)) { return false; } // Compute Reference matrix
|
||||
|
||||
// Transform to the Tangent Space
|
||||
const size_t nbClass = dataset.size();
|
||||
std::vector<std::vector<Eigen::RowVectorXd>> tsSample(nbClass);
|
||||
for (size_t k = 0; k < nbClass; ++k)
|
||||
{
|
||||
const size_t nbTrials = dataset[k].size();
|
||||
tsSample[k].resize(nbTrials);
|
||||
for (size_t i = 0; i < nbTrials; ++i) { if (!TangentSpace(dataset[k][i], tsSample[k][i], m_ref)) { return false; } }
|
||||
}
|
||||
|
||||
// Compute FgDA Weight
|
||||
if (!FgDACompute(tsSample, m_weight)) { return false; }
|
||||
|
||||
// Convert dataset
|
||||
std::vector<std::vector<Eigen::MatrixXd>> newDataset(nbClass);
|
||||
std::vector<std::vector<Eigen::RowVectorXd>> filtered(nbClass);
|
||||
for (size_t k = 0; k < nbClass; ++k)
|
||||
{
|
||||
const size_t nbTrials = dataset[k].size();
|
||||
newDataset[k].resize(nbTrials);
|
||||
filtered[k].resize(nbTrials);
|
||||
for (size_t i = 0; i < nbTrials; ++i)
|
||||
{
|
||||
if (!FgDAApply(tsSample[k][i], filtered[k][i], m_weight)) { return false; } // Apply Filter
|
||||
if (!UnTangentSpace(filtered[k][i], newDataset[k][i], m_ref)) { return false; } // Return to Matrix Space
|
||||
}
|
||||
}
|
||||
|
||||
return CMatrixClassifierMDM::train(newDataset); // Train MDM
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRT::classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance,
|
||||
std::vector<double>& probability, const EAdaptations adaptation, const size_t& realClassId)
|
||||
{
|
||||
Eigen::RowVectorXd tsSample, filtered;
|
||||
Eigen::MatrixXd newSample;
|
||||
|
||||
if (!TangentSpace(sample, tsSample, m_ref)) { return false; } // Transform to the Tangent Space
|
||||
if (!FgDAApply(tsSample, filtered, m_weight)) { return false; } // Apply Filter
|
||||
if (!UnTangentSpace(filtered, newSample, m_ref)) { return false; } // Return to Matrix Space
|
||||
return CMatrixClassifierMDM::classify(newSample, classId, distance, probability, adaptation, realClassId);
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRT::isEqual(const CMatrixClassifierFgMDMRT& obj, const double precision) const
|
||||
{
|
||||
if (!CMatrixClassifierMDM::isEqual(obj, precision)) { return false; } // Compare base members
|
||||
if (!AreEquals(m_ref, obj.m_ref, precision)) { return false; } // Compare Reference
|
||||
if (!AreEquals(m_weight, obj.m_weight, precision)) { return false; } // Compare Weight
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CMatrixClassifierFgMDMRT::copy(const CMatrixClassifierFgMDMRT& obj)
|
||||
{
|
||||
CMatrixClassifierMDM::copy(obj);
|
||||
m_ref = obj.m_ref;
|
||||
m_weight = obj.m_weight;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRT::saveAdditional(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const
|
||||
{
|
||||
// Save Reference
|
||||
tinyxml2::XMLElement* reference = doc.NewElement("Reference"); // Create Reference node
|
||||
if (!saveMatrix(reference, m_ref)) { return false; } // Save class
|
||||
data->InsertEndChild(reference); // Add class node to data node
|
||||
|
||||
// Save Weight
|
||||
tinyxml2::XMLElement* weight = doc.NewElement("Weight"); // Create LDA Weight node
|
||||
if (!saveMatrix(weight, m_weight)) { return false; } // Save class
|
||||
data->InsertEndChild(weight); // Add class node to data node
|
||||
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRT::loadAdditional(tinyxml2::XMLElement* data)
|
||||
{
|
||||
// Load Reference
|
||||
tinyxml2::XMLElement* ref = data->FirstChildElement("Reference"); // Get Reference Node
|
||||
if (!loadMatrix(ref, m_ref)) { return false; } // Load Reference Matrix
|
||||
|
||||
// Load Weight
|
||||
tinyxml2::XMLElement* weight = data->FirstChildElement("Weight"); // Get LDA Weight Node
|
||||
return loadMatrix(weight, m_weight); // Load LDA Weight Matrix
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
std::stringstream CMatrixClassifierFgMDMRT::printAdditional() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Reference matrix : " << std::endl << m_ref.format(MATRIX_FORMAT) << std::endl; // Reference
|
||||
ss << "Weight matrix : " << std::endl << m_weight.format(MATRIX_FORMAT) << std::endl; // Print Weight
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
#include "geometry/classifier/CMatrixClassifierFgMDMRTRebias.hpp"
|
||||
|
||||
#include "geometry/Mean.hpp"
|
||||
#include "geometry/Covariance.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions> // SQRT of Matrix
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//**********************
|
||||
//***** Classifier *****
|
||||
//**********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRTRebias::train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset)
|
||||
{
|
||||
if (!m_bias.computeBias(dataset, m_metric)) { return false; }
|
||||
std::vector<std::vector<Eigen::MatrixXd>> newDataset;
|
||||
m_bias.applyBias(dataset, newDataset);
|
||||
if (!CMatrixClassifierFgMDMRT::train(newDataset)) { return false; } // Train FgMDM
|
||||
const Eigen::MatrixXd identity = Eigen::MatrixXd::Identity(m_ref.rows(), m_ref.cols()); // Identity matrix
|
||||
if (AreEquals(m_ref, identity)) { m_ref = identity; } // Normally it's always the case with Identity matrix we simplify future operation
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRTRebias::classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance,
|
||||
std::vector<double>& probability, const EAdaptations adaptation, const size_t& realClassId)
|
||||
{
|
||||
if (!IsSquare(sample)) { return false; } // Verification if it's a square matrix
|
||||
Eigen::MatrixXd newSample;
|
||||
m_bias.applyBias(sample, newSample);
|
||||
m_bias.updateBias(sample, m_metric);
|
||||
return CMatrixClassifierFgMDMRT::classify(newSample, classId, distance, probability, adaptation, realClassId);
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//***********************
|
||||
//***** XML Manager *****
|
||||
//***********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRTRebias::saveAdditional(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const
|
||||
{
|
||||
if (!CMatrixClassifierFgMDMRT::saveAdditional(doc, data)) { return false; }
|
||||
if (!m_bias.saveAdditional(doc, data)) { return false; }
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRTRebias::loadAdditional(tinyxml2::XMLElement* data)
|
||||
{
|
||||
if (!CMatrixClassifierFgMDMRT::loadAdditional(data)) { return false; }
|
||||
if (!m_bias.loadAdditional(data)) { return false; }
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//*****************************
|
||||
//***** Override Operator *****
|
||||
//*****************************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRTRebias::isEqual(const CMatrixClassifierFgMDMRTRebias& obj, const double precision) const
|
||||
{
|
||||
return CMatrixClassifierFgMDMRT::isEqual(obj, precision) && m_bias == obj.m_bias;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CMatrixClassifierFgMDMRTRebias::copy(const CMatrixClassifierFgMDMRTRebias& obj)
|
||||
{
|
||||
CMatrixClassifierFgMDMRT::copy(obj);
|
||||
m_bias = obj.m_bias;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
std::stringstream CMatrixClassifierFgMDMRTRebias::printAdditional() const
|
||||
{
|
||||
std::stringstream ss = CMatrixClassifierFgMDMRT::printAdditional();
|
||||
ss << m_bias;
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
#include "geometry/classifier/CMatrixClassifierMDM.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
#include "geometry/Distance.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include "geometry/Geodesic.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions> // SQRT of Matrix
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//***********************
|
||||
//***** Constructor *****
|
||||
//***********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
CMatrixClassifierMDM::CMatrixClassifierMDM(const size_t nbClass, const EMetric metric)
|
||||
{
|
||||
CMatrixClassifierMDM::setClassCount(nbClass);
|
||||
m_metric = metric;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
CMatrixClassifierMDM::~CMatrixClassifierMDM()
|
||||
{
|
||||
m_means.clear();
|
||||
m_nbTrials.clear();
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//**********************
|
||||
//***** Classifier *****
|
||||
//**********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CMatrixClassifierMDM::setClassCount(const size_t nbClass)
|
||||
{
|
||||
if (m_nbClass != nbClass || m_means.size() != nbClass || m_nbTrials.size() != nbClass)
|
||||
{
|
||||
IMatrixClassifier::setClassCount(nbClass);
|
||||
m_means.resize(m_nbClass);
|
||||
m_nbTrials.resize(nbClass);
|
||||
}
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDM::train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset)
|
||||
{
|
||||
if (dataset.empty()) { return false; }
|
||||
setClassCount(dataset.size()); // Change the number of classes if needed
|
||||
for (size_t k = 0; k < m_nbClass; ++k) // for each class
|
||||
{
|
||||
if (!Mean(dataset[k], m_means[k], m_metric)) { return false; } // Compute the mean of each class
|
||||
m_nbTrials[k] = dataset[k].size();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDM::classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance,
|
||||
std::vector<double>& probability, const EAdaptations adaptation, const size_t& realClassId)
|
||||
{
|
||||
if (!IsSquare(sample)) { return false; } // Verification if it's a square matrix
|
||||
double distMin = std::numeric_limits<double>::max(); // Init of distance min
|
||||
|
||||
// Compute Distances
|
||||
distance.resize(m_nbClass);
|
||||
for (size_t k = 0; k < m_nbClass; ++k)
|
||||
{
|
||||
distance[k] = Distance(sample, m_means[k], m_metric);
|
||||
if (distMin > distance[k])
|
||||
{
|
||||
classId = k;
|
||||
distMin = distance[k];
|
||||
}
|
||||
}
|
||||
|
||||
// Compute Probabilities (personnal method)
|
||||
probability.resize(m_nbClass);
|
||||
double sumProbability = 0.0;
|
||||
for (size_t k = 0; k < m_nbClass; ++k)
|
||||
{
|
||||
probability[k] = distMin / distance[k];
|
||||
sumProbability += probability[k];
|
||||
}
|
||||
|
||||
for (auto& p : probability) { p /= sumProbability; }
|
||||
|
||||
// Adaptation
|
||||
if (adaptation == EAdaptations::None) { return true; }
|
||||
// Get class id for adaptation and increase number of trials, expected if supervised, predicted if unsupervised
|
||||
const size_t id = adaptation == EAdaptations::Supervised ? realClassId : classId;
|
||||
if (id >= m_nbClass) { return false; } // Check id (if supervised and bad input)
|
||||
m_nbTrials[id]++; // Update number of trials for the class id
|
||||
return Geodesic(m_means[id], sample, m_means[id], m_metric, 1.0 / m_nbTrials[id]);
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//***********************
|
||||
//***** XML Manager *****
|
||||
//***********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDM::saveClasses(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const
|
||||
{
|
||||
for (size_t k = 0; k < m_nbClass; ++k) // for each class
|
||||
{
|
||||
tinyxml2::XMLElement* element = doc.NewElement("Class"); // Create class node
|
||||
element->SetAttribute("class-id", int(k)); // Set attribute class id (0 to K)
|
||||
element->SetAttribute("nb-trials", int(m_nbTrials[k])); // Set attribute class number of trials
|
||||
if (!saveMatrix(element, m_means[k])) { return false; } // Save class Matrix Reference
|
||||
data->InsertEndChild(element); // Add class node to data node
|
||||
}
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDM::loadClasses(tinyxml2::XMLElement* data)
|
||||
{
|
||||
tinyxml2::XMLElement* element = data->FirstChildElement("Class"); // Get First Class Node
|
||||
for (size_t k = 0; k < m_nbClass; ++k) // for each class
|
||||
{
|
||||
if (element == nullptr) { return false; } // Check if Node Exist
|
||||
const size_t idx = element->IntAttribute("class-id"); // Get Id (normally idx == k)
|
||||
if (idx != k) { return false; } // Check Id
|
||||
m_nbTrials[k] = element->IntAttribute("nb-trials"); // Get the number of Trials for this class
|
||||
if (!loadMatrix(element, m_means[k])) { return false; } // Load Class Matrix
|
||||
element = element->NextSiblingElement("Class"); // Next Class
|
||||
}
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//*****************************
|
||||
//***** Override Operator *****
|
||||
//*****************************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
std::stringstream CMatrixClassifierMDM::printClasses() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i < m_nbClass; ++i)
|
||||
{
|
||||
ss << "Mean of class " << i << " (" << m_nbTrials[i] << " trials): ";
|
||||
if (m_means[i].size() != 0) { ss << std::endl << m_means[i].format(MATRIX_FORMAT) << std::endl; }
|
||||
else { ss << "Not Computed" << std::endl; }
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDM::isEqual(const CMatrixClassifierMDM& obj, const double precision) const
|
||||
{
|
||||
if (!IMatrixClassifier::isEqual(obj)) { return false; }
|
||||
if (m_nbClass != obj.getClassCount()) { return false; }
|
||||
for (size_t i = 0; i < m_nbClass; ++i)
|
||||
{
|
||||
if (!AreEquals(m_means[i], obj.m_means[i], precision)) { return false; }
|
||||
if (m_nbTrials[i] != obj.m_nbTrials[i]) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CMatrixClassifierMDM::copy(const CMatrixClassifierMDM& obj)
|
||||
{
|
||||
IMatrixClassifier::copy(obj);
|
||||
setClassCount(m_nbClass);
|
||||
for (size_t i = 0; i < m_nbClass; ++i)
|
||||
{
|
||||
m_means[i] = obj.m_means[i];
|
||||
m_nbTrials[i] = obj.m_nbTrials[i];
|
||||
}
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
#include "geometry/classifier/CMatrixClassifierMDMRebias.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions> // SQRT of Matrix
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//**********************
|
||||
//***** Classifier *****
|
||||
//**********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDMRebias::train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset)
|
||||
{
|
||||
if (!m_bias.computeBias(dataset, m_metric)) { return false; }
|
||||
std::vector<std::vector<Eigen::MatrixXd>> newDataset;
|
||||
m_bias.applyBias(dataset, newDataset);
|
||||
return CMatrixClassifierMDM::train(newDataset); // Train MDM
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDMRebias::classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance,
|
||||
std::vector<double>& probability, const EAdaptations adaptation, const size_t& realClassId)
|
||||
{
|
||||
if (!IsSquare(sample)) { return false; } // Verification if it's a square matrix
|
||||
Eigen::MatrixXd newSample;
|
||||
m_bias.applyBias(sample, newSample);
|
||||
m_bias.updateBias(sample, m_metric);
|
||||
return CMatrixClassifierMDM::classify(newSample, classId, distance, probability, adaptation, realClassId);
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//***********************
|
||||
//***** XML Manager *****
|
||||
//***********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDMRebias::saveAdditional(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const
|
||||
{
|
||||
if (!CMatrixClassifierMDM::saveAdditional(doc, data)) { return false; }
|
||||
if (!m_bias.saveAdditional(doc, data)) { return false; }
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDMRebias::loadAdditional(tinyxml2::XMLElement* data)
|
||||
{
|
||||
if (!CMatrixClassifierMDM::loadAdditional(data)) { return false; }
|
||||
if (!m_bias.loadAdditional(data)) { return false; }
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//*****************************
|
||||
//***** Override Operator *****
|
||||
//*****************************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDMRebias::isEqual(const CMatrixClassifierMDMRebias& obj, const double precision) const
|
||||
{
|
||||
return CMatrixClassifierMDM::isEqual(obj, precision) && m_bias == obj.m_bias;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CMatrixClassifierMDMRebias::copy(const CMatrixClassifierMDMRebias& obj)
|
||||
{
|
||||
CMatrixClassifierMDM::copy(obj);
|
||||
m_bias = obj.m_bias;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
std::stringstream CMatrixClassifierMDMRebias::printAdditional() const
|
||||
{
|
||||
std::stringstream ss = CMatrixClassifierMDM::printAdditional();
|
||||
ss << m_bias;
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
#include "geometry/classifier/IMatrixClassifier.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//***********************
|
||||
//***** Constructor *****
|
||||
//***********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
IMatrixClassifier::IMatrixClassifier(const size_t nbClass, const EMetric metric)
|
||||
{
|
||||
IMatrixClassifier::setClassCount(nbClass);
|
||||
m_metric = metric;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void IMatrixClassifier::setClassCount(const size_t nbClass) { m_nbClass = nbClass; }
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::classify(const Eigen::MatrixXd& sample, size_t& classId, const EAdaptations adaptation, const size_t& realClassId)
|
||||
{
|
||||
std::vector<double> distance, probability;
|
||||
return classify(sample, classId, distance, probability, adaptation, realClassId);
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//***********************
|
||||
//***** XML Manager *****
|
||||
//***********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::saveXML(const std::string& filename) const
|
||||
{
|
||||
tinyxml2::XMLDocument xmlDoc;
|
||||
// Create Root
|
||||
tinyxml2::XMLNode* root = xmlDoc.NewElement("Classifier"); // Create root node
|
||||
xmlDoc.InsertFirstChild(root); // Add root to XML
|
||||
|
||||
tinyxml2::XMLElement* data = xmlDoc.NewElement("Classifier-data"); // Create data node
|
||||
if (!saveHeader(data)) { return false; } // Save Header attribute
|
||||
if (!saveAdditional(xmlDoc, data)) { return false; } // Save Optionnal Informations
|
||||
if (!saveClasses(xmlDoc, data)) { return false; } // Save Classes
|
||||
|
||||
root->InsertEndChild(data); // Add data to root
|
||||
return xmlDoc.SaveFile(filename.c_str()) == 0; // save XML (if != 0 it means error)
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::loadXML(const std::string& filename)
|
||||
{
|
||||
// Load File
|
||||
tinyxml2::XMLDocument xmlDoc;
|
||||
if (xmlDoc.LoadFile(filename.c_str()) != 0) { return false; } // Check File Exist and Loading
|
||||
|
||||
// Load Root
|
||||
tinyxml2::XMLNode* root = xmlDoc.FirstChild(); // Get Root Node
|
||||
if (root == nullptr) { return false; } // Check Root Node Exist
|
||||
|
||||
// Load Data
|
||||
tinyxml2::XMLElement* data = root->FirstChildElement("Classifier-data"); // Get Data Node
|
||||
if (data == nullptr) { return false; } // Check Root Node Exist
|
||||
if (!loadHeader(data)) { return false; } // Load Header attribute
|
||||
if (!loadAdditional(data)) { return false; } // Load Optionnal Informations
|
||||
if (!loadClasses(data)) { return false; } // Load Classes
|
||||
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::convertMatrixToXMLFormat(const Eigen::MatrixXd& in, std::stringstream& out)
|
||||
{
|
||||
out << in.format(MATRIX_FORMAT);
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::convertXMLFormatToMatrix(std::stringstream& in, Eigen::MatrixXd& out, const size_t rows, const size_t cols)
|
||||
{
|
||||
out = Eigen::MatrixXd::Identity(rows, cols); // Init With Identity Matrix (in case of)
|
||||
for (size_t i = 0; i < rows; ++i) // Fill Matrix
|
||||
{
|
||||
for (size_t j = 0; j < cols; ++j) { in >> out(i, j); }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::saveMatrix(tinyxml2::XMLElement* element, const Eigen::MatrixXd& matrix)
|
||||
{
|
||||
element->SetAttribute("size", int(matrix.rows())); // Set Matrix size NxN
|
||||
std::stringstream ss;
|
||||
convertMatrixToXMLFormat(matrix, ss);
|
||||
element->SetText(ss.str().c_str()); // Write Means Value
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::loadMatrix(tinyxml2::XMLElement* element, Eigen::MatrixXd& matrix)
|
||||
{
|
||||
const size_t size = element->IntAttribute("size"); // Get number of row/col
|
||||
if (size == 0) { return true; }
|
||||
std::stringstream ss(element->GetText()); // String stream to parse Matrix value
|
||||
convertXMLFormatToMatrix(ss, matrix, size, size);
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//*****************************
|
||||
//***** Override Operator *****
|
||||
//*****************************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::isEqual(const IMatrixClassifier& obj, const double /*precision*/) const
|
||||
{
|
||||
return m_metric == obj.m_metric && m_nbClass == obj.getClassCount();
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void IMatrixClassifier::copy(const IMatrixClassifier& obj)
|
||||
{
|
||||
m_metric = obj.m_metric;
|
||||
setClassCount(obj.getClassCount());
|
||||
}
|
||||
/// -------------------------------------------------------------------------------------------------
|
||||
|
||||
/// -------------------------------------------------------------------------------------------------
|
||||
std::stringstream IMatrixClassifier::print() const { return std::stringstream(printHeader().str() + printAdditional().str() + printClasses().str()); }
|
||||
/// -------------------------------------------------------------------------------------------------
|
||||
|
||||
/// -------------------------------------------------------------------------------------------------
|
||||
std::stringstream IMatrixClassifier::printHeader() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << getType() << " Classifier" << std::endl;
|
||||
ss << "Metric : " << toString(m_metric) << std::endl;
|
||||
ss << "Number of Classes : " << m_nbClass << std::endl;
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//*******************************************
|
||||
//***** XML Manager (Private Functions) *****
|
||||
//*******************************************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::saveHeader(tinyxml2::XMLElement* data) const
|
||||
{
|
||||
data->SetAttribute("type", getType().c_str()); // Set attribute classifier type
|
||||
data->SetAttribute("class-count", int(m_nbClass)); // Set attribute class count
|
||||
data->SetAttribute("metric", toString(m_metric).c_str()); // Set attribute metric
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::loadHeader(tinyxml2::XMLElement* data)
|
||||
{
|
||||
if (data == nullptr) { return false; } // Check if Node Exist
|
||||
const std::string classifierType = data->Attribute("type"); // Get type
|
||||
if (classifierType != getType()) { return false; } // Check Type
|
||||
setClassCount(data->IntAttribute("class-count")); // Update Number of classes
|
||||
m_metric = StringToMetric(data->Attribute("metric")); // Update Metric
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
Reference in New Issue
Block a user