mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 07:43:03 +04:00
Merge pull request #29175 from asmorkalov:as/geometry2
Geometry module #29175 OpenCV Contrib: https://github.com/opencv/opencv_contrib/pull/4129 CI changes: https://github.com/opencv/ci-gha-workflow/pull/313 Continues - https://github.com/opencv/opencv/pull/28804 - https://github.com/opencv/opencv/pull/29101 - https://github.com/opencv/opencv/pull/29108 - https://github.com/opencv/opencv/pull/28810 Todo for followup PRs: - [x] Rename doxygen groups - [x] Fix JS modules layout and whitelists - [ ] Sort tutorials code/snippets ### 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 - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
committed by
GitHub
parent
14a475aa0b
commit
59218f9edd
@@ -0,0 +1,47 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#include "perf_precomp.hpp"
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/ts/ts_perf.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
using namespace perf;
|
||||
|
||||
typedef TestBaseWithParam< tuple<MatDepth, int> > TestMinEnclosingCircle;
|
||||
PERF_TEST_P(TestMinEnclosingCircle, minEnclosingCircle,
|
||||
Combine(
|
||||
testing::Values(CV_32S, CV_32F),
|
||||
Values(400, 1000, 10000, 100000)
|
||||
))
|
||||
{
|
||||
int ptType = get<0>(GetParam());
|
||||
int n = get<1>(GetParam());
|
||||
Mat pts(n, 2, ptType);
|
||||
declare.in(pts, WARMUP_RNG);
|
||||
|
||||
Point2f center;
|
||||
float radius;
|
||||
TEST_CYCLE() minEnclosingCircle(pts, center, radius);
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
typedef TestBaseWithParam<int> TestMinEnclosingCircleWorstCase;
|
||||
PERF_TEST_P(TestMinEnclosingCircleWorstCase, minEnclosingCircle_sequential,
|
||||
Values(400, 1000, 5000, 10000))
|
||||
{
|
||||
int n = GetParam();
|
||||
vector<Point2f> contour;
|
||||
for(int i = 0; i < n; ++i) {
|
||||
float angle = (float)(i * 2 * CV_PI / n);
|
||||
contour.push_back(Point2f(cos(angle) * 100, sin(angle) * 100));
|
||||
}
|
||||
|
||||
Point2f center;
|
||||
float radius;
|
||||
TEST_CYCLE() minEnclosingCircle(contour, center, radius);
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,165 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
// (3-clause BSD License)
|
||||
//
|
||||
// Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the names of the copyright holders nor the names of the contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall copyright holders or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
using namespace perf;
|
||||
|
||||
CV_ENUM(Method, RANSAC, LMEDS)
|
||||
typedef tuple<int, double, Method, size_t> AffineParams;
|
||||
typedef TestBaseWithParam<AffineParams> EstimateAffine;
|
||||
#define ESTIMATE_PARAMS Combine(Values(100000, 5000, 100), Values(0.99, 0.95, 0.9), Method::all(), Values(10, 0))
|
||||
|
||||
static float rngIn(float from, float to) { return from + (to-from) * (float)theRNG(); }
|
||||
|
||||
static Mat rngPartialAffMat() {
|
||||
double theta = rngIn(0, (float)CV_PI*2.f);
|
||||
double scale = rngIn(0, 3);
|
||||
double tx = rngIn(-2, 2);
|
||||
double ty = rngIn(-2, 2);
|
||||
double aff[2*3] = { std::cos(theta) * scale, -std::sin(theta) * scale, tx,
|
||||
std::sin(theta) * scale, std::cos(theta) * scale, ty };
|
||||
return Mat(2, 3, CV_64F, aff).clone();
|
||||
}
|
||||
|
||||
PERF_TEST_P( EstimateAffine, EstimateAffine2D, ESTIMATE_PARAMS )
|
||||
{
|
||||
AffineParams params = GetParam();
|
||||
const int n = get<0>(params);
|
||||
const double confidence = get<1>(params);
|
||||
const int method = get<2>(params);
|
||||
const size_t refining = get<3>(params);
|
||||
|
||||
Mat aff(2, 3, CV_64F);
|
||||
cv::randu(aff, -2., 2.);
|
||||
|
||||
// LMEDS can't handle more than 50% outliers (by design)
|
||||
int m;
|
||||
if (method == LMEDS)
|
||||
m = 3*n/5;
|
||||
else
|
||||
m = 2*n/5;
|
||||
const float shift_outl = 15.f;
|
||||
const float noise_level = 20.f;
|
||||
|
||||
Mat fpts(1, n, CV_32FC2);
|
||||
Mat tpts(1, n, CV_32FC2);
|
||||
|
||||
randu(fpts, 0., 100.);
|
||||
transform(fpts, tpts, aff);
|
||||
|
||||
/* adding noise to some points */
|
||||
Mat outliers = tpts.colRange(m, n);
|
||||
outliers.reshape(1) += shift_outl;
|
||||
|
||||
Mat noise (outliers.size(), outliers.type());
|
||||
randu(noise, 0., noise_level);
|
||||
outliers += noise;
|
||||
|
||||
Mat aff_est;
|
||||
vector<uchar> inliers (n);
|
||||
|
||||
warmup(inliers, WARMUP_WRITE);
|
||||
warmup(fpts, WARMUP_READ);
|
||||
warmup(tpts, WARMUP_READ);
|
||||
|
||||
TEST_CYCLE()
|
||||
{
|
||||
aff_est = estimateAffine2D(fpts, tpts, inliers, method, 3, 2000, confidence, refining);
|
||||
}
|
||||
|
||||
// we already have accuracy tests
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
PERF_TEST_P( EstimateAffine, EstimateAffinePartial2D, ESTIMATE_PARAMS )
|
||||
{
|
||||
AffineParams params = GetParam();
|
||||
const int n = get<0>(params);
|
||||
const double confidence = get<1>(params);
|
||||
const int method = get<2>(params);
|
||||
const size_t refining = get<3>(params);
|
||||
|
||||
Mat aff = rngPartialAffMat();
|
||||
|
||||
int m;
|
||||
// LMEDS can't handle more than 50% outliers (by design)
|
||||
if (method == LMEDS)
|
||||
m = 3*n/5;
|
||||
else
|
||||
m = 2*n/5;
|
||||
const float shift_outl = 15.f; const float noise_level = 20.f;
|
||||
|
||||
Mat fpts(1, n, CV_32FC2);
|
||||
Mat tpts(1, n, CV_32FC2);
|
||||
|
||||
randu(fpts, 0., 100.);
|
||||
transform(fpts, tpts, aff);
|
||||
|
||||
/* adding noise*/
|
||||
Mat outliers = tpts.colRange(m, n);
|
||||
outliers.reshape(1) += shift_outl;
|
||||
|
||||
Mat noise (outliers.size(), outliers.type());
|
||||
randu(noise, 0., noise_level);
|
||||
outliers += noise;
|
||||
|
||||
Mat aff_est;
|
||||
vector<uchar> inliers (n);
|
||||
|
||||
warmup(inliers, WARMUP_WRITE);
|
||||
warmup(fpts, WARMUP_READ);
|
||||
warmup(tpts, WARMUP_READ);
|
||||
|
||||
TEST_CYCLE()
|
||||
{
|
||||
aff_est = estimateAffinePartial2D(fpts, tpts, inliers, method, 3, 2000, confidence, refining);
|
||||
}
|
||||
|
||||
// we already have accuracy tests
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
} // namespace opencv_test
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
#if defined(HAVE_HPX)
|
||||
#include <hpx/hpx_main.hpp>
|
||||
#endif
|
||||
|
||||
CV_PERF_TEST_MAIN(calib3d)
|
||||
@@ -0,0 +1,153 @@
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
using namespace perf;
|
||||
|
||||
CV_ENUM(pnpAlgo, SOLVEPNP_ITERATIVE, SOLVEPNP_EPNP, SOLVEPNP_P3P)
|
||||
|
||||
typedef tuple<int, pnpAlgo> PointsNum_Algo_t;
|
||||
typedef perf::TestBaseWithParam<PointsNum_Algo_t> PointsNum_Algo;
|
||||
|
||||
typedef perf::TestBaseWithParam<int> PointsNum;
|
||||
|
||||
PERF_TEST_P(PointsNum_Algo, solvePnP,
|
||||
testing::Combine( //When non planar, DLT needs at least 6 points for SOLVEPNP_ITERATIVE flag
|
||||
testing::Values(6, 3*9, 7*13), //TODO: find why results on 4 points are too unstable
|
||||
testing::Values((int)SOLVEPNP_ITERATIVE, (int)SOLVEPNP_EPNP)
|
||||
)
|
||||
)
|
||||
{
|
||||
int pointsNum = get<0>(GetParam());
|
||||
pnpAlgo algo = get<1>(GetParam());
|
||||
|
||||
vector<Point2f> points2d(pointsNum);
|
||||
vector<Point3f> points3d(pointsNum);
|
||||
Mat rvec = Mat::zeros(3, 1, CV_32FC1);
|
||||
Mat tvec = Mat::zeros(3, 1, CV_32FC1);
|
||||
|
||||
Mat distortion = Mat::zeros(5, 1, CV_32FC1);
|
||||
Mat intrinsics = Mat::eye(3, 3, CV_32FC1);
|
||||
intrinsics.at<float> (0, 0) = 400.0;
|
||||
intrinsics.at<float> (1, 1) = 400.0;
|
||||
intrinsics.at<float> (0, 2) = 640 / 2;
|
||||
intrinsics.at<float> (1, 2) = 480 / 2;
|
||||
|
||||
warmup(points3d, WARMUP_RNG);
|
||||
warmup(rvec, WARMUP_RNG);
|
||||
warmup(tvec, WARMUP_RNG);
|
||||
|
||||
projectPoints(points3d, rvec, tvec, intrinsics, distortion, points2d);
|
||||
|
||||
//add noise
|
||||
int sz = (int)points2d.size();
|
||||
Mat noise(1, &sz, CV_32FC2);
|
||||
randu(noise, 0, 0.01);
|
||||
cv::add(points2d, noise, points2d);
|
||||
|
||||
declare.in(points3d, points2d);
|
||||
declare.time(100);
|
||||
|
||||
TEST_CYCLE_N(1000)
|
||||
{
|
||||
cv::solvePnP(points3d, points2d, intrinsics, distortion, rvec, tvec, false, algo);
|
||||
}
|
||||
|
||||
SANITY_CHECK(rvec, 1e-4);
|
||||
// the check is relaxed from 1e-4 to 2e-2 after LevMarq replacement
|
||||
SANITY_CHECK(tvec, 2e-2);
|
||||
}
|
||||
|
||||
PERF_TEST_P(PointsNum_Algo, solvePnPSmallPoints,
|
||||
testing::Combine(
|
||||
testing::Values(5),
|
||||
testing::Values((int)SOLVEPNP_P3P, (int)SOLVEPNP_EPNP)
|
||||
)
|
||||
)
|
||||
{
|
||||
int pointsNum = get<0>(GetParam());
|
||||
pnpAlgo algo = get<1>(GetParam());
|
||||
if( algo == SOLVEPNP_P3P )
|
||||
pointsNum = 4;
|
||||
|
||||
vector<Point2f> points2d(pointsNum);
|
||||
vector<Point3f> points3d(pointsNum);
|
||||
Mat rvec = Mat::zeros(3, 1, CV_32FC1);
|
||||
Mat tvec = Mat::zeros(3, 1, CV_32FC1);
|
||||
|
||||
Mat distortion = Mat::zeros(5, 1, CV_32FC1);
|
||||
Mat intrinsics = Mat::eye(3, 3, CV_32FC1);
|
||||
intrinsics.at<float> (0, 0) = 400.0f;
|
||||
intrinsics.at<float> (1, 1) = 400.0f;
|
||||
intrinsics.at<float> (0, 2) = 640 / 2;
|
||||
intrinsics.at<float> (1, 2) = 480 / 2;
|
||||
|
||||
warmup(points3d, WARMUP_RNG);
|
||||
warmup(rvec, WARMUP_RNG);
|
||||
warmup(tvec, WARMUP_RNG);
|
||||
|
||||
// normalize Rodrigues vector
|
||||
Mat rvec_tmp = Mat::eye(3, 3, CV_32F);
|
||||
cv::Rodrigues(rvec, rvec_tmp);
|
||||
cv::Rodrigues(rvec_tmp, rvec);
|
||||
|
||||
cv::projectPoints(points3d, rvec, tvec, intrinsics, distortion, points2d);
|
||||
|
||||
//add noise
|
||||
int npoints = (int)points2d.size();
|
||||
Mat noise(1, &npoints, CV_32FC2);
|
||||
randu(noise, -0.001, 0.001);
|
||||
cv::add(points2d, noise, points2d);
|
||||
|
||||
declare.in(points3d, points2d);
|
||||
declare.time(100);
|
||||
|
||||
TEST_CYCLE_N(1000)
|
||||
{
|
||||
cv::solvePnP(points3d, points2d, intrinsics, distortion, rvec, tvec, false, algo);
|
||||
}
|
||||
|
||||
SANITY_CHECK(rvec, 1e-1);
|
||||
SANITY_CHECK(tvec, 1e-2);
|
||||
}
|
||||
|
||||
PERF_TEST_P(PointsNum, DISABLED_SolvePnPRansac, testing::Values(5, 3*9, 7*13))
|
||||
{
|
||||
int count = GetParam();
|
||||
|
||||
Mat object(1, count, CV_32FC3);
|
||||
randu(object, -100, 100);
|
||||
|
||||
Mat camera_mat(3, 3, CV_32FC1);
|
||||
randu(camera_mat, 0.5, 1);
|
||||
camera_mat.at<float>(0, 1) = 0.f;
|
||||
camera_mat.at<float>(1, 0) = 0.f;
|
||||
camera_mat.at<float>(2, 0) = 0.f;
|
||||
camera_mat.at<float>(2, 1) = 0.f;
|
||||
|
||||
Mat dist_coef(1, 8, CV_32F, cv::Scalar::all(0));
|
||||
|
||||
vector<cv::Point2f> image_vec;
|
||||
|
||||
Mat rvec_gold(1, 3, CV_32FC1);
|
||||
randu(rvec_gold, 0, 1);
|
||||
|
||||
Mat tvec_gold(1, 3, CV_32FC1);
|
||||
randu(tvec_gold, 0, 1);
|
||||
projectPoints(object, rvec_gold, tvec_gold, camera_mat, dist_coef, image_vec);
|
||||
|
||||
Mat image(1, count, CV_32FC2, &image_vec[0]);
|
||||
|
||||
Mat rvec;
|
||||
Mat tvec;
|
||||
|
||||
TEST_CYCLE()
|
||||
{
|
||||
cv::solvePnPRansac(object, image, camera_mat, dist_coef, rvec, tvec);
|
||||
}
|
||||
|
||||
SANITY_CHECK(rvec, 1e-6);
|
||||
SANITY_CHECK(tvec, 1e-6);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,14 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
#ifndef __OPENCV_PERF_PRECOMP_HPP__
|
||||
#define __OPENCV_PERF_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/geometry.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
#include <opencv2/core/ocl.hpp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,342 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
|
||||
// that was easier than using CV_ENUM() macro
|
||||
namespace
|
||||
{
|
||||
using namespace cv;
|
||||
struct ShadingTypeEnum
|
||||
{
|
||||
static const std::array<TriangleShadingType, 3> vals;
|
||||
static const std::array<std::string, 3> svals;
|
||||
|
||||
ShadingTypeEnum(TriangleShadingType v = RASTERIZE_SHADING_WHITE) : val(v) {}
|
||||
operator TriangleShadingType() const { return val; }
|
||||
void PrintTo(std::ostream *os) const
|
||||
{
|
||||
int v = int(val);
|
||||
if (v >= 0 && v < (int)vals.size())
|
||||
{
|
||||
*os << svals[v];
|
||||
}
|
||||
else
|
||||
{
|
||||
*os << "UNKNOWN";
|
||||
}
|
||||
}
|
||||
static ::testing::internal::ParamGenerator<ShadingTypeEnum> all()
|
||||
{
|
||||
return ::testing::Values(ShadingTypeEnum(vals[0]),
|
||||
ShadingTypeEnum(vals[1]),
|
||||
ShadingTypeEnum(vals[2]));
|
||||
}
|
||||
|
||||
private:
|
||||
TriangleShadingType val;
|
||||
};
|
||||
|
||||
const std::array<TriangleShadingType, 3> ShadingTypeEnum::vals
|
||||
{
|
||||
RASTERIZE_SHADING_WHITE,
|
||||
RASTERIZE_SHADING_FLAT,
|
||||
RASTERIZE_SHADING_SHADED
|
||||
};
|
||||
const std::array<std::string, 3> ShadingTypeEnum::svals
|
||||
{
|
||||
std::string("White"),
|
||||
std::string("Flat"),
|
||||
std::string("Shaded")
|
||||
};
|
||||
|
||||
static inline void PrintTo(const ShadingTypeEnum &t, std::ostream *os) { t.PrintTo(os); }
|
||||
|
||||
|
||||
using namespace cv;
|
||||
struct GlCompatibleModeEnum
|
||||
{
|
||||
static const std::array<TriangleGlCompatibleMode, 2> vals;
|
||||
static const std::array<std::string, 2> svals;
|
||||
|
||||
GlCompatibleModeEnum(TriangleGlCompatibleMode v = RASTERIZE_COMPAT_DISABLED) : val(v) {}
|
||||
operator TriangleGlCompatibleMode() const { return val; }
|
||||
void PrintTo(std::ostream *os) const
|
||||
{
|
||||
int v = int(val);
|
||||
if (v >= 0 && v < (int)vals.size())
|
||||
{
|
||||
*os << svals[v];
|
||||
}
|
||||
else
|
||||
{
|
||||
*os << "UNKNOWN";
|
||||
}
|
||||
}
|
||||
static ::testing::internal::ParamGenerator<GlCompatibleModeEnum> all()
|
||||
{
|
||||
return ::testing::Values(GlCompatibleModeEnum(vals[0]),
|
||||
GlCompatibleModeEnum(vals[1]));
|
||||
}
|
||||
|
||||
private:
|
||||
TriangleGlCompatibleMode val;
|
||||
};
|
||||
|
||||
const std::array<TriangleGlCompatibleMode, 2> GlCompatibleModeEnum::vals
|
||||
{
|
||||
RASTERIZE_COMPAT_DISABLED,
|
||||
RASTERIZE_COMPAT_INVDEPTH,
|
||||
};
|
||||
const std::array<std::string, 2> GlCompatibleModeEnum::svals
|
||||
{
|
||||
std::string("Disabled"),
|
||||
std::string("InvertedDepth"),
|
||||
};
|
||||
|
||||
static inline void PrintTo(const GlCompatibleModeEnum &t, std::ostream *os) { t.PrintTo(os); }
|
||||
}
|
||||
|
||||
enum class Outputs
|
||||
{
|
||||
DepthOnly = 0,
|
||||
ColorOnly = 1,
|
||||
DepthColor = 2,
|
||||
};
|
||||
|
||||
// that was easier than using CV_ENUM() macro
|
||||
namespace
|
||||
{
|
||||
using namespace cv;
|
||||
struct OutputsEnum
|
||||
{
|
||||
static const std::array<Outputs, 3> vals;
|
||||
static const std::array<std::string, 3> svals;
|
||||
|
||||
OutputsEnum(Outputs v = Outputs::DepthColor) : val(v) {}
|
||||
operator Outputs() const { return val; }
|
||||
void PrintTo(std::ostream *os) const
|
||||
{
|
||||
int v = int(val);
|
||||
if (v >= 0 && v < (int)vals.size())
|
||||
{
|
||||
*os << svals[v];
|
||||
}
|
||||
else
|
||||
{
|
||||
*os << "UNKNOWN";
|
||||
}
|
||||
}
|
||||
static ::testing::internal::ParamGenerator<OutputsEnum> all()
|
||||
{
|
||||
return ::testing::Values(OutputsEnum(vals[0]),
|
||||
OutputsEnum(vals[1]),
|
||||
OutputsEnum(vals[2]));
|
||||
}
|
||||
|
||||
private:
|
||||
Outputs val;
|
||||
};
|
||||
|
||||
const std::array<Outputs, 3> OutputsEnum::vals
|
||||
{
|
||||
Outputs::DepthOnly,
|
||||
Outputs::ColorOnly,
|
||||
Outputs::DepthColor
|
||||
};
|
||||
const std::array<std::string, 3> OutputsEnum::svals
|
||||
{
|
||||
std::string("DepthOnly"),
|
||||
std::string("ColorOnly"),
|
||||
std::string("DepthColor")
|
||||
};
|
||||
|
||||
static inline void PrintTo(const OutputsEnum &t, std::ostream *os) { t.PrintTo(os); }
|
||||
}
|
||||
|
||||
static Matx44d lookAtMatrixCal(const Vec3d& position, const Vec3d& lookat, const Vec3d& upVector)
|
||||
{
|
||||
Vec3d w = cv::normalize(position - lookat);
|
||||
Vec3d u = cv::normalize(upVector.cross(w));
|
||||
|
||||
Vec3d v = w.cross(u);
|
||||
|
||||
Matx44d res(u[0], u[1], u[2], 0,
|
||||
v[0], v[1], v[2], 0,
|
||||
w[0], w[1], w[2], 0,
|
||||
0, 0, 0, 1.0);
|
||||
|
||||
Matx44d translate(1.0, 0, 0, -position[0],
|
||||
0, 1.0, 0, -position[1],
|
||||
0, 0, 1.0, -position[2],
|
||||
0, 0, 0, 1.0);
|
||||
res = res * translate;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static void generateNormals(const std::vector<Vec3f>& points, const std::vector<std::vector<int>>& indices,
|
||||
std::vector<Vec3f>& normals)
|
||||
{
|
||||
std::vector<std::vector<Vec3f>> preNormals(points.size(), std::vector<Vec3f>());
|
||||
|
||||
for (const auto& tri : indices)
|
||||
{
|
||||
Vec3f p0 = points[tri[0]];
|
||||
Vec3f p1 = points[tri[1]];
|
||||
Vec3f p2 = points[tri[2]];
|
||||
|
||||
Vec3f cross = cv::normalize((p1 - p0).cross(p2 - p0));
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
preNormals[tri[i]].push_back(cross);
|
||||
}
|
||||
}
|
||||
|
||||
normals.reserve(points.size());
|
||||
for (const auto& pn : preNormals)
|
||||
{
|
||||
Vec3f sum { };
|
||||
for (const auto& n : pn)
|
||||
{
|
||||
sum += n;
|
||||
}
|
||||
normals.push_back(cv::normalize(sum));
|
||||
}
|
||||
}
|
||||
|
||||
// load model once and keep it in static memory
|
||||
static void getModelOnce(const std::string& objectPath, std::vector<Vec3f>& vertices,
|
||||
std::vector<Vec3i>& indices, std::vector<Vec3f>& colors)
|
||||
{
|
||||
static bool load = false;
|
||||
static std::vector<Vec3f> vert, col;
|
||||
static std::vector<Vec3i> ind;
|
||||
|
||||
if (!load)
|
||||
{
|
||||
std::vector<vector<int>> indvec;
|
||||
// using per-vertex normals as colors
|
||||
loadMesh(objectPath, vert, indvec);
|
||||
generateNormals(vert, indvec, col);
|
||||
|
||||
for (const auto &vec : indvec)
|
||||
{
|
||||
ind.push_back({vec[0], vec[1], vec[2]});
|
||||
}
|
||||
|
||||
for (auto &color : col)
|
||||
{
|
||||
color = Vec3f(abs(color[0]), abs(color[1]), abs(color[2]));
|
||||
}
|
||||
|
||||
load = true;
|
||||
}
|
||||
|
||||
vertices = vert;
|
||||
colors = col;
|
||||
indices = ind;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string printEnum(T v)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
v.PrintTo(&ss);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// resolution, shading type, outputs needed
|
||||
typedef perf::TestBaseWithParam<std::tuple<std::tuple<int, int>, ShadingTypeEnum, OutputsEnum, GlCompatibleModeEnum>> RenderingTest;
|
||||
|
||||
PERF_TEST_P(RenderingTest, rasterizeTriangles, ::testing::Combine(
|
||||
::testing::Values(std::make_tuple(1920, 1080), std::make_tuple(1024, 768), std::make_tuple(640, 480)),
|
||||
ShadingTypeEnum::all(),
|
||||
OutputsEnum::all(),
|
||||
GlCompatibleModeEnum::all()
|
||||
))
|
||||
{
|
||||
auto t = GetParam();
|
||||
auto wh = std::get<0>(t);
|
||||
int width = std::get<0>(wh);
|
||||
int height = std::get<1>(wh);
|
||||
auto shadingType = std::get<1>(t);
|
||||
auto outputs = std::get<2>(t);
|
||||
auto glCompatibleMode = std::get<3>(t);
|
||||
|
||||
string objectPath = findDataFile("viz/dragon.ply");
|
||||
|
||||
Vec3f position = Vec3d( 1.9, 0.4, 1.3);
|
||||
Vec3f lookat = Vec3d( 0.0, 0.0, 0.0);
|
||||
Vec3f upVector = Vec3d( 0.0, 1.0, 0.0);
|
||||
|
||||
double fovy = 45.0;
|
||||
|
||||
std::vector<Vec3f> vertices;
|
||||
std::vector<Vec3i> indices;
|
||||
std::vector<Vec3f> colors;
|
||||
|
||||
getModelOnce(objectPath, vertices, indices, colors);
|
||||
if (shadingType != RASTERIZE_SHADING_WHITE)
|
||||
{
|
||||
// let vertices be in BGR format to avoid later color conversions
|
||||
// mixChannels does not support in-place operation
|
||||
cv::mixChannels(Mat(colors).clone(), colors, {0, 2, 1, 1, 2, 0});
|
||||
}
|
||||
|
||||
double zNear = 0.1, zFar = 50.0;
|
||||
|
||||
Matx44d cameraPose = lookAtMatrixCal(position, lookat, upVector);
|
||||
double fovYradians = fovy * (CV_PI / 180.0);
|
||||
TriangleRasterizeSettings settings;
|
||||
settings.setCullingMode(RASTERIZE_CULLING_CW)
|
||||
.setShadingType(shadingType)
|
||||
.setGlCompatibleMode(glCompatibleMode);
|
||||
|
||||
Mat depth_buf, color_buf;
|
||||
while (next())
|
||||
{
|
||||
// Prefilled to measure pure rendering time w/o allocation and clear
|
||||
float zMax = (glCompatibleMode == RASTERIZE_COMPAT_INVDEPTH) ? 1.f : (float)zFar;
|
||||
depth_buf = Mat(height, width, CV_32F, zMax);
|
||||
color_buf = Mat(height, width, CV_32FC3, Scalar::all(0));
|
||||
|
||||
startTimer();
|
||||
if (outputs == Outputs::ColorOnly)
|
||||
{
|
||||
cv::triangleRasterizeColor(vertices, indices, colors, color_buf, cameraPose,
|
||||
fovYradians, zNear, zFar, settings);
|
||||
}
|
||||
else if (outputs == Outputs::DepthOnly)
|
||||
{
|
||||
cv::triangleRasterizeDepth(vertices, indices, depth_buf, cameraPose,
|
||||
fovYradians, zNear, zFar, settings);
|
||||
}
|
||||
else // Outputs::DepthColor
|
||||
{
|
||||
cv::triangleRasterize(vertices, indices, colors, color_buf, depth_buf,
|
||||
cameraPose, fovYradians, zNear, zFar, settings);
|
||||
}
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
if (debugLevel > 0)
|
||||
{
|
||||
depth_buf.convertTo(depth_buf, CV_16U, 1000.0);
|
||||
|
||||
std::string shadingName = printEnum(shadingType);
|
||||
std::string suffix = cv::format("%dx%d_%s", width, height, shadingName.c_str());
|
||||
|
||||
imwrite("perf_color_image_" + suffix + ".png", color_buf * 255.f);
|
||||
imwrite("perf_depth_image_" + suffix + ".png", depth_buf);
|
||||
}
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,683 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
using namespace cv;
|
||||
|
||||
/** Reprojects screen point to camera space given z coord. */
|
||||
struct Reprojector
|
||||
{
|
||||
Reprojector() {}
|
||||
inline Reprojector(Matx33f intr)
|
||||
{
|
||||
fxinv = 1.f / intr(0, 0), fyinv = 1.f / intr(1, 1);
|
||||
cx = intr(0, 2), cy = intr(1, 2);
|
||||
}
|
||||
template<typename T>
|
||||
inline cv::Point3_<T> operator()(cv::Point3_<T> p) const
|
||||
{
|
||||
T x = p.z * (p.x - cx) * fxinv;
|
||||
T y = p.z * (p.y - cy) * fyinv;
|
||||
return cv::Point3_<T>(x, y, p.z);
|
||||
}
|
||||
|
||||
float fxinv, fyinv, cx, cy;
|
||||
};
|
||||
|
||||
template<class Scene>
|
||||
struct RenderInvoker : ParallelLoopBody
|
||||
{
|
||||
RenderInvoker(Mat_<float>& _frame, Affine3f _pose,
|
||||
Reprojector _reproj, float _depthFactor, bool _onlySemisphere)
|
||||
: ParallelLoopBody(),
|
||||
frame(_frame),
|
||||
pose(_pose),
|
||||
reproj(_reproj),
|
||||
depthFactor(_depthFactor),
|
||||
onlySemisphere(_onlySemisphere)
|
||||
{ }
|
||||
|
||||
virtual void operator ()(const cv::Range& r) const
|
||||
{
|
||||
for (int y = r.start; y < r.end; y++)
|
||||
{
|
||||
float* frameRow = frame[y];
|
||||
for (int x = 0; x < frame.cols; x++)
|
||||
{
|
||||
float pix = 0;
|
||||
|
||||
Point3f orig = pose.translation();
|
||||
// direction through pixel
|
||||
Point3f screenVec = reproj(Point3f((float)x, (float)y, 1.f));
|
||||
float xyt = 1.f / (screenVec.x * screenVec.x +
|
||||
screenVec.y * screenVec.y + 1.f);
|
||||
Point3f dir = normalize(Vec3f(pose.rotation() * screenVec));
|
||||
// screen space axis
|
||||
dir.y = -dir.y;
|
||||
|
||||
const float maxDepth = 20.f;
|
||||
const float maxSteps = 256;
|
||||
float t = 0.f;
|
||||
for (int step = 0; step < maxSteps && t < maxDepth; step++)
|
||||
{
|
||||
Point3f p = orig + dir * t;
|
||||
float d = Scene::map(p, onlySemisphere);
|
||||
if (d < 0.000001f)
|
||||
{
|
||||
float depth = std::sqrt(t * t * xyt);
|
||||
pix = depth * depthFactor;
|
||||
break;
|
||||
}
|
||||
t += d;
|
||||
}
|
||||
|
||||
frameRow[x] = pix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mat_<float>& frame;
|
||||
Affine3f pose;
|
||||
Reprojector reproj;
|
||||
float depthFactor;
|
||||
bool onlySemisphere;
|
||||
};
|
||||
|
||||
template<class Scene>
|
||||
struct RenderColorInvoker : ParallelLoopBody
|
||||
{
|
||||
RenderColorInvoker(Mat_<Vec3f>& _frame, Affine3f _pose,
|
||||
Reprojector _reproj,
|
||||
float _depthFactor, bool _onlySemisphere) : ParallelLoopBody(),
|
||||
frame(_frame),
|
||||
pose(_pose),
|
||||
reproj(_reproj),
|
||||
depthFactor(_depthFactor),
|
||||
onlySemisphere(_onlySemisphere)
|
||||
{ }
|
||||
|
||||
virtual void operator ()(const cv::Range& r) const
|
||||
{
|
||||
for (int y = r.start; y < r.end; y++)
|
||||
{
|
||||
Vec3f* frameRow = frame[y];
|
||||
for (int x = 0; x < frame.cols; x++)
|
||||
{
|
||||
Vec3f pix = 0;
|
||||
|
||||
Point3f orig = pose.translation();
|
||||
// direction through pixel
|
||||
Point3f screenVec = reproj(Point3f((float)x, (float)y, 1.f));
|
||||
Point3f dir = normalize(Vec3f(pose.rotation() * screenVec));
|
||||
// screen space axis
|
||||
dir.y = -dir.y;
|
||||
|
||||
const float maxDepth = 20.f;
|
||||
const float maxSteps = 256;
|
||||
float t = 0.f;
|
||||
for (int step = 0; step < maxSteps && t < maxDepth; step++)
|
||||
{
|
||||
Point3f p = orig + dir * t;
|
||||
float d = Scene::map(p, onlySemisphere);
|
||||
if (d < 0.000001f)
|
||||
{
|
||||
float m = 0.25f;
|
||||
float p0 = float(abs(fmod(p.x, m)) > m / 2.f);
|
||||
float p1 = float(abs(fmod(p.y, m)) > m / 2.f);
|
||||
float p2 = float(abs(fmod(p.z, m)) > m / 2.f);
|
||||
|
||||
pix[0] = p0 + p1;
|
||||
pix[1] = p1 + p2;
|
||||
pix[2] = p0 + p2;
|
||||
|
||||
pix *= 128.f;
|
||||
break;
|
||||
}
|
||||
t += d;
|
||||
}
|
||||
|
||||
frameRow[x] = pix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mat_<Vec3f>& frame;
|
||||
Affine3f pose;
|
||||
Reprojector reproj;
|
||||
float depthFactor;
|
||||
bool onlySemisphere;
|
||||
};
|
||||
|
||||
|
||||
struct Scene
|
||||
{
|
||||
virtual ~Scene() {}
|
||||
static Ptr<Scene> create(Size sz, Matx33f _intr, float _depthFactor, bool onlySemisphere);
|
||||
virtual Mat_<float> depth(Affine3f pose) = 0;
|
||||
virtual Mat_<Vec3f> rgb(Affine3f pose) = 0;
|
||||
virtual std::vector<Affine3f> getPoses() = 0;
|
||||
};
|
||||
|
||||
struct SemisphereScene : Scene
|
||||
{
|
||||
const int framesPerCycle = 72;
|
||||
const float nCycles = 0.25f;
|
||||
const Affine3f startPose = Affine3f(Vec3f(0.f, 0.f, 0.f), Vec3f(1.5f, 0.3f, -2.1f));
|
||||
|
||||
Size frameSize;
|
||||
Matx33f intr;
|
||||
float depthFactor;
|
||||
bool onlySemisphere;
|
||||
|
||||
SemisphereScene(Size sz, Matx33f _intr, float _depthFactor, bool _onlySemisphere) :
|
||||
frameSize(sz), intr(_intr), depthFactor(_depthFactor), onlySemisphere(_onlySemisphere)
|
||||
{ }
|
||||
|
||||
static float map(Point3f p, bool onlySemisphere)
|
||||
{
|
||||
float plane = p.y + 0.5f;
|
||||
Point3f spherePose = p - Point3f(-0.0f, 0.3f, 1.1f);
|
||||
float sphereRadius = 0.5f;
|
||||
float sphere = (float)cv::norm(spherePose) - sphereRadius;
|
||||
float sphereMinusBox = sphere;
|
||||
|
||||
float subSphereRadius = 0.05f;
|
||||
Point3f subSpherePose = p - Point3f(0.3f, -0.1f, -0.3f);
|
||||
float subSphere = (float)cv::norm(subSpherePose) - subSphereRadius;
|
||||
|
||||
float res;
|
||||
if (!onlySemisphere)
|
||||
res = min({ sphereMinusBox, subSphere, plane });
|
||||
else
|
||||
res = sphereMinusBox;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Mat_<float> depth(Affine3f pose) override
|
||||
{
|
||||
Mat_<float> frame(frameSize);
|
||||
Reprojector reproj(intr);
|
||||
|
||||
Range range(0, frame.rows);
|
||||
parallel_for_(range, RenderInvoker<SemisphereScene>(frame, pose, reproj, depthFactor, onlySemisphere));
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
Mat_<Vec3f> rgb(Affine3f pose) override
|
||||
{
|
||||
Mat_<Vec3f> frame(frameSize);
|
||||
Reprojector reproj(intr);
|
||||
|
||||
Range range(0, frame.rows);
|
||||
parallel_for_(range, RenderColorInvoker<SemisphereScene>(frame, pose, reproj, depthFactor, onlySemisphere));
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
std::vector<Affine3f> getPoses() override
|
||||
{
|
||||
std::vector<Affine3f> poses;
|
||||
for (int i = 0; i < framesPerCycle * nCycles; i++)
|
||||
{
|
||||
float angle = (float)(CV_2PI * i / framesPerCycle);
|
||||
Affine3f pose;
|
||||
pose = pose.rotate(startPose.rotation());
|
||||
pose = pose.rotate(Vec3f(0.f, -0.5f, 0.f) * angle);
|
||||
pose = pose.translate(Vec3f(startPose.translation()[0] * sin(angle),
|
||||
startPose.translation()[1],
|
||||
startPose.translation()[2] * cos(angle)));
|
||||
poses.push_back(pose);
|
||||
}
|
||||
|
||||
return poses;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Ptr<Scene> Scene::create(Size sz, Matx33f _intr, float _depthFactor, bool _onlySemisphere)
|
||||
{
|
||||
return makePtr<SemisphereScene>(sz, _intr, _depthFactor, _onlySemisphere);
|
||||
}
|
||||
|
||||
// this is a temporary solution
|
||||
// ----------------------------
|
||||
|
||||
typedef cv::Vec4f ptype;
|
||||
typedef cv::Mat_< ptype > Points;
|
||||
typedef cv::Mat_< ptype > Colors;
|
||||
typedef Points Normals;
|
||||
typedef Size2i Size;
|
||||
|
||||
template<int p>
|
||||
inline float specPow(float x)
|
||||
{
|
||||
if (p % 2 == 0)
|
||||
{
|
||||
float v = specPow<p / 2>(x);
|
||||
return v * v;
|
||||
}
|
||||
else
|
||||
{
|
||||
float v = specPow<(p - 1) / 2>(x);
|
||||
return v * v * x;
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
inline float specPow<0>(float /*x*/)
|
||||
{
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline float specPow<1>(float x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
inline cv::Vec3f fromPtype(const ptype& x)
|
||||
{
|
||||
return cv::Vec3f(x[0], x[1], x[2]);
|
||||
}
|
||||
|
||||
inline Point3f normalize(const Vec3f& v)
|
||||
{
|
||||
double nv = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
||||
return v * (nv ? 1. / nv : 0.);
|
||||
}
|
||||
|
||||
void renderPointsNormals(InputArray _points, InputArray _normals, OutputArray image, Affine3f lightPose)
|
||||
{
|
||||
Size sz = _points.size();
|
||||
image.create(sz, CV_8UC4);
|
||||
|
||||
Points points = _points.getMat();
|
||||
Normals normals = _normals.getMat();
|
||||
|
||||
Mat_<Vec4b> img = image.getMat();
|
||||
|
||||
Mat goods;
|
||||
finiteMask(points, goods);
|
||||
|
||||
Range range(0, sz.height);
|
||||
const int nstripes = -1;
|
||||
parallel_for_(range, [&](const Range&)
|
||||
{
|
||||
for (int y = range.start; y < range.end; y++)
|
||||
{
|
||||
Vec4b* imgRow = img[y];
|
||||
const ptype* ptsRow = points[y];
|
||||
const ptype* nrmRow = normals[y];
|
||||
const uchar* goodRow = goods.ptr<uchar>(y);
|
||||
|
||||
for (int x = 0; x < sz.width; x++)
|
||||
{
|
||||
Point3f p = fromPtype(ptsRow[x]);
|
||||
Point3f n = fromPtype(nrmRow[x]);
|
||||
|
||||
Vec4b color;
|
||||
|
||||
if ( !goodRow[x] )
|
||||
{
|
||||
color = Vec4b(0, 32, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float Ka = 0.3f; //ambient coeff
|
||||
const float Kd = 0.5f; //diffuse coeff
|
||||
const float Ks = 0.2f; //specular coeff
|
||||
const int sp = 20; //specular power
|
||||
|
||||
const float Ax = 1.f; //ambient color, can be RGB
|
||||
const float Dx = 1.f; //diffuse color, can be RGB
|
||||
const float Sx = 1.f; //specular color, can be RGB
|
||||
const float Lx = 1.f; //light color
|
||||
|
||||
Point3f l = normalize(lightPose.translation() - Vec3f(p));
|
||||
Point3f v = normalize(-Vec3f(p));
|
||||
Point3f r = normalize(Vec3f(2.f * n * n.dot(l) - l));
|
||||
|
||||
uchar ix = (uchar)((Ax * Ka * Dx + Lx * Kd * Dx * max(0.f, n.dot(l)) +
|
||||
Lx * Ks * Sx * specPow<sp>(max(0.f, r.dot(v)))) * 255.f);
|
||||
color = Vec4b(ix, ix, ix, 0);
|
||||
}
|
||||
|
||||
imgRow[x] = color;
|
||||
}
|
||||
}
|
||||
}, nstripes);
|
||||
}
|
||||
void renderPointsNormalsColors(InputArray _points, InputArray, InputArray _colors, OutputArray image, Affine3f)
|
||||
{
|
||||
Size sz = _points.size();
|
||||
image.create(sz, CV_8UC4);
|
||||
|
||||
Points points = _points.getMat();
|
||||
Colors colors = _colors.getMat();
|
||||
|
||||
Mat goods, goodp, goodc;
|
||||
finiteMask(points, goodp);
|
||||
finiteMask(colors, goodc);
|
||||
goods = goodp & goodc;
|
||||
|
||||
Mat_<Vec4b> img = image.getMat();
|
||||
|
||||
Range range(0, sz.height);
|
||||
const int nstripes = -1;
|
||||
parallel_for_(range, [&](const Range&)
|
||||
{
|
||||
for (int y = range.start; y < range.end; y++)
|
||||
{
|
||||
Vec4b* imgRow = img[y];
|
||||
const ptype* clrRow = colors[y];
|
||||
const uchar* goodRow = goods.ptr<uchar>(y);
|
||||
|
||||
for (int x = 0; x < sz.width; x++)
|
||||
{
|
||||
Point3f c = fromPtype(clrRow[x]);
|
||||
|
||||
Vec4b color;
|
||||
|
||||
if ( !goodRow[x] )
|
||||
{
|
||||
color = Vec4b(0, 32, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
color = Vec4b((uchar)c.x, (uchar)c.y, (uchar)c.z, (uchar)0);
|
||||
}
|
||||
|
||||
imgRow[x] = color;
|
||||
}
|
||||
}
|
||||
}, nstripes);
|
||||
}
|
||||
// ----------------------------
|
||||
|
||||
void displayImage(Mat depth, Mat points, Mat normals, float depthFactor, Vec3f lightPose)
|
||||
{
|
||||
Mat image;
|
||||
patchNaNs(points);
|
||||
imshow("depth", depth * (1.f / depthFactor / 4.f));
|
||||
renderPointsNormals(points, normals, image, lightPose);
|
||||
imshow("render", image);
|
||||
waitKey(100);
|
||||
}
|
||||
|
||||
void displayColorImage(Mat depth, Mat rgb, Mat points, Mat normals, Mat colors, float depthFactor, Vec3f lightPose)
|
||||
{
|
||||
Mat image;
|
||||
patchNaNs(points);
|
||||
imshow("depth", depth * (1.f / depthFactor / 4.f));
|
||||
imshow("rgb", rgb * (1.f / 255.f));
|
||||
renderPointsNormalsColors(points, normals, colors, image, lightPose);
|
||||
imshow("render", image);
|
||||
waitKey(100);
|
||||
}
|
||||
|
||||
static const bool display = false;
|
||||
|
||||
enum PlatformType
|
||||
{
|
||||
CPU = 0, GPU = 1
|
||||
};
|
||||
CV_ENUM(PlatformTypeEnum, PlatformType::CPU, PlatformType::GPU);
|
||||
|
||||
enum Sequence
|
||||
{
|
||||
ALL = 0, FIRST = 1
|
||||
};
|
||||
CV_ENUM(SequenceEnum, Sequence::ALL, Sequence::FIRST);
|
||||
|
||||
enum class VolumeTestSrcType
|
||||
{
|
||||
MAT = 0,
|
||||
ODOMETRY_FRAME = 1
|
||||
};
|
||||
|
||||
// used to store current OpenCL status (on/off) and revert it after test is done
|
||||
// works even after exceptions thrown in test body
|
||||
struct OpenCLStatusRevert
|
||||
{
|
||||
#ifdef HAVE_OPENCL
|
||||
OpenCLStatusRevert()
|
||||
{
|
||||
originalOpenCLStatus = cv::ocl::useOpenCL();
|
||||
}
|
||||
~OpenCLStatusRevert()
|
||||
{
|
||||
cv::ocl::setUseOpenCL(originalOpenCLStatus);
|
||||
}
|
||||
void off()
|
||||
{
|
||||
cv::ocl::setUseOpenCL(false);
|
||||
}
|
||||
bool originalOpenCLStatus;
|
||||
#else
|
||||
void off() { }
|
||||
#endif
|
||||
};
|
||||
|
||||
// CV_ENUM does not support enum class types, so let's implement the class explicitly
|
||||
namespace
|
||||
{
|
||||
struct VolumeTypeEnum
|
||||
{
|
||||
static const std::array<VolumeType, 3> vals;
|
||||
static const std::array<std::string, 3> svals;
|
||||
|
||||
VolumeTypeEnum(VolumeType v = VolumeType::TSDF) : val(v) {}
|
||||
operator VolumeType() const { return val; }
|
||||
void PrintTo(std::ostream *os) const
|
||||
{
|
||||
int v = int(val);
|
||||
if (v >= 0 && v < 3)
|
||||
{
|
||||
*os << svals[v];
|
||||
}
|
||||
else
|
||||
{
|
||||
*os << "UNKNOWN";
|
||||
}
|
||||
}
|
||||
static ::testing::internal::ParamGenerator<VolumeTypeEnum> all()
|
||||
{
|
||||
return ::testing::Values(VolumeTypeEnum(vals[0]), VolumeTypeEnum(vals[1]), VolumeTypeEnum(vals[2]));
|
||||
}
|
||||
|
||||
private:
|
||||
VolumeType val;
|
||||
};
|
||||
const std::array<VolumeType, 3> VolumeTypeEnum::vals{VolumeType::TSDF, VolumeType::HashTSDF, VolumeType::ColorTSDF};
|
||||
const std::array<std::string, 3> VolumeTypeEnum::svals{std::string("TSDF"), std::string("HashTSDF"), std::string("ColorTSDF")};
|
||||
|
||||
static inline void PrintTo(const VolumeTypeEnum &t, std::ostream *os) { t.PrintTo(os); }
|
||||
|
||||
|
||||
struct VolumeTestSrcTypeEnum
|
||||
{
|
||||
static const std::array<VolumeTestSrcType, 2> vals;
|
||||
static const std::array<std::string, 2> svals;
|
||||
|
||||
VolumeTestSrcTypeEnum(VolumeTestSrcType v = VolumeTestSrcType::MAT) : val(v) {}
|
||||
operator VolumeTestSrcType() const { return val; }
|
||||
void PrintTo(std::ostream *os) const
|
||||
{
|
||||
int v = int(val);
|
||||
if (v >= 0 && v < 3)
|
||||
{
|
||||
*os << svals[v];
|
||||
}
|
||||
else
|
||||
{
|
||||
*os << "UNKNOWN";
|
||||
}
|
||||
}
|
||||
static ::testing::internal::ParamGenerator<VolumeTestSrcTypeEnum> all()
|
||||
{
|
||||
return ::testing::Values(VolumeTestSrcTypeEnum(vals[0]), VolumeTestSrcTypeEnum(vals[1]));
|
||||
}
|
||||
|
||||
private:
|
||||
VolumeTestSrcType val;
|
||||
};
|
||||
const std::array<VolumeTestSrcType, 2> VolumeTestSrcTypeEnum::vals{VolumeTestSrcType::MAT, VolumeTestSrcType::ODOMETRY_FRAME};
|
||||
const std::array<std::string, 2> VolumeTestSrcTypeEnum::svals{std::string("UMat"), std::string("OdometryFrame")};
|
||||
|
||||
static inline void PrintTo(const VolumeTestSrcTypeEnum &t, std::ostream *os) { t.PrintTo(os); }
|
||||
}
|
||||
|
||||
typedef std::tuple<PlatformTypeEnum, VolumeTypeEnum> PlatformVolumeType;
|
||||
class VolumePerfFixture : public perf::TestBaseWithParam<std::tuple<PlatformVolumeType, VolumeTestSrcTypeEnum, SequenceEnum>>
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
TestBase::SetUp();
|
||||
|
||||
auto p = GetParam();
|
||||
gpu = (std::get<0>(std::get<0>(p)) == PlatformType::GPU);
|
||||
volumeType = std::get<1>(std::get<0>(p));
|
||||
|
||||
testSrcType = std::get<1>(p);
|
||||
|
||||
repeat1st = (std::get<2>(p) == Sequence::FIRST);
|
||||
|
||||
if (!gpu)
|
||||
oclStatus.off();
|
||||
|
||||
VolumeSettings vs(volumeType);
|
||||
volume = makePtr<Volume>(volumeType, vs);
|
||||
|
||||
frameSize = Size(vs.getRaycastWidth(), vs.getRaycastHeight());
|
||||
Matx33f intrIntegrate;
|
||||
vs.getCameraIntegrateIntrinsics(intrIntegrate);
|
||||
vs.getCameraRaycastIntrinsics(intrRaycast);
|
||||
bool onlySemisphere = false;
|
||||
depthFactor = vs.getDepthFactor();
|
||||
scene = Scene::create(frameSize, intrIntegrate, depthFactor, onlySemisphere);
|
||||
poses = scene->getPoses();
|
||||
}
|
||||
|
||||
bool gpu;
|
||||
VolumeType volumeType;
|
||||
VolumeTestSrcType testSrcType;
|
||||
bool repeat1st;
|
||||
|
||||
OpenCLStatusRevert oclStatus;
|
||||
|
||||
Ptr<Volume> volume;
|
||||
Size frameSize;
|
||||
Matx33f intrRaycast;
|
||||
Ptr<Scene> scene;
|
||||
std::vector<Affine3f> poses;
|
||||
float depthFactor;
|
||||
};
|
||||
|
||||
|
||||
PERF_TEST_P_(VolumePerfFixture, integrate)
|
||||
{
|
||||
for (size_t i = 0; i < (repeat1st ? 1 : poses.size()); i++)
|
||||
{
|
||||
Matx44f pose = poses[i].matrix;
|
||||
Mat depth = scene->depth(pose);
|
||||
Mat rgb = scene->rgb(pose);
|
||||
UMat urgb, udepth;
|
||||
depth.copyTo(udepth);
|
||||
rgb.copyTo(urgb);
|
||||
OdometryFrame odf(udepth, urgb);
|
||||
|
||||
bool done = false;
|
||||
while (repeat1st ? next() : !done)
|
||||
{
|
||||
startTimer();
|
||||
if (testSrcType == VolumeTestSrcType::MAT)
|
||||
if (volumeType == VolumeType::ColorTSDF)
|
||||
volume->integrate(udepth, urgb, pose);
|
||||
else
|
||||
volume->integrate(udepth, pose);
|
||||
else if (testSrcType == VolumeTestSrcType::ODOMETRY_FRAME)
|
||||
volume->integrate(odf, pose);
|
||||
stopTimer();
|
||||
|
||||
// perf check makes sense only for identical states
|
||||
if (repeat1st)
|
||||
volume->reset();
|
||||
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
|
||||
PERF_TEST_P_(VolumePerfFixture, raycast)
|
||||
{
|
||||
for (size_t i = 0; i < (repeat1st ? 1 : poses.size()); i++)
|
||||
{
|
||||
Matx44f pose = poses[i].matrix;
|
||||
Mat depth = scene->depth(pose);
|
||||
Mat rgb = scene->rgb(pose);
|
||||
UMat urgb, udepth;
|
||||
depth.copyTo(udepth);
|
||||
rgb.copyTo(urgb);
|
||||
|
||||
OdometryFrame odf(udepth, urgb);
|
||||
|
||||
if (testSrcType == VolumeTestSrcType::MAT)
|
||||
if (volumeType == VolumeType::ColorTSDF)
|
||||
volume->integrate(udepth, urgb, pose);
|
||||
else
|
||||
volume->integrate(udepth, pose);
|
||||
else if (testSrcType == VolumeTestSrcType::ODOMETRY_FRAME)
|
||||
volume->integrate(odf, pose);
|
||||
|
||||
UMat upoints, unormals, ucolors;
|
||||
|
||||
bool done = false;
|
||||
while (repeat1st ? next() : !done)
|
||||
{
|
||||
startTimer();
|
||||
if (volumeType == VolumeType::ColorTSDF)
|
||||
volume->raycast(pose, frameSize.height, frameSize.width, intrRaycast, upoints, unormals, ucolors);
|
||||
else
|
||||
volume->raycast(pose, frameSize.height, frameSize.width, intrRaycast, upoints, unormals);
|
||||
stopTimer();
|
||||
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (display)
|
||||
{
|
||||
Mat points, normals, colors;
|
||||
points = upoints.getMat(ACCESS_READ);
|
||||
normals = unormals.getMat(ACCESS_READ);
|
||||
colors = ucolors.getMat(ACCESS_READ);
|
||||
|
||||
Vec3f lightPose = Vec3f::all(0.f);
|
||||
if (volumeType == VolumeType::ColorTSDF)
|
||||
displayColorImage(depth, rgb, points, normals, colors, depthFactor, lightPose);
|
||||
else
|
||||
displayImage(depth, points, normals, depthFactor, lightPose);
|
||||
}
|
||||
}
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
//TODO: fix it when ColorTSDF gets GPU version
|
||||
INSTANTIATE_TEST_CASE_P(Volume, VolumePerfFixture, /*::testing::Combine(PlatformTypeEnum::all(), VolumeTypeEnum::all())*/
|
||||
::testing::Combine(
|
||||
::testing::Values(PlatformVolumeType {PlatformType::CPU, VolumeType::TSDF},
|
||||
PlatformVolumeType {PlatformType::CPU, VolumeType::HashTSDF},
|
||||
PlatformVolumeType {PlatformType::CPU, VolumeType::ColorTSDF},
|
||||
PlatformVolumeType {PlatformType::GPU, VolumeType::TSDF},
|
||||
PlatformVolumeType {PlatformType::GPU, VolumeType::HashTSDF}),
|
||||
VolumeTestSrcTypeEnum::all(), SequenceEnum::all()));
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,62 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test {
|
||||
|
||||
PERF_TEST(Undistort, InitUndistortMap)
|
||||
{
|
||||
Size size_w_h(512 + 3, 512);
|
||||
Mat k(3, 3, CV_32FC1);
|
||||
Mat d(1, 14, CV_64FC1);
|
||||
Mat dst(size_w_h, CV_32FC2);
|
||||
declare.in(k, d, WARMUP_RNG).out(dst);
|
||||
TEST_CYCLE() initUndistortRectifyMap(k, d, noArray(), k, size_w_h, CV_32FC2, dst, noArray());
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
PERF_TEST(Undistort, DISABLED_InitInverseRectificationMap)
|
||||
{
|
||||
Size size_w_h(512 + 3, 512);
|
||||
Mat k(3, 3, CV_32FC1);
|
||||
Mat d(1, 14, CV_64FC1);
|
||||
Mat dst(size_w_h, CV_32FC2);
|
||||
declare.in(k, d, WARMUP_RNG).out(dst);
|
||||
TEST_CYCLE() initInverseRectificationMap(k, d, noArray(), k, size_w_h, CV_32FC2, dst, noArray());
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
PERF_TEST(Undistort, fisheye_undistortPoints_100k_10iter)
|
||||
{
|
||||
const int pointsNumber = 100000;
|
||||
const Size imageSize(1280, 800);
|
||||
|
||||
/* Set camera matrix */
|
||||
const Matx33d K(558.478087865323, 0, 620.458515360843,
|
||||
0, 560.506767351568, 381.939424848348,
|
||||
0, 0, 1);
|
||||
|
||||
/* Set distortion coefficients */
|
||||
const Matx14d D(2.81e-06, 1.31e-06, -4.42e-06, -1.25e-06);
|
||||
|
||||
/* Create two-channel points matrix */
|
||||
Mat xy[2] = {};
|
||||
xy[0].create(pointsNumber, 1, CV_64F);
|
||||
theRNG().fill(xy[0], RNG::UNIFORM, 0, imageSize.width); // x
|
||||
xy[1].create(pointsNumber, 1, CV_64F);
|
||||
theRNG().fill(xy[1], RNG::UNIFORM, 0, imageSize.height); // y
|
||||
|
||||
Mat points;
|
||||
merge(xy, 2, points);
|
||||
|
||||
/* Set fixed iteration number to check only c++ code, not algo convergence */
|
||||
TermCriteria termCriteria(TermCriteria::MAX_ITER, 10, 0);
|
||||
|
||||
Mat undistortedPoints;
|
||||
TEST_CYCLE() fisheye::undistortPoints(points, undistortedPoints, K, D, noArray(), noArray(), termCriteria);
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user