Commit ce20efb8 authored by Alexander Alekhin's avatar Alexander Alekhin

Merge pull request #9804 from woodychow:optimize_cveigen

parents b0bce60c c0b6061a
......@@ -394,7 +394,13 @@ public class CoreTest extends OpenCVTestCase {
}
public void testEigen() {
Mat src = new Mat(3, 3, CvType.CV_32FC1, new Scalar(2.0));
Mat src = new Mat(3, 3, CvType.CV_32FC1) {
{
put(0, 0, 2, 0, 0);
put(1, 0, 0, 6, 0);
put(2, 0, 0, 0, 4);
}
};
Mat eigenVals = new Mat();
Mat eigenVecs = new Mat();
......@@ -402,18 +408,22 @@ public class CoreTest extends OpenCVTestCase {
Mat expectedEigenVals = new Mat(3, 1, CvType.CV_32FC1) {
{
put(0, 0, 6, 0, 0);
}
};
Mat expectedEigenVecs = new Mat(3, 3, CvType.CV_32FC1) {
{
put(0, 0, 0.57735026, 0.57735026, 0.57735032);
put(1, 0, 0.70710677, -0.70710677, 0);
put(2, 0, -0.40824831, -0.40824831, 0.81649661);
put(0, 0, 6, 4, 2);
}
};
assertMatEqual(eigenVals, expectedEigenVals, EPS);
assertMatEqual(eigenVecs, expectedEigenVecs, EPS);
// check by definition
double eps = 1e-3;
for(int i = 0; i < 3; i++)
{
Mat vec = eigenVecs.row(i).t();
Mat lhs = new Mat(3, 1, CvType.CV_32FC1);
Core.gemm(src, vec, 1.0, new Mat(), 1.0, lhs);
Mat rhs = new Mat(3, 1, CvType.CV_32FC1);
Core.gemm(vec, eigenVals.row(i), 1.0, new Mat(), 1.0, rhs);
assertMatEqual(lhs, rhs, eps);
}
}
public void testExp() {
......@@ -1326,7 +1336,8 @@ public class CoreTest extends OpenCVTestCase {
Mat vectors = new Mat();
Core.PCACompute(data, mean, vectors);
//System.out.println(mean.dump());
//System.out.println(vectors.dump());
Mat mean_truth = new Mat(1, 4, CvType.CV_32F) {
{
put(0, 0, 2, 4, 4, 8);
......@@ -1338,7 +1349,21 @@ public class CoreTest extends OpenCVTestCase {
}
};
assertMatEqual(mean_truth, mean, EPS);
assertMatEqual(vectors_truth, vectors, EPS);
// eigenvectors are normalized (length = 1),
// but direction is unknown (v and -v are both eigen vectors)
// so this direct check doesn't work:
// assertMatEqual(vectors_truth, vectors, EPS);
for(int i = 0; i < 3; i++)
{
Mat vec0 = vectors_truth.row(i);
Mat vec1 = vectors.row(i);
Mat vec1_ = new Mat();
Core.subtract(new Mat(1, 4, CvType.CV_32F, new Scalar(0)), vec1, vec1_);
double scale1 = Core.norm(vec0, vec1);
double scale2 = Core.norm(vec0, vec1_);
assertTrue(Math.min(scale1, scale2) < EPS);
}
}
public void testPCAComputeMatMatMatInt() {
......@@ -1365,7 +1390,20 @@ public class CoreTest extends OpenCVTestCase {
}
};
assertMatEqual(mean_truth, mean, EPS);
assertMatEqual(vectors_truth, vectors, EPS);
// eigenvectors are normalized (length = 1),
// but direction is unknown (v and -v are both eigen vectors)
// so this direct check doesn't work:
// assertMatEqual(vectors_truth, vectors, EPS);
for(int i = 0; i < 1; i++)
{
Mat vec0 = vectors_truth.row(i);
Mat vec1 = vectors.row(i);
Mat vec1_ = new Mat();
Core.subtract(new Mat(1, 4, CvType.CV_32F, new Scalar(0)), vec1, vec1_);
double scale1 = Core.norm(vec0, vec1);
double scale2 = Core.norm(vec0, vec1_);
assertTrue(Math.min(scale1, scale2) < EPS);
}
}
public void testPCAProject() {
......
......@@ -43,6 +43,12 @@
#include "precomp.hpp"
#include <limits>
#ifdef HAVE_EIGEN
#include <Eigen/Core>
#include <Eigen/Eigenvalues>
#include "opencv2/core/eigen.hpp"
#endif
#if defined _M_IX86 && defined _MSC_VER && _MSC_VER < 1700
#pragma float_control(precise, on)
#endif
......@@ -1396,6 +1402,47 @@ bool cv::eigen( InputArray _src, OutputArray _evals, OutputArray _evects )
v = _evects.getMat();
}
#ifdef HAVE_EIGEN
const bool evecNeeded = _evects.needed();
const int esOptions = evecNeeded ? Eigen::ComputeEigenvectors : Eigen::EigenvaluesOnly;
_evals.create(n, 1, type);
cv::Mat evals = _evals.getMat();
if ( type == CV_64F )
{
Eigen::MatrixXd src_eig, zeros_eig;
cv::cv2eigen(src, src_eig);
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es;
es.compute(src_eig, esOptions);
if ( es.info() == Eigen::Success )
{
cv::eigen2cv(es.eigenvalues().reverse().eval(), evals);
if ( evecNeeded )
{
cv::Mat evects = _evects.getMat();
cv::eigen2cv(es.eigenvectors().rowwise().reverse().transpose().eval(), v);
}
return true;
}
} else { // CV_32F
Eigen::MatrixXf src_eig, zeros_eig;
cv::cv2eigen(src, src_eig);
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> es;
es.compute(src_eig, esOptions);
if ( es.info() == Eigen::Success )
{
cv::eigen2cv(es.eigenvalues().reverse().eval(), evals);
if ( evecNeeded )
{
cv::eigen2cv(es.eigenvectors().rowwise().reverse().transpose().eval(), v);
}
return true;
}
}
return false;
#else
size_t elemSize = src.elemSize(), astep = alignSize(n*elemSize, 16);
AutoBuffer<uchar> buf(n*astep + n*5*elemSize + 32);
uchar* ptr = alignPtr((uchar*)buf, 16);
......@@ -1408,6 +1455,7 @@ bool cv::eigen( InputArray _src, OutputArray _evals, OutputArray _evects )
w.copyTo(_evals);
return ok;
#endif
}
namespace cv
......
......@@ -59,7 +59,7 @@ using namespace std;
#define MESSAGE_ERROR_DIFF_1 "Accuracy of eigen values computing less than required."
#define MESSAGE_ERROR_DIFF_2 "Accuracy of eigen vectors computing less than required."
#define MESSAGE_ERROR_ORTHO "Matrix of eigen vectors is not orthogonal."
#define MESSAGE_ERROR_ORDER "Eigen values are not sorted in ascending order."
#define MESSAGE_ERROR_ORDER "Eigen values are not sorted in descending order."
const int COUNT_NORM_TYPES = 3;
const int NORM_TYPE[COUNT_NORM_TYPES] = {cv::NORM_L1, cv::NORM_L2, cv::NORM_INF};
......@@ -164,8 +164,8 @@ void Core_EigenTest_32::run(int) { check_full(CV_32FC1); }
void Core_EigenTest_64::run(int) { check_full(CV_64FC1); }
Core_EigenTest::Core_EigenTest()
: eps_val_32(1e-3f), eps_vec_32(12e-3f),
eps_val_64(1e-4f), eps_vec_64(1e-3f), ntests(100) {}
: eps_val_32(1e-3f), eps_vec_32(1e-3f),
eps_val_64(1e-4f), eps_vec_64(1e-4f), ntests(100) {}
Core_EigenTest::~Core_EigenTest() {}
bool Core_EigenTest::check_pair_count(const cv::Mat& src, const cv::Mat& evalues, int low_index, int high_index)
......@@ -234,7 +234,7 @@ bool Core_EigenTest::check_orthogonality(const cv::Mat& U)
for (int i = 0; i < COUNT_NORM_TYPES; ++i)
{
double diff = cvtest::norm(UUt, E, NORM_TYPE[i]);
double diff = cvtest::norm(UUt, E, NORM_TYPE[i] | cv::NORM_RELATIVE);
if (diff > eps_vec)
{
std::cout << endl; std::cout << "Checking orthogonality of matrix " << U << ": ";
......@@ -257,7 +257,7 @@ bool Core_EigenTest::check_pairs_order(const cv::Mat& eigen_values)
if (!(eigen_values.at<float>(i, 0) > eigen_values.at<float>(i+1, 0)))
{
std::cout << endl; std::cout << "Checking order of eigen values vector " << eigen_values << "..." << endl;
std::cout << "Pair of indexes with non ascending of eigen values: (" << i << ", " << i+1 << ")." << endl;
std::cout << "Pair of indexes with non descending of eigen values: (" << i << ", " << i+1 << ")." << endl;
std::cout << endl;
CV_Error(CORE_EIGEN_ERROR_ORDER, MESSAGE_ERROR_ORDER);
return false;
......@@ -272,9 +272,9 @@ bool Core_EigenTest::check_pairs_order(const cv::Mat& eigen_values)
if (!(eigen_values.at<double>(i, 0) > eigen_values.at<double>(i+1, 0)))
{
std::cout << endl; std::cout << "Checking order of eigen values vector " << eigen_values << "..." << endl;
std::cout << "Pair of indexes with non ascending of eigen values: (" << i << ", " << i+1 << ")." << endl;
std::cout << "Pair of indexes with non descending of eigen values: (" << i << ", " << i+1 << ")." << endl;
std::cout << endl;
CV_Error(CORE_EIGEN_ERROR_ORDER, "Eigen values are not sorted in ascending order.");
CV_Error(CORE_EIGEN_ERROR_ORDER, "Eigen values are not sorted in descending order.");
return false;
}
......@@ -307,43 +307,28 @@ bool Core_EigenTest::test_pairs(const cv::Mat& src)
cv::Mat eigen_vectors_t; cv::transpose(eigen_vectors, eigen_vectors_t);
cv::Mat src_evec(src.rows, src.cols, type);
src_evec = src*eigen_vectors_t;
// Check:
// src * eigenvector = eigenval * eigenvector
cv::Mat lhs(src.rows, src.cols, type);
cv::Mat rhs(src.rows, src.cols, type);
cv::Mat eval_evec(src.rows, src.cols, type);
lhs = src*eigen_vectors_t;
switch (type)
for (int i = 0; i < src.cols; ++i)
{
case CV_32FC1:
{
for (int i = 0; i < src.cols; ++i)
{
cv::Mat tmp = eigen_values.at<float>(i, 0) * eigen_vectors_t.col(i);
for (int j = 0; j < src.rows; ++j) eval_evec.at<float>(j, i) = tmp.at<float>(j, 0);
}
break;
}
case CV_64FC1:
double eigenval = 0;
switch (type)
{
for (int i = 0; i < src.cols; ++i)
{
cv::Mat tmp = eigen_values.at<double>(i, 0) * eigen_vectors_t.col(i);
for (int j = 0; j < src.rows; ++j) eval_evec.at<double>(j, i) = tmp.at<double>(j, 0);
}
break;
case CV_32FC1: eigenval = eigen_values.at<float>(i, 0); break;
case CV_64FC1: eigenval = eigen_values.at<double>(i, 0); break;
}
default:;
cv::Mat rhs_v = eigenval * eigen_vectors_t.col(i);
rhs_v.copyTo(rhs.col(i));
}
cv::Mat disparity = src_evec - eval_evec;
for (int i = 0; i < COUNT_NORM_TYPES; ++i)
{
double diff = cvtest::norm(disparity, NORM_TYPE[i]);
double diff = cvtest::norm(lhs, rhs, NORM_TYPE[i] | cv::NORM_RELATIVE);
if (diff > eps_vec)
{
std::cout << endl; std::cout << "Checking accuracy of eigen vectors computing for matrix " << src << ": ";
......@@ -372,7 +357,7 @@ bool Core_EigenTest::test_values(const cv::Mat& src)
for (int i = 0; i < COUNT_NORM_TYPES; ++i)
{
double diff = cvtest::norm(eigen_values_1, eigen_values_2, NORM_TYPE[i]);
double diff = cvtest::norm(eigen_values_1, eigen_values_2, NORM_TYPE[i] | cv::NORM_RELATIVE);
if (diff > eps_val)
{
std::cout << endl; std::cout << "Checking accuracy of eigen values computing for matrix " << src << ": ";
......@@ -419,7 +404,7 @@ static void testEigen(const Mat_<T>& src, const Mat_<T>& expected_eigenvalues, b
SCOPED_TRACE(runSymmetric ? "cv::eigen" : "cv::eigenNonSymmetric");
int type = traits::Type<T>::value;
const T eps = 1e-6f;
const T eps = src.type() == CV_32F ? 1e-4f : 1e-6f;
Mat eigenvalues, eigenvectors, eigenvalues0;
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment