feat: 切换后端至PaddleOCR-NCNN,切换工程为CMake

1.项目后端整体迁移至PaddleOCR-NCNN算法,已通过基本的兼容性测试
2.工程改为使用CMake组织,后续为了更好地兼容第三方库,不再提供QMake工程
3.重整权利声明文件,重整代码工程,确保最小化侵权风险

Log: 切换后端至PaddleOCR-NCNN,切换工程为CMake
Change-Id: I4d5d2c5d37505a4a24b389b1a4c5d12f17bfa38c
This commit is contained in:
wangzhengyang
2022-05-10 09:54:44 +08:00
parent ecdd171c6f
commit 718c41634f
10018 changed files with 3593797 additions and 186748 deletions

View File

@ -0,0 +1,97 @@
///////////////////////////////////////////////////////////////////////////////////////
//
// 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) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, 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 "cvconfig.h"
#include "opencv2/ts/ocl_test.hpp"
#ifdef HAVE_OPENCL
namespace opencv_test {
namespace ocl {
PARAM_TEST_CASE(StereoBMFixture, int, int)
{
int n_disp;
int winSize;
Mat left, right, disp;
UMat uleft, uright, udisp;
virtual void SetUp()
{
n_disp = GET_PARAM(0);
winSize = GET_PARAM(1);
left = readImage("gpu/stereobm/aloe-L.png", IMREAD_GRAYSCALE);
right = readImage("gpu/stereobm/aloe-R.png", IMREAD_GRAYSCALE);
ASSERT_FALSE(left.empty());
ASSERT_FALSE(right.empty());
left.copyTo(uleft);
right.copyTo(uright);
}
void Near(double eps = 0.0)
{
EXPECT_MAT_NEAR_RELATIVE(disp, udisp, eps);
}
};
OCL_TEST_P(StereoBMFixture, StereoBM)
{
Ptr<StereoBM> bm = StereoBM::create( n_disp, winSize);
bm->setPreFilterType(bm->PREFILTER_XSOBEL);
bm->setTextureThreshold(0);
OCL_OFF(bm->compute(left, right, disp));
OCL_ON(bm->compute(uleft, uright, udisp));
Near(1e-3);
}
OCL_INSTANTIATE_TEST_CASE_P(StereoMatcher, StereoBMFixture, testing::Combine(testing::Values(32, 64, 128),
testing::Values(11, 21)));
}//ocl
}//cvtest
#endif //HAVE_OPENCL

View File

@ -0,0 +1,197 @@
/*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)
{
// 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, GetParam() /* 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 = count(inliers.begin(), inliers.end(), 1) == m &&
m == 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

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

View File

@ -0,0 +1,225 @@
/*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_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 = count(outl.begin(), outl.end(), 1) == m &&
m == 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

View File

@ -0,0 +1,206 @@
/*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)
{
// 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, GetParam() /* 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 = count(inliers.begin(), inliers.end(), 1) == m &&
m == 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

View File

@ -0,0 +1,759 @@
// 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/calib3d.hpp"
namespace opencv_test { namespace {
static void generatePose(RNG& rng, double min_theta, double max_theta,
double min_tx, double max_tx,
double min_ty, double max_ty,
double min_tz, double max_tz,
Mat& R, Mat& tvec,
bool random_sign)
{
Mat axis(3, 1, CV_64FC1);
for (int i = 0; i < 3; i++)
{
axis.at<double>(i,0) = rng.uniform(-1.0, 1.0);
}
double theta = rng.uniform(min_theta, max_theta);
if (random_sign)
{
theta *= std::copysign(1.0, rng.uniform(-1.0, 1.0));
}
Mat rvec(3, 1, CV_64FC1);
rvec.at<double>(0,0) = theta*axis.at<double>(0,0);
rvec.at<double>(1,0) = theta*axis.at<double>(1,0);
rvec.at<double>(2,0) = theta*axis.at<double>(2,0);
tvec.create(3, 1, CV_64FC1);
tvec.at<double>(0,0) = rng.uniform(min_tx, max_tx);
tvec.at<double>(1,0) = rng.uniform(min_ty, max_ty);
tvec.at<double>(2,0) = rng.uniform(min_tz, max_tz);
if (random_sign)
{
tvec.at<double>(0,0) *= std::copysign(1.0, rng.uniform(-1.0, 1.0));
tvec.at<double>(1,0) *= std::copysign(1.0, rng.uniform(-1.0, 1.0));
tvec.at<double>(2,0) *= std::copysign(1.0, rng.uniform(-1.0, 1.0));
}
cv::Rodrigues(rvec, R);
}
static Mat homogeneousInverse(const Mat& T)
{
CV_Assert( T.rows == 4 && T.cols == 4 );
Mat R = T(Rect(0, 0, 3, 3));
Mat t = T(Rect(3, 0, 1, 3));
Mat Rt = R.t();
Mat tinv = -Rt * t;
Mat Tinv = Mat::eye(4, 4, T.type());
Rt.copyTo(Tinv(Rect(0, 0, 3, 3)));
tinv.copyTo(Tinv(Rect(3, 0, 1, 3)));
return Tinv;
}
static void simulateDataEyeInHand(RNG& rng, int nPoses,
std::vector<Mat> &R_gripper2base, std::vector<Mat> &t_gripper2base,
std::vector<Mat> &R_target2cam, std::vector<Mat> &t_target2cam,
bool noise, Mat& R_cam2gripper, Mat& t_cam2gripper)
{
//to avoid generating values close to zero,
//we use positive range values and randomize the sign
const bool random_sign = true;
generatePose(rng, 10.0*CV_PI/180.0, 50.0*CV_PI/180.0,
0.05, 0.5, 0.05, 0.5, 0.05, 0.5,
R_cam2gripper, t_cam2gripper, random_sign);
Mat R_target2base, t_target2base;
generatePose(rng, 5.0*CV_PI/180.0, 85.0*CV_PI/180.0,
0.5, 3.5, 0.5, 3.5, 0.5, 3.5,
R_target2base, t_target2base, random_sign);
for (int i = 0; i < nPoses; i++)
{
Mat R_gripper2base_, t_gripper2base_;
generatePose(rng, 5.0*CV_PI/180.0, 45.0*CV_PI/180.0,
0.5, 1.5, 0.5, 1.5, 0.5, 1.5,
R_gripper2base_, t_gripper2base_, random_sign);
R_gripper2base.push_back(R_gripper2base_);
t_gripper2base.push_back(t_gripper2base_);
Mat T_cam2gripper = Mat::eye(4, 4, CV_64FC1);
R_cam2gripper.copyTo(T_cam2gripper(Rect(0, 0, 3, 3)));
t_cam2gripper.copyTo(T_cam2gripper(Rect(3, 0, 1, 3)));
Mat T_gripper2base = Mat::eye(4, 4, CV_64FC1);
R_gripper2base_.copyTo(T_gripper2base(Rect(0, 0, 3, 3)));
t_gripper2base_.copyTo(T_gripper2base(Rect(3, 0, 1, 3)));
Mat T_base2cam = homogeneousInverse(T_cam2gripper) * homogeneousInverse(T_gripper2base);
Mat T_target2base = Mat::eye(4, 4, CV_64FC1);
R_target2base.copyTo(T_target2base(Rect(0, 0, 3, 3)));
t_target2base.copyTo(T_target2base(Rect(3, 0, 1, 3)));
Mat T_target2cam = T_base2cam * T_target2base;
if (noise)
{
//Add some noise for the transformation between the target and the camera
Mat R_target2cam_noise = T_target2cam(Rect(0, 0, 3, 3));
Mat rvec_target2cam_noise;
cv::Rodrigues(R_target2cam_noise, rvec_target2cam_noise);
rvec_target2cam_noise.at<double>(0,0) += rng.gaussian(0.002);
rvec_target2cam_noise.at<double>(1,0) += rng.gaussian(0.002);
rvec_target2cam_noise.at<double>(2,0) += rng.gaussian(0.002);
cv::Rodrigues(rvec_target2cam_noise, R_target2cam_noise);
Mat t_target2cam_noise = T_target2cam(Rect(3, 0, 1, 3));
t_target2cam_noise.at<double>(0,0) += rng.gaussian(0.005);
t_target2cam_noise.at<double>(1,0) += rng.gaussian(0.005);
t_target2cam_noise.at<double>(2,0) += rng.gaussian(0.005);
//Add some noise for the transformation between the gripper and the robot base
Mat R_gripper2base_noise = T_gripper2base(Rect(0, 0, 3, 3));
Mat rvec_gripper2base_noise;
cv::Rodrigues(R_gripper2base_noise, rvec_gripper2base_noise);
rvec_gripper2base_noise.at<double>(0,0) += rng.gaussian(0.001);
rvec_gripper2base_noise.at<double>(1,0) += rng.gaussian(0.001);
rvec_gripper2base_noise.at<double>(2,0) += rng.gaussian(0.001);
cv::Rodrigues(rvec_gripper2base_noise, R_gripper2base_noise);
Mat t_gripper2base_noise = T_gripper2base(Rect(3, 0, 1, 3));
t_gripper2base_noise.at<double>(0,0) += rng.gaussian(0.001);
t_gripper2base_noise.at<double>(1,0) += rng.gaussian(0.001);
t_gripper2base_noise.at<double>(2,0) += rng.gaussian(0.001);
}
//Test rvec representation
Mat rvec_target2cam;
cv::Rodrigues(T_target2cam(Rect(0, 0, 3, 3)), rvec_target2cam);
R_target2cam.push_back(rvec_target2cam);
t_target2cam.push_back(T_target2cam(Rect(3, 0, 1, 3)));
}
}
static void simulateDataEyeToHand(RNG& rng, int nPoses,
std::vector<Mat> &R_base2gripper, std::vector<Mat> &t_base2gripper,
std::vector<Mat> &R_target2cam, std::vector<Mat> &t_target2cam,
bool noise, Mat& R_cam2base, Mat& t_cam2base)
{
//to avoid generating values close to zero,
//we use positive range values and randomize the sign
const bool random_sign = true;
generatePose(rng, 10.0*CV_PI/180.0, 50.0*CV_PI/180.0,
0.5, 3.5, 0.5, 3.5, 0.5, 3.5,
R_cam2base, t_cam2base, random_sign);
Mat R_target2gripper, t_target2gripper;
generatePose(rng, 5.0*CV_PI/180.0, 85.0*CV_PI/180.0,
0.05, 0.5, 0.05, 0.5, 0.05, 0.5,
R_target2gripper, t_target2gripper, random_sign);
Mat T_target2gripper = Mat::eye(4, 4, CV_64FC1);
R_target2gripper.copyTo(T_target2gripper(Rect(0, 0, 3, 3)));
t_target2gripper.copyTo(T_target2gripper(Rect(3, 0, 1, 3)));
for (int i = 0; i < nPoses; i++)
{
Mat R_gripper2base_, t_gripper2base_;
generatePose(rng, 5.0*CV_PI/180.0, 45.0*CV_PI/180.0,
0.5, 1.5, 0.5, 1.5, 0.5, 1.5,
R_gripper2base_, t_gripper2base_, random_sign);
Mat R_base2gripper_ = R_gripper2base_.t();
Mat t_base2gripper_ = -R_base2gripper_ * t_gripper2base_;
Mat T_gripper2base = Mat::eye(4, 4, CV_64FC1);
R_gripper2base_.copyTo(T_gripper2base(Rect(0, 0, 3, 3)));
t_gripper2base_.copyTo(T_gripper2base(Rect(3, 0, 1, 3)));
Mat T_cam2base = Mat::eye(4, 4, CV_64FC1);
R_cam2base.copyTo(T_cam2base(Rect(0, 0, 3, 3)));
t_cam2base.copyTo(T_cam2base(Rect(3, 0, 1, 3)));
Mat T_target2cam = homogeneousInverse(T_cam2base) * T_gripper2base * T_target2gripper;
if (noise)
{
//Add some noise for the transformation between the target and the camera
Mat R_target2cam_noise = T_target2cam(Rect(0, 0, 3, 3));
Mat rvec_target2cam_noise;
cv::Rodrigues(R_target2cam_noise, rvec_target2cam_noise);
rvec_target2cam_noise.at<double>(0,0) += rng.gaussian(0.002);
rvec_target2cam_noise.at<double>(1,0) += rng.gaussian(0.002);
rvec_target2cam_noise.at<double>(2,0) += rng.gaussian(0.002);
cv::Rodrigues(rvec_target2cam_noise, R_target2cam_noise);
Mat t_target2cam_noise = T_target2cam(Rect(3, 0, 1, 3));
t_target2cam_noise.at<double>(0,0) += rng.gaussian(0.005);
t_target2cam_noise.at<double>(1,0) += rng.gaussian(0.005);
t_target2cam_noise.at<double>(2,0) += rng.gaussian(0.005);
//Add some noise for the transformation between the robot base and the gripper
Mat rvec_base2gripper_noise;
cv::Rodrigues(R_base2gripper_, rvec_base2gripper_noise);
rvec_base2gripper_noise.at<double>(0,0) += rng.gaussian(0.001);
rvec_base2gripper_noise.at<double>(1,0) += rng.gaussian(0.001);
rvec_base2gripper_noise.at<double>(2,0) += rng.gaussian(0.001);
cv::Rodrigues(rvec_base2gripper_noise, R_base2gripper_);
t_base2gripper_.at<double>(0,0) += rng.gaussian(0.001);
t_base2gripper_.at<double>(1,0) += rng.gaussian(0.001);
t_base2gripper_.at<double>(2,0) += rng.gaussian(0.001);
}
R_base2gripper.push_back(R_base2gripper_);
t_base2gripper.push_back(t_base2gripper_);
//Test rvec representation
Mat rvec_target2cam;
cv::Rodrigues(T_target2cam(Rect(0, 0, 3, 3)), rvec_target2cam);
R_target2cam.push_back(rvec_target2cam);
t_target2cam.push_back(T_target2cam(Rect(3, 0, 1, 3)));
}
}
static std::string getMethodName(HandEyeCalibrationMethod method)
{
std::string method_name = "";
switch (method)
{
case CALIB_HAND_EYE_TSAI:
method_name = "Tsai";
break;
case CALIB_HAND_EYE_PARK:
method_name = "Park";
break;
case CALIB_HAND_EYE_HORAUD:
method_name = "Horaud";
break;
case CALIB_HAND_EYE_ANDREFF:
method_name = "Andreff";
break;
case CALIB_HAND_EYE_DANIILIDIS:
method_name = "Daniilidis";
break;
default:
break;
}
return method_name;
}
static std::string getMethodName(RobotWorldHandEyeCalibrationMethod method)
{
std::string method_name = "";
switch (method)
{
case CALIB_ROBOT_WORLD_HAND_EYE_SHAH:
method_name = "Shah";
break;
case CALIB_ROBOT_WORLD_HAND_EYE_LI:
method_name = "Li";
break;
default:
break;
}
return method_name;
}
static void printStats(const std::string& methodName, const std::vector<double>& rvec_diff, const std::vector<double>& tvec_diff)
{
double max_rvec_diff = *std::max_element(rvec_diff.begin(), rvec_diff.end());
double mean_rvec_diff = std::accumulate(rvec_diff.begin(),
rvec_diff.end(), 0.0) / rvec_diff.size();
double sq_sum_rvec_diff = std::inner_product(rvec_diff.begin(), rvec_diff.end(),
rvec_diff.begin(), 0.0);
double std_rvec_diff = std::sqrt(sq_sum_rvec_diff / rvec_diff.size() - mean_rvec_diff * mean_rvec_diff);
double max_tvec_diff = *std::max_element(tvec_diff.begin(), tvec_diff.end());
double mean_tvec_diff = std::accumulate(tvec_diff.begin(),
tvec_diff.end(), 0.0) / tvec_diff.size();
double sq_sum_tvec_diff = std::inner_product(tvec_diff.begin(), tvec_diff.end(),
tvec_diff.begin(), 0.0);
double std_tvec_diff = std::sqrt(sq_sum_tvec_diff / tvec_diff.size() - mean_tvec_diff * mean_tvec_diff);
std::cout << "Method " << methodName << ":\n"
<< "Max rvec error: " << max_rvec_diff << ", Mean rvec error: " << mean_rvec_diff
<< ", Std rvec error: " << std_rvec_diff << "\n"
<< "Max tvec error: " << max_tvec_diff << ", Mean tvec error: " << mean_tvec_diff
<< ", Std tvec error: " << std_tvec_diff << std::endl;
}
static void loadDataset(std::vector<Mat>& R_target2cam, std::vector<Mat>& t_target2cam,
std::vector<Mat>& R_base2gripper, std::vector<Mat>& t_base2gripper)
{
const std::string camera_poses_filename = findDataFile("cv/robot_world_hand_eye_calibration/cali.txt");
const std::string end_effector_poses = findDataFile("cv/robot_world_hand_eye_calibration/robot_cali.txt");
// Parse camera poses, the pose of the chessboard in the camera frame
{
std::ifstream file(camera_poses_filename);
ASSERT_TRUE(file.is_open());
int ndata = 0;
file >> ndata;
R_target2cam.reserve(ndata);
t_target2cam.reserve(ndata);
std::string image_name;
Matx33d cameraMatrix;
Matx33d R;
Matx31d t;
Matx16d distCoeffs;
Matx13d distCoeffs2;
while (file >> image_name >>
cameraMatrix(0,0) >> cameraMatrix(0,1) >> cameraMatrix(0,2) >>
cameraMatrix(1,0) >> cameraMatrix(1,1) >> cameraMatrix(1,2) >>
cameraMatrix(2,0) >> cameraMatrix(2,1) >> cameraMatrix(2,2) >>
R(0,0) >> R(0,1) >> R(0,2) >>
R(1,0) >> R(1,1) >> R(1,2) >>
R(2,0) >> R(2,1) >> R(2,2) >>
t(0) >> t(1) >> t(2) >>
distCoeffs(0) >> distCoeffs(1) >> distCoeffs(2) >> distCoeffs(3) >> distCoeffs(4) >>
distCoeffs2(0) >> distCoeffs2(1) >> distCoeffs2(2)) {
R_target2cam.push_back(Mat(R));
t_target2cam.push_back(Mat(t));
}
}
// Parse robot poses, the pose of the robot base in the robot hand frame
{
std::ifstream file(end_effector_poses);
ASSERT_TRUE(file.is_open());
int ndata = 0;
file >> ndata;
R_base2gripper.reserve(ndata);
t_base2gripper.reserve(ndata);
Matx33d R;
Matx31d t;
Matx14d last_row;
while (file >>
R(0,0) >> R(0,1) >> R(0,2) >> t(0) >>
R(1,0) >> R(1,1) >> R(1,2) >> t(1) >>
R(2,0) >> R(2,1) >> R(2,2) >> t(2) >>
last_row(0) >> last_row(1) >> last_row(2) >> last_row(3)) {
R_base2gripper.push_back(Mat(R));
t_base2gripper.push_back(Mat(t));
}
}
}
static void loadResults(Matx33d& wRb, Matx31d& wtb, Matx33d& cRg, Matx31d& ctg)
{
const std::string transformations_filename = findDataFile("cv/robot_world_hand_eye_calibration/rwhe_AA_RPI/transformations.txt");
std::ifstream file(transformations_filename);
ASSERT_TRUE(file.is_open());
std::string str;
//Parse X
file >> str;
Matx44d wTb;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
file >> wTb(i,j);
}
}
//Parse Z
file >> str;
int cam_num = 0;
//Parse camera number
file >> cam_num;
Matx44d cTg;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
file >> cTg(i,j);
}
}
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
wRb(i,j) = wTb(i,j);
cRg(i,j) = cTg(i,j);
}
wtb(i) = wTb(i,3);
ctg(i) = cTg(i,3);
}
}
class CV_CalibrateHandEyeTest : public cvtest::BaseTest
{
public:
CV_CalibrateHandEyeTest(bool eyeToHand) : eyeToHandConfig(eyeToHand) {
eps_rvec[CALIB_HAND_EYE_TSAI] = 1.0e-8;
eps_rvec[CALIB_HAND_EYE_PARK] = 1.0e-8;
eps_rvec[CALIB_HAND_EYE_HORAUD] = 1.0e-8;
eps_rvec[CALIB_HAND_EYE_ANDREFF] = 1.0e-8;
eps_rvec[CALIB_HAND_EYE_DANIILIDIS] = 1.0e-8;
eps_tvec[CALIB_HAND_EYE_TSAI] = 1.0e-8;
eps_tvec[CALIB_HAND_EYE_PARK] = 1.0e-8;
eps_tvec[CALIB_HAND_EYE_HORAUD] = 1.0e-8;
eps_tvec[CALIB_HAND_EYE_ANDREFF] = 1.0e-8;
eps_tvec[CALIB_HAND_EYE_DANIILIDIS] = 1.0e-8;
eps_rvec_noise[CALIB_HAND_EYE_TSAI] = 2.0e-2;
eps_rvec_noise[CALIB_HAND_EYE_PARK] = 2.0e-2;
eps_rvec_noise[CALIB_HAND_EYE_HORAUD] = 2.0e-2;
eps_rvec_noise[CALIB_HAND_EYE_ANDREFF] = 1.0e-2;
eps_rvec_noise[CALIB_HAND_EYE_DANIILIDIS] = 1.0e-2;
eps_tvec_noise[CALIB_HAND_EYE_TSAI] = 5.0e-2;
eps_tvec_noise[CALIB_HAND_EYE_PARK] = 5.0e-2;
eps_tvec_noise[CALIB_HAND_EYE_HORAUD] = 5.0e-2;
if (eyeToHandConfig)
{
eps_tvec_noise[CALIB_HAND_EYE_ANDREFF] = 7.0e-2;
}
else
{
eps_tvec_noise[CALIB_HAND_EYE_ANDREFF] = 5.0e-2;
}
eps_tvec_noise[CALIB_HAND_EYE_DANIILIDIS] = 5.0e-2;
}
protected:
virtual void run(int);
bool eyeToHandConfig;
double eps_rvec[5];
double eps_tvec[5];
double eps_rvec_noise[5];
double eps_tvec_noise[5];
};
void CV_CalibrateHandEyeTest::run(int)
{
ts->set_failed_test_info(cvtest::TS::OK);
RNG& rng = ts->get_rng();
std::vector<std::vector<double> > vec_rvec_diff(5);
std::vector<std::vector<double> > vec_tvec_diff(5);
std::vector<std::vector<double> > vec_rvec_diff_noise(5);
std::vector<std::vector<double> > vec_tvec_diff_noise(5);
std::vector<HandEyeCalibrationMethod> methods;
methods.push_back(CALIB_HAND_EYE_TSAI);
methods.push_back(CALIB_HAND_EYE_PARK);
methods.push_back(CALIB_HAND_EYE_HORAUD);
methods.push_back(CALIB_HAND_EYE_ANDREFF);
methods.push_back(CALIB_HAND_EYE_DANIILIDIS);
const int nTests = 100;
for (int i = 0; i < nTests; i++)
{
const int nPoses = 10;
if (eyeToHandConfig)
{
{
//No noise
std::vector<Mat> R_base2gripper, t_base2gripper;
std::vector<Mat> R_target2cam, t_target2cam;
Mat R_cam2base_true, t_cam2base_true;
const bool noise = false;
simulateDataEyeToHand(rng, nPoses, R_base2gripper, t_base2gripper, R_target2cam, t_target2cam, noise,
R_cam2base_true, t_cam2base_true);
for (size_t idx = 0; idx < methods.size(); idx++)
{
Mat rvec_cam2base_true;
cv::Rodrigues(R_cam2base_true, rvec_cam2base_true);
Mat R_cam2base_est, t_cam2base_est;
calibrateHandEye(R_base2gripper, t_base2gripper, R_target2cam, t_target2cam, R_cam2base_est, t_cam2base_est, methods[idx]);
Mat rvec_cam2base_est;
cv::Rodrigues(R_cam2base_est, rvec_cam2base_est);
double rvecDiff = cvtest::norm(rvec_cam2base_true, rvec_cam2base_est, NORM_L2);
double tvecDiff = cvtest::norm(t_cam2base_true, t_cam2base_est, NORM_L2);
vec_rvec_diff[idx].push_back(rvecDiff);
vec_tvec_diff[idx].push_back(tvecDiff);
const double epsilon_rvec = eps_rvec[idx];
const double epsilon_tvec = eps_tvec[idx];
//Maybe a better accuracy test would be to compare the mean and std errors with some thresholds?
if (rvecDiff > epsilon_rvec || tvecDiff > epsilon_tvec)
{
ts->printf(cvtest::TS::LOG, "Invalid accuracy (no noise) for method: %s, rvecDiff: %f, epsilon_rvec: %f, tvecDiff: %f, epsilon_tvec: %f\n",
getMethodName(methods[idx]).c_str(), rvecDiff, epsilon_rvec, tvecDiff, epsilon_tvec);
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
}
}
{
//Gaussian noise on transformations between calibration target frame and camera frame and between robot base and gripper frames
std::vector<Mat> R_base2gripper, t_base2gripper;
std::vector<Mat> R_target2cam, t_target2cam;
Mat R_cam2base_true, t_cam2base_true;
const bool noise = true;
simulateDataEyeToHand(rng, nPoses, R_base2gripper, t_base2gripper, R_target2cam, t_target2cam, noise,
R_cam2base_true, t_cam2base_true);
for (size_t idx = 0; idx < methods.size(); idx++)
{
Mat rvec_cam2base_true;
cv::Rodrigues(R_cam2base_true, rvec_cam2base_true);
Mat R_cam2base_est, t_cam2base_est;
calibrateHandEye(R_base2gripper, t_base2gripper, R_target2cam, t_target2cam, R_cam2base_est, t_cam2base_est, methods[idx]);
Mat rvec_cam2base_est;
cv::Rodrigues(R_cam2base_est, rvec_cam2base_est);
double rvecDiff = cvtest::norm(rvec_cam2base_true, rvec_cam2base_est, NORM_L2);
double tvecDiff = cvtest::norm(t_cam2base_true, t_cam2base_est, NORM_L2);
vec_rvec_diff_noise[idx].push_back(rvecDiff);
vec_tvec_diff_noise[idx].push_back(tvecDiff);
const double epsilon_rvec = eps_rvec_noise[idx];
const double epsilon_tvec = eps_tvec_noise[idx];
//Maybe a better accuracy test would be to compare the mean and std errors with some thresholds?
if (rvecDiff > epsilon_rvec || tvecDiff > epsilon_tvec)
{
ts->printf(cvtest::TS::LOG, "Invalid accuracy (noise) for method: %s, rvecDiff: %f, epsilon_rvec: %f, tvecDiff: %f, epsilon_tvec: %f\n",
getMethodName(methods[idx]).c_str(), rvecDiff, epsilon_rvec, tvecDiff, epsilon_tvec);
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
}
}
}
else
{
{
//No noise
std::vector<Mat> R_gripper2base, t_gripper2base;
std::vector<Mat> R_target2cam, t_target2cam;
Mat R_cam2gripper_true, t_cam2gripper_true;
const bool noise = false;
simulateDataEyeInHand(rng, nPoses, R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, noise,
R_cam2gripper_true, t_cam2gripper_true);
for (size_t idx = 0; idx < methods.size(); idx++)
{
Mat rvec_cam2gripper_true;
cv::Rodrigues(R_cam2gripper_true, rvec_cam2gripper_true);
Mat R_cam2gripper_est, t_cam2gripper_est;
calibrateHandEye(R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, R_cam2gripper_est, t_cam2gripper_est, methods[idx]);
Mat rvec_cam2gripper_est;
cv::Rodrigues(R_cam2gripper_est, rvec_cam2gripper_est);
double rvecDiff = cvtest::norm(rvec_cam2gripper_true, rvec_cam2gripper_est, NORM_L2);
double tvecDiff = cvtest::norm(t_cam2gripper_true, t_cam2gripper_est, NORM_L2);
vec_rvec_diff[idx].push_back(rvecDiff);
vec_tvec_diff[idx].push_back(tvecDiff);
const double epsilon_rvec = eps_rvec[idx];
const double epsilon_tvec = eps_tvec[idx];
//Maybe a better accuracy test would be to compare the mean and std errors with some thresholds?
if (rvecDiff > epsilon_rvec || tvecDiff > epsilon_tvec)
{
ts->printf(cvtest::TS::LOG, "Invalid accuracy (no noise) for method: %s, rvecDiff: %f, epsilon_rvec: %f, tvecDiff: %f, epsilon_tvec: %f\n",
getMethodName(methods[idx]).c_str(), rvecDiff, epsilon_rvec, tvecDiff, epsilon_tvec);
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
}
}
{
//Gaussian noise on transformations between calibration target frame and camera frame and between gripper and robot base frames
std::vector<Mat> R_gripper2base, t_gripper2base;
std::vector<Mat> R_target2cam, t_target2cam;
Mat R_cam2gripper_true, t_cam2gripper_true;
const bool noise = true;
simulateDataEyeInHand(rng, nPoses, R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, noise,
R_cam2gripper_true, t_cam2gripper_true);
for (size_t idx = 0; idx < methods.size(); idx++)
{
Mat rvec_cam2gripper_true;
cv::Rodrigues(R_cam2gripper_true, rvec_cam2gripper_true);
Mat R_cam2gripper_est, t_cam2gripper_est;
calibrateHandEye(R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, R_cam2gripper_est, t_cam2gripper_est, methods[idx]);
Mat rvec_cam2gripper_est;
cv::Rodrigues(R_cam2gripper_est, rvec_cam2gripper_est);
double rvecDiff = cvtest::norm(rvec_cam2gripper_true, rvec_cam2gripper_est, NORM_L2);
double tvecDiff = cvtest::norm(t_cam2gripper_true, t_cam2gripper_est, NORM_L2);
vec_rvec_diff_noise[idx].push_back(rvecDiff);
vec_tvec_diff_noise[idx].push_back(tvecDiff);
const double epsilon_rvec = eps_rvec_noise[idx];
const double epsilon_tvec = eps_tvec_noise[idx];
//Maybe a better accuracy test would be to compare the mean and std errors with some thresholds?
if (rvecDiff > epsilon_rvec || tvecDiff > epsilon_tvec)
{
ts->printf(cvtest::TS::LOG, "Invalid accuracy (noise) for method: %s, rvecDiff: %f, epsilon_rvec: %f, tvecDiff: %f, epsilon_tvec: %f\n",
getMethodName(methods[idx]).c_str(), rvecDiff, epsilon_rvec, tvecDiff, epsilon_tvec);
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
}
}
}
}
for (size_t idx = 0; idx < methods.size(); idx++)
{
std::cout << std::endl;
printStats(getMethodName(methods[idx]), vec_rvec_diff[idx], vec_tvec_diff[idx]);
printStats("(noise) " + getMethodName(methods[idx]), vec_rvec_diff_noise[idx], vec_tvec_diff_noise[idx]);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
TEST(Calib3d_CalibrateHandEye, regression_eye_in_hand)
{
//Eye-in-Hand configuration (camera mounted on the robot end-effector observing a static calibration pattern)
const bool eyeToHand = false;
CV_CalibrateHandEyeTest test(eyeToHand);
test.safe_run();
}
TEST(Calib3d_CalibrateHandEye, regression_eye_to_hand)
{
//Eye-to-Hand configuration (static camera observing a calibration pattern mounted on the robot end-effector)
const bool eyeToHand = true;
CV_CalibrateHandEyeTest test(eyeToHand);
test.safe_run();
}
TEST(Calib3d_CalibrateHandEye, regression_17986)
{
std::vector<Mat> R_target2cam, t_target2cam;
// Dataset contains transformation from base to gripper frame since it contains data for AX = ZB calibration problem
std::vector<Mat> R_base2gripper, t_base2gripper;
loadDataset(R_target2cam, t_target2cam, R_base2gripper, t_base2gripper);
std::vector<HandEyeCalibrationMethod> methods = {CALIB_HAND_EYE_TSAI,
CALIB_HAND_EYE_PARK,
CALIB_HAND_EYE_HORAUD,
CALIB_HAND_EYE_ANDREFF,
CALIB_HAND_EYE_DANIILIDIS};
for (auto method : methods) {
SCOPED_TRACE(cv::format("method=%s", getMethodName(method).c_str()));
Matx33d R_cam2base_est;
Matx31d t_cam2base_est;
calibrateHandEye(R_base2gripper, t_base2gripper, R_target2cam, t_target2cam, R_cam2base_est, t_cam2base_est, method);
EXPECT_TRUE(checkRange(R_cam2base_est));
EXPECT_TRUE(checkRange(t_cam2base_est));
}
}
TEST(Calib3d_CalibrateRobotWorldHandEye, regression)
{
std::vector<Mat> R_world2cam, t_worldt2cam;
std::vector<Mat> R_base2gripper, t_base2gripper;
loadDataset(R_world2cam, t_worldt2cam, R_base2gripper, t_base2gripper);
std::vector<Mat> rvec_R_world2cam;
rvec_R_world2cam.reserve(R_world2cam.size());
for (size_t i = 0; i < R_world2cam.size(); i++)
{
Mat rvec;
cv::Rodrigues(R_world2cam[i], rvec);
rvec_R_world2cam.push_back(rvec);
}
std::vector<RobotWorldHandEyeCalibrationMethod> methods = {CALIB_ROBOT_WORLD_HAND_EYE_SHAH,
CALIB_ROBOT_WORLD_HAND_EYE_LI};
Matx33d wRb, cRg;
Matx31d wtb, ctg;
loadResults(wRb, wtb, cRg, ctg);
for (auto method : methods) {
SCOPED_TRACE(cv::format("method=%s", getMethodName(method).c_str()));
Matx33d wRb_est, cRg_est;
Matx31d wtb_est, ctg_est;
calibrateRobotWorldHandEye(rvec_R_world2cam, t_worldt2cam, R_base2gripper, t_base2gripper,
wRb_est, wtb_est, cRg_est, ctg_est, method);
EXPECT_TRUE(checkRange(wRb_est));
EXPECT_TRUE(checkRange(wtb_est));
EXPECT_TRUE(checkRange(cRg_est));
EXPECT_TRUE(checkRange(ctg_est));
//Arbitrary thresholds
const double rotation_threshold = 1.0; //1deg
const double translation_threshold = 50.0; //5cm
//X
//rotation error
Matx33d wRw_est = wRb * wRb_est.t();
Matx31d rvec_wRw_est;
cv::Rodrigues(wRw_est, rvec_wRw_est);
double X_rotation_error = cv::norm(rvec_wRw_est)*180/CV_PI;
//translation error
double X_t_error = cv::norm(wtb_est - wtb);
SCOPED_TRACE(cv::format("X rotation error=%f", X_rotation_error));
SCOPED_TRACE(cv::format("X translation error=%f", X_t_error));
EXPECT_TRUE(X_rotation_error < rotation_threshold);
EXPECT_TRUE(X_t_error < translation_threshold);
//Z
//rotation error
Matx33d cRc_est = cRg * cRg_est.t();
Matx31d rvec_cMc_est;
cv::Rodrigues(cRc_est, rvec_cMc_est);
double Z_rotation_error = cv::norm(rvec_cMc_est)*180/CV_PI;
//translation error
double Z_t_error = cv::norm(ctg_est - ctg);
SCOPED_TRACE(cv::format("Z rotation error=%f", Z_rotation_error));
SCOPED_TRACE(cv::format("Z translation error=%f", Z_t_error));
EXPECT_TRUE(Z_rotation_error < rotation_threshold);
EXPECT_TRUE(Z_t_error < translation_threshold);
}
}
}} // namespace

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,426 @@
/*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 "test_chessboardgenerator.hpp"
namespace opencv_test { namespace {
//template<class T> ostream& operator<<(ostream& out, const Mat_<T>& mat)
//{
// for(Mat_<T>::const_iterator pos = mat.begin(), end = mat.end(); pos != end; ++pos)
// out << *pos << " ";
// return out;
//}
//ostream& operator<<(ostream& out, const Mat& mat) { return out << Mat_<double>(mat); }
Mat calcRvec(const vector<Point3f>& points, const Size& cornerSize)
{
Point3f p00 = points[0];
Point3f p10 = points[1];
Point3f p01 = points[cornerSize.width];
Vec3d ex(p10.x - p00.x, p10.y - p00.y, p10.z - p00.z);
Vec3d ey(p01.x - p00.x, p01.y - p00.y, p01.z - p00.z);
Vec3d ez = ex.cross(ey);
Mat rot(3, 3, CV_64F);
*rot.ptr<Vec3d>(0) = ex;
*rot.ptr<Vec3d>(1) = ey;
*rot.ptr<Vec3d>(2) = ez * (1.0/cv::norm(ez)); // TODO cvtest
Mat res;
cvtest::Rodrigues(rot.t(), res);
return res.reshape(1, 1);
}
class CV_CalibrateCameraArtificialTest : public cvtest::BaseTest
{
public:
CV_CalibrateCameraArtificialTest() :
r(0)
{
}
~CV_CalibrateCameraArtificialTest() {}
protected:
int r;
const static int JUST_FIND_CORNERS = 0;
const static int USE_CORNERS_SUBPIX = 1;
const static int USE_4QUAD_CORNERS = 2;
const static int ARTIFICIAL_CORNERS = 4;
bool checkErr(double a, double a0, double eps, double delta)
{
return fabs(a - a0) > eps * (fabs(a0) + delta);
}
void compareCameraMatrs(const Mat_<double>& camMat, const Mat& camMat_est)
{
if ( camMat_est.at<double>(0, 1) != 0 || camMat_est.at<double>(1, 0) != 0 ||
camMat_est.at<double>(2, 0) != 0 || camMat_est.at<double>(2, 1) != 0 ||
camMat_est.at<double>(2, 2) != 1)
{
ts->printf( cvtest::TS::LOG, "Bad shape of camera matrix returned \n");
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
}
double fx_e = camMat_est.at<double>(0, 0), fy_e = camMat_est.at<double>(1, 1);
double cx_e = camMat_est.at<double>(0, 2), cy_e = camMat_est.at<double>(1, 2);
double fx = camMat(0, 0), fy = camMat(1, 1), cx = camMat(0, 2), cy = camMat(1, 2);
const double eps = 1e-2;
const double dlt = 1e-5;
bool fail = checkErr(fx_e, fx, eps, dlt) || checkErr(fy_e, fy, eps, dlt) ||
checkErr(cx_e, cx, eps, dlt) || checkErr(cy_e, cy, eps, dlt);
if (fail)
{
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
ts->printf( cvtest::TS::LOG, "%d) Expected [Fx Fy Cx Cy] = [%.3f %.3f %.3f %.3f]\n", r, fx, fy, cx, cy);
ts->printf( cvtest::TS::LOG, "%d) Estimated [Fx Fy Cx Cy] = [%.3f %.3f %.3f %.3f]\n", r, fx_e, fy_e, cx_e, cy_e);
}
void compareDistCoeffs(const Mat_<double>& distCoeffs, const Mat& distCoeffs_est)
{
const double *dt_e = distCoeffs_est.ptr<double>();
double k1_e = dt_e[0], k2_e = dt_e[1], k3_e = dt_e[4];
double p1_e = dt_e[2], p2_e = dt_e[3];
double k1 = distCoeffs(0, 0), k2 = distCoeffs(0, 1), k3 = distCoeffs(0, 4);
double p1 = distCoeffs(0, 2), p2 = distCoeffs(0, 3);
const double eps = 5e-2;
const double dlt = 1e-3;
const double eps_k3 = 5;
const double dlt_k3 = 1e-3;
bool fail = checkErr(k1_e, k1, eps, dlt) || checkErr(k2_e, k2, eps, dlt) || checkErr(k3_e, k3, eps_k3, dlt_k3) ||
checkErr(p1_e, p1, eps, dlt) || checkErr(p2_e, p2, eps, dlt);
if (fail)
{
// commented according to vp123's recommendation. TODO - improve accuracy
//ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); ss
}
ts->printf( cvtest::TS::LOG, "%d) DistCoeff exp=(%.2f, %.2f, %.4f, %.4f %.2f)\n", r, k1, k2, p1, p2, k3);
ts->printf( cvtest::TS::LOG, "%d) DistCoeff est=(%.2f, %.2f, %.4f, %.4f %.2f)\n", r, k1_e, k2_e, p1_e, p2_e, k3_e);
ts->printf( cvtest::TS::LOG, "%d) AbsError = [%.5f %.5f %.5f %.5f %.5f]\n", r, fabs(k1-k1_e), fabs(k2-k2_e), fabs(p1-p1_e), fabs(p2-p2_e), fabs(k3-k3_e));
}
void compareShiftVecs(const vector<Mat>& tvecs, const vector<Mat>& tvecs_est)
{
const double eps = 1e-2;
const double dlt = 1e-4;
int err_count = 0;
const int errMsgNum = 4;
for(size_t i = 0; i < tvecs.size(); ++i)
{
const Point3d& tvec = *tvecs[i].ptr<Point3d>();
const Point3d& tvec_est = *tvecs_est[i].ptr<Point3d>();
double n1 = cv::norm(tvec_est - tvec); // TODO cvtest
double n2 = cv::norm(tvec); // TODO cvtest
if (n1 > eps* (n2 + dlt))
{
if (err_count++ < errMsgNum)
{
if (err_count == errMsgNum)
ts->printf( cvtest::TS::LOG, "%d) ...\n", r);
else
{
ts->printf( cvtest::TS::LOG, "%d) Bad accuracy in returned tvecs. Index = %d\n", r, i);
ts->printf( cvtest::TS::LOG, "%d) norm(tvec_est - tvec) = %f, norm(tvec_exp) = %f \n", r, n1, n2);
}
}
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
}
}
void compareRotationVecs(const vector<Mat>& rvecs, const vector<Mat>& rvecs_est)
{
const double eps = 2e-2;
const double dlt = 1e-4;
Mat rmat, rmat_est;
int err_count = 0;
const int errMsgNum = 4;
for(size_t i = 0; i < rvecs.size(); ++i)
{
cvtest::Rodrigues(rvecs[i], rmat);
cvtest::Rodrigues(rvecs_est[i], rmat_est);
if (cvtest::norm(rmat_est, rmat, NORM_L2) > eps* (cvtest::norm(rmat, NORM_L2) + dlt))
{
if (err_count++ < errMsgNum)
{
if (err_count == errMsgNum)
ts->printf( cvtest::TS::LOG, "%d) ...\n", r);
else
{
ts->printf( cvtest::TS::LOG, "%d) Bad accuracy in returned rvecs (rotation matrs). Index = %d\n", r, i);
ts->printf( cvtest::TS::LOG, "%d) norm(rot_mat_est - rot_mat_exp) = %f, norm(rot_mat_exp) = %f \n", r,
cvtest::norm(rmat_est, rmat, NORM_L2), cvtest::norm(rmat, NORM_L2));
}
}
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
}
}
double reprojectErrorWithoutIntrinsics(const vector<Point3f>& cb3d, const vector<Mat>& _rvecs_exp, const vector<Mat>& _tvecs_exp,
const vector<Mat>& rvecs_est, const vector<Mat>& tvecs_est)
{
const static Mat eye33 = Mat::eye(3, 3, CV_64F);
const static Mat zero15 = Mat::zeros(1, 5, CV_64F);
Mat _chessboard3D(cb3d);
vector<Point2f> uv_exp, uv_est;
double res = 0;
for(size_t i = 0; i < rvecs_exp.size(); ++i)
{
projectPoints(_chessboard3D, _rvecs_exp[i], _tvecs_exp[i], eye33, zero15, uv_exp);
projectPoints(_chessboard3D, rvecs_est[i], tvecs_est[i], eye33, zero15, uv_est);
for(size_t j = 0; j < cb3d.size(); ++j)
res += cv::norm(uv_exp[i] - uv_est[i]); // TODO cvtest
}
return res;
}
Size2f sqSile;
vector<Point3f> chessboard3D;
vector<Mat> boards, rvecs_exp, tvecs_exp, rvecs_spnp, tvecs_spnp;
vector< vector<Point3f> > objectPoints;
vector< vector<Point2f> > imagePoints_art;
vector< vector<Point2f> > imagePoints_findCb;
void prepareForTest(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, size_t brdsNum, const ChessBoardGenerator& cbg)
{
sqSile = Size2f(1.f, 1.f);
Size cornersSize = cbg.cornersSize();
chessboard3D.clear();
for(int j = 0; j < cornersSize.height; ++j)
for(int i = 0; i < cornersSize.width; ++i)
chessboard3D.push_back(Point3f(sqSile.width * i, sqSile.height * j, 0));
boards.resize(brdsNum);
rvecs_exp.resize(brdsNum);
tvecs_exp.resize(brdsNum);
objectPoints.clear();
objectPoints.resize(brdsNum, chessboard3D);
imagePoints_art.clear();
imagePoints_findCb.clear();
vector<Point2f> corners_art, corners_fcb;
for(size_t i = 0; i < brdsNum; ++i)
{
for(;;)
{
boards[i] = cbg(bg, camMat, distCoeffs, sqSile, corners_art);
if(findChessboardCorners(boards[i], cornersSize, corners_fcb))
break;
}
//cv::namedWindow("CB"); imshow("CB", boards[i]); cv::waitKey();
imagePoints_art.push_back(corners_art);
imagePoints_findCb.push_back(corners_fcb);
tvecs_exp[i].create(1, 3, CV_64F);
*tvecs_exp[i].ptr<Point3d>() = cbg.corners3d[0];
rvecs_exp[i] = calcRvec(cbg.corners3d, cbg.cornersSize());
}
}
void runTest(const Size& imgSize, const Mat_<double>& camMat, const Mat_<double>& distCoeffs, size_t brdsNum, const Size& cornersSize, int flag = 0)
{
const TermCriteria tc(TermCriteria::EPS|TermCriteria::MAX_ITER, 30, 0.1);
vector< vector<Point2f> > imagePoints;
switch(flag)
{
case JUST_FIND_CORNERS: imagePoints = imagePoints_findCb; break;
case ARTIFICIAL_CORNERS: imagePoints = imagePoints_art; break;
case USE_CORNERS_SUBPIX:
for(size_t i = 0; i < brdsNum; ++i)
{
Mat gray;
cvtColor(boards[i], gray, COLOR_BGR2GRAY);
vector<Point2f> tmp = imagePoints_findCb[i];
cornerSubPix(gray, tmp, Size(5, 5), Size(-1,-1), tc);
imagePoints.push_back(tmp);
}
break;
case USE_4QUAD_CORNERS:
for(size_t i = 0; i < brdsNum; ++i)
{
Mat gray;
cvtColor(boards[i], gray, COLOR_BGR2GRAY);
vector<Point2f> tmp = imagePoints_findCb[i];
find4QuadCornerSubpix(gray, tmp, Size(5, 5));
imagePoints.push_back(tmp);
}
break;
default:
throw std::exception();
}
Mat camMat_est = Mat::eye(3, 3, CV_64F), distCoeffs_est = Mat::zeros(1, 5, CV_64F);
vector<Mat> rvecs_est, tvecs_est;
int flags = /*CALIB_FIX_K3|*/CALIB_FIX_K4|CALIB_FIX_K5|CALIB_FIX_K6; //CALIB_FIX_K3; //CALIB_FIX_ASPECT_RATIO | | CALIB_ZERO_TANGENT_DIST;
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 100, DBL_EPSILON);
double rep_error = calibrateCamera(objectPoints, imagePoints, imgSize, camMat_est, distCoeffs_est, rvecs_est, tvecs_est, flags, criteria);
rep_error /= brdsNum * cornersSize.area();
const double thres = 1;
if (rep_error > thres)
{
ts->printf( cvtest::TS::LOG, "%d) Too big reproject error = %f\n", r, rep_error);
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
compareCameraMatrs(camMat, camMat_est);
compareDistCoeffs(distCoeffs, distCoeffs_est);
compareShiftVecs(tvecs_exp, tvecs_est);
compareRotationVecs(rvecs_exp, rvecs_est);
double rep_errorWOI = reprojectErrorWithoutIntrinsics(chessboard3D, rvecs_exp, tvecs_exp, rvecs_est, tvecs_est);
rep_errorWOI /= brdsNum * cornersSize.area();
const double thres2 = 0.01;
if (rep_errorWOI > thres2)
{
ts->printf( cvtest::TS::LOG, "%d) Too big reproject error without intrinsics = %f\n", r, rep_errorWOI);
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
ts->printf( cvtest::TS::LOG, "%d) Testing solvePnP...\n", r);
rvecs_spnp.resize(brdsNum);
tvecs_spnp.resize(brdsNum);
for(size_t i = 0; i < brdsNum; ++i)
solvePnP(objectPoints[i], imagePoints[i], camMat, distCoeffs, rvecs_spnp[i], tvecs_spnp[i]);
compareShiftVecs(tvecs_exp, tvecs_spnp);
compareRotationVecs(rvecs_exp, rvecs_spnp);
}
void run(int)
{
ts->set_failed_test_info(cvtest::TS::OK);
RNG& rng = theRNG();
int progress = 0;
int repeat_num = 3;
for(r = 0; r < repeat_num; ++r)
{
const int brds_num = 20;
Mat bg(Size(640, 480), CV_8UC3);
randu(bg, Scalar::all(32), Scalar::all(255));
GaussianBlur(bg, bg, Size(5, 5), 2);
double fx = 300 + (20 * (double)rng - 10);
double fy = 300 + (20 * (double)rng - 10);
double cx = bg.cols/2 + (40 * (double)rng - 20);
double cy = bg.rows/2 + (40 * (double)rng - 20);
Mat_<double> camMat(3, 3);
camMat << fx, 0., cx, 0, fy, cy, 0., 0., 1.;
double k1 = 0.5 + (double)rng/5;
double k2 = (double)rng/5;
double k3 = (double)rng/5;
double p1 = 0.001 + (double)rng/10;
double p2 = 0.001 + (double)rng/10;
Mat_<double> distCoeffs(1, 5, 0.0);
distCoeffs << k1, k2, p1, p2, k3;
ChessBoardGenerator cbg(Size(9, 8));
cbg.min_cos = 0.9;
cbg.cov = 0.8;
progress = update_progress(progress, r, repeat_num, 0);
ts->printf( cvtest::TS::LOG, "\n");
prepareForTest(bg, camMat, distCoeffs, brds_num, cbg);
ts->printf( cvtest::TS::LOG, "artificial corners\n");
runTest(bg.size(), camMat, distCoeffs, brds_num, cbg.cornersSize(), ARTIFICIAL_CORNERS);
progress = update_progress(progress, r, repeat_num, 0);
ts->printf( cvtest::TS::LOG, "findChessboard corners\n");
runTest(bg.size(), camMat, distCoeffs, brds_num, cbg.cornersSize(), JUST_FIND_CORNERS);
progress = update_progress(progress, r, repeat_num, 0);
ts->printf( cvtest::TS::LOG, "cornersSubPix corners\n");
runTest(bg.size(), camMat, distCoeffs, brds_num, cbg.cornersSize(), USE_CORNERS_SUBPIX);
progress = update_progress(progress, r, repeat_num, 0);
ts->printf( cvtest::TS::LOG, "4quad corners\n");
runTest(bg.size(), camMat, distCoeffs, brds_num, cbg.cornersSize(), USE_4QUAD_CORNERS);
progress = update_progress(progress, r, repeat_num, 0);
}
}
};
TEST(Calib3d_CalibrateCamera_CPP, DISABLED_accuracy_on_artificial_data) { CV_CalibrateCameraArtificialTest test; test.safe_run(); }
}} // namespace

