#include "perf_precomp.hpp" namespace opencv_test { using namespace perf; namespace { typedef perf::TestBaseWithParam VectorLength; PERF_TEST_P(VectorLength, phase32f, testing::Values(128, 1000, 128*1024, 512*1024, 1024*1024)) { size_t length = GetParam(); vector X(length); vector Y(length); vector 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 X(length); vector Y(length); vector 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 Mat buildRandomMat(int rows, int cols, RNG& rng, int rank, bool symmetrical) { int mtype = cv::traits::Depth::value; Mat u = randomOrtho(rows, mtype, rng); Mat v = randomOrtho(cols, mtype, rng); Mat s(rows, cols, mtype, Scalar(0)); std::vector 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(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(rows, cols, rng, rank, symmetrical); } else if (mtype == CV_64F) { return buildRandomMat(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> 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, 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 > 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