Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/core/test/test_eigen.cpp
16337 views
1
/*M///////////////////////////////////////////////////////////////////////////////////////
2
//
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4
//
5
// By downloading, copying, installing or using the software you agree to this license.
6
// If you do not agree to this license, do not download, install,
7
// copy or use the software.
8
//
9
//
10
// License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15
// Third party copyrights are property of their respective owners.
16
//
17
// Redistribution and use in source and binary forms, with or without modification,
18
// are permitted provided that the following conditions are met:
19
//
20
// * Redistribution's of source code must retain the above copyright notice,
21
// this list of conditions and the following disclaimer.
22
//
23
// * Redistribution's in binary form must reproduce the above copyright notice,
24
// this list of conditions and the following disclaimer in the documentation
25
// and/or other materials provided with the distribution.
26
//
27
// * The name of the copyright holders may not be used to endorse or promote products
28
// derived from this software without specific prior written permission.
29
//
30
// This software is provided by the copyright holders and contributors "as is" and
31
// any express or implied warranties, including, but not limited to, the implied
32
// warranties of merchantability and fitness for a particular purpose are disclaimed.
33
// In no event shall the Intel Corporation or contributors be liable for any direct,
34
// indirect, incidental, special, exemplary, or consequential damages
35
// (including, but not limited to, procurement of substitute goods or services;
36
// loss of use, data, or profits; or business interruption) however caused
37
// and on any theory of liability, whether in contract, strict liability,
38
// or tort (including negligence or otherwise) arising in any way out of
39
// the use of this software, even if advised of the possibility of such damage.
40
//
41
//M*/
42
#include "test_precomp.hpp"
43
44
namespace opencv_test { namespace {
45
46
#define sign(a) a > 0 ? 1 : a == 0 ? 0 : -1
47
48
#define CORE_EIGEN_ERROR_COUNT 1
49
#define CORE_EIGEN_ERROR_SIZE 2
50
#define CORE_EIGEN_ERROR_DIFF 3
51
#define CORE_EIGEN_ERROR_ORTHO 4
52
#define CORE_EIGEN_ERROR_ORDER 5
53
54
#define MESSAGE_ERROR_COUNT "Matrix of eigen values must have the same rows as source matrix and 1 column."
55
#define MESSAGE_ERROR_SIZE "Source matrix and matrix of eigen vectors must have the same sizes."
56
#define MESSAGE_ERROR_DIFF_1 "Accuracy of eigen values computing less than required."
57
#define MESSAGE_ERROR_DIFF_2 "Accuracy of eigen vectors computing less than required."
58
#define MESSAGE_ERROR_ORTHO "Matrix of eigen vectors is not orthogonal."
59
#define MESSAGE_ERROR_ORDER "Eigen values are not sorted in descending order."
60
61
const int COUNT_NORM_TYPES = 3;
62
const int NORM_TYPE[COUNT_NORM_TYPES] = {cv::NORM_L1, cv::NORM_L2, cv::NORM_INF};
63
64
enum TASK_TYPE_EIGEN {VALUES, VECTORS};
65
66
class Core_EigenTest: public cvtest::BaseTest
67
{
68
public:
69
70
Core_EigenTest();
71
~Core_EigenTest();
72
73
protected:
74
75
bool test_values(const cv::Mat& src); // complex test for eigen without vectors
76
bool check_full(int type); // compex test for symmetric matrix
77
virtual void run (int) = 0; // main testing method
78
79
protected:
80
81
float eps_val_32, eps_vec_32;
82
float eps_val_64, eps_vec_64;
83
int ntests;
84
85
bool check_pair_count(const cv::Mat& src, const cv::Mat& evalues, int low_index = -1, int high_index = -1);
86
bool check_pair_count(const cv::Mat& src, const cv::Mat& evalues, const cv::Mat& evectors, int low_index = -1, int high_index = -1);
87
bool check_pairs_order(const cv::Mat& eigen_values); // checking order of eigen values & vectors (it should be none up)
88
bool check_orthogonality(const cv::Mat& U); // checking is matrix of eigen vectors orthogonal
89
bool test_pairs(const cv::Mat& src); // complex test for eigen with vectors
90
91
void print_information(const size_t norm_idx, const cv::Mat& src, double diff, double max_diff);
92
};
93
94
class Core_EigenTest_Scalar : public Core_EigenTest
95
{
96
public:
97
Core_EigenTest_Scalar() : Core_EigenTest() {}
98
~Core_EigenTest_Scalar();
99
100
virtual void run(int) = 0;
101
};
102
103
class Core_EigenTest_Scalar_32 : public Core_EigenTest_Scalar
104
{
105
public:
106
Core_EigenTest_Scalar_32() : Core_EigenTest_Scalar() {}
107
~Core_EigenTest_Scalar_32();
108
109
void run(int);
110
};
111
112
class Core_EigenTest_Scalar_64 : public Core_EigenTest_Scalar
113
{
114
public:
115
Core_EigenTest_Scalar_64() : Core_EigenTest_Scalar() {}
116
~Core_EigenTest_Scalar_64();
117
void run(int);
118
};
119
120
class Core_EigenTest_32 : public Core_EigenTest
121
{
122
public:
123
Core_EigenTest_32(): Core_EigenTest() {}
124
~Core_EigenTest_32() {}
125
void run(int);
126
};
127
128
class Core_EigenTest_64 : public Core_EigenTest
129
{
130
public:
131
Core_EigenTest_64(): Core_EigenTest() {}
132
~Core_EigenTest_64() {}
133
void run(int);
134
};
135
136
Core_EigenTest_Scalar::~Core_EigenTest_Scalar() {}
137
Core_EigenTest_Scalar_32::~Core_EigenTest_Scalar_32() {}
138
Core_EigenTest_Scalar_64::~Core_EigenTest_Scalar_64() {}
139
140
void Core_EigenTest_Scalar_32::run(int)
141
{
142
for (int i = 0; i < ntests; ++i)
143
{
144
float value = cv::randu<float>();
145
cv::Mat src(1, 1, CV_32FC1, Scalar::all((float)value));
146
test_values(src);
147
}
148
}
149
150
void Core_EigenTest_Scalar_64::run(int)
151
{
152
for (int i = 0; i < ntests; ++i)
153
{
154
float value = cv::randu<float>();
155
cv::Mat src(1, 1, CV_64FC1, Scalar::all((double)value));
156
test_values(src);
157
}
158
}
159
160
void Core_EigenTest_32::run(int) { check_full(CV_32FC1); }
161
void Core_EigenTest_64::run(int) { check_full(CV_64FC1); }
162
163
Core_EigenTest::Core_EigenTest()
164
: eps_val_32(1e-3f), eps_vec_32(1e-3f),
165
eps_val_64(1e-4f), eps_vec_64(1e-4f), ntests(100) {}
166
Core_EigenTest::~Core_EigenTest() {}
167
168
bool Core_EigenTest::check_pair_count(const cv::Mat& src, const cv::Mat& evalues, int low_index, int high_index)
169
{
170
int n = src.rows, s = sign(high_index);
171
if (!( (evalues.rows == n - max<int>(0, low_index) - ((int)((n/2.0)*(s*s-s)) + (1+s-s*s)*(n - (high_index+1)))) && (evalues.cols == 1)))
172
{
173
std::cout << endl; std::cout << "Checking sizes of eigen values matrix " << evalues << "..." << endl;
174
std::cout << "Number of rows: " << evalues.rows << " Number of cols: " << evalues.cols << endl;
175
std::cout << "Size of src symmetric matrix: " << src.rows << " * " << src.cols << endl; std::cout << endl;
176
CV_Error(CORE_EIGEN_ERROR_COUNT, MESSAGE_ERROR_COUNT);
177
}
178
return true;
179
}
180
181
bool Core_EigenTest::check_pair_count(const cv::Mat& src, const cv::Mat& evalues, const cv::Mat& evectors, int low_index, int high_index)
182
{
183
int n = src.rows, s = sign(high_index);
184
int right_eigen_pair_count = n - max<int>(0, low_index) - ((int)((n/2.0)*(s*s-s)) + (1+s-s*s)*(n - (high_index+1)));
185
186
if (!(evectors.rows == right_eigen_pair_count && evectors.cols == right_eigen_pair_count))
187
{
188
std::cout << endl; std::cout << "Checking sizes of eigen vectors matrix " << evectors << "..." << endl;
189
std::cout << "Number of rows: " << evectors.rows << " Number of cols: " << evectors.cols << endl;
190
std:: cout << "Size of src symmetric matrix: " << src.rows << " * " << src.cols << endl; std::cout << endl;
191
CV_Error (CORE_EIGEN_ERROR_SIZE, MESSAGE_ERROR_SIZE);
192
}
193
194
if (!(evalues.rows == right_eigen_pair_count && evalues.cols == 1))
195
{
196
std::cout << endl; std::cout << "Checking sizes of eigen values matrix " << evalues << "..." << endl;
197
std::cout << "Number of rows: " << evalues.rows << " Number of cols: " << evalues.cols << endl;
198
std:: cout << "Size of src symmetric matrix: " << src.rows << " * " << src.cols << endl; std::cout << endl;
199
CV_Error (CORE_EIGEN_ERROR_COUNT, MESSAGE_ERROR_COUNT);
200
}
201
202
return true;
203
}
204
205
void Core_EigenTest::print_information(const size_t norm_idx, const cv::Mat& src, double diff, double max_diff)
206
{
207
switch (NORM_TYPE[norm_idx])
208
{
209
case cv::NORM_L1: std::cout << "L1"; break;
210
case cv::NORM_L2: std::cout << "L2"; break;
211
case cv::NORM_INF: std::cout << "INF"; break;
212
default: break;
213
}
214
215
cout << "-criteria... " << endl;
216
cout << "Source size: " << src.rows << " * " << src.cols << endl;
217
cout << "Difference between original eigen vectors matrix and result: " << diff << endl;
218
cout << "Maximum allowed difference: " << max_diff << endl; cout << endl;
219
}
220
221
bool Core_EigenTest::check_orthogonality(const cv::Mat& U)
222
{
223
int type = U.type();
224
double eps_vec = type == CV_32FC1 ? eps_vec_32 : eps_vec_64;
225
cv::Mat UUt; cv::mulTransposed(U, UUt, false);
226
227
cv::Mat E = Mat::eye(U.rows, U.cols, type);
228
229
for (int i = 0; i < COUNT_NORM_TYPES; ++i)
230
{
231
double diff = cvtest::norm(UUt, E, NORM_TYPE[i] | cv::NORM_RELATIVE);
232
if (diff > eps_vec)
233
{
234
std::cout << endl; std::cout << "Checking orthogonality of matrix " << U << ": ";
235
print_information(i, U, diff, eps_vec);
236
CV_Error(CORE_EIGEN_ERROR_ORTHO, MESSAGE_ERROR_ORTHO);
237
}
238
}
239
240
return true;
241
}
242
243
bool Core_EigenTest::check_pairs_order(const cv::Mat& eigen_values)
244
{
245
switch (eigen_values.type())
246
{
247
case CV_32FC1:
248
{
249
for (int i = 0; i < (int)(eigen_values.total() - 1); ++i)
250
if (!(eigen_values.at<float>(i, 0) > eigen_values.at<float>(i+1, 0)))
251
{
252
std::cout << endl; std::cout << "Checking order of eigen values vector " << eigen_values << "..." << endl;
253
std::cout << "Pair of indexes with non descending of eigen values: (" << i << ", " << i+1 << ")." << endl;
254
std::cout << endl;
255
CV_Error(CORE_EIGEN_ERROR_ORDER, MESSAGE_ERROR_ORDER);
256
}
257
258
break;
259
}
260
261
case CV_64FC1:
262
{
263
for (int i = 0; i < (int)(eigen_values.total() - 1); ++i)
264
if (!(eigen_values.at<double>(i, 0) > eigen_values.at<double>(i+1, 0)))
265
{
266
std::cout << endl; std::cout << "Checking order of eigen values vector " << eigen_values << "..." << endl;
267
std::cout << "Pair of indexes with non descending of eigen values: (" << i << ", " << i+1 << ")." << endl;
268
std::cout << endl;
269
CV_Error(CORE_EIGEN_ERROR_ORDER, "Eigen values are not sorted in descending order.");
270
}
271
272
break;
273
}
274
275
default:;
276
}
277
278
return true;
279
}
280
281
bool Core_EigenTest::test_pairs(const cv::Mat& src)
282
{
283
int type = src.type();
284
double eps_vec = type == CV_32FC1 ? eps_vec_32 : eps_vec_64;
285
286
cv::Mat eigen_values, eigen_vectors;
287
288
cv::eigen(src, eigen_values, eigen_vectors);
289
290
if (!check_pair_count(src, eigen_values, eigen_vectors))
291
return false;
292
293
if (!check_orthogonality (eigen_vectors))
294
return false;
295
296
if (!check_pairs_order(eigen_values))
297
return false;
298
299
cv::Mat eigen_vectors_t; cv::transpose(eigen_vectors, eigen_vectors_t);
300
301
// Check:
302
// src * eigenvector = eigenval * eigenvector
303
cv::Mat lhs(src.rows, src.cols, type);
304
cv::Mat rhs(src.rows, src.cols, type);
305
306
lhs = src*eigen_vectors_t;
307
308
for (int i = 0; i < src.cols; ++i)
309
{
310
double eigenval = 0;
311
switch (type)
312
{
313
case CV_32FC1: eigenval = eigen_values.at<float>(i, 0); break;
314
case CV_64FC1: eigenval = eigen_values.at<double>(i, 0); break;
315
}
316
cv::Mat rhs_v = eigenval * eigen_vectors_t.col(i);
317
rhs_v.copyTo(rhs.col(i));
318
}
319
320
for (int i = 0; i < COUNT_NORM_TYPES; ++i)
321
{
322
double diff = cvtest::norm(lhs, rhs, NORM_TYPE[i] | cv::NORM_RELATIVE);
323
if (diff > eps_vec)
324
{
325
std::cout << endl; std::cout << "Checking accuracy of eigen vectors computing for matrix " << src << ": ";
326
print_information(i, src, diff, eps_vec);
327
CV_Error(CORE_EIGEN_ERROR_DIFF, MESSAGE_ERROR_DIFF_2);
328
}
329
}
330
331
return true;
332
}
333
334
bool Core_EigenTest::test_values(const cv::Mat& src)
335
{
336
int type = src.type();
337
double eps_val = type == CV_32FC1 ? eps_val_32 : eps_val_64;
338
339
cv::Mat eigen_values_1, eigen_values_2, eigen_vectors;
340
341
if (!test_pairs(src)) return false;
342
343
cv::eigen(src, eigen_values_1, eigen_vectors);
344
cv::eigen(src, eigen_values_2);
345
346
if (!check_pair_count(src, eigen_values_2)) return false;
347
348
for (int i = 0; i < COUNT_NORM_TYPES; ++i)
349
{
350
double diff = cvtest::norm(eigen_values_1, eigen_values_2, NORM_TYPE[i] | cv::NORM_RELATIVE);
351
if (diff > eps_val)
352
{
353
std::cout << endl; std::cout << "Checking accuracy of eigen values computing for matrix " << src << ": ";
354
print_information(i, src, diff, eps_val);
355
CV_Error(CORE_EIGEN_ERROR_DIFF, MESSAGE_ERROR_DIFF_1);
356
}
357
}
358
359
return true;
360
}
361
362
bool Core_EigenTest::check_full(int type)
363
{
364
const int MAX_DEGREE = 7;
365
366
RNG rng = cv::theRNG(); // fix the seed
367
368
for (int i = 0; i < ntests; ++i)
369
{
370
int src_size = (int)(std::pow(2.0, (rng.uniform(0, MAX_DEGREE) + 1.)));
371
372
cv::Mat src(src_size, src_size, type);
373
374
for (int j = 0; j < src.rows; ++j)
375
for (int k = j; k < src.cols; ++k)
376
if (type == CV_32FC1) src.at<float>(k, j) = src.at<float>(j, k) = cv::randu<float>();
377
else src.at<double>(k, j) = src.at<double>(j, k) = cv::randu<double>();
378
379
if (!test_values(src)) return false;
380
}
381
382
return true;
383
}
384
385
TEST(Core_Eigen, scalar_32) {Core_EigenTest_Scalar_32 test; test.safe_run(); }
386
TEST(Core_Eigen, scalar_64) {Core_EigenTest_Scalar_64 test; test.safe_run(); }
387
TEST(Core_Eigen, vector_32) { Core_EigenTest_32 test; test.safe_run(); }
388
TEST(Core_Eigen, vector_64) { Core_EigenTest_64 test; test.safe_run(); }
389
390
template<typename T>
391
static void testEigen(const Mat_<T>& src, const Mat_<T>& expected_eigenvalues, bool runSymmetric = false)
392
{
393
SCOPED_TRACE(runSymmetric ? "cv::eigen" : "cv::eigenNonSymmetric");
394
395
int type = traits::Type<T>::value;
396
const T eps = src.type() == CV_32F ? 1e-4f : 1e-6f;
397
398
Mat eigenvalues, eigenvectors, eigenvalues0;
399
400
if (runSymmetric)
401
{
402
cv::eigen(src, eigenvalues0, noArray());
403
cv::eigen(src, eigenvalues, eigenvectors);
404
}
405
else
406
{
407
cv::eigenNonSymmetric(src, eigenvalues0, noArray());
408
cv::eigenNonSymmetric(src, eigenvalues, eigenvectors);
409
}
410
#if 0
411
std::cout << "src = " << src << std::endl;
412
std::cout << "eigenvalues.t() = " << eigenvalues.t() << std::endl;
413
std::cout << "eigenvectors = " << eigenvectors << std::endl;
414
#endif
415
ASSERT_EQ(type, eigenvalues0.type());
416
ASSERT_EQ(type, eigenvalues.type());
417
ASSERT_EQ(type, eigenvectors.type());
418
419
ASSERT_EQ(src.rows, eigenvalues.rows);
420
ASSERT_EQ(eigenvalues.rows, eigenvectors.rows);
421
ASSERT_EQ(src.rows, eigenvectors.cols);
422
423
EXPECT_LT(cvtest::norm(eigenvalues, eigenvalues0, NORM_INF), eps);
424
425
// check definition: src*eigenvectors.row(i).t() = eigenvalues.at<srcType>(i)*eigenvectors.row(i).t()
426
for (int i = 0; i < src.rows; i++)
427
{
428
EXPECT_NEAR(eigenvalues.at<T>(i), expected_eigenvalues(i), eps) << "i=" << i;
429
Mat lhs = src*eigenvectors.row(i).t();
430
Mat rhs = eigenvalues.at<T>(i)*eigenvectors.row(i).t();
431
EXPECT_LT(cvtest::norm(lhs, rhs, NORM_INF), eps)
432
<< "i=" << i << " eigenvalue=" << eigenvalues.at<T>(i) << std::endl
433
<< "lhs=" << lhs.t() << std::endl
434
<< "rhs=" << rhs.t();
435
}
436
}
437
438
template<typename T>
439
static void testEigenSymmetric3x3()
440
{
441
/*const*/ T values_[] = {
442
2, -1, 0,
443
-1, 2, -1,
444
0, -1, 2
445
};
446
Mat_<T> src(3, 3, values_);
447
448
/*const*/ T expected_eigenvalues_[] = { 3.414213562373095f, 2, 0.585786437626905f };
449
Mat_<T> expected_eigenvalues(3, 1, expected_eigenvalues_);
450
451
testEigen(src, expected_eigenvalues);
452
testEigen(src, expected_eigenvalues, true);
453
}
454
TEST(Core_EigenSymmetric, float3x3) { testEigenSymmetric3x3<float>(); }
455
TEST(Core_EigenSymmetric, double3x3) { testEigenSymmetric3x3<double>(); }
456
457
template<typename T>
458
static void testEigenSymmetric5x5()
459
{
460
/*const*/ T values_[5*5] = {
461
5, -1, 0, 2, 1,
462
-1, 4, -1, 0, 0,
463
0, -1, 3, 1, -1,
464
2, 0, 1, 4, 0,
465
1, 0, -1, 0, 1
466
};
467
Mat_<T> src(5, 5, values_);
468
469
/*const*/ T expected_eigenvalues_[] = { 7.028919644935684f, 4.406130784616501f, 3.73626552682258f, 1.438067799899037f, 0.390616243726198f };
470
Mat_<T> expected_eigenvalues(5, 1, expected_eigenvalues_);
471
472
testEigen(src, expected_eigenvalues);
473
testEigen(src, expected_eigenvalues, true);
474
}
475
TEST(Core_EigenSymmetric, float5x5) { testEigenSymmetric5x5<float>(); }
476
TEST(Core_EigenSymmetric, double5x5) { testEigenSymmetric5x5<double>(); }
477
478
479
template<typename T>
480
static void testEigen2x2()
481
{
482
/*const*/ T values_[] = { 4, 1, 6, 3 };
483
Mat_<T> src(2, 2, values_);
484
485
/*const*/ T expected_eigenvalues_[] = { 6, 1 };
486
Mat_<T> expected_eigenvalues(2, 1, expected_eigenvalues_);
487
488
testEigen(src, expected_eigenvalues);
489
}
490
TEST(Core_EigenNonSymmetric, float2x2) { testEigen2x2<float>(); }
491
TEST(Core_EigenNonSymmetric, double2x2) { testEigen2x2<double>(); }
492
493
template<typename T>
494
static void testEigen3x3()
495
{
496
/*const*/ T values_[3*3] = {
497
3,1,0,
498
0,3,1,
499
0,0,3
500
};
501
Mat_<T> src(3, 3, values_);
502
503
/*const*/ T expected_eigenvalues_[] = { 3, 3, 3 };
504
Mat_<T> expected_eigenvalues(3, 1, expected_eigenvalues_);
505
506
testEigen(src, expected_eigenvalues);
507
}
508
TEST(Core_EigenNonSymmetric, float3x3) { testEigen3x3<float>(); }
509
TEST(Core_EigenNonSymmetric, double3x3) { testEigen3x3<double>(); }
510
511
typedef testing::TestWithParam<int> Core_EigenZero;
512
TEST_P(Core_EigenZero, double)
513
{
514
int N = GetParam();
515
Mat_<double> srcZero = Mat_<double>::zeros(N, N);
516
Mat_<double> expected_eigenvalueZero = Mat_<double>::zeros(N, 1); // 1D Mat
517
testEigen(srcZero, expected_eigenvalueZero);
518
testEigen(srcZero, expected_eigenvalueZero, true);
519
}
520
INSTANTIATE_TEST_CASE_P(/**/, Core_EigenZero, testing::Values(2, 3, 5));
521
522
}} // namespace
523
524