View File

@ -0,0 +1,399 @@
/*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 "test_chessboardgenerator.hpp"
#include "opencv2/core/types_c.h"
namespace opencv_test { namespace {
class CV_CameraCalibrationBadArgTest : public cvtest::BadArgTest
{
public:
CV_CameraCalibrationBadArgTest() {}
~CV_CameraCalibrationBadArgTest() {}
protected:
void run(int);
void run_func(void) {}
struct C_Caller
{
_InputArray imgPts_arg;
_InputArray objPts_arg;
_OutputArray rvecs_arg;
_OutputArray tvecs_arg;
_OutputArray newObjPts_arg;
_InputOutputArray cameraMatrix_arg;
_InputOutputArray distCoeffs_arg;
std::vector<std::vector<Point2f> > imgPts;
std::vector<std::vector<Point3f> > objPts;
Size imageSize0, imageSize;
int iFixedPoint0, iFixedPoint;
Mat cameraMatrix;
Mat distCoeffs;
std::vector<Mat> rvecs;
std::vector<Mat> tvecs;
std::vector<Point3f> newObjPts;
int flags0, flags;
void initArgs()
{
imgPts_arg = imgPts;
objPts_arg = objPts;
rvecs_arg = rvecs;
tvecs_arg = tvecs;
newObjPts_arg = newObjPts;
cameraMatrix_arg = cameraMatrix;
distCoeffs_arg = distCoeffs;
imageSize = imageSize0;
flags = flags0;
iFixedPoint = iFixedPoint0;
}
void operator()() const
{
calibrateCameraRO(objPts_arg, imgPts_arg, imageSize, iFixedPoint,
cameraMatrix_arg, distCoeffs_arg, rvecs_arg, tvecs_arg,
newObjPts_arg, flags);
}
};
};
void CV_CameraCalibrationBadArgTest::run( int /* start_from */ )
{
const int M = 2;
Size imgSize(800, 600);
Mat_<float> camMat(3, 3);
Mat_<float> distCoeffs0(1, 5);
camMat << 300.f, 0.f, imgSize.width/2.f, 0, 300.f, imgSize.height/2.f, 0.f, 0.f, 1.f;
distCoeffs0 << 1.2f, 0.2f, 0.f, 0.f, 0.f;
ChessBoardGenerator cbg(Size(8,6));
Size corSize = cbg.cornersSize();
vector<Point2f> corners;
cbg(Mat(imgSize, CV_8U, Scalar(0)), camMat, distCoeffs0, corners);
C_Caller caller;
caller.imageSize0 = imgSize;
caller.iFixedPoint0 = -1;
caller.flags0 = 0;
/////////////////////////////
Mat cameraMatrix_cpp;
Mat distCoeffs_cpp;
Mat rvecs_cpp;
Mat tvecs_cpp;
Mat newObjPts_cpp;
std::vector<Point3f> objPts_cpp;
for(int y = 0; y < corSize.height; ++y)
for(int x = 0; x < corSize.width; ++x)
objPts_cpp.push_back(Point3f((float)x, (float)y, 0.f));
caller.objPts.resize(M);
caller.imgPts.resize(M);
for(int i = 0; i < M; i++)
{
caller.objPts[i] = objPts_cpp;
caller.imgPts[i] = corners;
}
caller.cameraMatrix.create(3, 3, CV_32F);
caller.distCoeffs.create(5, 1, CV_32F);
caller.rvecs.clear();
caller.tvecs.clear();
caller.newObjPts.clear();
/* /*//*/ */
int errors = 0;
caller.initArgs();
caller.objPts_arg = noArray();
errors += run_test_case( CV_StsBadArg, "None passed in objPts", caller);
caller.initArgs();
caller.imgPts_arg = noArray();
errors += run_test_case( CV_StsBadArg, "None passed in imgPts", caller );
caller.initArgs();
caller.cameraMatrix_arg = noArray();
errors += run_test_case( CV_StsBadArg, "Zero passed in cameraMatrix", caller );
caller.initArgs();
caller.distCoeffs_arg = noArray();
errors += run_test_case( CV_StsBadArg, "Zero passed in distCoeffs", caller );
caller.initArgs();
caller.imageSize.width = -1;
errors += run_test_case( CV_StsOutOfRange, "Bad image width", caller );
caller.initArgs();
caller.imageSize.height = -1;
errors += run_test_case( CV_StsOutOfRange, "Bad image height", caller );
caller.initArgs();
caller.imgPts[0].clear();
errors += run_test_case( CV_StsBadSize, "Bad imgpts[0]", caller );
caller.imgPts[0] = caller.imgPts[1];
caller.initArgs();
caller.objPts[1].clear();
errors += run_test_case( CV_StsBadSize, "Bad objpts[1]", caller );
caller.objPts[1] = caller.objPts[0];
caller.initArgs();
Mat badCM = Mat::zeros(4, 4, CV_64F);
caller.cameraMatrix_arg = badCM;
caller.flags = CALIB_USE_INTRINSIC_GUESS;
errors += run_test_case( CV_StsBadArg, "Bad camearaMatrix header", caller );
caller.initArgs();
Mat badDC = Mat::zeros(10, 10, CV_64F);
caller.distCoeffs_arg = badDC;
caller.flags = CALIB_USE_INTRINSIC_GUESS;
errors += run_test_case( CV_StsBadArg, "Bad camearaMatrix header", caller );
if (errors)
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
else
ts->set_failed_test_info(cvtest::TS::OK);
}
class CV_Rodrigues2BadArgTest : public cvtest::BadArgTest
{
public:
CV_Rodrigues2BadArgTest() {}
~CV_Rodrigues2BadArgTest() {}
protected:
void run_func(void) {}
struct C_Caller
{
_InputArray src_arg;
_OutputArray dst_arg, j_arg;
Mat src;
Mat dst;
Mat jacobian;
void initArgs()
{
src_arg = src;
dst_arg = dst;
j_arg = jacobian;
}
void operator()()
{
cv::Rodrigues(src_arg, dst_arg, j_arg);
}
};
void run(int /* start_from */ )
{
Mat zeros(1, sizeof(CvMat), CV_8U, Scalar(0));
Mat src_cpp(3, 1, CV_32F);
Mat dst_cpp(3, 3, CV_32F);
C_Caller caller;
/*/*//*/*/
int errors = 0;
caller.initArgs();
caller.src_arg = noArray();
errors += run_test_case( CV_StsBadArg, "Src is empty matrix", caller );
caller.initArgs();
caller.src = Mat::zeros(3, 1, CV_8U);
errors += run_test_case( CV_StsUnsupportedFormat, "Bad src formart", caller );
caller.initArgs();
caller.src = Mat::zeros(1, 1, CV_32F);
errors += run_test_case( CV_StsBadSize, "Bad src size", caller );
if (errors)
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
else
ts->set_failed_test_info(cvtest::TS::OK);
}
};
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
class CV_ProjectPoints2BadArgTest : public cvtest::BadArgTest
{
public:
CV_ProjectPoints2BadArgTest() : camMat(3, 3), distCoeffs(1, 5)
{
Size imsSize(800, 600);
camMat << 300.f, 0.f, imsSize.width/2.f, 0, 300.f, imsSize.height/2.f, 0.f, 0.f, 1.f;
distCoeffs << 1.2f, 0.2f, 0.f, 0.f, 0.f;
}
~CV_ProjectPoints2BadArgTest() {}
protected:
void run_func(void) {}
Mat_<float> camMat;
Mat_<float> distCoeffs;
struct C_Caller
{
_InputArray objectPoints_arg, rvec_arg, tvec_arg, A_arg, DC_arg;
_OutputArray imagePoints_arg;
Mat objectPoints;
Mat r_vec;
Mat t_vec;
Mat A;
Mat distCoeffs;
Mat imagePoints;
Mat J;
double aspectRatio0, aspectRatio;
void initArgs()
{
objectPoints_arg = objectPoints;
imagePoints_arg = imagePoints;
rvec_arg = r_vec;
tvec_arg = t_vec;
A_arg = A;
DC_arg = distCoeffs;
aspectRatio = aspectRatio0;
}
void operator()()
{
projectPoints(objectPoints_arg, rvec_arg, tvec_arg, A_arg, DC_arg,
imagePoints_arg, J, aspectRatio );
}
};
void run(int /* start_from */ )
{
C_Caller caller;
const int n = 10;
Mat objectPoints_cpp(1, n, CV_32FC3);
randu(objectPoints_cpp, Scalar::all(1), Scalar::all(10));
caller.objectPoints = objectPoints_cpp;
caller.t_vec = Mat::zeros(1, 3, CV_32F);
cvtest::Rodrigues(Mat::eye(3, 3, CV_32F), caller.r_vec);
caller.A = Mat::eye(3, 3, CV_32F);
caller.distCoeffs = Mat::zeros(1, 5, CV_32F);
caller.aspectRatio0 = 1.0;
/********************/
int errors = 0;
caller.initArgs();
caller.objectPoints_arg = noArray();
errors += run_test_case( CV_StsBadArg, "Zero objectPoints", caller );
caller.initArgs();
caller.rvec_arg = noArray();
errors += run_test_case( CV_StsBadArg, "Zero r_vec", caller );
caller.initArgs();
caller.tvec_arg = noArray();
errors += run_test_case( CV_StsBadArg, "Zero t_vec", caller );
caller.initArgs();
caller.A_arg = noArray();
errors += run_test_case( CV_StsBadArg, "Zero camMat", caller );
caller.initArgs();
caller.imagePoints_arg = noArray();
errors += run_test_case( CV_StsBadArg, "Zero imagePoints", caller );
Mat save_rvec = caller.r_vec;
caller.initArgs();
caller.r_vec.create(2, 2, CV_32F);
errors += run_test_case( CV_StsBadArg, "Bad rvec format", caller );
caller.initArgs();
caller.r_vec.create(1, 3, CV_8U);
errors += run_test_case( CV_StsBadArg, "Bad rvec format", caller );
caller.r_vec = save_rvec;
/****************************/
Mat save_tvec = caller.t_vec;
caller.initArgs();
caller.t_vec.create(3, 3, CV_32F);
errors += run_test_case( CV_StsBadArg, "Bad tvec format", caller );
caller.initArgs();
caller.t_vec.create(1, 3, CV_8U);
errors += run_test_case( CV_StsBadArg, "Bad tvec format", caller );
caller.t_vec = save_tvec;
/****************************/
Mat save_A = caller.A;
caller.initArgs();
caller.A.create(2, 2, CV_32F);
errors += run_test_case( CV_StsBadArg, "Bad A format", caller );
caller.A = save_A;
/****************************/
Mat save_DC = caller.distCoeffs;
caller.initArgs();
caller.distCoeffs.create(3, 3, CV_32F);
errors += run_test_case( CV_StsBadArg, "Bad distCoeffs format", caller );
caller.distCoeffs = save_DC;
if (errors)
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
else
ts->set_failed_test_info(cvtest::TS::OK);
}
};
TEST(Calib3d_CalibrateCamera_CPP, badarg) { CV_CameraCalibrationBadArgTest test; test.safe_run(); }
TEST(Calib3d_Rodrigues_CPP, badarg) { CV_Rodrigues2BadArgTest test; test.safe_run(); }
TEST(Calib3d_ProjectPoints_CPP, badarg) { CV_ProjectPoints2BadArgTest test; test.safe_run(); }
}} // namespace

