mirror of
https://github.com/opencv/opencv.git
synced 2024-11-24 03:00:14 +08:00
357b9abaef
Perf tests for SVD and solve() created #25450 fixes #25336 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
391 lines
9.9 KiB
C++
391 lines
9.9 KiB
C++
#include "perf_precomp.hpp"
|
|
|
|
namespace opencv_test
|
|
{
|
|
using namespace perf;
|
|
|
|
namespace {
|
|
|
|
typedef perf::TestBaseWithParam<size_t> VectorLength;
|
|
|
|
PERF_TEST_P(VectorLength, phase32f, testing::Values(128, 1000, 128*1024, 512*1024, 1024*1024))
|
|
{
|
|
size_t length = GetParam();
|
|
vector<float> X(length);
|
|
vector<float> Y(length);
|
|
vector<float> angle(length);
|
|
|
|
declare.in(X, Y, WARMUP_RNG).out(angle);
|
|
|
|
TEST_CYCLE_N(200) cv::phase(X, Y, angle, true);
|
|
|
|
SANITY_CHECK(angle, 5e-5);
|
|
}
|
|
|
|
PERF_TEST_P(VectorLength, phase64f, testing::Values(128, 1000, 128*1024, 512*1024, 1024*1024))
|
|
{
|
|
size_t length = GetParam();
|
|
vector<double> X(length);
|
|
vector<double> Y(length);
|
|
vector<double> angle(length);
|
|
|
|
declare.in(X, Y, WARMUP_RNG).out(angle);
|
|
|
|
TEST_CYCLE_N(200) cv::phase(X, Y, angle, true);
|
|
|
|
SANITY_CHECK(angle, 5e-5);
|
|
}
|
|
|
|
// generates random vectors, performs Gram-Schmidt orthogonalization on them
|
|
Mat randomOrtho(int rows, int ftype, RNG& rng)
|
|
{
|
|
Mat result(rows, rows, ftype);
|
|
rng.fill(result, RNG::UNIFORM, cv::Scalar(-1), cv::Scalar(1));
|
|
|
|
for (int i = 0; i < rows; i++)
|
|
{
|
|
Mat v = result.row(i);
|
|
|
|
for (int j = 0; j < i; j++)
|
|
{
|
|
Mat p = result.row(j);
|
|
v -= p.dot(v) * p;
|
|
}
|
|
|
|
v = v * (1. / cv::norm(v));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template<typename FType>
|
|
Mat buildRandomMat(int rows, int cols, RNG& rng, int rank, bool symmetrical)
|
|
{
|
|
int mtype = cv::traits::Depth<FType>::value;
|
|
Mat u = randomOrtho(rows, mtype, rng);
|
|
Mat v = randomOrtho(cols, mtype, rng);
|
|
Mat s(rows, cols, mtype, Scalar(0));
|
|
|
|
std::vector<FType> singVals(rank);
|
|
rng.fill(singVals, RNG::UNIFORM, Scalar(0), Scalar(10));
|
|
std::sort(singVals.begin(), singVals.end());
|
|
auto singIter = singVals.rbegin();
|
|
for (int i = 0; i < rank; i++)
|
|
{
|
|
s.at<FType>(i, i) = *singIter++;
|
|
}
|
|
|
|
if (symmetrical)
|
|
return u * s * u.t();
|
|
else
|
|
return u * s * v.t();
|
|
}
|
|
|
|
Mat buildRandomMat(int rows, int cols, int mtype, RNG& rng, int rank, bool symmetrical)
|
|
{
|
|
if (mtype == CV_32F)
|
|
{
|
|
return buildRandomMat<float>(rows, cols, rng, rank, symmetrical);
|
|
}
|
|
else if (mtype == CV_64F)
|
|
{
|
|
return buildRandomMat<double>(rows, cols, rng, rank, symmetrical);
|
|
}
|
|
else
|
|
{
|
|
CV_Error(cv::Error::StsBadArg, "This type is not supported");
|
|
}
|
|
}
|
|
|
|
CV_ENUM(SolveDecompEnum, DECOMP_LU, DECOMP_SVD, DECOMP_EIG, DECOMP_CHOLESKY, DECOMP_QR)
|
|
|
|
enum RankMatrixOptions
|
|
{
|
|
RANK_HALF, RANK_MINUS_1, RANK_FULL
|
|
};
|
|
|
|
CV_ENUM(RankEnum, RANK_HALF, RANK_MINUS_1, RANK_FULL)
|
|
|
|
enum SolutionsOptions
|
|
{
|
|
NO_SOLUTIONS, ONE_SOLUTION, MANY_SOLUTIONS
|
|
};
|
|
|
|
CV_ENUM(SolutionsEnum, NO_SOLUTIONS, ONE_SOLUTION, MANY_SOLUTIONS)
|
|
|
|
typedef perf::TestBaseWithParam<std::tuple<int, RankEnum, MatDepth, SolveDecompEnum, bool, SolutionsEnum>> SolveTest;
|
|
|
|
PERF_TEST_P(SolveTest, randomMat, ::testing::Combine(
|
|
::testing::Values(31, 64, 100),
|
|
::testing::Values(RANK_HALF, RANK_MINUS_1, RANK_FULL),
|
|
::testing::Values(CV_32F, CV_64F),
|
|
::testing::Values(DECOMP_LU, DECOMP_SVD, DECOMP_EIG, DECOMP_CHOLESKY, DECOMP_QR),
|
|
::testing::Bool(), // normal
|
|
::testing::Values(NO_SOLUTIONS, ONE_SOLUTION, MANY_SOLUTIONS)
|
|
))
|
|
{
|
|
auto t = GetParam();
|
|
int size = std::get<0>(t);
|
|
auto rankEnum = std::get<1>(t);
|
|
int mtype = std::get<2>(t);
|
|
int method = std::get<3>(t);
|
|
bool normal = std::get<4>(t);
|
|
auto solutions = std::get<5>(t);
|
|
|
|
bool symmetrical = (method == DECOMP_CHOLESKY || method == DECOMP_LU);
|
|
|
|
if (normal)
|
|
{
|
|
method |= DECOMP_NORMAL;
|
|
}
|
|
|
|
int rank = size;
|
|
switch (rankEnum)
|
|
{
|
|
case RANK_HALF: rank /= 2; break;
|
|
case RANK_MINUS_1: rank -= 1; break;
|
|
default: break;
|
|
}
|
|
|
|
RNG& rng = theRNG();
|
|
Mat A = buildRandomMat(size, size, mtype, rng, rank, symmetrical);
|
|
Mat x(size, 1, mtype);
|
|
Mat b(size, 1, mtype);
|
|
|
|
switch (solutions)
|
|
{
|
|
// no solutions, let's make b random
|
|
case NO_SOLUTIONS:
|
|
{
|
|
rng.fill(b, RNG::UNIFORM, Scalar(-1), Scalar(1));
|
|
}
|
|
break;
|
|
// exactly 1 solution, let's combine b from A and x
|
|
case ONE_SOLUTION:
|
|
{
|
|
rng.fill(x, RNG::UNIFORM, Scalar(-10), Scalar(10));
|
|
b = A * x;
|
|
}
|
|
break;
|
|
// infinitely many solutions, let's make b zero
|
|
default:
|
|
{
|
|
b = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
TEST_CYCLE() cv::solve(A, b, x, method);
|
|
|
|
SANITY_CHECK_NOTHING();
|
|
}
|
|
|
|
typedef perf::TestBaseWithParam<std::tuple<std::tuple<int, int>, RankEnum, MatDepth, bool, bool>> SvdTest;
|
|
|
|
PERF_TEST_P(SvdTest, decompose, ::testing::Combine(
|
|
::testing::Values(std::make_tuple(5, 15), std::make_tuple(32, 32), std::make_tuple(100, 100)),
|
|
::testing::Values(RANK_HALF, RANK_MINUS_1, RANK_FULL),
|
|
::testing::Values(CV_32F, CV_64F),
|
|
::testing::Bool(), // symmetrical
|
|
::testing::Bool() // needUV
|
|
))
|
|
{
|
|
auto t = GetParam();
|
|
auto rc = std::get<0>(t);
|
|
auto rankEnum = std::get<1>(t);
|
|
int mtype = std::get<2>(t);
|
|
bool symmetrical = std::get<3>(t);
|
|
bool needUV = std::get<4>(t);
|
|
|
|
int rows = std::get<0>(rc);
|
|
int cols = std::get<1>(rc);
|
|
|
|
if (symmetrical)
|
|
{
|
|
rows = max(rows, cols);
|
|
cols = rows;
|
|
}
|
|
|
|
int rank = std::min(rows, cols);
|
|
switch (rankEnum)
|
|
{
|
|
case RANK_HALF: rank /= 2; break;
|
|
case RANK_MINUS_1: rank -= 1; break;
|
|
default: break;
|
|
}
|
|
|
|
int flags = needUV ? 0 : SVD::NO_UV;
|
|
|
|
RNG& rng = theRNG();
|
|
Mat A = buildRandomMat(rows, cols, mtype, rng, rank, symmetrical);
|
|
TEST_CYCLE() cv::SVD svd(A, flags);
|
|
|
|
SANITY_CHECK_NOTHING();
|
|
}
|
|
|
|
|
|
PERF_TEST_P(SvdTest, backSubst, ::testing::Combine(
|
|
::testing::Values(std::make_tuple(5, 15), std::make_tuple(32, 32), std::make_tuple(100, 100)),
|
|
::testing::Values(RANK_HALF, RANK_MINUS_1, RANK_FULL),
|
|
::testing::Values(CV_32F, CV_64F),
|
|
// back substitution works the same regardless of source matrix properties
|
|
::testing::Values(true),
|
|
// back substitution has no sense without u and v
|
|
::testing::Values(true) // needUV
|
|
))
|
|
{
|
|
auto t = GetParam();
|
|
auto rc = std::get<0>(t);
|
|
auto rankEnum = std::get<1>(t);
|
|
int mtype = std::get<2>(t);
|
|
|
|
int rows = std::get<0>(rc);
|
|
int cols = std::get<1>(rc);
|
|
|
|
int rank = std::min(rows, cols);
|
|
switch (rankEnum)
|
|
{
|
|
case RANK_HALF: rank /= 2; break;
|
|
case RANK_MINUS_1: rank -= 1; break;
|
|
default: break;
|
|
}
|
|
|
|
RNG& rng = theRNG();
|
|
Mat A = buildRandomMat(rows, cols, mtype, rng, rank, /* symmetrical */ false);
|
|
cv::SVD svd(A);
|
|
// preallocate to not spend time on it during backSubst()
|
|
Mat dst(cols, 1, mtype);
|
|
Mat rhs(rows, 1, mtype);
|
|
rng.fill(rhs, RNG::UNIFORM, Scalar(-10), Scalar(10));
|
|
|
|
TEST_CYCLE() svd.backSubst(rhs, dst);
|
|
|
|
SANITY_CHECK_NOTHING();
|
|
}
|
|
|
|
|
|
typedef perf::TestBaseWithParam< testing::tuple<int, int, int> > KMeans;
|
|
|
|
PERF_TEST_P_(KMeans, single_iter)
|
|
{
|
|
RNG& rng = theRNG();
|
|
const int K = testing::get<0>(GetParam());
|
|
const int dims = testing::get<1>(GetParam());
|
|
const int N = testing::get<2>(GetParam());
|
|
const int attempts = 5;
|
|
|
|
Mat data(N, dims, CV_32F);
|
|
rng.fill(data, RNG::UNIFORM, -0.1, 0.1);
|
|
|
|
const int N0 = K;
|
|
Mat data0(N0, dims, CV_32F);
|
|
rng.fill(data0, RNG::UNIFORM, -1, 1);
|
|
|
|
for (int i = 0; i < N; i++)
|
|
{
|
|
int base = rng.uniform(0, N0);
|
|
cv::add(data0.row(base), data.row(i), data.row(i));
|
|
}
|
|
|
|
declare.in(data);
|
|
|
|
Mat labels, centers;
|
|
|
|
TEST_CYCLE()
|
|
{
|
|
kmeans(data, K, labels, TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, 1, 0),
|
|
attempts, KMEANS_PP_CENTERS, centers);
|
|
}
|
|
|
|
SANITY_CHECK_NOTHING();
|
|
}
|
|
|
|
PERF_TEST_P_(KMeans, good)
|
|
{
|
|
RNG& rng = theRNG();
|
|
const int K = testing::get<0>(GetParam());
|
|
const int dims = testing::get<1>(GetParam());
|
|
const int N = testing::get<2>(GetParam());
|
|
const int attempts = 5;
|
|
|
|
Mat data(N, dims, CV_32F);
|
|
rng.fill(data, RNG::UNIFORM, -0.1, 0.1);
|
|
|
|
const int N0 = K;
|
|
Mat data0(N0, dims, CV_32F);
|
|
rng.fill(data0, RNG::UNIFORM, -1, 1);
|
|
|
|
for (int i = 0; i < N; i++)
|
|
{
|
|
int base = rng.uniform(0, N0);
|
|
cv::add(data0.row(base), data.row(i), data.row(i));
|
|
}
|
|
|
|
declare.in(data);
|
|
|
|
Mat labels, centers;
|
|
|
|
TEST_CYCLE()
|
|
{
|
|
kmeans(data, K, labels, TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, 30, 0),
|
|
attempts, KMEANS_PP_CENTERS, centers);
|
|
}
|
|
|
|
SANITY_CHECK_NOTHING();
|
|
}
|
|
|
|
PERF_TEST_P_(KMeans, with_duplicates)
|
|
{
|
|
RNG& rng = theRNG();
|
|
const int K = testing::get<0>(GetParam());
|
|
const int dims = testing::get<1>(GetParam());
|
|
const int N = testing::get<2>(GetParam());
|
|
const int attempts = 5;
|
|
|
|
Mat data(N, dims, CV_32F, Scalar::all(0));
|
|
|
|
const int N0 = std::max(2, K * 2 / 3);
|
|
Mat data0(N0, dims, CV_32F);
|
|
rng.fill(data0, RNG::UNIFORM, -1, 1);
|
|
|
|
for (int i = 0; i < N; i++)
|
|
{
|
|
int base = rng.uniform(0, N0);
|
|
data0.row(base).copyTo(data.row(i));
|
|
}
|
|
|
|
declare.in(data);
|
|
|
|
Mat labels, centers;
|
|
|
|
TEST_CYCLE()
|
|
{
|
|
kmeans(data, K, labels, TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, 30, 0),
|
|
attempts, KMEANS_PP_CENTERS, centers);
|
|
}
|
|
|
|
SANITY_CHECK_NOTHING();
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(/*nothing*/ , KMeans,
|
|
testing::Values(
|
|
// K clusters, dims, N points
|
|
testing::make_tuple(2, 3, 100000),
|
|
testing::make_tuple(4, 3, 500),
|
|
testing::make_tuple(4, 3, 1000),
|
|
testing::make_tuple(4, 3, 10000),
|
|
testing::make_tuple(8, 3, 1000),
|
|
testing::make_tuple(8, 16, 1000),
|
|
testing::make_tuple(8, 64, 1000),
|
|
testing::make_tuple(16, 16, 1000),
|
|
testing::make_tuple(16, 32, 1000),
|
|
testing::make_tuple(32, 16, 1000),
|
|
testing::make_tuple(32, 32, 1000),
|
|
testing::make_tuple(100, 2, 1000)
|
|
)
|
|
);
|
|
|
|
}
|
|
|
|
} // namespace
|