vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:10:33 +08:00
commit f7f077da11
6933 changed files with 2335208 additions and 0 deletions
@@ -0,0 +1,204 @@
/*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 "test_precomp.hpp"
namespace opencv_test { namespace {
CV_ENUM(Method, RANSAC, LMEDS)
typedef TestWithParam<Method> EstimateAffine2D;
static float rngIn(float from, float to) { return from + (to-from) * (float)theRNG(); }
TEST_P(EstimateAffine2D, test3Points)
{
const int method = GetParam();
for (size_t i = 0; i < 2; ++i)
{
std::vector<cv::Vec2f> fpts(i), tpts(i);
vector<uchar> inliers;
EXPECT_THROW(estimateAffine2D(fpts, tpts, inliers, method), cv::Exception);
}
// try more transformations
for (size_t i = 0; i < 500; ++i)
{
Mat aff(2, 3, CV_64F);
cv::randu(aff, 1., 3.);
Mat fpts(1, 3, CV_32FC2);
Mat tpts(1, 3, CV_32FC2);
// setting points that are not in the same line
fpts.at<Point2f>(0) = Point2f( rngIn(1,2), rngIn(5,6) );
fpts.at<Point2f>(1) = Point2f( rngIn(3,4), rngIn(3,4) );
fpts.at<Point2f>(2) = Point2f( rngIn(1,2), rngIn(3,4) );
transform(fpts, tpts, aff);
vector<uchar> inliers;
Mat aff_est = estimateAffine2D(fpts, tpts, inliers, method);
EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-3);
// all must be inliers
EXPECT_EQ(countNonZero(inliers), 3);
}
}
TEST_P(EstimateAffine2D, testNPoints)
{
// try more transformations
for (size_t i = 0; i < 500; ++i)
{
Mat aff(2, 3, CV_64F);
cv::randu(aff, -2., 2.);
const int method = GetParam();
const int n = 100;
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 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;
vector<uchar> inliers;
Mat aff_est = estimateAffine2D(fpts, tpts, inliers, method);
EXPECT_FALSE(aff_est.empty()) << "estimation failed, unable to estimate transform";
EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-4);
bool inliers_good = std::count(inliers.begin(), inliers.end(), 1) == m &&
m == std::accumulate(inliers.begin(), inliers.begin() + m, 0);
EXPECT_TRUE(inliers_good);
}
}
// test conversion from other datatypes than float
TEST_P(EstimateAffine2D, testConversion)
{
Mat aff(2, 3, CV_32S);
cv::randu(aff, 1., 3.);
std::vector<Point> fpts(3);
std::vector<Point> tpts(3);
// setting points that are not in the same line
fpts[0] = Point2f( rngIn(1,2), rngIn(5,6) );
fpts[1] = Point2f( rngIn(3,4), rngIn(3,4) );
fpts[2] = Point2f( rngIn(1,2), rngIn(3,4) );
transform(fpts, tpts, aff);
vector<uchar> inliers;
Mat aff_est = estimateAffine2D(fpts, tpts, inliers, GetParam() /* method */);
ASSERT_FALSE(aff_est.empty());
aff.convertTo(aff, CV_64F); // need to convert before compare
EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-3);
// all must be inliers
EXPECT_EQ(countNonZero(inliers), 3);
}
INSTANTIATE_TEST_CASE_P(Calib3d, EstimateAffine2D, Method::all());
// https://github.com/opencv/opencv/issues/14259
TEST(EstimateAffine2D, issue_14259_dont_change_inputs)
{
/*const static*/ float pts0_[10] = {
0.0f, 0.0f,
0.0f, 8.0f,
4.0f, 0.0f, // outlier
8.0f, 8.0f,
8.0f, 0.0f
};
/*const static*/ float pts1_[10] = {
0.1f, 0.1f,
0.1f, 8.1f,
0.0f, 4.0f, // outlier
8.1f, 8.1f,
8.1f, 0.1f
};
Mat pts0(Size(1, 5), CV_32FC2, (void*)pts0_);
Mat pts1(Size(1, 5), CV_32FC2, (void*)pts1_);
Mat pts0_copy = pts0.clone();
Mat pts1_copy = pts1.clone();
Mat inliers;
cv::Mat A = cv::estimateAffine2D(pts0, pts1, inliers);
for(int i = 0; i < pts0.rows; ++i)
{
EXPECT_EQ(pts0_copy.at<Vec2f>(i), pts0.at<Vec2f>(i)) << "pts0: i=" << i;
}
for(int i = 0; i < pts1.rows; ++i)
{
EXPECT_EQ(pts1_copy.at<Vec2f>(i), pts1.at<Vec2f>(i)) << "pts1: i=" << i;
}
EXPECT_EQ(0, (int)inliers.at<uchar>(2));
}
}} // namespace
+109
View File
@@ -0,0 +1,109 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// 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
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2008-2013, Willow Garage Inc., 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:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 the Intel Corporation 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 "test_precomp.hpp"
#include "opencv2/core/affine.hpp"
namespace opencv_test { namespace {
TEST(Calib3d_Affine3f, accuracy)
{
const double eps = 1e-5;
cv::Vec3d rvec(0.2, 0.5, 0.3);
cv::Affine3d affine(rvec);
cv::Mat expected;
cv::Rodrigues(rvec, expected);
ASSERT_LE(cvtest::norm(cv::Mat(affine.matrix, false).colRange(0, 3).rowRange(0, 3), expected, cv::NORM_L2), eps);
ASSERT_LE(cvtest::norm(cv::Mat(affine.linear()), expected, cv::NORM_L2), eps);
cv::Matx33d R = cv::Matx33d::eye();
double angle = 50;
R.val[0] = R.val[4] = std::cos(CV_PI*angle/180.0);
R.val[3] = std::sin(CV_PI*angle/180.0);
R.val[1] = -R.val[3];
cv::Affine3d affine1(cv::Mat(cv::Vec3d(0.2, 0.5, 0.3)).reshape(1, 1), cv::Vec3d(4, 5, 6));
cv::Affine3d affine2(R, cv::Vec3d(1, 1, 0.4));
cv::Affine3d result = affine1.inv() * affine2;
expected = cv::Mat(affine1.matrix.inv(cv::DECOMP_SVD)) * cv::Mat(affine2.matrix, false);
cv::Mat diff;
cv::absdiff(expected, result.matrix, diff);
ASSERT_LT(cvtest::norm(diff, cv::NORM_INF), 1e-15);
}
TEST(Calib3d_Affine3f, accuracy_rvec)
{
cv::RNG rng;
typedef float T;
cv::Affine3<T>::Vec3 w;
cv::Affine3<T>::Mat3 u, vt, R;
for(int i = 0; i < 100; ++i)
{
rng.fill(R, cv::RNG::UNIFORM, -10, 10, true);
cv::SVD::compute(R, w, u, vt, cv::SVD::FULL_UV + cv::SVD::MODIFY_A);
R = u * vt;
//double s = (double)cv::getTickCount();
cv::Affine3<T>::Vec3 va = cv::Affine3<T>(R).rvec();
//std::cout << "M:" <<(cv::getTickCount() - s)*1000/cv::getTickFrequency() << std::endl;
cv::Affine3<T>::Vec3 vo;
//s = (double)cv::getTickCount();
cv::Rodrigues(R, vo);
//std::cout << "O:" <<(cv::getTickCount() - s)*1000/cv::getTickFrequency() << std::endl;
ASSERT_LT(cvtest::norm(va, vo, cv::NORM_L2), 1e-9);
}
}
}} // namespace
@@ -0,0 +1,226 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// 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
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., 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:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 the Intel Corporation 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 "test_precomp.hpp"
#include "opencv2/core/affine.hpp"
namespace opencv_test { namespace {
class CV_Affine3D_EstTest : public cvtest::BaseTest
{
public:
CV_Affine3D_EstTest();
~CV_Affine3D_EstTest();
protected:
void run(int);
bool test4Points();
bool testNPoints();
};
CV_Affine3D_EstTest::CV_Affine3D_EstTest()
{
}
CV_Affine3D_EstTest::~CV_Affine3D_EstTest() {}
float rngIn(float from, float to) { return from + (to-from) * (float)theRNG(); }
struct WrapAff
{
const double *F;
WrapAff(const Mat& aff) : F(aff.ptr<double>()) {}
Point3f operator()(const Point3f& p)
{
return Point3f( (float)(p.x * F[0] + p.y * F[1] + p.z * F[2] + F[3]),
(float)(p.x * F[4] + p.y * F[5] + p.z * F[6] + F[7]),
(float)(p.x * F[8] + p.y * F[9] + p.z * F[10] + F[11]) );
}
};
bool CV_Affine3D_EstTest::test4Points()
{
Mat aff(3, 4, CV_64F);
cv::randu(aff, Scalar(1), Scalar(3));
// setting points that are no in the same line
Mat fpts(1, 4, CV_32FC3);
Mat tpts(1, 4, CV_32FC3);
fpts.ptr<Point3f>()[0] = Point3f( rngIn(1,2), rngIn(1,2), rngIn(5, 6) );
fpts.ptr<Point3f>()[1] = Point3f( rngIn(3,4), rngIn(3,4), rngIn(5, 6) );
fpts.ptr<Point3f>()[2] = Point3f( rngIn(1,2), rngIn(3,4), rngIn(5, 6) );
fpts.ptr<Point3f>()[3] = Point3f( rngIn(3,4), rngIn(1,2), rngIn(5, 6) );
std::transform(fpts.ptr<Point3f>(), fpts.ptr<Point3f>() + 4, tpts.ptr<Point3f>(), WrapAff(aff));
Mat aff_est;
vector<uchar> outliers;
estimateAffine3D(fpts, tpts, aff_est, outliers);
const double thres = 1e-3;
if (cvtest::norm(aff_est, aff, NORM_INF) > thres)
{
//cout << cvtest::norm(aff_est, aff, NORM_INF) << endl;
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return false;
}
return true;
}
struct Noise
{
float l;
Noise(float level) : l(level) {}
Point3f operator()(const Point3f& p)
{
RNG& rng = theRNG();
return Point3f( p.x + l * (float)rng, p.y + l * (float)rng, p.z + l * (float)rng);
}
};
bool CV_Affine3D_EstTest::testNPoints()
{
Mat aff(3, 4, CV_64F);
cv::randu(aff, Scalar(-2), Scalar(2));
// setting points that are no in the same line
const int n = 100;
const int m = 3*n/5;
const Point3f shift_outl = Point3f(15, 15, 15);
const float noise_level = 20.f;
Mat fpts(1, n, CV_32FC3);
Mat tpts(1, n, CV_32FC3);
randu(fpts, Scalar::all(0), Scalar::all(100));
std::transform(fpts.ptr<Point3f>(), fpts.ptr<Point3f>() + n, tpts.ptr<Point3f>(), WrapAff(aff));
/* adding noise*/
std::transform(tpts.ptr<Point3f>() + m, tpts.ptr<Point3f>() + n, tpts.ptr<Point3f>() + m,
[=] (const Point3f& pt) -> Point3f { return Noise(noise_level)(pt + shift_outl); });
Mat aff_est;
vector<uchar> outl;
int res = estimateAffine3D(fpts, tpts, aff_est, outl);
if (!res)
{
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return false;
}
const double thres = 1e-4;
if (cvtest::norm(aff_est, aff, NORM_INF) > thres)
{
cout << "aff est: " << aff_est << endl;
cout << "aff ref: " << aff << endl;
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return false;
}
bool outl_good = std::count(outl.begin(), outl.end(), 1) == m &&
m == std::accumulate(outl.begin(), outl.begin() + m, 0);
if (!outl_good)
{
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return false;
}
return true;
}
void CV_Affine3D_EstTest::run( int /* start_from */)
{
cvtest::DefaultRngAuto dra;
if (!test4Points())
return;
if (!testNPoints())
return;
ts->set_failed_test_info(cvtest::TS::OK);
}
TEST(Calib3d_EstimateAffine3D, accuracy) { CV_Affine3D_EstTest test; test.safe_run(); }
TEST(Calib3d_EstimateAffine3D, regression_16007)
{
std::vector<cv::Point3f> m1, m2;
m1.push_back(Point3f(1.0f, 0.0f, 0.0f)); m2.push_back(Point3f(1.0f, 1.0f, 0.0f));
m1.push_back(Point3f(1.0f, 0.0f, 1.0f)); m2.push_back(Point3f(1.0f, 1.0f, 1.0f));
m1.push_back(Point3f(0.5f, 0.0f, 0.5f)); m2.push_back(Point3f(0.5f, 1.0f, 0.5f));
m1.push_back(Point3f(2.5f, 0.0f, 2.5f)); m2.push_back(Point3f(2.5f, 1.0f, 2.5f));
m1.push_back(Point3f(2.0f, 0.0f, 1.0f)); m2.push_back(Point3f(2.0f, 1.0f, 1.0f));
cv::Mat m3D, inl;
int res = cv::estimateAffine3D(m1, m2, m3D, inl);
EXPECT_EQ(1, res);
}
TEST(Calib3d_EstimateAffine3D, umeyama_3_pt)
{
std::vector<cv::Vec3d> points = {{{0.80549149, 0.8225781, 0.79949521},
{0.28906756, 0.57158557, 0.9864789},
{0.58266182, 0.65474983, 0.25078834}}};
cv::Mat R = (cv::Mat_<double>(3,3) << 0.9689135, -0.0232753, 0.2463025,
0.0236362, 0.9997195, 0.0014915,
-0.2462682, 0.0043765, 0.9691918);
cv::Vec3d t(1., 2., 3.);
cv::Affine3d transform(R, t);
std::vector<cv::Vec3d> transformed_points(points.size());
std::transform(points.begin(), points.end(), transformed_points.begin(), [transform](const cv::Vec3d v){return transform * v;});
double scale;
cv::Mat trafo_est = estimateAffine3D(points, transformed_points, &scale);
Mat R_est(trafo_est(Rect(0, 0, 3, 3)));
EXPECT_LE(cvtest::norm(R_est, R, NORM_INF), 1e-6);
Vec3d t_est = trafo_est.col(3);
EXPECT_LE(cvtest::norm(t_est, t, NORM_INF), 1e-6);
EXPECT_NEAR(scale, 1.0, 1e-6);
}
}} // namespace
@@ -0,0 +1,213 @@
/*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 "test_precomp.hpp"
namespace opencv_test { namespace {
CV_ENUM(Method, RANSAC, LMEDS)
typedef TestWithParam<Method> EstimateAffinePartial2D;
static float rngIn(float from, float to) { return from + (to-from) * (float)theRNG(); }
// get random matrix of affine transformation limited to combinations of translation,
// rotation, and uniform scaling
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();
}
TEST_P(EstimateAffinePartial2D, test2Points)
{
const int method = GetParam();
for (size_t i = 0; i < 2; ++i)
{
std::vector<cv::Vec2f> fpts(i), tpts(i);
vector<uchar> inliers;
EXPECT_THROW(estimateAffinePartial2D(fpts, tpts, inliers, method), cv::Exception);
}
// try more transformations
for (size_t i = 0; i < 500; ++i)
{
Mat aff = rngPartialAffMat();
// setting points that are no in the same line
Mat fpts(1, 2, CV_32FC2);
Mat tpts(1, 2, CV_32FC2);
fpts.at<Point2f>(0) = Point2f( rngIn(1,2), rngIn(5,6) );
fpts.at<Point2f>(1) = Point2f( rngIn(3,4), rngIn(3,4) );
transform(fpts, tpts, aff);
vector<uchar> inliers;
Mat aff_est = estimateAffinePartial2D(fpts, tpts, inliers, method);
EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-3);
// all must be inliers
EXPECT_EQ(countNonZero(inliers), 2);
}
}
TEST_P(EstimateAffinePartial2D, testNPoints)
{
// try more transformations
for (size_t i = 0; i < 500; ++i)
{
Mat aff = rngPartialAffMat();
const int method = GetParam();
const int n = 100;
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 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;
vector<uchar> inliers;
Mat aff_est = estimateAffinePartial2D(fpts, tpts, inliers, method);
EXPECT_FALSE(aff_est.empty());
EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-4);
bool inliers_good = std::count(inliers.begin(), inliers.end(), 1) == m &&
m == std::accumulate(inliers.begin(), inliers.begin() + m, 0);
EXPECT_TRUE(inliers_good);
}
}
// test conversion from other datatypes than float
TEST_P(EstimateAffinePartial2D, testConversion)
{
Mat aff = rngPartialAffMat();
aff.convertTo(aff, CV_32S); // convert to int to transform ints properly
std::vector<Point> fpts(3);
std::vector<Point> tpts(3);
fpts[0] = Point2f( rngIn(1,2), rngIn(5,6) );
fpts[1] = Point2f( rngIn(3,4), rngIn(3,4) );
fpts[2] = Point2f( rngIn(1,2), rngIn(3,4) );
transform(fpts, tpts, aff);
vector<uchar> inliers;
Mat aff_est = estimateAffinePartial2D(fpts, tpts, inliers, GetParam() /* method */);
ASSERT_FALSE(aff_est.empty());
aff.convertTo(aff, CV_64F); // need to convert back before compare
EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-3);
// all must be inliers
EXPECT_EQ(countNonZero(inliers), 3);
}
INSTANTIATE_TEST_CASE_P(Calib3d, EstimateAffinePartial2D, Method::all());
// https://github.com/opencv/opencv/issues/14259
TEST(EstimateAffinePartial2D, issue_14259_dont_change_inputs)
{
/*const static*/ float pts0_[10] = {
0.0f, 0.0f,
0.0f, 8.0f,
4.0f, 0.0f, // outlier
8.0f, 8.0f,
8.0f, 0.0f
};
/*const static*/ float pts1_[10] = {
0.1f, 0.1f,
0.1f, 8.1f,
0.0f, 4.0f, // outlier
8.1f, 8.1f,
8.1f, 0.1f
};
Mat pts0(Size(1, 5), CV_32FC2, (void*)pts0_);
Mat pts1(Size(1, 5), CV_32FC2, (void*)pts1_);
Mat pts0_copy = pts0.clone();
Mat pts1_copy = pts1.clone();
Mat inliers;
cv::Mat A = cv::estimateAffinePartial2D(pts0, pts1, inliers);
for(int i = 0; i < pts0.rows; ++i)
{
EXPECT_EQ(pts0_copy.at<Vec2f>(i), pts0.at<Vec2f>(i)) << "pts0: i=" << i;
}
for(int i = 0; i < pts1.rows; ++i)
{
EXPECT_EQ(pts1_copy.at<Vec2f>(i), pts1.at<Vec2f>(i)) << "pts1: i=" << i;
}
EXPECT_EQ(0, (int)inliers.at<uchar>(2));
}
}} // namespace
+167
View File
@@ -0,0 +1,167 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// 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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, 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:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of Intel Corporation may not 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 the Intel Corporation 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 "test_precomp.hpp"
namespace opencv_test { namespace {
//
// TODO!!!:
// check_slice (and/or check) seem(s) to be broken, or this is a bug in function
// (or its inability to handle possible self-intersections in the generated contours).
//
// At least, if // return TotalErrors;
// is uncommented in check_slice, the test fails easily.
// So, now (and it looks like since 0.9.6)
// we only check that the set of vertices of the approximated polygon is
// a subset of vertices of the original contour.
//
//Tests to make sure that unreasonable epsilon (error)
//values never get passed to the Douglas-Peucker algorithm.
TEST(Imgproc_ApproxPoly, bad_epsilon)
{
std::vector<Point2f> inputPoints;
inputPoints.push_back(Point2f(0.0f, 0.0f));
std::vector<Point2f> outputPoints;
double eps = std::numeric_limits<double>::infinity();
ASSERT_ANY_THROW(approxPolyDP(inputPoints, outputPoints, eps, false));
eps = 9e99;
ASSERT_ANY_THROW(approxPolyDP(inputPoints, outputPoints, eps, false));
eps = -1e-6;
ASSERT_ANY_THROW(approxPolyDP(inputPoints, outputPoints, eps, false));
eps = NAN;
ASSERT_ANY_THROW(approxPolyDP(inputPoints, outputPoints, eps, false));
}
TEST(Imgproc_ApproxPoly, distace_between_point_and_segment)
{
vector<Point2f> inputPoints = {
{ {0.f, 0.f}, {4.f, 2.f}, {11.f, 1.f}, {8.f, 0.f} }
};
std::vector<Point2f> result;
approxPolyDP(inputPoints, result, 1.9, false);
vector<Point2f> expectedResult = {
{ {0.f, 0.f}, {11.f, 1.f}, {8.f, 0.f} }
};
ASSERT_EQ(result, expectedResult);
}
struct ApproxPolyN: public testing::Test
{
void SetUp()
{
vector<vector<Point>> inputPoints = {
{ {87, 103}, {100, 112}, {96, 138}, {80, 169}, {60, 183}, {38, 176}, {41, 145}, {56, 118}, {76, 104} },
{ {196, 102}, {205, 118}, {174, 196}, {152, 207}, {102, 194}, {100, 175}, {131, 109} },
{ {372, 101}, {377, 119}, {337, 238}, {324, 248}, {240, 229}, {199, 214}, {232, 123}, {245, 103} },
{ {463, 86}, {563, 112}, {574, 135}, {596, 221}, {518, 298}, {412, 266}, {385, 164}, {462, 86} }
};
Mat image(600, 600, CV_8UC1, Scalar(0));
for (vector<Point>& polygon : inputPoints) {
polylines(image, { polygon }, true, Scalar(255), 1);
}
findContours(image, contours, RETR_LIST, CHAIN_APPROX_NONE);
}
vector<vector<Point>> contours;
};
TEST_F(ApproxPolyN, accuracyInt)
{
vector<vector<Point>> rightCorners = {
{ {72, 187}, {37, 176}, {42, 127}, {133, 64} },
{ {168, 212}, {92, 192}, {131, 109}, {213, 100} },
{ {72, 187}, {37, 176}, {42, 127}, {133, 64} },
{ {384, 100}, {333, 251}, {197, 220}, {239, 103} },
{ {168, 212}, {92, 192}, {131, 109}, {213, 100} },
{ {333, 251}, {197, 220}, {239, 103}, {384, 100} },
{ {542, 6}, {596, 221}, {518, 299}, {312, 236} },
{ {596, 221}, {518, 299}, {312, 236}, {542, 6} }
};
EXPECT_EQ(rightCorners.size(), contours.size());
for (size_t i = 0; i < contours.size(); ++i) {
std::vector<Point> corners;
approxPolyN(contours[i], corners, 4, -1, true);
ASSERT_EQ(rightCorners[i], corners );
}
}
TEST_F(ApproxPolyN, accuracyFloat)
{
vector<vector<Point2f>> rightCorners = {
{ {72.f, 187.f}, {37.f, 176.f}, {42.f, 127.f}, {133.f, 64.f} },
{ {168.f, 212.f}, {92.f, 192.f}, {131.f, 109.f}, {213.f, 100.f} },
{ {72.f, 187.f}, {37.f, 176.f}, {42.f, 127.f}, {133.f, 64.f} },
{ {384.f, 100.f}, {333.f, 251.f}, {197.f, 220.f}, {239.f, 103.f} },
{ {168.f, 212.f}, {92.f, 192.f}, {131.f, 109.f}, {213.f, 100.f} },
{ {333.f, 251.f}, {197.f, 220.f}, {239.f, 103.f}, {384.f, 100.f} },
{ {542.f, 6.f}, {596.f, 221.f}, {518.f, 299.f}, {312.f, 236.f} },
{ {596.f, 221.f}, {518.f, 299.f}, {312.f, 236.f}, {542.f, 6.f} }
};
EXPECT_EQ(rightCorners.size(), contours.size());
for (size_t i = 0; i < contours.size(); ++i) {
std::vector<Point2f> corners;
approxPolyN(contours[i], corners, 4, -1, true);
EXPECT_LT(cvtest::norm(rightCorners[i], corners, NORM_INF), .5f);
}
}
TEST_F(ApproxPolyN, bad_args)
{
Mat contour(10, 1, CV_32FC2);
vector<vector<Point>> bad_contours;
vector<Point> corners;
ASSERT_ANY_THROW(approxPolyN(contour, corners, 0));
ASSERT_ANY_THROW(approxPolyN(contour, corners, 3, 0));
ASSERT_ANY_THROW(approxPolyN(bad_contours, corners, 4));
}
}} // namespace
@@ -0,0 +1,99 @@
// 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 "opencv2/core/types.hpp"
#include "test_precomp.hpp"
using namespace cv;
using namespace std;
namespace opencv_test { namespace {
template <typename T>
cv::Rect calcBoundingRect(Mat pts)
{
CV_Assert(pts.type() == CV_32FC2 || pts.type() == CV_32SC2);
CV_Assert(pts.size().width == 1 && pts.size().height > 0);
const int N = pts.size().height;
// NOTE: using ::lowest(), not ::min()
T min_w = std::numeric_limits<T>::max(), max_w = std::numeric_limits<T>::lowest();
T min_h = min_w, max_h = max_w;
for (int i = 0; i < N; ++i)
{
const Point_<T> & pt = pts.at<Point_<T>>(i, 0);
min_w = std::min<T>(pt.x, min_w);
max_w = std::max<T>(pt.x, max_w);
min_h = std::min<T>(pt.y, min_h);
max_h = std::max<T>(pt.y, max_h);
}
return Rect(cvFloor(min_w), cvFloor(min_h), cvFloor(max_w) - cvFloor(min_w) + 1, cvFloor(max_h) - cvFloor(min_h) + 1);
}
typedef ::testing::TestWithParam<int> Imgproc_BoundingRect_Types;
TEST_P(Imgproc_BoundingRect_Types, accuracy)
{
const int depth = GetParam();
RNG& rng = ::cvtest::TS::ptr()->get_rng();
for (int k = 0; k < 1000; ++k)
{
SCOPED_TRACE(cv::format("k=%d", k));
const int sz = rng.uniform(1, 10000);
Mat src(sz, 1, CV_MAKETYPE(depth, 2));
rng.fill(src, RNG::UNIFORM, Scalar(-100000, -100000), Scalar(100000, 100000));
Rect reference;
if (depth == CV_32F)
reference = calcBoundingRect<float>(src);
else if (depth == CV_32S)
reference = calcBoundingRect<int>(src);
else
CV_Error(Error::StsError, "Test error");
Rect result = cv::boundingRect(src);
EXPECT_EQ(reference, result);
}
}
TEST_P(Imgproc_BoundingRect_Types, alignment)
{
const int depth = GetParam();
const int SZ = 100;
int idata[SZ];
float fdata[SZ];
for (int i = 0; i < SZ; ++i)
{
idata[i] = i;
fdata[i] = (float)i;
}
for (int i = 0; i < 10; ++i)
{
for (int len = 1; len < 40; ++len)
{
SCOPED_TRACE(cv::format("i=%d, len=%d", i, len));
Mat sub(len, 1, CV_MAKETYPE(depth, 2), (depth == CV_32S) ? (void*)(idata + i) : (void*)(fdata + i));
EXPECT_NO_THROW(boundingRect(sub));
}
}
}
INSTANTIATE_TEST_CASE_P(, Imgproc_BoundingRect_Types, ::testing::Values(CV_32S, CV_32F));
TEST(Imgproc_BoundingRect, bug_24217)
{
for (int image_width = 3; image_width < 20; image_width++)
{
for (int image_height = 1; image_height < 15; image_height++)
{
cv::Rect rect(0, image_height - 1, 3, 1);
cv::Mat image(cv::Size(image_width, image_height), CV_8UC1, cv::Scalar(0));
image(rect) = 255;
ASSERT_EQ(boundingRect(image), rect);
}
}
}
}} // namespace
File diff suppressed because it is too large Load Diff
+215
View File
@@ -0,0 +1,215 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// 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
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., 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:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 the Intel Corporation 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 "test_precomp.hpp"
namespace opencv_test { namespace {
class Differential
{
public:
typedef Mat_<double> mat_t;
Differential(double eps_, const mat_t& rv1_, const mat_t& tv1_, const mat_t& rv2_, const mat_t& tv2_)
: rv1(rv1_), tv1(tv1_), rv2(rv2_), tv2(tv2_), eps(eps_), ev(3, 1) {}
void dRv1(mat_t& dr3_dr1, mat_t& dt3_dr1)
{
dr3_dr1.create(3, 3); dt3_dr1.create(3, 3);
for(int i = 0; i < 3; ++i)
{
ev.setTo(Scalar(0)); ev(i, 0) = eps;
composeRT( rv1 + ev, tv1, rv2, tv2, rv3_p, tv3_p);
composeRT( rv1 - ev, tv1, rv2, tv2, rv3_m, tv3_m);
dr3_dr1.col(i) = rv3_p - rv3_m;
dt3_dr1.col(i) = tv3_p - tv3_m;
}
dr3_dr1 /= 2 * eps; dt3_dr1 /= 2 * eps;
}
void dRv2(mat_t& dr3_dr2, mat_t& dt3_dr2)
{
dr3_dr2.create(3, 3); dt3_dr2.create(3, 3);
for(int i = 0; i < 3; ++i)
{
ev.setTo(Scalar(0)); ev(i, 0) = eps;
composeRT( rv1, tv1, rv2 + ev, tv2, rv3_p, tv3_p);
composeRT( rv1, tv1, rv2 - ev, tv2, rv3_m, tv3_m);
dr3_dr2.col(i) = rv3_p - rv3_m;
dt3_dr2.col(i) = tv3_p - tv3_m;
}
dr3_dr2 /= 2 * eps; dt3_dr2 /= 2 * eps;
}
void dTv1(mat_t& drt3_dt1, mat_t& dt3_dt1)
{
drt3_dt1.create(3, 3); dt3_dt1.create(3, 3);
for(int i = 0; i < 3; ++i)
{
ev.setTo(Scalar(0)); ev(i, 0) = eps;
composeRT( rv1, tv1 + ev, rv2, tv2, rv3_p, tv3_p);
composeRT( rv1, tv1 - ev, rv2, tv2, rv3_m, tv3_m);
drt3_dt1.col(i) = rv3_p - rv3_m;
dt3_dt1.col(i) = tv3_p - tv3_m;
}
drt3_dt1 /= 2 * eps; dt3_dt1 /= 2 * eps;
}
void dTv2(mat_t& dr3_dt2, mat_t& dt3_dt2)
{
dr3_dt2.create(3, 3); dt3_dt2.create(3, 3);
for(int i = 0; i < 3; ++i)
{
ev.setTo(Scalar(0)); ev(i, 0) = eps;
composeRT( rv1, tv1, rv2, tv2 + ev, rv3_p, tv3_p);
composeRT( rv1, tv1, rv2, tv2 - ev, rv3_m, tv3_m);
dr3_dt2.col(i) = rv3_p - rv3_m;
dt3_dt2.col(i) = tv3_p - tv3_m;
}
dr3_dt2 /= 2 * eps; dt3_dt2 /= 2 * eps;
}
private:
const mat_t& rv1, tv1, rv2, tv2;
double eps;
Mat_<double> ev;
Differential& operator=(const Differential&);
Mat rv3_m, tv3_m, rv3_p, tv3_p;
};
class CV_composeRT_Test : public cvtest::BaseTest
{
public:
CV_composeRT_Test() {}
~CV_composeRT_Test() {}
protected:
void run(int)
{
ts->set_failed_test_info(cvtest::TS::OK);
Mat_<double> rvec1(3, 1), tvec1(3, 1), rvec2(3, 1), tvec2(3, 1);
randu(rvec1, Scalar(0), Scalar(6.29));
randu(rvec2, Scalar(0), Scalar(6.29));
randu(tvec1, Scalar(-2), Scalar(2));
randu(tvec2, Scalar(-2), Scalar(2));
Mat rvec3, tvec3;
composeRT(rvec1, tvec1, rvec2, tvec2, rvec3, tvec3);
Mat rvec3_exp, tvec3_exp;
Mat rmat1, rmat2;
cv::Rodrigues(rvec1, rmat1); // TODO cvtest
cv::Rodrigues(rvec2, rmat2); // TODO cvtest
cv::Rodrigues(rmat2 * rmat1, rvec3_exp); // TODO cvtest
tvec3_exp = rmat2 * tvec1 + tvec2;
const double thres = 1e-5;
if (cv::norm(rvec3_exp, rvec3) > thres || cv::norm(tvec3_exp, tvec3) > thres)
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
const double eps = 1e-3;
Differential diff(eps, rvec1, tvec1, rvec2, tvec2);
Mat dr3dr1, dr3dt1, dr3dr2, dr3dt2, dt3dr1, dt3dt1, dt3dr2, dt3dt2;
composeRT(rvec1, tvec1, rvec2, tvec2, rvec3, tvec3,
dr3dr1, dr3dt1, dr3dr2, dr3dt2, dt3dr1, dt3dt1, dt3dr2, dt3dt2);
Mat_<double> dr3_dr1, dt3_dr1;
diff.dRv1(dr3_dr1, dt3_dr1);
if (cv::norm(dr3_dr1, dr3dr1) > thres || cv::norm(dt3_dr1, dt3dr1) > thres)
{
ts->printf( cvtest::TS::LOG, "Invalid derivates by r1\n" );
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
Mat_<double> dr3_dr2, dt3_dr2;
diff.dRv2(dr3_dr2, dt3_dr2);
if (cv::norm(dr3_dr2, dr3dr2) > thres || cv::norm(dt3_dr2, dt3dr2) > thres)
{
ts->printf( cvtest::TS::LOG, "Invalid derivates by r2\n" );
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
Mat_<double> dr3_dt1, dt3_dt1;
diff.dTv1(dr3_dt1, dt3_dt1);
if (cv::norm(dr3_dt1, dr3dt1) > thres || cv::norm(dt3_dt1, dt3dt1) > thres)
{
ts->printf( cvtest::TS::LOG, "Invalid derivates by t1\n" );
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
Mat_<double> dr3_dt2, dt3_dt2;
diff.dTv2(dr3_dt2, dt3_dt2);
if (cv::norm(dr3_dt2, dr3dt2) > thres || cv::norm(dt3_dt2, dt3dt2) > thres)
{
ts->printf( cvtest::TS::LOG, "Invalid derivates by t2\n" );
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
}
};
TEST(Calib3d_ComposeRT, accuracy) { CV_composeRT_Test test; test.safe_run(); }
}} // namespace
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,183 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// 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
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., 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:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 the Intel Corporation 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 "test_precomp.hpp"
namespace opencv_test { namespace {
class CV_DecomposeProjectionMatrixTest : public cvtest::BaseTest
{
public:
CV_DecomposeProjectionMatrixTest();
protected:
void run(int);
};
CV_DecomposeProjectionMatrixTest::CV_DecomposeProjectionMatrixTest()
{
test_case_count = 30;
}
void CV_DecomposeProjectionMatrixTest::run(int start_from)
{
ts->set_failed_test_info(cvtest::TS::OK);
cv::RNG& rng = ts->get_rng();
int progress = 0;
for (int iter = start_from; iter < test_case_count; ++iter)
{
ts->update_context(this, iter, true);
progress = update_progress(progress, iter, test_case_count, 0);
// Create the original (and random) camera matrix, rotation, and translation
cv::Vec2d f, c;
rng.fill(f, cv::RNG::UNIFORM, 300, 1000);
rng.fill(c, cv::RNG::UNIFORM, 150, 600);
double alpha = 0.01*rng.gaussian(1);
cv::Matx33d origK(f(0), alpha*f(0), c(0),
0, f(1), c(1),
0, 0, 1);
cv::Vec3d rVec;
rng.fill(rVec, cv::RNG::UNIFORM, -CV_PI, CV_PI);
cv::Matx33d origR;
cv::Rodrigues(rVec, origR); // TODO cvtest
cv::Vec3d origT;
rng.fill(origT, cv::RNG::NORMAL, 0, 1);
// Compose the projection matrix
cv::Matx34d P(3,4);
hconcat(origK*origR, origK*origT, P);
// Decompose
cv::Matx33d K, R;
cv::Vec4d homogCameraCenter;
decomposeProjectionMatrix(P, K, R, homogCameraCenter);
// Recover translation from the camera center
cv::Vec3d cameraCenter(homogCameraCenter(0), homogCameraCenter(1), homogCameraCenter(2));
cameraCenter /= homogCameraCenter(3);
cv::Vec3d t = -R*cameraCenter;
const double thresh = 1e-6;
if (cv::norm(origK, K, cv::NORM_INF) > thresh)
{
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
break;
}
if (cv::norm(origR, R, cv::NORM_INF) > thresh)
{
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
break;
}
if (cv::norm(origT, t, cv::NORM_INF) > thresh)
{
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
break;
}
}
}
TEST(Calib3d_DecomposeProjectionMatrix, accuracy)
{
CV_DecomposeProjectionMatrixTest test;
test.safe_run();
}
TEST(Calib3d_DecomposeProjectionMatrix, degenerate_cases)
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 2; j++)
{
cv::Matx34d P;
P(0, i) = 1;
P(1, (i + j + 1) % 3) = 1;
P(2, (i + 2 * j + 2) % 3) = 1;
cv::Matx33d K, R;
cv::Vec4d t;
decomposeProjectionMatrix(P, K, R, t);
EXPECT_LT(cv::norm(K * R, P.get_minor<3, 3>(0, 0), cv::NORM_INF), 1e-6);
}
}
}
TEST(Calib3d_DecomposeProjectionMatrix, bug_23733)
{
cv::Matx34d P(52, -7, 4, 12,
-6, 49, 12, 8,
4, 17, 1, 0);
P *= 1e-6;
cv::Matx33d K, R;
cv::Vec4d t;
decomposeProjectionMatrix(P, K, R, t);
EXPECT_LT(cv::norm(R.t() * R - cv::Matx33d::eye(), cv::NORM_INF), 1e-10);
cv::Matx34d M;
cv::hconcat(R, -R * cv::Vec3d(t[0] / t[3], t[1] / t[3], t[2] / t[3]), M);
cv::Matx34d P_recompose = K * M;
EXPECT_LT(cv::norm(P_recompose - P, cv::NORM_INF), 1e-16);
}
}} // namespace
@@ -0,0 +1,575 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// 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
//
// Copyright (C) 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:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 the Intel Corporation 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 "test_precomp.hpp"
#include "opencv2/geometry.hpp"
namespace opencv_test { namespace {
class CV_FilterHomographyDecompTest : public cvtest::BaseTest {
public:
CV_FilterHomographyDecompTest()
{
buildTestDataSet();
}
protected:
void run(int)
{
vector<int> finalSolutions;
filterHomographyDecompByVisibleRefpoints(_rotations, _normals, _prevRectifiedPoints, _currRectifiedPoints, finalSolutions, _mask);
//there should be at least 2 solution
ASSERT_EQ(finalSolutions, _validSolutions);
}
private:
void buildTestDataSet()
{
double rotationsArray[4][9] = {
{
0.98811084196540500,
-0.15276633082836735,
0.017303530150126534,
0.14161851662094097,
0.94821044891315664,
0.28432576443578628,
-0.059842791884259422,
-0.27849487021693553,
0.95857156619751127
},
{
0.98811084196540500,
-0.15276633082836735,
0.017303530150126534,
0.14161851662094097,
0.94821044891315664,
0.28432576443578628,
-0.059842791884259422,
-0.27849487021693553,
0.95857156619751127
},
{
0.95471096402077438,
-0.21080808634428211,
-0.20996886890771557,
0.20702063153797226,
0.97751379914116743,
-0.040115216641822840,
0.21370407880090386,
-0.0051694506925720751,
0.97688476468997820
},
{
0.95471096402077438,
-0.21080808634428211,
-0.20996886890771557,
0.20702063153797226,
0.97751379914116743,
-0.040115216641822840,
0.21370407880090386,
-0.0051694506925720751,
0.97688476468997820
}
};
double normalsArray[4][3] = {
{
-0.023560516110791116,
0.085818414407956692,
0.99603217911325403
},
{
0.023560516110791116,
-0.085818414407956692,
-0.99603217911325403
},
{
-0.62483547397726014,
-0.56011861446691769,
0.54391889853844289
},
{
0.62483547397726014,
0.56011861446691769,
-0.54391889853844289
}
};
uchar maskArray[514] =
{
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0,
0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0,
1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1,
0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0,
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1,
1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1,
1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0,
0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0,
1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static const float currRectifiedPointArr[] =
{
-0.565732896f, -0.321162999f, -0.416198403f, -0.299646467f, -0.408354312f, -0.290387660f,
-0.386555284f, -0.287677139f, -0.348475337f, -0.276208878f, -0.415957332f, -0.266133875f,
-0.354961902f, -0.257545590f, -0.420189440f, -0.255190015f, -0.379785866f, -0.252570540f,
-0.144345313f, -0.249134675f, -0.162417486f, -0.223227784f, -0.129876539f, -0.219722182f,
-0.470801264f, -0.211166814f, 0.0992607549f, -0.209064797f, 0.123508267f, -0.196303099f,
-0.521849990f, -0.190706849f, -0.513497114f, -0.189186409f, -0.534959674f, -0.185138911f,
0.121614374f, -0.182721153f, 0.154205695f, -0.183763996f, -0.516449869f, -0.181606859f,
-0.523427486f, -0.180088669f, 0.149494573f, -0.179563865f, -0.552187204f, -0.172630817f,
-0.322249800f, -0.172333881f, 0.127574071f, -0.165683150f, 0.159817487f, -0.162389070f,
-0.578930736f, -0.160272732f, -0.600617707f, -0.155920163f, -0.249115735f, -0.154711768f,
-0.543279886f, -0.144873798f, -0.529992998f, -0.142433196f, 0.0554505363f, -0.142878756f,
-0.613355398f, -0.132748783f, 0.190059289f, -0.128930226f, -0.255682647f, -0.127393380f,
0.0299431719f, -0.125339776f, -0.282943249f, -0.118550651f, -0.0348402821f, -0.115398556f,
-0.0362741761f, -0.110100254f, -0.319089264f, -0.104354575f, -0.0401916653f, -0.0852083191f,
-0.372183621f, -0.0812346712f, -0.00707253255f, -0.0810251758f, 0.267345309f, -0.0787685066f,
0.258760840f, -0.0768160895f, -0.377273679f, -0.0763452053f, -0.0314898677f, -0.0743160769f,
0.223423928f, -0.0724818707f, 0.00284322398f, -0.0720727518f, 0.232531011f, -0.0682833865f,
0.282355100f, -0.0655683428f, -0.233353317f, -0.0613981225f, 0.290982842f, -0.0607336313f,
-0.0994169787f, -0.0376472026f, 0.257561266f, -0.0331368558f, 0.265076399f, -0.0320781991f,
0.0454338901f, -0.0238198638f, 0.0409987904f, -0.0186991505f, -0.502306283f, -0.0172236171f,
-0.464807063f, -0.0149533665f, -0.185798749f, -0.00540314987f, 0.182073534f, -0.000651287497f,
-0.435764432f, 0.00162558386f, -0.181552932f, 0.00792864431f, -0.700565279f, 0.0110246018f,
-0.144087434f, 0.0120453080f, -0.524990261f, 0.0138590708f, -0.182723984f, 0.0165519360f,
-0.217308879f, 0.0208590515f, 0.462978750f, 0.0247372910f, 0.0956632495f, 0.0323494300f,
0.0843820646f, 0.0424364135f, 0.122466311f, 0.0441578403f, -0.162433729f, 0.0528083183f,
0.0964344442f, 0.0624147579f, -0.271349967f, 0.0727724135f, -0.266336441f, 0.0719895661f,
0.0675768778f, 0.0848240927f, -0.689944625f, 0.0889045894f, -0.680990934f, 0.0903657600f,
-0.119472280f, 0.0930491239f, -0.124393739f, 0.0933082998f, -0.323403478f, 0.0937438533f,
-0.323273063f, 0.0969979763f, -0.352427900f, 0.101048596f, -0.327554941f, 0.104539163f,
-0.330044419f, 0.114519835f, 0.0235135648f, 0.118004657f, -0.671623945f, 0.130437061f,
-0.385111898f, 0.142786101f, -0.376281500f, 0.145800456f, -0.0169987213f, 0.148056105f,
-0.326495141f, 0.152596891f, -0.337120056f, 0.154522225f, -0.336885720f, 0.154304653f,
0.322089493f, 0.155130088f, -0.0713477954f, 0.163638428f, -0.0208650175f, 0.171433330f,
-0.380652726f, 0.172022790f, -0.0599780641f, 0.182294667f, 0.244408697f, 0.194245726f,
-0.101454332f, 0.198159069f, 0.257901788f, 0.200226694f, -0.0775909275f, 0.205242962f,
0.231870517f, 0.222396746f, -0.546760798f, 0.242291704f, -0.538914979f, 0.243761152f,
0.206653103f, 0.244874880f, -0.595693469f, 0.264329463f, -0.581023335f, 0.265664101f,
0.00444878871f, 0.267031074f, -0.573156178f, 0.271591753f, -0.543381274f, 0.271759123f,
0.00450209389f, 0.271335930f, -0.223618075f, 0.278416723f, 0.161934286f, 0.289435983f,
-0.199636295f, 0.296817899f, -0.250217140f, 0.299677849f, -0.258231103f, 0.314012855f,
-0.628315628f, 0.316889286f, 0.320948511f, 0.316358119f, -0.246845752f, 0.320511192f,
0.0687271580f, 0.321383297f, 0.0784438103f, 0.322898388f, 0.0946765989f, 0.325111747f,
-0.249674007f, 0.328731328f, -0.244633347f, 0.329467386f, -0.245841011f, 0.334985316f,
0.118609101f, 0.343532443f, 0.0497615598f, 0.348162144f, -0.221477821f, 0.349263757f,
0.0759577379f, 0.351840734f, 0.0504637137f, 0.373238713f, 0.0730970055f, 0.376537383f,
-0.204333842f, 0.381100655f, -0.557245076f, -0.339432925f, -0.402010202f, -0.288829565f,
-0.350465477f, -0.281259984f, -0.352995187f, -0.264569730f, -0.466762394f, -0.217114508f,
0.152002022f, -0.217566550f, 0.146226048f, -0.183914393f, 0.0949312001f, -0.177005857f,
-0.211882949f, -0.175594494f, -0.531562269f, -0.173924312f, -0.0727246776f, -0.167270422f,
0.0546481088f, -0.140193000f, -0.296819001f, -0.137850702f, -0.261863053f, -0.139540121f,
0.187967837f, -0.131033540f, 0.322852045f, -0.112108752f, -0.0432251953f, -0.102951847f,
-0.0453428440f, -0.0914504975f, -0.0182842426f, -0.0918859020f, 0.0140433423f, -0.0904538929f,
-0.377287626f, -0.0817026496f, 0.266108125f, -0.0797783583f, 0.257961422f, -0.0767710134f,
-0.495943695f, -0.0683977529f, 0.231466040f, -0.0675206482f, -0.240675926f, -0.0551427566f,
-0.482824773f, -0.0510699376f, -0.491354793f, -0.0414650664f, -0.0960614979f, -0.0377000235f,
-0.102409534f, -0.0369749814f, -0.471273214f, -0.0325376652f, -0.483320534f, -0.0174943600f,
-0.457503378f, -0.0152483145f, -0.178161725f, -0.0153892851f, -0.483233035f, -0.0106405178f,
-0.472914547f, -0.0105228210f, -0.166542307f, -0.00667150877f, 0.181261331f, -0.00449455017f,
-0.474292487f, -0.00428914558f, -0.185297221f, -0.00575157674f, -0.494381040f, -0.00278507406f,
-0.141748473f, -0.00289725070f, -0.487515569f, 0.000758233888f, 0.322646528f, 0.0197495818f,
0.142943904f, 0.0276249554f, -0.563232243f, 0.0306834858f, -0.555995941f, 0.0367121249f,
0.114935011f, 0.0496927276f, -0.152954608f, 0.0538645200f, -0.594885707f, 0.0562511310f,
0.0678326488f, 0.0756176412f, -0.667605639f, 0.0828208700f, -0.354470938f, 0.101424232f,
0.0228204262f, 0.120382607f, -0.639557123f, 0.124422595f, -0.690505445f, 0.126883239f,
-0.395509213f, 0.130242139f, -0.00618012529f, 0.139929801f, 0.175945997f, 0.140235618f,
0.198833048f, 0.167587668f, -0.334679037f, 0.177859858f, 0.236127406f, 0.192743436f,
0.283146858f, 0.204260647f, -0.0354267135f, 0.206209183f, 0.247388184f, 0.207016930f,
-0.0422560424f, 0.212493256f, 0.261681855f, 0.215763748f, 0.207528576f, 0.219807997f,
-0.300219178f, 0.221922547f, 0.206393883f, 0.245171010f, 0.239619836f, 0.244768366f,
-0.523026288f, 0.250639766f, -0.591975033f, 0.254252791f, 0.246785000f, 0.252878994f,
0.272995651f, 0.255815417f, 0.00825022161f, 0.265591830f, 0.192723796f, 0.266924977f,
-0.222951472f, 0.290150762f, -0.545146644f, 0.304910392f, 0.131736591f, 0.319247276f,
0.319435924f, 0.317917794f, 0.0687546134f, 0.321296155f, -0.255853772f, 0.327258259f,
0.0948092714f, 0.325284332f, 0.104488030f, 0.327628911f, -0.245483562f, 0.327617317f,
0.0647632629f, 0.363111496f, -0.382861346f, -0.287226975f, -0.354297429f, -0.278708905f,
-0.356116027f, -0.262691110f, -0.369049937f, -0.237850189f, -0.146217853f, -0.233530551f,
0.102752604f, -0.223108903f, 0.137545392f, -0.218163848f, 0.125815898f, -0.216970086f,
-0.557826996f, -0.194665924f, -0.533946335f, -0.184958249f, 0.0976954028f, -0.173691019f,
-0.240166873f, -0.160652772f, 0.166464865f, -0.154563308f, -0.0330923162f, -0.125799045f,
-0.290044904f, -0.118914597f, 0.00350888353f, -0.108661920f, -0.0109116854f, -0.106212743f,
-0.0298740193f, -0.102953635f, -0.287203342f, -0.0997403413f, -0.269498408f, -0.0981520712f,
-0.000815737061f, -0.0938294530f, 0.274663270f, -0.0844340026f, -0.371082008f, -0.0805466920f,
-0.368196100f, -0.0743779093f, 0.00675902702f, -0.0735078678f, 0.226267770f, -0.0744194537f,
-0.241736412f, -0.0630025938f, -0.408663541f, -0.0564615242f, 0.251640886f, -0.0519632548f,
0.249993712f, -0.0519672707f, -0.426033378f, -0.0365641154f, -0.467352122f, -0.0305716563f,
0.251341015f, -0.0268137120f, -0.443456501f, -0.0243669953f, -0.502199471f, -0.0151771074f,
-0.178487480f, -0.0155749097f, 0.178145915f, -0.00528379623f, -0.492981344f, -0.00174682145f,
-0.150337398f, 0.000692513015f, -0.457302928f, 0.00352234906f, 0.190587431f, 0.00151424226f,
-0.482671946f, 0.00682042213f, -0.158589542f, 0.0150188655f, -0.182223722f, 0.0145649035f,
0.107089065f, 0.0223725326f, 0.135399371f, 0.0275243558f, -0.552838683f, 0.0275048595f,
-0.432176501f, 0.0248741303f, -0.192510992f, 0.0281074084f, -0.553043425f, 0.0298770685f,
-0.684887648f, 0.0436144769f, 0.0850105733f, 0.0448755622f, -0.165784389f, 0.0439001285f,
0.102653719f, 0.0457992665f, 0.114853017f, 0.0504316092f, -0.647432685f, 0.0608204119f,
0.0828530043f, 0.0608987175f, 0.0894377902f, 0.0742467493f, 0.0702404827f, 0.0767309442f,
-0.613642335f, 0.0779517740f, -0.670592189f, 0.0849624202f, -0.395209312f, 0.0854151621f,
0.125186160f, 0.0919951499f, -0.359707922f, 0.102121405f, -0.354259193f, 0.101300709f,
0.0304000825f, 0.110619470f, -0.677573025f, 0.114422500f, 0.0305799693f, 0.121603437f,
-0.358950615f, 0.121660560f, -0.718753040f, 0.134569481f, 0.256451160f, 0.141883001f,
-0.0904129520f, 0.146879435f, -0.0184279438f, 0.148968369f, -0.356992692f, 0.160104826f,
-0.337676436f, 0.161766291f, 0.201174691f, 0.169025913f, -0.378423393f, 0.170933828f,
-0.601599216f, 0.174998865f, -0.0902864039f, 0.184311926f, -0.0584819093f, 0.184186250f,
0.294467270f, 0.182560727f, 0.250262231f, 0.186239958f, -0.326370239f, 0.191697389f,
-0.0980727375f, 0.196913749f, 0.253085673f, 0.201914877f, -0.0344332159f, 0.205900863f,
0.255287141f, 0.203029931f, -0.452713937f, 0.205191836f, 0.264822274f, 0.217408702f,
-0.0290334225f, 0.221684650f, -0.583990574f, 0.237398431f, -0.145020664f, 0.240374506f,
0.249667659f, 0.254706532f, 0.274279058f, 0.256447285f, -0.282936275f, 0.259140193f,
0.241211995f, 0.260401577f, -0.590560019f, 0.272659779f, -0.574947417f, 0.272671998f,
-0.224780366f, 0.279990941f, -0.525540829f, 0.287235677f, -0.247069210f, 0.298608154f,
-0.201292604f, 0.298156679f, 0.319822490f, 0.317605704f, -0.248013541f, 0.320789784f,
0.0957527757f, 0.326543272f, 0.105006196f, 0.328469753f, -0.264089525f, 0.332354158f,
-0.670460403f, 0.339870930f, 0.118318990f, 0.345167071f, 0.0737744719f, 0.353734553f,
0.0655663237f, 0.361025929f, -0.306805104f, 0.363820761f, 0.0524423867f, 0.371921480f,
0.0713953897f, 0.375074357f, -0.411387652f, -0.268335998f, -0.357590824f, -0.263346583f,
-0.407676578f, -0.253785878f, 0.0660323426f, -0.253718942f, -0.157670841f, -0.225629836f,
0.170453921f, -0.220800355f, -0.475751191f, -0.209005311f, -0.331408232f, -0.203059763f,
-0.173841938f, -0.199112654f, -0.503261328f, -0.193795130f, -0.532277644f, -0.190292686f,
-0.0972326621f, -0.191563144f, -0.0692789108f, -0.172031537f, -0.318824291f, -0.169072524f,
-0.576232314f, -0.162124678f, -0.0839322209f, -0.156304389f, -0.583625376f, -0.142171323f,
-0.0546422042f, -0.135338858f, 0.0501612425f, -0.132490858f, -0.645011544f, -0.111341864f,
-0.0925374180f, -0.0483307689f, -0.444242209f, -0.0263337940f, 0.0335495919f, -0.0281750113f,
0.274629444f, -0.0259516705f, 0.213774025f, -0.0240113474f, -0.194874078f, -0.0151330847f,
0.175111562f, -0.00868577976f, -0.185011521f, -0.000680683181f, 0.152071685f, 0.0204544198f,
0.321354061f, 0.0199794695f, -0.192160159f, 0.0275637116f, -0.189656645f, 0.0275667012f,
0.137452200f, 0.0298070628f, -0.194602579f, 0.0449027494f, -0.647751570f, 0.0625102371f,
0.124078721f, 0.0639316663f, 0.125849217f, 0.0762147456f, -0.614036798f, 0.0778791085f,
-0.684063017f, 0.0867959261f, -0.670344174f, 0.0846142769f, -0.127689242f, 0.0883567855f,
0.123796627f, 0.0907361880f, -0.356352538f, 0.101948388f, -0.388843179f, 0.110183217f,
0.0316384435f, 0.123791300f, -0.627986908f, 0.146491125f, -0.0747071728f, 0.158135459f,
-0.0235102437f, 0.168867558f, -0.0903210714f, 0.184088305f, 0.292073458f, 0.183571488f,
-0.0585953295f, 0.184784085f, -0.0317775607f, 0.218368888f, 0.209752038f, 0.223883361f,
-0.295424402f, 0.229150623f, -0.144439027f, 0.237902716f, -0.284140587f, 0.262761474f,
0.289083928f, 0.276900887f, 0.159017235f, 0.300793648f, -0.204925507f, 0.298536539f,
-0.544958472f, 0.305164427f, -0.261615157f, 0.306550682f, 0.0977220088f, 0.327949613f,
0.109876208f, 0.337665111f, -0.283918083f, 0.347385526f, 0.0436712503f, 0.350702018f,
0.114512287f, 0.367949426f, 0.106543839f, 0.375095814f, 0.505324781f, -0.272183985f,
0.0645913780f, -0.251512915f, -0.457196057f, -0.225893468f, -0.480293810f, -0.222602293f,
-0.138176888f, -0.209798917f, -0.110901751f, -0.198036820f, -0.196451947f, -0.191723794f,
-0.537742376f, -0.174413025f, -0.0650562346f, -0.174762890f, -0.567489207f, -0.165461496f,
0.0879585966f, -0.163023785f, -0.303777844f, -0.142031133f, 0.199195996f, -0.141861767f,
0.0491657220f, -0.132264882f, -0.497363061f, -0.107934952f, -0.000536393432f, -0.102828167f,
0.0155952247f, -0.0998895392f, -0.363601953f, -0.0897399634f, -0.224325985f, -0.0719678402f,
-0.0638299435f, -0.0646244809f, -0.108656809f, -0.0468749776f, -0.0865045264f, -0.0512534790f,
-0.469339728f, -0.0279338267f, 0.0578282699f, -0.0133374622f, -0.195265710f, -0.0115369316f,
0.296735317f, -0.0132813146f, 0.0664219409f, 0.0134935537f, 0.126060545f, 0.0333039127f,
0.139887005f, 0.0334976614f, -0.547339618f, 0.0433730707f, 0.0866046399f, 0.0527233221f,
0.131943896f, 0.0657638907f, -0.280056775f, 0.0685855150f, 0.0746403933f, 0.0795079395f,
0.125382811f, 0.0822770745f, -0.648187757f, 0.103887804f, -0.107411072f, 0.107508548f,
0.0155869983f, 0.108978622f, 0.0189307462f, 0.129617691f, 0.162685350f, 0.127225950f,
-0.0875291452f, 0.142281070f, 0.319728941f, 0.148827255f, -0.0259547811f, 0.169724479f,
0.259297132f, 0.190075457f, -0.467013776f, 0.212794706f, -0.315732479f, 0.219243437f,
-0.111042649f, 0.217940107f, 0.239550352f, 0.222786069f, 0.263966352f, 0.260309041f,
0.320023954f, -0.222228840f, -0.322707742f, -0.213004455f, -0.224977970f, -0.169595599f,
-0.605799317f, -0.142425537f, 0.0454332717f, -0.129945949f, 0.205748767f, -0.113405459f,
0.317985803f, -0.118630089f, 0.497755647f, -0.0962266177f, -0.393495560f, -0.0904672816f,
0.240035087f, -0.0737613589f, -0.212947786f, -0.0280145984f, 0.0674179196f, 0.0124880793f,
-0.545862198f, 0.0207057912f, -0.284409463f, 0.0626631007f, -0.107082598f, 0.0854173824f,
0.0578137375f, 0.0917839557f, 0.145844117f, 0.102937251f, 0.183878779f, 0.119614877f,
-0.626380265f, 0.140862882f, -0.0325521491f, 0.161834121f, -0.590211987f, 0.167720392f,
0.289599866f, 0.186565816f, -0.328821093f, 0.187714070f, -0.289086968f, 0.205165654f,
-0.445392698f, 0.215343162f, 0.173715711f, 0.273563296f, 0.284015119f, 0.270610362f,
0.0174398609f, 0.283809274f, -0.496335506f, -0.202981815f, 0.0389454551f, -0.166210428f,
-0.317301393f, -0.156280205f, -0.396320462f, -0.0949599668f, -0.213638976f, -0.0776446015f,
0.497601509f, -0.0928353444f, -0.260220319f, -0.0718628615f, -0.116495222f, -0.0543703064f,
-0.118132629f, -0.0156126227f, 0.0242815297f, 0.00629332382f, -0.537928998f, 0.00815516617f,
0.317720622f, 0.0271231923f, -0.582170665f, 0.0478387438f, -0.536856830f, 0.0466793887f,
-0.220819592f, 0.0433096550f, -0.246473342f, 0.0572598167f, 0.481240988f, 0.0503845438f,
-0.102453016f, 0.0649363101f, -0.149955124f, 0.0744054317f, -0.248215869f, 0.0916868672f,
-0.101221249f, 0.110788561f, -0.437672526f, 0.179065496f, -0.0383506976f, 0.183546484f,
-0.279600590f, 0.208760634f, 0.182261929f, 0.275244594f, 0.0253023170f, -0.170456246f,
-0.476852804f, -0.123630777f, -0.0803126246f, -0.0782076195f, -0.133338496f, -0.0659459904f,
-0.0822777376f, -0.00390591589f, 0.149250969f, 0.104314201f, 0.0418044887f, 0.149009049f,
-0.438308835f, 0.164682120f
};
const Point2f* currRectifiedPointArr_2f = (const Point2f*)currRectifiedPointArr;
vector<Point2f> currRectifiedPoints(currRectifiedPointArr_2f,
currRectifiedPointArr_2f + sizeof(currRectifiedPointArr) / sizeof(currRectifiedPointArr[0]) / 2);
_currRectifiedPoints.swap(currRectifiedPoints);
static const float prevRectifiedPointArr[] = {
-0.599324584f, -0.381164283f, -0.387985110f, -0.385367423f, -0.371437579f, -0.371891201f,
-0.340867460f, -0.370632380f, -0.289822906f, -0.364118159f, -0.372411519f, -0.335272551f,
-0.289586753f, -0.335766882f, -0.372335523f, -0.316857219f, -0.321099430f, -0.323233813f,
0.208661616f, -0.153931335f, -0.559897065f, 0.193362445f, 0.0181128159f, -0.325224668f,
-0.427504510f, 0.105302416f, 0.487470537f, -0.187071189f, 0.343267351f, -0.339755565f,
-0.477639943f, -0.204375938f, -0.466626763f, -0.204072326f, 0.340813518f, -0.347292691f,
0.342682719f, -0.320172101f, 0.383663863f, -0.327343374f, -0.467062414f, -0.193995550f,
-0.475603998f, -0.189820126f, 0.552475691f, 0.198386014f, -0.508027375f, -0.174297482f,
-0.211989403f, -0.217261642f, 0.180832058f, -0.127527758f, -0.112721168f, -0.125876635f,
-0.112387165f, -0.167135969f, -0.562491000f, -0.140186235f, 0.395156831f, -0.298828602f,
-0.485202312f, -0.135626689f, 0.148358017f, -0.195937276f, -0.248159677f, -0.254669130f,
-0.568366945f, -0.105187029f, -0.0714842379f, -0.0832463056f, -0.497599572f, -0.205334768f,
-0.0948727652f, 0.245045587f, 0.160857186f, 0.138075173f, 0.164952606f, -0.195109487f,
0.165254518f, -0.186554477f, -0.183777973f, -0.124357253f, 0.166813776f, -0.153241888f,
-0.241765827f, -0.0820638761f, 0.208661616f, -0.153931335f, 0.540147483f, -0.203156039f,
0.529201686f, -0.199348077f, -0.248159677f, -0.254669130f, 0.180369601f, -0.139303327f,
0.570952237f, -0.185722873f, 0.221771300f, -0.143187970f, 0.498627752f, -0.183768719f,
0.561214447f, -0.188666284f, -0.241409421f, -0.253560483f, 0.569648385f, -0.184499770f,
0.276665628f, -0.0881819800f, 0.533934176f, -0.142226711f, -0.299728751f, -0.330407321f,
0.270322412f, -0.256552309f, -0.255016476f, -0.0823200271f, -0.378096581f, 0.0264666155f,
-0.331565350f, 0.0210608803f, 0.0100810500f, -0.0213523544f, -0.248159677f, -0.254669130f,
0.249623299f, 0.164078355f, 0.0190342199f, -0.00415771967f, 0.604407132f, -0.259350061f,
0.0660026148f, -0.00787150953f, 0.605921566f, 0.114344336f, 0.0208173525f, 0.00527517078f,
-0.0200567022f, 0.0183092188f, -0.184784368f, -0.193566754f, -0.0125719802f, -0.344967902f,
0.343063682f, -0.0121044181f, 0.389022052f, -0.0171062462f, 0.163190305f, 0.200014487f,
0.362440646f, 0.0120019922f, -0.427743971f, 0.100272447f, -0.0714842379f, -0.0832463056f,
0.0664352402f, 0.0467514023f, -0.559897065f, 0.193362445f, -0.549086213f, 0.193808615f,
-0.241472989f, -0.253163874f, -0.241765827f, -0.0820638761f, -0.122216024f, 0.132651567f,
-0.122216024f, 0.132651567f, 0.515065968f, 0.205271944f, 0.180832058f, -0.127527758f,
-0.123633556f, 0.154476687f, -0.248159677f, -0.254669130f, 0.0208173525f, 0.00527517078f,
-0.483276874f, 0.191274792f, -0.167928949f, 0.200682297f, 0.232745290f, -0.211950779f,
-0.288701504f, -0.334238827f, -0.119621970f, 0.204155236f, -0.119621970f, 0.204155236f,
0.632996142f, 0.0804972649f, 0.189231426f, 0.164325386f, 0.249623299f, 0.164078355f,
0.0676716864f, 0.0479496233f, 0.207636267f, 0.184271768f, -0.300510556f, 0.358790994f,
-0.107678331f, 0.188473806f, 0.565983415f, 0.144723341f, 0.191329703f, 0.213909492f,
-0.0283227600f, -0.373237878f, -0.184958130f, 0.200373843f, 0.0346363746f, -0.0259889495f,
-0.112387165f, -0.167135969f, 0.251426309f, 0.210430339f, -0.477397382f, -0.131372169f,
-0.0667442903f, 0.0997460634f, 0.251426309f, 0.210430339f, -0.317926824f, 0.375238001f,
-0.0621999837f, 0.280056626f, 0.0443522707f, 0.321513236f, 0.471269101f, 0.260774940f,
-0.107678331f, 0.188473806f, 0.0208210852f, 0.350526422f, 0.0157474391f, 0.367335707f,
0.632996142f, 0.0804972649f, 0.646697879f, 0.265504390f, 0.0295150280f, 0.371205181f,
0.376071006f, 0.313471258f, -0.379525930f, 0.364357829f, -0.00628023129f, -0.0373278372f,
0.0291138459f, 0.381194293f, 0.0358079821f, 0.381886899f, 0.0344478637f, 0.386993408f,
0.433862329f, 0.328515977f, 0.359724253f, 0.345606029f, 0.0651357397f, 0.397334814f,
0.388413996f, 0.344747871f, -0.140228778f, 0.216103494f, 0.389989913f, 0.372472703f,
0.444995403f, 0.300240308f, -0.606455386f, 0.100793049f, -0.362332910f, -0.371920794f,
-0.478956074f, 0.234040022f, -0.289441198f, -0.344822973f, -0.0714842379f, -0.0832463056f,
0.375879139f, -0.374975592f, 0.376526117f, -0.326493502f, 0.313251913f, -0.306372881f,
-0.0577337518f, 0.0893306211f, -0.483683407f, -0.179540694f, -0.0763650239f, -0.258294433f,
0.276665628f, -0.0881819800f, -0.167122558f, -0.175508693f, -0.164081737f, 0.176902041f,
0.276665628f, -0.0881819800f, 0.602967978f, -0.260941893f, 0.158573851f, -0.178748295f,
0.159815103f, -0.160761341f, 0.194283918f, -0.165657878f, 0.231515527f, -0.172808051f,
-0.247000366f, 0.277822912f, 0.538969517f, -0.204621449f, 0.531404376f, -0.198565826f,
-0.388338953f, -0.0433262810f, 0.499413073f, -0.181929186f, -0.237337112f, 0.0934364349f,
-0.368045300f, -0.0204487685f, -0.374767631f, -0.00678646797f, -0.0667242110f, -0.248651102f,
-0.248159677f, -0.254669130f, -0.345217139f, -0.00101677026f, -0.353382975f, 0.0210586078f,
-0.322639942f, 0.0211628731f, 0.0184581745f, -0.0366852731f, 0.0259528626f, -0.0136881955f,
-0.339446336f, 0.0286702402f, 0.0335014127f, -0.0271516014f, 0.465966076f, 0.0830826238f,
-0.337860256f, 0.0362124667f, 0.188271523f, -0.146541893f, -0.298272073f, -0.323130161f,
0.0643569306f, -0.0264105909f, -0.353804410f, 0.0433940105f, 0.618646920f, -0.0855877250f,
0.411329508f, -0.0414552018f, -0.427743971f, 0.100272447f, -0.247000366f, 0.277822912f,
0.381912649f, -0.00914942939f, 0.0664352402f, 0.0467514023f, 0.138687640f, -0.114854909f,
-0.0170480162f, -0.372787565f, -0.535477102f, 0.183755845f, -0.155668780f, 0.144164801f,
-0.427504510f, 0.105302416f, -0.484430760f, 0.227277100f, -0.361284673f, -0.373513311f,
-0.316764563f, 0.331503242f, -0.0230990555f, 0.314180285f, 0.101539977f, -0.256640851f,
-0.210743994f, -0.111771651f, -0.560086846f, 0.151153624f, 0.542884171f, 0.141691014f,
0.596041858f, 0.144990161f, 0.239398748f, 0.207432285f, 0.557545543f, 0.155783832f,
0.233033463f, 0.214694947f, 0.572789013f, 0.162068501f, 0.512761712f, 0.176260322f,
0.287076950f, 0.0868823677f, 0.515065968f, 0.205271944f, 0.552475691f, 0.198386014f,
-0.301232725f, 0.347804308f, -0.379525930f, 0.364357829f, 0.561403453f, 0.206571117f,
0.590792358f, 0.206283644f, -0.428855836f, 0.100270294f, 0.300039053f, -0.283949375f,
0.0481642894f, 0.334260821f, -0.173260480f, -0.167126089f, 0.444995403f, 0.300240308f,
0.646697879f, 0.265504390f, 0.375487208f, 0.314186513f, 0.0217850581f, 0.381838262f,
0.404422343f, 0.313856274f, 0.417644382f, 0.314869910f, 0.0358079821f, 0.381886899f,
0.378262609f, 0.358303785f, -0.336999178f, -0.367679387f, -0.295442462f, -0.365161836f,
-0.293496192f, -0.342732310f, -0.298767596f, -0.303165644f, -0.0111337993f, -0.342149645f,
0.310648471f, -0.374146342f, 0.359467417f, -0.373746723f, 0.340779394f, -0.369219989f,
-0.527450860f, -0.203896046f, -0.490746915f, -0.194764644f, 0.314866364f, -0.300261766f,
-0.0298556220f, 0.0591949411f, 0.319549739f, 0.0552458987f, 0.163977623f, -0.209844783f,
-0.149107113f, -0.149005055f, 0.212483421f, -0.191198543f, 0.197611198f, -0.187811792f,
0.174361721f, -0.179897651f, 0.0387913659f, -0.0366905928f, -0.122265801f, -0.126270071f,
0.211038783f, -0.172842503f, 0.246728286f, 0.134398326f, -0.0577337518f, 0.0893306211f,
-0.415295422f, 0.105914228f, -0.292730510f, 0.0379575789f, 0.489636958f, -0.194117576f,
-0.254337519f, 0.0937413648f, 0.336177140f, 0.305443168f, 0.526942134f, -0.164069965f,
0.524966419f, -0.165161178f, -0.379173398f, 0.332068861f, -0.340792000f, 0.00105464540f,
0.525632977f, -0.134992197f, -0.308774501f, 0.00290521770f, -0.375407755f, 0.0294080544f,
0.0178439785f, -0.0365749858f, -0.255016476f, -0.0823200271f, -0.359951973f, 0.0446678996f,
0.0564084686f, -0.0197724514f, -0.315141559f, 0.0424463004f, 0.292196661f, 0.279810339f,
-0.345294952f, 0.0533128195f, 0.0458479226f, -0.00109126628f, 0.0179449394f, 0.00371767790f,
0.365872562f, -0.0412087664f, 0.403013051f, -0.0416624695f, -0.0714842379f, -0.0832463056f,
-0.209011748f, 0.133690849f, 0.0122421598f, 0.0230175443f, -0.0577337518f, 0.0893306211f,
-0.572846889f, 0.141102776f, 0.345340014f, -0.0111671211f, 0.0479373708f, 0.0379454680f,
0.363291621f, -0.00829032529f, 0.381912649f, -0.00914942939f, -0.521542430f, 0.151489466f,
0.345966965f, 0.0110620018f, 0.354562849f, 0.0254590791f, 0.334322065f, 0.0310698878f,
-0.00463629747f, -0.0357710384f, -0.538667142f, 0.185365483f, -0.209011748f, 0.133690849f,
0.398122877f, 0.0403857268f, -0.160881191f, 0.145009249f, -0.155668780f, 0.144164801f,
-0.0714842379f, -0.0832463056f, -0.536377013f, 0.221241340f, -0.0632879063f, -0.247039422f,
-0.155869946f, 0.169341147f, 0.578685045f, -0.223878756f, 0.557447612f, 0.0768704116f,
-0.188812047f, 0.228197843f, 0.246747240f, 0.136472240f, -0.142677084f, 0.213736445f,
-0.118143238f, 0.208306640f, -0.388338953f, -0.0433262810f, -0.163515776f, 0.231573820f,
-0.0738375857f, -0.256104171f, 0.173092276f, 0.191535592f, 0.208548918f, 0.185476139f,
-0.392410189f, 0.0686017647f, 0.555366814f, 0.130478472f, -0.101943128f, -0.113997340f,
0.0716935173f, 0.340265751f, 0.561738014f, 0.148283109f, 0.242452115f, 0.205116034f,
0.561738014f, 0.148283109f, -0.427743971f, 0.100272447f, 0.578137994f, 0.163653031f,
0.251277626f, 0.223055005f, -0.376505047f, 0.343530416f, -0.0714842379f, -0.0832463056f,
0.567448437f, 0.207419440f, 0.590792358f, 0.206283644f, 0.578685045f, -0.223878756f,
0.0635343120f, -0.00499309227f, -0.370767444f, 0.384881169f, -0.485191971f, -0.120962359f,
0.512761712f, 0.176260322f, -0.375972956f, 0.0288736783f, -0.147176415f, -0.185790271f,
0.0752977654f, 0.339190871f, 0.646697879f, 0.265504390f, 0.0282997675f, 0.373214334f,
0.410353780f, 0.316089481f, 0.417644382f, 0.314869910f, 0.0147482762f, 0.389459789f,
-0.182916895f, -0.140514761f, 0.433515042f, 0.330774426f, 0.388069838f, 0.347381502f,
0.378925055f, 0.357438952f, 0.247128293f, -0.116897359f, -0.0230906308f, 0.314556211f,
0.388534039f, 0.370789021f, -0.368050814f, -0.339653373f, -0.292694926f, -0.341653705f,
-0.353774697f, -0.320387989f, 0.599263310f, -0.264537901f, -0.0213720929f, -0.326088905f,
-0.571947694f, 0.141147330f, -0.0577337518f, 0.0893306211f, 0.108424753f, -0.267108470f,
-0.0317604132f, -0.0458168685f, -0.0967136100f, 0.242639020f, -0.486509413f, -0.204596937f,
0.239178345f, -0.219647482f, 0.108424753f, -0.267108470f, -0.280393064f, -0.283867925f,
-0.533659995f, -0.151733354f, 0.0880429000f, -0.240412414f, -0.534965396f, -0.124174178f,
0.142445788f, -0.118948005f, 0.0947291106f, 0.0767719224f, -0.597055852f, -0.0692315027f,
-0.254337519f, 0.0937413648f, -0.308869720f, 0.00354974205f, -0.409894019f, -0.0694356859f,
0.556049764f, -0.137727231f, -0.0317604132f, -0.0458168685f, -0.524152219f, 0.239541322f,
0.108424753f, -0.267108470f, 0.0143662402f, -0.0164190196f, 0.150936082f, 0.128616557f,
0.618646920f, -0.0855877250f, 0.0122421598f, 0.0230175443f, 0.0122421598f, 0.0230175443f,
-0.188812047f, 0.228197843f, 0.00441747159f, -0.297387213f, -0.520719767f, 0.152393058f,
0.392849416f, 0.00738697406f, 0.400074363f, 0.0185570847f, -0.161484867f, -0.192373112f,
-0.554901838f, 0.190730989f, -0.538667142f, 0.185365483f, -0.0667442903f, 0.0997460634f,
0.399885803f, 0.0410231315f, -0.159816831f, 0.145826310f, -0.193316415f, 0.161277503f,
-0.0678345188f, 0.287081748f, -0.383089483f, -0.283330113f, -0.538667142f, 0.185365483f,
0.245664895f, 0.162005231f, 0.173092276f, 0.191535592f, 0.601281762f, 0.120500855f,
0.208548918f, 0.185476139f, 0.246893004f, 0.220670119f, 0.516039073f, 0.178782418f,
-0.254337519f, 0.0937413648f, -0.254337519f, 0.0937413648f, -0.0230990555f, 0.314180285f,
0.610029638f, 0.227215171f, -0.254337519f, 0.0937413648f, 0.0697976872f, 0.343245506f,
0.538969517f, -0.204621449f, 0.00916308723f, 0.359826297f, 0.410353780f, 0.316089481f,
0.423950195f, 0.324112266f, 0.166566655f, 0.145402640f, 0.354594171f, 0.350193948f,
0.433712035f, 0.356235564f, 0.425307065f, 0.364637494f, 0.166924104f, -0.152513608f,
0.594130874f, -0.268246830f, -0.0843627378f, -0.0962528363f, 0.108424753f, -0.267108470f,
0.00760878995f, -0.304247797f, -0.471018314f, -0.178305879f, -0.0817007348f, -0.0933016762f,
0.232274890f, 0.154553935f, 0.108424753f, -0.267108470f, -0.525787771f, -0.161353886f,
-0.206048280f, 0.241006181f, -0.178062543f, -0.184703678f, 0.105906568f, 0.268231422f,
-0.0817007348f, -0.0933016762f, 0.490914792f, 0.276718110f, -0.176861435f, -0.153617889f,
0.0387795344f, 0.0457828715f, 0.456206828f, -0.250739783f, 0.0982551053f, 0.104225174f,
0.142445788f, -0.118948005f, 0.108424753f, -0.267108470f, -0.0817007348f, -0.0933016762f,
-0.340707630f, 0.00498990202f, 0.0947291106f, 0.0767719224f, 0.169802040f, 0.203134149f,
0.577375948f, -0.125099033f, 0.318376005f, -0.0486588739f, 0.388697982f, -0.0351444185f,
0.406605273f, -0.0364143848f, 0.274859309f, 0.0776181892f, 0.349759877f, -7.70174083e-05f,
0.402967423f, 0.00697830878f, 0.105906568f, 0.268231422f, 0.338973522f, 0.0359939188f,
0.394951165f, 0.0322254188f, -0.503028810f, 0.203627899f, -0.0840740278f, -0.234684706f,
0.108424753f, -0.267108470f, 0.286642373f, 0.103878126f, -0.0817007348f, -0.0933016762f,
0.332983583f, -0.0356097035f, 0.628004134f, 0.0766527727f, -0.112659439f, -0.196833044f,
0.568797410f, 0.136423931f, 0.456206828f, -0.250739783f, -0.254337519f, 0.0937413648f,
-0.206692874f, -0.210832119f, 0.550912619f, 0.171586066f, 0.581267595f, 0.213235661f,
0.334484309f, 0.303876013f, -0.469516128f, 0.0883551016f, 0.133899942f, 0.106862970f,
-0.560961962f, -0.114681393f, -0.0840740278f, -0.234684706f, 0.459386230f, -0.236088052f,
0.594130874f, -0.268246830f, -0.124856450f, 0.193096936f, -0.469516128f, 0.0883551016f,
0.514290810f, -0.193822652f, 0.158255994f, 0.233290926f, 0.317973822f, -0.0477817170f,
-0.0817007348f, -0.0933016762f, -0.0702776462f, -0.0671426803f, 0.440836668f, -0.100193374f,
0.326240778f, 0.0523138903f, -0.279556662f, -0.283929169f, -0.485202312f, -0.135626689f,
-0.467358112f, 0.246376559f, 0.232274890f, 0.154553935f, 0.258349210f, -0.269529581f,
0.600620329f, 0.126268178f, -0.0985416993f, 0.245674044f, -0.279264033f, -0.0990248993f,
0.108424753f, -0.267108470f, 0.259638488f, -0.100053802f, 0.605106652f, 0.223564968f,
0.129683495f, -0.100376993f, -0.0953388065f, 0.112722203f, -0.440420747f, -0.0396305211f,
-0.0181254297f, 0.0439292751f, -0.0878356919f, 0.0847257674f, -0.271582603f, 0.126064256f,
-0.183777973f, -0.124357253f, 0.431088895f, 0.0680654719f, -0.469516128f, 0.0883551016f,
-0.445174575f, 0.133306518f, -0.0878356919f, 0.0847257674f, -0.279039949f, 0.0810008645f,
0.612402737f, -0.0826834291f, -0.454494953f, 0.122878648f, 0.244000912f, -0.264438629f,
0.142445788f, -0.118948005f, 0.129683495f, -0.100376993f, -0.210078895f, 0.131698489f,
-0.277847171f, 0.0665081516f, 0.431088895f, 0.0680654719f, 0.252345473f, 0.0688349009f,
0.133899942f, 0.106862970f, 0.133899942f, 0.106862970f, -0.486509413f, -0.204596937f,
-0.0940247625f, 0.0698821172f, 0.133899942f, 0.106862970f, -0.440420747f, -0.0396305211f,
-0.0878356919f, 0.0847257674f, -0.0954068601f, -0.0968973264f, -0.277847171f, 0.0665081516f,
-0.277847171f, 0.0665081516f, 0.266677618f, 0.111257851f, 0.292424291f, -0.230888903f,
-0.0954068601f, -0.0968973264f
};
const Point2f* prevRectifiedPointArr_2f = (const Point2f*)prevRectifiedPointArr;
vector<Point2f> prevRectifiedPoints(prevRectifiedPointArr_2f, prevRectifiedPointArr_2f +
sizeof(prevRectifiedPointArr) / sizeof(prevRectifiedPointArr[0]) / 2);
_prevRectifiedPoints.swap(prevRectifiedPoints);
int validSolutionArr[2] = { 0, 2 };
vector<int> validSolutions(validSolutionArr, validSolutionArr +
sizeof(validSolutionArr) / sizeof(validSolutionArr[0]));
_validSolutions.swap(validSolutions);
vector<Mat> rotations;
vector<Mat> normals;
for (size_t i = 0; i < (sizeof(rotationsArray) / sizeof(*rotationsArray)); i++) {
Mat tempRotMat = Mat(Matx33d(
rotationsArray[i][0],
rotationsArray[i][1],
rotationsArray[i][2],
rotationsArray[i][3],
rotationsArray[i][4],
rotationsArray[i][5],
rotationsArray[i][6],
rotationsArray[i][7],
rotationsArray[i][8]
));
Mat tempNormMat = Mat(Matx31d(
normalsArray[i][0],
normalsArray[i][1],
normalsArray[i][2]
));
rotations.push_back(tempRotMat);
normals.push_back(tempNormMat);
}
_rotations.swap(rotations);
_normals.swap(normals);
_mask = Mat(514, 1, CV_8S, maskArray).clone();
}
bool isValidResult(const vector<int>& solutions)
{
return (solutions == _validSolutions);
}
vector<int> _validSolutions;
vector<Point2f> _prevRectifiedPoints, _currRectifiedPoints;
Mat _mask;
vector<Mat> _rotations, _normals;
};
TEST(Calib3d_FilterDecomposeHomography, regression) { CV_FilterHomographyDecompTest test; test.safe_run(); }
}}
+556
View File
@@ -0,0 +1,556 @@
// 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 "test_precomp.hpp"
#include <opencv2/ts/cuda_test.hpp> // EXPECT_MAT_NEAR
#include "opencv2/videoio.hpp"
namespace opencv_test { namespace {
class fisheyeTest : public ::testing::Test {
protected:
const static cv::Size imageSize;
const static cv::Matx33d K;
const static cv::Vec4d D;
std::string datasets_repository_path;
virtual void SetUp() {
datasets_repository_path = combine(cvtest::TS::ptr()->get_data_path(), "cv/cameracalibration/fisheye");
}
protected:
std::string combine(const std::string& _item1, const std::string& _item2);
};
const cv::Size fisheyeTest::imageSize(1280, 800);
const cv::Matx33d fisheyeTest::K(558.478087865323, 0, 620.458515360843,
0, 560.506767351568, 381.939424848348,
0, 0, 1);
const cv::Vec4d fisheyeTest::D(-0.0014613319981768, -0.00329861110580401, 0.00605760088590183, -0.00374209380722371);
std::string fisheyeTest::combine(const std::string& _item1, const std::string& _item2)
{
std::string item1 = _item1, item2 = _item2;
std::replace(item1.begin(), item1.end(), '\\', '/');
std::replace(item2.begin(), item2.end(), '\\', '/');
if (item1.empty())
return item2;
if (item2.empty())
return item1;
char last = item1[item1.size()-1];
return item1 + (last != '/' ? "/" : "") + item2;
}
TEST_F(fisheyeTest, projectPoints)
{
double cols = this->imageSize.width,
rows = this->imageSize.height;
const int N = 20;
cv::Mat distorted0(1, N*N, CV_64FC2), undist1, undist2, distorted1, distorted2;
undist2.create(distorted0.size(), CV_MAKETYPE(distorted0.depth(), 3));
cv::Vec2d* pts = distorted0.ptr<cv::Vec2d>();
cv::Vec2d c(this->K(0, 2), this->K(1, 2));
for(int y = 0, k = 0; y < N; ++y)
for(int x = 0; x < N; ++x)
{
cv::Vec2d point(x*cols/(N-1.f), y*rows/(N-1.f));
pts[k++] = (point - c) * 0.85 + c;
}
cv::fisheye::undistortPoints(distorted0, undist1, this->K, this->D);
cv::Vec2d* u1 = undist1.ptr<cv::Vec2d>();
cv::Vec3d* u2 = undist2.ptr<cv::Vec3d>();
for(int i = 0; i < (int)distorted0.total(); ++i)
u2[i] = cv::Vec3d(u1[i][0], u1[i][1], 1.0);
cv::fisheye::distortPoints(undist1, distorted1, this->K, this->D);
cv::fisheye::projectPoints(undist2, distorted2, cv::Vec3d::all(0), cv::Vec3d::all(0), this->K, this->D);
EXPECT_MAT_NEAR(distorted0, distorted1, 1e-10);
EXPECT_MAT_NEAR(distorted0, distorted2, 1e-10);
}
TEST_F(fisheyeTest, distortUndistortPoints)
{
int width = imageSize.width;
int height = imageSize.height;
/* Create test points */
cv::Mat principalPoints = (cv::Mat_<double>(5, 2) << K(0, 2), K(1, 2), // (cx, cy)
/* Image corners */
0, 0,
0, height,
width, 0,
width, height
);
/* Random points inside image */
cv::Mat xy[2] = {};
xy[0].create(100, 1, CV_64F);
theRNG().fill(xy[0], cv::RNG::UNIFORM, 0, width); // x
xy[1].create(100, 1, CV_64F);
theRNG().fill(xy[1], cv::RNG::UNIFORM, 0, height); // y
cv::Mat randomPoints;
merge(xy, 2, randomPoints);
cv::Mat points0;
cv::vconcat(principalPoints.reshape(2), randomPoints, points0);
/* Test with random D set */
for (size_t i = 0; i < 10; ++i) {
cv::Mat distortion(1, 4, CV_64F);
theRNG().fill(distortion, cv::RNG::UNIFORM, -0.00001, 0.00001);
/* Distort -> Undistort */
cv::Mat distortedPoints;
cv::fisheye::distortPoints(points0, distortedPoints, K, distortion);
cv::Mat undistortedPoints;
cv::fisheye::undistortPoints(distortedPoints, undistortedPoints, K, distortion);
EXPECT_MAT_NEAR(points0, undistortedPoints, 1e-8);
/* Undistort -> Distort */
cv::fisheye::undistortPoints(points0, undistortedPoints, K, distortion);
cv::fisheye::distortPoints(undistortedPoints, distortedPoints, K, distortion);
EXPECT_MAT_NEAR(points0, distortedPoints, 1e-8);
}
}
TEST_F(fisheyeTest, distortUndistortPointsNewCameraFixed)
{
int width = imageSize.width;
int height = imageSize.height;
/* Random points inside image */
cv::Mat xy[2] = {};
xy[0].create(100, 1, CV_64F);
theRNG().fill(xy[0], cv::RNG::UNIFORM, 0, width); // x
xy[1].create(100, 1, CV_64F);
theRNG().fill(xy[1], cv::RNG::UNIFORM, 0, height); // y
cv::Mat randomPoints;
merge(xy, 2, randomPoints);
cv::Mat points0 = randomPoints;
cv::Mat Reye = cv::Mat::eye(3, 3, CV_64FC1);
cv::Mat Knew;
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(K, D, imageSize, Reye, Knew);
/* Distort -> Undistort */
cv::Mat distortedPoints;
cv::fisheye::distortPoints(points0, distortedPoints, Knew, K, D);
cv::Mat undistortedPoints;
cv::fisheye::undistortPoints(distortedPoints, undistortedPoints, K, D, Reye, Knew);
EXPECT_MAT_NEAR(points0, undistortedPoints, 1e-8);
/* Undistort -> Distort */
cv::fisheye::undistortPoints(points0, undistortedPoints, K, D, Reye, Knew);
cv::fisheye::distortPoints(undistortedPoints, distortedPoints, Knew, K, D);
EXPECT_MAT_NEAR(points0, distortedPoints, 1e-8);
}
TEST_F(fisheyeTest, distortUndistortPointsNewCameraRandom)
{
int width = imageSize.width;
int height = imageSize.height;
/* Create test points */
std::vector<cv::Point2d> points0Vector;
cv::Mat principalPoints = (cv::Mat_<double>(5, 2) << K(0, 2), K(1, 2), // (cx, cy)
/* Image corners */
0, 0,
0, height,
width, 0,
width, height
);
/* Random points inside image */
cv::Mat xy[2] = {};
xy[0].create(100, 1, CV_64F);
theRNG().fill(xy[0], cv::RNG::UNIFORM, 0, width); // x
xy[1].create(100, 1, CV_64F);
theRNG().fill(xy[1], cv::RNG::UNIFORM, 0, height); // y
cv::Mat randomPoints;
merge(xy, 2, randomPoints);
cv::Mat points0;
cv::Mat Reye = cv::Mat::eye(3, 3, CV_64FC1);
cv::vconcat(principalPoints.reshape(2), randomPoints, points0);
/* Test with random D set */
for (size_t i = 0; i < 10; ++i) {
cv::Mat distortion(1, 4, CV_64F);
theRNG().fill(distortion, cv::RNG::UNIFORM, -0.001, 0.001);
cv::Mat Knew;
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(K, distortion, imageSize, Reye, Knew);
/* Distort -> Undistort */
cv::Mat distortedPoints;
cv::fisheye::distortPoints(points0, distortedPoints, Knew, K, distortion);
cv::Mat undistortedPoints;
cv::fisheye::undistortPoints(distortedPoints, undistortedPoints, K, distortion, Reye, Knew);
EXPECT_MAT_NEAR(points0, undistortedPoints, 1e-8);
/* Undistort -> Distort */
cv::fisheye::undistortPoints(points0, undistortedPoints, K, distortion, Reye, Knew);
cv::fisheye::distortPoints(undistortedPoints, distortedPoints, Knew, K, distortion);
EXPECT_MAT_NEAR(points0, distortedPoints, 1e-8);
}
}
TEST_F(fisheyeTest, solvePnP)
{
const int n = 16;
const cv::Matx33d R_mat ( 9.9756700084424932e-01, 6.9698277640183867e-02, 1.4929569991321144e-03,
-6.9711825162322980e-02, 9.9748249845531767e-01, 1.2997180766418455e-02,
-5.8331736398316541e-04,-1.3069635393884985e-02, 9.9991441852366736e-01);
const cv::Vec3d T(-9.9217369356044638e-02, 3.1741831972356663e-03, 1.8551007952921010e-04);
cv::Mat obj_points(1, n, CV_64FC3);
theRNG().fill(obj_points, cv::RNG::NORMAL, 2, 1);
obj_points = cv::abs(obj_points) * 10;
cv::Mat R;
cv::Rodrigues(R_mat, R);
cv::Mat img_points;
cv::fisheye::projectPoints(obj_points, img_points, R, T, this->K, this->D);
cv::Mat rvec_pred;
cv::Mat tvec_pred;
bool converged = cv::fisheye::solvePnP(obj_points, img_points, this->K, this->D, rvec_pred, tvec_pred);
EXPECT_MAT_NEAR(R, rvec_pred, 1e-6);
EXPECT_MAT_NEAR(T, tvec_pred, 1e-6);
ASSERT_TRUE(converged);
}
TEST_F(fisheyeTest, solvePnPRansac)
{
const int inliers_n = 16;
const int outliers_n = 4;
const bool use_extrinsic_guess = false;
const int iterations_count = 100;
const float reprojection_error = 1.0;
const double confidence = 0.99;
const cv::Matx33d R_mat ( 9.9756700084424932e-01, 6.9698277640183867e-02, 1.4929569991321144e-03,
-6.9711825162322980e-02, 9.9748249845531767e-01, 1.2997180766418455e-02,
-5.8331736398316541e-04,-1.3069635393884985e-02, 9.9991441852366736e-01);
const cv::Vec3d T(-9.9217369356044638e-02, 3.1741831972356663e-03, 1.8551007952921010e-04);
cv::Mat rvec;
cv::Rodrigues(R_mat, rvec);
// inliers
cv::Mat inlier_obj_points(1, inliers_n, CV_64FC3);
theRNG().fill(inlier_obj_points, cv::RNG::NORMAL, 2, 1);
inlier_obj_points = cv::abs(inlier_obj_points) * 10;
cv::Mat inlier_img_points;
cv::fisheye::projectPoints(inlier_obj_points, inlier_img_points, rvec, T, this->K, this->D);
// outliers
cv::Mat outlier_obj_points(1, outliers_n, CV_64FC3);
theRNG().fill(outlier_obj_points, cv::RNG::NORMAL, 2, 1);
outlier_obj_points = cv::abs(outlier_obj_points) * 10;
cv::Mat outlier_img_points;
cv::fisheye::projectPoints(outlier_obj_points, outlier_img_points, rvec, (T * 10), this->K, this->D);
cv::Mat obj_points;
cv::hconcat(outlier_obj_points, inlier_obj_points, obj_points);
cv::Mat img_points;
cv::hconcat(outlier_img_points, inlier_img_points, img_points);
cv::Mat rvec_pred;
cv::Mat tvec_pred;
cv::Mat inliers_pred;
bool converged = cv::fisheye::solvePnPRansac(obj_points, img_points, this->K, this->D,
rvec_pred, tvec_pred, use_extrinsic_guess,
iterations_count, reprojection_error, confidence, inliers_pred);
EXPECT_MAT_NEAR(rvec, rvec_pred, 1e-5);
EXPECT_MAT_NEAR(T, tvec_pred, 1e-5);
EXPECT_EQ(inliers_pred.size[0], inliers_n);
ASSERT_TRUE(converged);
}
TEST_F(fisheyeTest, undistortImage)
{
// we use it to reduce patch size for images in testdata
auto throwAwayHalf = [](Mat img)
{
int whalf = img.cols / 2, hhalf = img.rows / 2;
Rect tl(0, 0, whalf, hhalf), br(whalf, hhalf, whalf, hhalf);
img(tl) = 0;
img(br) = 0;
};
cv::Matx33d theK = this->K;
cv::Mat theD = cv::Mat(this->D);
std::string file = combine(datasets_repository_path, "stereo_pair_014.png");
cv::Matx33d newK = theK;
cv::Mat distorted = cv::imread(file), undistorted;
{
newK(0, 0) = 100;
newK(1, 1) = 100;
cv::fisheye::undistortImage(distorted, undistorted, theK, theD, newK);
std::string imageFilename = combine(datasets_repository_path, "new_f_100.png");
cv::Mat correct = cv::imread(imageFilename);
ASSERT_FALSE(correct.empty()) << "Correct image " << imageFilename.c_str() << " can not be read" << std::endl;
throwAwayHalf(correct);
throwAwayHalf(undistorted);
EXPECT_MAT_NEAR(correct, undistorted, 1e-10);
}
{
double balance = 1.0;
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(theK, theD, distorted.size(), cv::noArray(), newK, balance);
cv::fisheye::undistortImage(distorted, undistorted, theK, theD, newK);
std::string imageFilename = combine(datasets_repository_path, "balance_1.0.png");
cv::Mat correct = cv::imread(imageFilename);
ASSERT_FALSE(correct.empty()) << "Correct image " << imageFilename.c_str() << " can not be read" << std::endl;
throwAwayHalf(correct);
throwAwayHalf(undistorted);
EXPECT_MAT_NEAR(correct, undistorted, 1e-10);
}
{
double balance = 0.0;
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(theK, theD, distorted.size(), cv::noArray(), newK, balance);
cv::fisheye::undistortImage(distorted, undistorted, theK, theD, newK);
std::string imageFilename = combine(datasets_repository_path, "balance_0.0.png");
cv::Mat correct = cv::imread(imageFilename);
ASSERT_FALSE(correct.empty()) << "Correct image " << imageFilename.c_str() << " can not be read" << std::endl;
throwAwayHalf(correct);
throwAwayHalf(undistorted);
EXPECT_MAT_NEAR(correct, undistorted, 1e-10);
}
}
TEST_F(fisheyeTest, undistortAndDistortImage)
{
cv::Matx33d K_src = this->K;
cv::Mat D_src = cv::Mat(this->D);
std::string file = combine(datasets_repository_path, "/calib-3_stereo_from_JY/left/stereo_pair_014.jpg");
cv::Matx33d K_dst = K_src;
cv::Mat image = cv::imread(file), image_projected;
cv::Vec4d D_dst_vec (-1.0, 0.0, 0.0, 0.0);
cv::Mat D_dst = cv::Mat(D_dst_vec);
int imageWidth = (int)this->imageSize.width;
int imageHeight = (int)this->imageSize.height;
cv::Mat imagePoints(imageHeight, imageWidth, CV_32FC2), undPoints, distPoints;
cv::Vec2f* pts = imagePoints.ptr<cv::Vec2f>();
for(int y = 0, k = 0; y < imageHeight; ++y)
{
for(int x = 0; x < imageWidth; ++x)
{
cv::Vec2f point((float)x, (float)y);
pts[k++] = point;
}
}
cv::fisheye::undistortPoints(imagePoints, undPoints, K_dst, D_dst);
cv::fisheye::distortPoints(undPoints, distPoints, K_src, D_src);
cv::remap(image, image_projected, distPoints, cv::noArray(), cv::INTER_LINEAR);
float dx, dy, r_sq;
float R_MAX = 250;
float imageCenterX = (float)imageWidth / 2;
float imageCenterY = (float)imageHeight / 2;
cv::Mat undPointsGt(imageHeight, imageWidth, CV_32FC2);
cv::Mat imageGt(imageHeight, imageWidth, CV_8UC3);
for(int y = 0; y < imageHeight; ++y)
{
for(int x = 0; x < imageWidth; ++x)
{
dx = x - imageCenterX;
dy = y - imageCenterY;
r_sq = dy * dy + dx * dx;
Vec2f & und_vec = undPoints.at<Vec2f>(y,x);
Vec3b & pixel = image_projected.at<Vec3b>(y,x);
Vec2f & undist_vec_gt = undPointsGt.at<Vec2f>(y,x);
Vec3b & pixel_gt = imageGt.at<Vec3b>(y,x);
if (r_sq > R_MAX * R_MAX)
{
undist_vec_gt[0] = -1e6;
undist_vec_gt[1] = -1e6;
pixel_gt[0] = 0;
pixel_gt[1] = 0;
pixel_gt[2] = 0;
}
else
{
undist_vec_gt[0] = und_vec[0];
undist_vec_gt[1] = und_vec[1];
pixel_gt[0] = pixel[0];
pixel_gt[1] = pixel[1];
pixel_gt[2] = pixel[2];
}
}
}
EXPECT_MAT_NEAR(undPoints, undPointsGt, 1e-10);
EXPECT_MAT_NEAR(image_projected, imageGt, 1e-10);
Vec2f dist_point_1 = distPoints.at<Vec2f>(400, 640);
Vec2f dist_point_1_gt(640.044f, 400.041f);
Vec2f dist_point_2 = distPoints.at<Vec2f>(400, 440);
Vec2f dist_point_2_gt(409.731f, 403.029f);
Vec2f dist_point_3 = distPoints.at<Vec2f>(200, 640);
Vec2f dist_point_3_gt(643.341f, 168.896f);
Vec2f dist_point_4 = distPoints.at<Vec2f>(300, 480);
Vec2f dist_point_4_gt(463.402f, 290.317f);
Vec2f dist_point_5 = distPoints.at<Vec2f>(550, 750);
Vec2f dist_point_5_gt(797.51f, 611.637f);
EXPECT_MAT_NEAR(dist_point_1, dist_point_1_gt, 1e-2);
EXPECT_MAT_NEAR(dist_point_2, dist_point_2_gt, 1e-2);
EXPECT_MAT_NEAR(dist_point_3, dist_point_3_gt, 1e-2);
EXPECT_MAT_NEAR(dist_point_4, dist_point_4_gt, 1e-2);
EXPECT_MAT_NEAR(dist_point_5, dist_point_5_gt, 1e-2);
// Add the "--test_debug" to arguments for file output
if (cvtest::debugLevel > 0)
cv::imwrite(combine(datasets_repository_path, "new_distortion.png"), image_projected);
}
TEST_F(fisheyeTest, jacobians)
{
int n = 10;
cv::Mat X(1, n, CV_64FC3);
cv::Mat om(3, 1, CV_64F), theT(3, 1, CV_64F);
cv::Mat f(2, 1, CV_64F), c(2, 1, CV_64F);
cv::Mat k(4, 1, CV_64F);
double alpha;
cv::RNG r;
r.fill(X, cv::RNG::NORMAL, 2, 1);
X = cv::abs(X) * 10;
r.fill(om, cv::RNG::NORMAL, 0, 1);
om = cv::abs(om);
r.fill(theT, cv::RNG::NORMAL, 0, 1);
theT = cv::abs(theT); theT.at<double>(2) = 4; theT *= 10;
r.fill(f, cv::RNG::NORMAL, 0, 1);
f = cv::abs(f) * 1000;
r.fill(c, cv::RNG::NORMAL, 0, 1);
c = cv::abs(c) * 1000;
r.fill(k, cv::RNG::NORMAL, 0, 1);
k*= 0.5;
alpha = 0.01*r.gaussian(1);
cv::Mat x1, x2, xpred;
cv::Matx33d theK(f.at<double>(0), alpha * f.at<double>(0), c.at<double>(0),
0, f.at<double>(1), c.at<double>(1),
0, 0, 1);
cv::Mat jacobians;
cv::fisheye::projectPoints(X, x1, om, theT, theK, k, alpha, jacobians);
//test on T:
cv::Mat dT(3, 1, CV_64FC1);
r.fill(dT, cv::RNG::NORMAL, 0, 1);
dT *= 1e-9*cv::norm(theT);
cv::Mat T2 = theT + dT;
cv::fisheye::projectPoints(X, x2, om, T2, theK, k, alpha, cv::noArray());
xpred = x1 + cv::Mat(jacobians.colRange(11,14) * dT).reshape(2, 1);
CV_Assert (cv::norm(x2 - xpred) < 1e-10);
//test on om:
cv::Mat dom(3, 1, CV_64FC1);
r.fill(dom, cv::RNG::NORMAL, 0, 1);
dom *= 1e-9*cv::norm(om);
cv::Mat om2 = om + dom;
cv::fisheye::projectPoints(X, x2, om2, theT, theK, k, alpha, cv::noArray());
xpred = x1 + cv::Mat(jacobians.colRange(8,11) * dom).reshape(2, 1);
CV_Assert (cv::norm(x2 - xpred) < 1e-10);
//test on f:
cv::Mat df(2, 1, CV_64FC1);
r.fill(df, cv::RNG::NORMAL, 0, 1);
df *= 1e-9*cv::norm(f);
cv::Matx33d K2 = theK + cv::Matx33d(df.at<double>(0), df.at<double>(0) * alpha, 0, 0, df.at<double>(1), 0, 0, 0, 0);
cv::fisheye::projectPoints(X, x2, om, theT, K2, k, alpha, cv::noArray());
xpred = x1 + cv::Mat(jacobians.colRange(0,2) * df).reshape(2, 1);
CV_Assert (cv::norm(x2 - xpred) < 1e-10);
//test on c:
cv::Mat dc(2, 1, CV_64FC1);
r.fill(dc, cv::RNG::NORMAL, 0, 1);
dc *= 1e-9*cv::norm(c);
K2 = theK + cv::Matx33d(0, 0, dc.at<double>(0), 0, 0, dc.at<double>(1), 0, 0, 0);
cv::fisheye::projectPoints(X, x2, om, theT, K2, k, alpha, cv::noArray());
xpred = x1 + cv::Mat(jacobians.colRange(2,4) * dc).reshape(2, 1);
CV_Assert (cv::norm(x2 - xpred) < 1e-10);
//test on k:
cv::Mat dk(4, 1, CV_64FC1);
r.fill(dk, cv::RNG::NORMAL, 0, 1);
dk *= 1e-9*cv::norm(k);
cv::Mat k2 = k + dk;
cv::fisheye::projectPoints(X, x2, om, theT, theK, k2, alpha, cv::noArray());
xpred = x1 + cv::Mat(jacobians.colRange(4,8) * dk).reshape(2, 1);
CV_Assert (cv::norm(x2 - xpred) < 1e-10);
//test on alpha:
cv::Mat dalpha(1, 1, CV_64FC1);
r.fill(dalpha, cv::RNG::NORMAL, 0, 1);
dalpha *= 1e-9*cv::norm(f);
double alpha2 = alpha + dalpha.at<double>(0);
K2 = theK + cv::Matx33d(0, f.at<double>(0) * dalpha.at<double>(0), 0, 0, 0, 0, 0, 0, 0);
cv::fisheye::projectPoints(X, x2, om, theT, theK, k, alpha2, cv::noArray());
xpred = x1 + cv::Mat(jacobians.col(14) * dalpha).reshape(2, 1);
CV_Assert (cv::norm(x2 - xpred) < 1e-10);
}
}}
+301
View File
@@ -0,0 +1,301 @@
// 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.
//
// Copyright (C) 2016, Itseez, Inc, all rights reserved.
#include "test_precomp.hpp"
namespace opencv_test { namespace {
static double algebraic_dist(const Point2f& pt, const RotatedRect& el) {
const Point2d to_pt = pt - el.center;
const double el_angle = el.angle * CV_PI / 180;
const Point2d to_pt_el(
to_pt.x * cos(-el_angle) - to_pt.y * sin(-el_angle),
to_pt.x * sin(-el_angle) + to_pt.y * cos(-el_angle));
return normL2Sqr<double>(Point2d(2 * to_pt_el.x / el.size.width, 2 * to_pt_el.y / el.size.height)) - 1;
}
static double rms_algebraic_dist(const vector<Point2f>& pts, const RotatedRect& el) {
double sum_algebraic_dists_sqr = 0;
for (const auto& pt : pts) {
const auto pt_algebraic_dist = algebraic_dist(pt, el);
sum_algebraic_dists_sqr += pt_algebraic_dist * pt_algebraic_dist;
}
return sqrt(sum_algebraic_dists_sqr / pts.size());
}
TEST(Imgproc_FitEllipse_Issue_4515, accuracy) {
vector<Point2f> pts;
pts.push_back(Point2f(327, 317));
pts.push_back(Point2f(328, 316));
pts.push_back(Point2f(329, 315));
pts.push_back(Point2f(330, 314));
pts.push_back(Point2f(331, 314));
pts.push_back(Point2f(332, 314));
pts.push_back(Point2f(333, 315));
pts.push_back(Point2f(333, 316));
pts.push_back(Point2f(333, 317));
pts.push_back(Point2f(333, 318));
pts.push_back(Point2f(333, 319));
pts.push_back(Point2f(333, 320));
const RotatedRect ellipse = fitEllipseDirect(pts); // fitEllipseAMS() also works fine
EXPECT_LT(rms_algebraic_dist(pts, ellipse), 1e-1);
}
TEST(Imgproc_FitEllipse_Issue_6544, accuracy) {
vector<Point2f> pts;
pts.push_back(Point2f(924.784f, 764.160f));
pts.push_back(Point2f(928.388f, 615.903f));
pts.push_back(Point2f(847.4f, 888.014f));
pts.push_back(Point2f(929.406f, 741.675f));
pts.push_back(Point2f(904.564f, 825.605f));
pts.push_back(Point2f(926.742f, 760.746f));
pts.push_back(Point2f(863.479f, 873.406f));
pts.push_back(Point2f(910.987f, 808.863f));
pts.push_back(Point2f(929.145f, 744.976f));
pts.push_back(Point2f(917.474f, 791.823f));
const RotatedRect ellipse = fitEllipseDirect(pts); // fitEllipseAMS() also works fine
EXPECT_LT(rms_algebraic_dist(pts, ellipse), 5e-2);
}
TEST(Imgproc_FitEllipse_Issue_10270, accuracy) {
vector<Point2f> pts;
pts.push_back(Point2f(0, 1));
pts.push_back(Point2f(0, 2));
pts.push_back(Point2f(0, 3));
pts.push_back(Point2f(2, 3));
pts.push_back(Point2f(0, 4));
// check that we get almost vertical ellipse centered around (1, 3)
RotatedRect e = fitEllipse(pts);
EXPECT_LT(std::min(fabs(e.angle-180), fabs(e.angle)), 10.);
EXPECT_NEAR(e.center.x, 1, 1);
EXPECT_NEAR(e.center.y, 3, 1);
EXPECT_LT(e.size.width*3, e.size.height);
}
TEST(Imgproc_FitEllipse_JavaCase, accuracy) {
vector<Point2f> pts;
pts.push_back(Point2f(0, 0));
pts.push_back(Point2f(1, 1));
pts.push_back(Point2f(-1, 1));
pts.push_back(Point2f(-1, -1));
pts.push_back(Point2f(1, -1));
// check that we get almost circle centered around (0, 0)
RotatedRect e = fitEllipse(pts);
EXPECT_NEAR(e.center.x, 0, 0.01);
EXPECT_NEAR(e.center.y, 0, 0.01);
EXPECT_NEAR(e.size.width, sqrt(2.)*2, 0.4);
EXPECT_NEAR(e.size.height, sqrt(2.)*2, 0.4);
}
TEST(Imgproc_FitEllipse_HorizontalLine, accuracy) {
vector<Point2f> pts({{-300, 100}, {-200, 100}, {-100, 100}, {0, 100}, {100, 100}, {200, 100}, {300, 100}});
const RotatedRect el = fitEllipse(pts);
EXPECT_NEAR(el.center.x, -100, 100);
EXPECT_NEAR(el.center.y, 100, 1);
EXPECT_NEAR(el.size.width, 1, 1);
EXPECT_GE(el.size.height, 150);
EXPECT_NEAR(el.angle, 90, 0.1);
}
template<typename T>
static float get_ellipse_fitting_error(const std::vector<T>& points, const Mat& closest_points) {
float mse = 0.0f;
for (int i = 0; i < static_cast<int>(points.size()); i++)
{
Point2f pt_err = Point2f(static_cast<float>(points[i].x), static_cast<float>(points[i].y)) - closest_points.at<Point2f>(i);
mse += pt_err.x*pt_err.x + pt_err.y*pt_err.y;
}
return mse / points.size();
}
TEST(Imgproc_getClosestEllipsePoints, ellipse_mse) {
// https://github.com/opencv/opencv/issues/26078
std::vector<Point2i> points_list;
// [1434, 308], [1434, 309], [1433, 310], [1427, 310], [1427, 312], [1426, 313], [1422, 313], [1422, 314],
points_list.push_back(Point2i(1434, 308));
points_list.push_back(Point2i(1434, 309));
points_list.push_back(Point2i(1433, 310));
points_list.push_back(Point2i(1427, 310));
points_list.push_back(Point2i(1427, 312));
points_list.push_back(Point2i(1426, 313));
points_list.push_back(Point2i(1422, 313));
points_list.push_back(Point2i(1422, 314));
// [1421, 315], [1415, 315], [1415, 316], [1414, 317], [1408, 317], [1408, 319], [1407, 320], [1403, 320],
points_list.push_back(Point2i(1421, 315));
points_list.push_back(Point2i(1415, 315));
points_list.push_back(Point2i(1415, 316));
points_list.push_back(Point2i(1414, 317));
points_list.push_back(Point2i(1408, 317));
points_list.push_back(Point2i(1408, 319));
points_list.push_back(Point2i(1407, 320));
points_list.push_back(Point2i(1403, 320));
// [1403, 321], [1402, 322], [1396, 322], [1396, 323], [1395, 324], [1389, 324], [1389, 326], [1388, 327],
points_list.push_back(Point2i(1403, 321));
points_list.push_back(Point2i(1402, 322));
points_list.push_back(Point2i(1396, 322));
points_list.push_back(Point2i(1396, 323));
points_list.push_back(Point2i(1395, 324));
points_list.push_back(Point2i(1389, 324));
points_list.push_back(Point2i(1389, 326));
points_list.push_back(Point2i(1388, 327));
// [1382, 327], [1382, 328], [1381, 329], [1376, 329], [1376, 330], [1375, 331], [1369, 331], [1369, 333],
points_list.push_back(Point2i(1382, 327));
points_list.push_back(Point2i(1382, 328));
points_list.push_back(Point2i(1381, 329));
points_list.push_back(Point2i(1376, 329));
points_list.push_back(Point2i(1376, 330));
points_list.push_back(Point2i(1375, 331));
points_list.push_back(Point2i(1369, 331));
points_list.push_back(Point2i(1369, 333));
// [1368, 334], [1362, 334], [1362, 335], [1361, 336], [1359, 336], [1359, 1016], [1365, 1016], [1366, 1017],
points_list.push_back(Point2i(1368, 334));
points_list.push_back(Point2i(1362, 334));
points_list.push_back(Point2i(1362, 335));
points_list.push_back(Point2i(1361, 336));
points_list.push_back(Point2i(1359, 336));
points_list.push_back(Point2i(1359, 1016));
points_list.push_back(Point2i(1365, 1016));
points_list.push_back(Point2i(1366, 1017));
// [1366, 1019], [1430, 1019], [1430, 1017], [1431, 1016], [1440, 1016], [1440, 308]
points_list.push_back(Point2i(1366, 1019));
points_list.push_back(Point2i(1430, 1019));
points_list.push_back(Point2i(1430, 1017));
points_list.push_back(Point2i(1431, 1016));
points_list.push_back(Point2i(1440, 1016));
points_list.push_back(Point2i(1440, 308));
RotatedRect fit_ellipse_params(
Point2f(1442.97900390625, 662.1879272460938),
Size2f(579.5570678710938, 730.834228515625),
20.190902709960938
);
// Point2i
{
Mat pointsi(points_list);
Mat closest_pts;
getClosestEllipsePoints(fit_ellipse_params, pointsi, closest_pts);
EXPECT_TRUE(pointsi.rows == closest_pts.rows);
EXPECT_TRUE(pointsi.cols == closest_pts.cols);
EXPECT_TRUE(pointsi.channels() == closest_pts.channels());
float fit_ellipse_mse = get_ellipse_fitting_error(points_list, closest_pts);
EXPECT_NEAR(fit_ellipse_mse, 1.61994, 1e-4);
}
// Point2f
{
Mat pointsf;
Mat(points_list).convertTo(pointsf, CV_32F);
Mat closest_pts;
getClosestEllipsePoints(fit_ellipse_params, pointsf, closest_pts);
EXPECT_TRUE(pointsf.rows == closest_pts.rows);
EXPECT_TRUE(pointsf.cols == closest_pts.cols);
EXPECT_TRUE(pointsf.channels() == closest_pts.channels());
float fit_ellipse_mse = get_ellipse_fitting_error(points_list, closest_pts);
EXPECT_NEAR(fit_ellipse_mse, 1.61994, 1e-4);
}
}
static std::vector<Point2f> sample_ellipse_pts(const RotatedRect& ellipse_params) {
// Sample N points using the ellipse parametric form
float xc = ellipse_params.center.x;
float yc = ellipse_params.center.y;
float a = ellipse_params.size.width / 2;
float b = ellipse_params.size.height / 2;
float theta = static_cast<float>(ellipse_params.angle * M_PI / 180);
float cos_th = std::cos(theta);
float sin_th = std::sin(theta);
int nb_samples = 180;
std::vector<Point2f> ellipse_pts(nb_samples);
for (int i = 0; i < nb_samples; i++) {
float ax = a * cos_th;
float ay = a * sin_th;
float bx = -b * sin_th;
float by = b * cos_th;
float t = static_cast<float>(i / static_cast<float>(nb_samples) * 2*M_PI);
float cos_t = std::cos(t);
float sin_t = std::sin(t);
ellipse_pts[i].x = xc + ax*cos_t + bx*sin_t;
ellipse_pts[i].y = yc + ay*cos_t + by*sin_t;
}
return ellipse_pts;
}
TEST(Imgproc_getClosestEllipsePoints, ellipse_mse_2) {
const float tol = 1e-3f;
// bb height > width
// Check correctness of the minor/major axes swapping and updated angle in getClosestEllipsePoints
{
RotatedRect ellipse_params(
Point2f(-142.97f, -662.1878f),
Size2f(539.557f, 730.83f),
27.09960938f
);
std::vector<Point2f> ellipse_pts = sample_ellipse_pts(ellipse_params);
Mat pointsf, closest_pts;
Mat(ellipse_pts).convertTo(pointsf, CV_32F);
getClosestEllipsePoints(ellipse_params, pointsf, closest_pts);
float ellipse_pts_mse = get_ellipse_fitting_error(ellipse_pts, closest_pts);
EXPECT_NEAR(ellipse_pts_mse, 0, tol);
}
// bb height > width + negative angle
{
RotatedRect ellipse_params(
Point2f(-142.97f, 562.1878f),
Size2f(53.557f, 730.83f),
-75.09960938f
);
std::vector<Point2f> ellipse_pts = sample_ellipse_pts(ellipse_params);
Mat pointsf, closest_pts;
Mat(ellipse_pts).convertTo(pointsf, CV_32F);
getClosestEllipsePoints(ellipse_params, pointsf, closest_pts);
float ellipse_pts_mse = get_ellipse_fitting_error(ellipse_pts, closest_pts);
EXPECT_NEAR(ellipse_pts_mse, 0, tol);
}
// Negative angle
{
RotatedRect ellipse_params(
Point2f(742.97f, -462.1878f),
Size2f(535.57f, 130.83f),
-75.09960938f
);
std::vector<Point2f> ellipse_pts = sample_ellipse_pts(ellipse_params);
Mat pointsf, closest_pts;
Mat(ellipse_pts).convertTo(pointsf, CV_32F);
getClosestEllipsePoints(ellipse_params, pointsf, closest_pts);
float ellipse_pts_mse = get_ellipse_fitting_error(ellipse_pts, closest_pts);
EXPECT_NEAR(ellipse_pts_mse, 0, tol);
}
}
}} // namespace
@@ -0,0 +1,371 @@
// 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.
//
// Copyright (C) 2016, Itseez, Inc, all rights reserved.
#include "test_precomp.hpp"
namespace opencv_test { namespace {
static bool checkEllipse(const RotatedRect& ellipseAMSTest, const RotatedRect& ellipseAMSTrue, const float tol) {
Point2f ellipseAMSTrueVertices[4];
Point2f ellipseAMSTestVertices[4];
ellipseAMSTest.points(ellipseAMSTestVertices);
ellipseAMSTrue.points(ellipseAMSTrueVertices);
float AMSDiff = 0.0f;
for (size_t i=0; i <=3; i++) {
Point2f diff = ellipseAMSTrueVertices[i] - ellipseAMSTestVertices[0];
float d = diff.x * diff.x + diff.y * diff.y;
for (size_t j=1; j <=3; j++) {
diff = ellipseAMSTrueVertices[i] - ellipseAMSTestVertices[j];
float dd = diff.x * diff.x + diff.y * diff.y;
if(dd<d){d=dd;}
}
AMSDiff += std::sqrt(d);
}
return AMSDiff < tol;
}
TEST(Imgproc_FitEllipseAMS_Issue_1, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(173.41854895999165f, 125.84473135880411f));
pts.push_back(Point2f(180.63769498640912f, 130.960006577589f));
pts.push_back(Point2f(174.99173759130173f, 137.34265632926764f));
pts.push_back(Point2f(170.9044645313217f, 141.68017556480243f));
pts.push_back(Point2f(163.48965388499656f, 141.9404438924043f));
pts.push_back(Point2f(159.37687818401147f, 148.60835331594876f));
pts.push_back(Point2f(150.38917629356735f, 155.68825577720446f));
pts.push_back(Point2f(147.16319653316862f, 157.06039984963923f));
pts.push_back(Point2f(141.73118707843207f, 157.2570155198414f));
pts.push_back(Point2f(130.61569602948597f, 159.40742182929364f));
pts.push_back(Point2f(127.00573042229027f, 161.34430232187867f));
pts.push_back(Point2f(120.49383815053747f, 163.72610883128334f));
pts.push_back(Point2f(114.62383760040998f, 162.6788666385239f));
pts.push_back(Point2f(108.84871269183333f, 161.90597054388132f));
pts.push_back(Point2f(103.04574087829076f, 167.44352944383985f));
pts.push_back(Point2f(96.31623870161255f, 163.71641295746116f));
pts.push_back(Point2f(89.86174417295126f, 157.2967811253635f));
pts.push_back(Point2f(84.27940674801192f, 168.6331304010667f));
pts.push_back(Point2f(76.61995117937661f, 159.4445412678832f));
pts.push_back(Point2f(72.22526316142418f, 154.60770776728293f));
pts.push_back(Point2f(64.97742405067658f, 152.3687174339018f));
pts.push_back(Point2f(58.34612797237003f, 155.61116802371583f));
pts.push_back(Point2f(55.59089117268539f, 148.56245696566418f));
pts.push_back(Point2f(45.22711195983706f, 145.6713241271927f));
pts.push_back(Point2f(40.090542298840234f, 142.36141304004002f));
pts.push_back(Point2f(31.788996807277414f, 136.26164877915585f));
pts.push_back(Point2f(27.27613006088805f, 137.46860042141503f));
pts.push_back(Point2f(23.972392188502226f, 129.17993872328594f));
pts.push_back(Point2f(20.688046711616977f, 121.52750840733087f));
pts.push_back(Point2f(14.635115184257643f, 115.36942800110485f));
pts.push_back(Point2f(14.850919318756809f, 109.43609786936987f));
pts.push_back(Point2f(7.476847697758103f, 102.67657265589285f));
pts.push_back(Point2f(1.8896944088091914f, 95.78878215565676f));
pts.push_back(Point2f(1.731997022935417f, 88.17674033990495f));
pts.push_back(Point2f(1.6780841363402033f, 80.65581939883002f));
pts.push_back(Point2f(0.035330281415411946f, 73.1088693846768f));
pts.push_back(Point2f(0.14652518786238033f, 65.42769523404296f));
pts.push_back(Point2f(6.99914645302843f, 58.436451064804245f));
pts.push_back(Point2f(6.719616410428614f, 50.15263031354927f));
pts.push_back(Point2f(5.122267598477748f, 46.03603214691343f));
float tol = 0.01f;
RotatedRect ellipseAMSTrue = cv::RotatedRect(Point2f(94.4037f, 84.743f), Size2f(190.614f, 153.543f), 19.832f);
RotatedRect ellipseAMSTest = fitEllipseAMS(pts);
EXPECT_TRUE(checkEllipse(ellipseAMSTest, ellipseAMSTrue, tol));
}
TEST(Imgproc_FitEllipseAMS_Issue_2, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(436.59985753246326f, 99.52113368023126f));
pts.push_back(Point2f(454.40214161915856f, 160.47565296546912f));
pts.push_back(Point2f(406.01996690372687f, 215.41999534561575f));
pts.push_back(Point2f(362.8738685722881f, 262.1842668997318f));
pts.push_back(Point2f(300.72864073265407f, 290.8182699272777f));
pts.push_back(Point2f(247.62963883830972f, 311.383137106776f));
pts.push_back(Point2f(194.15394659099445f, 313.30260991427565f));
pts.push_back(Point2f(138.934393338296f, 310.50203123324223f));
pts.push_back(Point2f(91.66999301197541f, 300.57303988670515f));
pts.push_back(Point2f(28.286233855826133f, 268.0670159317756f));
float tol = 0.01f;
RotatedRect ellipseAMSTrue = cv::RotatedRect(Point2f(223.917f, 169.701f), Size2f(456.628f, 277.809f), -12.6378f);
RotatedRect ellipseAMSTest = fitEllipseAMS(pts);
EXPECT_TRUE(checkEllipse(ellipseAMSTest, ellipseAMSTrue, tol));
}
TEST(Imgproc_FitEllipseAMS_Issue_3, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(459.59217920219083f, 480.1054989283611f));
pts.push_back(Point2f(427.2759071813645f, 501.82653857689616f));
pts.push_back(Point2f(388.35145730295574f, 520.9488690267101f));
pts.push_back(Point2f(349.53248668650656f, 522.9153107979839f));
pts.push_back(Point2f(309.56018996762094f, 527.449631776843f));
pts.push_back(Point2f(272.07480726768665f, 508.12367135706165f));
pts.push_back(Point2f(234.69230939247115f, 519.8943877180591f));
pts.push_back(Point2f(201.65185545142472f, 509.47870288702813f));
pts.push_back(Point2f(169.37222144138462f, 498.2681549419808f));
pts.push_back(Point2f(147.96233740677815f, 467.0923094529034f));
pts.push_back(Point2f(109.68331701139209f, 433.39069422941986f));
pts.push_back(Point2f(81.95454413977822f, 397.34325168750087f));
pts.push_back(Point2f(63.74923800767195f, 371.939105294963f));
pts.push_back(Point2f(39.966434417279885f, 329.9581349942296f));
pts.push_back(Point2f(21.581668415402532f, 292.6692716276865f));
pts.push_back(Point2f(13.687334926511767f, 248.91164234903772f));
pts.push_back(Point2f(0.0f, 201.25693715845716f));
pts.push_back(Point2f(3.90259455356599f, 155.68155247210575f));
pts.push_back(Point2f(39.683930802331844f, 110.26290871953987f));
pts.push_back(Point2f(47.85826684019932f, 70.82454140948524f));
float tol = 0.01f;
RotatedRect ellipseAMSTrue = cv::RotatedRect(Point2f(266.796f, 260.167f), Size2f(580.374f, 469.465f), 50.3961f);
RotatedRect ellipseAMSTest = fitEllipseAMS(pts);
EXPECT_TRUE(checkEllipse(ellipseAMSTest, ellipseAMSTrue, tol));
}
TEST(Imgproc_FitEllipseAMS_Issue_4, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(461.1761758124861f, 79.55196261616746f));
pts.push_back(Point2f(470.5034888757249f, 100.56760245239015f));
pts.push_back(Point2f(470.7814479849749f, 127.45783922150272f));
pts.push_back(Point2f(465.214384653262f, 157.51792078285405f));
pts.push_back(Point2f(465.3739691861813f, 185.89204350118942f));
pts.push_back(Point2f(443.36043162278366f, 214.43399982709002f));
pts.push_back(Point2f(435.04682693174095f, 239.2657073987589f));
pts.push_back(Point2f(444.48553588292697f, 262.0816619678671f));
pts.push_back(Point2f(407.1290185495328f, 285.07828783776347f));
pts.push_back(Point2f(397.71436554935804f, 304.782713567108f));
pts.push_back(Point2f(391.65678619785854f, 323.6809382153118f));
pts.push_back(Point2f(366.3904205781036f, 328.09416679736563f));
pts.push_back(Point2f(341.7656517790918f, 346.9672607008338f));
pts.push_back(Point2f(335.8021864809171f, 358.22416661090296f));
pts.push_back(Point2f(313.29224574204227f, 373.3267160317279f));
pts.push_back(Point2f(291.121216115417f, 377.3339312050791f));
pts.push_back(Point2f(284.20367595990547f, 389.5930108233698f));
pts.push_back(Point2f(270.9682061106809f, 388.4352006517971f));
pts.push_back(Point2f(253.10188273008825f, 392.35120876055373f));
pts.push_back(Point2f(234.2306946938868f, 407.0773705761117f));
pts.push_back(Point2f(217.0544384092144f, 407.54850609237235f));
pts.push_back(Point2f(198.40910966657933f, 423.7008860314684f));
pts.push_back(Point2f(175.47011114845057f, 420.4223434173364f));
pts.push_back(Point2f(154.92083551695902f, 418.5288198459268f));
pts.push_back(Point2f(136.52988517939698f, 417.8311217226818f));
pts.push_back(Point2f(114.74657291069317f, 410.1534699388714f));
pts.push_back(Point2f(78.9220388330042f, 397.6266608135022f));
pts.push_back(Point2f(76.82658673144391f, 404.27399269891055f));
pts.push_back(Point2f(50.953595435605116f, 386.3824077178053f));
pts.push_back(Point2f(43.603489077456985f, 368.7894972436907f));
pts.push_back(Point2f(19.37402592752713f, 343.3511017547511f));
pts.push_back(Point2f(8.714663367287343f, 322.2148323327599f));
pts.push_back(Point2f(0., 288.7836318007535f));
pts.push_back(Point2f(3.98686689837605f, 263.1748167870333f));
pts.push_back(Point2f(9.536389714519785f, 233.02995195684738f));
pts.push_back(Point2f(17.83246556512455f, 205.6536519851621f));
pts.push_back(Point2f(33.00593702846919f, 180.52628138608327f));
pts.push_back(Point2f(41.572400996463394f, 153.95185568689314f));
pts.push_back(Point2f(54.55733659450332f, 136.54322891729444f));
pts.push_back(Point2f(78.60990563833005f, 112.76538180538182f));
float tol = 0.01f;
RotatedRect ellipseAMSTrue = cv::RotatedRect(Point2f(237.108f, 207.32f), Size2f(517.287f, 357.591f), -36.3653f);
RotatedRect ellipseAMSTest = fitEllipseAMS(pts);
EXPECT_TRUE(checkEllipse(ellipseAMSTest, ellipseAMSTrue, tol));
}
TEST(Imgproc_FitEllipseAMS_Issue_5, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(509.60609444351917f, 484.8233016998119f));
pts.push_back(Point2f(508.55357451809846f, 498.61004779125176f));
pts.push_back(Point2f(495.59325478416525f, 507.9238702677585f));
pts.push_back(Point2f(455.32905012177747f, 517.7518674113691f));
pts.push_back(Point2f(461.24821761238667f, 524.2115477440211f));
pts.push_back(Point2f(438.8983455906825f, 528.424911702069f));
pts.push_back(Point2f(425.9259699875303f, 532.5700430134499f));
pts.push_back(Point2f(405.77496728300616f, 535.7295008444993f));
pts.push_back(Point2f(384.31968113982475f, 536.3076260371831f));
pts.push_back(Point2f(381.5356536818977f, 540.183355729414f));
pts.push_back(Point2f(378.2530503455792f, 540.2871855284832f));
pts.push_back(Point2f(357.7242088314752f, 543.473075733281f));
pts.push_back(Point2f(339.27871831324853f, 541.2099003613087f));
pts.push_back(Point2f(339.22481874867435f, 541.1105421426018f));
pts.push_back(Point2f(331.50337377509396f, 539.7296050163102f));
pts.push_back(Point2f(317.8306501537862f, 540.9077275195326f));
pts.push_back(Point2f(304.9192648323086f, 541.3434792768918f));
pts.push_back(Point2f(297.33855427908617f, 543.0590309600501f));
pts.push_back(Point2f(288.95330515997694f, 543.8756702506837f));
pts.push_back(Point2f(278.5850913122515f, 538.1343888329859f));
pts.push_back(Point2f(266.05355938101724f, 538.4115695907074f));
pts.push_back(Point2f(255.30186994366096f, 534.2459272411796f));
pts.push_back(Point2f(238.52054973466758f, 537.5007401480628f));
pts.push_back(Point2f(228.444463024996f, 533.8992361116678f));
pts.push_back(Point2f(217.8111623149833f, 538.2269193558991f));
pts.push_back(Point2f(209.43502138981037f, 532.8057062984569f));
pts.push_back(Point2f(193.33570716763276f, 527.2038128630041f));
pts.push_back(Point2f(172.66725340039625f, 526.4020881005537f));
pts.push_back(Point2f(158.33654199771337f, 525.2093856704676f));
pts.push_back(Point2f(148.65905485249067f, 521.0146762179431f));
pts.push_back(Point2f(147.6615365176719f, 517.4315201992808f));
pts.push_back(Point2f(122.43568509949394f, 514.2089723387337f));
pts.push_back(Point2f(110.88482982039073f, 509.14004840857046f));
pts.push_back(Point2f(107.10516681523065f, 502.49943180234266f));
pts.push_back(Point2f(82.66611013934804f, 494.0581153893113f));
pts.push_back(Point2f(63.573319848965966f, 485.6772487054385f));
pts.push_back(Point2f(47.65729058071245f, 475.4468806518075f));
pts.push_back(Point2f(19.96819458379347f, 463.98285210241943f));
pts.push_back(Point2f(27.855803175234342f, 450.2298664426336f));
pts.push_back(Point2f(12.832198085636549f, 435.6317753810441f));
float tol = 0.01f;
RotatedRect ellipseAMSTrue = cv::RotatedRect(Point2f(265.252f, 451.597f), Size2f(503.386f, 174.674f), 5.31814f);
RotatedRect ellipseAMSTest = fitEllipseAMS(pts);
EXPECT_TRUE(checkEllipse(ellipseAMSTest, ellipseAMSTrue, tol));
}
TEST(Imgproc_FitEllipseAMS_Issue_6, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(414.90156479295905f, 29.063453659930833f));
pts.push_back(Point2f(393.79576036337977f, 58.59512774879134f));
pts.push_back(Point2f(387.9100725249931f, 94.65067695657254f));
pts.push_back(Point2f(351.6987114318621f, 124.6049267560123f));
pts.push_back(Point2f(335.3270519942532f, 154.52182750730412f));
pts.push_back(Point2f(329.2955843262556f, 179.38031343427303f));
pts.push_back(Point2f(322.7316812937696f, 201.88774427737036f));
pts.push_back(Point2f(301.48326350826585f, 217.63331351026562f));
pts.push_back(Point2f(287.4603938315088f, 228.68790184154113f));
pts.push_back(Point2f(273.36617750656023f, 234.48397257849905f));
pts.push_back(Point2f(270.7787206270782f, 242.85279436204632f));
pts.push_back(Point2f(268.6973828073692f, 246.10891460870312f));
pts.push_back(Point2f(261.60715070464255f, 252.65744793902192f));
pts.push_back(Point2f(262.9041824871923f, 257.1813047575656f));
pts.push_back(Point2f(263.3210079177046f, 260.0532193246593f));
pts.push_back(Point2f(248.49568488533242f, 264.56723557175013f));
pts.push_back(Point2f(245.4134174127509f, 264.87259401292f));
pts.push_back(Point2f(244.73208618171216f, 272.32307359830884f));
pts.push_back(Point2f(232.82093196087555f, 272.0239734764616f));
pts.push_back(Point2f(235.28539413113458f, 276.8668447478244f));
pts.push_back(Point2f(231.9766571511147f, 277.71179872893083f));
pts.push_back(Point2f(227.23880706209866f, 284.5588878789101f));
pts.push_back(Point2f(222.53202223537826f, 282.2293154479012f));
pts.push_back(Point2f(217.27525654729595f, 297.42961148365725f));
pts.push_back(Point2f(212.19490057230672f, 294.5344078014253f));
pts.push_back(Point2f(207.47417472945446f, 301.72230412668307f));
pts.push_back(Point2f(202.11143229969164f, 298.8588627545512f));
pts.push_back(Point2f(196.62967096845824f, 309.39738607353223f));
pts.push_back(Point2f(190.37809841992106f, 318.3250479151242f));
pts.push_back(Point2f(183.1296129732803f, 322.35242231955453f));
pts.push_back(Point2f(171.58530535265993f, 330.4981441404153f));
pts.push_back(Point2f(160.40092880652247f, 337.47275990208226f));
pts.push_back(Point2f(149.44888762618092f, 343.42296086656717f));
pts.push_back(Point2f(139.7923528305302f, 353.4821948045352f));
pts.push_back(Point2f(121.08414969113318f, 359.7010225709457f));
pts.push_back(Point2f(100.10629739219641f, 375.3155744055458f));
pts.push_back(Point2f(78.15715630786733f, 389.0311284319413f));
pts.push_back(Point2f(51.22820988075294f, 396.98646504159547f));
pts.push_back(Point2f(30.71132492338431f, 402.85098740402844f));
pts.push_back(Point2f(10.994737323179852f, 394.6764602972333f));
float tol = 0.01f;
RotatedRect ellipseAMSTrue = cv::RotatedRect(Point2f(192.467f, 204.404f), Size2f(551.397f, 165.068f), 136.913f);
RotatedRect ellipseAMSTest = fitEllipseAMS(pts);
EXPECT_TRUE(checkEllipse(ellipseAMSTest, ellipseAMSTrue, tol));
}
TEST(Imgproc_FitEllipseAMS_Issue_7, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(386.7497806918209f, 119.55623710363142f));
pts.push_back(Point2f(399.0712613744503f, 132.61095972401034f));
pts.push_back(Point2f(400.3582576852657f, 146.71942033652573f));
pts.push_back(Point2f(383.31046706707906f, 160.13631428164982f));
pts.push_back(Point2f(387.1626582455823f, 173.82700569763574f));
pts.push_back(Point2f(378.88843308401425f, 186.10333319745317f));
pts.push_back(Point2f(367.55061701208f, 201.41492900400164f));
pts.push_back(Point2f(360.3254967185148f, 209.03834085076022f));
pts.push_back(Point2f(346.2645164278429f, 222.03214282040395f));
pts.push_back(Point2f(342.3483403634167f, 230.58290419787073f));
pts.push_back(Point2f(326.2900969991908f, 240.23679566682756f));
pts.push_back(Point2f(324.5622396580625f, 249.56961396707823f));
pts.push_back(Point2f(304.23417130914095f, 259.6693711280021f));
pts.push_back(Point2f(295.54035697534675f, 270.82284542557704f));
pts.push_back(Point2f(291.7403057147348f, 276.1536825048371f));
pts.push_back(Point2f(269.19344116558665f, 287.1705579044651f));
pts.push_back(Point2f(256.5350613899267f, 274.91264707500943f));
pts.push_back(Point2f(245.93644351417183f, 286.12398028743064f));
pts.push_back(Point2f(232.40892420943732f, 282.73986583867065f));
pts.push_back(Point2f(216.17957969101082f, 293.22229708237705f));
pts.push_back(Point2f(205.66843722622573f, 295.7032575625158f));
pts.push_back(Point2f(192.219969335765f, 302.6968969534755f));
pts.push_back(Point2f(178.37758801730416f, 295.56656776633287f));
pts.push_back(Point2f(167.60089103756644f, 301.4629292267722f));
pts.push_back(Point2f(157.44802813915317f, 298.90830855734504f));
pts.push_back(Point2f(138.44311818820313f, 293.951927187897f));
pts.push_back(Point2f(128.92747660038592f, 291.4122695492978f));
pts.push_back(Point2f(119.75160909865994f, 282.5809454721714f));
pts.push_back(Point2f(98.48443737042328f, 290.39938776333247f));
pts.push_back(Point2f(88.05275635126131f, 280.11156058895745f));
pts.push_back(Point2f(82.45799026448167f, 271.46668468419773f));
pts.push_back(Point2f(68.04031962064084f, 267.8136468580707f));
pts.push_back(Point2f(58.99967170878713f, 263.8859310392943f));
pts.push_back(Point2f(41.256097220823484f, 260.6041605773932f));
pts.push_back(Point2f(40.66198797608645f, 246.64973068177196f));
pts.push_back(Point2f(31.085484380646008f, 239.28615601336074f));
pts.push_back(Point2f(24.069417111444253f, 225.2228746297288f));
pts.push_back(Point2f(22.10122953275156f, 212.75509683149195f));
pts.push_back(Point2f(9.929991244497518f, 203.20662088477752f));
pts.push_back(Point2f(0.0f, 190.04891498441148f));
float tol = 0.01f;
RotatedRect ellipseAMSTrue = cv::RotatedRect(Point2f(197.292f, 134.64f), Size2f(401.092f, 320.051f), 165.429f);
RotatedRect ellipseAMSTest = fitEllipseAMS(pts);
EXPECT_TRUE(checkEllipse(ellipseAMSTest, ellipseAMSTrue, tol));
}
TEST(Imgproc_FitEllipseAMS_NearCircular, accuracy)
{
std::vector<cv::Point2f> points;
double cx = 27.0, cy = 27.0, a = 17.0, b = 16.5;
for (int i = 0; i < 360; i++) {
double theta = 2.0 * CV_PI * i / 360.0;
points.push_back(cv::Point2f(
(float)(cx + a * cos(theta)),
(float)(cy + b * sin(theta))));
}
cv::RotatedRect ams = cv::fitEllipseAMS(points);
// AMS should produce a valid result close to ground truth
EXPECT_NEAR(ams.center.x, 27.0, 0.5);
EXPECT_NEAR(ams.center.y, 27.0, 0.5);
EXPECT_NEAR(std::max(ams.size.width, ams.size.height), 34.0, 1.0);
EXPECT_NEAR(std::min(ams.size.width, ams.size.height), 33.0, 1.0);
}
TEST(Imgproc_FitEllipseAMS_HorizontalLine, accuracy) {
vector<Point2f> pts({{-300, 100}, {-200, 100}, {-100, 100}, {0, 100}, {100, 100}, {200, 100}, {300, 100}});
const RotatedRect el = fitEllipseAMS(pts);
EXPECT_NEAR(el.center.x, 0, 200);
EXPECT_NEAR(el.center.y, 100, 1);
EXPECT_NEAR(el.size.width, 1, 1);
EXPECT_NEAR(el.size.height, 600, 100);
EXPECT_NEAR(el.angle, 90, 0.1);
}
}} // namespace
@@ -0,0 +1,373 @@
// 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.
//
// Copyright (C) 2016, Itseez, Inc, all rights reserved.
#include "test_precomp.hpp"
namespace opencv_test { namespace {
static bool checkEllipse(const RotatedRect& ellipseDirectTest, const RotatedRect& ellipseDirectTrue, const float tol) {
Point2f ellipseDirectTrueVertices[4];
Point2f ellipseDirectTestVertices[4];
ellipseDirectTest.points(ellipseDirectTestVertices);
ellipseDirectTrue.points(ellipseDirectTrueVertices);
float directDiff = 0.0f;
for (size_t i=0; i <=3; i++) {
Point2f diff = ellipseDirectTrueVertices[i] - ellipseDirectTestVertices[0];
float d = diff.x * diff.x + diff.y * diff.y;
for (size_t j=1; j <=3; j++) {
diff = ellipseDirectTrueVertices[i] - ellipseDirectTestVertices[j];
float dd = diff.x * diff.x + diff.y * diff.y;
if(dd<d){d=dd;}
}
directDiff += std::sqrt(d);
}
return directDiff < tol;
}
TEST(Imgproc_FitEllipseDirect_Issue_1, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(173.41854895999165f, 125.84473135880411f));
pts.push_back(Point2f(180.63769498640912f, 130.960006577589f));
pts.push_back(Point2f(174.99173759130173f, 137.34265632926764f));
pts.push_back(Point2f(170.9044645313217f, 141.68017556480243f));
pts.push_back(Point2f(163.48965388499656f, 141.9404438924043f));
pts.push_back(Point2f(159.37687818401147f, 148.60835331594876f));
pts.push_back(Point2f(150.38917629356735f, 155.68825577720446f));
pts.push_back(Point2f(147.16319653316862f, 157.06039984963923f));
pts.push_back(Point2f(141.73118707843207f, 157.2570155198414f));
pts.push_back(Point2f(130.61569602948597f, 159.40742182929364f));
pts.push_back(Point2f(127.00573042229027f, 161.34430232187867f));
pts.push_back(Point2f(120.49383815053747f, 163.72610883128334f));
pts.push_back(Point2f(114.62383760040998f, 162.6788666385239f));
pts.push_back(Point2f(108.84871269183333f, 161.90597054388132f));
pts.push_back(Point2f(103.04574087829076f, 167.44352944383985f));
pts.push_back(Point2f(96.31623870161255f, 163.71641295746116f));
pts.push_back(Point2f(89.86174417295126f, 157.2967811253635f));
pts.push_back(Point2f(84.27940674801192f, 168.6331304010667f));
pts.push_back(Point2f(76.61995117937661f, 159.4445412678832f));
pts.push_back(Point2f(72.22526316142418f, 154.60770776728293f));
pts.push_back(Point2f(64.97742405067658f, 152.3687174339018f));
pts.push_back(Point2f(58.34612797237003f, 155.61116802371583f));
pts.push_back(Point2f(55.59089117268539f, 148.56245696566418f));
pts.push_back(Point2f(45.22711195983706f, 145.6713241271927f));
pts.push_back(Point2f(40.090542298840234f, 142.36141304004002f));
pts.push_back(Point2f(31.788996807277414f, 136.26164877915585f));
pts.push_back(Point2f(27.27613006088805f, 137.46860042141503f));
pts.push_back(Point2f(23.972392188502226f, 129.17993872328594f));
pts.push_back(Point2f(20.688046711616977f, 121.52750840733087f));
pts.push_back(Point2f(14.635115184257643f, 115.36942800110485f));
pts.push_back(Point2f(14.850919318756809f, 109.43609786936987f));
pts.push_back(Point2f(7.476847697758103f, 102.67657265589285f));
pts.push_back(Point2f(1.8896944088091914f, 95.78878215565676f));
pts.push_back(Point2f(1.731997022935417f, 88.17674033990495f));
pts.push_back(Point2f(1.6780841363402033f, 80.65581939883002f));
pts.push_back(Point2f(0.035330281415411946f, 73.1088693846768f));
pts.push_back(Point2f(0.14652518786238033f, 65.42769523404296f));
pts.push_back(Point2f(6.99914645302843f, 58.436451064804245f));
pts.push_back(Point2f(6.719616410428614f, 50.15263031354927f));
pts.push_back(Point2f(5.122267598477748f, 46.03603214691343f));
float tol = 0.01f;
RotatedRect ellipseDirectTrue = cv::RotatedRect(Point2f(91.3256f, 90.4668f),Size2f(187.211f, 140.031f), 21.5808f);
RotatedRect ellipseDirectTest = fitEllipseDirect(pts);
EXPECT_TRUE(checkEllipse(ellipseDirectTest, ellipseDirectTrue, tol));
}
TEST(Imgproc_FitEllipseDirect_Issue_2, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(436.59985753246326f, 99.52113368023126f));
pts.push_back(Point2f(454.40214161915856f, 160.47565296546912f));
pts.push_back(Point2f(406.01996690372687f, 215.41999534561575f));
pts.push_back(Point2f(362.8738685722881f, 262.1842668997318f));
pts.push_back(Point2f(300.72864073265407f, 290.8182699272777f));
pts.push_back(Point2f(247.62963883830972f, 311.383137106776f));
pts.push_back(Point2f(194.15394659099445f, 313.30260991427565f));
pts.push_back(Point2f(138.934393338296f, 310.50203123324223f));
pts.push_back(Point2f(91.66999301197541f, 300.57303988670515f));
pts.push_back(Point2f(28.286233855826133f, 268.0670159317756f));
float tol = 0.01f;
RotatedRect ellipseDirectTrue = cv::RotatedRect(Point2f(228.232f, 174.879f),Size2f(450.68f, 265.556f), 166.181f);
RotatedRect ellipseDirectTest = fitEllipseDirect(pts);
EXPECT_TRUE(checkEllipse(ellipseDirectTest, ellipseDirectTrue, tol));
}
TEST(Imgproc_FitEllipseDirect_Issue_3, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(459.59217920219083f, 480.1054989283611f));
pts.push_back(Point2f(427.2759071813645f, 501.82653857689616f));
pts.push_back(Point2f(388.35145730295574f, 520.9488690267101f));
pts.push_back(Point2f(349.53248668650656f, 522.9153107979839f));
pts.push_back(Point2f(309.56018996762094f, 527.449631776843f));
pts.push_back(Point2f(272.07480726768665f, 508.12367135706165f));
pts.push_back(Point2f(234.69230939247115f, 519.8943877180591f));
pts.push_back(Point2f(201.65185545142472f, 509.47870288702813f));
pts.push_back(Point2f(169.37222144138462f, 498.2681549419808f));
pts.push_back(Point2f(147.96233740677815f, 467.0923094529034f));
pts.push_back(Point2f(109.68331701139209f, 433.39069422941986f));
pts.push_back(Point2f(81.95454413977822f, 397.34325168750087f));
pts.push_back(Point2f(63.74923800767195f, 371.939105294963f));
pts.push_back(Point2f(39.966434417279885f, 329.9581349942296f));
pts.push_back(Point2f(21.581668415402532f, 292.6692716276865f));
pts.push_back(Point2f(13.687334926511767f, 248.91164234903772f));
pts.push_back(Point2f(0.0f, 201.25693715845716f));
pts.push_back(Point2f(3.90259455356599f, 155.68155247210575f));
pts.push_back(Point2f(39.683930802331844f, 110.26290871953987f));
pts.push_back(Point2f(47.85826684019932f, 70.82454140948524f));
float tol = 0.01f;
RotatedRect ellipseDirectTrue = cv::RotatedRect(Point2f(255.326f, 272.626f),Size2f(570.999f, 434.23f), 49.0265f);
RotatedRect ellipseDirectTest = fitEllipseDirect(pts);
EXPECT_TRUE(checkEllipse(ellipseDirectTest, ellipseDirectTrue, tol));
}
TEST(Imgproc_FitEllipseDirect_Issue_4, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(461.1761758124861f, 79.55196261616746f));
pts.push_back(Point2f(470.5034888757249f, 100.56760245239015f));
pts.push_back(Point2f(470.7814479849749f, 127.45783922150272f));
pts.push_back(Point2f(465.214384653262f, 157.51792078285405f));
pts.push_back(Point2f(465.3739691861813f, 185.89204350118942f));
pts.push_back(Point2f(443.36043162278366f, 214.43399982709002f));
pts.push_back(Point2f(435.04682693174095f, 239.2657073987589f));
pts.push_back(Point2f(444.48553588292697f, 262.0816619678671f));
pts.push_back(Point2f(407.1290185495328f, 285.07828783776347f));
pts.push_back(Point2f(397.71436554935804f, 304.782713567108f));
pts.push_back(Point2f(391.65678619785854f, 323.6809382153118f));
pts.push_back(Point2f(366.3904205781036f, 328.09416679736563f));
pts.push_back(Point2f(341.7656517790918f, 346.9672607008338f));
pts.push_back(Point2f(335.8021864809171f, 358.22416661090296f));
pts.push_back(Point2f(313.29224574204227f, 373.3267160317279f));
pts.push_back(Point2f(291.121216115417f, 377.3339312050791f));
pts.push_back(Point2f(284.20367595990547f, 389.5930108233698f));
pts.push_back(Point2f(270.9682061106809f, 388.4352006517971f));
pts.push_back(Point2f(253.10188273008825f, 392.35120876055373f));
pts.push_back(Point2f(234.2306946938868f, 407.0773705761117f));
pts.push_back(Point2f(217.0544384092144f, 407.54850609237235f));
pts.push_back(Point2f(198.40910966657933f, 423.7008860314684f));
pts.push_back(Point2f(175.47011114845057f, 420.4223434173364f));
pts.push_back(Point2f(154.92083551695902f, 418.5288198459268f));
pts.push_back(Point2f(136.52988517939698f, 417.8311217226818f));
pts.push_back(Point2f(114.74657291069317f, 410.1534699388714f));
pts.push_back(Point2f(78.9220388330042f, 397.6266608135022f));
pts.push_back(Point2f(76.82658673144391f, 404.27399269891055f));
pts.push_back(Point2f(50.953595435605116f, 386.3824077178053f));
pts.push_back(Point2f(43.603489077456985f, 368.7894972436907f));
pts.push_back(Point2f(19.37402592752713f, 343.3511017547511f));
pts.push_back(Point2f(8.714663367287343f, 322.2148323327599f));
pts.push_back(Point2f(0., 288.7836318007535f));
pts.push_back(Point2f(3.98686689837605f, 263.1748167870333f));
pts.push_back(Point2f(9.536389714519785f, 233.02995195684738f));
pts.push_back(Point2f(17.83246556512455f, 205.6536519851621f));
pts.push_back(Point2f(33.00593702846919f, 180.52628138608327f));
pts.push_back(Point2f(41.572400996463394f, 153.95185568689314f));
pts.push_back(Point2f(54.55733659450332f, 136.54322891729444f));
pts.push_back(Point2f(78.60990563833005f, 112.76538180538182f));
float tol = 0.01f;
RotatedRect ellipseDirectTrue = cv::RotatedRect(Point2f(236.836f, 208.089f),Size2f(515.893f, 357.166f), -35.9996f);
RotatedRect ellipseDirectTest = fitEllipseDirect(pts);
EXPECT_TRUE(checkEllipse(ellipseDirectTest, ellipseDirectTrue, tol));
}
TEST(Imgproc_FitEllipseDirect_Issue_5, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(509.60609444351917f, 484.8233016998119f));
pts.push_back(Point2f(508.55357451809846f, 498.61004779125176f));
pts.push_back(Point2f(495.59325478416525f, 507.9238702677585f));
pts.push_back(Point2f(455.32905012177747f, 517.7518674113691f));
pts.push_back(Point2f(461.24821761238667f, 524.2115477440211f));
pts.push_back(Point2f(438.8983455906825f, 528.424911702069f));
pts.push_back(Point2f(425.9259699875303f, 532.5700430134499f));
pts.push_back(Point2f(405.77496728300616f, 535.7295008444993f));
pts.push_back(Point2f(384.31968113982475f, 536.3076260371831f));
pts.push_back(Point2f(381.5356536818977f, 540.183355729414f));
pts.push_back(Point2f(378.2530503455792f, 540.2871855284832f));
pts.push_back(Point2f(357.7242088314752f, 543.473075733281f));
pts.push_back(Point2f(339.27871831324853f, 541.2099003613087f));
pts.push_back(Point2f(339.22481874867435f, 541.1105421426018f));
pts.push_back(Point2f(331.50337377509396f, 539.7296050163102f));
pts.push_back(Point2f(317.8306501537862f, 540.9077275195326f));
pts.push_back(Point2f(304.9192648323086f, 541.3434792768918f));
pts.push_back(Point2f(297.33855427908617f, 543.0590309600501f));
pts.push_back(Point2f(288.95330515997694f, 543.8756702506837f));
pts.push_back(Point2f(278.5850913122515f, 538.1343888329859f));
pts.push_back(Point2f(266.05355938101724f, 538.4115695907074f));
pts.push_back(Point2f(255.30186994366096f, 534.2459272411796f));
pts.push_back(Point2f(238.52054973466758f, 537.5007401480628f));
pts.push_back(Point2f(228.444463024996f, 533.8992361116678f));
pts.push_back(Point2f(217.8111623149833f, 538.2269193558991f));
pts.push_back(Point2f(209.43502138981037f, 532.8057062984569f));
pts.push_back(Point2f(193.33570716763276f, 527.2038128630041f));
pts.push_back(Point2f(172.66725340039625f, 526.4020881005537f));
pts.push_back(Point2f(158.33654199771337f, 525.2093856704676f));
pts.push_back(Point2f(148.65905485249067f, 521.0146762179431f));
pts.push_back(Point2f(147.6615365176719f, 517.4315201992808f));
pts.push_back(Point2f(122.43568509949394f, 514.2089723387337f));
pts.push_back(Point2f(110.88482982039073f, 509.14004840857046f));
pts.push_back(Point2f(107.10516681523065f, 502.49943180234266f));
pts.push_back(Point2f(82.66611013934804f, 494.0581153893113f));
pts.push_back(Point2f(63.573319848965966f, 485.6772487054385f));
pts.push_back(Point2f(47.65729058071245f, 475.4468806518075f));
pts.push_back(Point2f(19.96819458379347f, 463.98285210241943f));
pts.push_back(Point2f(27.855803175234342f, 450.2298664426336f));
pts.push_back(Point2f(12.832198085636549f, 435.6317753810441f));
float tol = 0.01f;
RotatedRect ellipseDirectTrue = cv::RotatedRect(Point2f(264.354f, 457.336f),Size2f(493.728f, 162.9f), 5.36186f);
RotatedRect ellipseDirectTest = fitEllipseDirect(pts);
EXPECT_TRUE(checkEllipse(ellipseDirectTest, ellipseDirectTrue, tol));
}
TEST(Imgproc_FitEllipseDirect_Issue_6, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(414.90156479295905f, 29.063453659930833f));
pts.push_back(Point2f(393.79576036337977f, 58.59512774879134f));
pts.push_back(Point2f(387.9100725249931f, 94.65067695657254f));
pts.push_back(Point2f(351.6987114318621f, 124.6049267560123f));
pts.push_back(Point2f(335.3270519942532f, 154.52182750730412f));
pts.push_back(Point2f(329.2955843262556f, 179.38031343427303f));
pts.push_back(Point2f(322.7316812937696f, 201.88774427737036f));
pts.push_back(Point2f(301.48326350826585f, 217.63331351026562f));
pts.push_back(Point2f(287.4603938315088f, 228.68790184154113f));
pts.push_back(Point2f(273.36617750656023f, 234.48397257849905f));
pts.push_back(Point2f(270.7787206270782f, 242.85279436204632f));
pts.push_back(Point2f(268.6973828073692f, 246.10891460870312f));
pts.push_back(Point2f(261.60715070464255f, 252.65744793902192f));
pts.push_back(Point2f(262.9041824871923f, 257.1813047575656f));
pts.push_back(Point2f(263.3210079177046f, 260.0532193246593f));
pts.push_back(Point2f(248.49568488533242f, 264.56723557175013f));
pts.push_back(Point2f(245.4134174127509f, 264.87259401292f));
pts.push_back(Point2f(244.73208618171216f, 272.32307359830884f));
pts.push_back(Point2f(232.82093196087555f, 272.0239734764616f));
pts.push_back(Point2f(235.28539413113458f, 276.8668447478244f));
pts.push_back(Point2f(231.9766571511147f, 277.71179872893083f));
pts.push_back(Point2f(227.23880706209866f, 284.5588878789101f));
pts.push_back(Point2f(222.53202223537826f, 282.2293154479012f));
pts.push_back(Point2f(217.27525654729595f, 297.42961148365725f));
pts.push_back(Point2f(212.19490057230672f, 294.5344078014253f));
pts.push_back(Point2f(207.47417472945446f, 301.72230412668307f));
pts.push_back(Point2f(202.11143229969164f, 298.8588627545512f));
pts.push_back(Point2f(196.62967096845824f, 309.39738607353223f));
pts.push_back(Point2f(190.37809841992106f, 318.3250479151242f));
pts.push_back(Point2f(183.1296129732803f, 322.35242231955453f));
pts.push_back(Point2f(171.58530535265993f, 330.4981441404153f));
pts.push_back(Point2f(160.40092880652247f, 337.47275990208226f));
pts.push_back(Point2f(149.44888762618092f, 343.42296086656717f));
pts.push_back(Point2f(139.7923528305302f, 353.4821948045352f));
pts.push_back(Point2f(121.08414969113318f, 359.7010225709457f));
pts.push_back(Point2f(100.10629739219641f, 375.3155744055458f));
pts.push_back(Point2f(78.15715630786733f, 389.0311284319413f));
pts.push_back(Point2f(51.22820988075294f, 396.98646504159547f));
pts.push_back(Point2f(30.71132492338431f, 402.85098740402844f));
pts.push_back(Point2f(10.994737323179852f, 394.6764602972333f));
float tol = 0.01f;
RotatedRect ellipseDirectTrue = cv::RotatedRect(Point2f(207.145f, 223.308f),Size2f(499.583f, 117.473f), -42.6851f);
RotatedRect ellipseDirectTest = fitEllipseDirect(pts);
EXPECT_TRUE(checkEllipse(ellipseDirectTest, ellipseDirectTrue, tol));
}
TEST(Imgproc_FitEllipseDirect_Issue_7, accuracy) {
vector<Point2f>pts;
pts.push_back(Point2f(386.7497806918209f, 119.55623710363142f));
pts.push_back(Point2f(399.0712613744503f, 132.61095972401034f));
pts.push_back(Point2f(400.3582576852657f, 146.71942033652573f));
pts.push_back(Point2f(383.31046706707906f, 160.13631428164982f));
pts.push_back(Point2f(387.1626582455823f, 173.82700569763574f));
pts.push_back(Point2f(378.88843308401425f, 186.10333319745317f));
pts.push_back(Point2f(367.55061701208f, 201.41492900400164f));
pts.push_back(Point2f(360.3254967185148f, 209.03834085076022f));
pts.push_back(Point2f(346.2645164278429f, 222.03214282040395f));
pts.push_back(Point2f(342.3483403634167f, 230.58290419787073f));
pts.push_back(Point2f(326.2900969991908f, 240.23679566682756f));
pts.push_back(Point2f(324.5622396580625f, 249.56961396707823f));
pts.push_back(Point2f(304.23417130914095f, 259.6693711280021f));
pts.push_back(Point2f(295.54035697534675f, 270.82284542557704f));
pts.push_back(Point2f(291.7403057147348f, 276.1536825048371f));
pts.push_back(Point2f(269.19344116558665f, 287.1705579044651f));
pts.push_back(Point2f(256.5350613899267f, 274.91264707500943f));
pts.push_back(Point2f(245.93644351417183f, 286.12398028743064f));
pts.push_back(Point2f(232.40892420943732f, 282.73986583867065f));
pts.push_back(Point2f(216.17957969101082f, 293.22229708237705f));
pts.push_back(Point2f(205.66843722622573f, 295.7032575625158f));
pts.push_back(Point2f(192.219969335765f, 302.6968969534755f));
pts.push_back(Point2f(178.37758801730416f, 295.56656776633287f));
pts.push_back(Point2f(167.60089103756644f, 301.4629292267722f));
pts.push_back(Point2f(157.44802813915317f, 298.90830855734504f));
pts.push_back(Point2f(138.44311818820313f, 293.951927187897f));
pts.push_back(Point2f(128.92747660038592f, 291.4122695492978f));
pts.push_back(Point2f(119.75160909865994f, 282.5809454721714f));
pts.push_back(Point2f(98.48443737042328f, 290.39938776333247f));
pts.push_back(Point2f(88.05275635126131f, 280.11156058895745f));
pts.push_back(Point2f(82.45799026448167f, 271.46668468419773f));
pts.push_back(Point2f(68.04031962064084f, 267.8136468580707f));
pts.push_back(Point2f(58.99967170878713f, 263.8859310392943f));
pts.push_back(Point2f(41.256097220823484f, 260.6041605773932f));
pts.push_back(Point2f(40.66198797608645f, 246.64973068177196f));
pts.push_back(Point2f(31.085484380646008f, 239.28615601336074f));
pts.push_back(Point2f(24.069417111444253f, 225.2228746297288f));
pts.push_back(Point2f(22.10122953275156f, 212.75509683149195f));
pts.push_back(Point2f(9.929991244497518f, 203.20662088477752f));
pts.push_back(Point2f(0.0f, 190.04891498441148f));
float tol = 0.01f;
RotatedRect ellipseDirectTrue = cv::RotatedRect(Point2f(199.463f, 150.997f),Size2f(390.341f, 286.01f), -12.9696f);
RotatedRect ellipseDirectTest = fitEllipseDirect(pts);
EXPECT_TRUE(checkEllipse(ellipseDirectTest, ellipseDirectTrue, tol));
}
TEST(Imgproc_FitEllipseDirect_NearCircular, accuracy)
{
// 360 points on a near-circular ellipse (a=17, b=16.5)
// This data previously triggered unnecessary fallback to fitEllipseNoDirect
std::vector<cv::Point2f> points;
double cx = 27.0, cy = 27.0, a = 17.0, b = 16.5;
for (int i = 0; i < 360; i++) {
double theta = 2.0 * CV_PI * i / 360.0;
points.push_back(cv::Point2f(
(float)(cx + a * cos(theta)),
(float)(cy + b * sin(theta))));
}
cv::RotatedRect direct = cv::fitEllipseDirect(points);
// Direct should produce a valid result close to ground truth
EXPECT_NEAR(direct.center.x, 27.0, 0.1);
EXPECT_NEAR(direct.center.y, 27.0, 0.1);
EXPECT_NEAR(std::max(direct.size.width, direct.size.height), 34.0, 0.5);
EXPECT_NEAR(std::min(direct.size.width, direct.size.height), 33.0, 0.5);
}
TEST(Imgproc_FitEllipseDirect_HorizontalLine, accuracy) {
vector<Point2f> pts({{-300, 100}, {-200, 100}, {-100, 100}, {0, 100}, {100, 100}, {200, 100}, {300, 100}});
const RotatedRect el = fitEllipseDirect(pts);
EXPECT_NEAR(el.center.x, 0, 100);
EXPECT_NEAR(el.center.y, 100, 1);
EXPECT_NEAR(el.size.width, 2, 2);
EXPECT_NEAR(el.size.height, 600, 100);
EXPECT_NEAR(el.angle, 90, 0.1);
}
}} // namespace
+432
View File
@@ -0,0 +1,432 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// 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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, 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:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of Intel Corporation may not 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 the Intel Corporation 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 "test_precomp.hpp"
namespace opencv_test {
static void test_convertHomogeneous( const Mat& _src, Mat& _dst )
{
Mat src = _src, dst = _dst;
int i, count, sdims, ddims;
int sstep1, sstep2, dstep1, dstep2;
if( src.depth() != CV_64F )
_src.convertTo(src, CV_64F);
if( dst.depth() != CV_64F )
dst.create(dst.size(), CV_MAKETYPE(CV_64F, _dst.channels()));
if( src.rows > src.cols )
{
count = src.rows;
sdims = src.channels()*src.cols;
sstep1 = (int)(src.step/sizeof(double));
sstep2 = 1;
}
else
{
count = src.cols;
sdims = src.channels()*src.rows;
if( src.rows == 1 )
{
sstep1 = sdims;
sstep2 = 1;
}
else
{
sstep1 = 1;
sstep2 = (int)(src.step/sizeof(double));
}
}
if( dst.rows > dst.cols )
{
CV_Assert( count == dst.rows );
ddims = dst.channels()*dst.cols;
dstep1 = (int)(dst.step/sizeof(double));
dstep2 = 1;
}
else
{
CV_Assert( count == dst.cols );
ddims = dst.channels()*dst.rows;
if( dst.rows == 1 )
{
dstep1 = ddims;
dstep2 = 1;
}
else
{
dstep1 = 1;
dstep2 = (int)(dst.step/sizeof(double));
}
}
double* s = src.ptr<double>();
double* d = dst.ptr<double>();
if( sdims <= ddims )
{
int wstep = dstep2*(ddims - 1);
for( i = 0; i < count; i++, s += sstep1, d += dstep1 )
{
double x = s[0];
double y = s[sstep2];
d[wstep] = 1;
d[0] = x;
d[dstep2] = y;
if( sdims >= 3 )
{
d[dstep2*2] = s[sstep2*2];
if( sdims == 4 )
d[dstep2*3] = s[sstep2*3];
}
}
}
else
{
int wstep = sstep2*(sdims - 1);
for( i = 0; i < count; i++, s += sstep1, d += dstep1 )
{
double w = s[wstep];
double x = s[0];
double y = s[sstep2];
w = w ? 1./w : 1;
d[0] = x*w;
d[dstep2] = y*w;
if( ddims == 3 )
d[dstep2*2] = s[sstep2*2]*w;
}
}
if( dst.data != _dst.data )
dst.convertTo(_dst, _dst.depth());
}
namespace {
/********************************** convert homogeneous *********************************/
class CV_ConvertHomogeneousTest : public cvtest::ArrayTest
{
public:
CV_ConvertHomogeneousTest();
protected:
int read_params( const cv::FileStorage& fs );
void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types );
void fill_array( int test_case_idx, int i, int j, Mat& arr );
double get_success_error_level( int test_case_idx, int i, int j );
void run_func();
void prepare_to_validation( int );
int dims1, dims2;
int pt_count;
};
CV_ConvertHomogeneousTest::CV_ConvertHomogeneousTest()
{
test_array[INPUT].push_back(NULL);
test_array[OUTPUT].push_back(NULL);
test_array[REF_OUTPUT].push_back(NULL);
element_wise_relative_error = false;
pt_count = dims1 = dims2 = 0;
}
int CV_ConvertHomogeneousTest::read_params( const cv::FileStorage& fs )
{
int code = cvtest::ArrayTest::read_params( fs );
return code;
}
void CV_ConvertHomogeneousTest::get_test_array_types_and_sizes( int /*test_case_idx*/,
vector<vector<Size> >& sizes, vector<vector<int> >& types )
{
RNG& rng = ts->get_rng();
int pt_depth1 = cvtest::randInt(rng) % 2 == 0 ? CV_32F : CV_64F;
int pt_depth2 = pt_depth1;//cvtest::randInt(rng) % 2 == 0 ? CV_32F : CV_64F;
double pt_count_exp = cvtest::randReal(rng)*6 + 1;
int t;
pt_count = cvRound(exp(pt_count_exp));
pt_count = MAX( pt_count, 5 );
dims1 = 2 + (cvtest::randInt(rng) % 2);
dims2 = dims1 + 1;
if( cvtest::randInt(rng) % 2 )
CV_SWAP( dims1, dims2, t );
types[INPUT][0] = CV_MAKETYPE(pt_depth1, 1);
sizes[INPUT][0] = Size(dims1, pt_count);
if( cvtest::randInt(rng) % 2 )
{
types[INPUT][0] = CV_MAKETYPE(pt_depth1, dims1);
if( cvtest::randInt(rng) % 2 )
sizes[INPUT][0] = Size(pt_count, 1);
else
sizes[INPUT][0] = Size(1, pt_count);
}
types[OUTPUT][0] = CV_MAKETYPE(pt_depth2, dims2);
sizes[OUTPUT][0] = Size(1, pt_count);
types[REF_OUTPUT][0] = types[OUTPUT][0];
sizes[REF_OUTPUT][0] = sizes[OUTPUT][0];
}
double CV_ConvertHomogeneousTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ )
{
return 1e-5;
}
void CV_ConvertHomogeneousTest::fill_array( int /*test_case_idx*/, int /*i*/, int /*j*/, Mat& arr )
{
Mat temp( 1, pt_count, CV_MAKETYPE(CV_64FC1,dims1) );
RNG& rng = ts->get_rng();
Scalar low = Scalar::all(0), high = Scalar::all(10);
if( dims1 > dims2 )
low.val[dims1-1] = 1.;
cvtest::randUni( rng, temp, low, high );
test_convertHomogeneous( temp, arr );
}
void CV_ConvertHomogeneousTest::run_func()
{
cv::Mat _input = test_mat[INPUT][0], &_output = test_mat[OUTPUT][0];
if( dims1 > dims2 )
cv::convertPointsFromHomogeneous(_input, _output);
else
cv::convertPointsToHomogeneous(_input, _output);
}
void CV_ConvertHomogeneousTest::prepare_to_validation( int /*test_case_idx*/ )
{
test_convertHomogeneous( test_mat[INPUT][0], test_mat[REF_OUTPUT][0] );
}
/************************** compute corresponding epipolar lines ************************/
class CV_ComputeEpilinesTest : public cvtest::ArrayTest
{
public:
CV_ComputeEpilinesTest();
protected:
int read_params( const cv::FileStorage& fs );
void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types );
void fill_array( int test_case_idx, int i, int j, Mat& arr );
double get_success_error_level( int test_case_idx, int i, int j );
void run_func();
void prepare_to_validation( int );
int which_image;
int dims;
int pt_count;
};
CV_ComputeEpilinesTest::CV_ComputeEpilinesTest()
{
test_array[INPUT].push_back(NULL);
test_array[INPUT].push_back(NULL);
test_array[OUTPUT].push_back(NULL);
test_array[REF_OUTPUT].push_back(NULL);
element_wise_relative_error = false;
pt_count = dims = which_image = 0;
}
int CV_ComputeEpilinesTest::read_params( const cv::FileStorage& fs )
{
int code = cvtest::ArrayTest::read_params( fs );
return code;
}
void CV_ComputeEpilinesTest::get_test_array_types_and_sizes( int /*test_case_idx*/,
vector<vector<Size> >& sizes, vector<vector<int> >& types )
{
RNG& rng = ts->get_rng();
int fm_depth = cvtest::randInt(rng) % 2 == 0 ? CV_32F : CV_64F;
int pt_depth = cvtest::randInt(rng) % 2 == 0 ? CV_32F : CV_64F;
int ln_depth = pt_depth;
double pt_count_exp = cvtest::randReal(rng)*6;
which_image = 1 + (cvtest::randInt(rng) % 2);
pt_count = cvRound(exp(pt_count_exp));
pt_count = MAX( pt_count, 1 );
bool few_points = pt_count < 5;
dims = 2 + (cvtest::randInt(rng) % 2);
types[INPUT][0] = CV_MAKETYPE(pt_depth, 1);
sizes[INPUT][0] = Size(dims, pt_count);
if( cvtest::randInt(rng) % 2 || few_points )
{
types[INPUT][0] = CV_MAKETYPE(pt_depth, dims);
if( cvtest::randInt(rng) % 2 )
sizes[INPUT][0] = Size(pt_count, 1);
else
sizes[INPUT][0] = Size(1, pt_count);
}
types[INPUT][1] = CV_MAKETYPE(fm_depth, 1);
sizes[INPUT][1] = Size(3, 3);
types[OUTPUT][0] = CV_MAKETYPE(ln_depth, 3);
sizes[OUTPUT][0] = Size(1, pt_count);
types[REF_OUTPUT][0] = types[OUTPUT][0];
sizes[REF_OUTPUT][0] = sizes[OUTPUT][0];
}
double CV_ComputeEpilinesTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ )
{
return 1e-5;
}
void CV_ComputeEpilinesTest::fill_array( int test_case_idx, int i, int j, Mat& arr )
{
RNG& rng = ts->get_rng();
if( i == INPUT && j == 0 )
{
Mat temp( 1, pt_count, CV_MAKETYPE(CV_64FC1,dims) );
cvtest::randUni( rng, temp, Scalar(0,0,1), Scalar::all(10) );
test_convertHomogeneous( temp, arr );
}
else if( i == INPUT && j == 1 )
cvtest::randUni( rng, arr, Scalar::all(0), Scalar::all(10) );
else
cvtest::ArrayTest::fill_array( test_case_idx, i, j, arr );
}
void CV_ComputeEpilinesTest::run_func()
{
cv::Mat _points = test_mat[INPUT][0], _F = test_mat[INPUT][1], &_lines = test_mat[OUTPUT][0];
cv::computeCorrespondEpilines( _points, which_image, _F, _lines );
}
void CV_ComputeEpilinesTest::prepare_to_validation( int /*test_case_idx*/ )
{
Mat pt( 1, pt_count, CV_MAKETYPE(CV_64F, 3) );
Mat lines( 1, pt_count, CV_MAKETYPE(CV_64F, 3) );
double f[9];
Mat F( 3, 3, CV_64F, f );
test_convertHomogeneous( test_mat[INPUT][0], pt );
test_mat[INPUT][1].convertTo(F, CV_64F);
if( which_image == 2 )
cv::transpose( F, F );
for( int i = 0; i < pt_count; i++ )
{
double* p = pt.ptr<double>() + i*3;
double* l = lines.ptr<double>() + i*3;
double t0 = f[0]*p[0] + f[1]*p[1] + f[2]*p[2];
double t1 = f[3]*p[0] + f[4]*p[1] + f[5]*p[2];
double t2 = f[6]*p[0] + f[7]*p[1] + f[8]*p[2];
double d = sqrt(t0*t0 + t1*t1);
d = d ? 1./d : 1.;
l[0] = t0*d; l[1] = t1*d; l[2] = t2*d;
}
test_convertHomogeneous( lines, test_mat[REF_OUTPUT][0] );
}
TEST(Calib3d_ConvertHomogeneoous, accuracy) { CV_ConvertHomogeneousTest test; test.safe_run(); }
TEST(Calib3d_ComputeEpilines, accuracy) { CV_ComputeEpilinesTest test; test.safe_run(); }
TEST(Calib3d_FindFundamentalMat, correctMatches)
{
double fdata[] = {0, 0, 0, 0, 0, -1, 0, 1, 0};
double p1data[] = {200, 0, 1};
double p2data[] = {170, 0, 1};
Mat F(3, 3, CV_64F, fdata);
Mat p1(1, 1, CV_64FC2, p1data);
Mat p2(1, 1, CV_64FC2, p2data);
Mat np1, np2;
correctMatches(F, p1, p2, np1, np2);
cout << np1 << endl;
cout << np2 << endl;
}
TEST(Calib3d_FindFundamentalMat, Crash)
{
vector<Point2f> m1 = {{245, 128},{284, 226},{140, 60},{133, 127},{71, 218},{152, 138},{181, 106}};
vector<Point2f> m2 = m1;
vector<uchar> mask;
findFundamentalMat(m1, m2, mask, FM_LMEDS, 1, 0.99);
}
}} // namespace
/* End of file. */
+794
View File
@@ -0,0 +1,794 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// 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
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2015, Itseez Inc., 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:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 the Intel Corporation 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 "test_precomp.hpp"
// #define SAVE_FEATURES 1
#ifdef SAVE_FEATURES
# include "opencv2/features.hpp"
#endif
namespace opencv_test { namespace {
#define MESSAGE_MATRIX_SIZE "Homography matrix must have 3*3 sizes."
#define MESSAGE_MATRIX_DIFF "Accuracy of homography transformation matrix less than required."
#define MESSAGE_REPROJ_DIFF_1 "Reprojection error for current pair of points more than required."
#define MESSAGE_REPROJ_DIFF_2 "Reprojection error is not optimal."
#define MESSAGE_RANSAC_MASK_1 "Sizes of inliers/outliers mask are incorrect."
#define MESSAGE_RANSAC_MASK_2 "Mask mustn't have any outliers."
#define MESSAGE_RANSAC_MASK_3 "All values of mask must be 1 (true) or 0 (false)."
#define MESSAGE_RANSAC_MASK_4 "Mask of inliers/outliers is incorrect."
#define MESSAGE_RANSAC_MASK_5 "Inlier in original mask shouldn't be outlier in found mask."
#define MESSAGE_RANSAC_DIFF "Reprojection error for current pair of points more than required."
#define MAX_COUNT_OF_POINTS 303
#define MIN_COUNT_OF_POINTS 4
#define COUNT_NORM_TYPES 3
#define METHODS_COUNT 4
int NORM_TYPE[COUNT_NORM_TYPES] = {cv::NORM_L1, cv::NORM_L2, cv::NORM_INF};
int METHOD[METHODS_COUNT] = {0, cv::RANSAC, cv::LMEDS, cv::RHO};
using namespace cv;
using namespace std;
namespace HomographyTestUtils {
static const float max_diff = 0.032f;
static const float max_2diff = 0.020f;
static const int image_size = 100;
static const double reproj_threshold = 3.0;
static const double sigma = 0.01;
static bool check_matrix_size(const cv::Mat& H)
{
return (H.rows == 3) && (H.cols == 3);
}
static bool check_matrix_diff(const cv::Mat& original, const cv::Mat& found, const int norm_type, double &diff)
{
diff = cvtest::norm(original, found, norm_type);
return diff <= max_diff;
}
static int check_ransac_mask_1(const Mat& src, const Mat& mask)
{
if (!(mask.cols == 1) && (mask.rows == src.cols)) return 1;
if (countNonZero(mask) < mask.rows) return 2;
for (int i = 0; i < mask.rows; ++i) if (mask.at<uchar>(i, 0) > 1) return 3;
return 0;
}
static int check_ransac_mask_2(const Mat& original_mask, const Mat& found_mask)
{
if (!(found_mask.cols == 1) && (found_mask.rows == original_mask.rows)) return 1;
for (int i = 0; i < found_mask.rows; ++i) if (found_mask.at<uchar>(i, 0) > 1) return 2;
return 0;
}
static void print_information_1(int j, int N, int _method, const Mat& H)
{
cout << endl; cout << "Checking for homography matrix sizes..." << endl; cout << endl;
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
cout << "Count of points: " << N << endl; cout << endl;
cout << "Method: "; if (_method == 0) cout << 0; else if (_method == 8) cout << "RANSAC"; else if (_method == cv::RHO) cout << "RHO"; else cout << "LMEDS"; cout << endl;
cout << "Homography matrix:" << endl; cout << endl;
cout << H << endl; cout << endl;
cout << "Number of rows: " << H.rows << " Number of cols: " << H.cols << endl; cout << endl;
}
static void print_information_2(int j, int N, int _method, const Mat& H, const Mat& H_res, int k, double diff)
{
cout << endl; cout << "Checking for accuracy of homography matrix computing..." << endl; cout << endl;
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
cout << "Count of points: " << N << endl; cout << endl;
cout << "Method: "; if (_method == 0) cout << 0; else if (_method == 8) cout << "RANSAC"; else if (_method == cv::RHO) cout << "RHO"; else cout << "LMEDS"; cout << endl;
cout << "Original matrix:" << endl; cout << endl;
cout << H << endl; cout << endl;
cout << "Found matrix:" << endl; cout << endl;
cout << H_res << endl; cout << endl;
cout << "Norm type using in criteria: "; if (NORM_TYPE[k] == 1) cout << "INF"; else if (NORM_TYPE[k] == 2) cout << "L1"; else cout << "L2"; cout << endl;
cout << "Difference between matrices: " << diff << endl;
cout << "Maximum allowed difference: " << max_diff << endl; cout << endl;
}
static void print_information_3(int _method, int j, int N, const Mat& mask)
{
cout << endl; cout << "Checking for inliers/outliers mask..." << endl; cout << endl;
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
cout << "Count of points: " << N << endl; cout << endl;
cout << "Method: "; if (_method == RANSAC) cout << "RANSAC" << endl; else if (_method == cv::RHO) cout << "RHO" << endl; else cout << _method << endl;
cout << "Found mask:" << endl; cout << endl;
cout << mask << endl; cout << endl;
cout << "Number of rows: " << mask.rows << " Number of cols: " << mask.cols << endl; cout << endl;
}
static void print_information_4(int _method, int j, int N, int k, int l, double diff)
{
cout << endl; cout << "Checking for accuracy of reprojection error computing..." << endl; cout << endl;
cout << "Method: "; if (_method == 0) cout << 0 << endl; else cout << "CV_LMEDS" << endl;
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
cout << "Sigma of normal noise: " << sigma << endl;
cout << "Count of points: " << N << endl;
cout << "Number of point: " << k << endl;
cout << "Norm type using in criteria: "; if (NORM_TYPE[l] == 1) cout << "INF"; else if (NORM_TYPE[l] == 2) cout << "L1"; else cout << "L2"; cout << endl;
cout << "Difference with noise of point: " << diff << endl;
cout << "Maximum allowed difference: " << max_2diff << endl; cout << endl;
}
static void print_information_5(int _method, int j, int N, int l, double diff)
{
cout << endl; cout << "Checking for accuracy of reprojection error computing..." << endl; cout << endl;
cout << "Method: "; if (_method == 0) cout << 0 << endl; else cout << "CV_LMEDS" << endl;
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
cout << "Sigma of normal noise: " << sigma << endl;
cout << "Count of points: " << N << endl;
cout << "Norm type using in criteria: "; if (NORM_TYPE[l] == 1) cout << "INF"; else if (NORM_TYPE[l] == 2) cout << "L1"; else cout << "L2"; cout << endl;
cout << "Difference with noise of points: " << diff << endl;
cout << "Maximum allowed difference: " << max_diff << endl; cout << endl;
}
static void print_information_6(int _method, int j, int N, int k, double diff, bool value)
{
cout << endl; cout << "Checking for inliers/outliers mask..." << endl; cout << endl;
cout << "Method: "; if (_method == RANSAC) cout << "RANSAC" << endl; else if (_method == cv::RHO) cout << "RHO" << endl; else cout << _method << endl;
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
cout << "Count of points: " << N << " " << endl;
cout << "Number of point: " << k << " " << endl;
cout << "Reprojection error for this point: " << diff << " " << endl;
cout << "Reprojection error threshold: " << reproj_threshold << " " << endl;
cout << "Value of found mask: "<< value << endl; cout << endl;
}
static void print_information_7(int _method, int j, int N, int k, double diff, bool original_value, bool found_value)
{
cout << endl; cout << "Checking for inliers/outliers mask..." << endl; cout << endl;
cout << "Method: "; if (_method == RANSAC) cout << "RANSAC" << endl; else if (_method == cv::RHO) cout << "RHO" << endl; else cout << _method << endl;
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
cout << "Count of points: " << N << " " << endl;
cout << "Number of point: " << k << " " << endl;
cout << "Reprojection error for this point: " << diff << " " << endl;
cout << "Reprojection error threshold: " << reproj_threshold << " " << endl;
cout << "Value of original mask: "<< original_value << " Value of found mask: " << found_value << endl; cout << endl;
}
static void print_information_8(int _method, int j, int N, int k, int l, double diff)
{
cout << endl; cout << "Checking for reprojection error of inlier..." << endl; cout << endl;
cout << "Method: "; if (_method == RANSAC) cout << "RANSAC" << endl; else if (_method == cv::RHO) cout << "RHO" << endl; else cout << _method << endl;
cout << "Sigma of normal noise: " << sigma << endl;
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
cout << "Count of points: " << N << " " << endl;
cout << "Number of point: " << k << " " << endl;
cout << "Norm type using in criteria: "; if (NORM_TYPE[l] == 1) cout << "INF"; else if (NORM_TYPE[l] == 2) cout << "L1"; else cout << "L2"; cout << endl;
cout << "Difference with noise of point: " << diff << endl;
cout << "Maximum allowed difference: " << max_2diff << endl; cout << endl;
}
} // HomographyTestUtils::
TEST(Calib3d_Homography, accuracy)
{
using namespace HomographyTestUtils;
for (int N = MIN_COUNT_OF_POINTS; N <= MAX_COUNT_OF_POINTS; ++N)
{
RNG& rng = cv::theRNG();
float *src_data = new float [2*N];
for (int i = 0; i < N; ++i)
{
src_data[2*i] = (float)cvtest::randReal(rng)*image_size;
src_data[2*i+1] = (float)cvtest::randReal(rng)*image_size;
}
cv::Mat src_mat_2f(1, N, CV_32FC2, src_data),
src_mat_2d(2, N, CV_32F, src_data),
src_mat_3d(3, N, CV_32F);
cv::Mat dst_mat_2f, dst_mat_2d, dst_mat_3d;
vector <Point2f> src_vec, dst_vec;
for (int i = 0; i < N; ++i)
{
float *tmp = src_mat_2d.ptr<float>()+2*i;
src_mat_3d.at<float>(0, i) = tmp[0];
src_mat_3d.at<float>(1, i) = tmp[1];
src_mat_3d.at<float>(2, i) = 1.0f;
src_vec.push_back(Point2f(tmp[0], tmp[1]));
}
double fi = cvtest::randReal(rng)*2*CV_PI;
double t_x = cvtest::randReal(rng)*sqrt(image_size*1.0),
t_y = cvtest::randReal(rng)*sqrt(image_size*1.0);
double Hdata[9] = { cos(fi), -sin(fi), t_x,
sin(fi), cos(fi), t_y,
0.0f, 0.0f, 1.0f };
cv::Mat H_64(3, 3, CV_64F, Hdata), H_32;
H_64.convertTo(H_32, CV_32F);
dst_mat_3d = H_32*src_mat_3d;
dst_mat_2d.create(2, N, CV_32F); dst_mat_2f.create(1, N, CV_32FC2);
for (int i = 0; i < N; ++i)
{
float *tmp_2f = dst_mat_2f.ptr<float>()+2*i;
tmp_2f[0] = dst_mat_2d.at<float>(0, i) = dst_mat_3d.at<float>(0, i) /= dst_mat_3d.at<float>(2, i);
tmp_2f[1] = dst_mat_2d.at<float>(1, i) = dst_mat_3d.at<float>(1, i) /= dst_mat_3d.at<float>(2, i);
dst_mat_3d.at<float>(2, i) = 1.0f;
dst_vec.push_back(Point2f(tmp_2f[0], tmp_2f[1]));
}
for (int i = 0; i < METHODS_COUNT; ++i)
{
const int method = METHOD[i];
switch (method)
{
case 0:
case LMEDS:
{
Mat H_res_64 [4] = { cv::findHomography(src_mat_2f, dst_mat_2f, method),
cv::findHomography(src_mat_2f, dst_vec, method),
cv::findHomography(src_vec, dst_mat_2f, method),
cv::findHomography(src_vec, dst_vec, method) };
for (int j = 0; j < 4; ++j)
{
if (!check_matrix_size(H_res_64[j]))
{
print_information_1(j, N, method, H_res_64[j]);
CV_Error(cv::Error::StsError, MESSAGE_MATRIX_SIZE);
return;
}
double diff;
for (int k = 0; k < COUNT_NORM_TYPES; ++k)
if (!check_matrix_diff(H_64, H_res_64[j], NORM_TYPE[k], diff))
{
print_information_2(j, N, method, H_64, H_res_64[j], k, diff);
CV_Error(cv::Error::StsError, MESSAGE_MATRIX_DIFF);
return;
}
}
continue;
}
case cv::RHO:
case RANSAC:
{
cv::Mat mask [4]; double diff;
Mat H_res_64 [4] = { cv::findHomography(src_mat_2f, dst_mat_2f, method, reproj_threshold, mask[0]),
cv::findHomography(src_mat_2f, dst_vec, method, reproj_threshold, mask[1]),
cv::findHomography(src_vec, dst_mat_2f, method, reproj_threshold, mask[2]),
cv::findHomography(src_vec, dst_vec, method, reproj_threshold, mask[3]) };
for (int j = 0; j < 4; ++j)
{
if (!check_matrix_size(H_res_64[j]))
{
print_information_1(j, N, method, H_res_64[j]);
CV_Error(cv::Error::StsError, MESSAGE_MATRIX_SIZE);
return;
}
for (int k = 0; k < COUNT_NORM_TYPES; ++k)
if (!check_matrix_diff(H_64, H_res_64[j], NORM_TYPE[k], diff))
{
print_information_2(j, N, method, H_64, H_res_64[j], k, diff);
CV_Error(cv::Error::StsError, MESSAGE_MATRIX_DIFF);
return;
}
int code = check_ransac_mask_1(src_mat_2f, mask[j]);
if (code)
{
print_information_3(method, j, N, mask[j]);
switch (code)
{
case 1: { CV_Error(cv::Error::StsError, MESSAGE_RANSAC_MASK_1); break; }
case 2: { CV_Error(cv::Error::StsError, MESSAGE_RANSAC_MASK_2); break; }
case 3: { CV_Error(cv::Error::StsError, MESSAGE_RANSAC_MASK_3); break; }
default: break;
}
return;
}
}
continue;
}
default: continue;
}
}
Mat noise_2f(1, N, CV_32FC2);
rng.fill(noise_2f, RNG::NORMAL, Scalar::all(0), Scalar::all(sigma));
cv::Mat mask(N, 1, CV_8UC1);
for (int i = 0; i < N; ++i)
{
float *a = noise_2f.ptr<float>()+2*i, *_2f = dst_mat_2f.ptr<float>()+2*i;
_2f[0] += a[0]; _2f[1] += a[1];
mask.at<bool>(i, 0) = !(sqrt(a[0]*a[0]+a[1]*a[1]) > reproj_threshold);
}
for (int i = 0; i < METHODS_COUNT; ++i)
{
const int method = METHOD[i];
switch (method)
{
case 0:
case LMEDS:
{
Mat H_res_64 [4] = { cv::findHomography(src_mat_2f, dst_mat_2f),
cv::findHomography(src_mat_2f, dst_vec),
cv::findHomography(src_vec, dst_mat_2f),
cv::findHomography(src_vec, dst_vec) };
for (int j = 0; j < 4; ++j)
{
if (!check_matrix_size(H_res_64[j]))
{
print_information_1(j, N, method, H_res_64[j]);
CV_Error(cv::Error::StsError, MESSAGE_MATRIX_SIZE);
return;
}
Mat H_res_32; H_res_64[j].convertTo(H_res_32, CV_32F);
cv::Mat dst_res_3d(3, N, CV_32F), noise_2d(2, N, CV_32F);
for (int k = 0; k < N; ++k)
{
Mat tmp_mat_3d = H_res_32*src_mat_3d.col(k);
dst_res_3d.at<float>(0, k) = tmp_mat_3d.at<float>(0, 0) /= tmp_mat_3d.at<float>(2, 0);
dst_res_3d.at<float>(1, k) = tmp_mat_3d.at<float>(1, 0) /= tmp_mat_3d.at<float>(2, 0);
dst_res_3d.at<float>(2, k) = tmp_mat_3d.at<float>(2, 0) = 1.0f;
float *a = noise_2f.ptr<float>()+2*k;
noise_2d.at<float>(0, k) = a[0]; noise_2d.at<float>(1, k) = a[1];
for (int l = 0; l < COUNT_NORM_TYPES; ++l)
if (cv::norm(tmp_mat_3d, dst_mat_3d.col(k), NORM_TYPE[l]) - cv::norm(noise_2d.col(k), NORM_TYPE[l]) > max_2diff)
{
print_information_4(method, j, N, k, l, cv::norm(tmp_mat_3d, dst_mat_3d.col(k), NORM_TYPE[l]) - cv::norm(noise_2d.col(k), NORM_TYPE[l]));
CV_Error(cv::Error::StsError, MESSAGE_REPROJ_DIFF_1);
return;
}
}
for (int l = 0; l < COUNT_NORM_TYPES; ++l)
if (cv::norm(dst_res_3d, dst_mat_3d, NORM_TYPE[l]) - cv::norm(noise_2d, NORM_TYPE[l]) > max_diff)
{
print_information_5(method, j, N, l, cv::norm(dst_res_3d, dst_mat_3d, NORM_TYPE[l]) - cv::norm(noise_2d, NORM_TYPE[l]));
CV_Error(cv::Error::StsError, MESSAGE_REPROJ_DIFF_2);
return;
}
}
continue;
}
case cv::RHO:
case RANSAC:
{
cv::Mat mask_res [4];
Mat H_res_64 [4] = { cv::findHomography(src_mat_2f, dst_mat_2f, method, reproj_threshold, mask_res[0]),
cv::findHomography(src_mat_2f, dst_vec, method, reproj_threshold, mask_res[1]),
cv::findHomography(src_vec, dst_mat_2f, method, reproj_threshold, mask_res[2]),
cv::findHomography(src_vec, dst_vec, method, reproj_threshold, mask_res[3]) };
for (int j = 0; j < 4; ++j)
{
if (!check_matrix_size(H_res_64[j]))
{
print_information_1(j, N, method, H_res_64[j]);
CV_Error(cv::Error::StsError, MESSAGE_MATRIX_SIZE);
return;
}
int code = check_ransac_mask_2(mask, mask_res[j]);
if (code)
{
print_information_3(method, j, N, mask_res[j]);
switch (code)
{
case 1: { CV_Error(cv::Error::StsError, MESSAGE_RANSAC_MASK_1); break; }
case 2: { CV_Error(cv::Error::StsError, MESSAGE_RANSAC_MASK_3); break; }
default: break;
}
return;
}
cv::Mat H_res_32; H_res_64[j].convertTo(H_res_32, CV_32F);
cv::Mat dst_res_3d = H_res_32*src_mat_3d;
for (int k = 0; k < N; ++k)
{
dst_res_3d.at<float>(0, k) /= dst_res_3d.at<float>(2, k);
dst_res_3d.at<float>(1, k) /= dst_res_3d.at<float>(2, k);
dst_res_3d.at<float>(2, k) = 1.0f;
float *p = dst_mat_2f.ptr<float>()+2*k;
dst_mat_3d.at<float>(0, k) = p[0];
dst_mat_3d.at<float>(1, k) = p[1];
double diff = cv::norm(dst_res_3d.col(k), dst_mat_3d.col(k), NORM_L2);
if (mask_res[j].at<bool>(k, 0) != (diff <= reproj_threshold))
{
print_information_6(method, j, N, k, diff, mask_res[j].at<bool>(k, 0));
CV_Error(cv::Error::StsError, MESSAGE_RANSAC_MASK_4);
return;
}
if (mask.at<bool>(k, 0) && !mask_res[j].at<bool>(k, 0))
{
print_information_7(method, j, N, k, diff, mask.at<bool>(k, 0), mask_res[j].at<bool>(k, 0));
CV_Error(cv::Error::StsError, MESSAGE_RANSAC_MASK_5);
return;
}
if (mask_res[j].at<bool>(k, 0))
{
float *a = noise_2f.ptr<float>()+2*k;
dst_mat_3d.at<float>(0, k) -= a[0];
dst_mat_3d.at<float>(1, k) -= a[1];
cv::Mat noise_2d(2, 1, CV_32F);
noise_2d.at<float>(0, 0) = a[0]; noise_2d.at<float>(1, 0) = a[1];
for (int l = 0; l < COUNT_NORM_TYPES; ++l)
{
diff = cv::norm(dst_res_3d.col(k), dst_mat_3d.col(k), NORM_TYPE[l]);
if (diff - cv::norm(noise_2d, NORM_TYPE[l]) > max_2diff)
{
print_information_8(method, j, N, k, l, diff - cv::norm(noise_2d, NORM_TYPE[l]));
CV_Error(cv::Error::StsError, MESSAGE_RANSAC_DIFF);
return;
}
}
}
}
}
continue;
}
default: continue;
}
}
delete[]src_data;
src_data = NULL;
}
}
TEST(Calib3d_Homography, EKcase)
{
float pt1data[] =
{
2.80073029e+002f, 2.39591217e+002f, 2.21912201e+002f, 2.59783997e+002f,
2.16053192e+002f, 2.78826569e+002f, 2.22782532e+002f, 2.82330383e+002f,
2.09924820e+002f, 2.89122559e+002f, 2.11077698e+002f, 2.89384674e+002f,
2.25287689e+002f, 2.88795532e+002f, 2.11180801e+002f, 2.89653503e+002f,
2.24126404e+002f, 2.90466064e+002f, 2.10914429e+002f, 2.90886963e+002f,
2.23439362e+002f, 2.91657715e+002f, 2.24809387e+002f, 2.91891602e+002f,
2.09809082e+002f, 2.92891113e+002f, 2.08771164e+002f, 2.93093231e+002f,
2.23160095e+002f, 2.93259460e+002f, 2.07874023e+002f, 2.93989990e+002f,
2.08963638e+002f, 2.94209839e+002f, 2.23963165e+002f, 2.94479645e+002f,
2.23241791e+002f, 2.94887817e+002f, 2.09438782e+002f, 2.95233337e+002f,
2.08901886e+002f, 2.95762878e+002f, 2.21867981e+002f, 2.95747711e+002f,
2.24195511e+002f, 2.98270905e+002f, 2.09331345e+002f, 3.05958191e+002f,
2.24727875e+002f, 3.07186035e+002f, 2.26718842e+002f, 3.08095795e+002f,
2.25363953e+002f, 3.08200226e+002f, 2.19897797e+002f, 3.13845093e+002f,
2.25013474e+002f, 3.15558777e+002f
};
float pt2data[] =
{
1.84072723e+002f, 1.43591202e+002f, 1.25912483e+002f, 1.63783859e+002f,
2.06439407e+002f, 2.20573929e+002f, 1.43801437e+002f, 1.80703903e+002f,
9.77904129e+000f, 2.49660202e+002f, 1.38458405e+001f, 2.14502701e+002f,
1.50636337e+002f, 2.15597183e+002f, 6.43103180e+001f, 2.51667648e+002f,
1.54952499e+002f, 2.20780014e+002f, 1.26638412e+002f, 2.43040924e+002f,
3.67568909e+002f, 1.83624954e+001f, 1.60657944e+002f, 2.21794052e+002f,
-1.29507828e+000f, 3.32472443e+002f, 8.51442242e+000f, 4.15561554e+002f,
1.27161377e+002f, 1.97260361e+002f, 5.40714645e+000f, 4.90978302e+002f,
2.25571690e+001f, 3.96912415e+002f, 2.95664978e+002f, 7.36064959e+000f,
1.27241104e+002f, 1.98887573e+002f, -1.25569367e+000f, 3.87713226e+002f,
1.04194012e+001f, 4.31495758e+002f, 1.25868874e+002f, 1.99751617e+002f,
1.28195480e+002f, 2.02270355e+002f, 2.23436356e+002f, 1.80489182e+002f,
1.28727692e+002f, 2.11185410e+002f, 2.03336639e+002f, 2.52182083e+002f,
1.29366486e+002f, 2.12201904e+002f, 1.23897598e+002f, 2.17847351e+002f,
1.29015259e+002f, 2.19560623e+002f
};
int npoints = (int)(sizeof(pt1data)/sizeof(pt1data[0])/2);
Mat p1(1, npoints, CV_32FC2, pt1data);
Mat p2(1, npoints, CV_32FC2, pt2data);
Mat mask;
Mat h = findHomography(p1, p2, RANSAC, 0.01, mask);
ASSERT_TRUE(!h.empty());
cv::transpose(mask, mask);
Mat p3, mask2;
int ninliers = countNonZero(mask);
Mat nmask[] = { mask, mask };
merge(nmask, 2, mask2);
perspectiveTransform(p1, p3, h);
mask2 = mask2.reshape(1);
p2 = p2.reshape(1);
p3 = p3.reshape(1);
double err = cvtest::norm(p2, p3, NORM_INF, mask2);
printf("ninliers: %d, inliers err: %.2g\n", ninliers, err);
ASSERT_GE(ninliers, 10);
ASSERT_LE(err, 0.01);
}
#ifdef SAVE_FEATURES
static void saveKeypoints(const std::string& fname, const std::vector<cv::Point2f> points)
{
FileStorage fs(fname, cv::FileStorage::WRITE);
ASSERT_TRUE(fs.isOpened());
fs << "keypoints" << points;
fs.release();
}
#else
static void loadKeypoints(const std::string& fname, std::vector<cv::Point2f>& points)
{
FileStorage fs(fname, cv::FileStorage::READ);
ASSERT_TRUE(fs.isOpened());
fs["keypoints"] >> points;
fs.release();
}
#endif
TEST(Calib3d_Homography, fromImages)
{
const std::string points_file1 = cvtest::TS::ptr()->get_data_path() + "cv/optflow/image1.ORB.yaml.gz";
const std::string points_file2 = cvtest::TS::ptr()->get_data_path() + "cv/optflow/image2.ORB.yaml.gz";
std::vector<Point2f> pointframe1;
std::vector<Point2f> pointframe2;
#ifdef SAVE_FEATURES
vector<KeyPoint> keypoints_1, keypoints_2;
Mat descriptors_1, descriptors_2;
Mat img_1 = imread(cvtest::TS::ptr()->get_data_path() + "cv/optflow/image1.png", 0);
Mat img_2 = imread(cvtest::TS::ptr()->get_data_path() + "cv/optflow/image2.png", 0);
Ptr<ORB> orb = ORB::create();
orb->detectAndCompute( img_1, Mat(), keypoints_1, descriptors_1, false );
orb->detectAndCompute( img_2, Mat(), keypoints_2, descriptors_2, false );
//-- Step 3: Matching descriptor vectors using Brute Force matcher
BFMatcher matcher(NORM_HAMMING,false);
std::vector< DMatch > matches;
matcher.match( descriptors_1, descriptors_2, matches );
double max_dist = 0; double min_dist = 100;
//-- Quick calculation of max and min distances between keypoints
for( int i = 0; i < descriptors_1.rows; i++ )
{
double dist = matches[i].distance;
if( dist < min_dist ) min_dist = dist;
if( dist > max_dist ) max_dist = dist;
}
//-- Draw only "good" matches (i.e. whose distance is less than 3*min_dist )
std::vector< DMatch > good_matches;
for( int i = 0; i < descriptors_1.rows; i++ )
{
if( matches[i].distance <= 100 )
good_matches.push_back( matches[i]);
}
//-- Localize the model
for( int i = 0; i < (int)good_matches.size(); i++ )
{
//-- Get the keypoints from the good matches
pointframe1.push_back( keypoints_1[ good_matches[i].queryIdx ].pt );
pointframe2.push_back( keypoints_2[ good_matches[i].trainIdx ].pt );
}
saveKeypoints(points_file1, pointframe1);
saveKeypoints(points_file2, pointframe2);
#else
loadKeypoints(points_file1, pointframe1);
loadKeypoints(points_file2, pointframe2);
#endif
ASSERT_FALSE(pointframe1.empty());
ASSERT_FALSE(pointframe1.empty());
Mat H0, H1, inliers0, inliers1;
double min_t0 = DBL_MAX, min_t1 = DBL_MAX;
for( int i = 0; i < 10; i++ )
{
double t = (double)getTickCount();
H0 = findHomography( pointframe1, pointframe2, RANSAC, 3.0, inliers0 );
t = (double)getTickCount() - t;
min_t0 = std::min(min_t0, t);
}
int ninliers0 = countNonZero(inliers0);
for( int i = 0; i < 10; i++ )
{
double t = (double)getTickCount();
H1 = findHomography( pointframe1, pointframe2, RHO, 3.0, inliers1 );
t = (double)getTickCount() - t;
min_t1 = std::min(min_t1, t);
}
int ninliers1 = countNonZero(inliers1);
ASSERT_TRUE(!H0.empty());
ASSERT_GE(ninliers0, 80);
ASSERT_TRUE(!H1.empty());
ASSERT_GE(ninliers1, 80);
}
TEST(Calib3d_Homography, minPoints)
{
float pt1data[] =
{
2.80073029e+002f, 2.39591217e+002f, 2.21912201e+002f, 2.59783997e+002f
};
float pt2data[] =
{
1.84072723e+002f, 1.43591202e+002f, 1.25912483e+002f, 1.63783859e+002f
};
int npoints = (int)(sizeof(pt1data)/sizeof(pt1data[0])/2);
printf("npoints = %d\n", npoints); // npoints = 2
Mat p1(1, npoints, CV_32FC2, pt1data);
Mat p2(1, npoints, CV_32FC2, pt2data);
Mat mask;
// findHomography should raise an error since npoints < MIN_COUNT_OF_POINTS
EXPECT_THROW(findHomography(p1, p2, RANSAC, 0.01, mask), cv::Exception);
}
TEST(Calib3d_Homography, not_normalized)
{
Mat_<double> p1({5, 2}, {-1, -1, -2, -2, -1, 1, -2, 2, -1, 0});
Mat_<double> p2({5, 2}, {0, -1, -1, -1, 0, 0, -1, 0, 0, -0.5});
Mat_<double> ref({3, 3}, {
0.74276086, 0., 0.74276086,
0.18569022, 0.18569022, 0.,
-0.37138043, 0., 0.
});
for (int method : std::vector<int>({0, RANSAC, LMEDS}))
{
Mat h = findHomography(p1, p2, method);
for (auto it = h.begin<double>(); it != h.end<double>(); ++it) {
ASSERT_FALSE(cvIsNaN(*it)) << cv::format("method %d\nResult:\n", method) << h;
}
if (h.at<double>(0, 0) * ref.at<double>(0, 0) < 0) {
h *= -1;
}
ASSERT_LE(cv::norm(h, ref, NORM_INF), 1e-8) << cv::format("method %d\nResult:\n", method) << h;
}
}
TEST(Calib3d_Homography, Refine)
{
Mat_<double> p1({10, 2}, {41, -86, -87, 99, 66, -96, -86, -8, -67, 24,
-87, -76, -19, 89, 37, -4, -86, -86, -66, -53});
Mat_<double> p2({10, 2}, {
0.007723226608700208, -1.177541410622515,
-0.1909072353027552, -0.4247610181930323,
-0.134992319993638, -0.6469949816560389,
-0.3570627451405215, 0.1811469436293486,
-0.3005671881038939, -0.02325733734262935,
-0.4404509481789249, 0.4851526464158342,
0.6343346428859541, -3.396187657072353,
-0.3539383967092603, 0.1469447227353143,
-0.4526924606856586, 0.5296757109061794,
-0.4309974583614644, 0.4522732662733471
});
hconcat(p1, Mat::ones(p1.rows, 1, CV_64F), p1);
hconcat(p2, Mat::ones(p2.rows, 1, CV_64F), p2);
for(int method : std::vector<int>({0, RANSAC, LMEDS}))
{
Mat h = findHomography(p1, p2, method);
EXPECT_NEAR(h.at<double>(2, 2), 1.0, 1e-7);
Mat proj = p1 * h.t();
proj.col(0) /= proj.col(2);
proj.col(1) /= proj.col(2);
Mat error;
cv::pow(p2.colRange(0, 2) - proj.colRange(0, 2), 2, error);
cv::reduce(error, error, 1, REDUCE_SUM);
cv::reduce(error, error, 0, REDUCE_AVG);
EXPECT_LE(sqrt(error.at<double>(0, 0)), method == LMEDS ? 7e-2 : 7e-5);
}
}
}} // namespace
@@ -0,0 +1,169 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// This is a test file for the function decomposeHomography contributed to OpenCV
// by Samson Yilma.
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// 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
//
// Copyright (C) 2014, Samson Yilma (samson_yilma@yahoo.com), 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:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 the Intel Corporation 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 "test_precomp.hpp"
namespace opencv_test { namespace {
class CV_HomographyDecompTest: public cvtest::BaseTest {
public:
CV_HomographyDecompTest()
{
buildTestDataSet();
}
protected:
void run(int)
{
vector<Mat> rotations;
vector<Mat> translations;
vector<Mat> normals;
decomposeHomographyMat(_H, _K, rotations, translations, normals);
//there should be at least 1 solution
ASSERT_GT(static_cast<int>(rotations.size()), 0);
ASSERT_GT(static_cast<int>(translations.size()), 0);
ASSERT_GT(static_cast<int>(normals.size()), 0);
ASSERT_EQ(rotations.size(), normals.size());
ASSERT_EQ(translations.size(), normals.size());
ASSERT_TRUE(containsValidMotion(rotations, translations, normals));
decomposeHomographyMat(_H, _K, rotations, noArray(), noArray());
ASSERT_GT(static_cast<int>(rotations.size()), 0);
}
private:
void buildTestDataSet()
{
_K = Matx33d(640, 0.0, 320,
0, 640, 240,
0, 0, 1);
_H = Matx33d(2.649157564634028, 4.583875997496426, 70.694447785121326,
-1.072756858861583, 3.533262150437228, 1513.656999614321649,
0.001303887589576, 0.003042206876298, 1.000000000000000
);
//expected solution for the given homography and intrinsic matrices
_R = Matx33d(0.43307983549125, 0.545749113549648, -0.717356090899523,
-0.85630229674426, 0.497582023798831, -0.138414255706431,
0.281404038139784, 0.67421809131173, 0.682818960388909);
_t = Vec3d(1.826751712278038, 1.264718492450820, 0.195080809998819);
_n = Vec3d(0.244875830334816, 0.480857890778889, 0.841909446789566);
}
bool containsValidMotion(std::vector<Mat>& rotations,
std::vector<Mat>& translations,
std::vector<Mat>& normals
)
{
double max_error = 1.0e-3;
vector<Mat>::iterator riter = rotations.begin();
vector<Mat>::iterator titer = translations.begin();
vector<Mat>::iterator niter = normals.begin();
for (;
riter != rotations.end() && titer != translations.end() && niter != normals.end();
++riter, ++titer, ++niter) {
double rdist = cvtest::norm(*riter, _R, NORM_INF);
double tdist = cvtest::norm(*titer, _t, NORM_INF);
double ndist = cvtest::norm(*niter, _n, NORM_INF);
if ( rdist < max_error
&& tdist < max_error
&& ndist < max_error )
return true;
}
return false;
}
Matx33d _R, _K, _H;
Vec3d _t, _n;
};
TEST(Calib3d_DecomposeHomography, regression) { CV_HomographyDecompTest test; test.safe_run(); }
TEST(Calib3d_DecomposeHomography, issue_4978)
{
Matx33d K(
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0
);
Matx33d H(
-0.102896, 0.270191, -0.0031153,
0.0406387, 1.19569, -0.0120456,
0.445351, 0.0410889, 1
);
vector<Mat> rotations;
vector<Mat> translations;
vector<Mat> normals;
decomposeHomographyMat(H, K, rotations, translations, normals);
ASSERT_GT(rotations.size(), (size_t)0u);
for (size_t i = 0; i < rotations.size(); i++)
{
// check: det(R) = 1
EXPECT_TRUE(std::fabs(cv::determinant(rotations[i]) - 1.0) < 0.01)
<< "R: det=" << cv::determinant(rotations[0]) << std::endl << rotations[i] << std::endl
<< "T:" << std::endl << translations[i] << std::endl;
}
}
}} // namespace
@@ -0,0 +1,318 @@
// 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 "test_precomp.hpp"
namespace opencv_test { namespace {
TEST(Imgproc_IntersectConvexConvex, no_intersection)
{
std::vector<cv::Point> convex1;
convex1.push_back(cv::Point(290, 126));
convex1.push_back(cv::Point(284, 132));
convex1.push_back(cv::Point(281, 133));
convex1.push_back(cv::Point(256, 124));
convex1.push_back(cv::Point(249, 116));
convex1.push_back(cv::Point(234, 91));
convex1.push_back(cv::Point(232, 86));
convex1.push_back(cv::Point(232, 79));
convex1.push_back(cv::Point(251, 69));
convex1.push_back(cv::Point(257, 68));
convex1.push_back(cv::Point(297, 85));
convex1.push_back(cv::Point(299, 87));
std::vector<cv::Point> convex2;
convex2.push_back(cv::Point(192, 236));
convex2.push_back(cv::Point(190, 245));
convex2.push_back(cv::Point(177, 260));
convex2.push_back(cv::Point(154, 271));
convex2.push_back(cv::Point(142, 270));
convex2.push_back(cv::Point(135, 263));
convex2.push_back(cv::Point(131, 254));
convex2.push_back(cv::Point(132, 240));
convex2.push_back(cv::Point(172, 213));
convex2.push_back(cv::Point(176, 216));
std::vector<cv::Point> intersection;
float area = cv::intersectConvexConvex(convex1, convex2, intersection);
EXPECT_TRUE(intersection.empty());
EXPECT_NEAR(area, 0, std::numeric_limits<float>::epsilon());
}
TEST(Imgproc_IntersectConvexConvex, no_intersection_with_1_vertex_on_edge_1)
{
std::vector<cv::Point> convex1;
convex1.push_back(cv::Point(0,0));
convex1.push_back(cv::Point(740, 0));
convex1.push_back(cv::Point(740, 540));
convex1.push_back(cv::Point(0, 540));
std::vector<cv::Point> convex2;
convex2.push_back(cv::Point(0, 210));
convex2.push_back(cv::Point(-30, 210));
convex2.push_back(cv::Point(-37, 170));
convex2.push_back(cv::Point(-7, 172));
std::vector<cv::Point> intersection;
float area = cv::intersectConvexConvex(convex1, convex2, intersection);
EXPECT_TRUE(intersection.empty());
EXPECT_NEAR(area, 0, std::numeric_limits<float>::epsilon());
}
TEST(Imgproc_IntersectConvexConvex, no_intersection_with_1_vertex_on_edge_2)
{
std::vector<cv::Point> convex1;
convex1.push_back(cv::Point(0,0));
convex1.push_back(cv::Point(740, 0));
convex1.push_back(cv::Point(740, 540));
convex1.push_back(cv::Point(0, 540));
std::vector<cv::Point> convex2;
convex2.push_back(cv::Point(740, 210));
convex2.push_back(cv::Point(750, 100));
convex2.push_back(cv::Point(790, 250));
convex2.push_back(cv::Point(800, 500));
std::vector<cv::Point> intersection;
float area = cv::intersectConvexConvex(convex1, convex2, intersection);
EXPECT_TRUE(intersection.empty());
EXPECT_NEAR(area, 0, std::numeric_limits<float>::epsilon());
}
TEST(Imgproc_IntersectConvexConvex, intersection_with_1_vertex_on_edge)
{
std::vector<cv::Point> convex1;
convex1.push_back(cv::Point(0,0));
convex1.push_back(cv::Point(740, 0));
convex1.push_back(cv::Point(740, 540));
convex1.push_back(cv::Point(0, 540));
std::vector<cv::Point> convex2;
convex2.push_back(cv::Point(30, 210));
convex2.push_back(cv::Point(0,210));
convex2.push_back(cv::Point(7, 172));
convex2.push_back(cv::Point(37, 170));
std::vector<cv::Point> intersection;
float area = cv::intersectConvexConvex(convex1, convex2, intersection);
std::vector<cv::Point> expected_intersection;
expected_intersection.push_back(cv::Point(0, 210));
expected_intersection.push_back(cv::Point(7, 172));
expected_intersection.push_back(cv::Point(37, 170));
expected_intersection.push_back(cv::Point(30, 210));
EXPECT_EQ(intersection, expected_intersection);
EXPECT_NEAR(area, 1163, std::numeric_limits<float>::epsilon());
}
TEST(Imgproc_IntersectConvexConvex, intersection_with_2_vertices_on_edge)
{
std::vector<cv::Point> convex1;
convex1.push_back(cv::Point(0,0));
convex1.push_back(cv::Point(740, 0));
convex1.push_back(cv::Point(740, 540));
convex1.push_back(cv::Point(0, 540));
std::vector<cv::Point> convex2;
convex2.push_back(cv::Point(30, 210));
convex2.push_back(cv::Point(37, 170));
convex2.push_back(cv::Point(0,210));
convex2.push_back(cv::Point(0, 300));
std::vector<cv::Point> intersection;
float area = cv::intersectConvexConvex(convex1, convex2, intersection);
std::vector<cv::Point> expected_intersection;
expected_intersection.push_back(cv::Point(0, 300));
expected_intersection.push_back(cv::Point(0, 210));
expected_intersection.push_back(cv::Point(37, 170));
expected_intersection.push_back(cv::Point(30, 210));
EXPECT_EQ(intersection, expected_intersection);
EXPECT_NEAR(area, 1950, std::numeric_limits<float>::epsilon());
}
TEST(Imgproc_IntersectConvexConvex, intersection_1)
{
std::vector<cv::Point> convex1;
convex1.push_back(cv::Point(0,0));
convex1.push_back(cv::Point(740, 0));
convex1.push_back(cv::Point(740, 540));
convex1.push_back(cv::Point(0, 540));
std::vector<cv::Point> convex2;
convex2.push_back(cv::Point(20,210));
convex2.push_back(cv::Point(30, 210));
convex2.push_back(cv::Point(37, 170));
convex2.push_back(cv::Point(7, 172));
std::vector<cv::Point> intersection;
float area = cv::intersectConvexConvex(convex1, convex2, intersection);
std::vector<cv::Point> expected_intersection;
expected_intersection.push_back(cv::Point(7, 172));
expected_intersection.push_back(cv::Point(37, 170));
expected_intersection.push_back(cv::Point(30, 210));
expected_intersection.push_back(cv::Point(20, 210));
EXPECT_EQ(intersection, expected_intersection);
EXPECT_NEAR(area, 783, std::numeric_limits<float>::epsilon());
}
TEST(Imgproc_IntersectConvexConvex, intersection_2)
{
std::vector<cv::Point> convex1;
convex1.push_back(cv::Point(0,0));
convex1.push_back(cv::Point(740, 0));
convex1.push_back(cv::Point(740, 540));
convex1.push_back(cv::Point(0, 540));
std::vector<cv::Point> convex2;
convex2.push_back(cv::Point(-2,210));
convex2.push_back(cv::Point(-5, 300));
convex2.push_back(cv::Point(37, 150));
convex2.push_back(cv::Point(7, 172));
std::vector<cv::Point> intersection;
float area = cv::intersectConvexConvex(convex1, convex2, intersection);
std::vector<cv::Point> expected_intersection;
expected_intersection.push_back(cv::Point(0, 202));
expected_intersection.push_back(cv::Point(7, 172));
expected_intersection.push_back(cv::Point(37, 150));
expected_intersection.push_back(cv::Point(0, 282));
EXPECT_EQ(intersection, expected_intersection);
EXPECT_NEAR(area, 1857.19836425781, std::numeric_limits<float>::epsilon());
}
TEST(Imgproc_IntersectConvexConvex, intersection_3)
{
std::vector<cv::Point> convex1;
convex1.push_back(cv::Point(15, 0));
convex1.push_back(cv::Point(740, 0));
convex1.push_back(cv::Point(740, 540));
convex1.push_back(cv::Point(15, 540));
std::vector<cv::Point> convex2;
convex2.push_back(cv::Point(0,210));
convex2.push_back(cv::Point(30, 210));
convex2.push_back(cv::Point(37, 170));
convex2.push_back(cv::Point(7, 172));
std::vector<cv::Point> intersection;
float area = cv::intersectConvexConvex(convex1, convex2, intersection);
std::vector<cv::Point> expected_intersection;
expected_intersection.push_back(cv::Point(15, 171));
expected_intersection.push_back(cv::Point(37, 170));
expected_intersection.push_back(cv::Point(30, 210));
expected_intersection.push_back(cv::Point(15, 210));
EXPECT_EQ(intersection, expected_intersection);
EXPECT_NEAR(area, 723.866760253906, std::numeric_limits<float>::epsilon());
}
TEST(Imgproc_IntersectConvexConvex, intersection_4)
{
std::vector<cv::Point> convex1;
convex1.push_back(cv::Point(15, 0));
convex1.push_back(cv::Point(740, 0));
convex1.push_back(cv::Point(740, 540));
convex1.push_back(cv::Point(15, 540));
std::vector<cv::Point> convex2;
convex2.push_back(cv::Point(15, 0));
convex2.push_back(cv::Point(740, 0));
convex2.push_back(cv::Point(740, 540));
convex2.push_back(cv::Point(15, 540));
std::vector<cv::Point> intersection;
float area = cv::intersectConvexConvex(convex1, convex2, intersection);
std::vector<cv::Point> expected_intersection;
expected_intersection.push_back(cv::Point(15, 0));
expected_intersection.push_back(cv::Point(740, 0));
expected_intersection.push_back(cv::Point(740, 540));
expected_intersection.push_back(cv::Point(15, 540));
EXPECT_EQ(intersection, expected_intersection);
EXPECT_NEAR(area, 391500, std::numeric_limits<float>::epsilon());
}
// The inputs are not convex and cuased buffer overflow
// See https://github.com/opencv/opencv/issues/25259
TEST(Imgproc_IntersectConvexConvex, not_convex)
{
std::vector<cv::Point2f> convex1 = {
{ 46.077175f , 228.66121f }, { 5.428622f , 250.05899f }, {207.51741f , 109.645676f },
{175.94789f , 32.6566f }, {217.4915f , 252.66176f }, {187.09386f , 6.3988557f},
{ 52.20488f , 69.266205f }, { 38.188286f , 134.48068f }, {246.4742f , 31.41043f },
{178.97946f , 169.52287f }, {103.40764f , 153.30397f }, {160.67746f , 17.166115f },
{152.44255f , 135.35f }, {197.03804f , 193.04782f }, {248.28397f , 56.821487f },
{ 10.907227f , 82.55291f }, {109.67949f , 70.7405f }, { 58.96842f , 150.132f },
{150.7613f , 129.54753f }, {254.98463f , 228.21748f }, {139.02563f , 193.89336f },
{ 84.79946f , 162.25363f }, { 39.83567f , 44.626484f }, {107.034996f , 209.38887f },
{ 67.61073f , 17.119232f }, {208.8617f , 33.67367f }, {182.65207f , 8.291072f },
{ 72.89319f , 42.51845f }, {202.4902f , 123.97209f }, { 79.945076f , 140.99268f },
{225.8952f , 66.226326f }, { 34.08404f , 219.2208f }, {243.1221f , 60.95162f }
};
std::vector<cv::Point2f> convex2 = {
{144.33624f , 247.15732f }, { 5.656847f , 17.461054f }, {230.54338f , 2.0446582f},
{143.0578f , 215.27856f }, {250.44626f , 82.54287f }, { 0.3846766f, 11.101262f },
{ 70.81022f , 17.243904f }, { 77.18812f , 75.760666f }, {190.34933f , 234.30962f },
{230.10204f , 133.67998f }, { 58.903755f , 252.96451f }, {213.57228f , 155.7058f },
{190.80992f , 212.90802f }, {203.4356f , 36.55016f }, { 32.276424f , 2.5646307f},
{ 39.73823f , 87.23782f }, {112.46902f , 101.81753f }, { 58.154305f , 238.40395f },
{187.01064f , 96.24343f }, { 44.42692f , 10.573529f }, {118.76949f , 233.35114f },
{ 86.26109f , 120.93148f }, {217.94751f , 130.5933f }, {148.2687f , 68.56015f },
{187.44174f , 214.32857f }, {247.19875f , 180.8494f }, { 17.986013f , 61.451443f },
{254.74344f , 204.71747f }, {211.92726f , 132.0139f }, { 51.36624f , 116.63085f },
{ 83.80044f , 124.20074f }, {122.125854f , 25.182402f }, { 39.08164f , 180.08517f }
};
std::vector<cv::Point> intersection;
float area = cv::intersectConvexConvex(convex1, convex2, intersection, false);
EXPECT_TRUE(intersection.empty());
EXPECT_LE(area, 0.f);
}
// The intersection was not properly detected when one line sneaked its way in through an edge point
TEST(Imgproc_IntersectConvexConvex, intersection_at_line_transition)
{
std::vector<cv::Point2f> convex1 = {
{ -1.7604526f, -0.00028443217f },
{1276.5778f , 0.2091252f},
{1276.4617f , 719.27f},
{ -1.8754264f, 719.06866f}
};
std::vector<cv::Point2f> convex2 = {
{ 0.f , 0.f },
{1280.f , 0.f },
{1280.f , 720.f},
{ 0.f , 720.f }
};
std::vector<cv::Point> intersection;
float area = cv::intersectConvexConvex(convex1, convex2, intersection, false);
EXPECT_GE(cv::contourArea(convex1), area);
EXPECT_GE(cv::contourArea(convex2), area);
}
} // namespace
} // opencv_test
+508
View File
@@ -0,0 +1,508 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// 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
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
// Nghia Ho, nghiaho12@yahoo.com
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of OpenCV Foundation may not 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 the OpenCV Foundation 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 "test_precomp.hpp"
namespace opencv_test { namespace {
#define ACCURACY 0.00001
// See pics/intersection.png for the scenarios we are testing
// Test the following scenarios:
// 1 - no intersection
// 2 - partial intersection, rectangle translated
// 3 - partial intersection, rectangle rotated 45 degree on the corner, forms a triangle intersection
// 4 - full intersection, rectangles of same size directly on top of each other
// 5 - partial intersection, rectangle on top rotated 45 degrees
// 6 - partial intersection, rectangle on top of different size
// 7 - full intersection, rectangle fully enclosed in the other
// 8 - partial intersection, rectangle corner just touching. point contact
// 9 - partial intersection. rectangle side by side, line contact
static void compare(const std::vector<Point2f>& test, const std::vector<Point2f>& target)
{
ASSERT_EQ(test.size(), target.size());
ASSERT_TRUE(test.size() < 4 || isContourConvex(test));
ASSERT_TRUE(target.size() < 4 || isContourConvex(target));
for( size_t i = 0; i < test.size(); i++ )
{
double r = sqrt(normL2Sqr<double>(test[i] - target[i]));
ASSERT_LT(r, ACCURACY);
}
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_1)
{
// no intersection
RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 12.0f);
RotatedRect rect2(Point2f(10, 10), Size2f(2, 2), 34.0f);
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_NONE);
CV_Assert(vertices.empty());
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_2)
{
// partial intersection, rectangles translated
RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f);
RotatedRect rect2(Point2f(1, 1), Size2f(2, 2), 0.0f);
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
vector<Point2f> targetVertices(4);
targetVertices[0] = Point2f(1.0f, 0.0f);
targetVertices[1] = Point2f(1.0f, 1.0f);
targetVertices[2] = Point2f(0.0f, 1.0f);
targetVertices[3] = Point2f(0.0f, 0.0f);
compare(vertices, targetVertices);
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_3)
{
// partial intersection, rectangles rotated 45 degree on the corner, forms a triangle intersection
RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f);
RotatedRect rect2(Point2f(1, 1), Size2f(sqrt(2.0f), 20), 45.0f);
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
vector<Point2f> targetVertices(3);
targetVertices[0] = Point2f(1.0f, 0.0f);
targetVertices[1] = Point2f(1.0f, 1.0f);
targetVertices[2] = Point2f(0.0f, 1.0f);
compare(vertices, targetVertices);
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_4)
{
// full intersection, rectangles of same size directly on top of each other
RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f);
RotatedRect rect2(Point2f(0, 0), Size2f(2, 2), 0.0f);
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_FULL);
vector<Point2f> targetVertices(4);
targetVertices[0] = Point2f(-1.0f, 1.0f);
targetVertices[1] = Point2f(-1.0f, -1.0f);
targetVertices[2] = Point2f(1.0f, -1.0f);
targetVertices[3] = Point2f(1.0f, 1.0f);
compare(vertices, targetVertices);
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_5)
{
// partial intersection, rectangle on top rotated 45 degrees
RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f);
RotatedRect rect2(Point2f(0, 0), Size2f(2, 2), 45.0f);
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
vector<Point2f> targetVertices(8);
targetVertices[0] = Point2f(-1.0f, -0.414214f);
targetVertices[1] = Point2f(-0.414214f, -1.0f);
targetVertices[2] = Point2f(0.414214f, -1.0f);
targetVertices[3] = Point2f(1.0f, -0.414214f);
targetVertices[4] = Point2f(1.0f, 0.414214f);
targetVertices[5] = Point2f(0.414214f, 1.0f);
targetVertices[6] = Point2f(-0.414214f, 1.0f);
targetVertices[7] = Point2f(-1.0f, 0.414214f);
compare(vertices, targetVertices);
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_6)
{
// 6 - partial intersection, rectangle on top of different size
RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f);
RotatedRect rect2(Point2f(0, 0), Size2f(2, 10), 0.0f);
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
vector<Point2f> targetVertices(4);
targetVertices[0] = Point2f(-1.0f, -1.0f);
targetVertices[1] = Point2f(1.0f, -1.0f);
targetVertices[2] = Point2f(1.0f, 1.0f);
targetVertices[3] = Point2f(-1.0f, 1.0f);
compare(vertices, targetVertices);
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_7)
{
// full intersection, rectangle fully enclosed in the other
RotatedRect rect1(Point2f(0, 0), Size2f(12.34f, 56.78f), 0.0f);
RotatedRect rect2(Point2f(0, 0), Size2f(2, 2), 0.0f);
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_FULL);
vector<Point2f> targetVertices(4);
targetVertices[0] = Point2f(-1.0f, 1.0f);
targetVertices[1] = Point2f(-1.0f, -1.0f);
targetVertices[2] = Point2f(1.0f, -1.0f);
targetVertices[3] = Point2f(1.0f, 1.0f);
compare(vertices, targetVertices);
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_8)
{
// intersection by a single vertex
RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f);
RotatedRect rect2(Point2f(2, 2), Size2f(2, 2), 0.0f);
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
compare(vertices, vector<Point2f>(1, Point2f(1.0f, 1.0f)));
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_9)
{
// full intersection, rectangle fully enclosed in the other
RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f);
RotatedRect rect2(Point2f(2, 0), Size2f(2, 123.45f), 0.0f);
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
vector<Point2f> targetVertices(2);
targetVertices[0] = Point2f(1.0f, -1.0f);
targetVertices[1] = Point2f(1.0f, 1.0f);
compare(vertices, targetVertices);
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_10)
{
// three points of rect2 are inside rect1.
RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f);
RotatedRect rect2(Point2f(0, 0.5), Size2f(1, 1), 45.0f);
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
vector<Point2f> targetVertices(5);
targetVertices[0] = Point2f(0.207107f, 1.0f);
targetVertices[1] = Point2f(-0.207107f, 1.0f);
targetVertices[2] = Point2f(-0.707107f, 0.5f);
targetVertices[3] = Point2f(0.0f, -0.207107f);
targetVertices[4] = Point2f(0.707107f, 0.5f);
compare(vertices, targetVertices);
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_11)
{
RotatedRect rect1(Point2f(0, 0), Size2f(4, 2), 0.0f);
RotatedRect rect2(Point2f(0, 0), Size2f(2, 2), -45.0f);
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
vector<Point2f> targetVertices(6);
targetVertices[0] = Point2f(-0.414214f, -1.0f);
targetVertices[1] = Point2f(0.414213f, -1.0f);
targetVertices[2] = Point2f(1.41421f, 0.0f);
targetVertices[3] = Point2f(0.414214f, 1.0f);
targetVertices[4] = Point2f(-0.414213f, 1.0f);
targetVertices[5] = Point2f(-1.41421f, 0.0f);
compare(vertices, targetVertices);
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_12)
{
RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f);
RotatedRect rect2(Point2f(0, 1), Size2f(1, 1), 0.0f);
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
vector<Point2f> targetVertices(4);
targetVertices[0] = Point2f(-0.5f, 1.0f);
targetVertices[1] = Point2f(-0.5f, 0.5f);
targetVertices[2] = Point2f(0.5f, 0.5f);
targetVertices[3] = Point2f(0.5f, 1.0f);
compare(vertices, targetVertices);
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_13)
{
RotatedRect rect1(Point2f(0, 0), Size2f(1, 3), 0.0f);
RotatedRect rect2(Point2f(0, 1), Size2f(3, 1), 0.0f);
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
vector<Point2f> targetVertices(4);
targetVertices[0] = Point2f(-0.5f, 0.5f);
targetVertices[1] = Point2f(0.5f, 0.5f);
targetVertices[2] = Point2f(0.5f, 1.5f);
targetVertices[3] = Point2f(-0.5f, 1.5f);
compare(vertices, targetVertices);
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_14)
{
const int kNumTests = 100;
const float kWidth = 5;
const float kHeight = 5;
RotatedRect rects[2];
std::vector<Point2f> inter;
cv::RNG& rng = cv::theRNG();
for (int i = 0; i < kNumTests; ++i)
{
for (int j = 0; j < 2; ++j)
{
rects[j].center = Point2f(rng.uniform(0.0f, kWidth), rng.uniform(0.0f, kHeight));
rects[j].size = Size2f(rng.uniform(1.0f, kWidth), rng.uniform(1.0f, kHeight));
rects[j].angle = rng.uniform(0.0f, 360.0f);
}
int res = rotatedRectangleIntersection(rects[0], rects[1], inter);
EXPECT_TRUE(res == INTERSECT_NONE || res == INTERSECT_PARTIAL || res == INTERSECT_FULL) << res;
ASSERT_TRUE(inter.size() < 4 || isContourConvex(inter)) << inter;
}
}
TEST(Imgproc_RotatedRectangleIntersection, regression_12221_1)
{
RotatedRect r1(
Point2f(259.65081787109375, 51.58895492553711),
Size2f(5487.8779296875, 233.8921661376953),
-29.488616943359375);
RotatedRect r2(
Point2f(293.70465087890625, 112.10154724121094),
Size2f(5487.8896484375, 234.87368774414062),
-31.27001953125);
std::vector<Point2f> intersections;
int interType = cv::rotatedRectangleIntersection(r1, r2, intersections);
EXPECT_EQ(INTERSECT_PARTIAL, interType);
EXPECT_LE(intersections.size(), (size_t)8);
}
TEST(Imgproc_RotatedRectangleIntersection, regression_12221_2)
{
RotatedRect r1(
Point2f(239.78500366210938, 515.72021484375),
Size2f(70.23420715332031, 39.74684524536133),
-42.86162567138672);
RotatedRect r2(
Point2f(242.4205322265625, 510.1195373535156),
Size2f(66.85948944091797, 61.46455383300781),
-9.840961456298828);
std::vector<Point2f> intersections;
int interType = cv::rotatedRectangleIntersection(r1, r2, intersections);
EXPECT_EQ(INTERSECT_PARTIAL, interType);
EXPECT_LE(intersections.size(), (size_t)8);
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_21659)
{
float scaleFactor = 1000;//to challenge the normalizationScale in the algorithm
cv::RectanglesIntersectTypes intersectionResult = cv::RectanglesIntersectTypes::INTERSECT_NONE;
std::vector<cv::Point2f> intersection;
double intersectionArea = 0;
cv::RotatedRect r1 = cv::RotatedRect(cv::Point2f(.5f, .5f)*scaleFactor, cv::Size2f(1.f, 1.f)*scaleFactor, 0);
cv::RotatedRect r2;
r2 = cv::RotatedRect(cv::Point2f(-2.f, -2.f)*scaleFactor, cv::Size2f(1.f, 1.f)*scaleFactor, 0);
intersectionResult = (cv::RectanglesIntersectTypes) cv::rotatedRectangleIntersection(r1, r2, intersection);
intersectionArea = (intersection.size() <= 2) ? 0. : cv::contourArea(intersection);
ASSERT_EQ(cv::RectanglesIntersectTypes::INTERSECT_NONE, intersectionResult);
ASSERT_LE(std::abs(intersectionArea-0), 1e-1);
r2 = cv::RotatedRect(cv::Point2f(1.5f, .5f)*scaleFactor, cv::Size2f(1.f, 2.f)*scaleFactor, 0);
intersectionResult = (cv::RectanglesIntersectTypes) cv::rotatedRectangleIntersection(r1, r2, intersection);
intersectionArea = (intersection.size() <= 2) ? 0. : cv::contourArea(intersection);
ASSERT_EQ(cv::RectanglesIntersectTypes::INTERSECT_PARTIAL, intersectionResult);
ASSERT_LE(std::abs(intersectionArea-0), 1e-1);
r2 = cv::RotatedRect(cv::Point2f(1.5f, 1.5f)*scaleFactor, cv::Size2f(1.f, 1.f)*scaleFactor, 0);
intersectionResult = (cv::RectanglesIntersectTypes) cv::rotatedRectangleIntersection(r1, r2, intersection);
intersectionArea = (intersection.size() <= 2) ? 0. : cv::contourArea(intersection);
ASSERT_EQ(cv::RectanglesIntersectTypes::INTERSECT_PARTIAL, intersectionResult);
ASSERT_LE(std::abs(intersectionArea-0), 1e-1);
r2 = cv::RotatedRect(cv::Point2f(.5f, .5f)*scaleFactor, cv::Size2f(1.f, 1.f)*scaleFactor, 0);
intersectionResult = (cv::RectanglesIntersectTypes) cv::rotatedRectangleIntersection(r1, r2, intersection);
intersectionArea = (intersection.size() <= 2) ? 0. : cv::contourArea(intersection);
ASSERT_EQ(cv::RectanglesIntersectTypes::INTERSECT_FULL, intersectionResult);
ASSERT_LE(std::abs(intersectionArea-r2.size.area()), 1e-1);
r2 = cv::RotatedRect(cv::Point2f(.5f, .5f)*scaleFactor, cv::Size2f(.5f, .5f)*scaleFactor, 0);
intersectionResult = (cv::RectanglesIntersectTypes) cv::rotatedRectangleIntersection(r1, r2, intersection);
intersectionArea = (intersection.size() <= 2) ? 0. : cv::contourArea(intersection);
ASSERT_EQ(cv::RectanglesIntersectTypes::INTERSECT_FULL, intersectionResult);
ASSERT_LE(std::abs(intersectionArea-r2.size.area()), 1e-1);
r2 = cv::RotatedRect(cv::Point2f(.5f, .5f)*scaleFactor, cv::Size2f(2.f, .5f)*scaleFactor, 0);
intersectionResult = (cv::RectanglesIntersectTypes) cv::rotatedRectangleIntersection(r1, r2, intersection);
intersectionArea = (intersection.size() <= 2) ? 0. : cv::contourArea(intersection);
ASSERT_EQ(cv::RectanglesIntersectTypes::INTERSECT_PARTIAL, intersectionResult);
ASSERT_LE(std::abs(intersectionArea-500000), 1e-1);
r2 = cv::RotatedRect(cv::Point2f(.5f, .5f)*scaleFactor, cv::Size2f(1.f, 1.f)*scaleFactor, 45);
intersectionResult = (cv::RectanglesIntersectTypes) cv::rotatedRectangleIntersection(r1, r2, intersection);
intersectionArea = (intersection.size() <= 2) ? 0. : cv::contourArea(intersection);
ASSERT_EQ(cv::RectanglesIntersectTypes::INTERSECT_PARTIAL, intersectionResult);
ASSERT_LE(std::abs(intersectionArea-828427), 1e-1);
r2 = cv::RotatedRect(cv::Point2f(1.f, 1.f)*scaleFactor, cv::Size2f(1.f, 1.f)*scaleFactor, 45);
intersectionResult = (cv::RectanglesIntersectTypes) cv::rotatedRectangleIntersection(r1, r2, intersection);
intersectionArea = (intersection.size() <= 2) ? 0. : cv::contourArea(intersection);
ASSERT_EQ(cv::RectanglesIntersectTypes::INTERSECT_PARTIAL, intersectionResult);
ASSERT_LE(std::abs(intersectionArea-250000), 1e-1);
//see #21659
r1 = cv::RotatedRect(cv::Point2f(4.48589373f, 12.5545063f), cv::Size2f(4.0f, 4.0f), 0.0347290039f);
r2 = cv::RotatedRect(cv::Point2f(4.48589373f, 12.5545235f), cv::Size2f(4.0f, 4.0f), 0.0347290039f);
intersectionResult = (cv::RectanglesIntersectTypes) cv::rotatedRectangleIntersection(r1, r2, intersection);
intersectionArea = (intersection.size() <= 2) ? 0. : cv::contourArea(intersection);
ASSERT_EQ(cv::RectanglesIntersectTypes::INTERSECT_PARTIAL, intersectionResult);
ASSERT_LE(std::abs(intersectionArea-r1.size.area()), 1e-3);
r1 = cv::RotatedRect(cv::Point2f(4.48589373f, 12.5545063f + 0.01f), cv::Size2f(4.0f, 4.0f), 0.0347290039f);
r2 = cv::RotatedRect(cv::Point2f(4.48589373f, 12.5545235f), cv::Size2f(4.0f, 4.0f), 0.0347290039f);
intersectionResult = (cv::RectanglesIntersectTypes) cv::rotatedRectangleIntersection(r1, r2, intersection);
intersectionArea = (intersection.size() <= 2) ? 0. : cv::contourArea(intersection);
ASSERT_LE(std::abs(intersectionArea-r1.size.area()), 1e-1);
r1 = cv::RotatedRect(cv::Point2f(45.0715866f, 39.8825722f), cv::Size2f(3.0f, 3.0f), 0.10067749f);
r2 = cv::RotatedRect(cv::Point2f(45.0715866f, 39.8825874f), cv::Size2f(3.0f, 3.0f), 0.10067749f);
intersectionResult = (cv::RectanglesIntersectTypes) cv::rotatedRectangleIntersection(r1, r2, intersection);
intersectionArea = (intersection.size() <= 2) ? 0. : cv::contourArea(intersection);
ASSERT_LE(std::abs(intersectionArea-r1.size.area()), 1e-3);
}
TEST(Imgproc_RotatedRectangleIntersection, regression_18520)
{
RotatedRect rr_empty(
Point2f(2, 2),
Size2f(0, 0), // empty
0);
RotatedRect rr(
Point2f(50, 50),
Size2f(4, 4),
0);
{
std::vector<Point2f> intersections;
int interType = cv::rotatedRectangleIntersection(rr_empty, rr, intersections);
EXPECT_EQ(INTERSECT_NONE, interType) << "rr_empty, rr";
EXPECT_EQ((size_t)0, intersections.size()) << "rr_empty, rr";
}
{
std::vector<Point2f> intersections;
int interType = cv::rotatedRectangleIntersection(rr, rr_empty, intersections);
EXPECT_EQ(INTERSECT_NONE, interType) << "rr, rr_empty";
EXPECT_EQ((size_t)0, intersections.size()) << "rr, rr_empty";
}
}
TEST(Imgproc_RotatedRectangleIntersection, regression_19824)
{
RotatedRect r1(
Point2f(246805.033f, 4002326.94f),
Size2f(26.40587f, 6.20026f),
-62.10156f);
RotatedRect r2(
Point2f(246805.122f, 4002326.59f),
Size2f(27.4821f, 8.5361f),
-56.33761f);
std::vector<Point2f> intersections;
int interType = cv::rotatedRectangleIntersection(r1, r2, intersections);
EXPECT_EQ(INTERSECT_PARTIAL, interType);
EXPECT_LE(intersections.size(), (size_t)7);
}
TEST(Imgproc_RotatedRectangleIntersection, accuracy_23546)
{
RotatedRect r1(
Point2f(824.6421183672817f, 280.28737007069833f),
Size2f(565.0f, 140.0f),
-177.80506896972656f);
RotatedRect r2(
Point2f(567.3310438828003f, 270.42527355719545f),
Size2f(275.0f, 50.0f),
92.19493103027344f);
std::vector<Point2f> intersection;
double intersectionArea = 0;
int interType = cv::rotatedRectangleIntersection(r1, r2, intersection);
intersectionArea = (intersection.size() <= 2) ? 0. : cv::contourArea(intersection);
EXPECT_EQ(INTERSECT_PARTIAL, interType);
EXPECT_EQ(intersection.size(), (size_t)4);
ASSERT_LE(std::abs(intersectionArea-7000), 1e-1);
}
}} // namespace
+10
View File
@@ -0,0 +1,10 @@
// 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 "test_precomp.hpp"
#if defined(HAVE_HPX)
#include <hpx/hpx_main.hpp>
#endif
CV_TEST_MAIN("")
+231
View File
@@ -0,0 +1,231 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// 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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, 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:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of Intel Corporation may not 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 the Intel Corporation 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 "test_precomp.hpp"
#if 0
#include "_modelest.h"
using namespace std;
using namespace cv;
class BareModelEstimator : public CvModelEstimator2
{
public:
BareModelEstimator(int modelPoints, CvSize modelSize, int maxBasicSolutions);
virtual int runKernel( const CvMat*, const CvMat*, CvMat* );
virtual void computeReprojError( const CvMat*, const CvMat*,
const CvMat*, CvMat* );
bool checkSubsetPublic( const CvMat* ms1, int count, bool checkPartialSubset );
};
BareModelEstimator::BareModelEstimator(int _modelPoints, CvSize _modelSize, int _maxBasicSolutions)
:CvModelEstimator2(_modelPoints, _modelSize, _maxBasicSolutions)
{
}
int BareModelEstimator::runKernel( const CvMat*, const CvMat*, CvMat* )
{
return 0;
}
void BareModelEstimator::computeReprojError( const CvMat*, const CvMat*,
const CvMat*, CvMat* )
{
}
bool BareModelEstimator::checkSubsetPublic( const CvMat* ms1, int count, bool checkPartialSubset )
{
checkPartialSubsets = checkPartialSubset;
return checkSubset(ms1, count);
}
class CV_ModelEstimator2_Test : public cvtest::ArrayTest
{
public:
CV_ModelEstimator2_Test();
protected:
void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types );
void fill_array( int test_case_idx, int i, int j, Mat& arr );
double get_success_error_level( int test_case_idx, int i, int j );
void run_func();
void prepare_to_validation( int test_case_idx );
bool checkPartialSubsets;
int usedPointsCount;
bool checkSubsetResult;
int generalPositionsCount;
int maxPointsCount;
};
CV_ModelEstimator2_Test::CV_ModelEstimator2_Test()
{
generalPositionsCount = get_test_case_count() / 2;
maxPointsCount = 100;
test_array[INPUT].push_back(NULL);
test_array[OUTPUT].push_back(NULL);
test_array[REF_OUTPUT].push_back(NULL);
}
void CV_ModelEstimator2_Test::get_test_array_types_and_sizes( int /*test_case_idx*/,
vector<vector<Size> > &sizes, vector<vector<int> > &types )
{
RNG &rng = ts->get_rng();
checkPartialSubsets = (cvtest::randInt(rng) % 2 == 0);
int pointsCount = cvtest::randInt(rng) % maxPointsCount;
usedPointsCount = pointsCount == 0 ? 0 : cvtest::randInt(rng) % pointsCount;
sizes[INPUT][0] = cvSize(1, pointsCount);
types[INPUT][0] = CV_64FC2;
sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = cvSize(1, 1);
types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_8UC1;
}
void CV_ModelEstimator2_Test::fill_array( int test_case_idx, int i, int j, Mat& arr )
{
if( i != INPUT )
{
cvtest::ArrayTest::fill_array( test_case_idx, i, j, arr );
return;
}
if (test_case_idx < generalPositionsCount)
{
//generate points in a general position (i.e. no three points can lie on the same line.)
bool isGeneralPosition;
do
{
ArrayTest::fill_array(test_case_idx, i, j, arr);
//a simple check that the position is general:
// for each line check that all other points don't belong to it
isGeneralPosition = true;
for (int startPointIndex = 0; startPointIndex < usedPointsCount && isGeneralPosition; startPointIndex++)
{
for (int endPointIndex = startPointIndex + 1; endPointIndex < usedPointsCount && isGeneralPosition; endPointIndex++)
{
for (int testPointIndex = 0; testPointIndex < usedPointsCount && isGeneralPosition; testPointIndex++)
{
if (testPointIndex == startPointIndex || testPointIndex == endPointIndex)
{
continue;
}
CV_Assert(arr.type() == CV_64FC2);
Point2d tangentVector_1 = arr.at<Point2d>(endPointIndex) - arr.at<Point2d>(startPointIndex);
Point2d tangentVector_2 = arr.at<Point2d>(testPointIndex) - arr.at<Point2d>(startPointIndex);
const float eps = 1e-4f;
//TODO: perhaps it is better to normalize the cross product by norms of the tangent vectors
if (fabs(tangentVector_1.cross(tangentVector_2)) < eps)
{
isGeneralPosition = false;
}
}
}
}
}
while(!isGeneralPosition);
}
else
{
//create points in a degenerate position (there are at least 3 points belonging to the same line)
ArrayTest::fill_array(test_case_idx, i, j, arr);
if (usedPointsCount <= 2)
{
return;
}
RNG &rng = ts->get_rng();
int startPointIndex, endPointIndex, modifiedPointIndex;
do
{
startPointIndex = cvtest::randInt(rng) % usedPointsCount;
endPointIndex = cvtest::randInt(rng) % usedPointsCount;
modifiedPointIndex = checkPartialSubsets ? usedPointsCount - 1 : cvtest::randInt(rng) % usedPointsCount;
}
while (startPointIndex == endPointIndex || startPointIndex == modifiedPointIndex || endPointIndex == modifiedPointIndex);
double startWeight = cvtest::randReal(rng);
CV_Assert(arr.type() == CV_64FC2);
arr.at<Point2d>(modifiedPointIndex) = startWeight * arr.at<Point2d>(startPointIndex) + (1.0 - startWeight) * arr.at<Point2d>(endPointIndex);
}
}
double CV_ModelEstimator2_Test::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ )
{
return 0;
}
void CV_ModelEstimator2_Test::prepare_to_validation( int test_case_idx )
{
test_mat[OUTPUT][0].at<uchar>(0) = checkSubsetResult;
test_mat[REF_OUTPUT][0].at<uchar>(0) = test_case_idx < generalPositionsCount || usedPointsCount <= 2;
}
void CV_ModelEstimator2_Test::run_func()
{
//make the input continuous
Mat input = test_mat[INPUT][0].clone();
CvMat _input = input;
RNG &rng = ts->get_rng();
int modelPoints = cvtest::randInt(rng);
CvSize modelSize = cvSize(2, modelPoints);
int maxBasicSolutions = cvtest::randInt(rng);
BareModelEstimator modelEstimator(modelPoints, modelSize, maxBasicSolutions);
checkSubsetResult = modelEstimator.checkSubsetPublic(&_input, usedPointsCount, checkPartialSubsets);
}
TEST(Calib3d_ModelEstimator2, accuracy) { CV_ModelEstimator2_Test test; test.safe_run(); }
#endif
+91
View File
@@ -0,0 +1,91 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// 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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, 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:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of Intel Corporation may not 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 the Intel Corporation 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 "test_precomp.hpp"
#include "opencv2/ts/ocl_test.hpp"
namespace opencv_test { namespace {
class CV_SmallContourMomentTest : public cvtest::BaseTest
{
public:
CV_SmallContourMomentTest() {}
~CV_SmallContourMomentTest() {}
protected:
void run(int)
{
try
{
vector<Point> points;
points.push_back(Point(50, 56));
points.push_back(Point(53, 53));
points.push_back(Point(46, 54));
points.push_back(Point(49, 51));
Moments m = moments(points, false);
double area = contourArea(points);
CV_Assert( m.m00 == 0 && m.m01 == 0 && m.m10 == 0 && area == 0 );
}
catch(...)
{
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
}
}
};
TEST(Imgproc_ContourMoment, small) { CV_SmallContourMomentTest test; test.safe_run(); }
TEST(Imgproc_Moments, degenerateContours)
{
std::vector<cv::Point> c1;
c1.push_back(cv::Point(10,10));
cv::Moments m1 = cv::moments(c1, false);
EXPECT_EQ(m1.m00, 0);
std::vector<cv::Point> c2;
c2.push_back(cv::Point(0,0));
c2.push_back(cv::Point(5,5));
c2.push_back(cv::Point(10,10));
cv::Moments m2 = cv::moments(c2, false);
EXPECT_EQ(m2.m00, 0);
}
}} // namespace
+402
View File
@@ -0,0 +1,402 @@
// 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 "test_precomp.hpp"
#include <opencv2/geometry/mst.hpp>
namespace opencv_test {
namespace {
using namespace cv;
typedef tuple<MSTAlgorithm /*MSTalgorithm*/,
int /*numNodes*/,
std::vector<MSTEdge>/*edges*/,
std::vector<MSTEdge>/*expectedEdges*/
> MSTParamType;
typedef testing::TestWithParam<MSTParamType> MST;
TEST_P(MST, checkCorrectness)
{
const int algorithm = get<0>(GetParam());
const int numNodes = get<1>(GetParam());
const std::vector<MSTEdge>& edges = get<2>(GetParam());
const std::vector<MSTEdge>& expectedEdges = get<3>(GetParam());
std::vector<MSTEdge> mstEdges;
bool result = false;
switch (algorithm) {
case MST_PRIM:
// Select first node for root
result = buildMST(numNodes, edges, mstEdges, MST_PRIM, 0);
break;
case MST_KRUSKAL:
result = buildMST(numNodes, edges, mstEdges, MST_KRUSKAL, 0);
break;
default:
FAIL() << "Unknown selected MST algorithm: " << algorithm;
}
EXPECT_TRUE(result);
EXPECT_EQ(mstEdges.size(), expectedEdges.size());
for (const auto& edge : expectedEdges)
{
auto it = std::find_if(mstEdges.begin(), mstEdges.end(), [&edge](const MSTEdge& e) {
return (e.source == edge.source && e.target == edge.target) ||
(e.source == edge.target && e.target == edge.source);
});
EXPECT_TRUE(it != mstEdges.end()) << "Missing expected edge: "
<< edge.source << " -> " << edge.target;
}
}
const MSTParamType mst_graphs[] =
{
// Small Graph
MSTParamType(MST_PRIM, 4,
{
{0, 1, 1.0}, {0, 2, 2.0}, {1, 2, 1.5}, {1, 3, 2.5}, {2, 3, 1.0}
},
{
{0, 1, 1.0}, {1, 2, 1.5}, {2, 3, 1.0}
}
),
MSTParamType(MST_KRUSKAL, 4,
{
{0, 1, 1.0}, {0, 2, 2.0}, {1, 2, 1.5}, {1, 3, 2.5}, {2, 3, 1.0}
},
{
{0, 1, 1.0}, {1, 2, 1.5}, {2, 3, 1.0}
}
),
// 2 Nodes, 1 Edge
MSTParamType(MST_PRIM, 2,
{
{0, 1, 42.0}
},
{
{0, 1, 42.0}
}
),
MSTParamType(MST_KRUSKAL, 2,
{
{0, 1, 42.0}
},
{
{0, 1, 42.0}
}
),
// Dense graph (clique)
MSTParamType(MST_PRIM, 4,
{
{0, 1, 1.0}, {0, 2, 2.0}, {0, 3, 3.0},
{1, 2, 1.5}, {1, 3, 2.5}, {2, 3, 1.0}
},
{
{0, 1, 1.0}, {2, 3, 1.0}, {1, 2, 1.5}
}
),
MSTParamType(MST_KRUSKAL, 4,
{
{0, 1, 1.0}, {0, 2, 2.0}, {0, 3, 3.0},
{1, 2, 1.5}, {1, 3, 2.5}, {2, 3, 1.0}
},
{
{0, 1, 1.0}, {2, 3, 1.0}, {1, 2, 1.5}
}
),
// Sparse
MSTParamType(MST_PRIM, 4,
{
{0, 1, 1.0}, {1, 2, 2.0}, {1, 3, 3.0}
},
{
{0, 1, 1.0}, {1, 2, 2.0}, {1, 3, 3.0}
}
),
MSTParamType(MST_KRUSKAL, 4,
{
{0, 1, 1.0}, {1, 2, 2.0}, {1, 3, 3.0}
},
{
{0, 1, 1.0}, {1, 2, 2.0}, {1, 3, 3.0}
}
),
// Weight Floating point check
MSTParamType(MST_PRIM, 3,
{
{0, 1, 1.000001}, {1, 2, 1.000002}, {0, 2, 1.000003}
},
{
{0, 1, 1.000001}, {1, 2, 1.000002}
}
),
MSTParamType(MST_KRUSKAL, 3,
{
{0, 1, 1.000001}, {1, 2, 1.000002}, {0, 2, 1.000003}
},
{
{0, 1, 1.000001}, {1, 2, 1.000002}
}
),
// 0 or ~0 weight valuess
MSTParamType(MST_PRIM, 3,
{
{0, 1, 0.0}, {1, 2, 1e-9}, {0, 2, 1.0}
},
{
{0, 1, 0.0}, {1, 2, 1e-9}
}
),
MSTParamType(MST_KRUSKAL, 3,
{
{0, 1, 0.0}, {1, 2, 1e-9}, {0, 2, 1.0}
},
{
{0, 1, 0.0}, {1, 2, 1e-9}
}
),
// Duplicate edges (picks the one with the smallest weight)
MSTParamType(MST_PRIM, 3,
{
{0, 1, 3.0}, {0, 1, 1.0}, {1, 2, 2.0}
},
{
{0, 1, 1.0}, {1, 2, 2.0}
}
),
MSTParamType(MST_KRUSKAL, 3,
{
{0, 1, 3.0}, {0, 1, 1.0}, {1, 2, 2.0}
},
{
{0, 1, 1.0}, {1, 2, 2.0}
}
),
// Negative weights
MSTParamType(MST_PRIM, 3,
{
{0, 1, -1.0}, {1, 2, -2.0}, {0, 2, -3.0}
},
{
{1, 2, -2.0}, {0, 2, -3.0}
}
),
MSTParamType(MST_KRUSKAL, 3,
{
{0, 1, -1.0}, {1, 2, -2.0}, {0, 2, -3.0}
},
{
{0, 2, -3.0}, {1, 2, -2.0}
}
),
};
inline static std::string MST_name_printer(const testing::TestParamInfo<MST::ParamType>& info)
{
std::ostringstream os;
const auto& algorithm = get<0>(info.param);
const auto& numNodes = get<1>(info.param);
const auto& edges = get<2>(info.param);
const auto& expectedEdges = get<3>(info.param);
os << "TestCase_" << info.index << "_";
switch (algorithm)
{
case MST_PRIM: os << "Prim"; break;
case MST_KRUSKAL: os << "Kruskal"; break;
default: os << "Unknown algorithm"; break;
}
os << "_Nodes_" << numNodes;
os << "_Edges_" << edges.size();
os << "_ExpectedEdges_" << expectedEdges.size();
return os.str();
}
INSTANTIATE_TEST_CASE_P(/**/, MST, testing::ValuesIn(mst_graphs), MST_name_printer);
TEST(MSTStress, largeGraph)
{
const int numNodes = 100000;
std::vector<MSTEdge> edges;
for (int i = 0; i < numNodes - 1; ++i)
edges.push_back({i, i + 1, static_cast<double>(i + 1)});
// Add extra edges for complexity
for (int i = 0; i < numNodes - 10; i += 10)
edges.push_back({i, i + 10, static_cast<double>(i)});
for (int i = 0; i + 20 < numNodes; i += 5)
edges.push_back({i, i + 20, static_cast<double>(i + 1)});
for (int i = 0; i + 30 < numNodes; i += 3)
edges.push_back({i, i + 30, static_cast<double>(i % 50 + 1)});
for (int i = 50; i < numNodes; i += 10)
edges.push_back({i, i - 25, static_cast<double>(i % 100 + 2)});
std::vector<MSTEdge> primMST, kruskalMST;
bool resultPrim = buildMST(numNodes, edges, primMST, MST_PRIM, 0);
bool resultKruskal = buildMST(numNodes, edges, kruskalMST, MST_KRUSKAL, 0);
EXPECT_TRUE(resultPrim) << "Prim's algorithm failed for large graph ( "
<< numNodes << " nodes & " << edges.size() << " edges)";
EXPECT_TRUE(resultKruskal) << "Kruskal's algorithm failed for large graph ( "
<< numNodes << " nodes & " << edges.size() << " edges)";
EXPECT_EQ(primMST.size(), static_cast<size_t>(numNodes - 1))
<< "Prim's MST size incorrect for large graph: expected "
<< (numNodes - 1) << " edges, got " << primMST.size() << ".";
EXPECT_EQ(kruskalMST.size(), static_cast<size_t>(numNodes - 1))
<< "Kruskal's MST size incorrect for large graph: expected "
<< (numNodes - 1) << " edges, got " << kruskalMST.size() << ".";
}
typedef tuple<int /*numNodes*/,
std::vector<MSTEdge>/*edges*/
> MSTNegativeParamType;
typedef testing::TestWithParam<MSTNegativeParamType> MSTNegative;
TEST_P(MSTNegative, disconnectedGraphs)
{
const int numNodes = get<0>(GetParam());
const std::vector<MSTEdge>& edges = get<1>(GetParam());
std::vector<MSTEdge> primMST, kruskalMST;
bool resultPrim = buildMST(numNodes, edges, primMST, MST_PRIM, 0);
bool resultKruskal = buildMST(numNodes, edges, kruskalMST, MST_KRUSKAL, 0);
EXPECT_FALSE(resultPrim) << "Prim's algorithm should fail for disconnected graphs ( "
<< numNodes << " nodes & " << edges.size() << " edges)";
EXPECT_FALSE(resultKruskal) << "Kruskal's algorithm should fail for disconnected graphs ( "
<< numNodes << " nodes & " << edges.size() << " edges)";
EXPECT_LT(primMST.size(), static_cast<size_t>(numNodes - 1))
<< "Prim's MST should not contain " << numNodes - 1 << " edges for disconnected graphs";
EXPECT_LT(kruskalMST.size(), static_cast<size_t>(numNodes - 1))
<< "Kruskal's MST should not contain " << numNodes - 1 << " edges for disconnected graphs";
}
const MSTNegativeParamType mst_negative[] =
{
// Two disconnected subgraphs
MSTNegativeParamType(5,
{
// Subgraph 1: 0-1
{0, 1, 1.0},
// Subgraph 2: 2-3-4
{2, 3, 2.0}, {3, 4, 3.0}
}
),
MSTNegativeParamType(8,
{
// Subgraph 1: 0-1-2-3
{0, 1, 1.0}, {1, 2, 2.0}, {2, 3, 3.0},
// Subgraph 2: 4-5-6-7
{4, 5, 4.0}, {5, 6, 5.0}, {6, 7, 6.0}
}
),
MSTNegativeParamType(4,
{
// Subgraph 1: 0-1
{0, 1, 1.0},
// Subgraph 2: 2-3
{2, 3, 1.0},
// self-loop edges (should be ignored by the algorithms)
{0, 0, 1.0}, {1, 1, 1.0}, {2, 2, 1.0}, {3, 3, 1.0}
}
),
// Three disconnected components
MSTNegativeParamType(6,
{
// Subgraph 1: 0-1
{0, 1, 1.0},
// Subgraph 2: 2-3
{2, 3, 1.0},
// Subgraph 3: 4-5
{4, 5, 1.0}
}
),
MSTNegativeParamType(9,
{
// Subgraph 1: 0-1-2-3
{0, 1, 1.0}, {1, 2, 1.0}, {2, 3, 1.0},
// Subgraph 2: 4-5-6
{4, 5, 1.0}, {5, 6, 1.0},
// Subgraph 3: 7-8
{7, 8, 1.0}
}
),
MSTNegativeParamType(8,
{
// Subgraph 1: 0-1-2
{0, 1, 1.0}, {1, 2, 1.0},
// Subgraph 2: 3-4-5
{3, 4, 1.0}, {4, 5, 1.0},
// Subgraph 3: 6-7
{6, 7, 1.0}
}
),
// Isolated nodes
MSTNegativeParamType(4,
{
// Subgraph 1: 0-1-2
{0, 1, 1.0}, {1, 2, 1.0},
// Isolated node: 3
}
),
MSTNegativeParamType(5,
{
// Subgraph 1: 0-1-2
{0, 1, 1.0}, {1, 2, 1.0},
// Isolated nodes: 3, 4
}
),
MSTNegativeParamType(3,
{
// Isolated node: 0, 1, 2 (no edges)
}
),
};
inline static std::string MST_negative_name_printer(const testing::TestParamInfo<MSTNegative::ParamType>& info) {
std::ostringstream os;
const auto& numNodes = get<0>(info.param);
const auto& edges = get<1>(info.param);
os << "TestCase_" << info.index << "_Nodes_" << numNodes;
os << "_Edges_" << edges.size();
return os.str();
}
INSTANTIATE_TEST_CASE_P(/**/, MSTNegative, testing::ValuesIn(mst_negative), MST_negative_name_printer);
}} // namespace
@@ -0,0 +1,64 @@
// 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.
//
// Copyright (C) 2022, Wanli Zhong <zhongwl2018@mail.sustech.edu.cn>
#include "test_precomp.hpp"
#include "test_ptcloud_utils.hpp"
#include "opencv2/flann.hpp"
namespace opencv_test { namespace {
TEST(NormalEstimationTest, PlaneNormalEstimation)
{
// generate a plane for test
Mat plane_pts;
vector<float> model({1, 2, 3, 4});
float thr = 0.f;
int num = 1000;
vector<float> limit({5, 55, 5, 55, 0, 0});
generatePlane(plane_pts, model, thr, num, limit);
// get knn search result
int k = 10;
Mat knn_idx(num, k, CV_32S);
// build kdtree
flann::Index tree(plane_pts, flann::KDTreeIndexParams());
tree.knnSearch(plane_pts, knn_idx, noArray(), k);
// estimate normal and curvature
vector<Point3f> normals;
vector<float> curvatures;
normalEstimate(normals, curvatures, plane_pts, knn_idx, k);
float theta_thr = 1.f; // threshold for degree of angle between normal of plane and normal of point
float curvature_thr = 0.01f; // threshold for curvature and actual curvature of the point
float actual_curvature = 0.f;
Point3f n1(model[0], model[1], model[2]);
float n1m = n1.dot(n1);
float total_theta = 0.f;
float total_diff_curvature = 0.f;
for (int i = 0; i < num; ++i)
{
float n12 = n1.dot(normals[i]);
float n2m = normals[i].dot(normals[i]);
float cos_theta = n12 / sqrt(n1m * n2m);
// accuracy problems caused by float numbers, need to be fixed
cos_theta = cos_theta > 1 ? 1 : cos_theta;
cos_theta = cos_theta < -1 ? -1 : cos_theta;
float theta = acos(abs(cos_theta));
total_theta += theta;
total_diff_curvature += abs(curvatures[i] - actual_curvature);
}
float avg_theta = total_theta / (float) num;
ASSERT_LE(avg_theta, theta_thr);
float avg_diff_curvature = total_diff_curvature / (float) num;
ASSERT_LE(avg_diff_curvature, curvature_thr);
}
} // namespace
} // opencv_test
+18
View File
@@ -0,0 +1,18 @@
// 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_TEST_PRECOMP_HPP__
#define __OPENCV_TEST_PRECOMP_HPP__
#include <functional>
#include <numeric>
#include "opencv2/ts.hpp"
#include "opencv2/geometry.hpp"
#include <opencv2/core/utils/logger.hpp>
#ifdef HAVE_OPENCL
#include <opencv2/core/ocl.hpp>
#endif
#endif
@@ -0,0 +1,110 @@
// 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.
//
// Copyright (C) 2021, Wanli Zhong <zhongwl2018@mail.sustech.edu.cn>
#include "test_ptcloud_utils.hpp"
namespace opencv_test {
void generatePlane(OutputArray plane_pts, const vector<float> &model, float thr, int num,
const vector<float> &limit)
{
if (plane_pts.channels() == 3 && plane_pts.isVector())
{
// std::vector<cv::Point3f>
plane_pts.create(1, num, CV_32FC3);
}
else
{
// cv::Mat
plane_pts.create(num, 3, CV_32F);
}
cv::RNG rng(0);
auto *plane_pts_ptr = (float *) plane_pts.getMat().data;
// Part of the points are generated for the specific model
// The other part of the points are used to increase the thickness of the plane
int std_num = (int) (num / 2);
// Difference of maximum d between two parallel planes
float d_thr = thr * sqrt(model[0] * model[0] + model[1] * model[1] + model[2] * model[2]);
for (int i = 0; i < num; i++)
{
// Let d change then generate thickness
float d = i < std_num ? model[3] : rng.uniform(model[3] - d_thr, model[3] + d_thr);
float x, y, z;
// c is 0 means the plane is vertical
if (model[2] == 0)
{
z = rng.uniform(limit[4], limit[5]);
if (model[0] == 0)
{
x = rng.uniform(limit[0], limit[1]);
y = -d / model[1];
}
else if (model[1] == 0)
{
x = -d / model[0];
y = rng.uniform(limit[2], limit[3]);
}
else
{
x = rng.uniform(limit[0], limit[1]);
y = -(model[0] * x + d) / model[1];
}
}
// c is not 0
else
{
x = rng.uniform(limit[0], limit[1]);
y = rng.uniform(limit[2], limit[3]);
z = -(model[0] * x + model[1] * y + d) / model[2];
}
plane_pts_ptr[3 * i] = x;
plane_pts_ptr[3 * i + 1] = y;
plane_pts_ptr[3 * i + 2] = z;
}
}
void generateSphere(OutputArray sphere_pts, const vector<float> &model, float thr, int num,
const vector<float> &limit)
{
if (sphere_pts.channels() == 3 && sphere_pts.isVector())
{
// std::vector<cv::Point3f>
sphere_pts.create(1, num, CV_32FC3);
}
else
{
// cv::Mat
sphere_pts.create(num, 3, CV_32F);
}
cv::RNG rng(0);
auto *sphere_pts_ptr = (float *) sphere_pts.getMat().data;
// Part of the points are generated for the specific model
// The other part of the points are used to increase the thickness of the sphere
int sphere_num = (int) (num / 1.5);
for (int i = 0; i < num; i++)
{
// Let r change then generate thickness
float r = i < sphere_num ? model[3] : rng.uniform(model[3] - thr, model[3] + thr);
// Generate a random vector and normalize it.
// Note: these vectors are not spread uniformly across the sphere
Vec3f vec(rng.uniform(limit[0], limit[1]), rng.uniform(limit[2], limit[3]),
rng.uniform(limit[4], limit[5]));
float l = sqrt(vec.dot(vec));
// Normalizes it to have a magnitude of r
vec /= l / r;
sphere_pts_ptr[3 * i] = model[0] + vec[0];
sphere_pts_ptr[3 * i + 1] = model[1] + vec[1];
sphere_pts_ptr[3 * i + 2] = model[2] + vec[2];
}
}
} // opencv_test
@@ -0,0 +1,43 @@
// 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.
//
// Copyright (C) 2021, Wanli Zhong <zhongwl2018@mail.sustech.edu.cn>
#ifndef OPENCV_TEST_PTCLOUD_UTILS_HPP
#define OPENCV_TEST_PTCLOUD_UTILS_HPP
#include "test_precomp.hpp"
namespace opencv_test {
/**
* @brief Generate a specific plane with random points.
*
* @param[out] plane_pts Point cloud of plane, only support vector<Point3f> or Mat with Nx3 layout
* in memory.
* @param model Plane coefficient [a,b,c,d] means ax+by+cz+d=0.
* @param thr Generate the maximum distance from the point to the plane.
* @param num The number of points.
* @param limit The range of xyz coordinates of the generated plane.
*
*/
void generatePlane(OutputArray plane_pts, const vector<float> &model, float thr, int num,
const vector<float> &limit);
/**
* @brief Generate a specific sphere with random points.
*
* @param[out] sphere_pts Point cloud of plane, only support vector<Point3f> or Mat with Nx3 layout
* in memory.
* @param model Plane coefficient [a,b,c,d] means x^2+y^2+z^2=r^2.
* @param thr Generate the maximum distance from the point to the surface of sphere.
* @param num The number of points.
* @param limit The range of vector to make the generated sphere incomplete.
*
*/
void generateSphere(OutputArray sphere_pts, const vector<float> &model, float thr, int num,
const vector<float> &limit);
} // opencv_test
#endif //OPENCV_TEST_PTCLOUD_UTILS_HPP
@@ -0,0 +1,313 @@
// 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.
//
// Copyright (C) 2021, Wanli Zhong <zhongwl2018@mail.sustech.edu.cn>
#include "test_precomp.hpp"
#include "test_ptcloud_utils.hpp"
namespace opencv_test { namespace {
int countNum(const vector<int> &m, int num)
{
int t = 0;
for (int a: m)
if (a == num) t++;
return t;
}
string getHeader(const Ptr<SACSegmentation> &s){
string r;
if(!s->isParallel())
r += "One thread ";
else
r += std::to_string(getNumThreads()) + "-thread ";
if(s->getNumberOfModelsExpected() == 1)
r += "single model segmentation ";
else
r += std::to_string(s->getNumberOfModelsExpected()) + " models segmentation ";
if(s->getCustomModelConstraints() == nullptr)
r += "without constraint:\n";
else
r += "with constraint:\n";
r += "Confidence: " + std::to_string(s->getConfidence()) + "\n";
r += "Max Iterations: " + std::to_string(s->getMaxIterations()) + "\n";
r += "Expected Models Number: " + std::to_string(s->getNumberOfModelsExpected()) + "\n";
r += "Distance Threshold: " + std::to_string(s->getDistanceThreshold());
return r;
}
class SacSegmentationTest : public ::testing::Test
{
public:
// Used to store the parameters of model generation
vector<vector<float>> models, limits;
vector<float> thrs;
vector<int> pt_nums;
int models_num = 0;
// Used to store point cloud, generated plane and model
Mat pt_cloud, generated_pts, segmented_models;
vector<int> label;
Ptr<SACSegmentation> sacSegmentation = SACSegmentation::create();
SACSegmentation::ModelConstraintFunction model_constraint = nullptr;
using CheckDiffFunction = std::function<bool(const Mat &, const Mat &)>;
void singleModelSegmentation(int iter_num, const CheckDiffFunction &checkDiff, int idx)
{
sacSegmentation->setSacMethodType(SAC_METHOD_RANSAC);
sacSegmentation->setConfidence(1);
sacSegmentation->setMaxIterations(iter_num);
sacSegmentation->setNumberOfModelsExpected(1);
//A point with a distance equal to the threshold is not considered an inliner point
sacSegmentation->setDistanceThreshold(thrs[idx] + 0.01);
int num = sacSegmentation->segment(pt_cloud, label, segmented_models);
string header = getHeader(sacSegmentation);
ASSERT_EQ(1, num)
<< header << endl
<< "Model number should be equal to 1.";
ASSERT_EQ(pt_cloud.rows, (int) (label.size()))
<< header << endl
<< "Label size should be equal to point number.";
Mat ans_model, segmented_model;
ans_model = Mat(1, (int) models[0].size(), CV_32F, models[idx].data());
segmented_models.row(0).convertTo(segmented_model, CV_32F);
ASSERT_TRUE(checkDiff(ans_model, segmented_model))
<< header << endl
<< "Initial model is " << ans_model << ". Segmented model is " << segmented_model
<< ". The difference in coefficients should not be too large.";
ASSERT_EQ(pt_nums[idx], countNum(label, 1))
<< header << endl
<< "There are " << pt_nums[idx] << " points need to be marked.";
int start_idx = 0;
for (int i = 0; i < idx; i++) start_idx += pt_nums[i];
for (int i = 0; i < pt_cloud.rows; i++)
{
if (i >= start_idx && i < start_idx + pt_nums[idx])
ASSERT_EQ(1, label[i])
<< header << endl
<< "This index should be marked: " << i
<< ". This point is " << pt_cloud.row(i);
else
ASSERT_EQ(0, label[i])
<< header << endl
<< "This index should not be marked: "
<< i << ". This point is " << pt_cloud.row(i);
}
}
void multiModelSegmentation(int iter_num, const CheckDiffFunction &checkDiff)
{
sacSegmentation->setSacMethodType(SAC_METHOD_RANSAC);
sacSegmentation->setConfidence(1);
sacSegmentation->setMaxIterations(iter_num);
sacSegmentation->setNumberOfModelsExpected(models_num);
sacSegmentation->setDistanceThreshold(thrs[models_num - 1] + 0.01);
int num = sacSegmentation->segment(pt_cloud, label, segmented_models);
string header = getHeader(sacSegmentation);
ASSERT_EQ(models_num, num)
<< header << endl
<< "Model number should be equal to " << models_num << ".";
ASSERT_EQ(pt_cloud.rows, (int) (label.size()))
<< header << endl
<< "Label size should be equal to point number.";
int checked_num = 0;
for (int i = 0; i < models_num; i++)
{
Mat ans_model, segmented_model;
ans_model = Mat(1, (int) models[0].size(), CV_32F, models[models_num - 1 - i].data());
segmented_models.row(i).convertTo(segmented_model, CV_32F);
ASSERT_TRUE(checkDiff(ans_model, segmented_model))
<< header << endl
<< "Initial model is " << ans_model << ". Segmented model is " << segmented_model
<< ". The difference in coefficients should not be too large.";
ASSERT_EQ(pt_nums[models_num - 1 - i], countNum(label, i + 1))
<< header << endl
<< "There are " << pt_nums[i] << " points need to be marked.";
for (int j = checked_num; j < pt_nums[i]; j++)
ASSERT_EQ(models_num - i, label[j])
<< header << endl
<< "This index " << j << " should be marked as " << models_num - i
<< ". This point is " << pt_cloud.row(j);
checked_num += pt_nums[i];
}
}
};
TEST_F(SacSegmentationTest, PlaneSacSegmentation)
{
sacSegmentation->setSacModelType(SAC_MODEL_PLANE);
models = {
{0, 0, 1, 0},
{1, 0, 0, 0},
{0, 1, 0, 0},
{1, 1, 1, -150},
};
thrs = {0.1f, 0.2f, 0.3f, 0.4f};
pt_nums = {100, 200, 300, 400};
limits = {
{5, 55, 5, 55, 0, 0},
{0, 0, 5, 55, 5, 55},
{5, 55, 0, 0, 5, 55},
{10, 50, 10, 50, 0, 0},
};
models_num = (int) models.size();
// 1 * 3.1415926f / 180
float vector_radian_tolerance = 0.0174533f, ratio_tolerance = 0.1f;
CheckDiffFunction planeCheckDiff = [vector_radian_tolerance, ratio_tolerance](const Mat &a,
const Mat &b) -> bool {
Mat m1, m2;
a.convertTo(m1, CV_32F);
b.convertTo(m2, CV_32F);
auto p1 = (float *) m1.data, p2 = (float *) m2.data;
Vec3f n1(p1[0], p1[1], p1[2]);
Vec3f n2(p2[0], p2[1], p2[2]);
float cos_theta_square = n1.dot(n2) * n1.dot(n2) / (n1.dot(n1) * n2.dot(n2));
float r1 = p1[3] * p1[3] / n1.dot(n1);
float r2 = p2[3] * p2[3] / n2.dot(n2);
return cos_theta_square >= cos(vector_radian_tolerance) * cos(vector_radian_tolerance)
&& abs(r1 - r2) <= ratio_tolerance * ratio_tolerance;
};
// Single plane segmentation
for (int i = 0; i < models_num; i++)
{
generatePlane(generated_pts, models[i], thrs[i], pt_nums[i], limits[i]);
pt_cloud.push_back(generated_pts);
singleModelSegmentation(1000, planeCheckDiff, i);
}
// Single plane segmentation with constraint
for (int i = models_num / 2; i < models_num; i++)
{
vector<float> constraint_normal = {models[i][0], models[i][1], models[i][2]};
// Normal vector constraint function
model_constraint = [constraint_normal](const vector<double> &model) -> bool {
// The angle between the model normals and the constraints must be less than 1 degree
// 1 * 3.1415926f / 180
float radian_thr = 0.0174533f;
vector<float> model_normal = {(float) model[0], (float) model[1], (float) model[2]};
float dot12 = constraint_normal[0] * model_normal[0] +
constraint_normal[1] * model_normal[1] +
constraint_normal[2] * model_normal[2];
float m1m1 = constraint_normal[0] * constraint_normal[0] +
constraint_normal[1] * constraint_normal[1] +
constraint_normal[2] * constraint_normal[2];
float m2m2 = model_normal[0] * model_normal[0] +
model_normal[1] * model_normal[1] +
model_normal[2] * model_normal[2];
float square_cos_theta = dot12 * dot12 / (m1m1 * m2m2);
return square_cos_theta >= cos(radian_thr) * cos(radian_thr);
};
sacSegmentation->setCustomModelConstraints(model_constraint);
singleModelSegmentation(5000, planeCheckDiff, i);
}
pt_cloud.release();
sacSegmentation->setCustomModelConstraints(nullptr);
// sacSegmentation->setParallel(true); // parallel version is not deterministic and should be initialized differently
// Multi-plane segmentation
for (int i = 0; i < models_num; i++)
{
generatePlane(generated_pts, models[i], thrs[models_num - 1], pt_nums[i], limits[i]);
pt_cloud.push_back(generated_pts);
}
multiModelSegmentation(1000, planeCheckDiff);
}
TEST_F(SacSegmentationTest, SphereSacSegmentation)
{
sacSegmentation->setSacModelType(cv::SAC_MODEL_SPHERE);
models = {
{15, 15, 30, 5},
{-15, -15, -30, 8},
{0, 0, -35, 10},
{0, 0, 0, 15},
{0, 0, 0, 20},
};
thrs = {0.1f, 0.2f, 0.3f, 0.4f, 0.5f};
pt_nums = {100, 200, 300, 400, 500};
limits = {
{0, 1, 0, 1, 0, 1},
{-1, 0, -1, 0, -1, 0},
{-1, 1, -1, 1, 0, 1},
{-1, 1, -1, 1, -1, 1},
{-1, 1, -1, 1, -1, 0},
};
models_num = (int) models.size();
float distance_tolerance = 0.1f, radius_tolerance = 0.1f;
CheckDiffFunction sphereCheckDiff = [distance_tolerance, radius_tolerance](const Mat &a,
const Mat &b) -> bool {
Mat d = a - b;
auto d_ptr = (float *) d.data;
// Distance square between sphere centers
float d_square = d_ptr[0] * d_ptr[0] + d_ptr[1] * d_ptr[1] + d_ptr[2] * d_ptr[2];
// Difference square between radius of two spheres
float r_square = d_ptr[3] * d_ptr[3];
return d_square <= distance_tolerance * distance_tolerance &&
r_square <= radius_tolerance * radius_tolerance;
};
// Single sphere segmentation
for (int i = 0; i < models_num; i++)
{
generateSphere(generated_pts, models[i], thrs[i], pt_nums[i], limits[i]);
pt_cloud.push_back(generated_pts);
singleModelSegmentation(3000, sphereCheckDiff, i);
}
// Single sphere segmentation with constraint
for (int i = models_num / 2; i < models_num; i++)
{
float constraint_radius = models[i][3] + 0.5f;
// Radius constraint function
model_constraint = [constraint_radius](
const vector<double> &model) -> bool {
auto model_radius = (float) model[3];
return model_radius <= constraint_radius;
};
sacSegmentation->setCustomModelConstraints(model_constraint);
singleModelSegmentation(10000, sphereCheckDiff, i);
}
pt_cloud.release();
sacSegmentation->setCustomModelConstraints(nullptr);
// sacSegmentation->setParallel(true); // parallel version is not deterministic and should be initialized differently
// Multi-sphere segmentation
for (int i = 0; i < models_num; i++)
{
generateSphere(generated_pts, models[i], thrs[models_num - 1], pt_nums[i], limits[i]);
pt_cloud.push_back(generated_pts);
}
multiModelSegmentation(5000, sphereCheckDiff);
}
} // namespace
} // opencv_test
+282
View File
@@ -0,0 +1,282 @@
// 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.
//
// Copyright (C) 2021, Wanli Zhong <zhongwl2018@mail.sustech.edu.cn>
#include "test_precomp.hpp"
namespace opencv_test { namespace {
class SamplingTest : public ::testing::Test {
public:
int ori_pts_size = 0;
// Different point clouds to test InputArray compatibility.
vector<Point3f> pts_vec_Pt3f;
Mat pts_mat_32F_Nx3;
Mat pts_mat_64F_3xN;
Mat pts_mat_32FC3_Nx1;
// Different masks to test OutputArray compatibility.
vector<char> mask_vec_char;
vector<int> mask_vec_int;
Mat mask_mat_Nx1;
Mat mask_mat_1xN;
// Combination of InputArray and OutputArray as information to mention where the test fail.
string header = "\n===================================================================\n";
string combination1 = "OutputArray: vector<char>\nInputArray: vector<point3f>\n";
string combination2 = "OutputArray: Nx1 Mat\nInputArray: Nx3 float Mat\n";
string combination3 = "OutputArray: 1xN Mat\nInputArray: 3xN double Mat\n";
string combination4 = "OutputArray: vector<int>\nInputArray: Nx1 3 channels float Mat\n";
// Initialize point clouds with different data type.
void dataInitialization(vector<float> &pts_data)
{
ori_pts_size = (int) pts_data.size() / 3;
// point cloud use Nx3 mat as data structure and float as value type.
pts_mat_32F_Nx3 = Mat(ori_pts_size, 3, CV_32F, pts_data.data());
// point cloud use vector<Point3f> as data structure.
pts_vec_Pt3f.clear();
for (int i = 0; i < ori_pts_size; i++)
{
int i3 = i * 3;
pts_vec_Pt3f.emplace_back(
Point3f(pts_data[i3], pts_data[i3 + 1], pts_data[i3 + 2]));
}
// point cloud use 3xN mat as data structure and double as value type.
pts_mat_64F_3xN = pts_mat_32F_Nx3.t();
pts_mat_64F_3xN.convertTo(pts_mat_64F_3xN, CV_64F);
// point cloud use Nx1 mat with 3 channels as data structure and float as value type.
pts_mat_32F_Nx3.convertTo(pts_mat_32FC3_Nx1, CV_32FC3);
}
};
// Get 1xN mat of mask from OutputArray.
void getMatFromMask(OutputArray &mask, Mat &mat)
{
int rows = mask.rows(), cols = mask.cols(), channels = mask.channels();
if (channels == 1 && cols == 1 && rows != 1)
mat = mask.getMat().t();
else
mat = mask.getMat().reshape(1, 1);
// Use int to store data.
if (mat.type() != CV_32S)
mat.convertTo(mat, CV_32S);
}
// Compare 2 rows in different point clouds.
bool compareRows(const Mat &m, int rm, const Mat &n, int rn)
{
Mat diff = m.row(rm) != n.row(rn);
return countNonZero(diff) == 0;
}
TEST_F(SamplingTest, VoxelGridFilterSampling)
{
vector<float> pts_info = {0.0f, 0.0f, 0.0f,
-3.0f, -3.0f, -3.0f, 3.0f, -3.0f, -3.0f, 3.0f, 3.0f, -3.0f, -3.0f, 3.0f, -3.0f,
-3.0f, -3.0f, 3.0f, 3.0f, -3.0f, 3.0f, 3.0f, 3.0f, 3.0f, -3.0f, 3.0f, 3.0f,
-0.9f, -0.9f, -0.9f, 0.9f, -0.9f, -0.9f, 0.9f, 0.9f, -0.9f, -0.9f, 0.9f, -0.9f,
-0.9f, -0.9f, 0.9f, 0.9f, -0.9f, 0.9f, 0.9f, 0.9f, 0.9f, -0.9f, 0.9f, 0.9f,};
dataInitialization(pts_info);
auto testVoxelGridFilterSampling = [this](OutputArray mask, InputArray pts, float *side,
int sampled_pts_size, const Mat &ans, const string &info) {
// Check the number of point cloud after sampling.
int res = voxelGridSampling(mask, pts, side[0], side[1], side[2]);
EXPECT_EQ(sampled_pts_size, res)
<< header << info << "The side length is " << side[0]
<< "\nThe return value of voxelGridSampling() is not equal to " << sampled_pts_size
<< endl;
EXPECT_EQ(sampled_pts_size, countNonZero(mask))
<< header << info << "The side length is " << side[0]
<< "\nThe number of selected points of mask is not equal to " << sampled_pts_size
<< endl;
// The mask after sampling should be equal to the answer.
Mat _mask;
getMatFromMask(mask, _mask);
ASSERT_TRUE(compareRows(_mask, 0, ans, 0))
<< header << info << "The side length is " << side[0]
<< "\nThe mask should be " << ans << endl;
};
// Set 6.1 as the side1 length, only the center point left.
Mat ans1({1, (int) (pts_info.size() / 3)}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
float side1[3] = {6.1f, 6.1f, 6.1f};
testVoxelGridFilterSampling(mask_vec_char, pts_vec_Pt3f, side1, 1, ans1, combination1);
testVoxelGridFilterSampling(mask_mat_Nx1, pts_mat_32F_Nx3, side1, 1, ans1, combination2);
testVoxelGridFilterSampling(mask_mat_1xN, pts_mat_64F_3xN, side1, 1, ans1, combination3);
testVoxelGridFilterSampling(mask_vec_int, pts_mat_32FC3_Nx1, side1, 1, ans1, combination4);
// Set 2 as the side1 length, only the center point and the vertexes of big cube left.
Mat ans2({1, (int) (pts_info.size() / 3)}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0});
float side2[3] = {2.0f, 2.0f, 2.0f};
testVoxelGridFilterSampling(mask_vec_char, pts_vec_Pt3f, side2, 9, ans2, combination1);
testVoxelGridFilterSampling(mask_mat_Nx1, pts_mat_32F_Nx3, side2, 9, ans2, combination2);
testVoxelGridFilterSampling(mask_mat_1xN, pts_mat_64F_3xN, side2, 9, ans2, combination3);
testVoxelGridFilterSampling(mask_vec_int, pts_mat_32FC3_Nx1, side2, 9, ans2, combination4);
// Set 1.1 as the side1 length, all points should be left.
Mat ans3({1, (int) (pts_info.size() / 3)}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1});
float side3[3] = {1.1f, 1.1f, 1.1f};
testVoxelGridFilterSampling(mask_vec_char, pts_vec_Pt3f, side3, 17, ans3, combination1);
testVoxelGridFilterSampling(mask_mat_Nx1, pts_mat_32F_Nx3, side3, 17, ans3, combination2);
testVoxelGridFilterSampling(mask_mat_1xN, pts_mat_64F_3xN, side3, 17, ans3, combination3);
testVoxelGridFilterSampling(mask_vec_int, pts_mat_32FC3_Nx1, side3, 17, ans3, combination4);
}
TEST_F(SamplingTest, RandomSampling)
{
vector<float> pts_info = {0, 0, 0, 1, 0, 0, 1, 2, 0, 0, 2, 0,
0, 0, 3, 1, 0, 3, 1, 2, 3, 0, 2, 3};
dataInitialization(pts_info);
auto testRandomSampling = [this](InputArray pts, int sampled_pts_size, const string &info) {
// Check the number of point cloud after sampling.
Mat sampled_pts;
randomSampling(sampled_pts, pts, sampled_pts_size);
EXPECT_EQ(sampled_pts_size, sampled_pts.rows)
<< header << info << "The sampled points size is " << sampled_pts_size
<< "\nThe number of sampled points is not equal to " << sampled_pts_size << endl;
// Convert InputArray to Mat of original point cloud.
int rows = pts.rows(), cols = pts.cols(), channels = pts.channels();
int total = rows * cols * channels;
Mat ori_pts;
if (channels == 1 && rows == 3 && cols != 3)
ori_pts = pts.getMat().t();
else
ori_pts = pts.getMat().reshape(1, (int) (total / 3));
if (ori_pts.type() != CV_32F)
ori_pts.convertTo(ori_pts, CV_32F);
// Each point should be in the original point cloud.
for (int i = 0; i < sampled_pts_size; i++)
{
// Check whether a point exists in the point cloud.
bool flag = false;
for (int j = 0; j < ori_pts.rows; j++)
flag |= compareRows(sampled_pts, i, ori_pts, j);
ASSERT_TRUE(flag)
<< header << info << "The sampled points size is " << sampled_pts_size
<< "\nThe sampled point " << sampled_pts.row(i)
<< " is not in original point cloud" << endl;
}
};
testRandomSampling(pts_vec_Pt3f, 3, combination1);
testRandomSampling(pts_mat_32F_Nx3, 4, combination2);
testRandomSampling(pts_mat_64F_3xN, 5, combination3);
testRandomSampling(pts_mat_32FC3_Nx1, 6, combination4);
}
TEST_F(SamplingTest, FarthestPointSampling)
{
vector<float> pts_info = {0, 0, 0, 1, 0, 0, 1, 2, 0, 0, 2, 0,
0, 0, 3, 1, 0, 3, 1, 2, 3, 0, 2, 3};
dataInitialization(pts_info);
auto testFarthestPointSampling = [this](OutputArray mask, InputArray pts, int sampled_pts_size,
const vector<Mat> &ans, const string &info) {
// Check the number of point cloud after sampling.
int res = farthestPointSampling(mask, pts, sampled_pts_size);
EXPECT_EQ(sampled_pts_size, res)
<< header << info << "The sampled points size is " << sampled_pts_size
<< "\nThe return value of farthestPointSampling() is not equal to "
<< sampled_pts_size << endl;
EXPECT_EQ(sampled_pts_size, countNonZero(mask))
<< header << info << "The sampled points size is " << sampled_pts_size
<< "\nThe number of selected points of mask is not equal to " << sampled_pts_size
<< endl;
// The mask after sampling should be one of the answers.
Mat _mask;
getMatFromMask(mask, _mask);
bool flag = false;
for (const Mat &a: ans)
flag |= compareRows(a, 0, _mask, 0);
string ans_info;
ASSERT_TRUE(flag)
<< header << info << "The sampled points size is " << sampled_pts_size
<< "\nThe mask should be one of the answer list" << endl;
};
// Set 2 as the size, and there should be 2 diagonal points after sampling.
vector<Mat> ans_list1;
ans_list1.emplace_back(Mat({1, ori_pts_size}, {1, 0, 0, 0, 0, 0, 1, 0}));
ans_list1.emplace_back(Mat({1, ori_pts_size}, {0, 1, 0, 0, 0, 0, 0, 1}));
ans_list1.emplace_back(Mat({1, ori_pts_size}, {0, 0, 1, 0, 1, 0, 0, 0}));
ans_list1.emplace_back(Mat({1, ori_pts_size}, {0, 0, 0, 1, 0, 1, 0, 0}));
testFarthestPointSampling(mask_vec_char, pts_vec_Pt3f, 2, ans_list1, combination1);
testFarthestPointSampling(mask_mat_Nx1, pts_mat_32F_Nx3, 2, ans_list1, combination2);
testFarthestPointSampling(mask_mat_1xN, pts_mat_64F_3xN, 2, ans_list1, combination3);
testFarthestPointSampling(mask_vec_int, pts_mat_32FC3_Nx1, 2, ans_list1, combination4);
// Set 4 as the size, and there should be 4 specific points form a plane perpendicular to the X and Y axes after sampling.
vector<Mat> ans_list2;
ans_list2.emplace_back(Mat({1, ori_pts_size}, {1, 0, 1, 0, 1, 0, 1, 0}));
ans_list2.emplace_back(Mat({1, ori_pts_size}, {0, 1, 0, 1, 0, 1, 0, 1}));
testFarthestPointSampling(mask_vec_char, pts_vec_Pt3f, 4, ans_list2, combination1);
testFarthestPointSampling(mask_mat_Nx1, pts_mat_32F_Nx3, 4, ans_list2, combination2);
testFarthestPointSampling(mask_mat_1xN, pts_mat_64F_3xN, 4, ans_list2, combination3);
testFarthestPointSampling(mask_vec_int, pts_mat_32FC3_Nx1, 4, ans_list2, combination4);
}
TEST_F(SamplingTest, FarthestPointSamplingDoublePtCloud)
{
// After doubling the point cloud, 8 points are sampled and each vertex is displayed only once.
vector<float> pts_info = {0, 0, 0, 1, 0, 0, 1, 2, 0, 0, 2, 0,
0, 0, 3, 1, 0, 3, 1, 2, 3, 0, 2, 3};
// Double the point cloud.
pts_info.insert(pts_info.end(), pts_info.begin(), pts_info.end());
dataInitialization(pts_info);
auto testFarthestPointSamplingDoublePtCloud = [this](OutputArray mask, InputArray pts, int sampled_pts_size,
const string &info) {
// Check the number of point cloud after sampling.
int res = farthestPointSampling(mask, pts, sampled_pts_size);
EXPECT_EQ(sampled_pts_size, res)
<< header << info << "The sampled points size is " << sampled_pts_size
<< "\nThe return value of farthestPointSampling() is not equal to "
<< sampled_pts_size << endl;
EXPECT_EQ(sampled_pts_size, countNonZero(mask))
<< header << info << "The sampled points size is " << sampled_pts_size
<< "\nThe number of selected points of mask is not equal to " << sampled_pts_size
<< endl;
// One and only one of mask[index] and mask[index + size/2] is true for first eight index.
Mat _mask;
getMatFromMask(mask, _mask);
auto *mask_ptr = (int *) _mask.data;
int offset = ori_pts_size / 2;
bool flag = true;
for (int i = 0; i < offset; i++)
flag &= (mask_ptr[i] == 0 && mask_ptr[i + offset] != 0) || (mask_ptr[i] != 0 && mask_ptr[i + offset] == 0);
ASSERT_TRUE(flag)
<< header << info << "The sampled points size is " << sampled_pts_size
<< "\nThe mask should be one of the answer list" << endl;
};
testFarthestPointSamplingDoublePtCloud(mask_vec_char, pts_vec_Pt3f, 8, combination1);
testFarthestPointSamplingDoublePtCloud(mask_mat_Nx1, pts_mat_32F_Nx3, 8, combination2);
testFarthestPointSamplingDoublePtCloud(mask_mat_1xN, pts_mat_64F_3xN, 8, combination3);
testFarthestPointSamplingDoublePtCloud(mask_vec_int, pts_mat_32FC3_Nx1, 8, combination4);
}
} // namespace
} // opencv_test
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,120 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
// 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.
//M*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
TEST(Imgproc_Subdiv2D_getTriangleList, regression_5788)
{
const float points[65][2] = {
{ 390, 802}, { 397, 883}, { 414, 963 }, { 439, 1042 }, { 472, 1113},
{ 521, 1181}, { 591, 1238}, { 678, 1284 }, { 771, 1292 }, { 853, 1281},
{ 921, 1243}, { 982, 1191}, {1030, 1121 }, {1059, 1038 }, {1072, 945},
{1081, 849}, {1082, 749}, { 459, 734 }, { 502, 704 }, { 554, 696},
{ 609, 698}, { 660, 707}, { 818, 688 }, { 874, 661 }, { 929, 646},
{ 982, 653}, {1026, 682}, { 740, 771 }, { 748, 834 }, { 756, 897},
{ 762, 960}, { 700, 998}, { 733, 1006 }, { 766, 1011 }, { 797, 999},
{ 825, 987}, { 528, 796}, { 566, 766 }, { 617, 763 }, { 659, 794},
{ 619, 808}, { 569, 812}, { 834, 777 }, { 870, 735 }, { 918, 729},
{ 958, 750}, { 929, 773}, { 882, 780 }, { 652, 1102 }, { 701, 1079},
{ 743, 1063}, { 774, 1068}, { 807, 1057 }, { 852, 1065 }, { 896, 1077},
{ 860, 1117}, { 820, 1135}, { 783, 1141 }, { 751, 1140 }, { 706, 1130},
{ 675, 1102}, { 743, 1094}, { 774, 1094 }, { 809, 1088 }, { 878, 1082}
};
std::vector<cv::Point2f> pts;
cv::Rect rect(0, 0, 1500, 2000);
cv::Subdiv2D subdiv(rect);
for( int i = 0; i < 65; i++ )
{
cv::Point2f pt(points[i][0], points[i][1]);
pts.push_back(pt);
}
subdiv.insert(pts);
std::vector<cv::Vec6f> triangles;
subdiv.getTriangleList(triangles);
int trig_cnt = 0;
for( std::vector<cv::Vec6f>::const_iterator it = triangles.begin(); it != triangles.end(); it++, trig_cnt++ )
{
EXPECT_TRUE( (0 <= triangles.at(trig_cnt).val[0] && triangles.at(trig_cnt).val[0] < 1500) &&
(0 <= triangles.at(trig_cnt).val[1] && triangles.at(trig_cnt).val[1] < 2000) &&
(0 <= triangles.at(trig_cnt).val[2] && triangles.at(trig_cnt).val[2] < 1500) &&
(0 <= triangles.at(trig_cnt).val[3] && triangles.at(trig_cnt).val[3] < 2000) &&
(0 <= triangles.at(trig_cnt).val[4] && triangles.at(trig_cnt).val[4] < 1500) &&
(0 <= triangles.at(trig_cnt).val[5] && triangles.at(trig_cnt).val[5] < 2000) );
}
EXPECT_EQ(trig_cnt, 105);
}
TEST(Imgproc_Subdiv2D, issue_25696) {
std::vector<cv::Point2f> points{
{0, 0}, {40, 40}, {84, 104}, {86, 108}
};
cv::Rect subdivRect{cv::Point{-10, -10}, cv::Point{96, 118}};
cv::Subdiv2D subdiv{subdivRect};
subdiv.insert(points);
std::vector<cv::Vec6f> triangles;
subdiv.getTriangleList(triangles);
ASSERT_EQ(static_cast<size_t>(2), triangles.size());
}
// Initialization test
TEST(Imgproc_Subdiv2D, rect2f_constructor_and_init)
{
cv::Rect2f rect_f(0.5f, 1.5f, 100.7f, 200.3f);
cv::Subdiv2D subdiv_f(rect_f);
cv::Point2f pt1(50.2f, 80.1f);
cv::Point2f pt2(75.8f, 120.9f);
cv::Point2f pt3(25.5f, 150.3f);
EXPECT_NO_THROW(subdiv_f.insert(pt1));
EXPECT_NO_THROW(subdiv_f.insert(pt2));
EXPECT_NO_THROW(subdiv_f.insert(pt3));
cv::Subdiv2D subdiv_init;
EXPECT_NO_THROW(subdiv_init.initDelaunay(rect_f));
EXPECT_NO_THROW(subdiv_init.insert(pt1));
EXPECT_NO_THROW(subdiv_init.insert(pt2));
EXPECT_NO_THROW(subdiv_init.insert(pt3));
std::vector<cv::Vec6f> triangles;
EXPECT_NO_THROW(subdiv_f.getTriangleList(triangles));
EXPECT_GT(triangles.size(), 0u);
}
// test with small coordinates
TEST(Imgproc_Subdiv2D, rect2f_edge_cases)
{
cv::Rect2f small_rect(0.0f, 0.0f, 0.1f, 0.1f);
cv::Subdiv2D subdiv_small(small_rect);
cv::Point2f small_pt(0.05f, 0.05f);
EXPECT_NO_THROW(subdiv_small.insert(small_pt));
cv::Rect2f float_rect(10.25f, 20.75f, 50.5f, 30.25f);
cv::Subdiv2D subdiv_float(float_rect);
cv::Point2f float_pt1(35.125f, 35.875f);
cv::Point2f float_pt2(45.375f, 25.625f);
cv::Point2f float_pt3(55.750f, 45.125f);
EXPECT_NO_THROW(subdiv_float.insert(float_pt1));
EXPECT_NO_THROW(subdiv_float.insert(float_pt2));
EXPECT_NO_THROW(subdiv_float.insert(float_pt3));
std::vector<cv::Vec6f> triangles;
subdiv_float.getTriangleList(triangles);
EXPECT_GT(triangles.size(), 0u);
}
}}
@@ -0,0 +1,211 @@
/*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 "test_precomp.hpp"
namespace opencv_test { namespace {
CV_ENUM(Method, RANSAC, LMEDS)
typedef TestWithParam<Method> EstimateTranslation2D;
static float rngIn(float from, float to) { return from + (to - from) * (float)theRNG(); }
// build a pure translation 2x3 matrix
static cv::Mat rngTranslationMat()
{
double tx = rngIn(-20.f, 20.f);
double ty = rngIn(-20.f, 20.f);
double t[2*3] = { 1.0, 0.0, tx,
0.0, 1.0, ty };
return cv::Mat(2, 3, CV_64F, t).clone();
}
static inline cv::Vec2d getTxTy(const cv::Mat& T)
{
CV_Assert(T.rows == 2 && T.cols == 3 && T.type() == CV_64F);
return cv::Vec2d(T.at<double>(0,2), T.at<double>(1,2));
}
TEST_P(EstimateTranslation2D, test1Point)
{
// minimal sample is 1 point
for (size_t i = 0; i < 500; ++i)
{
cv::Mat T = rngTranslationMat();
cv::Vec2d T_ref = getTxTy(T);
cv::Mat fpts(1, 1, CV_32FC2);
cv::Mat tpts(1, 1, CV_32FC2);
fpts.at<cv::Point2f>(0) = cv::Point2f(rngIn(1,2), rngIn(5,6));
transform(fpts, tpts, T);
std::vector<uchar> inliers;
cv::Vec2d T_est = estimateTranslation2D(fpts, tpts, inliers, GetParam() /* method */);
EXPECT_NEAR(T_est[0], T_ref[0], 1e-6);
EXPECT_NEAR(T_est[1], T_ref[1], 1e-6);
EXPECT_EQ((int)inliers.size(), 1);
EXPECT_EQ((int)inliers[0], 1);
}
}
TEST_P(EstimateTranslation2D, testNPoints)
{
for (size_t i = 0; i < 500; ++i)
{
cv::Mat T = rngTranslationMat();
cv::Vec2d T_ref = getTxTy(T);
const int method = GetParam();
const int n = 100;
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;
cv::Mat fpts(1, n, CV_32FC2);
cv::Mat tpts(1, n, CV_32FC2);
randu(fpts, 0.f, 100.f);
transform(fpts, tpts, T);
/* adding noise to some points (make last n-m points outliers) */
cv::Mat outliers = tpts.colRange(m, n);
outliers.reshape(1) += shift_outl;
cv::Mat noise(outliers.size(), outliers.type());
randu(noise, 0.f, noise_level);
outliers += noise;
std::vector<uchar> inliers;
cv::Vec2d T_est = estimateTranslation2D(fpts, tpts, inliers, method);
// Check estimation produced finite values
ASSERT_TRUE(std::isfinite(T_est[0]) && std::isfinite(T_est[1]));
EXPECT_NEAR(T_est[0], T_ref[0], 1e-4);
EXPECT_NEAR(T_est[1], T_ref[1], 1e-4);
bool inliers_good = std::count(inliers.begin(), inliers.end(), 1) == m &&
m == std::accumulate(inliers.begin(), inliers.begin() + m, 0);
EXPECT_TRUE(inliers_good);
}
}
// test conversion from other datatypes than float
TEST_P(EstimateTranslation2D, testConversion)
{
cv::Mat T = rngTranslationMat();
T.convertTo(T, CV_32S); // convert to int to transform ints properly
std::vector<cv::Point> fpts(3);
std::vector<cv::Point> tpts(3);
fpts[0] = cv::Point2f(rngIn(1,2), rngIn(5,6));
fpts[1] = cv::Point2f(rngIn(3,4), rngIn(3,4));
fpts[2] = cv::Point2f(rngIn(1,2), rngIn(3,4));
transform(fpts, tpts, T);
std::vector<uchar> inliers;
cv::Vec2d T_est = estimateTranslation2D(fpts, tpts, inliers, GetParam() /* method */);
ASSERT_TRUE(std::isfinite(T_est[0]) && std::isfinite(T_est[1]));
T.convertTo(T, CV_64F); // convert back for reference extraction
cv::Vec2d T_ref = getTxTy(T);
EXPECT_NEAR(T_est[0], T_ref[0], 1e-3);
EXPECT_NEAR(T_est[1], T_ref[1], 1e-3);
// all must be inliers
EXPECT_EQ(countNonZero(inliers), 3);
}
INSTANTIATE_TEST_CASE_P(Calib3d, EstimateTranslation2D, Method::all());
// "don't change inputs" regression, mirroring affine partial test
TEST(EstimateTranslation2D, dont_change_inputs)
{
/*const static*/ float pts0_[10] = {
0.0f, 0.0f,
0.0f, 8.0f,
4.0f, 0.0f, // outlier
8.0f, 8.0f,
8.0f, 0.0f
};
/*const static*/ float pts1_[10] = {
0.1f, 0.1f,
0.1f, 8.1f,
0.0f, 4.0f, // outlier
8.1f, 8.1f,
8.1f, 0.1f
};
cv::Mat pts0(cv::Size(1, 5), CV_32FC2, (void*)pts0_);
cv::Mat pts1(cv::Size(1, 5), CV_32FC2, (void*)pts1_);
cv::Mat pts0_copy = pts0.clone();
cv::Mat pts1_copy = pts1.clone();
cv::Mat inliers;
cv::Vec2d T = cv::estimateTranslation2D(pts0, pts1, inliers);
for (int i = 0; i < pts0.rows; ++i)
EXPECT_EQ(pts0_copy.at<cv::Vec2f>(i), pts0.at<cv::Vec2f>(i)) << "pts0: i=" << i;
for (int i = 0; i < pts1.rows; ++i)
EXPECT_EQ(pts1_copy.at<cv::Vec2f>(i), pts1.at<cv::Vec2f>(i)) << "pts1: i=" << i;
EXPECT_EQ(0, (int)inliers.at<uchar>(2));
// sanity: estimated translation should be finite
EXPECT_TRUE(std::isfinite(T[0]) && std::isfinite(T[1]));
}
}} // namespace
@@ -0,0 +1,100 @@
// 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 "test_precomp.hpp"
namespace opencv_test { namespace {
TEST(Calib3d_EstimateTranslation3D, test4Points)
{
Matx13d trans;
cv::randu(trans, Scalar(1), Scalar(3));
// setting points that are no in the same line
Mat fpts(1, 4, CV_32FC3);
Mat tpts(1, 4, CV_32FC3);
RNG& rng = theRNG();
fpts.at<Point3f>(0) = Point3f(rng.uniform(1.0f, 2.0f), rng.uniform(1.0f, 2.0f), rng.uniform(5.0f, 6.0f));
fpts.at<Point3f>(1) = Point3f(rng.uniform(3.0f, 4.0f), rng.uniform(3.0f, 4.0f), rng.uniform(5.0f, 6.0f));
fpts.at<Point3f>(2) = Point3f(rng.uniform(1.0f, 2.0f), rng.uniform(3.0f, 4.0f), rng.uniform(5.0f, 6.0f));
fpts.at<Point3f>(3) = Point3f(rng.uniform(3.0f, 4.0f), rng.uniform(1.0f, 2.0f), rng.uniform(5.0f, 6.0f));
std::transform(fpts.ptr<Point3f>(), fpts.ptr<Point3f>() + 4, tpts.ptr<Point3f>(),
[&] (const Point3f& p) -> Point3f
{
return Point3f((float)(p.x + trans(0, 0)),
(float)(p.y + trans(0, 1)),
(float)(p.z + trans(0, 2)));
}
);
Matx13d trans_est;
vector<uchar> outliers;
int res = estimateTranslation3D(fpts, tpts, trans_est, outliers);
EXPECT_GT(res, 0);
const double thres = 1e-3;
EXPECT_LE(cvtest::norm(trans_est, trans, NORM_INF), thres)
<< "aff est: " << trans_est << endl
<< "aff ref: " << trans;
}
TEST(Calib3d_EstimateTranslation3D, testNPoints)
{
Matx13d trans;
cv::randu(trans, Scalar(-2), Scalar(2));
// setting points that are no in the same line
const int n = 100;
const int m = 3*n/5;
const Point3f shift_outl = Point3f(15, 15, 15);
const float noise_level = 20.f;
Mat fpts(1, n, CV_32FC3);
Mat tpts(1, n, CV_32FC3);
randu(fpts, Scalar::all(0), Scalar::all(100));
std::transform(fpts.ptr<Point3f>(), fpts.ptr<Point3f>() + n, tpts.ptr<Point3f>(),
[&] (const Point3f& p) -> Point3f
{
return Point3f((float)(p.x + trans(0, 0)),
(float)(p.y + trans(0, 1)),
(float)(p.z + trans(0, 2)));
}
);
/* adding noise*/
std::transform(tpts.ptr<Point3f>() + m, tpts.ptr<Point3f>() + n, tpts.ptr<Point3f>() + m,
[&] (const Point3f& pt) -> Point3f
{
Point3f p = pt + shift_outl;
RNG& rng = theRNG();
return Point3f(p.x + noise_level * (float)rng,
p.y + noise_level * (float)rng,
p.z + noise_level * (float)rng);
}
);
Matx13d trans_est;
vector<uchar> outl;
int res = estimateTranslation3D(fpts, tpts, trans_est, outl);
EXPECT_GT(res, 0);
const double thres = 1e-4;
EXPECT_LE(cvtest::norm(trans_est, trans, NORM_INF), thres)
<< "aff est: " << trans_est << endl
<< "aff ref: " << trans;
bool outl_good = std::count(outl.begin(), outl.end(), 1) == m &&
m == std::accumulate(outl.begin(), outl.begin() + m, 0);
EXPECT_TRUE(outl_good);
}
}} // namespace
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,250 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// 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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, 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:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of Intel Corporation may not 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 the Intel Corporation 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 "test_precomp.hpp"
namespace opencv_test { namespace {
class CV_UndistortPointsBadArgTest : public cvtest::BadArgTest
{
public:
CV_UndistortPointsBadArgTest();
protected:
void run(int);
void run_func();
private:
//common
cv::Size img_size;
//static const int N_POINTS = 1;
static const int N_POINTS2 = 2;
//C++
cv::Mat camera_mat;
cv::Mat R;
cv::Mat P;
cv::Mat distortion_coeffs;
cv::Mat src_points;
std::vector<cv::Point2f> dst_points;
};
CV_UndistortPointsBadArgTest::CV_UndistortPointsBadArgTest ()
{
}
void CV_UndistortPointsBadArgTest::run_func()
{
cv::undistortPoints(src_points,dst_points,camera_mat,distortion_coeffs,R,P);
}
void CV_UndistortPointsBadArgTest::run(int)
{
//RNG& rng = ts->get_rng();
int errcount = 0;
//initializing
img_size.width = 800;
img_size.height = 600;
double cam[9] = {150.f, 0.f, img_size.width/2.f, 0, 300.f, img_size.height/2.f, 0.f, 0.f, 1.f};
double dist[4] = {0.01,0.02,0.001,0.0005};
double s_points[N_POINTS2] = {
static_cast<double>(img_size.width) / 4.0,
static_cast<double>(img_size.height) / 4.0,
};
double p[9] = {155.f, 0.f, img_size.width/2.f+img_size.width/50.f, 0, 310.f, img_size.height/2.f+img_size.height/50.f, 0.f, 0.f, 1.f};
double r[9] = {1,0,0,0,1,0,0,0,1};
camera_mat = Mat(3,3,CV_64F,cam);
distortion_coeffs = Mat(1,4,CV_64F,dist);
P = Mat(3,3,CV_64F,p);
R = Mat(3,3,CV_64F,r);
src_points.create(2, 2, CV_32FC2);
errcount += run_test_case( cv::Error::StsAssert, "Invalid input data matrix size" );
src_points = Mat(1,4,CV_64FC2,s_points);
src_points.create(1, 4, CV_64FC2);
errcount += run_test_case( cv::Error::StsAssert, "Invalid input data matrix type" );
src_points = Mat(1,4,CV_64FC2,s_points);
src_points = cv::Mat();
errcount += run_test_case( cv::Error::StsBadArg, "Input data matrix is not continuous" );
src_points = Mat(1,4,CV_64FC2,s_points);
//------------
ts->set_failed_test_info(errcount > 0 ? cvtest::TS::FAIL_BAD_ARG_CHECK : cvtest::TS::OK);
}
//=========
class CV_InitUndistortRectifyMapBadArgTest : public cvtest::BadArgTest
{
public:
CV_InitUndistortRectifyMapBadArgTest();
protected:
void run(int);
void run_func();
private:
cv::Size img_size;
cv::Mat camera_mat;
cv::Mat R;
cv::Mat new_camera_mat;
cv::Mat distortion_coeffs;
cv::Mat mapx;
cv::Mat mapy;
int mat_type;
};
CV_InitUndistortRectifyMapBadArgTest::CV_InitUndistortRectifyMapBadArgTest ()
{
}
void CV_InitUndistortRectifyMapBadArgTest::run_func()
{
cv::initUndistortRectifyMap(camera_mat,distortion_coeffs,R,new_camera_mat,img_size,mat_type,mapx,mapy);
}
void CV_InitUndistortRectifyMapBadArgTest::run(int)
{
int errcount = 0;
//initializing
img_size.width = 800;
img_size.height = 600;
double cam[9] = {150.f, 0.f, img_size.width/2.f, 0, 300.f, img_size.height/2.f, 0.f, 0.f, 1.f};
double dist[4] = {0.01,0.02,0.001,0.0005};
std::vector<float> arr_mapx(img_size.width*img_size.height);
std::vector<float> arr_mapy(img_size.width*img_size.height);
double arr_new_camera_mat[9] = {155.f, 0.f, img_size.width/2.f+img_size.width/50.f, 0, 310.f, img_size.height/2.f+img_size.height/50.f, 0.f, 0.f, 1.f};
double r[9] = {1,0,0,0,1,0,0,0,1};
int mat_type_orig = CV_32FC1;
camera_mat = Mat(3,3,CV_64F,cam);
distortion_coeffs = Mat(1,4,CV_64F,dist);
new_camera_mat = Mat(3,3,CV_64F,arr_new_camera_mat);
R = Mat(3,3,CV_64F,r);
mapx = Mat(img_size.height,img_size.width,CV_32FC1,&arr_mapx[0]);
mapy = Mat(img_size.height,img_size.width,CV_32FC1,&arr_mapy[0]);
mat_type = CV_64F;
errcount += run_test_case( cv::Error::StsAssert, "Invalid map matrix type" );
mat_type = mat_type_orig;
camera_mat.create(3, 2, CV_32F);
errcount += run_test_case( cv::Error::StsAssert, "Invalid camera data matrix size" );
camera_mat = Mat(3,3,CV_64F,cam);
R.create(4, 3, CV_32F);
errcount += run_test_case( cv::Error::StsAssert, "Invalid R data matrix size" );
R = Mat(3,3,CV_64F,r);
distortion_coeffs.create(6, 1, CV_32F);
errcount += run_test_case( cv::Error::StsAssert, "Invalid distortion coefficients data matrix size" );
distortion_coeffs = Mat(1,4,CV_64F,dist);
//------------
ts->set_failed_test_info(errcount > 0 ? cvtest::TS::FAIL_BAD_ARG_CHECK : cvtest::TS::OK);
}
//=========
class CV_UndistortBadArgTest : public cvtest::BadArgTest
{
public:
CV_UndistortBadArgTest();
protected:
void run(int);
void run_func();
private:
//common
cv::Size img_size;
cv::Mat camera_mat;
cv::Mat new_camera_mat;
cv::Mat distortion_coeffs;
cv::Mat src;
cv::Mat dst;
};
CV_UndistortBadArgTest::CV_UndistortBadArgTest ()
{
}
void CV_UndistortBadArgTest::run_func()
{
cv::undistort(src,dst,camera_mat,distortion_coeffs,new_camera_mat);
}
void CV_UndistortBadArgTest::run(int)
{
int errcount = 0;
//initializing
img_size.width = 800;
img_size.height = 600;
double cam[9] = {150.f, 0.f, img_size.width/2.f, 0, 300.f, img_size.height/2.f, 0.f, 0.f, 1.f};
double dist[4] = {0.01,0.02,0.001,0.0005};
std::vector<float> arr_src(img_size.width*img_size.height);
std::vector<float> arr_dst(img_size.width*img_size.height);
double arr_new_camera_mat[9] = {155.f, 0.f, img_size.width/2.f+img_size.width/50.f, 0, 310.f, img_size.height/2.f+img_size.height/50.f, 0.f, 0.f, 1.f};
camera_mat = Mat(3,3,CV_64F,cam);
distortion_coeffs = Mat(1,4,CV_64F,dist);
new_camera_mat = Mat(3,3,CV_64F,arr_new_camera_mat);
src = Mat(img_size.height,img_size.width,CV_32FC1,&arr_src[0]);
dst = Mat(img_size.height,img_size.width,CV_32FC1,&arr_dst[0]);
camera_mat.create(5, 5, CV_64F);
errcount += run_test_case( cv::Error::StsAssert, "Invalid camera data matrix size" );
//------------
ts->set_failed_test_info(errcount > 0 ? cvtest::TS::FAIL_BAD_ARG_CHECK : cvtest::TS::OK);
}
TEST(Calib3d_UndistortPoints, badarg) { CV_UndistortPointsBadArgTest test; test.safe_run(); }
TEST(Calib3d_InitUndistortRectifyMap, badarg) { CV_InitUndistortRectifyMapBadArgTest test; test.safe_run(); }
TEST(Calib3d_Undistort, badarg) { CV_UndistortBadArgTest test; test.safe_run(); }
}} // namespace
/* End of file. */
@@ -0,0 +1,257 @@
// 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 <opencv2/ts/cuda_test.hpp> // EXPECT_MAT_NEAR
#include "opencv2/core/types.hpp"
#include "test_precomp.hpp"
namespace opencv_test { namespace {
class UndistortPointsTest : public ::testing::Test
{
protected:
void generate3DPointCloud(vector<Point3f>& points, Point3f pmin = Point3f(-1,
-1, 5), Point3f pmax = Point3f(1, 1, 10));
void generateCameraMatrix(Mat& cameraMatrix);
void generateDistCoeffs(Mat& distCoeffs, int count);
cv::Mat generateRotationVector();
std::vector<cv::Point2d> distortPoints(const cv::Mat &cameraMatrix, const cv::Mat &dist, const std::vector<cv::Point2d> &points);
double thresh = 1.0e-2;
};
void UndistortPointsTest::generate3DPointCloud(vector<Point3f>& points, Point3f pmin, Point3f pmax)
{
RNG rng_Point = cv::theRNG(); // fix the seed to use "fixed" input 3D points
for (size_t i = 0; i < points.size(); i++)
{
float _x = rng_Point.uniform(pmin.x, pmax.x);
float _y = rng_Point.uniform(pmin.y, pmax.y);
float _z = rng_Point.uniform(pmin.z, pmax.z);
points[i] = Point3f(_x, _y, _z);
}
}
void UndistortPointsTest::generateCameraMatrix(Mat& cameraMatrix)
{
const double fcMinVal = 1e-3;
const double fcMaxVal = 100;
cameraMatrix.create(3, 3, CV_64FC1);
cameraMatrix.setTo(Scalar(0));
cameraMatrix.at<double>(0,0) = theRNG().uniform(fcMinVal, fcMaxVal);
cameraMatrix.at<double>(1,1) = theRNG().uniform(fcMinVal, fcMaxVal);
cameraMatrix.at<double>(0,2) = theRNG().uniform(fcMinVal, fcMaxVal);
cameraMatrix.at<double>(1,2) = theRNG().uniform(fcMinVal, fcMaxVal);
cameraMatrix.at<double>(2,2) = 1;
}
void UndistortPointsTest::generateDistCoeffs(Mat& distCoeffs, int count)
{
distCoeffs = Mat::zeros(count, 1, CV_64FC1);
for (int i = 0; i < count; i++)
distCoeffs.at<double>(i,0) = theRNG().uniform(-0.1, 0.1);
}
cv::Mat UndistortPointsTest::generateRotationVector()
{
Mat rvec(1, 3, CV_64F);
theRNG().fill(rvec, RNG::UNIFORM, -0.2, 0.2);
return rvec;
}
std::vector<cv::Point2d> UndistortPointsTest::distortPoints(const cv::Mat &cameraMatrix, const cv::Mat &dist, const std::vector<cv::Point2d> &points)
{
CV_Assert(cameraMatrix.rows == 3 && cameraMatrix.cols == 3);
CV_Assert(cameraMatrix.type() == CV_64F);
CV_Assert(dist.rows * dist.cols == 12);
CV_Assert(dist.type() == CV_64F);
double *k = reinterpret_cast<double *>(dist.data);
double fx = cameraMatrix.at<double>(0, 0);
double fy = cameraMatrix.at<double>(1, 1);
double cx = cameraMatrix.at<double>(0, 2);
double cy = cameraMatrix.at<double>(1, 2);
std::vector<cv::Point2d> distortedPoints;
distortedPoints.reserve(points.size());
for (const cv::Point2d p : points) {
double x = (p.x - cx) / fx;
double y = (p.y - cy) / fy;
double r2 = x*x + y*y;
double cdist = (1 + ((k[4]*r2 + k[1])*r2 + k[0])*r2)/(1 + ((k[7]*r2 + k[6])*r2 + k[5])*r2);
CV_Assert(cdist >= 0);
double deltaX = 2*k[2]*x*y + k[3]*(r2 + 2*x*x)+ k[8]*r2+k[9]*r2*r2;
double deltaY = k[2]*(r2 + 2*y*y) + 2*k[3]*x*y+ k[10]*r2+k[11]*r2*r2;
distortedPoints.push_back(cv::Point2d((x * cdist + deltaX) * fx + cx, (y * cdist + deltaY) * fy + cy));
}
return distortedPoints;
}
TEST_F(UndistortPointsTest, accuracy)
{
Mat intrinsics, distCoeffs;
generateCameraMatrix(intrinsics);
vector<Point3f> points(500);
generate3DPointCloud(points);
Mat rvec = generateRotationVector();
Mat R;
cv::Rodrigues(rvec, R);
int modelMembersCount[] = {4,5,8};
for (int idx = 0; idx < 3; idx++)
{
generateDistCoeffs(distCoeffs, modelMembersCount[idx]);
/* Project points with distortion */
vector<Point2f> projectedPoints;
projectPoints(Mat(points), Mat::zeros(3,1,CV_64FC1),
Mat::zeros(3,1,CV_64FC1), intrinsics,
distCoeffs, projectedPoints);
/* Project points without distortion */
vector<Point2f> realUndistortedPoints;
projectPoints(Mat(points), rvec,
Mat::zeros(3,1,CV_64FC1), intrinsics,
Mat::zeros(4,1,CV_64FC1), realUndistortedPoints);
/* Undistort points */
Mat undistortedPoints;
undistortPoints(Mat(projectedPoints), undistortedPoints, intrinsics, distCoeffs, R, intrinsics);
EXPECT_MAT_NEAR(realUndistortedPoints, undistortedPoints.t(), thresh);
}
}
TEST_F(UndistortPointsTest, undistortImagePointsAccuracy)
{
Mat intrinsics, distCoeffs;
generateCameraMatrix(intrinsics);
vector<Point3f> points(500);
generate3DPointCloud(points);
int modelMembersCount[] = {4,5,8};
for (int idx = 0; idx < 3; idx++)
{
generateDistCoeffs(distCoeffs, modelMembersCount[idx]);
/* Project points with distortion */
vector<Point2f> projectedPoints;
projectPoints(Mat(points), Mat::zeros(3,1,CV_64FC1),
Mat::zeros(3,1,CV_64FC1), intrinsics,
distCoeffs, projectedPoints);
/* Project points without distortion */
vector<Point2f> realUndistortedPoints;
projectPoints(Mat(points), Mat::zeros(3, 1, CV_64FC1),
Mat::zeros(3,1,CV_64FC1), intrinsics,
Mat::zeros(4,1,CV_64FC1), realUndistortedPoints);
/* Undistort points */
Mat undistortedPoints;
TermCriteria termCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 5, thresh / 2);
undistortImagePoints(Mat(projectedPoints), undistortedPoints, intrinsics, distCoeffs,
termCriteria);
EXPECT_MAT_NEAR(realUndistortedPoints, undistortedPoints.t(), thresh);
}
}
TEST_F(UndistortPointsTest, stop_criteria)
{
Mat cameraMatrix = (Mat_<double>(3,3,CV_64F) << 857.48296979, 0, 968.06224829,
0, 876.71824265, 556.37145899,
0, 0, 1);
Mat distCoeffs = (Mat_<double>(5,1,CV_64F) <<
-2.57614020e-01, 8.77086999e-02, -2.56970803e-04, -5.93390389e-04, -1.52194091e-02);
Point2d pt_distorted(theRNG().uniform(0.0, 1920.0), theRNG().uniform(0.0, 1080.0));
std::vector<Point2d> pt_distorted_vec;
pt_distorted_vec.push_back(pt_distorted);
const double maxError = 1e-6;
TermCriteria criteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 100, maxError);
std::vector<Point2d> pt_undist_vec;
Mat rVec = Mat(Matx31d(0.1, -0.2, 0.2));
Mat R;
cv::Rodrigues(rVec, R);
undistortPoints(pt_distorted_vec, pt_undist_vec, cameraMatrix, distCoeffs, R, noArray(), criteria);
std::vector<Point3d> pt_undist_vec_homogeneous;
pt_undist_vec_homogeneous.emplace_back(pt_undist_vec[0].x, pt_undist_vec[0].y, 1.0 );
std::vector<Point2d> pt_redistorted_vec;
projectPoints(pt_undist_vec_homogeneous, -rVec,
Mat::zeros(3,1,CV_64F), cameraMatrix, distCoeffs, pt_redistorted_vec);
const double obtainedError = sqrt( std::pow(pt_distorted.x - pt_redistorted_vec[0].x, 2) + std::pow(pt_distorted.y - pt_redistorted_vec[0].y, 2) );
ASSERT_LE(obtainedError, maxError);
}
TEST_F(UndistortPointsTest, regression_14583)
{
const int col = 720;
// const int row = 540;
float camera_matrix_value[] = {
437.8995f, 0.0f, 342.9241f,
0.0f, 438.8216f, 273.7163f,
0.0f, 0.0f, 1.0f
};
cv::Mat camera_interior(3, 3, CV_32F, camera_matrix_value);
float camera_distort_value[] = {-0.34329f, 0.11431f, 0.0f, 0.0f, -0.017375f};
cv::Mat camera_distort(1, 5, CV_32F, camera_distort_value);
float distort_points_value[] = {col, 0.};
cv::Mat distort_pt(1, 1, CV_32FC2, distort_points_value);
cv::Mat undistort_pt;
cv::undistortPoints(distort_pt, undistort_pt, camera_interior,
camera_distort, cv::Mat(), camera_interior);
EXPECT_NEAR(distort_pt.at<Vec2f>(0)[0], undistort_pt.at<Vec2f>(0)[0], col / 2)
<< "distort point: " << distort_pt << std::endl
<< "undistort point: " << undistort_pt;
}
TEST_F(UndistortPointsTest, regression_27916)
{
cv::Mat K = (cv::Mat_<double>(3, 3) <<
1570.8956145992222, 0., 744.87337646727406, 0.,
1570.3494207432338, 575.55087456337526, 0., 0., 1.);
cv::Mat dist = (cv::Mat_<double>(1, 12) <<
-2.8247717583453804, -0.80078070764368037,
-0.014595359484103326, 0.0018820998949700702, 1.9827795585249783,
-2.7306773773930897, -1.217725820479524, 2.4052243546080136,
-0.0020670359760441713, 3.4660880793174063e-05,
0.014100351510458799, -3.0935329736207612e-05);
const cv::TermCriteria termCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, 100, thresh / 2);
std::vector<cv::Point2d> distortedPoints, distortedPoints2;
std::vector<cv::Point2d> undistortedPoints;
for (int i = 0; i < 50; i++)
{
for (int j = 0; j < 50; j++)
{
distortedPoints.push_back(cv::Point2d(i, j));
}
}
cv::undistortPoints(distortedPoints, undistortedPoints, K, dist, cv::noArray(), K, termCriteria);
distortedPoints2 = distortPoints(K, dist, undistortedPoints);
EXPECT_MAT_NEAR(distortedPoints2, distortedPoints, thresh);
}
}} // namespace
+515
View File
@@ -0,0 +1,515 @@
// 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 "test_precomp.hpp"
namespace opencv_test { namespace {
enum TestSolver { Homogr, Fundam, Essen, PnP, Affine};
/*
* rng -- reference to random generator
* pts1 -- 2xN image points
* pts2 -- for PnP is 3xN object points, otherwise 2xN image points.
* two_calib -- True if two cameras have different calibration.
* K1 -- intrinsic matrix of the first camera. For PnP only one camera.
* K2 -- only if two_calib is True.
* pts_size -- required size of points.
* inlier_ratio -- required inlier ratio
* noise_std -- standard deviation of Gaussian noise of image points.
* gt_inliers -- has size of number of inliers. Contains indices of inliers.
*/
static int generatePoints (cv::RNG &rng, cv::Mat &pts1, cv::Mat &pts2, cv::Mat &K1, cv::Mat &K2,
bool two_calib, int pts_size, TestSolver test_case, double inlier_ratio, double noise_std,
std::vector<int> &gt_inliers) {
auto eulerAnglesToRotationMatrix = [] (double pitch, double yaw, double roll) {
// Calculate rotation about x axis
cv::Matx33d R_x (1, 0, 0, 0, cos(roll), -sin(roll), 0, sin(roll), cos(roll));
// Calculate rotation about y axis
cv::Matx33d R_y (cos(pitch), 0, sin(pitch), 0, 1, 0, -sin(pitch), 0, cos(pitch));
// Calculate rotation about z axis
cv::Matx33d R_z (cos(yaw), -sin(yaw), 0, sin(yaw), cos(yaw), 0, 0, 0, 1);
return cv::Mat(R_z * R_y * R_x); // Combined rotation matrix
};
const double pitch_min = -CV_PI / 6, pitch_max = CV_PI / 6; // 30 degrees
const double yaw_min = -CV_PI / 6, yaw_max = CV_PI / 6;
const double roll_min = -CV_PI / 6, roll_max = CV_PI / 6;
cv::Mat R = eulerAnglesToRotationMatrix(rng.uniform(pitch_min, pitch_max),
rng.uniform(yaw_min, yaw_max), rng.uniform(roll_min, roll_max));
// generate random translation,
// if test for homography fails try to fix translation to zero vec so H is related by transl.
cv::Vec3d t (rng.uniform(-0.5f, 0.5f), rng.uniform(-0.5f, 0.5f), rng.uniform(1.0f, 2.0f));
// generate random calibration
auto getRandomCalib = [&] () {
return cv::Mat(cv::Matx33d(rng.uniform(100.0, 1000.0), 0, rng.uniform(100.0, 100.0),
0, rng.uniform(100.0, 1000.0), rng.uniform(-100.0, 100.0),
0, 0, 1.));
};
K1 = getRandomCalib();
K2 = two_calib ? getRandomCalib() : K1.clone();
auto updateTranslation = [] (const cv::Mat &pts, const cv::Mat &R_, cv::Vec3d &t_) {
// Make sure the shape is in front of the camera
cv::Mat points3d_transformed = R_ * pts + t_ * cv::Mat::ones(1, pts.cols, pts.type());
double min_dist, max_dist;
cv::minMaxIdx(points3d_transformed.row(2), &min_dist, &max_dist);
if (min_dist < 0) t_(2) -= min_dist + 1.0;
};
// compute size of inliers and outliers
const int inl_size = static_cast<int>(inlier_ratio * pts_size);
const int out_size = pts_size - inl_size;
// all points will have top 'inl_size' of their points inliers
gt_inliers.clear(); gt_inliers.reserve(inl_size);
for (int i = 0; i < inl_size; i++)
gt_inliers.emplace_back(i);
// double precision to multiply points by models
const int pts_type = CV_64F;
cv::Mat points3d;
if (test_case == TestSolver::Homogr) {
points3d.create(2, inl_size, pts_type);
rng.fill(points3d, cv::RNG::UNIFORM, 0.0, 1.0); // keep small range
// inliers must be planar points, let their 3D coordinate be 1
cv::vconcat(points3d, cv::Mat::ones(1, inl_size, points3d.type()), points3d);
} else if (test_case == TestSolver::Fundam || test_case == TestSolver::Essen) {
// create 3D points which are inliers
points3d.create(3, inl_size, pts_type);
rng.fill(points3d, cv::RNG::UNIFORM, 0.0, 1.0);
} else if (test_case == TestSolver::PnP) {
//pts1 are image points, pts2 are object points
pts2.create(3, inl_size, pts_type); // 3D inliers
rng.fill(pts2, cv::RNG::UNIFORM, 0, 1);
updateTranslation(pts2, R, t);
// project 3D points (pts2) on image plane (pts1)
pts1 = K1 * (R * pts2 + t * cv::Mat::ones(1, pts2.cols, pts2.type()));
cv::divide(pts1.row(0), pts1.row(2), pts1.row(0));
cv::divide(pts1.row(1), pts1.row(2), pts1.row(1));
// make 2D points
pts1 = pts1.rowRange(0, 2);
// create random outliers
cv::Mat pts_outliers = cv::Mat(5, out_size, pts2.type());
rng.fill(pts_outliers, cv::RNG::UNIFORM, 0, 1000);
// merge inliers with random image points = outliers
cv::hconcat(pts1, pts_outliers.rowRange(0, 2), pts1);
// merge 3D inliers with 3D outliers
cv::hconcat(pts2, pts_outliers.rowRange(2, 5), pts2);
// add Gaussian noise to image points
cv::Mat noise(pts1.rows, pts1.cols, pts1.type());
rng.fill(noise, cv::RNG::NORMAL, 0, noise_std);
pts1 += noise;
return inl_size;
} else if (test_case == TestSolver::Affine) {
} else
CV_Error(cv::Error::StsBadArg, "Unknown solver!");
if (test_case != TestSolver::PnP) {
// project 3D point on image plane
// use two relative scenes. The first camera is P1 = K1 [I | 0], the second P2 = K2 [R | t]
if (test_case != TestSolver::Affine) {
updateTranslation(points3d, R, t);
pts1 = K1 * points3d;
pts2 = K2 * (R * points3d + t * cv::Mat::ones(1, points3d.cols, points3d.type()));
// normalize by 3 coordinate
cv::divide(pts1.row(0), pts1.row(2), pts1.row(0));
cv::divide(pts1.row(1), pts1.row(2), pts1.row(1));
cv::divide(pts2.row(0), pts2.row(2), pts2.row(0));
cv::divide(pts2.row(1), pts2.row(2), pts2.row(1));
} else {
pts1 = cv::Mat(2, inl_size, pts_type);
rng.fill(pts1, cv::RNG::UNIFORM, 0, 1000);
cv::Matx33d sc(rng.uniform(1., 5.),0,0,rng.uniform(1., 4.),0,0, 0, 0, 1);
cv::Matx33d tr(1,0,rng.uniform(50., 500.),0,1,rng.uniform(50., 500.), 0, 0, 1);
const double phi = rng.uniform(0., CV_PI);
cv::Matx33d rot(cos(phi), -sin(phi),0, sin(phi), cos(phi),0, 0, 0, 1);
cv::Matx33d A = sc * tr * rot;
cv::vconcat(pts1, cv::Mat::ones(1, pts1.cols, pts1.type()), points3d);
pts2 = A * points3d;
}
// get 2D points
pts1 = pts1.rowRange(0,2); pts2 = pts2.rowRange(0,2);
// generate random outliers as 2D image points
cv::Mat pts1_outliers(pts1.rows, out_size, pts1.type()),
pts2_outliers(pts2.rows, out_size, pts2.type());
rng.fill(pts1_outliers, cv::RNG::UNIFORM, 0, 1000);
rng.fill(pts2_outliers, cv::RNG::UNIFORM, 0, 1000);
// merge inliers and outliers
cv::hconcat(pts1, pts1_outliers, pts1);
cv::hconcat(pts2, pts2_outliers, pts2);
// add normal / Gaussian noise to image points
cv::Mat noise1 (pts1.rows, pts1.cols, pts1.type()), noise2 (pts2.rows, pts2.cols, pts2.type());
rng.fill(noise1, cv::RNG::NORMAL, 0, noise_std); pts1 += noise1;
rng.fill(noise2, cv::RNG::NORMAL, 0, noise_std); pts2 += noise2;
}
return inl_size;
}
/*
* for test case = 0, 1, 2 (homography and epipolar geometry): pts1 and pts2 are 3xN
* for test_case = 3 (PnP): pts1 are 3xN and pts2 are 4xN
* all points are of the same type as model
*/
static double getError (TestSolver test_case, int pt_idx, const cv::Mat &pts1, const cv::Mat &pts2, const cv::Mat &model) {
cv::Mat pt1 = pts1.col(pt_idx), pt2 = pts2.col(pt_idx);
if (test_case == TestSolver::Homogr) { // reprojection error
// compute Euclidean distance between given and reprojected points
cv::Mat est_pt2 = model * pt1; est_pt2 /= est_pt2.at<double>(2);
if (false) {
cv::Mat est_pt1 = model.inv() * pt2; est_pt1 /= est_pt1.at<double>(2);
return (cv::norm(est_pt1 - pt1) + cv::norm(est_pt2 - pt2)) / 2;
}
return cv::norm(est_pt2 - pt2);
} else
if (test_case == TestSolver::Fundam || test_case == TestSolver::Essen) {
cv::Mat l2 = model * pt1;
cv::Mat l1 = model.t() * pt2;
// Sampson error
return fabs(pt2.dot(l2)) / sqrt(std::pow(l1.at<double>(0), 2) + std::pow(l1.at<double>(1), 2) +
std::pow(l2.at<double>(0), 2) + std::pow(l2.at<double>(1), 2));
} else
if (test_case == TestSolver::PnP) { // PnP, reprojection error
cv::Mat img_pt = model * pt2; img_pt /= img_pt.at<double>(2);
return cv::norm(pt1 - img_pt);
} else
CV_Error(cv::Error::StsBadArg, "Undefined test case!");
}
/*
* inl_size -- number of ground truth inliers
* pts1 and pts2 are of the same size as from function generatePoints(...)
*/
static void checkInliersMask (TestSolver test_case, int inl_size, double thr, const cv::Mat &pts1_,
const cv::Mat &pts2_, const cv::Mat &model, const cv::Mat &mask) {
ASSERT_TRUE(!model.empty() && !mask.empty());
cv::Mat pts1 = pts1_, pts2 = pts2_;
if (pts1.type() != model.type()) {
pts1.convertTo(pts1, model.type());
pts2.convertTo(pts2, model.type());
}
// convert to homogeneous
cv::vconcat(pts1, cv::Mat::ones(1, pts1.cols, pts1.type()), pts1);
cv::vconcat(pts2, cv::Mat::ones(1, pts2.cols, pts2.type()), pts2);
thr *= 1.001; // increase a little threshold due to numerical imprecisions
const auto * const mask_ptr = mask.ptr<uchar>();
int num_found_inliers = 0;
for (int i = 0; i < pts1.cols; i++)
if (mask_ptr[i]) {
ASSERT_LT(getError(test_case, i, pts1, pts2, model), thr);
num_found_inliers++;
}
// check if RANSAC found at least 80% of inliers
ASSERT_GT(num_found_inliers, 0.8 * inl_size);
}
TEST(usac_Homography, accuracy) {
std::vector<int> gt_inliers;
const int pts_size = 1500;
cv::RNG &rng = cv::theRNG();
// do not test USAC_PARALLEL, because it is not deterministic
const std::vector<int> flags = {USAC_DEFAULT, USAC_ACCURATE, USAC_PROSAC, USAC_FAST, USAC_MAGSAC};
for (double inl_ratio = 0.1; inl_ratio < 0.91; inl_ratio += 0.1) {
cv::Mat pts1, pts2, K1, K2;
int inl_size = generatePoints(rng, pts1, pts2, K1, K2, false /*two calib*/,
pts_size, TestSolver ::Homogr, inl_ratio/*inl ratio*/, 0.1 /*noise std*/, gt_inliers);
// compute max_iters with standard upper bound rule for RANSAC with 1.5x tolerance
const double conf = 0.99, thr = 2., max_iters = 1.3 * log(1 - conf) /
log(1 - std::pow(inl_ratio, 4 /* sample size */));
for (auto flag : flags) {
cv::Mat mask, H = cv::findHomography(pts1, pts2,flag, thr, mask,
int(max_iters), conf);
checkInliersMask(TestSolver::Homogr, inl_size, thr, pts1, pts2, H, mask);
}
}
}
TEST(usac_Fundamental, accuracy) {
std::vector<int> gt_inliers;
const int pts_size = 2000;
cv::RNG &rng = cv::theRNG();
// start from 25% otherwise max_iters will be too big
const std::vector<int> flags = {USAC_DEFAULT, USAC_FM_8PTS, USAC_ACCURATE, USAC_PROSAC, USAC_FAST, USAC_MAGSAC};
const double conf = 0.99, thr = 1.;
for (double inl_ratio = 0.25; inl_ratio < 0.91; inl_ratio += 0.1) {
cv::Mat pts1, pts2, K1, K2;
int inl_size = generatePoints(rng, pts1, pts2, K1, K2, false /*two calib*/,
pts_size, TestSolver ::Fundam, inl_ratio, 0.1 /*noise std*/, gt_inliers);
for (auto flag : flags) {
const int sample_size = flag == USAC_FM_8PTS ? 8 : 7;
const double max_iters = 1.25 * log(1 - conf) /
log(1 - std::pow(inl_ratio, sample_size));
cv::Mat mask, F = cv::findFundamentalMat(pts1, pts2,flag, thr, conf,
int(max_iters), mask);
checkInliersMask(TestSolver::Fundam, inl_size, thr, pts1, pts2, F, mask);
}
}
}
TEST(usac_Fundamental, regression_19639)
{
double x_[] = {
941, 890,
596, 940,
898, 941,
894, 933,
586, 938,
902, 933,
887, 935
};
Mat x(7, 1, CV_64FC2, x_);
double y_[] = {
1416, 806,
1157, 852,
1380, 855,
1378, 843,
1145, 849,
1378, 843,
1378, 843
};
Mat y(7, 1, CV_64FC2, y_);
//std::cout << x << std::endl;
//std::cout << y << std::endl;
Mat m = cv::findFundamentalMat(x, y, USAC_MAGSAC, 3, 0.99);
EXPECT_TRUE(m.empty());
}
CV_ENUM(UsacMethod, USAC_DEFAULT, USAC_ACCURATE, USAC_PROSAC, USAC_FAST, USAC_MAGSAC)
typedef TestWithParam<UsacMethod> usac_Essential;
TEST_P(usac_Essential, accuracy) {
int method = GetParam();
std::vector<int> gt_inliers;
const int pts_size = 1500;
cv::RNG &rng = cv::theRNG();
// findEssentilaMat has by default number of maximum iterations equal to 1000.
// It means that with 99% confidence we assume at least 34.08% of inliers
const std::vector<int> flags = {USAC_DEFAULT, USAC_ACCURATE, USAC_PROSAC, USAC_FAST, USAC_MAGSAC};
for (double inl_ratio = 0.35; inl_ratio < 0.91; inl_ratio += 0.1) {
cv::Mat pts1, pts2, K1, K2;
int inl_size = generatePoints(rng, pts1, pts2, K1, K2, false /*two calib*/,
pts_size, TestSolver ::Fundam, inl_ratio, 0.01 /*noise std, works bad with high noise*/, gt_inliers);
const double conf = 0.99, thr = 1.;
cv::Mat mask, E;
try {
E = cv::findEssentialMat(pts1, pts2, K1, method, conf, thr, 1000/*maxIters*/, mask);
} catch (cv::Exception &e) {
if (e.code != cv::Error::StsNotImplemented)
FAIL() << "Essential matrix estimation failed!\n";
else continue;
}
// calibrate points
cv::Mat cpts1_3d, cpts2_3d;
cv::vconcat(pts1, cv::Mat::ones(1, pts1.cols, pts1.type()), cpts1_3d);
cv::vconcat(pts2, cv::Mat::ones(1, pts2.cols, pts2.type()), cpts2_3d);
cpts1_3d = K1.inv() * cpts1_3d; cpts2_3d = K1.inv() * cpts2_3d;
checkInliersMask(TestSolver::Essen, inl_size, thr / ((K1.at<double>(0,0) + K1.at<double>(1,1)) / 2),
cpts1_3d.rowRange(0,2), cpts2_3d.rowRange(0,2), E, mask);
}
}
TEST_P(usac_Essential, maxiters) {
int method = GetParam();
cv::RNG &rng = cv::theRNG();
cv::Mat mask;
cv::Mat K1 = cv::Mat(cv::Matx33d(1, 0, 0,
0, 1, 0,
0, 0, 1.));
const double conf = 0.99, thr = 0.25;
int roll_results_sum = 0;
for (int iters = 0; iters < 10; iters++) {
cv::Mat E1, E2;
try {
cv::Mat pts1 = cv::Mat(2, 50, CV_64F);
cv::Mat pts2 = cv::Mat(2, 50, CV_64F);
rng.fill(pts1, cv::RNG::UNIFORM, 0.0, 1.0);
rng.fill(pts2, cv::RNG::UNIFORM, 0.0, 1.0);
E1 = cv::findEssentialMat(pts1, pts2, K1, method, conf, thr, 1, mask);
E2 = cv::findEssentialMat(pts1, pts2, K1, method, conf, thr, 1000, mask);
if (E1.dims != E2.dims) { continue; }
roll_results_sum += cv::norm(E1, E2, NORM_L1) != 0;
} catch (cv::Exception &e) {
if (e.code != cv::Error::StsNotImplemented)
FAIL() << "Essential matrix estimation failed!\n";
else continue;
}
}
EXPECT_NE(roll_results_sum, 0);
}
INSTANTIATE_TEST_CASE_P(Calib3d, usac_Essential, UsacMethod::all());
TEST(usac_P3P, accuracy) {
std::vector<int> gt_inliers;
const int pts_size = 3000;
cv::Mat img_pts, obj_pts, K1, K2;
cv::RNG &rng = cv::theRNG();
const std::vector<int> flags = {USAC_DEFAULT, USAC_ACCURATE, USAC_PROSAC, USAC_FAST, USAC_MAGSAC};
for (double inl_ratio = 0.1; inl_ratio < 0.91; inl_ratio += 0.1) {
int inl_size = generatePoints(rng, img_pts, obj_pts, K1, K2, false /*two calib*/,
pts_size, TestSolver ::PnP, inl_ratio, 0.15 /*noise std*/, gt_inliers);
const double conf = 0.99, thr = 2., max_iters = 1.3 * log(1 - conf) /
log(1 - std::pow(inl_ratio, 3 /* sample size */));
for (auto flag : flags) {
std::vector<int> inliers;
cv::Mat rvec, tvec, mask, R, P;
CV_Assert(cv::solvePnPRansac(obj_pts, img_pts, K1, cv::noArray(), rvec, tvec,
false, (int)max_iters, (float)thr, conf, inliers, flag));
cv::Rodrigues(rvec, R);
cv::hconcat(K1 * R, K1 * tvec, P);
mask.create(pts_size, 1, CV_8U);
mask.setTo(Scalar::all(0));
for (auto inl : inliers)
mask.at<uchar>(inl) = true;
checkInliersMask(TestSolver ::PnP, inl_size, thr, img_pts, obj_pts, P, mask);
}
}
}
TEST (usac_Affine2D, accuracy) {
std::vector<int> gt_inliers;
const int pts_size = 2000;
cv::Mat pts1, pts2, K1, K2;
cv::RNG &rng = cv::theRNG();
const std::vector<int> flags = {USAC_DEFAULT, USAC_ACCURATE, USAC_PROSAC, USAC_FAST, USAC_MAGSAC};
for (double inl_ratio = 0.1; inl_ratio < 0.91; inl_ratio += 0.1) {
int inl_size = generatePoints(rng, pts1, pts2, K1, K2, false /*two calib*/,
pts_size, TestSolver ::Affine, inl_ratio, 0.15 /*noise std*/, gt_inliers);
const double conf = 0.99, thr = 2., max_iters = 1.3 * log(1 - conf) /
log(1 - std::pow(inl_ratio, 3 /* sample size */));
for (auto flag : flags) {
cv::Mat mask, A = cv::estimateAffine2D(pts1, pts2, mask, flag, thr, (size_t)max_iters, conf, 0);
cv::vconcat(A, cv::Mat(cv::Matx13d(0,0,1)), A);
checkInliersMask(TestSolver::Homogr /*use homography error*/, inl_size, thr, pts1, pts2, A, mask);
}
}
}
TEST(usac_testUsacParams, accuracy) {
std::vector<int> gt_inliers;
const int pts_size = 1500;
cv::RNG &rng = cv::theRNG();
const cv::UsacParams usac_params = cv::UsacParams();
cv::Mat pts1, pts2, K1, K2, mask, model, rvec, tvec, R;
int inl_size;
auto getInlierRatio = [] (int max_iters, int sample_size, double conf) {
return std::pow(1 - exp(log(1 - conf)/(double)max_iters), 1 / (double)sample_size);
};
cv::Vec4d dist_coeff (0, 0, 0, 0); // test with 0 distortion
// Homography matrix
inl_size = generatePoints(rng, pts1, pts2, K1, K2, false, pts_size, TestSolver::Homogr,
getInlierRatio(usac_params.maxIterations, 4, usac_params.confidence), 0.1, gt_inliers);
model = cv::findHomography(pts1, pts2, mask, usac_params);
checkInliersMask(TestSolver::Homogr, inl_size, usac_params.threshold, pts1, pts2, model, mask);
// Fundamental matrix
inl_size = generatePoints(rng, pts1, pts2, K1, K2, false, pts_size, TestSolver::Fundam,
getInlierRatio(usac_params.maxIterations, 7, usac_params.confidence), 0.1, gt_inliers);
model = cv::findFundamentalMat(pts1, pts2, mask, usac_params);
checkInliersMask(TestSolver::Fundam, inl_size, usac_params.threshold, pts1, pts2, model, mask);
// Essential matrix
inl_size = generatePoints(rng, pts1, pts2, K1, K2, true, pts_size, TestSolver::Essen,
getInlierRatio(usac_params.maxIterations, 5, usac_params.confidence), 0.01, gt_inliers);
try {
model = cv::findEssentialMat(pts1, pts2, K1, K2, dist_coeff, dist_coeff, mask, usac_params);
cv::Mat cpts1_3d, cpts2_3d;
cv::vconcat(pts1, cv::Mat::ones(1, pts1.cols, pts1.type()), cpts1_3d);
cv::vconcat(pts2, cv::Mat::ones(1, pts2.cols, pts2.type()), cpts2_3d);
cpts1_3d = K1.inv() * cpts1_3d; cpts2_3d = K2.inv() * cpts2_3d;
checkInliersMask(TestSolver::Essen, inl_size, usac_params.threshold /
((K1.at<double>(0,0) + K1.at<double>(1,1) + K2.at<double>(0,0) + K2.at<double>(1,1)) / 4),
cpts1_3d.rowRange(0,2), cpts2_3d.rowRange(0,2), model, mask);
} catch (cv::Exception &e) {
if (e.code != cv::Error::StsNotImplemented)
FAIL() << "Essential matrix estimation failed!\n";
// CV_Error(cv::Error::StsError, "Essential matrix estimation failed!");
}
std::vector<int> inliers(pts_size);
// P3P
inl_size = generatePoints(rng, pts1, pts2, K1, K2, false, pts_size, TestSolver::PnP,
getInlierRatio(usac_params.maxIterations, 3, usac_params.confidence), 0.01, gt_inliers);
CV_Assert(cv::solvePnPRansac(pts2, pts1, K1, dist_coeff, rvec, tvec, inliers, usac_params));
cv::Rodrigues(rvec, R); cv::hconcat(K1 * R, K1 * tvec, model);
mask.create(pts_size, 1, CV_8U);
mask.setTo(Scalar::all(0));
for (auto inl : inliers)
mask.at<uchar>(inl) = true;
checkInliersMask(TestSolver::PnP, inl_size, usac_params.threshold, pts1, pts2, model, mask);
// P6P
inl_size = generatePoints(rng, pts1, pts2, K1, K2, false, pts_size, TestSolver::PnP,
getInlierRatio(usac_params.maxIterations, 6, usac_params.confidence), 0.1, gt_inliers);
cv::Mat K_est;
CV_Assert(cv::solvePnPRansac(pts2, pts1, K_est, dist_coeff, rvec, tvec, inliers, usac_params));
cv::Rodrigues(rvec, R); cv::hconcat(K_est * R, K_est * tvec, model);
mask.setTo(Scalar::all(0));
for (auto inl : inliers)
mask.at<uchar>(inl) = true;
checkInliersMask(TestSolver::PnP, inl_size, usac_params.threshold, pts1, pts2, model, mask);
// Affine2D
inl_size = generatePoints(rng, pts1, pts2, K1, K2, false, pts_size, TestSolver::Affine,
getInlierRatio(usac_params.maxIterations, 3, usac_params.confidence), 0.1, gt_inliers);
model = cv::estimateAffine2D(pts1, pts2, mask, usac_params);
cv::vconcat(model, cv::Mat(cv::Matx13d(0,0,1)), model);
checkInliersMask(TestSolver::Homogr, inl_size, usac_params.threshold, pts1, pts2, model, mask);
}
TEST(usac_solvePnPRansac, regression_21105) {
std::vector<int> gt_inliers;
const int pts_size = 100;
double inl_ratio = 0.1;
cv::Mat img_pts, obj_pts, K1, K2;
cv::RNG &rng = cv::theRNG();
generatePoints(rng, img_pts, obj_pts, K1, K2, false /*two calib*/,
pts_size, TestSolver ::PnP, inl_ratio, 0.15 /*noise std*/, gt_inliers);
const double conf = 0.99, thr = 2., max_iters = 1.3 * log(1 - conf) /
log(1 - std::pow(inl_ratio, 3 /* sample size */));
const int flag = USAC_DEFAULT;
std::vector<int> inliers;
cv::Matx31d rvec, tvec;
CV_Assert(cv::solvePnPRansac(obj_pts, img_pts, K1, cv::noArray(), rvec, tvec,
false, (int)max_iters, (float)thr, conf, inliers, flag));
cv::Mat zero_column = cv::Mat::zeros(3, 1, K1.type());
cv::hconcat(K1, zero_column, K1);
cv::Mat K1_copy = K1.colRange(0, 3);
std::vector<int> inliers_copy;
cv::Matx31d rvec_copy, tvec_copy;
CV_Assert(cv::solvePnPRansac(obj_pts, img_pts, K1_copy, cv::noArray(), rvec_copy, tvec_copy,
false, (int)max_iters, (float)thr, conf, inliers_copy, flag));
EXPECT_EQ(rvec, rvec_copy);
EXPECT_EQ(tvec, tvec_copy);
EXPECT_EQ(inliers, inliers_copy);
}
}} // namespace