View File

@ -0,0 +1,693 @@
/*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-2011, 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/ts/cuda_test.hpp" // EXPECT_MAT_NEAR
namespace opencv_test { namespace {
#define NUM_DIST_COEFF_TILT 14
/**
Some conventions:
- the first camera determines the world coordinate system
- y points down, hence top means minimal y value (negative) and
bottom means maximal y value (positive)
- the field of view plane is tilted around x such that it
intersects the xy-plane in a line with a large (positive)
y-value
- image sensor and object are both modelled in the halfspace
z > 0
**/
class cameraCalibrationTiltTest : public ::testing::Test {
protected:
cameraCalibrationTiltTest()
: m_toRadian(acos(-1.0)/180.0)
, m_toDegree(180.0/acos(-1.0))
{}
virtual void SetUp();
protected:
static const cv::Size m_imageSize;
static const double m_pixelSize;
static const double m_circleConfusionPixel;
static const double m_lensFocalLength;
static const double m_lensFNumber;
static const double m_objectDistance;
static const double m_planeTiltDegree;
static const double m_pointTargetDist;
static const int m_pointTargetNum;
/** image distance corresponding to working distance */
double m_imageDistance;
/** image tilt angle corresponding to the tilt of the object plane */
double m_imageTiltDegree;
/** center of the field of view, near and far plane */
std::vector<cv::Vec3d> m_fovCenter;
/** normal of the field of view, near and far plane */
std::vector<cv::Vec3d> m_fovNormal;
/** points on a plane calibration target */
std::vector<cv::Point3d> m_pointTarget;
/** rotations for the calibration target */
std::vector<cv::Vec3d> m_pointTargetRvec;
/** translations for the calibration target */
std::vector<cv::Vec3d> m_pointTargetTvec;
/** camera matrix */
cv::Matx33d m_cameraMatrix;
/** distortion coefficients */
cv::Vec<double, NUM_DIST_COEFF_TILT> m_distortionCoeff;
/** random generator */
cv::RNG m_rng;
/** degree to radian conversion factor */
const double m_toRadian;
/** radian to degree conversion factor */
const double m_toDegree;
/**
computes for a given distance of an image or object point
the distance of the corresponding object or image point
*/
double opticalMap(double dist) {
return m_lensFocalLength*dist/(dist - m_lensFocalLength);
}
/** magnification of the optical map */
double magnification(double dist) {
return m_lensFocalLength/(dist - m_lensFocalLength);
}
/**
Changes given distortion coefficients randomly by adding
a uniformly distributed random variable in [-max max]
\param coeff input
\param max limits for the random variables
*/
void randomDistortionCoeff(
cv::Vec<double, NUM_DIST_COEFF_TILT>& coeff,
const cv::Vec<double, NUM_DIST_COEFF_TILT>& max)
{
for (int i = 0; i < coeff.rows; ++i)
coeff(i) += m_rng.uniform(-max(i), max(i));
}
/** numerical jacobian */
void numericalDerivative(
cv::Mat& jac,
double eps,
const std::vector<cv::Point3d>& obj,
const cv::Vec3d& rvec,
const cv::Vec3d& tvec,
const cv::Matx33d& camera,
const cv::Vec<double, NUM_DIST_COEFF_TILT>& distor);
/** remove points with projection outside the sensor array */
void removeInvalidPoints(
std::vector<cv::Point2d>& imagePoints,
std::vector<cv::Point3d>& objectPoints);
/** add uniform distribute noise in [-halfWidthNoise, halfWidthNoise]
to the image points and remove out of range points */
void addNoiseRemoveInvalidPoints(
std::vector<cv::Point2f>& imagePoints,
std::vector<cv::Point3f>& objectPoints,
std::vector<cv::Point2f>& noisyImagePoints,
double halfWidthNoise);
};
/** Number of Pixel of the sensor */
const cv::Size cameraCalibrationTiltTest::m_imageSize(1600, 1200);
/** Size of a pixel in mm */
const double cameraCalibrationTiltTest::m_pixelSize(.005);
/** Diameter of the circle of confusion */
const double cameraCalibrationTiltTest::m_circleConfusionPixel(3);
/** Focal length of the lens */
const double cameraCalibrationTiltTest::m_lensFocalLength(16.4);
/** F-Number */
const double cameraCalibrationTiltTest::m_lensFNumber(8);
/** Working distance */
const double cameraCalibrationTiltTest::m_objectDistance(200);
/** Angle between optical axis and object plane normal */
const double cameraCalibrationTiltTest::m_planeTiltDegree(55);
/** the calibration target are points on a square grid with this side length */
const double cameraCalibrationTiltTest::m_pointTargetDist(5);
/** the calibration target has (2*n + 1) x (2*n + 1) points */
const int cameraCalibrationTiltTest::m_pointTargetNum(15);
void cameraCalibrationTiltTest::SetUp()
{
m_imageDistance = opticalMap(m_objectDistance);
m_imageTiltDegree = m_toDegree * atan2(
m_imageDistance * tan(m_toRadian * m_planeTiltDegree),
m_objectDistance);
// half sensor height
double tmp = .5 * (m_imageSize.height - 1) * m_pixelSize
* cos(m_toRadian * m_imageTiltDegree);
// y-Value of tilted sensor
double yImage[2] = {tmp, -tmp};
// change in z because of the tilt
tmp *= sin(m_toRadian * m_imageTiltDegree);
// z-values of the sensor lower and upper corner
double zImage[2] = {
m_imageDistance + tmp,
m_imageDistance - tmp};
// circle of confusion
double circleConfusion = m_circleConfusionPixel*m_pixelSize;
// aperture of the lense
double aperture = m_lensFocalLength/m_lensFNumber;
// near and far factor on the image side
double nearFarFactorImage[2] = {
aperture/(aperture - circleConfusion),
aperture/(aperture + circleConfusion)};
// on the object side - points that determine the field of
// view
std::vector<cv::Vec3d> fovBottomTop(6);
std::vector<cv::Vec3d>::iterator itFov = fovBottomTop.begin();
for (size_t iBottomTop = 0; iBottomTop < 2; ++iBottomTop)
{
// mapping sensor to field of view
*itFov = cv::Vec3d(0,yImage[iBottomTop],zImage[iBottomTop]);
*itFov *= magnification((*itFov)(2));
++itFov;
for (size_t iNearFar = 0; iNearFar < 2; ++iNearFar, ++itFov)
{
// scaling to the near and far distance on the
// image side
*itFov = cv::Vec3d(0,yImage[iBottomTop],zImage[iBottomTop]) *
nearFarFactorImage[iNearFar];
// scaling to the object side
*itFov *= magnification((*itFov)(2));
}
}
m_fovCenter.resize(3);
m_fovNormal.resize(3);
for (size_t i = 0; i < 3; ++i)
{
m_fovCenter[i] = .5*(fovBottomTop[i] + fovBottomTop[i+3]);
m_fovNormal[i] = fovBottomTop[i+3] - fovBottomTop[i];
m_fovNormal[i] = cv::normalize(m_fovNormal[i]);
m_fovNormal[i] = cv::Vec3d(
m_fovNormal[i](0),
-m_fovNormal[i](2),
m_fovNormal[i](1));
// one target position in each plane
m_pointTargetTvec.push_back(m_fovCenter[i]);
cv::Vec3d rvec = cv::Vec3d(0,0,1).cross(m_fovNormal[i]);
rvec = cv::normalize(rvec);
rvec *= acos(m_fovNormal[i](2));
m_pointTargetRvec.push_back(rvec);
}
// calibration target
size_t num = 2*m_pointTargetNum + 1;
m_pointTarget.resize(num*num);
std::vector<cv::Point3d>::iterator itTarget = m_pointTarget.begin();
for (int iY = -m_pointTargetNum; iY <= m_pointTargetNum; ++iY)
{
for (int iX = -m_pointTargetNum; iX <= m_pointTargetNum; ++iX, ++itTarget)
{
*itTarget = cv::Point3d(iX, iY, 0) * m_pointTargetDist;
}
}
// oblique target positions
// approximate distance to the near and far plane
double dist = std::max(
std::abs(m_fovNormal[0].dot(m_fovCenter[0] - m_fovCenter[1])),
std::abs(m_fovNormal[0].dot(m_fovCenter[0] - m_fovCenter[2])));
// maximal angle such that target border "reaches" near and far plane
double maxAngle = atan2(dist, m_pointTargetNum*m_pointTargetDist);
std::vector<double> angle;
angle.push_back(-maxAngle);
angle.push_back(maxAngle);
cv::Matx33d baseMatrix;
cv::Rodrigues(m_pointTargetRvec.front(), baseMatrix);
for (std::vector<double>::const_iterator itAngle = angle.begin(); itAngle != angle.end(); ++itAngle)
{
cv::Matx33d rmat;
for (int i = 0; i < 2; ++i)
{
cv::Vec3d rvec(0,0,0);
rvec(i) = *itAngle;
cv::Rodrigues(rvec, rmat);
rmat = baseMatrix*rmat;
cv::Rodrigues(rmat, rvec);
m_pointTargetTvec.push_back(m_fovCenter.front());
m_pointTargetRvec.push_back(rvec);
}
}
// camera matrix
double cx = .5 * (m_imageSize.width - 1);
double cy = .5 * (m_imageSize.height - 1);
double f = m_imageDistance/m_pixelSize;
m_cameraMatrix = cv::Matx33d(
f,0,cx,
0,f,cy,
0,0,1);
// distortion coefficients
m_distortionCoeff = cv::Vec<double, NUM_DIST_COEFF_TILT>::all(0);
// tauX
m_distortionCoeff(12) = -m_toRadian*m_imageTiltDegree;
}
void cameraCalibrationTiltTest::numericalDerivative(
cv::Mat& jac,
double eps,
const std::vector<cv::Point3d>& obj,
const cv::Vec3d& rvec,
const cv::Vec3d& tvec,
const cv::Matx33d& camera,
const cv::Vec<double, NUM_DIST_COEFF_TILT>& distor)
{
cv::Vec3d r(rvec);
cv::Vec3d t(tvec);
cv::Matx33d cm(camera);
cv::Vec<double, NUM_DIST_COEFF_TILT> dc(distor);
double* param[10+NUM_DIST_COEFF_TILT] = {
&r(0), &r(1), &r(2),
&t(0), &t(1), &t(2),
&cm(0,0), &cm(1,1), &cm(0,2), &cm(1,2),
&dc(0), &dc(1), &dc(2), &dc(3), &dc(4), &dc(5), &dc(6),
&dc(7), &dc(8), &dc(9), &dc(10), &dc(11), &dc(12), &dc(13)};
std::vector<cv::Point2d> pix0, pix1;
double invEps = .5/eps;
for (int col = 0; col < 10+NUM_DIST_COEFF_TILT; ++col)
{
double save = *(param[col]);
*(param[col]) = save + eps;
cv::projectPoints(obj, r, t, cm, dc, pix0);
*(param[col]) = save - eps;
cv::projectPoints(obj, r, t, cm, dc, pix1);
*(param[col]) = save;
std::vector<cv::Point2d>::const_iterator it0 = pix0.begin();
std::vector<cv::Point2d>::const_iterator it1 = pix1.begin();
int row = 0;
for (;it0 != pix0.end(); ++it0, ++it1)
{
cv::Point2d d = invEps*(*it0 - *it1);
jac.at<double>(row, col) = d.x;
++row;
jac.at<double>(row, col) = d.y;
++row;
}
}
}
void cameraCalibrationTiltTest::removeInvalidPoints(
std::vector<cv::Point2d>& imagePoints,
std::vector<cv::Point3d>& objectPoints)
{
// remove object and imgage points out of range
std::vector<cv::Point2d>::iterator itImg = imagePoints.begin();
std::vector<cv::Point3d>::iterator itObj = objectPoints.begin();
while (itImg != imagePoints.end())
{
bool ok =
itImg->x >= 0 &&
itImg->x <= m_imageSize.width - 1.0 &&
itImg->y >= 0 &&
itImg->y <= m_imageSize.height - 1.0;
if (ok)
{
++itImg;
++itObj;
}
else
{
itImg = imagePoints.erase(itImg);
itObj = objectPoints.erase(itObj);
}
}
}
void cameraCalibrationTiltTest::addNoiseRemoveInvalidPoints(
std::vector<cv::Point2f>& imagePoints,
std::vector<cv::Point3f>& objectPoints,
std::vector<cv::Point2f>& noisyImagePoints,
double halfWidthNoise)
{
std::vector<cv::Point2f>::iterator itImg = imagePoints.begin();
std::vector<cv::Point3f>::iterator itObj = objectPoints.begin();
noisyImagePoints.clear();
noisyImagePoints.reserve(imagePoints.size());
while (itImg != imagePoints.end())
{
cv::Point2f pix = *itImg + cv::Point2f(
(float)m_rng.uniform(-halfWidthNoise, halfWidthNoise),
(float)m_rng.uniform(-halfWidthNoise, halfWidthNoise));
bool ok =
pix.x >= 0 &&
pix.x <= m_imageSize.width - 1.0 &&
pix.y >= 0 &&
pix.y <= m_imageSize.height - 1.0;
if (ok)
{
noisyImagePoints.push_back(pix);
++itImg;
++itObj;
}
else
{
itImg = imagePoints.erase(itImg);
itObj = objectPoints.erase(itObj);
}
}
}
TEST_F(cameraCalibrationTiltTest, projectPoints)
{
std::vector<cv::Point2d> imagePoints;
std::vector<cv::Point3d> objectPoints = m_pointTarget;
cv::Vec3d rvec = m_pointTargetRvec.front();
cv::Vec3d tvec = m_pointTargetTvec.front();
cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
.1, .1, // k1 k2
.01, .01, // p1 p2
.001, .001, .001, .001, // k3 k4 k5 k6
.001, .001, .001, .001, // s1 s2 s3 s4
.01, .01); // tauX tauY
for (size_t numTest = 0; numTest < 10; ++numTest)
{
// create random distortion coefficients
cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
// projection
cv::projectPoints(
objectPoints,
rvec,
tvec,
m_cameraMatrix,
distortionCoeff,
imagePoints);
// remove object and imgage points out of range
removeInvalidPoints(imagePoints, objectPoints);
int numPoints = (int)imagePoints.size();
int numParams = 10 + distortionCoeff.rows;
cv::Mat jacobian(2*numPoints, numParams, CV_64FC1);
// projection and jacobian
cv::projectPoints(
objectPoints,
rvec,
tvec,
m_cameraMatrix,
distortionCoeff,
imagePoints,
jacobian);
// numerical derivatives
cv::Mat numericJacobian(2*numPoints, numParams, CV_64FC1);
double eps = 1e-7;
numericalDerivative(
numericJacobian,
eps,
objectPoints,
rvec,
tvec,
m_cameraMatrix,
distortionCoeff);
#if 0
for (size_t row = 0; row < 2; ++row)
{
std::cout << "------ Row = " << row << " ------\n";
for (size_t i = 0; i < 10+NUM_DIST_COEFF_TILT; ++i)
{
std::cout << i
<< " jac = " << jacobian.at<double>(row,i)
<< " num = " << numericJacobian.at<double>(row,i)
<< " rel. diff = " << abs(numericJacobian.at<double>(row,i) - jacobian.at<double>(row,i))/abs(numericJacobian.at<double>(row,i))
<< "\n";
}
}
#endif
// relative difference for large values (rvec and tvec)
cv::Mat check = abs(jacobian(cv::Range::all(), cv::Range(0,6)) - numericJacobian(cv::Range::all(), cv::Range(0,6)))/
(1 + abs(jacobian(cv::Range::all(), cv::Range(0,6))));
double minVal, maxVal;
cv::minMaxIdx(check, &minVal, &maxVal);
EXPECT_LE(maxVal, .01);
// absolute difference for distortion and camera matrix
EXPECT_MAT_NEAR(jacobian(cv::Range::all(), cv::Range(6,numParams)), numericJacobian(cv::Range::all(), cv::Range(6,numParams)), 1e-5);
}
}
TEST_F(cameraCalibrationTiltTest, undistortPoints)
{
cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
.2, .1, // k1 k2
.01, .01, // p1 p2
.01, .01, .01, .01, // k3 k4 k5 k6
.001, .001, .001, .001, // s1 s2 s3 s4
.001, .001); // tauX tauY
double step = 99;
double toleranceBackProjection = 1e-5;
for (size_t numTest = 0; numTest < 10; ++numTest)
{
cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
// distorted points
std::vector<cv::Point2d> distorted;
for (double x = 0; x <= m_imageSize.width-1; x += step)
for (double y = 0; y <= m_imageSize.height-1; y += step)
distorted.push_back(cv::Point2d(x,y));
std::vector<cv::Point2d> normalizedUndistorted;
// undistort
cv::undistortPoints(distorted,
normalizedUndistorted,
m_cameraMatrix,
distortionCoeff);
// copy normalized points to 3D
std::vector<cv::Point3d> objectPoints;
for (std::vector<cv::Point2d>::const_iterator itPnt = normalizedUndistorted.begin();
itPnt != normalizedUndistorted.end(); ++itPnt)
objectPoints.push_back(cv::Point3d(itPnt->x, itPnt->y, 1));
// project
std::vector<cv::Point2d> imagePoints(objectPoints.size());
cv::projectPoints(objectPoints,
cv::Vec3d(0,0,0),
cv::Vec3d(0,0,0),
m_cameraMatrix,
distortionCoeff,
imagePoints);
EXPECT_MAT_NEAR(distorted, imagePoints, toleranceBackProjection);
}
}
template <typename INPUT, typename ESTIMATE>
void show(const std::string& name, const INPUT in, const ESTIMATE est)
{
std::cout << name << " = " << est << " (init = " << in
<< ", diff = " << est-in << ")\n";
}
template <typename INPUT>
void showVec(const std::string& name, const INPUT& in, const cv::Mat& est)
{
for (size_t i = 0; i < in.channels; ++i)
{
std::stringstream ss;
ss << name << "[" << i << "]";
show(ss.str(), in(i), est.at<double>(i));
}
}
/**
For given camera matrix and distortion coefficients
- project point target in different positions onto the sensor
- add pixel noise
- estimate camera model with noisy measurements
- compare result with initial model parameter
Parameter are differently affected by the noise
*/
TEST_F(cameraCalibrationTiltTest, calibrateCamera)
{
cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
.2, .1, // k1 k2
.01, .01, // p1 p2
0, 0, 0, 0, // k3 k4 k5 k6
.001, .001, .001, .001, // s1 s2 s3 s4
.001, .001); // tauX tauY
double pixelNoiseHalfWidth = .5;
std::vector<cv::Point3f> pointTarget;
pointTarget.reserve(m_pointTarget.size());
for (std::vector<cv::Point3d>::const_iterator it = m_pointTarget.begin(); it != m_pointTarget.end(); ++it)
pointTarget.push_back(cv::Point3f(
(float)(it->x),
(float)(it->y),
(float)(it->z)));
for (size_t numTest = 0; numTest < 5; ++numTest)
{
// create random distortion coefficients
cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
// container for calibration data
std::vector<std::vector<cv::Point3f> > viewsObjectPoints;
std::vector<std::vector<cv::Point2f> > viewsImagePoints;
std::vector<std::vector<cv::Point2f> > viewsNoisyImagePoints;
// simulate calibration data with projectPoints
std::vector<cv::Vec3d>::const_iterator itRvec = m_pointTargetRvec.begin();
std::vector<cv::Vec3d>::const_iterator itTvec = m_pointTargetTvec.begin();
// loop over different views
for (;itRvec != m_pointTargetRvec.end(); ++ itRvec, ++itTvec)
{
std::vector<cv::Point3f> objectPoints(pointTarget);
std::vector<cv::Point2f> imagePoints;
std::vector<cv::Point2f> noisyImagePoints;
// project calibration target to sensor
cv::projectPoints(
objectPoints,
*itRvec,
*itTvec,
m_cameraMatrix,
distortionCoeff,
imagePoints);
// remove invisible points
addNoiseRemoveInvalidPoints(
imagePoints,
objectPoints,
noisyImagePoints,
pixelNoiseHalfWidth);
// add data for view
viewsNoisyImagePoints.push_back(noisyImagePoints);
viewsImagePoints.push_back(imagePoints);
viewsObjectPoints.push_back(objectPoints);
}
// Output
std::vector<cv::Mat> outRvecs, outTvecs;
cv::Mat outCameraMatrix(3, 3, CV_64F, cv::Scalar::all(1)), outDistCoeff;
// Stopping criteria
cv::TermCriteria stop(
cv::TermCriteria::COUNT+cv::TermCriteria::EPS,
50000,
1e-14);
// model choice
int flag =
cv::CALIB_FIX_ASPECT_RATIO |
// cv::CALIB_RATIONAL_MODEL |
cv::CALIB_FIX_K3 |
// cv::CALIB_FIX_K6 |
cv::CALIB_THIN_PRISM_MODEL |
cv::CALIB_TILTED_MODEL;
// estimate
double backProjErr = cv::calibrateCamera(
viewsObjectPoints,
viewsNoisyImagePoints,
m_imageSize,
outCameraMatrix,
outDistCoeff,
outRvecs,
outTvecs,
flag,
stop);
EXPECT_LE(backProjErr, pixelNoiseHalfWidth);
#if 0
std::cout << "------ estimate ------\n";
std::cout << "back projection error = " << backProjErr << "\n";
std::cout << "points per view = {" << viewsObjectPoints.front().size();
for (size_t i = 1; i < viewsObjectPoints.size(); ++i)
std::cout << ", " << viewsObjectPoints[i].size();
std::cout << "}\n";
show("fx", m_cameraMatrix(0,0), outCameraMatrix.at<double>(0,0));
show("fy", m_cameraMatrix(1,1), outCameraMatrix.at<double>(1,1));
show("cx", m_cameraMatrix(0,2), outCameraMatrix.at<double>(0,2));
show("cy", m_cameraMatrix(1,2), outCameraMatrix.at<double>(1,2));
showVec("distor", distortionCoeff, outDistCoeff);
#endif
if (pixelNoiseHalfWidth > 0)
{
double tolRvec = pixelNoiseHalfWidth;
double tolTvec = m_objectDistance * tolRvec;
// back projection error
for (size_t i = 0; i < viewsNoisyImagePoints.size(); ++i)
{
double dRvec = cv::norm(m_pointTargetRvec[i],
cv::Vec3d(outRvecs[i].at<double>(0), outRvecs[i].at<double>(1), outRvecs[i].at<double>(2))
);
EXPECT_LE(dRvec, tolRvec);
double dTvec = cv::norm(m_pointTargetTvec[i],
cv::Vec3d(outTvecs[i].at<double>(0), outTvecs[i].at<double>(1), outTvecs[i].at<double>(2))
);
EXPECT_LE(dTvec, tolTvec);
std::vector<cv::Point2f> backProjection;
cv::projectPoints(
viewsObjectPoints[i],
outRvecs[i],
outTvecs[i],
outCameraMatrix,
outDistCoeff,
backProjection);
EXPECT_MAT_NEAR(backProjection, viewsNoisyImagePoints[i], 1.5*pixelNoiseHalfWidth);
EXPECT_MAT_NEAR(backProjection, viewsImagePoints[i], 1.5*pixelNoiseHalfWidth);
}
}
pixelNoiseHalfWidth *= .25;
}
}
}} // namespace

View File

@ -0,0 +1,331 @@
/*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 "test_chessboardgenerator.hpp"
namespace cv {
ChessBoardGenerator::ChessBoardGenerator(const Size& _patternSize) : sensorWidth(32), sensorHeight(24),
squareEdgePointsNum(200), min_cos(std::sqrt(3.f)*0.5f), cov(0.5),
patternSize(_patternSize), rendererResolutionMultiplier(4), tvec(Mat::zeros(1, 3, CV_32F))
{
rvec.create(3, 1, CV_32F);
cvtest::Rodrigues(Mat::eye(3, 3, CV_32F), rvec);
}
void ChessBoardGenerator::generateEdge(const Point3f& p1, const Point3f& p2, vector<Point3f>& out) const
{
Point3f step = (p2 - p1) * (1.f/squareEdgePointsNum);
for(size_t n = 0; n < squareEdgePointsNum; ++n)
out.push_back( p1 + step * (float)n);
}
Size ChessBoardGenerator::cornersSize() const
{
return Size(patternSize.width-1, patternSize.height-1);
}
struct Mult
{
float m;
Mult(int mult) : m((float)mult) {}
Point2f operator()(const Point2f& p)const { return p * m; }
};
void ChessBoardGenerator::generateBasis(Point3f& pb1, Point3f& pb2) const
{
RNG& rng = theRNG();
Vec3f n;
for(;;)
{
n[0] = rng.uniform(-1.f, 1.f);
n[1] = rng.uniform(-1.f, 1.f);
n[2] = rng.uniform(0.0f, 1.f);
float len = (float)norm(n);
if (len < 1e-3)
continue;
n[0]/=len;
n[1]/=len;
n[2]/=len;
if (n[2] > min_cos)
break;
}
Vec3f n_temp = n; n_temp[0] += 100;
Vec3f b1 = n.cross(n_temp);
Vec3f b2 = n.cross(b1);
float len_b1 = (float)norm(b1);
float len_b2 = (float)norm(b2);
pb1 = Point3f(b1[0]/len_b1, b1[1]/len_b1, b1[2]/len_b1);
pb2 = Point3f(b2[0]/len_b1, b2[1]/len_b2, b2[2]/len_b2);
}
Mat ChessBoardGenerator::generateChessBoard(const Mat& bg, const Mat& camMat, const Mat& distCoeffs,
const Point3f& zero, const Point3f& pb1, const Point3f& pb2,
float sqWidth, float sqHeight, const vector<Point3f>& whole,
vector<Point2f>& corners) const
{
vector< vector<Point> > squares_black;
for(int i = 0; i < patternSize.width; ++i)
for(int j = 0; j < patternSize.height; ++j)
if ( (i % 2 == 0 && j % 2 == 0) || (i % 2 != 0 && j % 2 != 0) )
{
vector<Point3f> pts_square3d;
vector<Point2f> pts_square2d;
Point3f p1 = zero + (i + 0) * sqWidth * pb1 + (j + 0) * sqHeight * pb2;
Point3f p2 = zero + (i + 1) * sqWidth * pb1 + (j + 0) * sqHeight * pb2;
Point3f p3 = zero + (i + 1) * sqWidth * pb1 + (j + 1) * sqHeight * pb2;
Point3f p4 = zero + (i + 0) * sqWidth * pb1 + (j + 1) * sqHeight * pb2;
generateEdge(p1, p2, pts_square3d);
generateEdge(p2, p3, pts_square3d);
generateEdge(p3, p4, pts_square3d);
generateEdge(p4, p1, pts_square3d);
projectPoints(pts_square3d, rvec, tvec, camMat, distCoeffs, pts_square2d);
squares_black.resize(squares_black.size() + 1);
vector<Point2f> temp;
approxPolyDP(pts_square2d, temp, 1.0, true);
transform(temp.begin(), temp.end(), back_inserter(squares_black.back()), Mult(rendererResolutionMultiplier));
}
/* calculate corners */
corners3d.clear();
for(int j = 0; j < patternSize.height - 1; ++j)
for(int i = 0; i < patternSize.width - 1; ++i)
corners3d.push_back(zero + (i + 1) * sqWidth * pb1 + (j + 1) * sqHeight * pb2);
corners.clear();
projectPoints(corners3d, rvec, tvec, camMat, distCoeffs, corners);
vector<Point3f> whole3d;
vector<Point2f> whole2d;
generateEdge(whole[0], whole[1], whole3d);
generateEdge(whole[1], whole[2], whole3d);
generateEdge(whole[2], whole[3], whole3d);
generateEdge(whole[3], whole[0], whole3d);
projectPoints(whole3d, rvec, tvec, camMat, distCoeffs, whole2d);
vector<Point2f> temp_whole2d;
approxPolyDP(whole2d, temp_whole2d, 1.0, true);
vector< vector<Point > > whole_contour(1);
transform(temp_whole2d.begin(), temp_whole2d.end(),
back_inserter(whole_contour.front()), Mult(rendererResolutionMultiplier));
Mat result;
if (rendererResolutionMultiplier == 1)
{
result = bg.clone();
drawContours(result, whole_contour, -1, Scalar::all(255), FILLED, LINE_AA);
drawContours(result, squares_black, -1, Scalar::all(0), FILLED, LINE_AA);
}
else
{
Mat tmp;
resize(bg, tmp, bg.size() * rendererResolutionMultiplier, 0, 0, INTER_LINEAR_EXACT);
drawContours(tmp, whole_contour, -1, Scalar::all(255), FILLED, LINE_AA);
drawContours(tmp, squares_black, -1, Scalar::all(0), FILLED, LINE_AA);
resize(tmp, result, bg.size(), 0, 0, INTER_AREA);
}
return result;
}
Mat ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, vector<Point2f>& corners) const
{
cov = std::min(cov, 0.8);
double fovx, fovy, focalLen;
Point2d principalPoint;
double aspect;
calibrationMatrixValues( camMat, bg.size(), sensorWidth, sensorHeight,
fovx, fovy, focalLen, principalPoint, aspect);
RNG& rng = theRNG();
float d1 = static_cast<float>(rng.uniform(0.1, 10.0));
float ah = static_cast<float>(rng.uniform(-fovx/2 * cov, fovx/2 * cov) * CV_PI / 180);
float av = static_cast<float>(rng.uniform(-fovy/2 * cov, fovy/2 * cov) * CV_PI / 180);
Point3f p;
p.z = cos(ah) * d1;
p.x = sin(ah) * d1;
p.y = p.z * tan(av);
Point3f pb1, pb2;
generateBasis(pb1, pb2);
float cbHalfWidth = static_cast<float>(norm(p) * sin( std::min(fovx, fovy) * 0.5 * CV_PI / 180));
float cbHalfHeight = cbHalfWidth * patternSize.height / patternSize.width;
float cbHalfWidthEx = cbHalfWidth * ( patternSize.width + 1) / patternSize.width;
float cbHalfHeightEx = cbHalfHeight * (patternSize.height + 1) / patternSize.height;
vector<Point3f> pts3d(4);
vector<Point2f> pts2d(4);
for(;;)
{
pts3d[0] = p + pb1 * cbHalfWidthEx + cbHalfHeightEx * pb2;
pts3d[1] = p + pb1 * cbHalfWidthEx - cbHalfHeightEx * pb2;
pts3d[2] = p - pb1 * cbHalfWidthEx - cbHalfHeightEx * pb2;
pts3d[3] = p - pb1 * cbHalfWidthEx + cbHalfHeightEx * pb2;
/* can remake with better perf */
projectPoints(pts3d, rvec, tvec, camMat, distCoeffs, pts2d);
bool inrect1 = pts2d[0].x < bg.cols && pts2d[0].y < bg.rows && pts2d[0].x > 0 && pts2d[0].y > 0;
bool inrect2 = pts2d[1].x < bg.cols && pts2d[1].y < bg.rows && pts2d[1].x > 0 && pts2d[1].y > 0;
bool inrect3 = pts2d[2].x < bg.cols && pts2d[2].y < bg.rows && pts2d[2].x > 0 && pts2d[2].y > 0;
bool inrect4 = pts2d[3].x < bg.cols && pts2d[3].y < bg.rows && pts2d[3].x > 0 && pts2d[3].y > 0;
if (inrect1 && inrect2 && inrect3 && inrect4)
break;
cbHalfWidth*=0.8f;
cbHalfHeight = cbHalfWidth * patternSize.height / patternSize.width;
cbHalfWidthEx = cbHalfWidth * ( patternSize.width + 1) / patternSize.width;
cbHalfHeightEx = cbHalfHeight * (patternSize.height + 1) / patternSize.height;
}
Point3f zero = p - pb1 * cbHalfWidth - cbHalfHeight * pb2;
float sqWidth = 2 * cbHalfWidth/patternSize.width;
float sqHeight = 2 * cbHalfHeight/patternSize.height;
return generateChessBoard(bg, camMat, distCoeffs, zero, pb1, pb2, sqWidth, sqHeight, pts3d, corners);
}
Mat ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs,
const Size2f& squareSize, vector<Point2f>& corners) const
{
cov = std::min(cov, 0.8);
double fovx, fovy, focalLen;
Point2d principalPoint;
double aspect;
calibrationMatrixValues( camMat, bg.size(), sensorWidth, sensorHeight,
fovx, fovy, focalLen, principalPoint, aspect);
RNG& rng = theRNG();
float d1 = static_cast<float>(rng.uniform(0.1, 10.0));
float ah = static_cast<float>(rng.uniform(-fovx/2 * cov, fovx/2 * cov) * CV_PI / 180);
float av = static_cast<float>(rng.uniform(-fovy/2 * cov, fovy/2 * cov) * CV_PI / 180);
Point3f p;
p.z = cos(ah) * d1;
p.x = sin(ah) * d1;
p.y = p.z * tan(av);
Point3f pb1, pb2;
generateBasis(pb1, pb2);
float cbHalfWidth = squareSize.width * patternSize.width * 0.5f;
float cbHalfHeight = squareSize.height * patternSize.height * 0.5f;
float cbHalfWidthEx = cbHalfWidth * ( patternSize.width + 1) / patternSize.width;
float cbHalfHeightEx = cbHalfHeight * (patternSize.height + 1) / patternSize.height;
vector<Point3f> pts3d(4);
vector<Point2f> pts2d(4);
for(;;)
{
pts3d[0] = p + pb1 * cbHalfWidthEx + cbHalfHeightEx * pb2;
pts3d[1] = p + pb1 * cbHalfWidthEx - cbHalfHeightEx * pb2;
pts3d[2] = p - pb1 * cbHalfWidthEx - cbHalfHeightEx * pb2;
pts3d[3] = p - pb1 * cbHalfWidthEx + cbHalfHeightEx * pb2;
/* can remake with better perf */
projectPoints(pts3d, rvec, tvec, camMat, distCoeffs, pts2d);
bool inrect1 = pts2d[0].x < bg.cols && pts2d[0].y < bg.rows && pts2d[0].x > 0 && pts2d[0].y > 0;
bool inrect2 = pts2d[1].x < bg.cols && pts2d[1].y < bg.rows && pts2d[1].x > 0 && pts2d[1].y > 0;
bool inrect3 = pts2d[2].x < bg.cols && pts2d[2].y < bg.rows && pts2d[2].x > 0 && pts2d[2].y > 0;
bool inrect4 = pts2d[3].x < bg.cols && pts2d[3].y < bg.rows && pts2d[3].x > 0 && pts2d[3].y > 0;
if ( inrect1 && inrect2 && inrect3 && inrect4)
break;
p.z *= 1.1f;
}
Point3f zero = p - pb1 * cbHalfWidth - cbHalfHeight * pb2;
return generateChessBoard(bg, camMat, distCoeffs, zero, pb1, pb2,
squareSize.width, squareSize.height, pts3d, corners);
}
Mat ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs,
const Size2f& squareSize, const Point3f& pos, vector<Point2f>& corners) const
{
cov = std::min(cov, 0.8);
Point3f p = pos;
Point3f pb1, pb2;
generateBasis(pb1, pb2);
float cbHalfWidth = squareSize.width * patternSize.width * 0.5f;
float cbHalfHeight = squareSize.height * patternSize.height * 0.5f;
float cbHalfWidthEx = cbHalfWidth * ( patternSize.width + 1) / patternSize.width;
float cbHalfHeightEx = cbHalfHeight * (patternSize.height + 1) / patternSize.height;
vector<Point3f> pts3d(4);
vector<Point2f> pts2d(4);
pts3d[0] = p + pb1 * cbHalfWidthEx + cbHalfHeightEx * pb2;
pts3d[1] = p + pb1 * cbHalfWidthEx - cbHalfHeightEx * pb2;
pts3d[2] = p - pb1 * cbHalfWidthEx - cbHalfHeightEx * pb2;
pts3d[3] = p - pb1 * cbHalfWidthEx + cbHalfHeightEx * pb2;
/* can remake with better perf */
projectPoints(pts3d, rvec, tvec, camMat, distCoeffs, pts2d);
Point3f zero = p - pb1 * cbHalfWidth - cbHalfHeight * pb2;
return generateChessBoard(bg, camMat, distCoeffs, zero, pb1, pb2,
squareSize.width, squareSize.height, pts3d, corners);
}
} // namespace

View File

@ -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.
#ifndef CV_CHESSBOARDGENERATOR_H143KJTVYM389YTNHKFDHJ89NYVMO3VLMEJNTBGUEIYVCM203P
#define CV_CHESSBOARDGENERATOR_H143KJTVYM389YTNHKFDHJ89NYVMO3VLMEJNTBGUEIYVCM203P
namespace cv
{
using std::vector;
class ChessBoardGenerator
{
public:
double sensorWidth;
double sensorHeight;
size_t squareEdgePointsNum;
double min_cos;
mutable double cov;
Size patternSize;
int rendererResolutionMultiplier;
ChessBoardGenerator(const Size& patternSize = Size(8, 6));
Mat operator()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, std::vector<Point2f>& corners) const;
Mat operator()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, const Size2f& squareSize, std::vector<Point2f>& corners) const;
Mat operator()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, const Size2f& squareSize, const Point3f& pos, std::vector<Point2f>& corners) const;
Size cornersSize() const;
mutable std::vector<Point3f> corners3d;
private:
void generateEdge(const Point3f& p1, const Point3f& p2, std::vector<Point3f>& out) const;
Mat generateChessBoard(const Mat& bg, const Mat& camMat, const Mat& distCoeffs,
const Point3f& zero, const Point3f& pb1, const Point3f& pb2,
float sqWidth, float sqHeight, const std::vector<Point3f>& whole, std::vector<Point2f>& corners) const;
void generateBasis(Point3f& pb1, Point3f& pb2) const;
Mat rvec, tvec;
};
}
#endif

View File

@ -0,0 +1,754 @@
/*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 "test_chessboardgenerator.hpp"
#include <functional>
namespace opencv_test { namespace {
#define _L2_ERR
//#define DEBUG_CHESSBOARD
#ifdef DEBUG_CHESSBOARD
void show_points( const Mat& gray, const Mat& expected, const vector<Point2f>& actual, bool was_found )
{
Mat rgb( gray.size(), CV_8U);
merge(vector<Mat>(3, gray), rgb);
for(size_t i = 0; i < actual.size(); i++ )
circle( rgb, actual[i], 5, Scalar(0, 0, 200), 1, LINE_AA);
if( !expected.empty() )
{
const Point2f* u_data = expected.ptr<Point2f>();
size_t count = expected.cols * expected.rows;
for(size_t i = 0; i < count; i++ )
circle(rgb, u_data[i], 4, Scalar(0, 240, 0), 1, LINE_AA);
}
putText(rgb, was_found ? "FOUND !!!" : "NOT FOUND", Point(5, 20), FONT_HERSHEY_PLAIN, 1, Scalar(0, 240, 0));
imshow( "test", rgb ); while ((uchar)waitKey(0) != 'q') {};
}
#else
#define show_points(...)
#endif
enum Pattern { CHESSBOARD,CHESSBOARD_SB,CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID};
class CV_ChessboardDetectorTest : public cvtest::BaseTest
{
public:
CV_ChessboardDetectorTest( Pattern pattern, int algorithmFlags = 0 );
protected:
void run(int);
void run_batch(const string& filename);
bool checkByGenerator();
bool checkByGeneratorHighAccuracy();
// wraps calls based on the given pattern
bool findChessboardCornersWrapper(InputArray image, Size patternSize, OutputArray corners,int flags);
Pattern pattern;
int algorithmFlags;
};
CV_ChessboardDetectorTest::CV_ChessboardDetectorTest( Pattern _pattern, int _algorithmFlags )
{
pattern = _pattern;
algorithmFlags = _algorithmFlags;
}
double calcError(const vector<Point2f>& v, const Mat& u)
{
int count_exp = u.cols * u.rows;
const Point2f* u_data = u.ptr<Point2f>();
double err = std::numeric_limits<double>::max();
for( int k = 0; k < 2; ++k )
{
double err1 = 0;
for( int j = 0; j < count_exp; ++j )
{
int j1 = k == 0 ? j : count_exp - j - 1;
double dx = fabs( v[j].x - u_data[j1].x );
double dy = fabs( v[j].y - u_data[j1].y );
#if defined(_L2_ERR)
err1 += dx*dx + dy*dy;
#else
dx = MAX( dx, dy );
if( dx > err1 )
err1 = dx;
#endif //_L2_ERR
//printf("dx = %f\n", dx);
}
//printf("\n");
err = min(err, err1);
}
#if defined(_L2_ERR)
err = sqrt(err/count_exp);
#endif //_L2_ERR
return err;
}
const double rough_success_error_level = 2.5;
const double precise_success_error_level = 2;
/* ///////////////////// chess_corner_test ///////////////////////// */
void CV_ChessboardDetectorTest::run( int /*start_from */)
{
ts->set_failed_test_info( cvtest::TS::OK );
/*if (!checkByGenerator())
return;*/
switch( pattern )
{
case CHESSBOARD_SB:
checkByGeneratorHighAccuracy(); // not supported by CHESSBOARD
/* fallthrough */
case CHESSBOARD:
checkByGenerator();
if (ts->get_err_code() != cvtest::TS::OK)
{
break;
}
run_batch("negative_list.dat");
if (ts->get_err_code() != cvtest::TS::OK)
{
break;
}
run_batch("chessboard_list.dat");
if (ts->get_err_code() != cvtest::TS::OK)
{
break;
}
run_batch("chessboard_list_subpixel.dat");
break;
case CIRCLES_GRID:
run_batch("circles_list.dat");
break;
case ASYMMETRIC_CIRCLES_GRID:
run_batch("acircles_list.dat");
break;
}
}
void CV_ChessboardDetectorTest::run_batch( const string& filename )
{
ts->printf(cvtest::TS::LOG, "\nRunning batch %s\n", filename.c_str());
//#define WRITE_POINTS 1
#ifndef WRITE_POINTS
double max_rough_error = 0, max_precise_error = 0;
#endif
string folder;
switch( pattern )
{
case CHESSBOARD:
case CHESSBOARD_SB:
folder = string(ts->get_data_path()) + "cv/cameracalibration/";
break;
case CIRCLES_GRID:
folder = string(ts->get_data_path()) + "cv/cameracalibration/circles/";
break;
case ASYMMETRIC_CIRCLES_GRID:
folder = string(ts->get_data_path()) + "cv/cameracalibration/asymmetric_circles/";
break;
}
FileStorage fs( folder + filename, FileStorage::READ );
FileNode board_list = fs["boards"];
if( !fs.isOpened() || board_list.empty() || !board_list.isSeq() || board_list.size() % 2 != 0 )
{
ts->printf( cvtest::TS::LOG, "%s can not be read or is not valid\n", (folder + filename).c_str() );
ts->printf( cvtest::TS::LOG, "fs.isOpened=%d, board_list.empty=%d, board_list.isSeq=%d,board_list.size()%2=%d\n",
fs.isOpened(), (int)board_list.empty(), board_list.isSeq(), board_list.size()%2);
ts->set_failed_test_info( cvtest::TS::FAIL_MISSING_TEST_DATA );
return;
}
int progress = 0;
int max_idx = (int)board_list.size()/2;
double sum_error = 0.0;
int count = 0;
for(int idx = 0; idx < max_idx; ++idx )
{
ts->update_context( this, idx, true );
/* read the image */
String img_file = board_list[idx * 2];
Mat gray = imread( folder + img_file, 0);
if( gray.empty() )
{
ts->printf( cvtest::TS::LOG, "one of chessboard images can't be read: %s\n", img_file.c_str() );
ts->set_failed_test_info( cvtest::TS::FAIL_MISSING_TEST_DATA );
return;
}
String _filename = folder + (String)board_list[idx * 2 + 1];
bool doesContatinChessboard;
float sharpness;
Mat expected;
{
FileStorage fs1(_filename, FileStorage::READ);
fs1["corners"] >> expected;
fs1["isFound"] >> doesContatinChessboard;
fs1["sharpness"] >> sharpness ;
fs1.release();
}
size_t count_exp = static_cast<size_t>(expected.cols * expected.rows);
Size pattern_size = expected.size();
vector<Point2f> v;
int flags = 0;
switch( pattern )
{
case CHESSBOARD:
flags = CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE;
break;
case CIRCLES_GRID:
case CHESSBOARD_SB:
case ASYMMETRIC_CIRCLES_GRID:
default:
flags = 0;
}
bool result = findChessboardCornersWrapper(gray, pattern_size,v,flags);
if(result && sharpness && (pattern == CHESSBOARD_SB || pattern == CHESSBOARD))
{
Scalar s= estimateChessboardSharpness(gray,pattern_size,v);
if(fabs(s[0] - sharpness) > 0.1)
{
ts->printf(cvtest::TS::LOG, "chessboard image has a wrong sharpness in %s. Expected %f but measured %f\n", img_file.c_str(),sharpness,s[0]);
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
show_points( gray, expected, v, result );
return;
}
}
if(result ^ doesContatinChessboard || (doesContatinChessboard && v.size() != count_exp))
{
ts->printf( cvtest::TS::LOG, "chessboard is detected incorrectly in %s\n", img_file.c_str() );
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
show_points( gray, expected, v, result );
return;
}
if( result )
{
#ifndef WRITE_POINTS
double err = calcError(v, expected);
max_rough_error = MAX( max_rough_error, err );
#endif
if( pattern == CHESSBOARD )
cornerSubPix( gray, v, Size(5, 5), Size(-1,-1), TermCriteria(TermCriteria::EPS|TermCriteria::MAX_ITER, 30, 0.1));
//find4QuadCornerSubpix(gray, v, Size(5, 5));
show_points( gray, expected, v, result );
#ifndef WRITE_POINTS
// printf("called find4QuadCornerSubpix\n");
err = calcError(v, expected);
sum_error += err;
count++;
if( err > precise_success_error_level )
{
ts->printf( cvtest::TS::LOG, "Image %s: bad accuracy of adjusted corners %f\n", img_file.c_str(), err );
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
return;
}
ts->printf(cvtest::TS::LOG, "Error on %s is %f\n", img_file.c_str(), err);
max_precise_error = MAX( max_precise_error, err );
#endif
}
else
{
show_points( gray, Mat(), v, result );
}
#ifdef WRITE_POINTS
Mat mat_v(pattern_size, CV_32FC2, (void*)&v[0]);
FileStorage fs(_filename, FileStorage::WRITE);
fs << "isFound" << result;
fs << "corners" << mat_v;
fs.release();
#endif
progress = update_progress( progress, idx, max_idx, 0 );
}
if (count != 0)
sum_error /= count;
ts->printf(cvtest::TS::LOG, "Average error is %f (%d patterns have been found)\n", sum_error, count);
}
double calcErrorMinError(const Size& cornSz, const vector<Point2f>& corners_found, const vector<Point2f>& corners_generated)
{
Mat m1(cornSz, CV_32FC2, (Point2f*)&corners_generated[0]);
Mat m2; flip(m1, m2, 0);
Mat m3; flip(m1, m3, 1); m3 = m3.t(); flip(m3, m3, 1);
Mat m4 = m1.t(); flip(m4, m4, 1);
double min1 = min(calcError(corners_found, m1), calcError(corners_found, m2));
double min2 = min(calcError(corners_found, m3), calcError(corners_found, m4));
return min(min1, min2);
}
bool validateData(const ChessBoardGenerator& cbg, const Size& imgSz,
const vector<Point2f>& corners_generated)
{
Size cornersSize = cbg.cornersSize();
Mat_<Point2f> mat(cornersSize.height, cornersSize.width, (Point2f*)&corners_generated[0]);
double minNeibDist = std::numeric_limits<double>::max();
double tmp = 0;
for(int i = 1; i < mat.rows - 2; ++i)
for(int j = 1; j < mat.cols - 2; ++j)
{
const Point2f& cur = mat(i, j);
tmp = cv::norm(cur - mat(i + 1, j + 1)); // TODO cvtest
if (tmp < minNeibDist)
minNeibDist = tmp;
tmp = cv::norm(cur - mat(i - 1, j + 1)); // TODO cvtest
if (tmp < minNeibDist)
minNeibDist = tmp;
tmp = cv::norm(cur - mat(i + 1, j - 1)); // TODO cvtest
if (tmp < minNeibDist)
minNeibDist = tmp;
tmp = cv::norm(cur - mat(i - 1, j - 1)); // TODO cvtest
if (tmp < minNeibDist)
minNeibDist = tmp;
}
const double threshold = 0.25;
double cbsize = (max(cornersSize.width, cornersSize.height) + 1) * minNeibDist;
int imgsize = min(imgSz.height, imgSz.width);
return imgsize * threshold < cbsize;
}
bool CV_ChessboardDetectorTest::findChessboardCornersWrapper(InputArray image, Size patternSize, OutputArray corners,int flags)
{
switch(pattern)
{
case CHESSBOARD:
return findChessboardCorners(image,patternSize,corners,flags);
case CHESSBOARD_SB:
// check default settings until flags have been specified
return findChessboardCornersSB(image,patternSize,corners,0);
case ASYMMETRIC_CIRCLES_GRID:
flags |= CALIB_CB_ASYMMETRIC_GRID | algorithmFlags;
return findCirclesGrid(image, patternSize,corners,flags);
case CIRCLES_GRID:
flags |= CALIB_CB_SYMMETRIC_GRID;
return findCirclesGrid(image, patternSize,corners,flags);
default:
ts->printf( cvtest::TS::LOG, "Internal Error: unsupported chessboard pattern" );
ts->set_failed_test_info( cvtest::TS::FAIL_GENERIC);
}
return false;
}
bool CV_ChessboardDetectorTest::checkByGenerator()
{
bool res = true;
//theRNG() = 0x58e6e895b9913160;
//cv::DefaultRngAuto dra;
//theRNG() = *ts->get_rng();
Mat bg(Size(800, 600), CV_8UC3, Scalar::all(255));
randu(bg, Scalar::all(0), Scalar::all(255));
GaussianBlur(bg, bg, Size(5, 5), 0.0);
Mat_<float> camMat(3, 3);
camMat << 300.f, 0.f, bg.cols/2.f, 0, 300.f, bg.rows/2.f, 0.f, 0.f, 1.f;
Mat_<float> distCoeffs(1, 5);
distCoeffs << 1.2f, 0.2f, 0.f, 0.f, 0.f;
const Size sizes[] = { Size(6, 6), Size(8, 6), Size(11, 12), Size(5, 4) };
const size_t sizes_num = sizeof(sizes)/sizeof(sizes[0]);
const int test_num = 16;
int progress = 0;
for(int i = 0; i < test_num; ++i)
{
SCOPED_TRACE(cv::format("test_num=%d", test_num));
progress = update_progress( progress, i, test_num, 0 );
ChessBoardGenerator cbg(sizes[i % sizes_num]);
vector<Point2f> corners_generated;
Mat cb = cbg(bg, camMat, distCoeffs, corners_generated);
if(!validateData(cbg, cb.size(), corners_generated))
{
ts->printf( cvtest::TS::LOG, "Chess board skipped - too small" );
continue;
}
/*cb = cb * 0.8 + Scalar::all(30);
GaussianBlur(cb, cb, Size(3, 3), 0.8); */
//cv::addWeighted(cb, 0.8, bg, 0.2, 20, cb);
//cv::namedWindow("CB"); cv::imshow("CB", cb); cv::waitKey();
vector<Point2f> corners_found;
int flags = i % 8; // need to check branches for all flags
bool found = findChessboardCornersWrapper(cb, cbg.cornersSize(), corners_found, flags);
if (!found)
{
ts->printf( cvtest::TS::LOG, "Chess board corners not found\n" );
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
res = false;
return res;
}
double err = calcErrorMinError(cbg.cornersSize(), corners_found, corners_generated);
EXPECT_LE(err, rough_success_error_level) << "bad accuracy of corner guesses";
#if 0
if (err >= rough_success_error_level)
{
imshow("cb", cb);
Mat cb_corners = cb.clone();
cv::drawChessboardCorners(cb_corners, cbg.cornersSize(), Mat(corners_found), found);
imshow("corners", cb_corners);
waitKey(0);
}
#endif
}
/* ***** negative ***** */
{
vector<Point2f> corners_found;
bool found = findChessboardCornersWrapper(bg, Size(8, 7), corners_found,0);
if (found)
res = false;
ChessBoardGenerator cbg(Size(8, 7));
vector<Point2f> cg;
Mat cb = cbg(bg, camMat, distCoeffs, cg);
found = findChessboardCornersWrapper(cb, Size(3, 4), corners_found,0);
if (found)
res = false;
Point2f c = std::accumulate(cg.begin(), cg.end(), Point2f(), std::plus<Point2f>()) * (1.f/cg.size());
Mat_<double> aff(2, 3);
aff << 1.0, 0.0, -(double)c.x, 0.0, 1.0, 0.0;
Mat sh;
warpAffine(cb, sh, aff, cb.size());
found = findChessboardCornersWrapper(sh, cbg.cornersSize(), corners_found,0);
if (found)
res = false;
vector< vector<Point> > cnts(1);
vector<Point>& cnt = cnts[0];
cnt.push_back(cg[ 0]); cnt.push_back(cg[0+2]);
cnt.push_back(cg[7+0]); cnt.push_back(cg[7+2]);
cv::drawContours(cb, cnts, -1, Scalar::all(128), FILLED);
found = findChessboardCornersWrapper(cb, cbg.cornersSize(), corners_found,0);
if (found)
res = false;
cv::drawChessboardCorners(cb, cbg.cornersSize(), Mat(corners_found), found);
}
return res;
}
// generates artificial checkerboards using warpPerspective which supports
// subpixel rendering. The transformation is found by transferring corners to
// the camera image using a virtual plane.
bool CV_ChessboardDetectorTest::checkByGeneratorHighAccuracy()
{
// draw 2D pattern
cv::Size pattern_size(6,5);
int cell_size = 80;
bool bwhite = true;
cv::Mat image = cv::Mat::ones((pattern_size.height+3)*cell_size,(pattern_size.width+3)*cell_size,CV_8UC1)*255;
cv::Mat pimage = image(Rect(cell_size,cell_size,(pattern_size.width+1)*cell_size,(pattern_size.height+1)*cell_size));
pimage = 0;
for(int row=0;row<=pattern_size.height;++row)
{
int y = int(cell_size*row+0.5F);
bool bwhite2 = bwhite;
for(int col=0;col<=pattern_size.width;++col)
{
if(bwhite2)
{
int x = int(cell_size*col+0.5F);
pimage(cv::Rect(x,y,cell_size,cell_size)) = 255;
}
bwhite2 = !bwhite2;
}
bwhite = !bwhite;
}
// generate 2d points
std::vector<Point2f> pts1,pts2,pts1_all,pts2_all;
std::vector<Point3f> pts3d;
for(int row=0;row<pattern_size.height;++row)
{
int y = int(cell_size*(row+2));
for(int col=0;col<pattern_size.width;++col)
{
int x = int(cell_size*(col+2));
pts1_all.push_back(cv::Point2f(x-0.5F,y-0.5F));
}
}
// back project chessboard corners to a virtual plane
double fx = 500;
double fy = 500;
cv::Point2f center(250,250);
double fxi = 1.0/fx;
double fyi = 1.0/fy;
for(auto &&pt : pts1_all)
{
// calc camera ray
cv::Vec3f ray(float((pt.x-center.x)*fxi),float((pt.y-center.y)*fyi),1.0F);
ray /= cv::norm(ray);
// intersect ray with virtual plane
cv::Scalar plane(0,0,1,-1);
cv::Vec3f n(float(plane(0)),float(plane(1)),float(plane(2)));
cv::Point3f p0(0,0,0);
cv::Point3f l0(0,0,0); // camera center in world coordinates
p0.z = float(-plane(3)/plane(2));
double val1 = ray.dot(n);
if(val1 == 0)
{
ts->printf( cvtest::TS::LOG, "Internal Error: ray and plane are parallel" );
ts->set_failed_test_info( cvtest::TS::FAIL_GENERIC);
return false;
}
pts3d.push_back(Point3f(ray/val1*cv::Vec3f((p0-l0)).dot(n))+l0);
}
// generate multiple rotations
for(int i=15;i<90;i=i+15)
{
// project 3d points to new camera
Vec3f rvec(0.0F,0.05F,float(float(i)/180.0*CV_PI));
Vec3f tvec(0,0,0);
cv::Mat k = (cv::Mat_<double>(3,3) << fx/2,0,center.x*2, 0,fy/2,center.y, 0,0,1);
cv::projectPoints(pts3d,rvec,tvec,k,cv::Mat(),pts2_all);
// get perspective transform using four correspondences and wrap original image
pts1.clear();
pts2.clear();
pts1.push_back(pts1_all[0]);
pts1.push_back(pts1_all[pattern_size.width-1]);
pts1.push_back(pts1_all[pattern_size.width*pattern_size.height-1]);
pts1.push_back(pts1_all[pattern_size.width*(pattern_size.height-1)]);
pts2.push_back(pts2_all[0]);
pts2.push_back(pts2_all[pattern_size.width-1]);
pts2.push_back(pts2_all[pattern_size.width*pattern_size.height-1]);
pts2.push_back(pts2_all[pattern_size.width*(pattern_size.height-1)]);
Mat m2 = getPerspectiveTransform(pts1,pts2);
Mat out(image.size(),image.type());
warpPerspective(image,out,m2,out.size());
// find checkerboard
vector<Point2f> corners_found;
bool found = findChessboardCornersWrapper(out,pattern_size,corners_found,0);
if (!found)
{
ts->printf( cvtest::TS::LOG, "Chess board corners not found\n" );
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
return false;
}
double err = calcErrorMinError(pattern_size,corners_found,pts2_all);
if(err > 0.08)
{
ts->printf( cvtest::TS::LOG, "bad accuracy of corner guesses" );
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
return false;
}
//cv::cvtColor(out,out,cv::COLOR_GRAY2BGR);
//cv::drawChessboardCorners(out,pattern_size,corners_found,true);
//cv::imshow("img",out);
//cv::waitKey(-1);
}
return true;
}
TEST(Calib3d_ChessboardDetector, accuracy) { CV_ChessboardDetectorTest test( CHESSBOARD ); test.safe_run(); }
TEST(Calib3d_ChessboardDetector2, accuracy) { CV_ChessboardDetectorTest test( CHESSBOARD_SB ); test.safe_run(); }
TEST(Calib3d_CirclesPatternDetector, accuracy) { CV_ChessboardDetectorTest test( CIRCLES_GRID ); test.safe_run(); }
TEST(Calib3d_AsymmetricCirclesPatternDetector, accuracy) { CV_ChessboardDetectorTest test( ASYMMETRIC_CIRCLES_GRID ); test.safe_run(); }
#ifdef HAVE_OPENCV_FLANN
TEST(Calib3d_AsymmetricCirclesPatternDetectorWithClustering, accuracy) { CV_ChessboardDetectorTest test( ASYMMETRIC_CIRCLES_GRID, CALIB_CB_CLUSTERING ); test.safe_run(); }
#endif
TEST(Calib3d_CirclesPatternDetectorWithClustering, accuracy)
{
cv::String dataDir = string(TS::ptr()->get_data_path()) + "cv/cameracalibration/circles/";
cv::Mat expected;
FileStorage fs(dataDir + "circles_corners15.dat", FileStorage::READ);
fs["corners"] >> expected;
fs.release();
cv::Mat image = cv::imread(dataDir + "circles15.png");
std::vector<Point2f> centers;
cv::findCirclesGrid(image, Size(10, 8), centers, CALIB_CB_SYMMETRIC_GRID | CALIB_CB_CLUSTERING);
ASSERT_EQ(expected.total(), centers.size());
double error = calcError(centers, expected);
ASSERT_LE(error, precise_success_error_level);
}
TEST(Calib3d_AsymmetricCirclesPatternDetector, regression_18713)
{
float pts_[][2] = {
{ 166.5, 107 }, { 146, 236 }, { 147, 92 }, { 184, 162 }, { 150, 185.5 },
{ 215, 105 }, { 270.5, 186 }, { 159, 142 }, { 6, 205.5 }, { 32, 148.5 },
{ 126, 163.5 }, { 181, 208.5 }, { 240.5, 62 }, { 84.5, 76.5 }, { 190, 120.5 },
{ 10, 189 }, { 266, 104 }, { 307.5, 207.5 }, { 97, 184 }, { 116.5, 210 },
{ 114, 139 }, { 84.5, 233 }, { 269.5, 139 }, { 136, 126.5 }, { 120, 107.5 },
{ 129.5, 65.5 }, { 212.5, 140.5 }, { 204.5, 60.5 }, { 207.5, 241 }, { 61.5, 94.5 },
{ 186.5, 61.5 }, { 220, 63 }, { 239, 120.5 }, { 212, 186 }, { 284, 87.5 },
{ 62, 114.5 }, { 283, 61.5 }, { 238.5, 88.5 }, { 243, 159 }, { 245, 208 },
{ 298.5, 158.5 }, { 57, 129 }, { 156.5, 63.5 }, { 192, 90.5 }, { 281, 235.5 },
{ 172, 62.5 }, { 291.5, 119.5 }, { 90, 127 }, { 68.5, 166.5 }, { 108.5, 83.5 },
{ 22, 176 }
};
Mat candidates(51, 1, CV_32FC2, (void*)pts_);
Size patternSize(4, 9);
std::vector< Point2f > result;
bool res = false;
// issue reports about hangs
EXPECT_NO_THROW(res = findCirclesGrid(candidates, patternSize, result, CALIB_CB_ASYMMETRIC_GRID, Ptr<FeatureDetector>()/*blobDetector=NULL*/));
EXPECT_FALSE(res);
if (cvtest::debugLevel > 0)
{
std::cout << Mat(candidates) << std::endl;
std::cout << Mat(result) << std::endl;
Mat img(Size(400, 300), CV_8UC3, Scalar::all(0));
std::vector< Point2f > centers;
candidates.copyTo(centers);
for (size_t i = 0; i < centers.size(); i++)
{
const Point2f& pt = centers[i];
//printf("{ %g, %g }, \n", pt.x, pt.y);
circle(img, pt, 5, Scalar(0, 255, 0));
}
for (size_t i = 0; i < result.size(); i++)
{
const Point2f& pt = result[i];
circle(img, pt, 10, Scalar(0, 0, 255));
}
imwrite("test_18713.png", img);
if (cvtest::debugLevel >= 10)
{
imshow("result", img);
waitKey();
}
}
}
TEST(Calib3d_AsymmetricCirclesPatternDetector, regression_19498)
{
float pts_[121][2] = {
{ 84.7462f, 404.504f }, { 49.1586f, 404.092f }, { 12.3362f, 403.434f }, { 102.542f, 386.214f }, { 67.6042f, 385.475f },
{ 31.4982f, 384.569f }, { 141.231f, 377.856f }, { 332.834f, 370.745f }, { 85.7663f, 367.261f }, { 50.346f, 366.051f },
{ 13.7726f, 364.663f }, { 371.746f, 362.011f }, { 68.8543f, 347.883f }, { 32.9334f, 346.263f }, { 331.926f, 343.291f },
{ 351.535f, 338.112f }, { 51.7951f, 328.247f }, { 15.4613f, 326.095f }, { 311.719f, 319.578f }, { 330.947f, 313.708f },
{ 256.706f, 307.584f }, { 34.6834f, 308.167f }, { 291.085f, 295.429f }, { 17.4316f, 287.824f }, { 252.928f, 277.92f },
{ 270.19f, 270.93f }, { 288.473f, 263.484f }, { 216.401f, 260.94f }, { 232.195f, 253.656f }, { 266.757f, 237.708f },
{ 211.323f, 229.005f }, { 227.592f, 220.498f }, { 154.749f, 188.52f }, { 222.52f, 184.906f }, { 133.85f, 163.968f },
{ 200.024f, 158.05f }, { 147.485f, 153.643f }, { 161.967f, 142.633f }, { 177.396f, 131.059f }, { 125.909f, 128.116f },
{ 139.817f, 116.333f }, { 91.8639f, 114.454f }, { 104.343f, 102.542f }, { 117.635f, 89.9116f }, { 70.9465f, 89.4619f },
{ 82.8524f, 76.7862f }, { 131.738f, 76.4741f }, { 95.5012f, 63.3351f }, { 109.034f, 49.0424f }, { 314.886f, 374.711f },
{ 351.735f, 366.489f }, { 279.113f, 357.05f }, { 313.371f, 348.131f }, { 260.123f, 335.271f }, { 276.346f, 330.325f },
{ 293.588f, 325.133f }, { 240.86f, 313.143f }, { 273.436f, 301.667f }, { 206.762f, 296.574f }, { 309.877f, 288.796f },
{ 187.46f, 274.319f }, { 201.521f, 267.804f }, { 248.973f, 245.918f }, { 181.644f, 244.655f }, { 196.025f, 237.045f },
{ 148.41f, 229.131f }, { 161.604f, 221.215f }, { 175.455f, 212.873f }, { 244.748f, 211.459f }, { 128.661f, 206.109f },
{ 190.217f, 204.108f }, { 141.346f, 197.568f }, { 205.876f, 194.781f }, { 168.937f, 178.948f }, { 121.006f, 173.714f },
{ 183.998f, 168.806f }, { 88.9095f, 159.731f }, { 100.559f, 149.867f }, { 58.553f, 146.47f }, { 112.849f, 139.302f },
{ 80.0968f, 125.74f }, { 39.24f, 123.671f }, { 154.582f, 103.85f }, { 59.7699f, 101.49f }, { 266.334f, 385.387f },
{ 234.053f, 368.718f }, { 263.347f, 361.184f }, { 244.763f, 339.958f }, { 198.16f, 328.214f }, { 211.675f, 323.407f },
{ 225.905f, 318.426f }, { 192.98f, 302.119f }, { 221.267f, 290.693f }, { 161.437f, 286.46f }, { 236.656f, 284.476f },
{ 168.023f, 251.799f }, { 105.385f, 221.988f }, { 116.724f, 214.25f }, { 97.2959f, 191.81f }, { 108.89f, 183.05f },
{ 77.9896f, 169.242f }, { 48.6763f, 156.088f }, { 68.9635f, 136.415f }, { 29.8484f, 133.886f }, { 49.1966f, 112.826f },
{ 113.059f, 29.003f }, { 251.698f, 388.562f }, { 281.689f, 381.929f }, { 297.875f, 378.518f }, { 248.376f, 365.025f },
{ 295.791f, 352.763f }, { 216.176f, 348.586f }, { 230.143f, 344.443f }, { 179.89f, 307.457f }, { 174.083f, 280.51f },
{ 142.867f, 265.085f }, { 155.127f, 258.692f }, { 124.187f, 243.661f }, { 136.01f, 236.553f }, { 86.4651f, 200.13f },
{ 67.5711f, 178.221f }
};
Mat candidates(121, 1, CV_32FC2, (void*)pts_);
Size patternSize(13, 8);
std::vector< Point2f > result;
bool res = false;
EXPECT_NO_THROW(res = findCirclesGrid(candidates, patternSize, result, CALIB_CB_SYMMETRIC_GRID, Ptr<FeatureDetector>()/*blobDetector=NULL*/));
EXPECT_FALSE(res);
}
}} // namespace
/* End of file. */

View File

@ -0,0 +1,116 @@
/*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 "test_chessboardgenerator.hpp"
#include "opencv2/calib3d/calib3d_c.h"
namespace opencv_test { namespace {
class CV_ChessboardDetectorBadArgTest : public cvtest::BadArgTest
{
public:
CV_ChessboardDetectorBadArgTest() { flags0 = 0; }
protected:
void run(int);
bool checkByGenerator();
Mat img;
Size pattern_size, pattern_size0;
int flags, flags0;
vector<Point2f> corners;
_InputArray img_arg;
_OutputArray corners_arg;
void initArgs()
{
img_arg = img;
corners_arg = corners;
pattern_size = pattern_size0;
flags = flags0;
}
void run_func()
{
findChessboardCorners(img_arg, pattern_size, corners_arg, flags);
}
};
/* ///////////////////// chess_corner_test ///////////////////////// */
void CV_ChessboardDetectorBadArgTest::run( int /*start_from */)
{
Mat bg(800, 600, CV_8U, Scalar(0));
Mat_<float> camMat(3, 3);
camMat << 300.f, 0.f, bg.cols/2.f, 0, 300.f, bg.rows/2.f, 0.f, 0.f, 1.f;
Mat_<float> distCoeffs(1, 5);
distCoeffs << 1.2f, 0.2f, 0.f, 0.f, 0.f;
ChessBoardGenerator cbg(Size(8,6));
vector<Point2f> exp_corn;
Mat cb = cbg(bg, camMat, distCoeffs, exp_corn);
/* /*//*/ */
int errors = 0;
flags = CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_NORMALIZE_IMAGE;
img = cb.clone();
initArgs();
pattern_size = Size(2,2);
errors += run_test_case( Error::StsOutOfRange, "Invalid pattern size" );
pattern_size = cbg.cornersSize();
cb.convertTo(img, CV_32F);
errors += run_test_case( Error::StsUnsupportedFormat, "Not 8-bit image" );
cv::merge(vector<Mat>(2, cb), img);
errors += run_test_case( Error::StsUnsupportedFormat, "2 channel image" );
if (errors)
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
else
ts->set_failed_test_info(cvtest::TS::OK);
}
TEST(Calib3d_ChessboardDetector, badarg) { CV_ChessboardDetectorBadArgTest test; test.safe_run(); }
}} // namespace
/* End of file. */

View File

@ -0,0 +1,161 @@
/*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/imgproc.hpp"
#include "opencv2/calib3d.hpp"
namespace opencv_test { namespace {
class CV_ChessboardDetectorTimingTest : public cvtest::BaseTest
{
public:
CV_ChessboardDetectorTimingTest();
protected:
void run(int);
};
CV_ChessboardDetectorTimingTest::CV_ChessboardDetectorTimingTest()
{
}
/* ///////////////////// chess_corner_test ///////////////////////// */
void CV_ChessboardDetectorTimingTest::run( int start_from )
{
int code = cvtest::TS::OK;
/* test parameters */
std::string filepath;
std::string filename;
std::vector<Point2f> v;
Mat img, gray, thresh;
int idx, max_idx;
int progress = 0;
filepath = cv::format("%scv/cameracalibration/", ts->get_data_path().c_str() );
filename = cv::format("%schessboard_timing_list.dat", filepath.c_str() );
cv::FileStorage fs( filename, FileStorage::READ );
cv::FileNode board_list = fs["boards"];
cv::FileNodeIterator bl_it = board_list.begin();
if( !fs.isOpened() || !board_list.isSeq() || board_list.size() % 4 != 0 )
{
ts->printf( cvtest::TS::LOG, "chessboard_timing_list.dat can not be read or is not valid" );
code = cvtest::TS::FAIL_MISSING_TEST_DATA;
goto _exit_;
}
max_idx = (int)(board_list.size()/4);
for( idx = 0; idx < start_from; idx++ )
{
bl_it += 4;
}
for( idx = start_from; idx < max_idx; idx++ )
{
Size pattern_size;
std::string imgname; read(*bl_it++, imgname, "dummy.txt");
int is_chessboard = 0;
read(*bl_it++, is_chessboard, 0);
read(*bl_it++, pattern_size.width, -1);
read(*bl_it++, pattern_size.height, -1);
ts->update_context( this, idx-1, true );
/* read the image */
filename = cv::format("%s%s", filepath.c_str(), imgname.c_str() );
img = cv::imread( filename );
if( img.empty() )
{
ts->printf( cvtest::TS::LOG, "one of chessboard images can't be read: %s\n", filename.c_str() );
code = cvtest::TS::FAIL_MISSING_TEST_DATA;
continue;
}
ts->printf(cvtest::TS::LOG, "%s: chessboard %d:\n", imgname.c_str(), is_chessboard);
cvtColor(img, gray, COLOR_BGR2GRAY);
int64 _time0 = cv::getTickCount();
bool result = cv::checkChessboard(gray, pattern_size);
int64 _time01 = cv::getTickCount();
bool result1 = findChessboardCorners(gray, pattern_size, v, 15);
int64 _time1 = cv::getTickCount();
if( result != (is_chessboard != 0))
{
ts->printf( cvtest::TS::LOG, "Error: chessboard was %sdetected in the image %s\n",
result ? "" : "not ", imgname.c_str() );
code = cvtest::TS::FAIL_INVALID_OUTPUT;
goto _exit_;
}
if(result != result1)
{
ts->printf( cvtest::TS::LOG, "Warning: results differ cvCheckChessboard %d, cvFindChessboardCorners %d\n",
(int)result, (int)result1);
}
int num_pixels = gray.cols*gray.rows;
float check_chessboard_time = float(_time01 - _time0)/(float)cv::getTickFrequency(); // in us
ts->printf(cvtest::TS::LOG, " cvCheckChessboard time s: %f, us per pixel: %f\n",
check_chessboard_time*1e-6, check_chessboard_time/num_pixels);
float find_chessboard_time = float(_time1 - _time01)/(float)cv::getTickFrequency();
ts->printf(cvtest::TS::LOG, " cvFindChessboard time s: %f, us per pixel: %f\n",
find_chessboard_time*1e-6, find_chessboard_time/num_pixels);
progress = update_progress( progress, idx-1, max_idx, 0 );
}
_exit_:
if( code < 0 )
ts->set_failed_test_info( code );
}
TEST(Calib3d_ChessboardDetector, timing) { CV_ChessboardDetectorTimingTest test; test.safe_run(); }
}} // namespace
/* End of file. */

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

View File

@ -0,0 +1,259 @@
/*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/imgproc/imgproc_c.h"
#include "test_chessboardgenerator.hpp"
namespace opencv_test { namespace {
class CV_ChessboardSubpixelTest : public cvtest::BaseTest
{
public:
CV_ChessboardSubpixelTest();
protected:
Mat intrinsic_matrix_;
Mat distortion_coeffs_;
Size image_size_;
void run(int);
void generateIntrinsicParams();
};
int calcDistance(const vector<Point2f>& set1, const vector<Point2f>& set2, double& mean_dist)
{
if(set1.size() != set2.size())
{
return 0;
}
std::vector<int> indices;
double sum_dist = 0.0;
for(size_t i = 0; i < set1.size(); i++)
{
double min_dist = std::numeric_limits<double>::max();
int min_idx = -1;
for(int j = 0; j < (int)set2.size(); j++)
{
double dist = cv::norm(set1[i] - set2[j]); // TODO cvtest
if(dist < min_dist)
{
min_idx = j;
min_dist = dist;
}
}
// check validity of min_idx
if(min_idx == -1)
{
return 0;
}
std::vector<int>::iterator it = std::find(indices.begin(), indices.end(), min_idx);
if(it != indices.end())
{
// there are two points in set1 corresponding to the same point in set2
return 0;
}
indices.push_back(min_idx);
// printf("dist %d = %f\n", (int)i, min_dist);
sum_dist += min_dist*min_dist;
}
mean_dist = sqrt(sum_dist/set1.size());
// printf("sum_dist = %f, set1.size() = %d, mean_dist = %f\n", sum_dist, (int)set1.size(), mean_dist);
return 1;
}
CV_ChessboardSubpixelTest::CV_ChessboardSubpixelTest() :
intrinsic_matrix_(Size(3, 3), CV_64FC1), distortion_coeffs_(Size(1, 4), CV_64FC1),
image_size_(640, 480)
{
}
/* ///////////////////// chess_corner_test ///////////////////////// */
void CV_ChessboardSubpixelTest::run( int )
{
int code = cvtest::TS::OK;
int progress = 0;
RNG& rng = ts->get_rng();
const int runs_count = 20;
const int max_pattern_size = 8;
const int min_pattern_size = 5;
Mat bg(image_size_, CV_8UC1);
bg = Scalar(0);
double sum_dist = 0.0;
int count = 0;
for(int i = 0; i < runs_count; i++)
{
const int pattern_width = min_pattern_size + cvtest::randInt(rng) % (max_pattern_size - min_pattern_size);
const int pattern_height = min_pattern_size + cvtest::randInt(rng) % (max_pattern_size - min_pattern_size);
Size pattern_size;
if(pattern_width > pattern_height)
{
pattern_size = Size(pattern_height, pattern_width);
}
else
{
pattern_size = Size(pattern_width, pattern_height);
}
ChessBoardGenerator gen_chessboard(Size(pattern_size.width + 1, pattern_size.height + 1));
// generates intrinsic camera and distortion matrices
generateIntrinsicParams();
vector<Point2f> corners;
Mat chessboard_image = gen_chessboard(bg, intrinsic_matrix_, distortion_coeffs_, corners);
vector<Point2f> test_corners;
bool result = findChessboardCorners(chessboard_image, pattern_size, test_corners, 15);
if (!result && cvtest::debugLevel > 0)
{
ts->printf(cvtest::TS::LOG, "Warning: chessboard was not detected! Writing image to test.png\n");
ts->printf(cvtest::TS::LOG, "Size = %d, %d\n", pattern_size.width, pattern_size.height);
ts->printf(cvtest::TS::LOG, "Intrinsic params: fx = %f, fy = %f, cx = %f, cy = %f\n",
intrinsic_matrix_.at<double>(0, 0), intrinsic_matrix_.at<double>(1, 1),
intrinsic_matrix_.at<double>(0, 2), intrinsic_matrix_.at<double>(1, 2));
ts->printf(cvtest::TS::LOG, "Distortion matrix: %f, %f, %f, %f, %f\n",
distortion_coeffs_.at<double>(0, 0), distortion_coeffs_.at<double>(0, 1),
distortion_coeffs_.at<double>(0, 2), distortion_coeffs_.at<double>(0, 3),
distortion_coeffs_.at<double>(0, 4));
imwrite("test.png", chessboard_image);
}
if (!result)
{
continue;
}
double dist1 = 0.0;
int ret = calcDistance(corners, test_corners, dist1);
if(ret == 0)
{
ts->printf(cvtest::TS::LOG, "findChessboardCorners returns invalid corner coordinates!\n");
code = cvtest::TS::FAIL_INVALID_OUTPUT;
break;
}
IplImage chessboard_image_header = cvIplImage(chessboard_image);
cvFindCornerSubPix(&chessboard_image_header, (CvPoint2D32f*)&test_corners[0],
(int)test_corners.size(), cvSize(3, 3), cvSize(1, 1), cvTermCriteria(CV_TERMCRIT_EPS|CV_TERMCRIT_ITER,300,0.1));
find4QuadCornerSubpix(chessboard_image, test_corners, Size(5, 5));
double dist2 = 0.0;
ret = calcDistance(corners, test_corners, dist2);
if(ret == 0)
{
ts->printf(cvtest::TS::LOG, "findCornerSubpix returns invalid corner coordinates!\n");
code = cvtest::TS::FAIL_INVALID_OUTPUT;
break;
}
ts->printf(cvtest::TS::LOG, "Error after findChessboardCorners: %f, after findCornerSubPix: %f\n",
dist1, dist2);
sum_dist += dist2;
count++;
const double max_reduce_factor = 0.8;
if(dist1 < dist2*max_reduce_factor)
{
ts->printf(cvtest::TS::LOG, "findCornerSubPix increases average error!\n");
code = cvtest::TS::FAIL_INVALID_OUTPUT;
break;
}
progress = update_progress( progress, i-1, runs_count, 0 );
}
ASSERT_NE(0, count);
sum_dist /= count;
ts->printf(cvtest::TS::LOG, "Average error after findCornerSubpix: %f\n", sum_dist);
if( code < 0 )
ts->set_failed_test_info( code );
}
void CV_ChessboardSubpixelTest::generateIntrinsicParams()
{
RNG& rng = ts->get_rng();
const double max_focus_length = 1000.0;
const double max_focus_diff = 5.0;
double fx = cvtest::randReal(rng)*max_focus_length;
double fy = fx + cvtest::randReal(rng)*max_focus_diff;
double cx = image_size_.width/2;
double cy = image_size_.height/2;
double k1 = 0.5*cvtest::randReal(rng);
double k2 = 0.05*cvtest::randReal(rng);
double p1 = 0.05*cvtest::randReal(rng);
double p2 = 0.05*cvtest::randReal(rng);
double k3 = 0.0;
intrinsic_matrix_ = (Mat_<double>(3, 3) << fx, 0.0, cx, 0.0, fy, cy, 0.0, 0.0, 1.0);
distortion_coeffs_ = (Mat_<double>(1, 5) << k1, k2, p1, p2, k3);
}
TEST(Calib3d_ChessboardSubPixDetector, accuracy) { CV_ChessboardSubpixelTest test; test.safe_run(); }
TEST(Calib3d_CornerSubPix, regression_7204)
{
cv::Mat image(cv::Size(70, 38), CV_8UC1, cv::Scalar::all(0));
image(cv::Rect(65, 26, 5, 5)).setTo(cv::Scalar::all(255));
image(cv::Rect(55, 31, 8, 1)).setTo(cv::Scalar::all(255));
image(cv::Rect(56, 35, 14, 2)).setTo(cv::Scalar::all(255));
image(cv::Rect(66, 24, 4, 2)).setTo(cv::Scalar::all(255));
image.at<uchar>(24, 69) = 0;
std::vector<cv::Point2f> corners;
corners.push_back(cv::Point2f(65, 30));
cv::cornerSubPix(image, corners, cv::Size(3, 3), cv::Size(-1, -1),
cv::TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
}
}} // namespace
/* End of file. */

View File

@ -0,0 +1,144 @@
/*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();
}
}} // namespace

View File

@ -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/calib3d.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_8U, 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(); }
}}

View File

@ -0,0 +1,910 @@
/*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-2011, 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/ts/cuda_test.hpp> // EXPECT_MAT_NEAR
#include "../src/fisheye.hpp"
#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;
const static cv::Matx33d R;
const static cv::Vec3d T;
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);
static void merge4(const cv::Mat& tl, const cv::Mat& tr, const cv::Mat& bl, const cv::Mat& br, cv::Mat& merged);
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// TESTS::
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, undistortImage)
{
cv::Matx33d theK = this->K;
cv::Mat theD = cv::Mat(this->D);
std::string file = combine(datasets_repository_path, "/calib-3_stereo_from_JY/left/stereo_pair_014.jpg");
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);
cv::Mat correct = cv::imread(combine(datasets_repository_path, "new_f_100.png"));
if (correct.empty())
CV_Assert(cv::imwrite(combine(datasets_repository_path, "new_f_100.png"), undistorted));
else
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);
cv::Mat correct = cv::imread(combine(datasets_repository_path, "balance_1.0.png"));
if (correct.empty())
CV_Assert(cv::imwrite(combine(datasets_repository_path, "balance_1.0.png"), undistorted));
else
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);
cv::Mat correct = cv::imread(combine(datasets_repository_path, "balance_0.0.png"));
if (correct.empty())
CV_Assert(cv::imwrite(combine(datasets_repository_path, "balance_0.0.png"), undistorted));
else
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, k = 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];
}
k++;
}
}
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);
CV_Assert(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);
}
TEST_F(fisheyeTest, Calibration)
{
const int n_images = 34;
std::vector<std::vector<cv::Point2d> > imagePoints(n_images);
std::vector<std::vector<cv::Point3d> > objectPoints(n_images);
const std::string folder = combine(datasets_repository_path, "calib-3_stereo_from_JY");
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ);
CV_Assert(fs_left.isOpened());
for(int i = 0; i < n_images; ++i)
fs_left[cv::format("image_%d", i )] >> imagePoints[i];
fs_left.release();
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ);
CV_Assert(fs_object.isOpened());
for(int i = 0; i < n_images; ++i)
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object.release();
int flag = 0;
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
flag |= cv::fisheye::CALIB_CHECK_COND;
flag |= cv::fisheye::CALIB_FIX_SKEW;
cv::Matx33d theK;
cv::Vec4d theD;
cv::fisheye::calibrate(objectPoints, imagePoints, imageSize, theK, theD,
cv::noArray(), cv::noArray(), flag, cv::TermCriteria(3, 20, 1e-6));
EXPECT_MAT_NEAR(theK, this->K, 1e-10);
EXPECT_MAT_NEAR(theD, this->D, 1e-10);
}
TEST_F(fisheyeTest, CalibrationWithFixedFocalLength)
{
const int n_images = 34;
std::vector<std::vector<cv::Point2d> > imagePoints(n_images);
std::vector<std::vector<cv::Point3d> > objectPoints(n_images);
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY");
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ);
CV_Assert(fs_left.isOpened());
for(int i = 0; i < n_images; ++i)
fs_left[cv::format("image_%d", i )] >> imagePoints[i];
fs_left.release();
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ);
CV_Assert(fs_object.isOpened());
for(int i = 0; i < n_images; ++i)
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object.release();
int flag = 0;
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
flag |= cv::fisheye::CALIB_CHECK_COND;
flag |= cv::fisheye::CALIB_FIX_SKEW;
flag |= cv::fisheye::CALIB_FIX_FOCAL_LENGTH;
flag |= cv::fisheye::CALIB_USE_INTRINSIC_GUESS;
cv::Matx33d theK = this->K;
const cv::Matx33d newK(
558.478088, 0.000000, 620.458461,
0.000000, 560.506767, 381.939362,
0.000000, 0.000000, 1.000000);
cv::Vec4d theD;
const cv::Vec4d newD(-0.001461, -0.003298, 0.006057, -0.003742);
cv::fisheye::calibrate(objectPoints, imagePoints, imageSize, theK, theD,
cv::noArray(), cv::noArray(), flag, cv::TermCriteria(3, 20, 1e-6));
// ensure that CALIB_FIX_FOCAL_LENGTH works and focal lenght has not changed
EXPECT_EQ(theK(0,0), K(0,0));
EXPECT_EQ(theK(1,1), K(1,1));
EXPECT_MAT_NEAR(theK, newK, 1e-6);
EXPECT_MAT_NEAR(theD, newD, 1e-6);
}
TEST_F(fisheyeTest, Homography)
{
const int n_images = 1;
std::vector<std::vector<cv::Point2d> > imagePoints(n_images);
std::vector<std::vector<cv::Point3d> > objectPoints(n_images);
const std::string folder = combine(datasets_repository_path, "calib-3_stereo_from_JY");
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ);
CV_Assert(fs_left.isOpened());
for(int i = 0; i < n_images; ++i)
fs_left[cv::format("image_%d", i )] >> imagePoints[i];
fs_left.release();
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ);
CV_Assert(fs_object.isOpened());
for(int i = 0; i < n_images; ++i)
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object.release();
cv::internal::IntrinsicParams param;
param.Init(cv::Vec2d(cv::max(imageSize.width, imageSize.height) / CV_PI, cv::max(imageSize.width, imageSize.height) / CV_PI),
cv::Vec2d(imageSize.width / 2.0 - 0.5, imageSize.height / 2.0 - 0.5));
cv::Mat _imagePoints (imagePoints[0]);
cv::Mat _objectPoints(objectPoints[0]);
cv::Mat imagePointsNormalized = NormalizePixels(_imagePoints, param).reshape(1).t();
_objectPoints = _objectPoints.reshape(1).t();
cv::Mat objectPointsMean, covObjectPoints;
int Np = imagePointsNormalized.cols;
cv::calcCovarMatrix(_objectPoints, covObjectPoints, objectPointsMean, cv::COVAR_NORMAL | cv::COVAR_COLS);
cv::SVD svd(covObjectPoints);
cv::Mat theR(svd.vt);
if (cv::norm(theR(cv::Rect(2, 0, 1, 2))) < 1e-6)
theR = cv::Mat::eye(3,3, CV_64FC1);
if (cv::determinant(theR) < 0)
theR = -theR;
cv::Mat theT = -theR * objectPointsMean;
cv::Mat X_new = theR * _objectPoints + theT * cv::Mat::ones(1, Np, CV_64FC1);
cv::Mat H = cv::internal::ComputeHomography(imagePointsNormalized, X_new.rowRange(0, 2));
cv::Mat M = cv::Mat::ones(3, X_new.cols, CV_64FC1);
X_new.rowRange(0, 2).copyTo(M.rowRange(0, 2));
cv::Mat mrep = H * M;
cv::divide(mrep, cv::Mat::ones(3,1, CV_64FC1) * mrep.row(2).clone(), mrep);
cv::Mat merr = (mrep.rowRange(0, 2) - imagePointsNormalized).t();
cv::Vec2d std_err;
cv::meanStdDev(merr.reshape(2), cv::noArray(), std_err);
std_err *= sqrt((double)merr.reshape(2).total() / (merr.reshape(2).total() - 1));
cv::Vec2d correct_std_err(0.00516740156010384, 0.00644205331553901);
EXPECT_MAT_NEAR(std_err, correct_std_err, 1e-12);
}
TEST_F(fisheyeTest, EstimateUncertainties)
{
const int n_images = 34;
std::vector<std::vector<cv::Point2d> > imagePoints(n_images);
std::vector<std::vector<cv::Point3d> > objectPoints(n_images);
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY");
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ);
CV_Assert(fs_left.isOpened());
for(int i = 0; i < n_images; ++i)
fs_left[cv::format("image_%d", i )] >> imagePoints[i];
fs_left.release();
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ);
CV_Assert(fs_object.isOpened());
for(int i = 0; i < n_images; ++i)
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object.release();
int flag = 0;
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
flag |= cv::fisheye::CALIB_CHECK_COND;
flag |= cv::fisheye::CALIB_FIX_SKEW;
cv::Matx33d theK;
cv::Vec4d theD;
std::vector<cv::Vec3d> rvec;
std::vector<cv::Vec3d> tvec;
cv::fisheye::calibrate(objectPoints, imagePoints, imageSize, theK, theD,
rvec, tvec, flag, cv::TermCriteria(3, 20, 1e-6));
cv::internal::IntrinsicParams param, errors;
cv::Vec2d err_std;
double thresh_cond = 1e6;
int check_cond = 1;
param.Init(cv::Vec2d(theK(0,0), theK(1,1)), cv::Vec2d(theK(0,2), theK(1, 2)), theD);
param.isEstimate = std::vector<uchar>(9, 1);
param.isEstimate[4] = 0;
errors.isEstimate = param.isEstimate;
double rms;
cv::internal::EstimateUncertainties(objectPoints, imagePoints, param, rvec, tvec,
errors, err_std, thresh_cond, check_cond, rms);
EXPECT_MAT_NEAR(errors.f, cv::Vec2d(1.29837104202046, 1.31565641071524), 1e-10);
EXPECT_MAT_NEAR(errors.c, cv::Vec2d(0.890439368129246, 0.816096854937896), 1e-10);
EXPECT_MAT_NEAR(errors.k, cv::Vec4d(0.00516248605191506, 0.0168181467500934, 0.0213118690274604, 0.00916010877545648), 1e-10);
EXPECT_MAT_NEAR(err_std, cv::Vec2d(0.187475975266883, 0.185678953263995), 1e-10);
CV_Assert(fabs(rms - 0.263782587133546) < 1e-10);
CV_Assert(errors.alpha == 0);
}
TEST_F(fisheyeTest, stereoRectify)
{
// For consistency purposes
CV_StaticAssert(
static_cast<int>(cv::CALIB_ZERO_DISPARITY) == static_cast<int>(cv::fisheye::CALIB_ZERO_DISPARITY),
"For the purpose of continuity the following should be true: cv::CALIB_ZERO_DISPARITY == cv::fisheye::CALIB_ZERO_DISPARITY"
);
const std::string folder = combine(datasets_repository_path, "calib-3_stereo_from_JY");
cv::Size calibration_size = this->imageSize, requested_size = calibration_size;
cv::Matx33d K1 = this->K, K2 = K1;
cv::Mat D1 = cv::Mat(this->D), D2 = D1;
cv::Vec3d theT = this->T;
cv::Matx33d theR = this->R;
double balance = 0.0, fov_scale = 1.1;
cv::Mat R1, R2, P1, P2, Q;
cv::fisheye::stereoRectify(K1, D1, K2, D2, calibration_size, theR, theT, R1, R2, P1, P2, Q,
cv::fisheye::CALIB_ZERO_DISPARITY, requested_size, balance, fov_scale);
// Collected with these CMake flags: -DWITH_IPP=OFF -DCV_ENABLE_INTRINSICS=OFF -DCV_DISABLE_OPTIMIZATION=ON -DCMAKE_BUILD_TYPE=Debug
cv::Matx33d R1_ref(
0.9992853269091279, 0.03779164101000276, -0.0007920188690205426,
-0.03778569762983931, 0.9992646472015868, 0.006511981857667881,
0.001037534936357442, -0.006477400933964018, 0.9999784831677112
);
cv::Matx33d R2_ref(
0.9994868963898833, -0.03197579751378937, -0.001868774538573449,
0.03196298186616116, 0.9994677442608699, -0.0065265589947392,
0.002076471801477729, 0.006463478587068991, 0.9999769555891836
);
cv::Matx34d P1_ref(
420.8551870450913, 0, 586.501617798451, 0,
0, 420.8551870450913, 374.7667511986098, 0,
0, 0, 1, 0
);
cv::Matx34d P2_ref(
420.8551870450913, 0, 586.501617798451, -41.77758076597302,
0, 420.8551870450913, 374.7667511986098, 0,
0, 0, 1, 0
);
cv::Matx44d Q_ref(
1, 0, 0, -586.501617798451,
0, 1, 0, -374.7667511986098,
0, 0, 0, 420.8551870450913,
0, 0, 10.07370889670733, -0
);
const double eps = 1e-10;
EXPECT_MAT_NEAR(R1_ref, R1, eps);
EXPECT_MAT_NEAR(R2_ref, R2, eps);
EXPECT_MAT_NEAR(P1_ref, P1, eps);
EXPECT_MAT_NEAR(P2_ref, P2, eps);
EXPECT_MAT_NEAR(Q_ref, Q, eps);
if (::testing::Test::HasFailure())
{
std::cout << "Actual values are:" << std::endl
<< "R1 =" << std::endl << R1 << std::endl
<< "R2 =" << std::endl << R2 << std::endl
<< "P1 =" << std::endl << P1 << std::endl
<< "P2 =" << std::endl << P2 << std::endl
<< "Q =" << std::endl << Q << std::endl;
}
if (cvtest::debugLevel == 0)
return;
// DEBUG code is below
cv::Mat lmapx, lmapy, rmapx, rmapy;
//rewrite for fisheye
cv::fisheye::initUndistortRectifyMap(K1, D1, R1, P1, requested_size, CV_32F, lmapx, lmapy);
cv::fisheye::initUndistortRectifyMap(K2, D2, R2, P2, requested_size, CV_32F, rmapx, rmapy);
cv::Mat l, r, lundist, rundist;
for (int i = 0; i < 34; ++i)
{
SCOPED_TRACE(cv::format("image %d", i));
l = imread(combine(folder, cv::format("left/stereo_pair_%03d.jpg", i)), cv::IMREAD_COLOR);
r = imread(combine(folder, cv::format("right/stereo_pair_%03d.jpg", i)), cv::IMREAD_COLOR);
ASSERT_FALSE(l.empty());
ASSERT_FALSE(r.empty());
int ndisp = 128;
cv::rectangle(l, cv::Rect(255, 0, 829, l.rows-1), cv::Scalar(0, 0, 255));
cv::rectangle(r, cv::Rect(255, 0, 829, l.rows-1), cv::Scalar(0, 0, 255));
cv::rectangle(r, cv::Rect(255-ndisp, 0, 829+ndisp ,l.rows-1), cv::Scalar(0, 0, 255));
cv::remap(l, lundist, lmapx, lmapy, cv::INTER_LINEAR);
cv::remap(r, rundist, rmapx, rmapy, cv::INTER_LINEAR);
for (int ii = 0; ii < lundist.rows; ii += 20)
{
cv::line(lundist, cv::Point(0, ii), cv::Point(lundist.cols, ii), cv::Scalar(0, 255, 0));
cv::line(rundist, cv::Point(0, ii), cv::Point(lundist.cols, ii), cv::Scalar(0, 255, 0));
}
cv::Mat rectification;
merge4(l, r, lundist, rundist, rectification);
cv::imwrite(cv::format("fisheye_rectification_AB_%03d.png", i), rectification);
}
}
TEST_F(fisheyeTest, stereoCalibrate)
{
const int n_images = 34;
const std::string folder = combine(datasets_repository_path, "calib-3_stereo_from_JY");
std::vector<std::vector<cv::Point2d> > leftPoints(n_images);
std::vector<std::vector<cv::Point2d> > rightPoints(n_images);
std::vector<std::vector<cv::Point3d> > objectPoints(n_images);
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ);
CV_Assert(fs_left.isOpened());
for(int i = 0; i < n_images; ++i)
fs_left[cv::format("image_%d", i )] >> leftPoints[i];
fs_left.release();
cv::FileStorage fs_right(combine(folder, "right.xml"), cv::FileStorage::READ);
CV_Assert(fs_right.isOpened());
for(int i = 0; i < n_images; ++i)
fs_right[cv::format("image_%d", i )] >> rightPoints[i];
fs_right.release();
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ);
CV_Assert(fs_object.isOpened());
for(int i = 0; i < n_images; ++i)
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object.release();
cv::Matx33d K1, K2, theR;
cv::Vec3d theT;
cv::Vec4d D1, D2;
int flag = 0;
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
flag |= cv::fisheye::CALIB_CHECK_COND;
flag |= cv::fisheye::CALIB_FIX_SKEW;
cv::fisheye::stereoCalibrate(objectPoints, leftPoints, rightPoints,
K1, D1, K2, D2, imageSize, theR, theT, flag,
cv::TermCriteria(3, 12, 0));
cv::Matx33d R_correct( 0.9975587205950972, 0.06953016383322372, 0.006492709911733523,
-0.06956823121068059, 0.9975601387249519, 0.005833595226966235,
-0.006071257768382089, -0.006271040135405457, 0.9999619062167968);
cv::Vec3d T_correct(-0.099402724724121, 0.00270812139265413, 0.00129330292472699);
cv::Matx33d K1_correct (561.195925927249, 0, 621.282400272412,
0, 562.849402029712, 380.555455380889,
0, 0, 1);
cv::Matx33d K2_correct (560.395452535348, 0, 678.971652040359,
0, 561.90171021422, 380.401340535339,
0, 0, 1);
cv::Vec4d D1_correct (-7.44253716539556e-05, -0.00702662033932424, 0.00737569823650885, -0.00342230256441771);
cv::Vec4d D2_correct (-0.0130785435677431, 0.0284434505383497, -0.0360333869900506, 0.0144724062347222);
EXPECT_MAT_NEAR(theR, R_correct, 1e-10);
EXPECT_MAT_NEAR(theT, T_correct, 1e-10);
EXPECT_MAT_NEAR(K1, K1_correct, 1e-10);
EXPECT_MAT_NEAR(K2, K2_correct, 1e-10);
EXPECT_MAT_NEAR(D1, D1_correct, 1e-10);
EXPECT_MAT_NEAR(D2, D2_correct, 1e-10);
}
TEST_F(fisheyeTest, stereoCalibrateFixIntrinsic)
{
const int n_images = 34;
const std::string folder = combine(datasets_repository_path, "calib-3_stereo_from_JY");
std::vector<std::vector<cv::Point2d> > leftPoints(n_images);
std::vector<std::vector<cv::Point2d> > rightPoints(n_images);
std::vector<std::vector<cv::Point3d> > objectPoints(n_images);
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ);
CV_Assert(fs_left.isOpened());
for(int i = 0; i < n_images; ++i)
fs_left[cv::format("image_%d", i )] >> leftPoints[i];
fs_left.release();
cv::FileStorage fs_right(combine(folder, "right.xml"), cv::FileStorage::READ);
CV_Assert(fs_right.isOpened());
for(int i = 0; i < n_images; ++i)
fs_right[cv::format("image_%d", i )] >> rightPoints[i];
fs_right.release();
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ);
CV_Assert(fs_object.isOpened());
for(int i = 0; i < n_images; ++i)
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object.release();
cv::Matx33d theR;
cv::Vec3d theT;
int flag = 0;
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
flag |= cv::fisheye::CALIB_CHECK_COND;
flag |= cv::fisheye::CALIB_FIX_SKEW;
flag |= cv::fisheye::CALIB_FIX_INTRINSIC;
cv::Matx33d K1 (561.195925927249, 0, 621.282400272412,
0, 562.849402029712, 380.555455380889,
0, 0, 1);
cv::Matx33d K2 (560.395452535348, 0, 678.971652040359,
0, 561.90171021422, 380.401340535339,
0, 0, 1);
cv::Vec4d D1 (-7.44253716539556e-05, -0.00702662033932424, 0.00737569823650885, -0.00342230256441771);
cv::Vec4d D2 (-0.0130785435677431, 0.0284434505383497, -0.0360333869900506, 0.0144724062347222);
cv::fisheye::stereoCalibrate(objectPoints, leftPoints, rightPoints,
K1, D1, K2, D2, imageSize, theR, theT, flag,
cv::TermCriteria(3, 12, 0));
cv::Matx33d R_correct( 0.9975587205950972, 0.06953016383322372, 0.006492709911733523,
-0.06956823121068059, 0.9975601387249519, 0.005833595226966235,
-0.006071257768382089, -0.006271040135405457, 0.9999619062167968);
cv::Vec3d T_correct(-0.099402724724121, 0.00270812139265413, 0.00129330292472699);
EXPECT_MAT_NEAR(theR, R_correct, 1e-10);
EXPECT_MAT_NEAR(theT, T_correct, 1e-10);
}
TEST_F(fisheyeTest, CalibrationWithDifferentPointsNumber)
{
const int n_images = 2;
std::vector<std::vector<cv::Point2d> > imagePoints(n_images);
std::vector<std::vector<cv::Point3d> > objectPoints(n_images);
std::vector<cv::Point2d> imgPoints1(10);
std::vector<cv::Point2d> imgPoints2(15);
std::vector<cv::Point3d> objectPoints1(imgPoints1.size());
std::vector<cv::Point3d> objectPoints2(imgPoints2.size());
for (size_t i = 0; i < imgPoints1.size(); i++)
{
imgPoints1[i] = cv::Point2d((double)i, (double)i);
objectPoints1[i] = cv::Point3d((double)i, (double)i, 10.0);
}
for (size_t i = 0; i < imgPoints2.size(); i++)
{
imgPoints2[i] = cv::Point2d(i + 0.5, i + 0.5);
objectPoints2[i] = cv::Point3d(i + 0.5, i + 0.5, 10.0);
}
imagePoints[0] = imgPoints1;
imagePoints[1] = imgPoints2;
objectPoints[0] = objectPoints1;
objectPoints[1] = objectPoints2;
cv::Matx33d theK = cv::Matx33d::eye();
cv::Vec4d theD;
int flag = 0;
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
flag |= cv::fisheye::CALIB_USE_INTRINSIC_GUESS;
flag |= cv::fisheye::CALIB_FIX_SKEW;
cv::fisheye::calibrate(objectPoints, imagePoints, cv::Size(100, 100), theK, theD,
cv::noArray(), cv::noArray(), flag, cv::TermCriteria(3, 20, 1e-6));
}
TEST_F(fisheyeTest, estimateNewCameraMatrixForUndistortRectify)
{
cv::Size size(1920, 1080);
cv::Mat K_fullhd(3, 3, cv::DataType<double>::type);
K_fullhd.at<double>(0, 0) = 600.44477382;
K_fullhd.at<double>(0, 1) = 0.0;
K_fullhd.at<double>(0, 2) = 992.06425788;
K_fullhd.at<double>(1, 0) = 0.0;
K_fullhd.at<double>(1, 1) = 578.99298055;
K_fullhd.at<double>(1, 2) = 549.26826242;
K_fullhd.at<double>(2, 0) = 0.0;
K_fullhd.at<double>(2, 1) = 0.0;
K_fullhd.at<double>(2, 2) = 1.0;
cv::Mat K_new_truth(3, 3, cv::DataType<double>::type);
K_new_truth.at<double>(0, 0) = 387.4809086880343;
K_new_truth.at<double>(0, 1) = 0.0;
K_new_truth.at<double>(0, 2) = 1036.669802754649;
K_new_truth.at<double>(1, 0) = 0.0;
K_new_truth.at<double>(1, 1) = 373.6375700303157;
K_new_truth.at<double>(1, 2) = 538.8373261247601;
K_new_truth.at<double>(2, 0) = 0.0;
K_new_truth.at<double>(2, 1) = 0.0;
K_new_truth.at<double>(2, 2) = 1.0;
cv::Mat D_fullhd(4, 1, cv::DataType<double>::type);
D_fullhd.at<double>(0, 0) = -0.05090103223466704;
D_fullhd.at<double>(1, 0) = 0.030944413642173308;
D_fullhd.at<double>(2, 0) = -0.021509225493198905;
D_fullhd.at<double>(3, 0) = 0.0043378096628297145;
cv::Mat E = cv::Mat::eye(3, 3, cv::DataType<double>::type);
cv::Mat K_new(3, 3, cv::DataType<double>::type);
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(K_fullhd, D_fullhd, size, E, K_new, 0.0, size);
EXPECT_MAT_NEAR(K_new, K_new_truth, 1e-6);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// fisheyeTest::
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);
const cv::Matx33d fisheyeTest::R ( 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 fisheyeTest::T(-9.9217369356044638e-02, 3.1741831972356663e-03, 1.8551007952921010e-04);
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;
}
void fisheyeTest::merge4(const cv::Mat& tl, const cv::Mat& tr, const cv::Mat& bl, const cv::Mat& br, cv::Mat& merged)
{
int type = tl.type();
cv::Size sz = tl.size();
ASSERT_EQ(type, tr.type()); ASSERT_EQ(type, bl.type()); ASSERT_EQ(type, br.type());
ASSERT_EQ(sz.width, tr.cols); ASSERT_EQ(sz.width, bl.cols); ASSERT_EQ(sz.width, br.cols);
ASSERT_EQ(sz.height, tr.rows); ASSERT_EQ(sz.height, bl.rows); ASSERT_EQ(sz.height, br.rows);
merged.create(cv::Size(sz.width * 2, sz.height * 2), type);
tl.copyTo(merged(cv::Rect(0, 0, sz.width, sz.height)));
tr.copyTo(merged(cv::Rect(sz.width, 0, sz.width, sz.height)));
bl.copyTo(merged(cv::Rect(0, sz.height, sz.width, sz.height)));
br.copyTo(merged(cv::Rect(sz.width, sz.height, sz.width, sz.height)));
}
}} // namespace

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,738 @@
/*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"
namespace opencv_test { namespace {
#define CALIB3D_HOMOGRAPHY_ERROR_MATRIX_SIZE 1
#define CALIB3D_HOMOGRAPHY_ERROR_MATRIX_DIFF 2
#define CALIB3D_HOMOGRAPHY_ERROR_REPROJ_DIFF 3
#define CALIB3D_HOMOGRAPHY_ERROR_RANSAC_MASK 4
#define CALIB3D_HOMOGRAPHY_ERROR_RANSAC_DIFF 5
#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;
class CV_HomographyTest: public cvtest::ArrayTest
{
public:
CV_HomographyTest();
~CV_HomographyTest();
void run (int);
protected:
int method;
int image_size;
double reproj_threshold;
double sigma;
private:
float max_diff, max_2diff;
bool check_matrix_size(const cv::Mat& H);
bool check_matrix_diff(const cv::Mat& original, const cv::Mat& found, const int norm_type, double &diff);
int check_ransac_mask_1(const Mat& src, const Mat& mask);
int check_ransac_mask_2(const Mat& original_mask, const Mat& found_mask);
void print_information_1(int j, int N, int method, const Mat& H);
void print_information_2(int j, int N, int method, const Mat& H, const Mat& H_res, int k, double diff);
void print_information_3(int method, int j, int N, const Mat& mask);
void print_information_4(int method, int j, int N, int k, int l, double diff);
void print_information_5(int method, int j, int N, int l, double diff);
void print_information_6(int method, int j, int N, int k, double diff, bool value);
void print_information_7(int method, int j, int N, int k, double diff, bool original_value, bool found_value);
void print_information_8(int method, int j, int N, int k, int l, double diff);
};
CV_HomographyTest::CV_HomographyTest() : max_diff(1e-2f), max_2diff(2e-2f)
{
method = 0;
image_size = 100;
reproj_threshold = 3.0;
sigma = 0.01;
}
CV_HomographyTest::~CV_HomographyTest() {}
bool CV_HomographyTest::check_matrix_size(const cv::Mat& H)
{
return (H.rows == 3) && (H.cols == 3);
}
bool CV_HomographyTest::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;
}
int CV_HomographyTest::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;
}
int CV_HomographyTest::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;
}
void CV_HomographyTest::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;
}
void CV_HomographyTest::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;
}
void CV_HomographyTest::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;
}
void CV_HomographyTest::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;
}
void CV_HomographyTest::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;
}
void CV_HomographyTest::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;
}
void CV_HomographyTest::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;
}
void CV_HomographyTest::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;
}
void CV_HomographyTest::run(int)
{
for (int N = MIN_COUNT_OF_POINTS; N <= MAX_COUNT_OF_POINTS; ++N)
{
RNG& rng = ts->get_rng();
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)
{
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(CALIB3D_HOMOGRAPHY_ERROR_MATRIX_SIZE, 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(CALIB3D_HOMOGRAPHY_ERROR_MATRIX_DIFF, 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(CALIB3D_HOMOGRAPHY_ERROR_MATRIX_SIZE, 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(CALIB3D_HOMOGRAPHY_ERROR_MATRIX_DIFF, 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(CALIB3D_HOMOGRAPHY_ERROR_RANSAC_MASK, MESSAGE_RANSAC_MASK_1); break; }
case 2: { CV_Error(CALIB3D_HOMOGRAPHY_ERROR_RANSAC_MASK, MESSAGE_RANSAC_MASK_2); break; }
case 3: { CV_Error(CALIB3D_HOMOGRAPHY_ERROR_RANSAC_MASK, 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)
{
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(CALIB3D_HOMOGRAPHY_ERROR_MATRIX_SIZE, 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(CALIB3D_HOMOGRAPHY_ERROR_REPROJ_DIFF, 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(CALIB3D_HOMOGRAPHY_ERROR_REPROJ_DIFF, 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(CALIB3D_HOMOGRAPHY_ERROR_MATRIX_SIZE, 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(CALIB3D_HOMOGRAPHY_ERROR_RANSAC_MASK, MESSAGE_RANSAC_MASK_1); break; }
case 2: { CV_Error(CALIB3D_HOMOGRAPHY_ERROR_RANSAC_MASK, 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(CALIB3D_HOMOGRAPHY_ERROR_RANSAC_MASK, 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(CALIB3D_HOMOGRAPHY_ERROR_RANSAC_MASK, 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(CALIB3D_HOMOGRAPHY_ERROR_RANSAC_DIFF, MESSAGE_RANSAC_DIFF);
return;
}
}
}
}
}
continue;
}
default: continue;
}
}
delete[]src_data;
src_data = NULL;
}
}
TEST(Calib3d_Homography, accuracy) { CV_HomographyTest test; test.safe_run(); }
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);
}
TEST(Calib3d_Homography, fromImages)
{
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();
vector<KeyPoint> keypoints_1, keypoints_2;
Mat descriptors_1, descriptors_2;
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
std::vector<Point2f> pointframe1;
std::vector<Point2f> pointframe2;
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 );
}
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);
double freq = getTickFrequency();
printf("nfeatures1 = %d, nfeatures2=%d, matches=%d, ninliers(RANSAC)=%d, "
"time(RANSAC)=%.2fmsec, ninliers(RHO)=%d, time(RHO)=%.2fmsec\n",
(int)keypoints_1.size(), (int)keypoints_2.size(),
(int)good_matches.size(), ninliers0, min_t0*1000./freq, ninliers1, min_t1*1000./freq);
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);
}
}} // namespace

View File

@ -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

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("")

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

View File

@ -0,0 +1,227 @@
/*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"
// POSIT is not exposed to C++ API yet, so the test is disabled
#if 0
namespace opencv_test { namespace {
class CV_POSITTest : public cvtest::BaseTest
{
public:
CV_POSITTest();
protected:
void run(int);
};
CV_POSITTest::CV_POSITTest()
{
test_case_count = 20;
}
void CV_POSITTest::run( int start_from )
{
int code = cvtest::TS::OK;
/* fixed parameters output */
/*float rot[3][3]={ 0.49010f, 0.85057f, 0.19063f,
-0.56948f, 0.14671f, 0.80880f,
0.65997f, -0.50495f, 0.55629f };
float trans[3] = { 0.0f, 0.0f, 40.02637f };
*/
/* Some variables */
int i, counter;
CvTermCriteria criteria;
CvPoint3D32f* obj_points;
CvPoint2D32f* img_points;
CvPOSITObject* object;
float angleX, angleY, angleZ;
RNG& rng = ts->get_rng();
int progress = 0;
CvMat* true_rotationX = cvCreateMat( 3, 3, CV_32F );
CvMat* true_rotationY = cvCreateMat( 3, 3, CV_32F );
CvMat* true_rotationZ = cvCreateMat( 3, 3, CV_32F );
CvMat* tmp_matrix = cvCreateMat( 3, 3, CV_32F );
CvMat* true_rotation = cvCreateMat( 3, 3, CV_32F );
CvMat* rotation = cvCreateMat( 3, 3, CV_32F );
CvMat* translation = cvCreateMat( 3, 1, CV_32F );
CvMat* true_translation = cvCreateMat( 3, 1, CV_32F );
const float flFocalLength = 760.f;
const float flEpsilon = 0.5f;
/* Initialization */
criteria.type = CV_TERMCRIT_EPS|CV_TERMCRIT_ITER;
criteria.epsilon = flEpsilon;
criteria.max_iter = 10000;
/* Allocating source arrays; */
obj_points = (CvPoint3D32f*)cvAlloc( 8 * sizeof(CvPoint3D32f) );
img_points = (CvPoint2D32f*)cvAlloc( 8 * sizeof(CvPoint2D32f) );
/* Fill points arrays with values */
/* cube model with edge size 10 */
obj_points[0].x = 0; obj_points[0].y = 0; obj_points[0].z = 0;
obj_points[1].x = 10; obj_points[1].y = 0; obj_points[1].z = 0;
obj_points[2].x = 10; obj_points[2].y = 10; obj_points[2].z = 0;
obj_points[3].x = 0; obj_points[3].y = 10; obj_points[3].z = 0;
obj_points[4].x = 0; obj_points[4].y = 0; obj_points[4].z = 10;
obj_points[5].x = 10; obj_points[5].y = 0; obj_points[5].z = 10;
obj_points[6].x = 10; obj_points[6].y = 10; obj_points[6].z = 10;
obj_points[7].x = 0; obj_points[7].y = 10; obj_points[7].z = 10;
/* Loop for test some random object positions */
for( counter = start_from; counter < test_case_count; counter++ )
{
ts->update_context( this, counter, true );
progress = update_progress( progress, counter, test_case_count, 0 );
/* set all rotation matrix to zero */
cvZero( true_rotationX );
cvZero( true_rotationY );
cvZero( true_rotationZ );
/* fill random rotation matrix */
angleX = (float)(cvtest::randReal(rng)*2*CV_PI);
angleY = (float)(cvtest::randReal(rng)*2*CV_PI);
angleZ = (float)(cvtest::randReal(rng)*2*CV_PI);
true_rotationX->data.fl[0 *3+ 0] = 1;
true_rotationX->data.fl[1 *3+ 1] = (float)cos(angleX);
true_rotationX->data.fl[2 *3+ 2] = true_rotationX->data.fl[1 *3+ 1];
true_rotationX->data.fl[1 *3+ 2] = -(float)sin(angleX);
true_rotationX->data.fl[2 *3+ 1] = -true_rotationX->data.fl[1 *3+ 2];
true_rotationY->data.fl[1 *3+ 1] = 1;
true_rotationY->data.fl[0 *3+ 0] = (float)cos(angleY);
true_rotationY->data.fl[2 *3+ 2] = true_rotationY->data.fl[0 *3+ 0];
true_rotationY->data.fl[0 *3+ 2] = -(float)sin(angleY);
true_rotationY->data.fl[2 *3+ 0] = -true_rotationY->data.fl[0 *3+ 2];
true_rotationZ->data.fl[2 *3+ 2] = 1;
true_rotationZ->data.fl[0 *3+ 0] = (float)cos(angleZ);
true_rotationZ->data.fl[1 *3+ 1] = true_rotationZ->data.fl[0 *3+ 0];
true_rotationZ->data.fl[0 *3+ 1] = -(float)sin(angleZ);
true_rotationZ->data.fl[1 *3+ 0] = -true_rotationZ->data.fl[0 *3+ 1];
cvMatMul( true_rotationX, true_rotationY, tmp_matrix);
cvMatMul( tmp_matrix, true_rotationZ, true_rotation);
/* fill translation vector */
true_translation->data.fl[2] = (float)(cvtest::randReal(rng)*(2*flFocalLength-40) + 60);
true_translation->data.fl[0] = (float)((cvtest::randReal(rng)*2-1)*true_translation->data.fl[2]);
true_translation->data.fl[1] = (float)((cvtest::randReal(rng)*2-1)*true_translation->data.fl[2]);
/* calculate perspective projection */
for ( i = 0; i < 8; i++ )
{
float vec[3];
CvMat Vec = cvMat( 3, 1, CV_32F, vec );
CvMat Obj_point = cvMat( 3, 1, CV_32F, &obj_points[i].x );
cvMatMul( true_rotation, &Obj_point, &Vec );
vec[0] += true_translation->data.fl[0];
vec[1] += true_translation->data.fl[1];
vec[2] += true_translation->data.fl[2];
img_points[i].x = flFocalLength * vec[0] / vec[2];
img_points[i].y = flFocalLength * vec[1] / vec[2];
}
/*img_points[0].x = 0 ; img_points[0].y = 0;
img_points[1].x = 80; img_points[1].y = -93;
img_points[2].x = 245;img_points[2].y = -77;
img_points[3].x = 185;img_points[3].y = 32;
img_points[4].x = 32; img_points[4].y = 135;
img_points[5].x = 99; img_points[5].y = 35;
img_points[6].x = 247; img_points[6].y = 62;
img_points[7].x = 195; img_points[7].y = 179;
*/
object = cvCreatePOSITObject( obj_points, 8 );
cvPOSIT( object, img_points, flFocalLength, criteria,
rotation->data.fl, translation->data.fl );
cvReleasePOSITObject( &object );
Mat _rotation = cvarrToMat(rotation), _true_rotation = cvarrToMat(true_rotation);
Mat _translation = cvarrToMat(translation), _true_translation = cvarrToMat(true_translation);
code = cvtest::cmpEps2( ts, _rotation, _true_rotation, flEpsilon, false, "rotation matrix" );
if( code < 0 )
break;
code = cvtest::cmpEps2( ts, _translation, _true_translation, flEpsilon, false, "translation vector" );
if( code < 0 )
break;
}
cvFree( &obj_points );
cvFree( &img_points );
cvReleaseMat( &true_rotationX );
cvReleaseMat( &true_rotationY );
cvReleaseMat( &true_rotationZ );
cvReleaseMat( &tmp_matrix );
cvReleaseMat( &true_rotation );
cvReleaseMat( &rotation );
cvReleaseMat( &translation );
cvReleaseMat( &true_translation );
if( code < 0 )
ts->set_failed_test_info( code );
}
TEST(Calib3d_POSIT, accuracy) { CV_POSITTest test; test.safe_run(); }
}} // namespace
#endif
/* End of file. */

View File

@ -0,0 +1,22 @@
// 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/calib3d.hpp"
namespace cvtest
{
void Rodrigues(const Mat& src, Mat& dst, Mat* jac=0);
}
namespace opencv_test {
CVTEST_GUARD_SYMBOL(Rodrigues)
} // namespace
#endif

View File

@ -0,0 +1,173 @@
/*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 {
template<class T> double thres() { return 1.0; }
template<> double thres<float>() { return 1e-5; }
class CV_ReprojectImageTo3DTest : public cvtest::BaseTest
{
public:
CV_ReprojectImageTo3DTest() {}
~CV_ReprojectImageTo3DTest() {}
protected:
void run(int)
{
ts->set_failed_test_info(cvtest::TS::OK);
int progress = 0;
int caseId = 0;
progress = update_progress( progress, 1, 14, 0 );
runCase<float, float>(++caseId, -100.f, 100.f);
progress = update_progress( progress, 2, 14, 0 );
runCase<int, float>(++caseId, -100, 100);
progress = update_progress( progress, 3, 14, 0 );
runCase<short, float>(++caseId, -100, 100);
progress = update_progress( progress, 4, 14, 0 );
runCase<unsigned char, float>(++caseId, 10, 100);
progress = update_progress( progress, 5, 14, 0 );
runCase<float, int>(++caseId, -100.f, 100.f);
progress = update_progress( progress, 6, 14, 0 );
runCase<int, int>(++caseId, -100, 100);
progress = update_progress( progress, 7, 14, 0 );
runCase<short, int>(++caseId, -100, 100);
progress = update_progress( progress, 8, 14, 0 );
runCase<unsigned char, int>(++caseId, 10, 100);
progress = update_progress( progress, 10, 14, 0 );
runCase<float, short>(++caseId, -100.f, 100.f);
progress = update_progress( progress, 11, 14, 0 );
runCase<int, short>(++caseId, -100, 100);
progress = update_progress( progress, 12, 14, 0 );
runCase<short, short>(++caseId, -100, 100);
progress = update_progress( progress, 13, 14, 0 );
runCase<unsigned char, short>(++caseId, 10, 100);
progress = update_progress( progress, 14, 14, 0 );
}
template<class U, class V> double error(const Vec<U, 3>& v1, const Vec<V, 3>& v2) const
{
double tmp, sum = 0;
double nsum = 0;
for(int i = 0; i < 3; ++i)
{
tmp = v1[i];
nsum += tmp * tmp;
tmp = tmp - v2[i];
sum += tmp * tmp;
}
return sqrt(sum)/(sqrt(nsum)+1.);
}
template<class InT, class OutT> void runCase(int caseId, InT min, InT max)
{
typedef Vec<OutT, 3> out3d_t;
bool handleMissingValues = (unsigned)theRNG() % 2 == 0;
Mat_<InT> disp(Size(320, 240));
randu(disp, Scalar(min), Scalar(max));
if (handleMissingValues)
disp(disp.rows/2, disp.cols/2) = min - 1;
Mat_<double> Q(4, 4);
randu(Q, Scalar(-5), Scalar(5));
Mat_<out3d_t> _3dImg(disp.size());
reprojectImageTo3D(disp, _3dImg, Q, handleMissingValues);
for(int y = 0; y < disp.rows; ++y)
for(int x = 0; x < disp.cols; ++x)
{
InT d = disp(y, x);
double from[4] = {
static_cast<double>(x),
static_cast<double>(y),
static_cast<double>(d),
1.0,
};
Mat_<double> res = Q * Mat_<double>(4, 1, from);
res /= res(3, 0);
out3d_t pixel_exp = *res.ptr<Vec3d>();
out3d_t pixel_out = _3dImg(y, x);
const int largeZValue = 10000; /* see documentation */
if (handleMissingValues && y == disp.rows/2 && x == disp.cols/2)
{
if (pixel_out[2] == largeZValue)
continue;
ts->printf(cvtest::TS::LOG, "Missing values are handled improperly\n");
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
return;
}
else
{
double err = error(pixel_out, pixel_exp), t = thres<OutT>();
if ( err > t )
{
ts->printf(cvtest::TS::LOG, "case %d. too big error at (%d, %d): %g vs expected %g: res = (%g, %g, %g, w=%g) vs pixel_out = (%g, %g, %g)\n",
caseId, x, y, err, t, res(0,0), res(1,0), res(2,0), res(3,0),
(double)pixel_out[0], (double)pixel_out[1], (double)pixel_out[2]);
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
return;
}
}
}
}
};
TEST(Calib3d_ReprojectImageTo3D, accuracy) { CV_ReprojectImageTo3DTest test; test.safe_run(); }
}} // namespace

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,945 @@
/*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*/
/*
This is a regression test for stereo matching algorithms. This test gets some quality metrics
described in "A Taxonomy and Evaluation of Dense Two-Frame Stereo Correspondence Algorithms".
Daniel Scharstein, Richard Szeliski
*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
const float EVAL_BAD_THRESH = 1.f;
const int EVAL_TEXTURELESS_WIDTH = 3;
const float EVAL_TEXTURELESS_THRESH = 4.f;
const float EVAL_DISP_THRESH = 1.f;
const float EVAL_DISP_GAP = 2.f;
const int EVAL_DISCONT_WIDTH = 9;
const int EVAL_IGNORE_BORDER = 10;
const int ERROR_KINDS_COUNT = 6;
//============================== quality measuring functions =================================================
/*
Calculate textureless regions of image (regions where the squared horizontal intensity gradient averaged over
a square window of size=evalTexturelessWidth is below a threshold=evalTexturelessThresh) and textured regions.
*/
void computeTextureBasedMasks( const Mat& _img, Mat* texturelessMask, Mat* texturedMask,
int texturelessWidth = EVAL_TEXTURELESS_WIDTH, float texturelessThresh = EVAL_TEXTURELESS_THRESH )
{
if( !texturelessMask && !texturedMask )
return;
if( _img.empty() )
CV_Error( Error::StsBadArg, "img is empty" );
Mat img = _img;
if( _img.channels() > 1)
{
Mat tmp; cvtColor( _img, tmp, COLOR_BGR2GRAY ); img = tmp;
}
Mat dxI; Sobel( img, dxI, CV_32FC1, 1, 0, 3 );
Mat dxI2; pow( dxI / 8.f/*normalize*/, 2, dxI2 );
Mat avgDxI2; boxFilter( dxI2, avgDxI2, CV_32FC1, Size(texturelessWidth,texturelessWidth) );
if( texturelessMask )
*texturelessMask = avgDxI2 < texturelessThresh;
if( texturedMask )
*texturedMask = avgDxI2 >= texturelessThresh;
}
void checkTypeAndSizeOfDisp( const Mat& dispMap, const Size* sz )
{
if( dispMap.empty() )
CV_Error( Error::StsBadArg, "dispMap is empty" );
if( dispMap.type() != CV_32FC1 )
CV_Error( Error::StsBadArg, "dispMap must have CV_32FC1 type" );
if( sz && (dispMap.rows != sz->height || dispMap.cols != sz->width) )
CV_Error( Error::StsBadArg, "dispMap has incorrect size" );
}
void checkTypeAndSizeOfMask( const Mat& mask, Size sz )
{
if( mask.empty() )
CV_Error( Error::StsBadArg, "mask is empty" );
if( mask.type() != CV_8UC1 )
CV_Error( Error::StsBadArg, "mask must have CV_8UC1 type" );
if( mask.rows != sz.height || mask.cols != sz.width )
CV_Error( Error::StsBadArg, "mask has incorrect size" );
}
void checkDispMapsAndUnknDispMasks( const Mat& leftDispMap, const Mat& rightDispMap,
const Mat& leftUnknDispMask, const Mat& rightUnknDispMask )
{
// check type and size of disparity maps
checkTypeAndSizeOfDisp( leftDispMap, 0 );
if( !rightDispMap.empty() )
{
Size sz = leftDispMap.size();
checkTypeAndSizeOfDisp( rightDispMap, &sz );
}
// check size and type of unknown disparity maps
if( !leftUnknDispMask.empty() )
checkTypeAndSizeOfMask( leftUnknDispMask, leftDispMap.size() );
if( !rightUnknDispMask.empty() )
checkTypeAndSizeOfMask( rightUnknDispMask, rightDispMap.size() );
// check values of disparity maps (known disparity values musy be positive)
double leftMinVal = 0, rightMinVal = 0;
if( leftUnknDispMask.empty() )
minMaxLoc( leftDispMap, &leftMinVal );
else
minMaxLoc( leftDispMap, &leftMinVal, 0, 0, 0, ~leftUnknDispMask );
if( !rightDispMap.empty() )
{
if( rightUnknDispMask.empty() )
minMaxLoc( rightDispMap, &rightMinVal );
else
minMaxLoc( rightDispMap, &rightMinVal, 0, 0, 0, ~rightUnknDispMask );
}
if( leftMinVal < 0 || rightMinVal < 0)
CV_Error( Error::StsBadArg, "known disparity values must be positive" );
}
/*
Calculate occluded regions of reference image (left image) (regions that are occluded in the matching image (right image),
i.e., where the forward-mapped disparity lands at a location with a larger (nearer) disparity) and non occluded regions.
*/
void computeOcclusionBasedMasks( const Mat& leftDisp, const Mat& _rightDisp,
Mat* occludedMask, Mat* nonOccludedMask,
const Mat& leftUnknDispMask = Mat(), const Mat& rightUnknDispMask = Mat(),
float dispThresh = EVAL_DISP_THRESH )
{
if( !occludedMask && !nonOccludedMask )
return;
checkDispMapsAndUnknDispMasks( leftDisp, _rightDisp, leftUnknDispMask, rightUnknDispMask );
Mat rightDisp;
if( _rightDisp.empty() )
{
if( !rightUnknDispMask.empty() )
CV_Error( Error::StsBadArg, "rightUnknDispMask must be empty if _rightDisp is empty" );
rightDisp.create(leftDisp.size(), CV_32FC1);
rightDisp.setTo(Scalar::all(0) );
for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
{
for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
{
if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
continue;
float leftDispVal = leftDisp.at<float>(leftY, leftX);
int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
if( rightX >= 0)
rightDisp.at<float>(rightY,rightX) = max(rightDisp.at<float>(rightY,rightX), leftDispVal);
}
}
}
else
_rightDisp.copyTo(rightDisp);
if( occludedMask )
{
occludedMask->create(leftDisp.size(), CV_8UC1);
occludedMask->setTo(Scalar::all(0) );
}
if( nonOccludedMask )
{
nonOccludedMask->create(leftDisp.size(), CV_8UC1);
nonOccludedMask->setTo(Scalar::all(0) );
}
for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
{
for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
{
if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
continue;
float leftDispVal = leftDisp.at<float>(leftY, leftX);
int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
if( rightX < 0 && occludedMask )
occludedMask->at<uchar>(leftY, leftX) = 255;
else
{
if( !rightUnknDispMask.empty() && rightUnknDispMask.at<uchar>(rightY,rightX) )
continue;
float rightDispVal = rightDisp.at<float>(rightY, rightX);
if( rightDispVal > leftDispVal + dispThresh )
{
if( occludedMask )
occludedMask->at<uchar>(leftY, leftX) = 255;
}
else
{
if( nonOccludedMask )
nonOccludedMask->at<uchar>(leftY, leftX) = 255;
}
}
}
}
}
/*
Calculate depth discontinuty regions: pixels whose neiboring disparities differ by more than
dispGap, dilated by window of width discontWidth.
*/
void computeDepthDiscontMask( const Mat& disp, Mat& depthDiscontMask, const Mat& unknDispMask = Mat(),
float dispGap = EVAL_DISP_GAP, int discontWidth = EVAL_DISCONT_WIDTH )
{
if( disp.empty() )
CV_Error( Error::StsBadArg, "disp is empty" );
if( disp.type() != CV_32FC1 )
CV_Error( Error::StsBadArg, "disp must have CV_32FC1 type" );
if( !unknDispMask.empty() )
checkTypeAndSizeOfMask( unknDispMask, disp.size() );
Mat curDisp; disp.copyTo( curDisp );
if( !unknDispMask.empty() )
curDisp.setTo( Scalar(std::numeric_limits<float>::min()), unknDispMask );
Mat maxNeighbDisp; dilate( curDisp, maxNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
if( !unknDispMask.empty() )
curDisp.setTo( Scalar(std::numeric_limits<float>::max()), unknDispMask );
Mat minNeighbDisp; erode( curDisp, minNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
depthDiscontMask = max( (Mat)(maxNeighbDisp-disp), (Mat)(disp-minNeighbDisp) ) > dispGap;
if( !unknDispMask.empty() )
depthDiscontMask &= ~unknDispMask;
dilate( depthDiscontMask, depthDiscontMask, Mat(discontWidth, discontWidth, CV_8UC1, Scalar(1)) );
}
/*
Get evaluation masks excluding a border.
*/
Mat getBorderedMask( Size maskSize, int border = EVAL_IGNORE_BORDER )
{
CV_Assert( border >= 0 );
Mat mask(maskSize, CV_8UC1, Scalar(0));
int w = maskSize.width - 2*border, h = maskSize.height - 2*border;
if( w < 0 || h < 0 )
mask.setTo(Scalar(0));
else
mask( Rect(Point(border,border),Size(w,h)) ).setTo(Scalar(255));
return mask;
}
/*
Calculate root-mean-squared error between the computed disparity map (computedDisp) and ground truth map (groundTruthDisp).
*/
float dispRMS( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask )
{
checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
Size sz = groundTruthDisp.size();
checkTypeAndSizeOfDisp( computedDisp, &sz );
int pointsCount = sz.height*sz.width;
if( !mask.empty() )
{
checkTypeAndSizeOfMask( mask, sz );
pointsCount = countNonZero(mask);
}
return 1.f/sqrt((float)pointsCount) * (float)cvtest::norm(computedDisp, groundTruthDisp, NORM_L2, mask);
}
/*
Calculate fraction of bad matching pixels.
*/
float badMatchPxlsFraction( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask,
float _badThresh = EVAL_BAD_THRESH )
{
int badThresh = cvRound(_badThresh);
checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
Size sz = groundTruthDisp.size();
checkTypeAndSizeOfDisp( computedDisp, &sz );
Mat badPxlsMap;
absdiff( computedDisp, groundTruthDisp, badPxlsMap );
badPxlsMap = badPxlsMap > badThresh;
int pointsCount = sz.height*sz.width;
if( !mask.empty() )
{
checkTypeAndSizeOfMask( mask, sz );
badPxlsMap = badPxlsMap & mask;
pointsCount = countNonZero(mask);
}
return 1.f/pointsCount * countNonZero(badPxlsMap);
}
//===================== regression test for stereo matching algorithms ==============================
const string ALGORITHMS_DIR = "stereomatching/algorithms/";
const string DATASETS_DIR = "stereomatching/datasets/";
const string DATASETS_FILE = "datasets.xml";
const string RUN_PARAMS_FILE = "_params.xml";
const string RESULT_FILE = "_res.xml";
const string LEFT_IMG_NAME = "im2.png";
const string RIGHT_IMG_NAME = "im6.png";
const string TRUE_LEFT_DISP_NAME = "disp2.png";
const string TRUE_RIGHT_DISP_NAME = "disp6.png";
string ERROR_PREFIXES[] = { "borderedAll",
"borderedNoOccl",
"borderedOccl",
"borderedTextured",
"borderedTextureless",
"borderedDepthDiscont" }; // size of ERROR_KINDS_COUNT
string ROI_PREFIXES[] = { "roiX",
"roiY",
"roiWidth",
"roiHeight" };
const string RMS_STR = "RMS";
const string BAD_PXLS_FRACTION_STR = "BadPxlsFraction";
const string ROI_STR = "ValidDisparityROI";
class QualityEvalParams
{
public:
QualityEvalParams() { setDefaults(); }
QualityEvalParams( int _ignoreBorder )
{
setDefaults();
ignoreBorder = _ignoreBorder;
}
void setDefaults()
{
badThresh = EVAL_BAD_THRESH;
texturelessWidth = EVAL_TEXTURELESS_WIDTH;
texturelessThresh = EVAL_TEXTURELESS_THRESH;
dispThresh = EVAL_DISP_THRESH;
dispGap = EVAL_DISP_GAP;
discontWidth = EVAL_DISCONT_WIDTH;
ignoreBorder = EVAL_IGNORE_BORDER;
}
float badThresh;
int texturelessWidth;
float texturelessThresh;
float dispThresh;
float dispGap;
int discontWidth;
int ignoreBorder;
};
class CV_StereoMatchingTest : public cvtest::BaseTest
{
public:
CV_StereoMatchingTest()
{ rmsEps.resize( ERROR_KINDS_COUNT, 0.01f ); fracEps.resize( ERROR_KINDS_COUNT, 1.e-6f ); }
protected:
// assumed that left image is a reference image
virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
Rect& calcROI, Mat& leftDisp, Mat& rightDisp, int caseIdx ) = 0; // return ignored border width
int readDatasetsParams( FileStorage& fs );
virtual int readRunParams( FileStorage& fs );
void writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs = 0 );
void writeROI( const Rect& calcROI, FileStorage* fs = 0 );
void readErrors( FileNode& fn, const string& errName, vector<float>& errors );
void readROI( FileNode& fn, Rect& trueROI );
int compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
const vector<float>& eps, const string& errName );
int compareROI( const Rect& calcROI, const Rect& validROI );
int processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
const Mat& leftImg, const Mat& rightImg,
const Rect& calcROI,
const Mat& trueLeftDisp, const Mat& trueRightDisp,
const Mat& leftDisp, const Mat& rightDisp,
const QualityEvalParams& qualityEvalParams );
void run( int );
vector<float> rmsEps;
vector<float> fracEps;
struct DatasetParams
{
int dispScaleFactor;
int dispUnknVal;
};
map<string, DatasetParams> datasetsParams;
vector<string> caseNames;
vector<string> caseDatasets;
};
void CV_StereoMatchingTest::run(int)
{
string dataPath = ts->get_data_path() + "cv/";
string algorithmName = name;
assert( !algorithmName.empty() );
if( dataPath.empty() )
{
ts->printf( cvtest::TS::LOG, "dataPath is empty" );
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ARG_CHECK );
return;
}
FileStorage datasetsFS( dataPath + DATASETS_DIR + DATASETS_FILE, FileStorage::READ );
int code = readDatasetsParams( datasetsFS );
if( code != cvtest::TS::OK )
{
ts->set_failed_test_info( code );
return;
}
FileStorage runParamsFS( dataPath + ALGORITHMS_DIR + algorithmName + RUN_PARAMS_FILE, FileStorage::READ );
code = readRunParams( runParamsFS );
if( code != cvtest::TS::OK )
{
ts->set_failed_test_info( code );
return;
}
string fullResultFilename = dataPath + ALGORITHMS_DIR + algorithmName + RESULT_FILE;
FileStorage resFS( fullResultFilename, FileStorage::READ );
bool isWrite = true; // write or compare results
if( resFS.isOpened() )
isWrite = false;
else
{
resFS.open( fullResultFilename, FileStorage::WRITE );
if( !resFS.isOpened() )
{
ts->printf( cvtest::TS::LOG, "file %s can not be read or written\n", fullResultFilename.c_str() );
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ARG_CHECK );
return;
}
resFS << "stereo_matching" << "{";
}
int progress = 0, caseCount = (int)caseNames.size();
for( int ci = 0; ci < caseCount; ci++)
{
progress = update_progress( progress, ci, caseCount, 0 );
printf("progress: %d%%\n", progress);
fflush(stdout);
string datasetName = caseDatasets[ci];
string datasetFullDirName = dataPath + DATASETS_DIR + datasetName + "/";
Mat leftImg = imread(datasetFullDirName + LEFT_IMG_NAME);
Mat rightImg = imread(datasetFullDirName + RIGHT_IMG_NAME);
Mat trueLeftDisp = imread(datasetFullDirName + TRUE_LEFT_DISP_NAME, 0);
Mat trueRightDisp = imread(datasetFullDirName + TRUE_RIGHT_DISP_NAME, 0);
Rect calcROI;
if( leftImg.empty() || rightImg.empty() || trueLeftDisp.empty() )
{
ts->printf( cvtest::TS::LOG, "images or left ground-truth disparities of dataset %s can not be read", datasetName.c_str() );
code = cvtest::TS::FAIL_INVALID_TEST_DATA;
continue;
}
int dispScaleFactor = datasetsParams[datasetName].dispScaleFactor;
Mat tmp;
trueLeftDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor );
trueLeftDisp = tmp;
tmp.release();
if( !trueRightDisp.empty() )
{
trueRightDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor );
trueRightDisp = tmp;
tmp.release();
}
Mat leftDisp, rightDisp;
int ignBorder = max(runStereoMatchingAlgorithm(leftImg, rightImg, calcROI, leftDisp, rightDisp, ci), EVAL_IGNORE_BORDER);
leftDisp.convertTo( tmp, CV_32FC1 );
leftDisp = tmp;
tmp.release();
rightDisp.convertTo( tmp, CV_32FC1 );
rightDisp = tmp;
tmp.release();
int tempCode = processStereoMatchingResults( resFS, ci, isWrite,
leftImg, rightImg, calcROI, trueLeftDisp, trueRightDisp, leftDisp, rightDisp, QualityEvalParams(ignBorder));
code = tempCode==cvtest::TS::OK ? code : tempCode;
}
if( isWrite )
resFS << "}"; // "stereo_matching"
ts->set_failed_test_info( code );
}
void calcErrors( const Mat& leftImg, const Mat& /*rightImg*/,
const Mat& trueLeftDisp, const Mat& trueRightDisp,
const Mat& trueLeftUnknDispMask, const Mat& trueRightUnknDispMask,
const Mat& calcLeftDisp, const Mat& /*calcRightDisp*/,
vector<float>& rms, vector<float>& badPxlsFractions,
const QualityEvalParams& qualityEvalParams )
{
Mat texturelessMask, texturedMask;
computeTextureBasedMasks( leftImg, &texturelessMask, &texturedMask,
qualityEvalParams.texturelessWidth, qualityEvalParams.texturelessThresh );
Mat occludedMask, nonOccludedMask;
computeOcclusionBasedMasks( trueLeftDisp, trueRightDisp, &occludedMask, &nonOccludedMask,
trueLeftUnknDispMask, trueRightUnknDispMask, qualityEvalParams.dispThresh);
Mat depthDiscontMask;
computeDepthDiscontMask( trueLeftDisp, depthDiscontMask, trueLeftUnknDispMask,
qualityEvalParams.dispGap, qualityEvalParams.discontWidth);
Mat borderedKnownMask = getBorderedMask( leftImg.size(), qualityEvalParams.ignoreBorder ) & ~trueLeftUnknDispMask;
nonOccludedMask &= borderedKnownMask;
occludedMask &= borderedKnownMask;
texturedMask &= nonOccludedMask; // & borderedKnownMask
texturelessMask &= nonOccludedMask; // & borderedKnownMask
depthDiscontMask &= nonOccludedMask; // & borderedKnownMask
rms.resize(ERROR_KINDS_COUNT);
rms[0] = dispRMS( calcLeftDisp, trueLeftDisp, borderedKnownMask );
rms[1] = dispRMS( calcLeftDisp, trueLeftDisp, nonOccludedMask );
rms[2] = dispRMS( calcLeftDisp, trueLeftDisp, occludedMask );
rms[3] = dispRMS( calcLeftDisp, trueLeftDisp, texturedMask );
rms[4] = dispRMS( calcLeftDisp, trueLeftDisp, texturelessMask );
rms[5] = dispRMS( calcLeftDisp, trueLeftDisp, depthDiscontMask );
badPxlsFractions.resize(ERROR_KINDS_COUNT);
badPxlsFractions[0] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, borderedKnownMask, qualityEvalParams.badThresh );
badPxlsFractions[1] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, nonOccludedMask, qualityEvalParams.badThresh );
badPxlsFractions[2] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, occludedMask, qualityEvalParams.badThresh );
badPxlsFractions[3] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturedMask, qualityEvalParams.badThresh );
badPxlsFractions[4] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturelessMask, qualityEvalParams.badThresh );
badPxlsFractions[5] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, depthDiscontMask, qualityEvalParams.badThresh );
}
int CV_StereoMatchingTest::processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
const Mat& leftImg, const Mat& rightImg,
const Rect& calcROI,
const Mat& trueLeftDisp, const Mat& trueRightDisp,
const Mat& leftDisp, const Mat& rightDisp,
const QualityEvalParams& qualityEvalParams )
{
// rightDisp is not used in current test virsion
int code = cvtest::TS::OK;
assert( fs.isOpened() );
assert( trueLeftDisp.type() == CV_32FC1 );
assert( trueRightDisp.empty() || trueRightDisp.type() == CV_32FC1 );
assert( leftDisp.type() == CV_32FC1 && (rightDisp.empty() || rightDisp.type() == CV_32FC1) );
// get masks for unknown ground truth disparity values
Mat leftUnknMask, rightUnknMask;
DatasetParams params = datasetsParams[caseDatasets[caseIdx]];
absdiff( trueLeftDisp, Scalar(params.dispUnknVal), leftUnknMask );
leftUnknMask = leftUnknMask < std::numeric_limits<float>::epsilon();
assert(leftUnknMask.type() == CV_8UC1);
if( !trueRightDisp.empty() )
{
absdiff( trueRightDisp, Scalar(params.dispUnknVal), rightUnknMask );
rightUnknMask = rightUnknMask < std::numeric_limits<float>::epsilon();
assert(rightUnknMask.type() == CV_8UC1);
}
// calculate errors
vector<float> rmss, badPxlsFractions;
calcErrors( leftImg, rightImg, trueLeftDisp, trueRightDisp, leftUnknMask, rightUnknMask,
leftDisp, rightDisp, rmss, badPxlsFractions, qualityEvalParams );
if( isWrite )
{
fs << caseNames[caseIdx] << "{";
fs.writeComment( RMS_STR, 0 );
writeErrors( RMS_STR, rmss, &fs );
fs.writeComment( BAD_PXLS_FRACTION_STR, 0 );
writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions, &fs );
fs.writeComment( ROI_STR, 0 );
writeROI( calcROI, &fs );
fs << "}"; // datasetName
}
else // compare
{
ts->printf( cvtest::TS::LOG, "\nquality of case named %s\n", caseNames[caseIdx].c_str() );
ts->printf( cvtest::TS::LOG, "%s\n", RMS_STR.c_str() );
writeErrors( RMS_STR, rmss );
ts->printf( cvtest::TS::LOG, "%s\n", BAD_PXLS_FRACTION_STR.c_str() );
writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions );
ts->printf( cvtest::TS::LOG, "%s\n", ROI_STR.c_str() );
writeROI( calcROI );
FileNode fn = fs.getFirstTopLevelNode()[caseNames[caseIdx]];
vector<float> validRmss, validBadPxlsFractions;
Rect validROI;
readErrors( fn, RMS_STR, validRmss );
readErrors( fn, BAD_PXLS_FRACTION_STR, validBadPxlsFractions );
readROI( fn, validROI );
int tempCode = compareErrors( rmss, validRmss, rmsEps, RMS_STR );
code = tempCode==cvtest::TS::OK ? code : tempCode;
tempCode = compareErrors( badPxlsFractions, validBadPxlsFractions, fracEps, BAD_PXLS_FRACTION_STR );
code = tempCode==cvtest::TS::OK ? code : tempCode;
tempCode = compareROI( calcROI, validROI );
code = tempCode==cvtest::TS::OK ? code : tempCode;
}
return code;
}
int CV_StereoMatchingTest::readDatasetsParams( FileStorage& fs )
{
if( !fs.isOpened() )
{
ts->printf( cvtest::TS::LOG, "datasetsParams can not be read " );
return cvtest::TS::FAIL_INVALID_TEST_DATA;
}
datasetsParams.clear();
FileNode fn = fs.getFirstTopLevelNode();
assert(fn.isSeq());
for( int i = 0; i < (int)fn.size(); i+=3 )
{
String _name = fn[i];
DatasetParams params;
String sf = fn[i+1]; params.dispScaleFactor = atoi(sf.c_str());
String uv = fn[i+2]; params.dispUnknVal = atoi(uv.c_str());
datasetsParams[_name] = params;
}
return cvtest::TS::OK;
}
int CV_StereoMatchingTest::readRunParams( FileStorage& fs )
{
if( !fs.isOpened() )
{
ts->printf( cvtest::TS::LOG, "runParams can not be read " );
return cvtest::TS::FAIL_INVALID_TEST_DATA;
}
caseNames.clear();;
caseDatasets.clear();
return cvtest::TS::OK;
}
void CV_StereoMatchingTest::writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs )
{
assert( (int)errors.size() == ERROR_KINDS_COUNT );
vector<float>::const_iterator it = errors.begin();
if( fs )
for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
*fs << ERROR_PREFIXES[i] + errName << *it;
else
for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
ts->printf( cvtest::TS::LOG, "%s = %f\n", string(ERROR_PREFIXES[i]+errName).c_str(), *it );
}
void CV_StereoMatchingTest::writeROI( const Rect& calcROI, FileStorage* fs )
{
if( fs )
{
*fs << ROI_PREFIXES[0] << calcROI.x;
*fs << ROI_PREFIXES[1] << calcROI.y;
*fs << ROI_PREFIXES[2] << calcROI.width;
*fs << ROI_PREFIXES[3] << calcROI.height;
}
else
{
ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[0].c_str(), calcROI.x );
ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[1].c_str(), calcROI.y );
ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[2].c_str(), calcROI.width );
ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[3].c_str(), calcROI.height );
}
}
void CV_StereoMatchingTest::readErrors( FileNode& fn, const string& errName, vector<float>& errors )
{
errors.resize( ERROR_KINDS_COUNT );
vector<float>::iterator it = errors.begin();
for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
fn[ERROR_PREFIXES[i]+errName] >> *it;
}
void CV_StereoMatchingTest::readROI( FileNode& fn, Rect& validROI )
{
fn[ROI_PREFIXES[0]] >> validROI.x;
fn[ROI_PREFIXES[1]] >> validROI.y;
fn[ROI_PREFIXES[2]] >> validROI.width;
fn[ROI_PREFIXES[3]] >> validROI.height;
}
int CV_StereoMatchingTest::compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
const vector<float>& eps, const string& errName )
{
assert( (int)calcErrors.size() == ERROR_KINDS_COUNT );
assert( (int)validErrors.size() == ERROR_KINDS_COUNT );
assert( (int)eps.size() == ERROR_KINDS_COUNT );
vector<float>::const_iterator calcIt = calcErrors.begin(),
validIt = validErrors.begin(),
epsIt = eps.begin();
bool ok = true;
for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++calcIt, ++validIt, ++epsIt )
if( *calcIt - *validIt > *epsIt )
{
ts->printf( cvtest::TS::LOG, "bad accuracy of %s (valid=%f; calc=%f)\n", string(ERROR_PREFIXES[i]+errName).c_str(), *validIt, *calcIt );
ok = false;
}
return ok ? cvtest::TS::OK : cvtest::TS::FAIL_BAD_ACCURACY;
}
int CV_StereoMatchingTest::compareROI( const Rect& calcROI, const Rect& validROI )
{
int compare[4][2] = {
{ calcROI.x, validROI.x },
{ calcROI.y, validROI.y },
{ calcROI.width, validROI.width },
{ calcROI.height, validROI.height },
};
bool ok = true;
for (int i = 0; i < 4; i++)
{
if (compare[i][0] != compare[i][1])
{
ts->printf( cvtest::TS::LOG, "bad accuracy of %s (valid=%d; calc=%d)\n", ROI_PREFIXES[i].c_str(), compare[i][1], compare[i][0] );
ok = false;
}
}
return ok ? cvtest::TS::OK : cvtest::TS::FAIL_BAD_ACCURACY;
}
//----------------------------------- StereoBM test -----------------------------------------------------
class CV_StereoBMTest : public CV_StereoMatchingTest
{
public:
CV_StereoBMTest()
{
name = "stereobm";
fill(rmsEps.begin(), rmsEps.end(), 0.4f);
fill(fracEps.begin(), fracEps.end(), 0.022f);
}
protected:
struct RunParams
{
int ndisp;
int mindisp;
int winSize;
};
vector<RunParams> caseRunParams;
virtual int readRunParams( FileStorage& fs )
{
int code = CV_StereoMatchingTest::readRunParams( fs );
FileNode fn = fs.getFirstTopLevelNode();
assert(fn.isSeq());
for( int i = 0; i < (int)fn.size(); i+=5 )
{
String caseName = fn[i], datasetName = fn[i+1];
RunParams params;
String ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
String mindisp = fn[i+3]; params.mindisp = atoi(mindisp.c_str());
String winSize = fn[i+4]; params.winSize = atoi(winSize.c_str());
caseNames.push_back( caseName );
caseDatasets.push_back( datasetName );
caseRunParams.push_back( params );
}
return code;
}
virtual int runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,
Rect& calcROI, Mat& leftDisp, Mat& /*rightDisp*/, int caseIdx )
{
RunParams params = caseRunParams[caseIdx];
assert( params.ndisp%16 == 0 );
assert( _leftImg.type() == CV_8UC3 && _rightImg.type() == CV_8UC3 );
Mat leftImg; cvtColor( _leftImg, leftImg, COLOR_BGR2GRAY );
Mat rightImg; cvtColor( _rightImg, rightImg, COLOR_BGR2GRAY );
Ptr<StereoBM> bm = StereoBM::create( params.ndisp, params.winSize );
Mat tempDisp;
bm->setMinDisparity(params.mindisp);
Rect cROI(0, 0, _leftImg.cols, _leftImg.rows);
calcROI = getValidDisparityROI(cROI, cROI, params.mindisp, params.ndisp, params.winSize);
bm->compute( leftImg, rightImg, tempDisp );
tempDisp.convertTo(leftDisp, CV_32F, 1./StereoMatcher::DISP_SCALE);
//check for fixed-type disparity data type
Mat_<float> fixedFloatDisp;
bm->compute( leftImg, rightImg, fixedFloatDisp );
EXPECT_LT(cvtest::norm(fixedFloatDisp, leftDisp, cv::NORM_L2 | cv::NORM_RELATIVE),
0.005 + DBL_EPSILON);
if (params.mindisp != 0)
for (int y = 0; y < leftDisp.rows; y++)
for (int x = 0; x < leftDisp.cols; x++)
{
if (leftDisp.at<float>(y, x) < params.mindisp)
leftDisp.at<float>(y, x) = -1./StereoMatcher::DISP_SCALE; // treat disparity < mindisp as no disparity
}
return params.winSize/2;
}
};
TEST(Calib3d_StereoBM, regression) { CV_StereoBMTest test; test.safe_run(); }
/* < preFilter, < preFilterCap, SADWindowSize > >*/
typedef tuple < int, tuple < int, int > > BufferBM_Params_t;
typedef testing::TestWithParam< BufferBM_Params_t > Calib3d_StereoBM_BufferBM;
const int preFilters[] =
{
StereoBM::PREFILTER_NORMALIZED_RESPONSE,
StereoBM::PREFILTER_XSOBEL
};
const tuple < int, int > useShortsConditions[] =
{
make_tuple(30, 19),
make_tuple(32, 23)
};
TEST_P(Calib3d_StereoBM_BufferBM, memAllocsTest)
{
const int preFilter = get<0>(GetParam());
const int preFilterCap = get<0>(get<1>(GetParam()));
const int SADWindowSize = get<1>(get<1>(GetParam()));
String path = cvtest::TS::ptr()->get_data_path() + "cv/stereomatching/datasets/teddy/";
Mat leftImg = imread(path + "im2.png", 0);
ASSERT_FALSE(leftImg.empty());
Mat rightImg = imread(path + "im6.png", 0);
ASSERT_FALSE(rightImg.empty());
Mat leftDisp;
{
Ptr<StereoBM> bm = StereoBM::create(16,9);
bm->setPreFilterType(preFilter);
bm->setPreFilterCap(preFilterCap);
bm->setBlockSize(SADWindowSize);
bm->compute( leftImg, rightImg, leftDisp);
ASSERT_FALSE(leftDisp.empty());
}
}
INSTANTIATE_TEST_CASE_P(/*nothing*/, Calib3d_StereoBM_BufferBM,
testing::Combine(
testing::ValuesIn(preFilters),
testing::ValuesIn(useShortsConditions)
)
);
//----------------------------------- StereoSGBM test -----------------------------------------------------
class CV_StereoSGBMTest : public CV_StereoMatchingTest
{
public:
CV_StereoSGBMTest()
{
name = "stereosgbm";
fill(rmsEps.begin(), rmsEps.end(), 0.25f);
fill(fracEps.begin(), fracEps.end(), 0.01f);
}
protected:
struct RunParams
{
int ndisp;
int winSize;
int mode;
};
vector<RunParams> caseRunParams;
virtual int readRunParams( FileStorage& fs )
{
int code = CV_StereoMatchingTest::readRunParams(fs);
FileNode fn = fs.getFirstTopLevelNode();
assert(fn.isSeq());
for( int i = 0; i < (int)fn.size(); i+=5 )
{
String caseName = fn[i], datasetName = fn[i+1];
RunParams params;
String ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
String winSize = fn[i+3]; params.winSize = atoi(winSize.c_str());
String mode = fn[i+4]; params.mode = atoi(mode.c_str());
caseNames.push_back( caseName );
caseDatasets.push_back( datasetName );
caseRunParams.push_back( params );
}
return code;
}
virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
Rect& calcROI, Mat& leftDisp, Mat& /*rightDisp*/, int caseIdx )
{
RunParams params = caseRunParams[caseIdx];
assert( params.ndisp%16 == 0 );
Ptr<StereoSGBM> sgbm = StereoSGBM::create( 0, params.ndisp, params.winSize,
10*params.winSize*params.winSize,
40*params.winSize*params.winSize,
1, 63, 10, 100, 32, params.mode );
Rect cROI(0, 0, leftImg.cols, leftImg.rows);
calcROI = getValidDisparityROI(cROI, cROI, 0, params.ndisp, params.winSize);
sgbm->compute( leftImg, rightImg, leftDisp );
CV_Assert( leftDisp.type() == CV_16SC1 );
leftDisp/=16;
return 0;
}
};
TEST(Calib3d_StereoSGBM, regression) { CV_StereoSGBMTest test; test.safe_run(); }
TEST(Calib3d_StereoSGBM_HH4, regression)
{
String path = cvtest::TS::ptr()->get_data_path() + "cv/stereomatching/datasets/teddy/";
Mat leftImg = imread(path + "im2.png", 0);
ASSERT_FALSE(leftImg.empty());
Mat rightImg = imread(path + "im6.png", 0);
ASSERT_FALSE(rightImg.empty());
Mat testData = imread(path + "disp2_hh4.png",-1);
ASSERT_FALSE(testData.empty());
Mat leftDisp;
Mat toCheck;
{
Ptr<StereoSGBM> sgbm = StereoSGBM::create( 0, 48, 3, 90, 360, 1, 63, 10, 100, 32, StereoSGBM::MODE_HH4);
sgbm->compute( leftImg, rightImg, leftDisp);
CV_Assert( leftDisp.type() == CV_16SC1 );
leftDisp.convertTo(toCheck, CV_16UC1,1,16);
}
Mat diff;
absdiff(toCheck, testData,diff);
CV_Assert( countNonZero(diff)==0);
}
}} // namespace

View File

@ -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 = count(outl.begin(), outl.end(), 1) == m &&
m == accumulate(outl.begin(), outl.begin() + m, 0);
EXPECT_TRUE(outl_good);
}
}} // namespace

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,270 @@
/*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/core/core_c.h"
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};
CvMat _camera_mat_orig = cvMat(3,3,CV_64F,cam);
CvMat _distortion_coeffs_orig = cvMat(1,4,CV_64F,dist);
CvMat _P_orig = cvMat(3,3,CV_64F,p);
CvMat _R_orig = cvMat(3,3,CV_64F,r);
CvMat _src_points_orig = cvMat(1,4,CV_64FC2,s_points);
camera_mat = cv::cvarrToMat(&_camera_mat_orig);
distortion_coeffs = cv::cvarrToMat(&_distortion_coeffs_orig);
P = cv::cvarrToMat(&_P_orig);
R = cv::cvarrToMat(&_R_orig);
src_points = cv::cvarrToMat(&_src_points_orig);
src_points.create(2, 2, CV_32FC2);
errcount += run_test_case( CV_StsAssert, "Invalid input data matrix size" );
src_points = cv::cvarrToMat(&_src_points_orig);
src_points.create(1, 4, CV_64FC2);
errcount += run_test_case( CV_StsAssert, "Invalid input data matrix type" );
src_points = cv::cvarrToMat(&_src_points_orig);
src_points = cv::Mat();
errcount += run_test_case( CV_StsBadArg, "Input data matrix is not continuous" );
src_points = cv::cvarrToMat(&_src_points_orig);
//------------
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};
CvMat _camera_mat_orig = cvMat(3,3,CV_64F,cam);
CvMat _distortion_coeffs_orig = cvMat(1,4,CV_64F,dist);
CvMat _new_camera_mat_orig = cvMat(3,3,CV_64F,arr_new_camera_mat);
CvMat _R_orig = cvMat(3,3,CV_64F,r);
CvMat _mapx_orig = cvMat(img_size.height,img_size.width,CV_32FC1,&arr_mapx[0]);
CvMat _mapy_orig = cvMat(img_size.height,img_size.width,CV_32FC1,&arr_mapy[0]);
int mat_type_orig = CV_32FC1;
camera_mat = cv::cvarrToMat(&_camera_mat_orig);
distortion_coeffs = cv::cvarrToMat(&_distortion_coeffs_orig);
new_camera_mat = cv::cvarrToMat(&_new_camera_mat_orig);
R = cv::cvarrToMat(&_R_orig);
mapx = cv::cvarrToMat(&_mapx_orig);
mapy = cv::cvarrToMat(&_mapy_orig);
mat_type = CV_64F;
errcount += run_test_case( CV_StsAssert, "Invalid map matrix type" );
mat_type = mat_type_orig;
camera_mat.create(3, 2, CV_32F);
errcount += run_test_case( CV_StsAssert, "Invalid camera data matrix size" );
camera_mat = cv::cvarrToMat(&_camera_mat_orig);
R.create(4, 3, CV_32F);
errcount += run_test_case( CV_StsAssert, "Invalid R data matrix size" );
R = cv::cvarrToMat(&_R_orig);
distortion_coeffs.create(6, 1, CV_32F);
errcount += run_test_case( CV_StsAssert, "Invalid distortion coefficients data matrix size" );
distortion_coeffs = cv::cvarrToMat(&_distortion_coeffs_orig);
//------------
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};
CvMat _camera_mat_orig = cvMat(3,3,CV_64F,cam);
CvMat _distortion_coeffs_orig = cvMat(1,4,CV_64F,dist);
CvMat _new_camera_mat_orig = cvMat(3,3,CV_64F,arr_new_camera_mat);
CvMat _src_orig = cvMat(img_size.height,img_size.width,CV_32FC1,&arr_src[0]);
CvMat _dst_orig = cvMat(img_size.height,img_size.width,CV_32FC1,&arr_dst[0]);
camera_mat = cv::cvarrToMat(&_camera_mat_orig);
distortion_coeffs = cv::cvarrToMat(&_distortion_coeffs_orig);
new_camera_mat = cv::cvarrToMat(&_new_camera_mat_orig);
src = cv::cvarrToMat(&_src_orig);
dst = cv::cvarrToMat(&_dst_orig);
camera_mat.create(5, 5, CV_64F);
errcount += run_test_case( CV_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. */

View File

@ -0,0 +1,148 @@
// 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 {
class CV_UndistortTest : public cvtest::BaseTest
{
public:
CV_UndistortTest();
~CV_UndistortTest();
protected:
void run(int);
private:
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);
double thresh;
RNG rng;
};
CV_UndistortTest::CV_UndistortTest()
{
thresh = 1.0e-2;
}
CV_UndistortTest::~CV_UndistortTest() {}
void CV_UndistortTest::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 CV_UndistortTest::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) = rng.uniform(fcMinVal, fcMaxVal);
cameraMatrix.at<double>(1,1) = rng.uniform(fcMinVal, fcMaxVal);
cameraMatrix.at<double>(0,2) = rng.uniform(fcMinVal, fcMaxVal);
cameraMatrix.at<double>(1,2) = rng.uniform(fcMinVal, fcMaxVal);
cameraMatrix.at<double>(2,2) = 1;
}
void CV_UndistortTest::generateDistCoeffs(Mat& distCoeffs, int count)
{
distCoeffs = Mat::zeros(count, 1, CV_64FC1);
for (int i = 0; i < count; i++)
distCoeffs.at<double>(i,0) = rng.uniform(0.0, 1.0e-3);
}
void CV_UndistortTest::run(int /* start_from */)
{
Mat intrinsics, distCoeffs;
generateCameraMatrix(intrinsics);
vector<Point3f> points(500);
generate3DPointCloud(points);
vector<Point2f> projectedPoints;
projectedPoints.resize(points.size());
int modelMembersCount[] = {4,5,8};
for (int idx = 0; idx < 3; idx++)
{
generateDistCoeffs(distCoeffs, modelMembersCount[idx]);
projectPoints(Mat(points), Mat::zeros(3,1,CV_64FC1), Mat::zeros(3,1,CV_64FC1), intrinsics, distCoeffs, projectedPoints);
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);
Mat undistortedPoints;
undistortPoints(Mat(projectedPoints), undistortedPoints, intrinsics, distCoeffs);
Mat p;
perspectiveTransform(undistortedPoints, p, intrinsics);
undistortedPoints = p;
double diff = cvtest::norm(Mat(realUndistortedPoints), undistortedPoints, NORM_L2);
if (diff > thresh)
{
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
return;
}
ts->set_failed_test_info(cvtest::TS::OK);
}
}
TEST(Calib3d_Undistort, accuracy) { CV_UndistortTest test; test.safe_run(); }
TEST(Calib3d_Undistort, 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);
RNG rng(2);
Point2d pt_distorted(rng.uniform(0.0, 1920.0), rng.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;
undistortPoints(pt_distorted_vec, pt_undist_vec, cameraMatrix, distCoeffs, noArray(), noArray(), criteria);
std::vector<Point2d> pt_redistorted_vec;
std::vector<Point3d> pt_undist_vec_homogeneous;
pt_undist_vec_homogeneous.push_back( Point3d(pt_undist_vec[0].x, pt_undist_vec[0].y, 1.0) );
projectPoints(pt_undist_vec_homogeneous, Mat::zeros(3,1,CV_64F), Mat::zeros(3,1,CV_64F), cameraMatrix, distCoeffs, pt_redistorted_vec);
const double obtainedError = sqrt( pow(pt_distorted.x - pt_redistorted_vec[0].x, 2) + pow(pt_distorted.y - pt_redistorted_vec[0].y, 2) );
ASSERT_LE(obtainedError, maxError);
}
TEST(undistortPoints, 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;
}
}} // namespace

View File

@ -0,0 +1,456 @@
// 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;
if (test_case == TestSolver::Fundam) // sampson error
return fabs(pt2.dot(l2)) / sqrt(pow(l1.at<double>(0), 2) + pow(l1.at<double>(1), 2) +
pow(l2.at<double>(0), 2) + pow(l2.at<double>(1), 2));
else // symmetric geometric distance
return sqrt(pow(pt1.dot(l1),2) / (pow(l1.at<double>(0),2) + pow(l1.at<double>(1),2)) +
pow(pt2.dot(l2),2) / (pow(l2.at<double>(0),2) + 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 - 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 - 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());
}
TEST(usac_Essential, accuracy) {
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.;
for (auto flag : flags) {
cv::Mat mask, E;
try {
E = cv::findEssentialMat(pts1, pts2, K1, flag, conf, thr, 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(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 - 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 - 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);
}
}} // namespace