diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c6a3e9ad..79a312587 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1275,10 +1275,10 @@ if(BUILD_EXAMPLES OR INSTALL_PYTHON_EXAMPLES) add_subdirectory(samples) endif() -if(BUILD_TESTS) - enable_testing() - add_subdirectory(tests) -endif() +#if(BUILD_TESTS) +# enable_testing() +# add_subdirectory(tests) +#endif() add_subdirectory(3rdparty) diff --git a/OpenCVModule.cmake b/OpenCVModule.cmake index a09acf71f..16916d169 100644 --- a/OpenCVModule.cmake +++ b/OpenCVModule.cmake @@ -82,13 +82,13 @@ macro(define_opencv_module name) DESTINATION include/opencv2/${name} COMPONENT main) - if(BUILD_TESTS AND NOT ANDROID AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/test) + if(BUILD_TESTS AND NOT ANDROID AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/test) include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/test" - "${CMAKE_CURRENT_BINARY_DIR}" - "${CMAKE_SOURCE_DIR}/modules/gtest/include") + "${CMAKE_CURRENT_BINARY_DIR}") - foreach(d ${ARGN}) + set(test_deps opencv_${name} ${ARGN} opencv_ts opencv_highgui ${EXTRA_${the_target}_DEPS}) + foreach(d ${test_deps}) if(${d} MATCHES "opencv_") if(${d} MATCHES "opencv_lapack") else() @@ -101,7 +101,7 @@ macro(define_opencv_module name) file(GLOB test_srcs "test/*.cpp") file(GLOB test_hdrs "test/*.h*") - set(the_target "opencv_gtest_${name}") + set(the_target "opencv_test_${name}") add_executable(${the_target} ${test_srcs} ${test_hdrs}) @@ -123,10 +123,10 @@ macro(define_opencv_module name) RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/" ) - add_dependencies(${the_target} ${ARGN} opencv_gtest) + add_dependencies(${the_target} ${test_deps}) # Add the required libraries for linking: - target_link_libraries(${the_target} ${OPENCV_LINKER_LIBS} ${ARGN} opencv_gtest) + target_link_libraries(${the_target} ${OPENCV_LINKER_LIBS} ${test_deps}) enable_testing() get_target_property(LOC ${the_target} LOCATION) @@ -138,66 +138,3 @@ macro(define_opencv_module name) endif() endmacro() - -if(0) -macro(define_opencv_test name) - - if(BUILD_TESTS AND NOT ANDROID AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/test) - include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include" - "${CMAKE_CURRENT_SOURCE_DIR}/test" - "${CMAKE_CURRENT_BINARY_DIR}" - "${CMAKE_SOURCE_DIR}/modules/gtest/include") - - foreach(d ${ARGN}) - if(${d} MATCHES "opencv_") - if(${d} MATCHES "opencv_lapack") - else() - string(REPLACE "opencv_" "${CMAKE_CURRENT_SOURCE_DIR}/../" d_dir ${d}) - include_directories("${d_dir}/include") - endif() - endif() - endforeach() - - file(GLOB test_srcs "test/*.cpp") - file(GLOB test_hdrs "test/*.h*") - - set(the_target "opencv_gtest_${name}") - - add_executable(${the_target} ${test_srcs} ${test_hdrs}) - - if(PCHSupport_FOUND) - set(pch_header ${CMAKE_CURRENT_SOURCE_DIR}/test/precomp.hpp) - if(${CMAKE_GENERATOR} MATCHES "Visual*" OR ${CMAKE_GENERATOR} MATCHES "Xcode*") - if(${CMAKE_GENERATOR} MATCHES "Visual*") - set(${the_target}_pch "test/precomp.cpp") - endif() - add_native_precompiled_header(${the_target} ${pch_header}) - elseif(CMAKE_COMPILER_IS_GNUCXX AND ${CMAKE_GENERATOR} MATCHES ".*Makefiles") - add_precompiled_header(${the_target} ${pch_header}) - endif() - endif() - - # Additional target properties - set_target_properties(${the_target} PROPERTIES - DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}" - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/" - ) - - add_dependencies(${the_target} ${ARGN} opencv_gtest) - - # Add the required libraries for linking: - target_link_libraries(${the_target} ${OPENCV_LINKER_LIBS} ${ARGN} opencv_gtest) - - enable_testing() - get_target_property(LOC ${the_target} LOCATION) - add_test(${the_target} "${LOC}") - - if(WIN32) - install(TARGETS ${the_target} RUNTIME DESTINATION bin COMPONENT main) - endif() - endif() - -endmacro() -endif() - - diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index d981b2585..ce50df37a 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -13,7 +13,7 @@ if(MSVC OR MINGW) endif() endif() -add_subdirectory(gtest) +add_subdirectory(ts) add_subdirectory(highgui) add_subdirectory(imgproc) add_subdirectory(legacy) diff --git a/modules/calib3d/include/opencv2/calib3d/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d/calib3d.hpp index d1599cf23..61324b3b8 100644 --- a/modules/calib3d/include/opencv2/calib3d/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d/calib3d.hpp @@ -534,6 +534,10 @@ CV_EXPORTS_W bool findChessboardCorners( const Mat& image, Size patternSize, int flags=CALIB_CB_ADAPTIVE_THRESH+ CALIB_CB_NORMALIZE_IMAGE ); +//! finds subpixel-accurate positions of the chessboard corners +CV_EXPORTS bool find4QuadCornerSubpix(const Mat& img, std::vector& corners, + Size region_size); + //! draws the checkerboard pattern (found or partly found) in the image CV_EXPORTS_W void drawChessboardCorners( Mat& image, Size patternSize, const Mat& corners, diff --git a/modules/contrib/src/quadsubpix.cpp b/modules/calib3d/src/quadsubpix.cpp similarity index 100% rename from modules/contrib/src/quadsubpix.cpp rename to modules/calib3d/src/quadsubpix.cpp diff --git a/modules/calib3d/test/test_affine3d_estimator.cpp b/modules/calib3d/test/test_affine3d_estimator.cpp new file mode 100644 index 000000000..95742a919 --- /dev/null +++ b/modules/calib3d/test/test_affine3d_estimator.cpp @@ -0,0 +1,195 @@ +/*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" + +using namespace cv; +using namespace std; + +#include +#include +#include +#include +#include +#include + +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()) {} + Point3f operator()(const Point3f& p) + { + return Point3d( p.x * F[0] + p.y * F[1] + p.z * F[2] + F[3], + p.x * F[4] + p.y * F[5] + p.z * F[6] + F[7], + 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()[0] = Point3f( rngIn(1,2), rngIn(1,2), rngIn(5, 6) ); + fpts.ptr()[1] = Point3f( rngIn(3,4), rngIn(3,4), rngIn(5, 6) ); + fpts.ptr()[2] = Point3f( rngIn(1,2), rngIn(3,4), rngIn(5, 6) ); + fpts.ptr()[3] = Point3f( rngIn(3,4), rngIn(1,2), rngIn(5, 6) ); + + transform(fpts.ptr(), fpts.ptr() + 4, tpts.ptr(), WrapAff(aff)); + + Mat aff_est; + vector outliers; + estimateAffine3D(fpts, tpts, aff_est, outliers); + + const double thres = 1e-3; + if (norm(aff_est, aff, NORM_INF) > thres) + { + //cout << 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)); + transform(fpts.ptr(), fpts.ptr() + n, tpts.ptr(), WrapAff(aff)); + + /* adding noise*/ + transform(tpts.ptr() + m, tpts.ptr() + n, tpts.ptr() + m, bind2nd(plus(), shift_outl)); + transform(tpts.ptr() + m, tpts.ptr() + n, tpts.ptr() + m, Noise(noise_level)); + + Mat aff_est; + vector 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 (norm(aff_est, aff, NORM_INF) > thres) + { + 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_EstimateAffineTransform, accuracy) { CV_Affine3D_EstTest test; test.safe_run(); } + diff --git a/modules/calib3d/test/test_cameracalibration.cpp b/modules/calib3d/test/test_cameracalibration.cpp new file mode 100644 index 000000000..c14b70b5f --- /dev/null +++ b/modules/calib3d/test/test_cameracalibration.cpp @@ -0,0 +1,1717 @@ +/*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 + +#if 0 +class CV_ProjectPointsTest : public cvtest::ArrayTest +{ +public: + CV_ProjectPointsTest(); + +protected: + int read_params( CvFileStorage* fs ); + void fill_array( int test_case_idx, int i, int j, Mat& arr ); + int prepare_test_case( int test_case_idx ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int ); + + bool calc_jacobians; +}; + + +CV_ProjectPointsTest::CV_ProjectPointsTest() + : cvtest::ArrayTest( "3d-ProjectPoints", "cvProjectPoints2", "" ) +{ + test_array[INPUT].push_back(NULL); // rotation vector + test_array[OUTPUT].push_back(NULL); // rotation matrix + test_array[OUTPUT].push_back(NULL); // jacobian (J) + test_array[OUTPUT].push_back(NULL); // rotation vector (backward transform result) + test_array[OUTPUT].push_back(NULL); // inverse transform jacobian (J1) + test_array[OUTPUT].push_back(NULL); // J*J1 (or J1*J) == I(3x3) + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + + element_wise_relative_error = false; + calc_jacobians = false; +} + + +int CV_ProjectPointsTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::ArrayTest::read_params( fs ); + return code; +} + + +void CV_ProjectPointsTest::get_test_array_types_and_sizes( + int /*test_case_idx*/, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int depth = cvtest::randInt(rng) % 2 == 0 ? CV_32F : CV_64F; + int i, code; + + code = cvtest::randInt(rng) % 3; + types[INPUT][0] = CV_MAKETYPE(depth, 1); + + if( code == 0 ) + { + sizes[INPUT][0] = cvSize(1,1); + types[INPUT][0] = CV_MAKETYPE(depth, 3); + } + else if( code == 1 ) + sizes[INPUT][0] = cvSize(3,1); + else + sizes[INPUT][0] = cvSize(1,3); + + sizes[OUTPUT][0] = cvSize(3, 3); + types[OUTPUT][0] = CV_MAKETYPE(depth, 1); + + types[OUTPUT][1] = CV_MAKETYPE(depth, 1); + + if( cvtest::randInt(rng) % 2 ) + sizes[OUTPUT][1] = cvSize(3,9); + else + sizes[OUTPUT][1] = cvSize(9,3); + + types[OUTPUT][2] = types[INPUT][0]; + sizes[OUTPUT][2] = sizes[INPUT][0]; + + types[OUTPUT][3] = types[OUTPUT][1]; + sizes[OUTPUT][3] = cvSize(sizes[OUTPUT][1].height, sizes[OUTPUT][1].width); + + types[OUTPUT][4] = types[OUTPUT][1]; + sizes[OUTPUT][4] = cvSize(3,3); + + calc_jacobians = 1;//cvtest::randInt(rng) % 3 != 0; + if( !calc_jacobians ) + sizes[OUTPUT][1] = sizes[OUTPUT][3] = sizes[OUTPUT][4] = cvSize(0,0); + + for( i = 0; i < 5; i++ ) + { + types[REF_OUTPUT][i] = types[OUTPUT][i]; + sizes[REF_OUTPUT][i] = sizes[OUTPUT][i]; + } +} + + +double CV_ProjectPointsTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int j ) +{ + return j == 4 ? 1e-2 : 1e-2; +} + + +void CV_ProjectPointsTest::fill_array( int /*test_case_idx*/, int /*i*/, int /*j*/, CvMat* arr ) +{ + double r[3], theta0, theta1, f; + CvMat _r = cvMat( arr->rows, arr->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(arr->type)), r ); + RNG& rng = ts->get_rng(); + + r[0] = cvtest::randReal(rng)*CV_PI*2; + r[1] = cvtest::randReal(rng)*CV_PI*2; + r[2] = cvtest::randReal(rng)*CV_PI*2; + + theta0 = sqrt(r[0]*r[0] + r[1]*r[1] + r[2]*r[2]); + theta1 = fmod(theta0, CV_PI*2); + + if( theta1 > CV_PI ) + theta1 = -(CV_PI*2 - theta1); + + f = theta1/(theta0 ? theta0 : 1); + r[0] *= f; + r[1] *= f; + r[2] *= f; + + cvTsConvert( &_r, arr ); +} + + +int CV_ProjectPointsTest::prepare_test_case( int test_case_idx ) +{ + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + return code; +} + + +void CV_ProjectPointsTest::run_func() +{ + CvMat *v2m_jac = 0, *m2v_jac = 0; + if( calc_jacobians ) + { + v2m_jac = &test_mat[OUTPUT][1]; + m2v_jac = &test_mat[OUTPUT][3]; + } + + cvProjectPoints2( &test_mat[INPUT][0], &test_mat[OUTPUT][0], v2m_jac ); + cvProjectPoints2( &test_mat[OUTPUT][0], &test_mat[OUTPUT][2], m2v_jac ); +} + + +void CV_ProjectPointsTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + const CvMat* vec = &test_mat[INPUT][0]; + CvMat* m = &test_mat[REF_OUTPUT][0]; + CvMat* vec2 = &test_mat[REF_OUTPUT][2]; + CvMat* v2m_jac = 0, *m2v_jac = 0; + double theta0, theta1; + + if( calc_jacobians ) + { + v2m_jac = &test_mat[REF_OUTPUT][1]; + m2v_jac = &test_mat[REF_OUTPUT][3]; + } + + + cvTsProjectPoints( vec, m, v2m_jac ); + cvTsProjectPoints( m, vec2, m2v_jac ); + cvTsCopy( vec, vec2 ); + + theta0 = cvNorm( vec2, 0, CV_L2 ); + theta1 = fmod( theta0, CV_PI*2 ); + + if( theta1 > CV_PI ) + theta1 = -(CV_PI*2 - theta1); + cvScale( vec2, vec2, theta1/(theta0 ? theta0 : 1) ); + + if( calc_jacobians ) + { + //cvInvert( v2m_jac, m2v_jac, CV_SVD ); + if( cvNorm(&test_mat[OUTPUT][3],0,CV_C) < 1000 ) + { + cvTsGEMM( &test_mat[OUTPUT][1], &test_mat[OUTPUT][3], + 1, 0, 0, &test_mat[OUTPUT][4], + v2m_jac->rows == 3 ? 0 : CV_GEMM_A_T + CV_GEMM_B_T ); + } + else + { + cvTsSetIdentity( &test_mat[OUTPUT][4], cvScalarAll(1.) ); + cvTsCopy( &test_mat[REF_OUTPUT][2], &test_mat[OUTPUT][2] ); + } + cvTsSetIdentity( &test_mat[REF_OUTPUT][4], cvScalarAll(1.) ); + } +} + + +CV_ProjectPointsTest ProjectPoints_test; + +#endif + +using namespace cv; + +// --------------------------------- CV_CameraCalibrationTest -------------------------------------------- + +class CV_CameraCalibrationTest : public cvtest::BaseTest +{ +public: + CV_CameraCalibrationTest(); + ~CV_CameraCalibrationTest(); + void clear(); +protected: + int compare(double* val, double* refVal, int len, + double eps, const char* paramName); + virtual void calibrate( int imageCount, int* pointCounts, + CvSize imageSize, CvPoint2D64f* imagePoints, CvPoint3D64f* objectPoints, + double* distortionCoeffs, double* cameraMatrix, double* translationVectors, + double* rotationMatrices, int flags ) = 0; + virtual void project( int pointCount, CvPoint3D64f* objectPoints, + double* rotationMatrix, double* translationVector, + double* cameraMatrix, double* distortion, CvPoint2D64f* imagePoints ) = 0; + + void run(int); +}; + +CV_CameraCalibrationTest::CV_CameraCalibrationTest() +{ +} + +CV_CameraCalibrationTest::~CV_CameraCalibrationTest() +{ + clear(); +} + +void CV_CameraCalibrationTest::clear() +{ + cvtest::BaseTest::clear(); +} + +int CV_CameraCalibrationTest::compare(double* val, double* ref_val, int len, + double eps, const char* param_name ) +{ + return cvtest::cmpEps2_64f( ts, val, ref_val, len, eps, param_name ); +} + +void CV_CameraCalibrationTest::run( int start_from ) +{ + int code = cvtest::TS::OK; + char filepath[200]; + char filename[200]; + + CvSize imageSize; + CvSize etalonSize; + int numImages; + + CvPoint2D64f* imagePoints; + CvPoint3D64f* objectPoints; + CvPoint2D64f* reprojectPoints; + + double* transVects; + double* rotMatrs; + + double* goodTransVects; + double* goodRotMatrs; + + double cameraMatrix[3*3]; + double distortion[5]={0,0,0,0,0}; + + double goodDistortion[4]; + + int* numbers; + FILE* file = 0; + FILE* datafile = 0; + int i,j; + int currImage; + int currPoint; + + int calibFlags; + char i_dat_file[100]; + int numPoints; + int numTests; + int currTest; + + imagePoints = 0; + objectPoints = 0; + reprojectPoints = 0; + numbers = 0; + + transVects = 0; + rotMatrs = 0; + goodTransVects = 0; + goodRotMatrs = 0; + int progress = 0; + + sprintf( filepath, "%scameracalibration/", ts->get_data_path().c_str() ); + sprintf( filename, "%sdatafiles.txt", filepath ); + datafile = fopen( filename, "r" ); + if( datafile == 0 ) + { + ts->printf( cvtest::TS::LOG, "Could not open file with list of test files: %s\n", filename ); + code = cvtest::TS::FAIL_MISSING_TEST_DATA; + goto _exit_; + } + + fscanf(datafile,"%d",&numTests); + + for( currTest = start_from; currTest < numTests; currTest++ ) + { + fscanf(datafile,"%s",i_dat_file); + sprintf(filename, "%s%s", filepath, i_dat_file); + file = fopen(filename,"r"); + + ts->update_context( this, currTest, true ); + + if( file == 0 ) + { + ts->printf( cvtest::TS::LOG, + "Can't open current test file: %s\n",filename); + if( numTests == 1 ) + { + code = cvtest::TS::FAIL_MISSING_TEST_DATA; + goto _exit_; + } + continue; // if there is more than one test, just skip the test + } + + fscanf(file,"%d %d\n",&(imageSize.width),&(imageSize.height)); + if( imageSize.width <= 0 || imageSize.height <= 0 ) + { + ts->printf( cvtest::TS::LOG, "Image size in test file is incorrect\n" ); + code = cvtest::TS::FAIL_INVALID_TEST_DATA; + goto _exit_; + } + + /* Read etalon size */ + fscanf(file,"%d %d\n",&(etalonSize.width),&(etalonSize.height)); + if( etalonSize.width <= 0 || etalonSize.height <= 0 ) + { + ts->printf( cvtest::TS::LOG, "Pattern size in test file is incorrect\n" ); + code = cvtest::TS::FAIL_INVALID_TEST_DATA; + goto _exit_; + } + + numPoints = etalonSize.width * etalonSize.height; + + /* Read number of images */ + fscanf(file,"%d\n",&numImages); + if( numImages <=0 ) + { + ts->printf( cvtest::TS::LOG, "Number of images in test file is incorrect\n"); + code = cvtest::TS::FAIL_INVALID_TEST_DATA; + goto _exit_; + } + + /* Need to allocate memory */ + imagePoints = (CvPoint2D64f*)cvAlloc( numPoints * + numImages * sizeof(CvPoint2D64f)); + + objectPoints = (CvPoint3D64f*)cvAlloc( numPoints * + numImages * sizeof(CvPoint3D64f)); + + reprojectPoints = (CvPoint2D64f*)cvAlloc( numPoints * + numImages * sizeof(CvPoint2D64f)); + + /* Alloc memory for numbers */ + numbers = (int*)cvAlloc( numImages * sizeof(int)); + + /* Fill it by numbers of points of each image*/ + for( currImage = 0; currImage < numImages; currImage++ ) + { + numbers[currImage] = etalonSize.width * etalonSize.height; + } + + /* Allocate memory for translate vectors and rotmatrixs*/ + transVects = (double*)cvAlloc(3 * 1 * numImages * sizeof(double)); + rotMatrs = (double*)cvAlloc(3 * 3 * numImages * sizeof(double)); + + goodTransVects = (double*)cvAlloc(3 * 1 * numImages * sizeof(double)); + goodRotMatrs = (double*)cvAlloc(3 * 3 * numImages * sizeof(double)); + + /* Read object points */ + i = 0;/* shift for current point */ + for( currImage = 0; currImage < numImages; currImage++ ) + { + for( currPoint = 0; currPoint < numPoints; currPoint++ ) + { + double x,y,z; + fscanf(file,"%lf %lf %lf\n",&x,&y,&z); + + (objectPoints+i)->x = x; + (objectPoints+i)->y = y; + (objectPoints+i)->z = z; + i++; + } + } + + /* Read image points */ + i = 0;/* shift for current point */ + for( currImage = 0; currImage < numImages; currImage++ ) + { + for( currPoint = 0; currPoint < numPoints; currPoint++ ) + { + double x,y; + fscanf(file,"%lf %lf\n",&x,&y); + + (imagePoints+i)->x = x; + (imagePoints+i)->y = y; + i++; + } + } + + /* Read good data computed before */ + + /* Focal lengths */ + double goodFcx,goodFcy; + fscanf(file,"%lf %lf",&goodFcx,&goodFcy); + + /* Principal points */ + double goodCx,goodCy; + fscanf(file,"%lf %lf",&goodCx,&goodCy); + + /* Read distortion */ + + fscanf(file,"%lf",goodDistortion+0); + fscanf(file,"%lf",goodDistortion+1); + fscanf(file,"%lf",goodDistortion+2); + fscanf(file,"%lf",goodDistortion+3); + + /* Read good Rot matrixes */ + for( currImage = 0; currImage < numImages; currImage++ ) + { + for( i = 0; i < 3; i++ ) + for( j = 0; j < 3; j++ ) + fscanf(file, "%lf", goodRotMatrs + currImage * 9 + j * 3 + i); + } + + /* Read good Trans vectors */ + for( currImage = 0; currImage < numImages; currImage++ ) + { + for( i = 0; i < 3; i++ ) + fscanf(file, "%lf", goodTransVects + currImage * 3 + i); + } + + calibFlags = 0 + // + CV_CALIB_FIX_PRINCIPAL_POINT + // + CV_CALIB_ZERO_TANGENT_DIST + // + CV_CALIB_FIX_ASPECT_RATIO + // + CV_CALIB_USE_INTRINSIC_GUESS + + CV_CALIB_FIX_K3 + + CV_CALIB_FIX_K4+CV_CALIB_FIX_K5 + + CV_CALIB_FIX_K6 + ; + memset( cameraMatrix, 0, 9*sizeof(cameraMatrix[0]) ); + cameraMatrix[0] = cameraMatrix[4] = 807.; + cameraMatrix[2] = (imageSize.width - 1)*0.5; + cameraMatrix[5] = (imageSize.height - 1)*0.5; + cameraMatrix[8] = 1.; + + /* Now we can calibrate camera */ + calibrate( numImages, + numbers, + imageSize, + imagePoints, + objectPoints, + distortion, + cameraMatrix, + transVects, + rotMatrs, + calibFlags ); + + /* ---- Reproject points to the image ---- */ + for( currImage = 0; currImage < numImages; currImage++ ) + { + int numPoints = etalonSize.width * etalonSize.height; + project( numPoints, + objectPoints + currImage * numPoints, + rotMatrs + currImage * 9, + transVects + currImage * 3, + cameraMatrix, + distortion, + reprojectPoints + currImage * numPoints); + } + + /* ----- Compute reprojection error ----- */ + i = 0; + double dx,dy; + double rx,ry; + double meanDx,meanDy; + double maxDx = 0.0; + double maxDy = 0.0; + + meanDx = 0; + meanDy = 0; + for( currImage = 0; currImage < numImages; currImage++ ) + { + for( currPoint = 0; currPoint < etalonSize.width * etalonSize.height; currPoint++ ) + { + rx = reprojectPoints[i].x; + ry = reprojectPoints[i].y; + dx = rx - imagePoints[i].x; + dy = ry - imagePoints[i].y; + + meanDx += dx; + meanDy += dy; + + dx = fabs(dx); + dy = fabs(dy); + + if( dx > maxDx ) + maxDx = dx; + + if( dy > maxDy ) + maxDy = dy; + i++; + } + } + + meanDx /= numImages * etalonSize.width * etalonSize.height; + meanDy /= numImages * etalonSize.width * etalonSize.height; + + /* ========= Compare parameters ========= */ + + /* ----- Compare focal lengths ----- */ + code = compare(cameraMatrix+0,&goodFcx,1,0.1,"fx"); + if( code < 0 ) + goto _exit_; + + code = compare(cameraMatrix+4,&goodFcy,1,0.1,"fy"); + if( code < 0 ) + goto _exit_; + + /* ----- Compare principal points ----- */ + code = compare(cameraMatrix+2,&goodCx,1,0.1,"cx"); + if( code < 0 ) + goto _exit_; + + code = compare(cameraMatrix+5,&goodCy,1,0.1,"cy"); + if( code < 0 ) + goto _exit_; + + /* ----- Compare distortion ----- */ + code = compare(distortion,goodDistortion,4,0.1,"[k1,k2,p1,p2]"); + if( code < 0 ) + goto _exit_; + + /* ----- Compare rot matrixs ----- */ + code = compare(rotMatrs,goodRotMatrs, 9*numImages,0.05,"rotation matrices"); + if( code < 0 ) + goto _exit_; + + /* ----- Compare rot matrixs ----- */ + code = compare(transVects,goodTransVects, 3*numImages,0.1,"translation vectors"); + if( code < 0 ) + goto _exit_; + + if( maxDx > 1.0 ) + { + ts->printf( cvtest::TS::LOG, + "Error in reprojection maxDx=%f > 1.0\n",maxDx); + code = cvtest::TS::FAIL_BAD_ACCURACY; goto _exit_; + } + + if( maxDy > 1.0 ) + { + ts->printf( cvtest::TS::LOG, + "Error in reprojection maxDy=%f > 1.0\n",maxDy); + code = cvtest::TS::FAIL_BAD_ACCURACY; goto _exit_; + } + + progress = update_progress( progress, currTest, numTests, 0 ); + + cvFree(&imagePoints); + cvFree(&objectPoints); + cvFree(&reprojectPoints); + cvFree(&numbers); + + cvFree(&transVects); + cvFree(&rotMatrs); + cvFree(&goodTransVects); + cvFree(&goodRotMatrs); + + fclose(file); + file = 0; + } + +_exit_: + + if( file ) + fclose(file); + + if( datafile ) + fclose(datafile); + + /* Free all allocated memory */ + cvFree(&imagePoints); + cvFree(&objectPoints); + cvFree(&reprojectPoints); + cvFree(&numbers); + + cvFree(&transVects); + cvFree(&rotMatrs); + cvFree(&goodTransVects); + cvFree(&goodRotMatrs); + + if( code < 0 ) + ts->set_failed_test_info( code ); +} + +// --------------------------------- CV_CameraCalibrationTest_C -------------------------------------------- + +class CV_CameraCalibrationTest_C : public CV_CameraCalibrationTest +{ +public: + CV_CameraCalibrationTest_C(){} +protected: + virtual void calibrate( int imageCount, int* pointCounts, + CvSize imageSize, CvPoint2D64f* imagePoints, CvPoint3D64f* objectPoints, + double* distortionCoeffs, double* cameraMatrix, double* translationVectors, + double* rotationMatrices, int flags ); + virtual void project( int pointCount, CvPoint3D64f* objectPoints, + double* rotationMatrix, double* translationVector, + double* cameraMatrix, double* distortion, CvPoint2D64f* imagePoints ); +}; + +void CV_CameraCalibrationTest_C::calibrate( int imageCount, int* pointCounts, + CvSize imageSize, CvPoint2D64f* imagePoints, CvPoint3D64f* objectPoints, + double* distortionCoeffs, double* cameraMatrix, double* translationVectors, + double* rotationMatrices, int flags ) +{ + int i, total = 0; + for( i = 0; i < imageCount; i++ ) + total += pointCounts[i]; + + CvMat _objectPoints = cvMat(1, total, CV_64FC3, objectPoints); + CvMat _imagePoints = cvMat(1, total, CV_64FC2, imagePoints); + CvMat _pointCounts = cvMat(1, imageCount, CV_32S, pointCounts); + CvMat _cameraMatrix = cvMat(3, 3, CV_64F, cameraMatrix); + CvMat _distCoeffs = cvMat(4, 1, CV_64F, distortionCoeffs); + CvMat _rotationMatrices = cvMat(imageCount, 9, CV_64F, rotationMatrices); + CvMat _translationVectors = cvMat(imageCount, 3, CV_64F, translationVectors); + + cvCalibrateCamera2(&_objectPoints, &_imagePoints, &_pointCounts, imageSize, + &_cameraMatrix, &_distCoeffs, &_rotationMatrices, &_translationVectors, + flags); +} + +void CV_CameraCalibrationTest_C::project( int pointCount, CvPoint3D64f* objectPoints, + double* rotationMatrix, double* translationVector, + double* cameraMatrix, double* distortion, CvPoint2D64f* imagePoints ) +{ + CvMat _objectPoints = cvMat(1, pointCount, CV_64FC3, objectPoints); + CvMat _imagePoints = cvMat(1, pointCount, CV_64FC2, imagePoints); + CvMat _cameraMatrix = cvMat(3, 3, CV_64F, cameraMatrix); + CvMat _distCoeffs = cvMat(4, 1, CV_64F, distortion); + CvMat _rotationMatrix = cvMat(3, 3, CV_64F, rotationMatrix); + CvMat _translationVector = cvMat(1, 3, CV_64F, translationVector); + + cvProjectPoints2(&_objectPoints, &_rotationMatrix, &_translationVector, &_cameraMatrix, &_distCoeffs, &_imagePoints); +} + +// --------------------------------- CV_CameraCalibrationTest_CPP -------------------------------------------- + +class CV_CameraCalibrationTest_CPP : public CV_CameraCalibrationTest +{ +public: + CV_CameraCalibrationTest_CPP(){} +protected: + virtual void calibrate( int imageCount, int* pointCounts, + CvSize imageSize, CvPoint2D64f* imagePoints, CvPoint3D64f* objectPoints, + double* distortionCoeffs, double* cameraMatrix, double* translationVectors, + double* rotationMatrices, int flags ); + virtual void project( int pointCount, CvPoint3D64f* objectPoints, + double* rotationMatrix, double* translationVector, + double* cameraMatrix, double* distortion, CvPoint2D64f* imagePoints ); +}; + +void CV_CameraCalibrationTest_CPP::calibrate( int imageCount, int* pointCounts, + CvSize _imageSize, CvPoint2D64f* _imagePoints, CvPoint3D64f* _objectPoints, + double* _distortionCoeffs, double* _cameraMatrix, double* translationVectors, + double* rotationMatrices, int flags ) +{ + vector > objectPoints( imageCount ); + vector > imagePoints( imageCount ); + Size imageSize = _imageSize; + Mat cameraMatrix, distCoeffs(1,4,CV_64F,Scalar::all(0)); + vector rvecs, tvecs; + + CvPoint3D64f* op = _objectPoints; + CvPoint2D64f* ip = _imagePoints; + vector >::iterator objectPointsIt = objectPoints.begin(); + vector >::iterator imagePointsIt = imagePoints.begin(); + for( int i = 0; i < imageCount; ++objectPointsIt, ++imagePointsIt, i++ ) + { + int num = pointCounts[i]; + objectPointsIt->resize( num ); + imagePointsIt->resize( num ); + vector::iterator oIt = objectPointsIt->begin(); + vector::iterator iIt = imagePointsIt->begin(); + for( int j = 0; j < num; ++oIt, ++iIt, j++, op++, ip++) + { + oIt->x = (float)op->x, oIt->y = (float)op->y, oIt->z = (float)op->z; + iIt->x = (float)ip->x, iIt->y = (float)ip->y; + } + } + + calibrateCamera( objectPoints, + imagePoints, + imageSize, + cameraMatrix, + distCoeffs, + rvecs, + tvecs, + flags ); + + assert( cameraMatrix.type() == CV_64FC1 ); + memcpy( _cameraMatrix, cameraMatrix.data, 9*sizeof(double) ); + + assert( cameraMatrix.type() == CV_64FC1 ); + memcpy( _distortionCoeffs, distCoeffs.data, 4*sizeof(double) ); + + vector::iterator rvecsIt = rvecs.begin(); + vector::iterator tvecsIt = tvecs.begin(); + double *rm = rotationMatrices, + *tm = translationVectors; + assert( rvecsIt->type() == CV_64FC1 ); + assert( tvecsIt->type() == CV_64FC1 ); + for( int i = 0; i < imageCount; ++rvecsIt, ++tvecsIt, i++, rm+=9, tm+=3 ) + { + Mat r9( 3, 3, CV_64FC1 ); + Rodrigues( *rvecsIt, r9 ); + memcpy( rm, r9.data, 9*sizeof(double) ); + memcpy( tm, tvecsIt->data, 3*sizeof(double) ); + } +} + +void CV_CameraCalibrationTest_CPP::project( int pointCount, CvPoint3D64f* _objectPoints, + double* rotationMatrix, double* translationVector, + double* _cameraMatrix, double* distortion, CvPoint2D64f* _imagePoints ) +{ + Mat objectPoints( pointCount, 3, CV_64FC1, _objectPoints ); + Mat rmat( 3, 3, CV_64FC1, rotationMatrix ), + rvec( 1, 3, CV_64FC1 ), + tvec( 1, 3, CV_64FC1, translationVector ); + Mat cameraMatrix( 3, 3, CV_64FC1, _cameraMatrix ); + Mat distCoeffs( 1, 4, CV_64FC1, distortion ); + vector imagePoints; + Rodrigues( rmat, rvec ); + + objectPoints.convertTo( objectPoints, CV_32FC1 ); + projectPoints( objectPoints, rvec, tvec, + cameraMatrix, distCoeffs, imagePoints ); + vector::const_iterator it = imagePoints.begin(); + for( int i = 0; it != imagePoints.end(); ++it, i++ ) + { + _imagePoints[i] = cvPoint2D64f( it->x, it->y ); + } +} + + +//----------------------------------------- CV_CalibrationMatrixValuesTest -------------------------------- + +class CV_CalibrationMatrixValuesTest : public cvtest::BaseTest +{ +public: + CV_CalibrationMatrixValuesTest() {} +protected: + void run(int); + virtual void calibMatrixValues( const Mat& cameraMatrix, Size imageSize, + double apertureWidth, double apertureHeight, double& fovx, double& fovy, double& focalLength, + Point2d& principalPoint, double& aspectRatio ) = 0; +}; + +void CV_CalibrationMatrixValuesTest::run(int) +{ + int code = cvtest::TS::OK; + const double fcMinVal = 1e-5; + const double fcMaxVal = 1000; + const double apertureMaxVal = 0.01; + + RNG rng = ts->get_rng(); + + double fx, fy, cx, cy, nx, ny; + Mat cameraMatrix( 3, 3, CV_64FC1 ); + cameraMatrix.setTo( Scalar(0) ); + fx = cameraMatrix.at(0,0) = rng.uniform( fcMinVal, fcMaxVal ); + fy = cameraMatrix.at(1,1) = rng.uniform( fcMinVal, fcMaxVal ); + cx = cameraMatrix.at(0,2) = rng.uniform( fcMinVal, fcMaxVal ); + cy = cameraMatrix.at(1,2) = rng.uniform( fcMinVal, fcMaxVal ); + cameraMatrix.at(2,2) = 1; + + Size imageSize( 600, 400 ); + + double apertureWidth = (double)rng * apertureMaxVal, + apertureHeight = (double)rng * apertureMaxVal; + + double fovx, fovy, focalLength, aspectRatio, + goodFovx, goodFovy, goodFocalLength, goodAspectRatio; + Point2d principalPoint, goodPrincipalPoint; + + + calibMatrixValues( cameraMatrix, imageSize, apertureWidth, apertureHeight, + fovx, fovy, focalLength, principalPoint, aspectRatio ); + + // calculate calibration matrix values + goodAspectRatio = fy / fx; + + if( apertureWidth != 0.0 && apertureHeight != 0.0 ) + { + nx = imageSize.width / apertureWidth; + ny = imageSize.height / apertureHeight; + } + else + { + nx = 1.0; + ny = goodAspectRatio; + } + + goodFovx = 2 * atan( imageSize.width / (2 * fx)) * 180.0 / CV_PI; + goodFovy = 2 * atan( imageSize.height / (2 * fy)) * 180.0 / CV_PI; + + goodFocalLength = fx / nx; + + goodPrincipalPoint.x = cx / nx; + goodPrincipalPoint.y = cy / ny; + + // check results + if( fabs(fovx - goodFovx) > FLT_EPSILON ) + { + ts->printf( cvtest::TS::LOG, "bad fovx (real=%f, good = %f\n", fovx, goodFovx ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + if( fabs(fovy - goodFovy) > FLT_EPSILON ) + { + ts->printf( cvtest::TS::LOG, "bad fovy (real=%f, good = %f\n", fovy, goodFovy ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + if( fabs(focalLength - goodFocalLength) > FLT_EPSILON ) + { + ts->printf( cvtest::TS::LOG, "bad focalLength (real=%f, good = %f\n", focalLength, goodFocalLength ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + if( fabs(aspectRatio - goodAspectRatio) > FLT_EPSILON ) + { + ts->printf( cvtest::TS::LOG, "bad aspectRatio (real=%f, good = %f\n", aspectRatio, goodAspectRatio ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + if( norm( principalPoint - goodPrincipalPoint ) > FLT_EPSILON ) + { + ts->printf( cvtest::TS::LOG, "bad principalPoint\n" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + +_exit_: + RNG& _rng = ts->get_rng(); + _rng = rng; + ts->set_failed_test_info( code ); +} + +//----------------------------------------- CV_CalibrationMatrixValuesTest_C -------------------------------- + +class CV_CalibrationMatrixValuesTest_C : public CV_CalibrationMatrixValuesTest +{ +public: + CV_CalibrationMatrixValuesTest_C(){} +protected: + virtual void calibMatrixValues( const Mat& cameraMatrix, Size imageSize, + double apertureWidth, double apertureHeight, double& fovx, double& fovy, double& focalLength, + Point2d& principalPoint, double& aspectRatio ); +}; + +void CV_CalibrationMatrixValuesTest_C::calibMatrixValues( const Mat& _cameraMatrix, Size imageSize, + double apertureWidth, double apertureHeight, + double& fovx, double& fovy, double& focalLength, + Point2d& principalPoint, double& aspectRatio ) +{ + CvMat cameraMatrix = _cameraMatrix; + CvPoint2D64f pp; + cvCalibrationMatrixValues( &cameraMatrix, imageSize, apertureWidth, apertureHeight, + &fovx, &fovy, &focalLength, &pp, &aspectRatio ); + principalPoint.x = pp.x; + principalPoint.y = pp.y; +} + + +//----------------------------------------- CV_CalibrationMatrixValuesTest_CPP -------------------------------- + +class CV_CalibrationMatrixValuesTest_CPP : public CV_CalibrationMatrixValuesTest +{ +public: + CV_CalibrationMatrixValuesTest_CPP() {} +protected: + virtual void calibMatrixValues( const Mat& cameraMatrix, Size imageSize, + double apertureWidth, double apertureHeight, double& fovx, double& fovy, double& focalLength, + Point2d& principalPoint, double& aspectRatio ); +}; + +void CV_CalibrationMatrixValuesTest_CPP::calibMatrixValues( const Mat& cameraMatrix, Size imageSize, + double apertureWidth, double apertureHeight, + double& fovx, double& fovy, double& focalLength, + Point2d& principalPoint, double& aspectRatio ) +{ + calibrationMatrixValues( cameraMatrix, imageSize, apertureWidth, apertureHeight, + fovx, fovy, focalLength, principalPoint, aspectRatio ); +} + + +//----------------------------------------- CV_ProjectPointsTest -------------------------------- +void calcdfdx( const vector >& leftF, const vector >& rightF, double eps, Mat& dfdx ) +{ + const int fdim = 2; + CV_Assert( !leftF.empty() && !rightF.empty() && !leftF[0].empty() && !rightF[0].empty() ); + CV_Assert( leftF[0].size() == rightF[0].size() ); + CV_Assert( fabs(eps) > std::numeric_limits::epsilon() ); + int fcount = (int)leftF[0].size(), xdim = (int)leftF.size(); + + dfdx.create( fcount*fdim, xdim, CV_64FC1 ); + + vector >::const_iterator arrLeftIt = leftF.begin(); + vector >::const_iterator arrRightIt = rightF.begin(); + for( int xi = 0; xi < xdim; xi++, ++arrLeftIt, ++arrRightIt ) + { + CV_Assert( (int)arrLeftIt->size() == fcount ); + CV_Assert( (int)arrRightIt->size() == fcount ); + vector::const_iterator lIt = arrLeftIt->begin(); + vector::const_iterator rIt = arrRightIt->begin(); + for( int fi = 0; fi < dfdx.rows; fi+=fdim, ++lIt, ++rIt ) + { + dfdx.at(fi, xi ) = 0.5 * ((double)(rIt->x - lIt->x)) / eps; + dfdx.at(fi+1, xi ) = 0.5 * ((double)(rIt->y - lIt->y)) / eps; + } + } +} + +class CV_ProjectPointsTest : public cvtest::BaseTest +{ +public: + CV_ProjectPointsTest() {} +protected: + void run(int); + virtual void project( const Mat& objectPoints, + const Mat& rvec, const Mat& tvec, + const Mat& cameraMatrix, + const Mat& distCoeffs, + vector& imagePoints, + Mat& dpdrot, Mat& dpdt, Mat& dpdf, + Mat& dpdc, Mat& dpddist, + double aspectRatio=0 ) = 0; +}; + +void CV_ProjectPointsTest::run(int) +{ + //typedef float matType; + + int code = cvtest::TS::OK; + const int pointCount = 100; + + const float zMinVal = 10.0f, zMaxVal = 100.0f, + rMinVal = -0.3f, rMaxVal = 0.3f, + tMinVal = -2.0f, tMaxVal = 2.0f; + + const float imgPointErr = 1e-3f, + dEps = 1e-3f; + + double err; + + Size imgSize( 600, 800 ); + Mat_ objPoints( pointCount, 3), rvec( 1, 3), rmat, tvec( 1, 3 ), cameraMatrix( 3, 3 ), distCoeffs( 1, 4 ), + leftRvec, rightRvec, leftTvec, rightTvec, leftCameraMatrix, rightCameraMatrix, leftDistCoeffs, rightDistCoeffs; + + RNG rng = ts->get_rng(); + + // generate data + cameraMatrix << 300.f, 0.f, imgSize.width/2.f, + 0.f, 300.f, imgSize.height/2.f, + 0.f, 0.f, 1.f; + distCoeffs << 0.1, 0.01, 0.001, 0.001; + + rvec(0,0) = rng.uniform( rMinVal, rMaxVal ); + rvec(0,1) = rng.uniform( rMinVal, rMaxVal ); + rvec(0,2) = rng.uniform( rMinVal, rMaxVal ); + Rodrigues( rvec, rmat ); + + tvec(0,0) = rng.uniform( tMinVal, tMaxVal ); + tvec(0,1) = rng.uniform( tMinVal, tMaxVal ); + tvec(0,2) = rng.uniform( tMinVal, tMaxVal ); + + for( int y = 0; y < objPoints.rows; y++ ) + { + Mat point(1, 3, CV_32FC1, objPoints.ptr(y) ); + float z = rng.uniform( zMinVal, zMaxVal ); + point.at(0,2) = z; + point.at(0,0) = (rng.uniform(2.f,(float)(imgSize.width-2)) - cameraMatrix(0,2)) / cameraMatrix(0,0) * z; + point.at(0,1) = (rng.uniform(2.f,(float)(imgSize.height-2)) - cameraMatrix(1,2)) / cameraMatrix(1,1) * z; + point = (point - tvec) * rmat; + } + + vector imgPoints; + vector > leftImgPoints; + vector > rightImgPoints; + Mat dpdrot, dpdt, dpdf, dpdc, dpddist, + valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist; + + project( objPoints, rvec, tvec, cameraMatrix, distCoeffs, + imgPoints, dpdrot, dpdt, dpdf, dpdc, dpddist, 0 ); + + // calculate and check image points + assert( (int)imgPoints.size() == pointCount ); + vector::const_iterator it = imgPoints.begin(); + for( int i = 0; i < pointCount; i++, ++it ) + { + Point3d p( objPoints(i,0), objPoints(i,1), objPoints(i,2) ); + double z = p.x*rmat(2,0) + p.y*rmat(2,1) + p.z*rmat(2,2) + tvec(0,2), + x = (p.x*rmat(0,0) + p.y*rmat(0,1) + p.z*rmat(0,2) + tvec(0,0)) / z, + y = (p.x*rmat(1,0) + p.y*rmat(1,1) + p.z*rmat(1,2) + tvec(0,1)) / z, + r2 = x*x + y*y, + r4 = r2*r2; + Point2f validImgPoint; + double a1 = 2*x*y, + a2 = r2 + 2*x*x, + a3 = r2 + 2*y*y, + cdist = 1+distCoeffs(0,0)*r2+distCoeffs(0,1)*r4; + validImgPoint.x = static_cast((double)cameraMatrix(0,0)*(x*cdist + (double)distCoeffs(0,2)*a1 + (double)distCoeffs(0,3)*a2) + + (double)cameraMatrix(0,2)); + validImgPoint.y = static_cast((double)cameraMatrix(1,1)*(y*cdist + (double)distCoeffs(0,2)*a3 + distCoeffs(0,3)*a1) + + (double)cameraMatrix(1,2)); + + Point2f ssdfp = *it; + if( fabs(it->x - validImgPoint.x) > imgPointErr || + fabs(it->y - validImgPoint.y) > imgPointErr ) + { + ts->printf( cvtest::TS::LOG, "bad image point\n" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + } + + // check derivatives + // 1. rotation + leftImgPoints.resize(3); + rightImgPoints.resize(3); + for( int i = 0; i < 3; i++ ) + { + rvec.copyTo( leftRvec ); leftRvec(0,i) -= dEps; + project( objPoints, leftRvec, tvec, cameraMatrix, distCoeffs, + leftImgPoints[i], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + rvec.copyTo( rightRvec ); rightRvec(0,i) += dEps; + project( objPoints, rightRvec, tvec, cameraMatrix, distCoeffs, + rightImgPoints[i], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + } + calcdfdx( leftImgPoints, rightImgPoints, dEps, valDpdrot ); + err = norm( dpdrot, valDpdrot, NORM_INF ); + if( err > 3 ) + { + ts->printf( cvtest::TS::LOG, "bad dpdrot: too big difference = %g\n", err ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + + // 2. translation + for( int i = 0; i < 3; i++ ) + { + tvec.copyTo( leftTvec ); leftTvec(0,i) -= dEps; + project( objPoints, rvec, leftTvec, cameraMatrix, distCoeffs, + leftImgPoints[i], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + tvec.copyTo( rightTvec ); rightTvec(0,i) += dEps; + project( objPoints, rvec, rightTvec, cameraMatrix, distCoeffs, + rightImgPoints[i], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + } + calcdfdx( leftImgPoints, rightImgPoints, dEps, valDpdt ); + if( norm( dpdt, valDpdt, NORM_INF ) > 0.2 ) + { + ts->printf( cvtest::TS::LOG, "bad dpdtvec\n" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + + // 3. camera matrix + // 3.1. focus + leftImgPoints.resize(2); + rightImgPoints.resize(2); + cameraMatrix.copyTo( leftCameraMatrix ); leftCameraMatrix(0,0) -= dEps; + project( objPoints, rvec, tvec, leftCameraMatrix, distCoeffs, + leftImgPoints[0], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + cameraMatrix.copyTo( leftCameraMatrix ); leftCameraMatrix(1,1) -= dEps; + project( objPoints, rvec, tvec, leftCameraMatrix, distCoeffs, + leftImgPoints[1], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + cameraMatrix.copyTo( rightCameraMatrix ); rightCameraMatrix(0,0) += dEps; + project( objPoints, rvec, tvec, rightCameraMatrix, distCoeffs, + rightImgPoints[0], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + cameraMatrix.copyTo( rightCameraMatrix ); rightCameraMatrix(1,1) += dEps; + project( objPoints, rvec, tvec, rightCameraMatrix, distCoeffs, + rightImgPoints[1], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + calcdfdx( leftImgPoints, rightImgPoints, dEps, valDpdf ); + if ( norm( dpdf, valDpdf ) > 0.2 ) + { + ts->printf( cvtest::TS::LOG, "bad dpdf\n" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + // 3.2. principal point + leftImgPoints.resize(2); + rightImgPoints.resize(2); + cameraMatrix.copyTo( leftCameraMatrix ); leftCameraMatrix(0,2) -= dEps; + project( objPoints, rvec, tvec, leftCameraMatrix, distCoeffs, + leftImgPoints[0], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + cameraMatrix.copyTo( leftCameraMatrix ); leftCameraMatrix(1,2) -= dEps; + project( objPoints, rvec, tvec, leftCameraMatrix, distCoeffs, + leftImgPoints[1], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + cameraMatrix.copyTo( rightCameraMatrix ); rightCameraMatrix(0,2) += dEps; + project( objPoints, rvec, tvec, rightCameraMatrix, distCoeffs, + rightImgPoints[0], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + cameraMatrix.copyTo( rightCameraMatrix ); rightCameraMatrix(1,2) += dEps; + project( objPoints, rvec, tvec, rightCameraMatrix, distCoeffs, + rightImgPoints[1], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + calcdfdx( leftImgPoints, rightImgPoints, dEps, valDpdc ); + if ( norm( dpdc, valDpdc ) > 0.2 ) + { + ts->printf( cvtest::TS::LOG, "bad dpdc\n" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + + // 4. distortion + leftImgPoints.resize(distCoeffs.cols); + rightImgPoints.resize(distCoeffs.cols); + for( int i = 0; i < distCoeffs.cols; i++ ) + { + distCoeffs.copyTo( leftDistCoeffs ); leftDistCoeffs(0,i) -= dEps; + project( objPoints, rvec, tvec, cameraMatrix, leftDistCoeffs, + leftImgPoints[i], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + distCoeffs.copyTo( rightDistCoeffs ); rightDistCoeffs(0,i) += dEps; + project( objPoints, rvec, tvec, cameraMatrix, rightDistCoeffs, + rightImgPoints[i], valDpdrot, valDpdt, valDpdf, valDpdc, valDpddist, 0 ); + } + calcdfdx( leftImgPoints, rightImgPoints, dEps, valDpddist ); + if( norm( dpddist, valDpddist ) > 0.3 ) + { + ts->printf( cvtest::TS::LOG, "bad dpddist\n" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + +_exit_: + RNG& _rng = ts->get_rng(); + _rng = rng; + ts->set_failed_test_info( code ); +} + +//----------------------------------------- CV_ProjectPointsTest_C -------------------------------- +class CV_ProjectPointsTest_C : public CV_ProjectPointsTest +{ +public: + CV_ProjectPointsTest_C() {} +protected: + virtual void project( const Mat& objectPoints, + const Mat& rvec, const Mat& tvec, + const Mat& cameraMatrix, + const Mat& distCoeffs, + vector& imagePoints, + Mat& dpdrot, Mat& dpdt, Mat& dpdf, + Mat& dpdc, Mat& dpddist, + double aspectRatio=0 ); +}; + +void CV_ProjectPointsTest_C::project( const Mat& opoints, const Mat& rvec, const Mat& tvec, + const Mat& cameraMatrix, const Mat& distCoeffs, vector& ipoints, + Mat& dpdrot, Mat& dpdt, Mat& dpdf, Mat& dpdc, Mat& dpddist, double aspectRatio) +{ + int npoints = opoints.cols*opoints.rows*opoints.channels()/3; + ipoints.resize(npoints); + dpdrot.create(npoints*2, 3, CV_64F); + dpdt.create(npoints*2, 3, CV_64F); + dpdf.create(npoints*2, 2, CV_64F); + dpdc.create(npoints*2, 2, CV_64F); + dpddist.create(npoints*2, distCoeffs.rows + distCoeffs.cols - 1, CV_64F); + CvMat _objectPoints = opoints, _imagePoints = Mat(ipoints); + CvMat _rvec = rvec, _tvec = tvec, _cameraMatrix = cameraMatrix, _distCoeffs = distCoeffs; + CvMat _dpdrot = dpdrot, _dpdt = dpdt, _dpdf = dpdf, _dpdc = dpdc, _dpddist = dpddist; + + cvProjectPoints2( &_objectPoints, &_rvec, &_tvec, &_cameraMatrix, &_distCoeffs, + &_imagePoints, &_dpdrot, &_dpdt, &_dpdf, &_dpdc, &_dpddist, aspectRatio ); +} + + +//----------------------------------------- CV_ProjectPointsTest_CPP -------------------------------- +class CV_ProjectPointsTest_CPP : public CV_ProjectPointsTest +{ +public: + CV_ProjectPointsTest_CPP() {} +protected: + virtual void project( const Mat& objectPoints, + const Mat& rvec, const Mat& tvec, + const Mat& cameraMatrix, + const Mat& distCoeffs, + vector& imagePoints, + Mat& dpdrot, Mat& dpdt, Mat& dpdf, + Mat& dpdc, Mat& dpddist, + double aspectRatio=0 ); +}; + +void CV_ProjectPointsTest_CPP::project( const Mat& objectPoints, const Mat& rvec, const Mat& tvec, + const Mat& cameraMatrix, const Mat& distCoeffs, vector& imagePoints, + Mat& dpdrot, Mat& dpdt, Mat& dpdf, Mat& dpdc, Mat& dpddist, double aspectRatio) +{ + projectPoints( objectPoints, rvec, tvec, cameraMatrix, distCoeffs, imagePoints, + dpdrot, dpdt, dpdf, dpdc, dpddist, aspectRatio ); +} + +///////////////////////////////// Stereo Calibration ///////////////////////////////////// + +class CV_StereoCalibrationTest : public cvtest::BaseTest +{ +public: + CV_StereoCalibrationTest(); + ~CV_StereoCalibrationTest(); + void clear(); +protected: + bool checkPandROI( int test_case_idx, + const Mat& M, const Mat& D, const Mat& R, + const Mat& P, Size imgsize, Rect roi ); + + // covers of tested functions + virtual double calibrateStereoCamera( const vector >& objectPoints, + const vector >& imagePoints1, + const vector >& imagePoints2, + Mat& cameraMatrix1, Mat& distCoeffs1, + Mat& cameraMatrix2, Mat& distCoeffs2, + Size imageSize, Mat& R, Mat& T, + Mat& E, Mat& F, TermCriteria criteria, int flags ) = 0; + virtual void rectify( const Mat& cameraMatrix1, const Mat& distCoeffs1, + const Mat& cameraMatrix2, const Mat& distCoeffs2, + Size imageSize, const Mat& R, const Mat& T, + Mat& R1, Mat& R2, Mat& P1, Mat& P2, Mat& Q, + double alpha, Size newImageSize, + Rect* validPixROI1, Rect* validPixROI2, int flags ) = 0; + virtual bool rectifyUncalibrated( const Mat& points1, + const Mat& points2, const Mat& F, Size imgSize, + Mat& H1, Mat& H2, double threshold=5 ) = 0; + + void run(int); +}; + + +CV_StereoCalibrationTest::CV_StereoCalibrationTest() +{ +} + + +CV_StereoCalibrationTest::~CV_StereoCalibrationTest() +{ + clear(); +} + +void CV_StereoCalibrationTest::clear() +{ + cvtest::BaseTest::clear(); +} + +bool CV_StereoCalibrationTest::checkPandROI( int test_case_idx, const Mat& M, const Mat& D, const Mat& R, + const Mat& P, Size imgsize, Rect roi ) +{ + const double eps = 0.05; + const int N = 21; + int x, y, k; + vector pts, upts; + + // step 1. check that all the original points belong to the destination image + for( y = 0; y < N; y++ ) + for( x = 0; x < N; x++ ) + pts.push_back(Point2f((float)x*imgsize.width/(N-1), (float)y*imgsize.height/(N-1))); + + undistortPoints(Mat(pts), upts, M, D, R, P ); + for( k = 0; k < N*N; k++ ) + if( upts[k].x < -imgsize.width*eps || upts[k].x > imgsize.width*(1+eps) || + upts[k].y < -imgsize.height*eps || upts[k].y > imgsize.height*(1+eps) ) + { + ts->printf(cvtest::TS::LOG, "Test #%d. The point (%g, %g) was mapped to (%g, %g) which is out of image\n", + test_case_idx, pts[k].x, pts[k].y, upts[k].x, upts[k].y); + return false; + } + + // step 2. check that all the points inside ROI belong to the original source image + Mat temp(imgsize, CV_8U), utemp, map1, map2; + temp = Scalar::all(1); + initUndistortRectifyMap(M, D, R, P, imgsize, CV_16SC2, map1, map2); + remap(temp, utemp, map1, map2, INTER_LINEAR); + + if(roi.x < 0 || roi.y < 0 || roi.x + roi.width > imgsize.width || roi.y + roi.height > imgsize.height) + { + ts->printf(cvtest::TS::LOG, "Test #%d. The ROI=(%d, %d, %d, %d) is outside of the imge rectangle\n", + test_case_idx, roi.x, roi.y, roi.width, roi.height); + return false; + } + double s = sum(utemp(roi))[0]; + if( s > roi.area() || roi.area() - s > roi.area()*(1-eps) ) + { + ts->printf(cvtest::TS::LOG, "Test #%d. The ratio of black pixels inside the valid ROI (~%g%%) is too large\n", + test_case_idx, s*100./roi.area()); + return false; + } + + return true; +} + +void CV_StereoCalibrationTest::run( int ) +{ + const int ntests = 1; + const double maxReprojErr = 2; + const double maxScanlineDistErr_c = 3; + const double maxScanlineDistErr_uc = 4; + FILE* f = 0; + + for(int testcase = 1; testcase <= ntests; testcase++) + { + char filepath[1000]; + char buf[1000]; + sprintf( filepath, "%sstereo/case%d/stereo_calib.txt", ts->get_data_path().c_str(), testcase ); + f = fopen(filepath, "rt"); + Size patternSize; + vector imglist; + + if( !f || !fgets(buf, sizeof(buf)-3, f) || sscanf(buf, "%d%d", &patternSize.width, &patternSize.height) != 2 ) + { + ts->printf( cvtest::TS::LOG, "The file %s can not be opened or has invalid content\n", filepath ); + ts->set_failed_test_info( f ? cvtest::TS::FAIL_INVALID_TEST_DATA : cvtest::TS::FAIL_MISSING_TEST_DATA ); + return; + } + + for(;;) + { + if( !fgets( buf, sizeof(buf)-3, f )) + break; + size_t len = strlen(buf); + while( len > 0 && isspace(buf[len-1])) + buf[--len] = '\0'; + if( buf[0] == '#') + continue; + sprintf(filepath, "%sstereo/case%d/%s", ts->get_data_path().c_str(), testcase, buf ); + imglist.push_back(string(filepath)); + } + fclose(f); + + if( imglist.size() == 0 || imglist.size() % 2 != 0 ) + { + ts->printf( cvtest::TS::LOG, "The number of images is 0 or an odd number in the case #%d\n", testcase ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + int nframes = (int)(imglist.size()/2); + int npoints = patternSize.width*patternSize.height; + vector > objpt(nframes); + vector > imgpt1(nframes); + vector > imgpt2(nframes); + Size imgsize; + int total = 0; + + for( int i = 0; i < nframes; i++ ) + { + Mat left = imread(imglist[i*2]); + Mat right = imread(imglist[i*2+1]); + if(!left.data || !right.data) + { + ts->printf( cvtest::TS::LOG, "Can not load images %s and %s, testcase %d\n", + imglist[i*2].c_str(), imglist[i*2+1].c_str(), testcase ); + ts->set_failed_test_info( cvtest::TS::FAIL_MISSING_TEST_DATA ); + return; + } + imgsize = left.size(); + bool found1 = findChessboardCorners(left, patternSize, imgpt1[i]); + bool found2 = findChessboardCorners(right, patternSize, imgpt2[i]); + if(!found1 || !found2) + { + ts->printf( cvtest::TS::LOG, "The function could not detect boards on the images %s and %s, testcase %d\n", + imglist[i*2].c_str(), imglist[i*2+1].c_str(), testcase ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + total += (int)imgpt1[i].size(); + for( int j = 0; j < npoints; j++ ) + objpt[i].push_back(Point3f((float)(j%patternSize.width), (float)(j/patternSize.width), 0.f)); + } + + // rectify (calibrated) + Mat M1 = Mat::eye(3,3,CV_64F), M2 = Mat::eye(3,3,CV_64F), D1(5,1,CV_64F), D2(5,1,CV_64F), R, T, E, F; + M1.at(0,2) = M2.at(0,2)=(imgsize.width-1)*0.5; + M1.at(1,2) = M2.at(1,2)=(imgsize.height-1)*0.5; + D1 = Scalar::all(0); + D2 = Scalar::all(0); + double err = calibrateStereoCamera(objpt, imgpt1, imgpt2, M1, D1, M2, D2, imgsize, R, T, E, F, + TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, 30, 1e-6), + CV_CALIB_SAME_FOCAL_LENGTH + //+ CV_CALIB_FIX_ASPECT_RATIO + + CV_CALIB_FIX_PRINCIPAL_POINT + + CV_CALIB_ZERO_TANGENT_DIST + + CV_CALIB_FIX_K3 + + CV_CALIB_FIX_K4 + CV_CALIB_FIX_K5 //+ CV_CALIB_FIX_K6 + ); + err /= nframes*npoints; + if( err > maxReprojErr ) + { + ts->printf( cvtest::TS::LOG, "The average reprojection error is too big (=%g), testcase %d\n", err, testcase); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + Mat R1, R2, P1, P2, Q; + Rect roi1, roi2; + rectify(M1, D1, M2, D2, imgsize, R, T, R1, R2, P1, P2, Q, 1, imgsize, &roi1, &roi2, 0); + Mat eye33 = Mat::eye(3,3,CV_64F); + Mat R1t = R1.t(), R2t = R2.t(); + + if( norm(R1t*R1 - eye33) > 0.01 || + norm(R2t*R2 - eye33) > 0.01 || + abs(determinant(F)) > 0.01) + { + ts->printf( cvtest::TS::LOG, "The computed (by rectify) R1 and R2 are not orthogonal," + "or the computed (by calibrate) F is not singular, testcase %d\n", testcase); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + if(!checkPandROI(testcase, M1, D1, R1, P1, imgsize, roi1)) + { + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + return; + } + + if(!checkPandROI(testcase, M2, D2, R2, P2, imgsize, roi2)) + { + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + return; + } + + // rectifyUncalibrated + CV_Assert( imgpt1.size() == imgpt2.size() ); + Mat _imgpt1( total, 1, CV_32FC2 ), _imgpt2( total, 1, CV_32FC2 ); + vector >::const_iterator iit1 = imgpt1.begin(); + vector >::const_iterator iit2 = imgpt2.begin(); + for( int pi = 0; iit1 != imgpt1.end(); ++iit1, ++iit2 ) + { + vector::const_iterator pit1 = iit1->begin(); + vector::const_iterator pit2 = iit2->begin(); + CV_Assert( iit1->size() == iit2->size() ); + for( ; pit1 != iit1->end(); ++pit1, ++pit2, pi++ ) + { + _imgpt1.at(pi,0) = Point2f( pit1->x, pit1->y ); + _imgpt2.at(pi,0) = Point2f( pit2->x, pit2->y ); + } + } + + Mat _M1, _M2, _D1, _D2; + vector _R1, _R2, _T1, _T2; + calibrateCamera( objpt, imgpt1, imgsize, _M1, _D1, _R1, _T1, 0 ); + calibrateCamera( objpt, imgpt2, imgsize, _M2, _D2, _R2, _T1, 0 ); + undistortPoints( _imgpt1, _imgpt1, _M1, _D1, Mat(), _M1 ); + undistortPoints( _imgpt2, _imgpt2, _M2, _D2, Mat(), _M2 ); + + Mat matF, _H1, _H2; + matF = findFundamentalMat( _imgpt1, _imgpt2 ); + rectifyUncalibrated( _imgpt1, _imgpt2, matF, imgsize, _H1, _H2 ); + + Mat rectifPoints1, rectifPoints2; + perspectiveTransform( _imgpt1, rectifPoints1, _H1 ); + perspectiveTransform( _imgpt2, rectifPoints2, _H2 ); + + bool verticalStereo = abs(P2.at(0,3)) < abs(P2.at(1,3)); + double maxDiff_c = 0, maxDiff_uc = 0; + for( int i = 0, k = 0; i < nframes; i++ ) + { + vector temp[2]; + undistortPoints(Mat(imgpt1[i]), temp[0], M1, D1, R1, P1); + undistortPoints(Mat(imgpt2[i]), temp[1], M2, D2, R2, P2); + + for( int j = 0; j < npoints; j++, k++ ) + { + double diff_c = verticalStereo ? abs(temp[0][j].x - temp[1][j].x) : abs(temp[0][j].y - temp[1][j].y); + Point2f d = rectifPoints1.at(k,0) - rectifPoints2.at(k,0); + double diff_uc = verticalStereo ? abs(d.x) : abs(d.y); + maxDiff_c = max(maxDiff_c, diff_c); + maxDiff_uc = max(maxDiff_uc, diff_uc); + if( maxDiff_c > maxScanlineDistErr_c ) + { + ts->printf( cvtest::TS::LOG, "The distance between %s coordinates is too big(=%g) (used calibrated stereo), testcase %d\n", + verticalStereo ? "x" : "y", diff_c, testcase); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + return; + } + if( maxDiff_uc > maxScanlineDistErr_uc ) + { + ts->printf( cvtest::TS::LOG, "The distance between %s coordinates is too big(=%g) (used uncalibrated stereo), testcase %d\n", + verticalStereo ? "x" : "y", diff_uc, testcase); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + return; + } + } + } + + ts->printf( cvtest::TS::LOG, "Testcase %d. Max distance (calibrated) =%g\n" + "Max distance (uncalibrated) =%g\n", testcase, maxDiff_c, maxDiff_uc ); + } +} + +//-------------------------------- CV_StereoCalibrationTest_C ------------------------------ + +class CV_StereoCalibrationTest_C : public CV_StereoCalibrationTest +{ +public: + CV_StereoCalibrationTest_C() {} +protected: + virtual double calibrateStereoCamera( const vector >& objectPoints, + const vector >& imagePoints1, + const vector >& imagePoints2, + Mat& cameraMatrix1, Mat& distCoeffs1, + Mat& cameraMatrix2, Mat& distCoeffs2, + Size imageSize, Mat& R, Mat& T, + Mat& E, Mat& F, TermCriteria criteria, int flags ); + virtual void rectify( const Mat& cameraMatrix1, const Mat& distCoeffs1, + const Mat& cameraMatrix2, const Mat& distCoeffs2, + Size imageSize, const Mat& R, const Mat& T, + Mat& R1, Mat& R2, Mat& P1, Mat& P2, Mat& Q, + double alpha, Size newImageSize, + Rect* validPixROI1, Rect* validPixROI2, int flags ); + virtual bool rectifyUncalibrated( const Mat& points1, + const Mat& points2, const Mat& F, Size imgSize, + Mat& H1, Mat& H2, double threshold=5 ); +}; + +double CV_StereoCalibrationTest_C::calibrateStereoCamera( const vector >& objectPoints, + const vector >& imagePoints1, + const vector >& imagePoints2, + Mat& cameraMatrix1, Mat& distCoeffs1, + Mat& cameraMatrix2, Mat& distCoeffs2, + Size imageSize, Mat& R, Mat& T, + Mat& E, Mat& F, TermCriteria criteria, int flags ) +{ + cameraMatrix1.create( 3, 3, CV_64F ); + cameraMatrix2.create( 3, 3, CV_64F); + distCoeffs1.create( 1, 5, CV_64F); + distCoeffs2.create( 1, 5, CV_64F); + R.create(3, 3, CV_64F); + T.create(3, 1, CV_64F); + E.create(3, 3, CV_64F); + F.create(3, 3, CV_64F); + + int nimages = (int)objectPoints.size(), total = 0; + for( int i = 0; i < nimages; i++ ) + { + total += (int)objectPoints[i].size(); + } + + Mat npoints( 1, nimages, CV_32S ), + objPt( 1, total, DataType::type ), + imgPt( 1, total, DataType::type ), + imgPt2( 1, total, DataType::type ); + + Point2f* imgPtData2 = imgPt2.ptr(); + Point3f* objPtData = objPt.ptr(); + Point2f* imgPtData = imgPt.ptr(); + for( int i = 0, ni = 0, j = 0; i < nimages; i++, j += ni ) + { + ni = (int)objectPoints[i].size(); + ((int*)npoints.data)[i] = ni; + std::copy(objectPoints[i].begin(), objectPoints[i].end(), objPtData + j); + std::copy(imagePoints1[i].begin(), imagePoints1[i].end(), imgPtData + j); + std::copy(imagePoints2[i].begin(), imagePoints2[i].end(), imgPtData2 + j); + } + CvMat _objPt = objPt, _imgPt = imgPt, _imgPt2 = imgPt2, _npoints = npoints; + CvMat _cameraMatrix1 = cameraMatrix1, _distCoeffs1 = distCoeffs1; + CvMat _cameraMatrix2 = cameraMatrix2, _distCoeffs2 = distCoeffs2; + CvMat matR = R, matT = T, matE = E, matF = F; + + return cvStereoCalibrate(&_objPt, &_imgPt, &_imgPt2, &_npoints, &_cameraMatrix1, + &_distCoeffs1, &_cameraMatrix2, &_distCoeffs2, imageSize, + &matR, &matT, &matE, &matF, criteria, flags ); +} + +void CV_StereoCalibrationTest_C::rectify( const Mat& cameraMatrix1, const Mat& distCoeffs1, + const Mat& cameraMatrix2, const Mat& distCoeffs2, + Size imageSize, const Mat& R, const Mat& T, + Mat& R1, Mat& R2, Mat& P1, Mat& P2, Mat& Q, + double alpha, Size newImageSize, + Rect* validPixROI1, Rect* validPixROI2, int flags ) +{ + int rtype = CV_64F; + R1.create(3, 3, rtype); + R2.create(3, 3, rtype); + P1.create(3, 4, rtype); + P2.create(3, 4, rtype); + Q.create(4, 4, rtype); + CvMat _cameraMatrix1 = cameraMatrix1, _distCoeffs1 = distCoeffs1; + CvMat _cameraMatrix2 = cameraMatrix2, _distCoeffs2 = distCoeffs2; + CvMat matR = R, matT = T, _R1 = R1, _R2 = R2, _P1 = P1, _P2 = P2, matQ = Q; + cvStereoRectify( &_cameraMatrix1, &_cameraMatrix2, &_distCoeffs1, &_distCoeffs2, + imageSize, &matR, &matT, &_R1, &_R2, &_P1, &_P2, &matQ, flags, + alpha, newImageSize, (CvRect*)validPixROI1, (CvRect*)validPixROI2); +} + +bool CV_StereoCalibrationTest_C::rectifyUncalibrated( const Mat& points1, + const Mat& points2, const Mat& F, Size imgSize, Mat& H1, Mat& H2, double threshold ) +{ + H1.create(3, 3, CV_64F); + H2.create(3, 3, CV_64F); + CvMat _pt1 = points1, _pt2 = points2, matF, *pF=0, _H1 = H1, _H2 = H2; + if( F.size() == Size(3, 3) ) + pF = &(matF = F); + return cvStereoRectifyUncalibrated(&_pt1, &_pt2, pF, imgSize, &_H1, &_H2, threshold) > 0; +} + + +//-------------------------------- CV_StereoCalibrationTest_CPP ------------------------------ + +class CV_StereoCalibrationTest_CPP : public CV_StereoCalibrationTest +{ +public: + CV_StereoCalibrationTest_CPP() {} +protected: + virtual double calibrateStereoCamera( const vector >& objectPoints, + const vector >& imagePoints1, + const vector >& imagePoints2, + Mat& cameraMatrix1, Mat& distCoeffs1, + Mat& cameraMatrix2, Mat& distCoeffs2, + Size imageSize, Mat& R, Mat& T, + Mat& E, Mat& F, TermCriteria criteria, int flags ); + virtual void rectify( const Mat& cameraMatrix1, const Mat& distCoeffs1, + const Mat& cameraMatrix2, const Mat& distCoeffs2, + Size imageSize, const Mat& R, const Mat& T, + Mat& R1, Mat& R2, Mat& P1, Mat& P2, Mat& Q, + double alpha, Size newImageSize, + Rect* validPixROI1, Rect* validPixROI2, int flags ); + virtual bool rectifyUncalibrated( const Mat& points1, + const Mat& points2, const Mat& F, Size imgSize, + Mat& H1, Mat& H2, double threshold=5 ); +}; + +double CV_StereoCalibrationTest_CPP::calibrateStereoCamera( const vector >& objectPoints, + const vector >& imagePoints1, + const vector >& imagePoints2, + Mat& cameraMatrix1, Mat& distCoeffs1, + Mat& cameraMatrix2, Mat& distCoeffs2, + Size imageSize, Mat& R, Mat& T, + Mat& E, Mat& F, TermCriteria criteria, int flags ) +{ + return stereoCalibrate( objectPoints, imagePoints1, imagePoints2, + cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, + imageSize, R, T, E, F, criteria, flags ); +} + +void CV_StereoCalibrationTest_CPP::rectify( const Mat& cameraMatrix1, const Mat& distCoeffs1, + const Mat& cameraMatrix2, const Mat& distCoeffs2, + Size imageSize, const Mat& R, const Mat& T, + Mat& R1, Mat& R2, Mat& P1, Mat& P2, Mat& Q, + double alpha, Size newImageSize, + Rect* validPixROI1, Rect* validPixROI2, int flags ) +{ + stereoRectify( cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, + imageSize, R, T, R1, R2, P1, P2, Q, alpha, newImageSize,validPixROI1, validPixROI2, flags ); +} + +bool CV_StereoCalibrationTest_CPP::rectifyUncalibrated( const Mat& points1, + const Mat& points2, const Mat& F, Size imgSize, Mat& H1, Mat& H2, double threshold ) +{ + return stereoRectifyUncalibrated( points1, points2, F, imgSize, H1, H2, threshold ); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +TEST(Calib3d_CalibrateCamera_C, regression) { CV_CameraCalibrationTest_C test; test.safe_run(); } +TEST(Calib3d_CalibrateCamera_CPP, regression) { CV_CameraCalibrationTest_CPP test; test.safe_run(); } +TEST(Calib3d_CalibrationMatrixValues_C, accuracy) { CV_CalibrationMatrixValuesTest_C test; test.safe_run(); } +TEST(Calib3d_CalibrationMatrixValues_CPP, accuracy) { CV_CalibrationMatrixValuesTest_CPP test; test.safe_run(); } +TEST(Calib3d_ProjectPoints_C, accuracy) { CV_ProjectPointsTest_C test; test.safe_run(); } +TEST(Calib3d_ProjectPoints_CPP, regression) { CV_ProjectPointsTest_CPP test; test.safe_run(); } +TEST(Calib3d_StereoCalibrate_C, regression) { CV_StereoCalibrationTest_C test; test.safe_run(); } +TEST(Calib3d_StereoCalibrate_CPP, regression) { CV_StereoCalibrationTest_CPP test; test.safe_run(); } diff --git a/modules/calib3d/test/test_cameracalibration_artificial.cpp b/modules/calib3d/test/test_cameracalibration_artificial.cpp new file mode 100644 index 000000000..390fa7192 --- /dev/null +++ b/modules/calib3d/test/test_cameracalibration_artificial.cpp @@ -0,0 +1,428 @@ +/*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 +#include +#include +#include +#include +#include + +#include "test_chessboardgenerator.hpp" + +using namespace cv; +using namespace std; + +//template ostream& operator<<(ostream& out, const Mat_& mat) +//{ +// for(Mat_::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_(mat); } + +Mat calcRvec(const vector& 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(0) = ex; + *rot.ptr(1) = ey; + *rot.ptr(2) = ez * (1.0/norm(ez)); + + Mat res; + Rodrigues(rot.t(), res); + return res.reshape(1, 1); +} + +class CV_CalibrateCameraArtificialTest : public cvtest::BaseTest +{ +public: + CV_CalibrateCameraArtificialTest() + { + } + ~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_& camMat, const Mat& camMat_est) + { + if ( camMat_est.at(0, 1) != 0 || camMat_est.at(1, 0) != 0 || + camMat_est.at(2, 0) != 0 || camMat_est.at(2, 1) != 0 || + camMat_est.at(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(0, 0), fy_e = camMat_est.at(1, 1); + double cx_e = camMat_est.at(0, 2), cy_e = camMat_est.at(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_& distCoeffs, const Mat& distCoeffs_est) + { + const double *dt_e = distCoeffs_est.ptr(); + + 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 recomendation. TODO - improve accuaracy + //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& tvecs, const vector& 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(); + const Point3d& tvec_est = *tvecs_est[i].ptr(); + + if (norm(tvec_est - tvec) > eps* (norm(tvec) + 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, norm(tvec_est - tvec), norm(tvec)); + } + } + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + } + } + } + + void compareRotationVecs(const vector& rvecs, const vector& 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) + { + Rodrigues(rvecs[i], rmat); + Rodrigues(rvecs_est[i], rmat_est); + + if (norm(rmat_est, rmat) > eps* (norm(rmat) + 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, norm(rmat_est, rmat), norm(rmat)); + + } + } + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + } + } + } + + double reprojectErrorWithoutIntrinsics(const vector& cb3d, const vector& rvecs_exp, const vector& tvecs_exp, + const vector& rvecs_est, const vector& 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 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 += norm(uv_exp[i] - uv_est[i]); + } + return res; + } + + Size2f sqSile; + + vector chessboard3D; + vector boards, rvecs_exp, tvecs_exp, rvecs_spnp, tvecs_spnp; + vector< vector > objectPoints; + vector< vector > imagePoints_art; + vector< vector > 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 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() = cbg.corners3d[0]; + rvecs_exp[i] = calcRvec(cbg.corners3d, cbg.cornersSize()); + } + + } + + void runTest(const Size& imgSize, const Mat_& camMat, const Mat_& distCoeffs, size_t brdsNum, const Size& cornersSize, int flag = 0) + { + const TermCriteria tc(TermCriteria::EPS|TermCriteria::MAX_ITER, 30, 0.1); + + vector< vector > 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, CV_BGR2GRAY); + vector 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, CV_BGR2GRAY); + vector 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 rvecs_est, tvecs_est; + + int flags = /*CV_CALIB_FIX_K3|*/CV_CALIB_FIX_K4|CV_CALIB_FIX_K5|CV_CALIB_FIX_K6; //CALIB_FIX_K3; //CALIB_FIX_ASPECT_RATIO | | CALIB_ZERO_TANGENT_DIST; + double rep_error = calibrateCamera(objectPoints, imagePoints, imgSize, camMat_est, distCoeffs_est, rvecs_est, tvecs_est, flags); + 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(Mat(objectPoints[i]), Mat(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_ 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_ 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, accuracy_on_artificial_data) { CV_CalibrateCameraArtificialTest test; test.safe_run(); } diff --git a/modules/calib3d/test/test_cameracalibration_badarg.cpp b/modules/calib3d/test/test_cameracalibration_badarg.cpp new file mode 100644 index 000000000..adfea50a0 --- /dev/null +++ b/modules/calib3d/test/test_cameracalibration_badarg.cpp @@ -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. +// +// +// 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 + +using namespace cv; +using namespace std; + +class CV_CameraCalibrationBadArgTest : public cvtest::BadArgTest +{ +public: + CV_CameraCalibrationBadArgTest() : imgSize(800, 600) {} + ~CV_CameraCalibrationBadArgTest() {} +protected: + void run(int); + void run_func(void) {}; + + const static int M = 1; + + Size imgSize; + Size corSize; + Mat chessBoard; + Mat corners; + + struct C_Caller + { + CvMat* objPts; + CvMat* imgPts; + CvMat* npoints; + Size imageSize; + CvMat *cameraMatrix; + CvMat *distCoeffs; + CvMat *rvecs; + CvMat *tvecs; + int flags; + + void operator()() const + { + cvCalibrateCamera2(objPts, imgPts, npoints, imageSize, + cameraMatrix, distCoeffs, rvecs, tvecs, flags ); + } + }; +}; + + +void CV_CameraCalibrationBadArgTest::run( int /* start_from */ ) +{ + Mat_ camMat(3, 3); + Mat_ 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)); + corSize = cbg.cornersSize(); + vector exp_corn; + chessBoard = cbg(Mat(imgSize, CV_8U, Scalar(0)), camMat, distCoeffs0, exp_corn); + Mat_(corSize.height, corSize.width, (Point2f*)&exp_corn[0]).copyTo(corners); + + CvMat objPts, imgPts, npoints, cameraMatrix, distCoeffs, rvecs, tvecs; + Mat zeros(1, sizeof(CvMat), CV_8U, Scalar(0)); + + C_Caller caller, bad_caller; + caller.imageSize = imgSize; + caller.objPts = &objPts; + caller.imgPts = &imgPts; + caller.npoints = &npoints; + caller.cameraMatrix = &cameraMatrix; + caller.distCoeffs = &distCoeffs; + caller.rvecs = &rvecs; + caller.tvecs = &tvecs; + + ///////////////////////////// + Mat objPts_cpp; + Mat imgPts_cpp; + Mat npoints_cpp; + Mat cameraMatrix_cpp; + Mat distCoeffs_cpp; + Mat rvecs_cpp; + Mat tvecs_cpp; + + objPts_cpp.create(corSize, CV_32FC3); + for(int j = 0; j < corSize.height; ++j) + for(int i = 0; i < corSize.width; ++i) + objPts_cpp.at(j, i) = Point3i(i, j, 0); + objPts_cpp = objPts_cpp.reshape(3, 1); + + imgPts_cpp = corners.clone().reshape(2, 1); + npoints_cpp = Mat_(M, 1, corSize.width * corSize.height); + cameraMatrix_cpp.create(3, 3, CV_32F); + distCoeffs_cpp.create(5, 1, CV_32F); + rvecs_cpp.create(M, 1, CV_32FC3); + tvecs_cpp.create(M, 1, CV_32FC3); + + caller.flags = 0; + //CV_CALIB_USE_INTRINSIC_GUESS; //CV_CALIB_FIX_ASPECT_RATIO + //CV_CALIB_USE_INTRINSIC_GUESS //CV_CALIB_FIX_ASPECT_RATIO + //CV_CALIB_FIX_PRINCIPAL_POINT //CV_CALIB_ZERO_TANGENT_DIST + //CV_CALIB_FIX_FOCAL_LENGTH //CV_CALIB_FIX_K1 //CV_CALIB_FIX_K2 //CV_CALIB_FIX_K3 + + objPts = objPts_cpp; + imgPts = imgPts_cpp; + npoints = npoints_cpp; + cameraMatrix = cameraMatrix_cpp; + distCoeffs = distCoeffs_cpp; + rvecs = rvecs_cpp; + tvecs = tvecs_cpp; + + /* /*//*/ */ + int errors = 0; + + bad_caller = caller; + bad_caller.objPts = 0; + errors += run_test_case( CV_StsBadArg, "Zero passed in objPts", bad_caller); + + bad_caller = caller; + bad_caller.imgPts = 0; + errors += run_test_case( CV_StsBadArg, "Zero passed in imgPts", bad_caller ); + + bad_caller = caller; + bad_caller.npoints = 0; + errors += run_test_case( CV_StsBadArg, "Zero passed in npoints", bad_caller ); + + bad_caller = caller; + bad_caller.cameraMatrix = 0; + errors += run_test_case( CV_StsBadArg, "Zero passed in cameraMatrix", bad_caller ); + + bad_caller = caller; + bad_caller.distCoeffs = 0; + errors += run_test_case( CV_StsBadArg, "Zero passed in distCoeffs", bad_caller ); + + bad_caller = caller; + bad_caller.imageSize.width = -1; + errors += run_test_case( CV_StsOutOfRange, "Bad image width", bad_caller ); + + bad_caller = caller; + bad_caller.imageSize.height = -1; + errors += run_test_case( CV_StsOutOfRange, "Bad image height", bad_caller ); + + Mat bad_nts_cpp1 = Mat_(M, 1, 1.f); + Mat bad_nts_cpp2 = Mat_(3, 3, corSize.width * corSize.height); + CvMat bad_npts_c1 = bad_nts_cpp1; + CvMat bad_npts_c2 = bad_nts_cpp2; + + bad_caller = caller; + bad_caller.npoints = &bad_npts_c1; + errors += run_test_case( CV_StsUnsupportedFormat, "Bad npoints format", bad_caller ); + + bad_caller = caller; + bad_caller.npoints = &bad_npts_c2; + errors += run_test_case( CV_StsUnsupportedFormat, "Bad npoints size", bad_caller ); + + bad_caller = caller; + bad_caller.rvecs = (CvMat*)zeros.ptr(); + errors += run_test_case( CV_StsBadArg, "Bad rvecs header", bad_caller ); + + bad_caller = caller; + bad_caller.tvecs = (CvMat*)zeros.ptr(); + errors += run_test_case( CV_StsBadArg, "Bad tvecs header", bad_caller ); + + Mat bad_rvecs_cpp1(M+1, 1, CV_32FC3); CvMat bad_rvecs_c1 = bad_rvecs_cpp1; + Mat bad_tvecs_cpp1(M+1, 1, CV_32FC3); CvMat bad_tvecs_c1 = bad_tvecs_cpp1; + + + + Mat bad_rvecs_cpp2(M, 2, CV_32FC3); CvMat bad_rvecs_c2 = bad_rvecs_cpp2; + Mat bad_tvecs_cpp2(M, 2, CV_32FC3); CvMat bad_tvecs_c2 = bad_tvecs_cpp2; + + bad_caller = caller; + bad_caller.rvecs = &bad_rvecs_c1; + errors += run_test_case( CV_StsBadArg, "Bad tvecs header", bad_caller ); + + bad_caller = caller; + bad_caller.rvecs = &bad_rvecs_c2; + errors += run_test_case( CV_StsBadArg, "Bad tvecs header", bad_caller ); + + bad_caller = caller; + bad_caller.tvecs = &bad_tvecs_c1; + errors += run_test_case( CV_StsBadArg, "Bad tvecs header", bad_caller ); + + bad_caller = caller; + bad_caller.tvecs = &bad_tvecs_c2; + errors += run_test_case( CV_StsBadArg, "Bad tvecs header", bad_caller ); + + Mat bad_cameraMatrix_cpp1(3, 3, CV_32S); CvMat bad_cameraMatrix_c1 = bad_cameraMatrix_cpp1; + Mat bad_cameraMatrix_cpp2(2, 3, CV_32F); CvMat bad_cameraMatrix_c2 = bad_cameraMatrix_cpp2; + Mat bad_cameraMatrix_cpp3(3, 2, CV_64F); CvMat bad_cameraMatrix_c3 = bad_cameraMatrix_cpp3; + + + + bad_caller = caller; + bad_caller.cameraMatrix = &bad_cameraMatrix_c1; + errors += run_test_case( CV_StsBadArg, "Bad camearaMatrix header", bad_caller ); + + bad_caller = caller; + bad_caller.cameraMatrix = &bad_cameraMatrix_c2; + errors += run_test_case( CV_StsBadArg, "Bad camearaMatrix header", bad_caller ); + + bad_caller = caller; + bad_caller.cameraMatrix = &bad_cameraMatrix_c3; + errors += run_test_case( CV_StsBadArg, "Bad camearaMatrix header", bad_caller ); + + Mat bad_distCoeffs_cpp1(1, 5, CV_32S); CvMat bad_distCoeffs_c1 = bad_distCoeffs_cpp1; + Mat bad_distCoeffs_cpp2(2, 2, CV_64F); CvMat bad_distCoeffs_c2 = bad_distCoeffs_cpp2; + Mat bad_distCoeffs_cpp3(1, 6, CV_64F); CvMat bad_distCoeffs_c3 = bad_distCoeffs_cpp3; + + + + bad_caller = caller; + bad_caller.distCoeffs = &bad_distCoeffs_c1; + errors += run_test_case( CV_StsBadArg, "Bad distCoeffs header", bad_caller ); + + bad_caller = caller; + bad_caller.distCoeffs = &bad_distCoeffs_c2; + errors += run_test_case( CV_StsBadArg, "Bad distCoeffs header", bad_caller ); + + + bad_caller = caller; + bad_caller.distCoeffs = &bad_distCoeffs_c3; + errors += run_test_case( CV_StsBadArg, "Bad distCoeffs header", bad_caller ); + + double CM[] = {0, 0, 0, /**/0, 0, 0, /**/0, 0, 0}; + Mat bad_cameraMatrix_cpp4(3, 3, CV_64F, CM); CvMat bad_cameraMatrix_c4 = bad_cameraMatrix_cpp4; + + bad_caller = caller; + bad_caller.flags |= CV_CALIB_USE_INTRINSIC_GUESS; + bad_caller.cameraMatrix = &bad_cameraMatrix_c4; + CM[0] = 0; //bad fx + errors += run_test_case( CV_StsOutOfRange, "Bad camearaMatrix data", bad_caller ); + + CM[0] = 500; CM[4] = 0; //bad fy + errors += run_test_case( CV_StsOutOfRange, "Bad camearaMatrix data", bad_caller ); + + CM[0] = 500; CM[4] = 500; CM[2] = -1; //bad cx + errors += run_test_case( CV_StsOutOfRange, "Bad camearaMatrix data", bad_caller ); + + CM[0] = 500; CM[4] = 500; CM[2] = imgSize.width*2; //bad cx + errors += run_test_case( CV_StsOutOfRange, "Bad camearaMatrix data", bad_caller ); + + CM[0] = 500; CM[4] = 500; CM[2] = imgSize.width/2; CM[5] = -1; //bad cy + errors += run_test_case( CV_StsOutOfRange, "Bad camearaMatrix data", bad_caller ); + + CM[0] = 500; CM[4] = 500; CM[2] = imgSize.width/2; CM[5] = imgSize.height*2; //bad cy + errors += run_test_case( CV_StsOutOfRange, "Bad camearaMatrix data", bad_caller ); + + CM[0] = 500; CM[4] = 500; CM[2] = imgSize.width/2; CM[5] = imgSize.height/2; + CM[1] = 0.1; //Non-zero skew + errors += run_test_case( CV_StsOutOfRange, "Bad camearaMatrix data", bad_caller ); + + CM[1] = 0; + CM[3] = 0.1; /* mad matrix shape */ + errors += run_test_case( CV_StsOutOfRange, "Bad camearaMatrix data", bad_caller ); + + CM[3] = 0; CM[6] = 0.1; /* mad matrix shape */ + errors += run_test_case( CV_StsOutOfRange, "Bad camearaMatrix data", bad_caller ); + + CM[3] = 0; CM[6] = 0; CM[7] = 0.1; /* mad matrix shape */ + errors += run_test_case( CV_StsOutOfRange, "Bad camearaMatrix data", bad_caller ); + + CM[3] = 0; CM[6] = 0; CM[7] = 0; CM[8] = 1.1; /* mad matrix shape */ + errors += run_test_case( CV_StsOutOfRange, "Bad camearaMatrix data", bad_caller ); + CM[8] = 1.0; + + ///////////////////////////////////////////////////////////////////////////////////// + bad_caller = caller; + Mat bad_objPts_cpp5 = objPts_cpp.clone(); CvMat bad_objPts_c5 = bad_objPts_cpp5; + bad_caller.objPts = &bad_objPts_c5; + + cv::RNG& rng = theRNG(); + for(int i = 0; i < bad_objPts_cpp5.rows; ++i) + bad_objPts_cpp5.at(0, i).z += ((float)rng - 0.5f); + + errors += run_test_case( CV_StsBadArg, "Bad objPts data", bad_caller ); + + if (errors) + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + else + ts->set_failed_test_info(cvtest::TS::OK); + + //try { caller(); } + //catch (...) + //{ + // ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + // printf("+!"); + //} +} + + +class CV_Rodrigues2BadArgTest : public cvtest::BadArgTest +{ +public: + CV_Rodrigues2BadArgTest() {} + ~CV_Rodrigues2BadArgTest() {} +protected: + void run_func(void) {}; + + struct C_Caller + { + CvMat* src; + CvMat* dst; + CvMat* jacobian; + + void operator()() { cvRodrigues2(src, dst, jacobian); } + }; + + void run(int /* start_from */ ) + { + Mat zeros(1, sizeof(CvMat), CV_8U, Scalar(0)); + CvMat src_c, dst_c, jacobian_c; + + Mat src_cpp(3, 1, CV_32F); src_c = src_cpp; + Mat dst_cpp(3, 3, CV_32F); dst_c = dst_cpp; + Mat jacobian_cpp(3, 9, CV_32F); jacobian_c = jacobian_cpp; + + C_Caller caller, bad_caller; + caller.src = &src_c; + caller.dst = &dst_c; + caller.jacobian = &jacobian_c; + + /* try { caller(); } + catch (...) + { + printf("badasfas"); + }*/ + + /*/*//*/*/ + int errors = 0; + + bad_caller = caller; + bad_caller.src = 0; + errors += run_test_case( CV_StsNullPtr, "Src is zero pointer", bad_caller ); + + bad_caller = caller; + bad_caller.dst = 0; + errors += run_test_case( CV_StsNullPtr, "Dst is zero pointer", bad_caller ); + + Mat bad_src_cpp1(3, 1, CV_8U); CvMat bad_src_c1 = bad_src_cpp1; + Mat bad_dst_cpp1(3, 1, CV_8U); CvMat bad_dst_c1 = bad_dst_cpp1; + Mat bad_jac_cpp1(3, 1, CV_8U); CvMat bad_jac_c1 = bad_jac_cpp1; + Mat bad_jac_cpp2(3, 1, CV_32FC2); CvMat bad_jac_c2 = bad_jac_cpp2; + Mat bad_jac_cpp3(3, 1, CV_32F); CvMat bad_jac_c3 = bad_jac_cpp3; + + bad_caller = caller; + bad_caller.src = &bad_src_c1; + errors += run_test_case( CV_StsUnsupportedFormat, "Bad src formart", bad_caller ); + + bad_caller = caller; + bad_caller.dst = &bad_dst_c1; + errors += run_test_case( CV_StsUnmatchedFormats, "Bad dst formart", bad_caller ); + + bad_caller = caller; + bad_caller.jacobian = (CvMat*)zeros.ptr(); + errors += run_test_case( CV_StsBadArg, "Bad jacobian ", bad_caller ); + + bad_caller = caller; + bad_caller.jacobian = &bad_jac_c1; + errors += run_test_case( CV_StsUnmatchedFormats, "Bad jacobian format", bad_caller ); + + bad_caller = caller; + bad_caller.jacobian = &bad_jac_c2; + errors += run_test_case( CV_StsUnmatchedFormats, "Bad jacobian format", bad_caller ); + + bad_caller = caller; + bad_caller.jacobian = &bad_jac_c3; + errors += run_test_case( CV_StsBadSize, "Bad jacobian format", bad_caller ); + + Mat bad_src_cpp2(1, 1, CV_32F); CvMat bad_src_c2 = bad_src_cpp2; + + bad_caller = caller; + bad_caller.src = &bad_src_c2; + errors += run_test_case( CV_StsBadSize, "Bad src format", bad_caller ); + + Mat bad_dst_cpp2(2, 1, CV_32F); CvMat bad_dst_c2 = bad_dst_cpp2; + Mat bad_dst_cpp3(3, 2, CV_32F); CvMat bad_dst_c3 = bad_dst_cpp3; + Mat bad_dst_cpp4(3, 3, CV_32FC2); CvMat bad_dst_c4 = bad_dst_cpp4; + + bad_caller = caller; + bad_caller.dst = &bad_dst_c2; + errors += run_test_case( CV_StsBadSize, "Bad dst format", bad_caller ); + + bad_caller = caller; + bad_caller.dst = &bad_dst_c3; + errors += run_test_case( CV_StsBadSize, "Bad dst format", bad_caller ); + + bad_caller = caller; + bad_caller.dst = &bad_dst_c4; + errors += run_test_case( CV_StsBadSize, "Bad dst format", bad_caller ); + + + /********/ + src_cpp.create(3, 3, CV_32F); src_c = src_cpp; + dst_cpp.create(3, 1, CV_32F); dst_c = dst_cpp; + + + Mat bad_dst_cpp5(5, 5, CV_32F); CvMat bad_dst_c5 = bad_dst_cpp5; + + bad_caller = caller; + bad_caller.dst = &bad_dst_c5; + errors += run_test_case( CV_StsBadSize, "Bad dst format", bad_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_ camMat; + Mat_ distCoeffs; + + struct C_Caller + { + CvMat* objectPoints; + CvMat* r_vec; + CvMat* t_vec; + CvMat* A; + CvMat* distCoeffs; + CvMat* imagePoints; + CvMat* dpdr; + CvMat* dpdt; + CvMat* dpdf; + CvMat* dpdc; + CvMat* dpdk; + double aspectRatio; + + void operator()() + { + cvProjectPoints2( objectPoints, r_vec, t_vec, A, distCoeffs, imagePoints, + dpdr, dpdt, dpdf, dpdc, dpdk, aspectRatio ); + } + }; + + void run(int /* start_from */ ) + { + CvMat zeros; + memset(&zeros, 0, sizeof(zeros)); + + C_Caller caller, bad_caller; + CvMat objectPoints_c, r_vec_c, t_vec_c, A_c, distCoeffs_c, imagePoints_c, + dpdr_c, dpdt_c, dpdf_c, dpdc_c, dpdk_c; + + const int n = 10; + + Mat imagePoints_cpp(1, n, CV_32FC2); imagePoints_c = imagePoints_cpp; + + Mat objectPoints_cpp(1, n, CV_32FC3); + randu(objectPoints_cpp, Scalar::all(1), Scalar::all(10)); + objectPoints_c = objectPoints_cpp; + + Mat t_vec_cpp(Mat::zeros(1, 3, CV_32F)); t_vec_c = t_vec_cpp; + Mat r_vec_cpp; + Rodrigues(Mat::eye(3, 3, CV_32F), r_vec_cpp); r_vec_c = r_vec_cpp; + + Mat A_cpp = camMat.clone(); A_c = A_cpp; + Mat distCoeffs_cpp = distCoeffs.clone(); distCoeffs_c = distCoeffs_cpp; + + Mat dpdr_cpp(2*n, 3, CV_32F); dpdr_c = dpdr_cpp; + Mat dpdt_cpp(2*n, 3, CV_32F); dpdt_c = dpdt_cpp; + Mat dpdf_cpp(2*n, 2, CV_32F); dpdf_c = dpdf_cpp; + Mat dpdc_cpp(2*n, 2, CV_32F); dpdc_c = dpdc_cpp; + Mat dpdk_cpp(2*n, 4, CV_32F); dpdk_c = dpdk_cpp; + + caller.aspectRatio = 1.0; + caller.objectPoints = &objectPoints_c; + caller.r_vec = &r_vec_c; + caller.t_vec = &t_vec_c; + caller.A = &A_c; + caller.distCoeffs = &distCoeffs_c; + caller.imagePoints = &imagePoints_c; + caller.dpdr = &dpdr_c; + caller.dpdt = &dpdt_c; + caller.dpdf = &dpdf_c; + caller.dpdc = &dpdc_c; + caller.dpdk = &dpdk_c; + + /********************/ + int errors = 0; + + + bad_caller = caller; + bad_caller.objectPoints = 0; + errors += run_test_case( CV_StsBadArg, "Zero objectPoints", bad_caller ); + + bad_caller = caller; + bad_caller.r_vec = 0; + errors += run_test_case( CV_StsBadArg, "Zero r_vec", bad_caller ); + + bad_caller = caller; + bad_caller.t_vec = 0; + errors += run_test_case( CV_StsBadArg, "Zero t_vec", bad_caller ); + + bad_caller = caller; + bad_caller.A = 0; + errors += run_test_case( CV_StsBadArg, "Zero camMat", bad_caller ); + + bad_caller = caller; + bad_caller.imagePoints = 0; + errors += run_test_case( CV_StsBadArg, "Zero imagePoints", bad_caller ); + + /****************************/ + Mat bad_r_vec_cpp1(r_vec_cpp.size(), CV_32S); CvMat bad_r_vec_c1 = bad_r_vec_cpp1; + Mat bad_r_vec_cpp2(2, 2, CV_32F); CvMat bad_r_vec_c2 = bad_r_vec_cpp2; + Mat bad_r_vec_cpp3(r_vec_cpp.size(), CV_32FC2); CvMat bad_r_vec_c3 = bad_r_vec_cpp3; + + bad_caller = caller; + bad_caller.r_vec = &bad_r_vec_c1; + errors += run_test_case( CV_StsBadArg, "Bad rvec format", bad_caller ); + + bad_caller = caller; + bad_caller.r_vec = &bad_r_vec_c2; + errors += run_test_case( CV_StsBadArg, "Bad rvec format", bad_caller ); + + bad_caller = caller; + bad_caller.r_vec = &bad_r_vec_c3; + errors += run_test_case( CV_StsBadArg, "Bad rvec format", bad_caller ); + + /****************************/ + Mat bad_t_vec_cpp1(t_vec_cpp.size(), CV_32S); CvMat bad_t_vec_c1 = bad_t_vec_cpp1; + Mat bad_t_vec_cpp2(2, 2, CV_32F); CvMat bad_t_vec_c2 = bad_t_vec_cpp2; + Mat bad_t_vec_cpp3(1, 1, CV_32FC2); CvMat bad_t_vec_c3 = bad_t_vec_cpp3; + + bad_caller = caller; + bad_caller.t_vec = &bad_t_vec_c1; + errors += run_test_case( CV_StsBadArg, "Bad tvec format", bad_caller ); + + bad_caller = caller; + bad_caller.t_vec = &bad_t_vec_c2; + errors += run_test_case( CV_StsBadArg, "Bad tvec format", bad_caller ); + + bad_caller = caller; + bad_caller.t_vec = &bad_t_vec_c3; + errors += run_test_case( CV_StsBadArg, "Bad tvec format", bad_caller ); + + /****************************/ + Mat bad_A_cpp1(A_cpp.size(), CV_32S); CvMat bad_A_c1 = bad_A_cpp1; + Mat bad_A_cpp2(2, 2, CV_32F); CvMat bad_A_c2 = bad_A_cpp2; + + bad_caller = caller; + bad_caller.A = &bad_A_c1; + errors += run_test_case( CV_StsBadArg, "Bad A format", bad_caller ); + + bad_caller = caller; + bad_caller.A = &bad_A_c2; + errors += run_test_case( CV_StsBadArg, "Bad A format", bad_caller ); + + /****************************/ + Mat bad_distCoeffs_cpp1(distCoeffs_cpp.size(), CV_32S); CvMat bad_distCoeffs_c1 = bad_distCoeffs_cpp1; + Mat bad_distCoeffs_cpp2(2, 2, CV_32F); CvMat bad_distCoeffs_c2 = bad_distCoeffs_cpp2; + Mat bad_distCoeffs_cpp3(1, 7, CV_32F); CvMat bad_distCoeffs_c3 = bad_distCoeffs_cpp3; + + bad_caller = caller; + bad_caller.distCoeffs = &zeros; + errors += run_test_case( CV_StsBadArg, "Bad distCoeffs format", bad_caller ); + + bad_caller = caller; + bad_caller.distCoeffs = &bad_distCoeffs_c1; + errors += run_test_case( CV_StsBadArg, "Bad distCoeffs format", bad_caller ); + + bad_caller = caller; + bad_caller.distCoeffs = &bad_distCoeffs_c2; + errors += run_test_case( CV_StsBadArg, "Bad distCoeffs format", bad_caller ); + + bad_caller = caller; + bad_caller.distCoeffs = &bad_distCoeffs_c3; + errors += run_test_case( CV_StsBadArg, "Bad distCoeffs format", bad_caller ); + + + /****************************/ + Mat bad_dpdr_cpp1(dpdr_cpp.size(), CV_32S); CvMat bad_dpdr_c1 = bad_dpdr_cpp1; + Mat bad_dpdr_cpp2(dpdr_cpp.cols+1, 3, CV_32F); CvMat bad_dpdr_c2 = bad_dpdr_cpp2; + Mat bad_dpdr_cpp3(dpdr_cpp.cols, 7, CV_32F); CvMat bad_dpdr_c3 = bad_dpdr_cpp3; + + bad_caller = caller; + bad_caller.dpdr = &zeros; + errors += run_test_case( CV_StsBadArg, "Bad dpdr format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdr = &bad_dpdr_c1; + errors += run_test_case( CV_StsBadArg, "Bad dpdr format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdr = &bad_dpdr_c2; + errors += run_test_case( CV_StsBadArg, "Bad dpdr format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdr = &bad_dpdr_c3; + errors += run_test_case( CV_StsBadArg, "Bad dpdr format", bad_caller ); + + /****************************/ + + bad_caller = caller; + bad_caller.dpdt = &zeros; + errors += run_test_case( CV_StsBadArg, "Bad dpdt format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdt = &bad_dpdr_c1; + errors += run_test_case( CV_StsBadArg, "Bad dpdt format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdt = &bad_dpdr_c2; + errors += run_test_case( CV_StsBadArg, "Bad dpdt format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdt = &bad_dpdr_c3; + errors += run_test_case( CV_StsBadArg, "Bad dpdt format", bad_caller ); + + /****************************/ + + Mat bad_dpdf_cpp2(dpdr_cpp.cols+1, 2, CV_32F); CvMat bad_dpdf_c2 = bad_dpdf_cpp2; + + bad_caller = caller; + bad_caller.dpdf = &zeros; + errors += run_test_case( CV_StsBadArg, "Bad dpdf format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdf = &bad_dpdr_c1; + errors += run_test_case( CV_StsBadArg, "Bad dpdf format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdf = &bad_dpdf_c2; + errors += run_test_case( CV_StsBadArg, "Bad dpdf format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdf = &bad_dpdr_c3; + errors += run_test_case( CV_StsBadArg, "Bad dpdf format", bad_caller ); + + /****************************/ + + bad_caller = caller; + bad_caller.dpdc = &zeros; + errors += run_test_case( CV_StsBadArg, "Bad dpdc format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdc = &bad_dpdr_c1; + errors += run_test_case( CV_StsBadArg, "Bad dpdc format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdc = &bad_dpdf_c2; + errors += run_test_case( CV_StsBadArg, "Bad dpdc format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdc = &bad_dpdr_c3; + errors += run_test_case( CV_StsBadArg, "Bad dpdc format", bad_caller ); + + /****************************/ + + bad_caller = caller; + bad_caller.dpdk = &zeros; + errors += run_test_case( CV_StsBadArg, "Bad dpdk format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdk = &bad_dpdr_c1; + errors += run_test_case( CV_StsBadArg, "Bad dpdk format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdk = &bad_dpdf_c2; + errors += run_test_case( CV_StsBadArg, "Bad dpdk format", bad_caller ); + + bad_caller = caller; + bad_caller.dpdk = &bad_dpdr_c3; + errors += run_test_case( CV_StsBadArg, "Bad dpdk format", bad_caller ); + + bad_caller = caller; + bad_caller.distCoeffs = 0; + errors += run_test_case( CV_StsNullPtr, "distCoeffs is NULL while dpdk is not", bad_caller ); + + + if (errors) + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + else + ts->set_failed_test_info(cvtest::TS::OK); + } +}; + + +TEST(Calib3d_CalibrateCamera_C, badarg) { CV_CameraCalibrationBadArgTest test; test.safe_run(); } +TEST(Calib3d_Rodrigues_C, badarg) { CV_Rodrigues2BadArgTest test; test.safe_run(); } +TEST(Calib3d_ProjectPoints_C, badarg) { CV_ProjectPoints2BadArgTest test; test.safe_run(); } + + diff --git a/modules/calib3d/test/test_chessboardgenerator.cpp b/modules/calib3d/test/test_chessboardgenerator.cpp new file mode 100644 index 000000000..bf05a0f10 --- /dev/null +++ b/modules/calib3d/test/test_chessboardgenerator.cpp @@ -0,0 +1,332 @@ +/*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" + +#include +#include +#include + +using namespace cv; +using namespace std; + +ChessBoardGenerator::ChessBoardGenerator(const Size& _patternSize) : sensorWidth(32), sensorHeight(24), + squareEdgePointsNum(200), min_cos(sqrt(2.f)*0.5f), cov(0.5), + patternSize(_patternSize), rendererResolutionMultiplier(4), tvec(Mat::zeros(1, 3, CV_32F)) +{ + Rodrigues(Mat::eye(3, 3, CV_32F), rvec); +} + +void cv::ChessBoardGenerator::generateEdge(const Point3f& p1, const Point3f& p2, vector& 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 cv::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 cv::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(-1.f, 1.f); + float len = (float)norm(n); + 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 cv::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& whole, + vector& corners) const +{ + vector< vector > 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 pts_square3d; + vector 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(Mat(pts_square3d), rvec, tvec, camMat, distCoeffs, pts_square2d); + squares_black.resize(squares_black.size() + 1); + vector temp; + approxPolyDP(Mat(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(Mat(corners3d), rvec, tvec, camMat, distCoeffs, corners); + + vector whole3d; + vector 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(Mat(whole3d), rvec, tvec, camMat, distCoeffs, whole2d); + vector temp_whole2d; + approxPolyDP(Mat(whole2d), temp_whole2d, 1.0, true); + + vector< vector > 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), CV_FILLED, CV_AA); + drawContours(result, squares_black, -1, Scalar::all(0), CV_FILLED, CV_AA); + } + else + { + Mat tmp; + resize(bg, tmp, bg.size() * rendererResolutionMultiplier); + drawContours(tmp, whole_contour, -1, Scalar::all(255), CV_FILLED, CV_AA); + drawContours(tmp, squares_black, -1, Scalar::all(0), CV_FILLED, CV_AA); + resize(tmp, result, bg.size(), 0, 0, INTER_AREA); + } + + return result; +} + +Mat cv::ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, vector& corners) const +{ + cov = 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(rng.uniform(0.1, 10.0)); + float ah = static_cast(rng.uniform(-fovx/2 * cov, fovx/2 * cov) * CV_PI / 180); + float av = static_cast(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(norm(p) * sin( 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 pts3d(4); + vector 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(Mat(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 cv::ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, + const Size2f& squareSize, vector& corners) const +{ + cov = 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(rng.uniform(0.1, 10.0)); + float ah = static_cast(rng.uniform(-fovx/2 * cov, fovx/2 * cov) * CV_PI / 180); + float av = static_cast(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 pts3d(4); + vector 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(Mat(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 cv::ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, + const Size2f& squareSize, const Point3f& pos, vector& corners) const +{ + cov = 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 pts3d(4); + vector 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(Mat(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); +} + diff --git a/modules/calib3d/test/test_chessboardgenerator.hpp b/modules/calib3d/test/test_chessboardgenerator.hpp new file mode 100644 index 000000000..f3a724332 --- /dev/null +++ b/modules/calib3d/test/test_chessboardgenerator.hpp @@ -0,0 +1,40 @@ +#ifndef CV_CHESSBOARDGENERATOR_H143KJTVYM389YTNHKFDHJ89NYVMO3VLMEJNTBGUEIYVCM203P +#define CV_CHESSBOARDGENERATOR_H143KJTVYM389YTNHKFDHJ89NYVMO3VLMEJNTBGUEIYVCM203P + +#include "opencv2/calib3d/calib3d.hpp" + +namespace cv +{ + +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, vector& corners) const; + Mat operator()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, const Size2f& squareSize, vector& corners) const; + Mat operator()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, const Size2f& squareSize, const Point3f& pos, vector& corners) const; + Size cornersSize() const; + + mutable vector corners3d; +private: + void generateEdge(const Point3f& p1, const Point3f& p2, vector& 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 vector& whole, vector& corners) const; + void generateBasis(Point3f& pb1, Point3f& pb2) const; + + Mat rvec, tvec; +}; + +}; + + +#endif diff --git a/modules/calib3d/test/test_chesscorners.cpp b/modules/calib3d/test/test_chesscorners.cpp new file mode 100644 index 000000000..0bca4e037 --- /dev/null +++ b/modules/calib3d/test/test_chesscorners.cpp @@ -0,0 +1,443 @@ +/*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 +#include + +using namespace std; +using namespace cv; + +#define _L2_ERR + +void show_points( const Mat& gray, const Mat& u, const vector& v, Size pattern_size, bool was_found ) +{ + Mat rgb( gray.size(), CV_8U); + merge(vector(3, gray), rgb); + + for(size_t i = 0; i < v.size(); i++ ) + circle( rgb, v[i], 3, CV_RGB(255, 0, 0), CV_FILLED); + + if( !u.empty() ) + { + const Point2f* u_data = u.ptr(); + size_t count = u.cols * u.rows; + for(size_t i = 0; i < count; i++ ) + circle( rgb, u_data[i], 3, CV_RGB(0, 255, 0), CV_FILLED); + } + if (!v.empty()) + { + Mat corners((int)v.size(), 1, CV_32FC2, (void*)&v[0]); + drawChessboardCorners( rgb, pattern_size, corners, was_found ); + } + //namedWindow( "test", 0 ); imshow( "test", rgb ); waitKey(0); +} + + +enum Pattern { CHESSBOARD, CIRCLES_GRID }; + +class CV_ChessboardDetectorTest : public cvtest::BaseTest +{ +public: + CV_ChessboardDetectorTest( Pattern pattern ); +protected: + void run(int); + void run_batch(const string& filename); + bool checkByGenerator(); + + Pattern pattern; +}; + +CV_ChessboardDetectorTest::CV_ChessboardDetectorTest( Pattern _pattern ) +{ + pattern = _pattern; +} + +double calcError(const vector& v, const Mat& u) +{ + int count_exp = u.cols * u.rows; + const Point2f* u_data = u.ptr(); + + double err = numeric_limits::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 */) +{ + /*if (!checkByGenerator()) + return;*/ + switch( pattern ) + { + case CHESSBOARD: + checkByGenerator(); + run_batch("chessboard_list.dat"); + run_batch("chessboard_list_subpixel.dat"); + break; + case CIRCLES_GRID: + run_batch("circles_list.dat"); + break; + } +} + +void CV_ChessboardDetectorTest::run_batch( const string& filename ) +{ + cvtest::TS& ts = *this->ts; + ts.set_failed_test_info( cvtest::TS::OK ); + + 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: + folder = string(ts.get_data_path()) + "cameracalibration/"; + break; + case CIRCLES_GRID: + folder = string(ts.get_data_path()) + "cameracalibration/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 readed 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 = board_list.node->data.seq->total/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 ); + continue; + } + + string filename = folder + (string)board_list[idx * 2 + 1]; + Mat expected; + { + CvMat *u = (CvMat*)cvLoad( filename.c_str() ); + if(!u ) + { + ts.printf( cvtest::TS::LOG, "one of chessboard corner files can't be read: %s\n", filename.c_str() ); + ts.set_failed_test_info( cvtest::TS::FAIL_MISSING_TEST_DATA ); + continue; + } + expected = Mat(u, true); + cvReleaseMat( &u ); + } + size_t count_exp = static_cast(expected.cols * expected.rows); + Size pattern_size = expected.size(); + + vector v; + bool result = false; + switch( pattern ) + { + case CHESSBOARD: + result = findChessboardCorners(gray, pattern_size, v, CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_NORMALIZE_IMAGE); + break; + case CIRCLES_GRID: + result = findCirclesGrid(gray, pattern_size, v); + break; + } + show_points( gray, Mat(), v, pattern_size, result ); + if( !result || v.size() != count_exp ) + { + ts.printf( cvtest::TS::LOG, "chessboard is not found in %s\n", img_file.c_str() ); + ts.set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + continue; + } + +#ifndef WRITE_POINTS + double err = calcError(v, expected); +#if 0 + if( err > rough_success_error_level ) + { + ts.printf( cvtest::TS::LOG, "bad accuracy of corner guesses\n" ); + ts.set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + continue; + } +#endif + 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, pattern_size, result ); + +#ifndef WRITE_POINTS +// printf("called find4QuadCornerSubpix\n"); + err = calcError(v, expected); + sum_error += err; + count++; +#if 1 + 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 ); + continue; + } +#endif + ts.printf(cvtest::TS::LOG, "Error on %s is %f\n", img_file.c_str(), err); + max_precise_error = MAX( max_precise_error, err ); +#else + Mat mat_v(pattern_size, CV_32FC2, (void*)&v[0]); + CvMat cvmat_v = mat_v; + cvSave( filename.c_str(), &cvmat_v ); +#endif + progress = update_progress( progress, idx, max_idx, 0 ); + } + + sum_error /= count; + ts.printf(cvtest::TS::LOG, "Average error is %f\n", sum_error); +} + +double calcErrorMinError(const Size& cornSz, const vector& corners_found, const vector& 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& corners_generated) +{ + Size cornersSize = cbg.cornersSize(); + Mat_ mat(cornersSize.height, cornersSize.width, (Point2f*)&corners_generated[0]); + + double minNeibDist = std::numeric_limits::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 = norm( cur - mat(i + 1, j + 1) ); + if (tmp < minNeibDist) + tmp = minNeibDist; + + tmp = norm( cur - mat(i - 1, j + 1 ) ); + if (tmp < minNeibDist) + tmp = minNeibDist; + + tmp = norm( cur - mat(i + 1, j - 1) ); + if (tmp < minNeibDist) + tmp = minNeibDist; + + tmp = norm( cur - mat(i - 1, j - 1) ); + if (tmp < minNeibDist) + tmp = minNeibDist; + } + + 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::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(7,7), 3.0); + + Mat_ 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_ 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) + { + progress = update_progress( progress, i, test_num, 0 ); + ChessBoardGenerator cbg(sizes[i % sizes_num]); + + vector 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 corners_found; + int flags = i % 8; // need to check branches for all flags + bool found = findChessboardCorners(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; + continue; + } + + double err = calcErrorMinError(cbg.cornersSize(), corners_found, corners_generated); + if( err > rough_success_error_level ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy of corner guesses" ); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + res = false; + continue; + } + } + + /* ***** negative ***** */ + { + vector corners_found; + bool found = findChessboardCorners(bg, Size(8, 7), corners_found); + if (found) + res = false; + + ChessBoardGenerator cbg(Size(8, 7)); + + vector cg; + Mat cb = cbg(bg, camMat, distCoeffs, cg); + + found = findChessboardCorners(cb, Size(3, 4), corners_found); + if (found) + res = false; + + Point2f c = std::accumulate(cg.begin(), cg.end(), Point2f(), plus()) * (1.f/cg.size()); + + Mat_ 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 = findChessboardCorners(sh, cbg.cornersSize(), corners_found); + if (found) + res = false; + + vector< vector > cnts(1); + vector& 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), CV_FILLED); + + found = findChessboardCorners(cb, cbg.cornersSize(), corners_found); + if (found) + res = false; + + cv::drawChessboardCorners(cb, cbg.cornersSize(), Mat(corners_found), found); + } + + return res; +} + +TEST(Calib3d_ChessboardDetector, accuracy) { CV_ChessboardDetectorTest test( CHESSBOARD ); test.safe_run(); } +TEST(Calib3d_CirclesPatternDetector, accuracy) { CV_ChessboardDetectorTest test( CIRCLES_GRID ); test.safe_run(); } + +/* End of file. */ diff --git a/modules/calib3d/test/test_chesscorners_badarg.cpp b/modules/calib3d/test/test_chesscorners_badarg.cpp new file mode 100644 index 000000000..5710cf686 --- /dev/null +++ b/modules/calib3d/test/test_chesscorners_badarg.cpp @@ -0,0 +1,145 @@ +/*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 + +using namespace std; +using namespace cv; + +class CV_ChessboardDetectorBadArgTest : public cvtest::BadArgTest +{ +public: + CV_ChessboardDetectorBadArgTest(); +protected: + void run(int); + bool checkByGenerator(); + + bool cpp; + + /* cpp interface */ + Mat img; + Size pattern_size; + int flags; + vector corners; + + /* c interface */ + CvMat arr; + CvPoint2D32f* out_corners; + int* out_corner_count; + + + /* c interface draw corners */ + bool drawCorners; + CvMat drawCorImg; + bool was_found; + + void run_func() + { + if (cpp) + findChessboardCorners(img, pattern_size, corners, flags); + else + if (!drawCorners) + cvFindChessboardCorners( &arr, pattern_size, out_corners, out_corner_count, flags ); + else + cvDrawChessboardCorners( &drawCorImg, pattern_size, + (CvPoint2D32f*)&corners[0], (int)corners.size(), was_found); + } +}; + +CV_ChessboardDetectorBadArgTest::CV_ChessboardDetectorBadArgTest() {} + +/* ///////////////////// chess_corner_test ///////////////////////// */ +void CV_ChessboardDetectorBadArgTest::run( int /*start_from */) +{ + Mat bg(800, 600, CV_8U, Scalar(0)); + Mat_ 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_ distCoeffs(1, 5); + distCoeffs << 1.2f, 0.2f, 0.f, 0.f, 0.f; + + ChessBoardGenerator cbg(Size(8,6)); + vector exp_corn; + Mat cb = cbg(bg, camMat, distCoeffs, exp_corn); + + /* /*//*/ */ + int errors = 0; + flags = CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_NORMALIZE_IMAGE; + cpp = true; + + img = cb.clone(); + pattern_size = Size(2,2); + errors += run_test_case( CV_StsOutOfRange, "Invlid pattern size" ); + + pattern_size = cbg.cornersSize(); + cb.convertTo(img, CV_32F); + errors += run_test_case( CV_StsUnsupportedFormat, "Not 8-bit image" ); + + cv::merge(vector(2, cb), img); + errors += run_test_case( CV_StsUnsupportedFormat, "2 channel image" ); + + cpp = false; + drawCorners = false; + + img = cb.clone(); + arr = img; + out_corner_count = 0; + out_corners = 0; + errors += run_test_case( CV_StsNullPtr, "Null pointer to corners" ); + + drawCorners = true; + Mat cvdrawCornImg(img.size(), CV_8UC2); + drawCorImg = cvdrawCornImg; + was_found = true; + errors += run_test_case( CV_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(); } + +/* End of file. */ diff --git a/modules/calib3d/test/test_chesscorners_timing.cpp b/modules/calib3d/test/test_chesscorners_timing.cpp new file mode 100644 index 000000000..cc7625793 --- /dev/null +++ b/modules/calib3d/test/test_chesscorners_timing.cpp @@ -0,0 +1,189 @@ +/*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" + +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 */ + char filepath[1000]; + char filename[1000]; + + CvMat* _v = 0; + CvPoint2D32f* v; + + IplImage* img = 0; + IplImage* gray = 0; + IplImage* thresh = 0; + + int idx, max_idx; + int progress = 0; + + sprintf( filepath, "%scameracalibration/", ts->get_data_path().c_str() ); + sprintf( filename, "%schessboard_timing_list.dat", filepath ); + printf("Reading file %s\n", filename); + CvFileStorage* fs = cvOpenFileStorage( filename, 0, CV_STORAGE_READ ); + CvFileNode* board_list = fs ? cvGetFileNodeByName( fs, 0, "boards" ) : 0; + + if( !fs || !board_list || !CV_NODE_IS_SEQ(board_list->tag) || + board_list->data.seq->total % 4 != 0 ) + { + ts->printf( cvtest::TS::LOG, "chessboard_timing_list.dat can not be readed or is not valid" ); + code = cvtest::TS::FAIL_MISSING_TEST_DATA; + goto _exit_; + } + + max_idx = board_list->data.seq->total/4; + + for( idx = start_from; idx < max_idx; idx++ ) + { + int count0 = -1; + int count = 0; + CvSize pattern_size; + int result, result1 = 0; + + const char* imgname = cvReadString((CvFileNode*)cvGetSeqElem(board_list->data.seq,idx*4), "dummy.txt"); + int is_chessboard = cvReadInt((CvFileNode*)cvGetSeqElem(board_list->data.seq,idx*4+1), 0); + pattern_size.width = cvReadInt((CvFileNode*)cvGetSeqElem(board_list->data.seq,idx*4 + 2), -1); + pattern_size.height = cvReadInt((CvFileNode*)cvGetSeqElem(board_list->data.seq,idx*4 + 3), -1); + + ts->update_context( this, idx-1, true ); + + /* read the image */ + sprintf( filename, "%s%s", filepath, imgname ); + + img = cvLoadImage( filename ); + + if( !img ) + { + ts->printf( cvtest::TS::LOG, "one of chessboard images can't be read: %s\n", filename ); + if( max_idx == 1 ) + { + code = cvtest::TS::FAIL_MISSING_TEST_DATA; + goto _exit_; + } + continue; + } + + ts->printf(cvtest::TS::LOG, "%s: chessboard %d:\n", imgname, is_chessboard); + + gray = cvCreateImage( cvSize( img->width, img->height ), IPL_DEPTH_8U, 1 ); + thresh = cvCreateImage( cvSize( img->width, img->height ), IPL_DEPTH_8U, 1 ); + cvCvtColor( img, gray, CV_BGR2GRAY ); + + + count0 = pattern_size.width*pattern_size.height; + + /* allocate additional buffers */ + _v = cvCreateMat(1, count0, CV_32FC2); + count = count0; + + v = (CvPoint2D32f*)_v->data.fl; + + int64 _time0 = cvGetTickCount(); + result = cvCheckChessboard(gray, pattern_size); + int64 _time01 = cvGetTickCount(); + + OPENCV_CALL( result1 = cvFindChessboardCorners( + gray, pattern_size, v, &count, 15 )); + int64 _time1 = cvGetTickCount(); + + if( result != is_chessboard ) + { + ts->printf( cvtest::TS::LOG, "Error: chessboard was %sdetected in the image %s\n", + result ? "" : "not ", imgname ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + if(result != result1) + { + ts->printf( cvtest::TS::LOG, "Warning: results differ cvCheckChessboard %d, cvFindChessboardCorners %d\n", + result, result1); + } + + int num_pixels = gray->width*gray->height; + float check_chessboard_time = float(_time01 - _time0)/(float)cvGetTickFrequency(); // 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)cvGetTickFrequency(); + ts->printf(cvtest::TS::LOG, " cvFindChessboard time s: %f, us per pixel: %f\n", + find_chessboard_time*1e-6, find_chessboard_time/num_pixels); + + cvReleaseMat( &_v ); + cvReleaseImage( &img ); + cvReleaseImage( &gray ); + cvReleaseImage( &thresh ); + progress = update_progress( progress, idx-1, max_idx, 0 ); + } + +_exit_: + + /* release occupied memory */ + cvReleaseMat( &_v ); + cvReleaseFileStorage( &fs ); + cvReleaseImage( &img ); + cvReleaseImage( &gray ); + cvReleaseImage( &thresh ); + + if( code < 0 ) + ts->set_failed_test_info( code ); +} + +TEST(Calib3d_ChessboardDetector, timing) { CV_ChessboardDetectorTimingTest test; test.safe_run(); } + +/* End of file. */ diff --git a/modules/calib3d/test/test_compose_rt.cpp b/modules/calib3d/test/test_compose_rt.cpp new file mode 100644 index 000000000..b55318cd1 --- /dev/null +++ b/modules/calib3d/test/test_compose_rt.cpp @@ -0,0 +1,216 @@ +/*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" + +using namespace cv; +using namespace std; + +class Differential +{ +public: + typedef Mat_ 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_ 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) + { + cvtest::TS& ts = *this->ts; + ts.set_failed_test_info(cvtest::TS::OK); + + Mat_ 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; + Rodrigues(rvec1, rmat1); + Rodrigues(rvec2, rmat2); + Rodrigues(rmat2 * rmat1, rvec3_exp); + + tvec3_exp = rmat2 * tvec1 + tvec2; + + const double thres = 1e-5; + if (norm(rvec3_exp, rvec3) > thres || 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_ dr3_dr1, dt3_dr1; + diff.dRv1(dr3_dr1, dt3_dr1); + + if (norm(dr3_dr1, dr3dr1) > thres || 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_ dr3_dr2, dt3_dr2; + diff.dRv2(dr3_dr2, dt3_dr2); + + if (norm(dr3_dr2, dr3dr2) > thres || 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_ dr3_dt1, dt3_dt1; + diff.dTv1(dr3_dt1, dt3_dt1); + + if (norm(dr3_dt1, dr3dt1) > thres || 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_ dr3_dt2, dt3_dt2; + diff.dTv2(dr3_dt2, dt3_dt2); + + if (norm(dr3_dt2, dr3dt2) > thres || 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(); } + diff --git a/modules/calib3d/test/test_cornerssubpix.cpp b/modules/calib3d/test/test_cornerssubpix.cpp new file mode 100644 index 000000000..6afcfc605 --- /dev/null +++ b/modules/calib3d/test/test_cornerssubpix.cpp @@ -0,0 +1,242 @@ +/*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 +#include "test_chessboardgenerator.hpp" + +using namespace cv; + +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& set1, const vector& set2, double& mean_dist) +{ + if(set1.size() != set2.size()) + { + return 0; + } + + std::vector indices; + double sum_dist = 0.0; + for(size_t i = 0; i < set1.size(); i++) + { + double min_dist = std::numeric_limits::max(); + int min_idx = -1; + + for(int j = 0; j < (int)set2.size(); j++) + { + double dist = norm(set1[i] - set2[j]); + if(dist < min_dist) + { + min_idx = j; + min_dist = dist; + } + } + + // check validity of min_idx + if(min_idx == -1) + { + return 0; + } + std::vector::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 corners; + Mat chessboard_image = gen_chessboard(bg, intrinsic_matrix_, distortion_coeffs_, corners); + + vector test_corners; + bool result = findChessboardCorners(chessboard_image, pattern_size, test_corners, 15); + if(!result) + { +#if 0 + ts->printf(cvtest::TS::LOG, "Warning: chessboard was not detected! Writing image to test.jpg\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(0, 0), intrinsic_matrix_.at(1, 1), + intrinsic_matrix_.at(0, 2), intrinsic_matrix_.at(1, 2)); + ts->printf(cvtest::TS::LOG, "Distortion matrix: %f, %f, %f, %f, %f\n", + distortion_coeffs_.at(0, 0), distortion_coeffs_.at(0, 1), + distortion_coeffs_.at(0, 2), distortion_coeffs_.at(0, 3), + distortion_coeffs_.at(0, 4)); + + imwrite("test.jpg", chessboard_image); +#endif + 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 = 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 ); + } + 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_(3, 3) << fx, 0.0, cx, 0.0, fy, cy, 0.0, 0.0, 1.0); + distortion_coeffs_ = (Mat_(1, 5) << k1, k2, p1, p2, k3); +} + +TEST(Calib3d_ChessboardSubPixDetector, accuracy) { CV_ChessboardSubpixelTest test; test.safe_run(); } + +/* End of file. */ diff --git a/modules/calib3d/test/test_fundam.cpp b/modules/calib3d/test/test_fundam.cpp new file mode 100644 index 000000000..4315ee040 --- /dev/null +++ b/modules/calib3d/test/test_fundam.cpp @@ -0,0 +1,1363 @@ +/*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" + +using namespace cv; +using namespace std; + +int cvTsRodrigues( const CvMat* src, CvMat* dst, CvMat* jacobian ) +{ + int depth; + int i; + float Jf[27]; + double J[27]; + CvMat _Jf, matJ = cvMat( 3, 9, CV_64F, J ); + + depth = CV_MAT_DEPTH(src->type); + + if( jacobian ) + { + assert( (jacobian->rows == 9 && jacobian->cols == 3) || + (jacobian->rows == 3 && jacobian->cols == 9) ); + } + + if( src->cols == 1 || src->rows == 1 ) + { + double r[3], theta; + CvMat _r = cvMat( src->rows, src->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(src->type)), r); + + assert( dst->rows == 3 && dst->cols == 3 ); + + cvConvert( src, &_r ); + + theta = sqrt(r[0]*r[0] + r[1]*r[1] + r[2]*r[2]); + if( theta < DBL_EPSILON ) + { + cvSetIdentity( dst ); + + if( jacobian ) + { + memset( J, 0, sizeof(J) ); + J[5] = J[15] = J[19] = 1; + J[7] = J[11] = J[21] = -1; + } + } + else + { + // omega = r/theta (~[w1, w2, w3]) + double itheta = 1./theta; + double w1 = r[0]*itheta, w2 = r[1]*itheta, w3 = r[2]*itheta; + double alpha = cos(theta); + double beta = sin(theta); + double gamma = 1 - alpha; + double omegav[] = + { + 0, -w3, w2, + w3, 0, -w1, + -w2, w1, 0 + }; + double A[] = + { + w1*w1, w1*w2, w1*w3, + w2*w1, w2*w2, w2*w3, + w3*w1, w3*w2, w3*w3 + }; + double R[9]; + CvMat _omegav = cvMat(3, 3, CV_64F, omegav); + CvMat matA = cvMat(3, 3, CV_64F, A); + CvMat matR = cvMat(3, 3, CV_64F, R); + + cvSetIdentity( &matR, cvRealScalar(alpha) ); + cvScaleAdd( &_omegav, cvRealScalar(beta), &matR, &matR ); + cvScaleAdd( &matA, cvRealScalar(gamma), &matR, &matR ); + cvConvert( &matR, dst ); + + if( jacobian ) + { + // m3 = [r, theta] + double dm3din[] = + { + 1, 0, 0, + 0, 1, 0, + 0, 0, 1, + w1, w2, w3 + }; + // m2 = [omega, theta] + double dm2dm3[] = + { + itheta, 0, 0, -w1*itheta, + 0, itheta, 0, -w2*itheta, + 0, 0, itheta, -w3*itheta, + 0, 0, 0, 1 + }; + double t0[9*4]; + double dm1dm2[21*4]; + double dRdm1[9*21]; + CvMat _dm3din = cvMat( 4, 3, CV_64FC1, dm3din ); + CvMat _dm2dm3 = cvMat( 4, 4, CV_64FC1, dm2dm3 ); + CvMat _dm1dm2 = cvMat( 21, 4, CV_64FC1, dm1dm2 ); + CvMat _dRdm1 = cvMat( 9, 21, CV_64FC1, dRdm1 ); + CvMat _dRdm1_part; + CvMat _t0 = cvMat( 9, 4, CV_64FC1, t0 ); + CvMat _t1 = cvMat( 9, 4, CV_64FC1, dRdm1 ); + + // m1 = [alpha, beta, gamma, omegav; A] + memset( dm1dm2, 0, sizeof(dm1dm2) ); + dm1dm2[3] = -beta; + dm1dm2[7] = alpha; + dm1dm2[11] = beta; + + // dm1dm2(4:12,1:3) = [0 0 0 0 0 1 0 -1 0; + // 0 0 -1 0 0 0 1 0 0; + // 0 1 0 -1 0 0 0 0 0]' + // ------------------- + // 0 0 0 0 0 0 0 0 0 + dm1dm2[12 + 6] = dm1dm2[12 + 20] = dm1dm2[12 + 25] = 1; + dm1dm2[12 + 9] = dm1dm2[12 + 14] = dm1dm2[12 + 28] = -1; + + double dm1dw[] = + { + 2*w1, w2, w3, w2, 0, 0, w3, 0, 0, + 0, w1, 0, w1, 2*w2, w3, 0, w3, 0, + 0, 0, w1, 0, 0, w2, w1, w2, 2*w3 + }; + + CvMat _dm1dw = cvMat( 3, 9, CV_64FC1, dm1dw ); + CvMat _dm1dm2_part; + + cvGetSubRect( &_dm1dm2, &_dm1dm2_part, cvRect(0,12,3,9) ); + cvTranspose( &_dm1dw, &_dm1dm2_part ); + + memset( dRdm1, 0, sizeof(dRdm1) ); + dRdm1[0*21] = dRdm1[4*21] = dRdm1[8*21] = 1; + + cvGetCol( &_dRdm1, &_dRdm1_part, 1 ); + cvTranspose( &_omegav, &_omegav ); + cvReshape( &_omegav, &_omegav, 1, 1 ); + cvTranspose( &_omegav, &_dRdm1_part ); + + cvGetCol( &_dRdm1, &_dRdm1_part, 2 ); + cvReshape( &matA, &matA, 1, 1 ); + cvTranspose( &matA, &_dRdm1_part ); + + cvGetSubRect( &_dRdm1, &_dRdm1_part, cvRect(3,0,9,9) ); + cvSetIdentity( &_dRdm1_part, cvScalarAll(beta) ); + + cvGetSubRect( &_dRdm1, &_dRdm1_part, cvRect(12,0,9,9) ); + cvSetIdentity( &_dRdm1_part, cvScalarAll(gamma) ); + + matJ = cvMat( 9, 3, CV_64FC1, J ); + + cvMatMul( &_dRdm1, &_dm1dm2, &_t0 ); + cvMatMul( &_t0, &_dm2dm3, &_t1 ); + cvMatMul( &_t1, &_dm3din, &matJ ); + + _t0 = cvMat( 3, 9, CV_64FC1, t0 ); + cvTranspose( &matJ, &_t0 ); + + for( i = 0; i < 3; i++ ) + { + _t1 = cvMat( 3, 3, CV_64FC1, t0 + i*9 ); + cvTranspose( &_t1, &_t1 ); + } + + cvTranspose( &_t0, &matJ ); + } + } + } + else if( src->cols == 3 && src->rows == 3 ) + { + double R[9], A[9], I[9], r[3], W[3], U[9], V[9]; + double tr, alpha, beta, theta; + CvMat matR = cvMat( 3, 3, CV_64F, R ); + CvMat matA = cvMat( 3, 3, CV_64F, A ); + CvMat matI = cvMat( 3, 3, CV_64F, I ); + CvMat _r = cvMat( dst->rows, dst->cols, CV_MAKETYPE(CV_64F, CV_MAT_CN(dst->type)), r ); + CvMat matW = cvMat( 1, 3, CV_64F, W ); + CvMat matU = cvMat( 3, 3, CV_64F, U ); + CvMat matV = cvMat( 3, 3, CV_64F, V ); + + cvConvert( src, &matR ); + cvSVD( &matR, &matW, &matU, &matV, CV_SVD_MODIFY_A + CV_SVD_U_T + CV_SVD_V_T ); + cvGEMM( &matU, &matV, 1, 0, 0, &matR, CV_GEMM_A_T ); + + cvMulTransposed( &matR, &matA, 0 ); + cvSetIdentity( &matI ); + + if( cvNorm( &matA, &matI, CV_C ) > 1e-3 || + fabs( cvDet(&matR) - 1 ) > 1e-3 ) + return 0; + + tr = (cvTrace(&matR).val[0] - 1.)*0.5; + tr = tr > 1. ? 1. : tr < -1. ? -1. : tr; + theta = acos(tr); + alpha = cos(theta); + beta = sin(theta); + + if( beta >= 1e-5 ) + { + double dtheta_dtr = -1./sqrt(1 - tr*tr); + double vth = 1/(2*beta); + + // om1 = [R(3,2) - R(2,3), R(1,3) - R(3,1), R(2,1) - R(1,2)]' + double om1[] = { R[7] - R[5], R[2] - R[6], R[3] - R[1] }; + // om = om1*vth + // r = om*theta + double d3 = vth*theta; + + r[0] = om1[0]*d3; r[1] = om1[1]*d3; r[2] = om1[2]*d3; + cvConvert( &_r, dst ); + + if( jacobian ) + { + // var1 = [vth;theta] + // var = [om1;var1] = [om1;vth;theta] + double dvth_dtheta = -vth*alpha/beta; + double d1 = 0.5*dvth_dtheta*dtheta_dtr; + double d2 = 0.5*dtheta_dtr; + // dvar1/dR = dvar1/dtheta*dtheta/dR = [dvth/dtheta; 1] * dtheta/dtr * dtr/dR + double dvardR[5*9] = + { + 0, 0, 0, 0, 0, 1, 0, -1, 0, + 0, 0, -1, 0, 0, 0, 1, 0, 0, + 0, 1, 0, -1, 0, 0, 0, 0, 0, + d1, 0, 0, 0, d1, 0, 0, 0, d1, + d2, 0, 0, 0, d2, 0, 0, 0, d2 + }; + // var2 = [om;theta] + double dvar2dvar[] = + { + vth, 0, 0, om1[0], 0, + 0, vth, 0, om1[1], 0, + 0, 0, vth, om1[2], 0, + 0, 0, 0, 0, 1 + }; + double domegadvar2[] = + { + theta, 0, 0, om1[0]*vth, + 0, theta, 0, om1[1]*vth, + 0, 0, theta, om1[2]*vth + }; + + CvMat _dvardR = cvMat( 5, 9, CV_64FC1, dvardR ); + CvMat _dvar2dvar = cvMat( 4, 5, CV_64FC1, dvar2dvar ); + CvMat _domegadvar2 = cvMat( 3, 4, CV_64FC1, domegadvar2 ); + double t0[3*5]; + CvMat _t0 = cvMat( 3, 5, CV_64FC1, t0 ); + + cvMatMul( &_domegadvar2, &_dvar2dvar, &_t0 ); + cvMatMul( &_t0, &_dvardR, &matJ ); + } + } + else if( tr > 0 ) + { + cvZero( dst ); + if( jacobian ) + { + memset( J, 0, sizeof(J) ); + J[5] = J[15] = J[19] = 0.5; + J[7] = J[11] = J[21] = -0.5; + } + } + else + { + r[0] = theta*sqrt((R[0] + 1)*0.5); + r[1] = theta*sqrt((R[4] + 1)*0.5)*(R[1] >= 0 ? 1 : -1); + r[2] = theta*sqrt((R[8] + 1)*0.5)*(R[2] >= 0 ? 1 : -1); + cvConvert( &_r, dst ); + + if( jacobian ) + memset( J, 0, sizeof(J) ); + } + + if( jacobian ) + { + for( i = 0; i < 3; i++ ) + { + CvMat t = cvMat( 3, 3, CV_64F, J + i*9 ); + cvTranspose( &t, &t ); + } + } + } + else + { + assert(0); + return 0; + } + + if( jacobian ) + { + if( depth == CV_32F ) + { + if( jacobian->rows == matJ.rows ) + cvConvert( &matJ, jacobian ); + else + { + _Jf = cvMat( matJ.rows, matJ.cols, CV_32FC1, Jf ); + cvConvert( &matJ, &_Jf ); + cvTranspose( &_Jf, jacobian ); + } + } + else if( jacobian->rows == matJ.rows ) + cvCopy( &matJ, jacobian ); + else + cvTranspose( &matJ, jacobian ); + } + + return 1; +} + + +void cvtest::Rodrigues(const Mat& src, Mat& dst, Mat* jac) +{ + CvMat _src = src, _dst = dst, _jac; + if( jac ) + _jac = *jac; + cvTsRodrigues(&_src, &_dst, jac ? &_jac : 0); +} + + +static void test_convertHomogeneous( const Mat& _src, Mat& _dst ) +{ + Mat src = _src, dst = _dst; + int i, count, sdims, ddims; + int sstep1, sstep2, dstep1, dstep2; + + if( src.depth() != CV_64F ) + _src.convertTo(src, CV_64F); + + if( dst.depth() != CV_64F ) + dst.create(dst.size(), CV_MAKETYPE(CV_64F, _dst.channels())); + + if( src.rows > src.cols ) + { + count = src.rows; + sdims = src.channels()*src.cols; + sstep1 = src.step/sizeof(double); + sstep2 = 1; + } + else + { + count = src.cols; + sdims = src.channels()*src.rows; + if( src.rows == 1 ) + { + sstep1 = sdims; + sstep2 = 1; + } + else + { + sstep1 = 1; + sstep2 = src.step/sizeof(double); + } + } + + if( dst.rows > dst.cols ) + { + CV_Assert( count == dst.rows ); + ddims = dst.channels()*dst.cols; + dstep1 = dst.step/sizeof(double); + dstep2 = 1; + } + else + { + assert( count == dst.cols ); + ddims = dst.channels()*dst.rows; + if( dst.rows == 1 ) + { + dstep1 = ddims; + dstep2 = 1; + } + else + { + dstep1 = 1; + dstep2 = dst.step/sizeof(double); + } + } + + double* s = src.ptr(); + double* d = dst.ptr(); + + if( sdims <= ddims ) + { + int wstep = dstep2*(ddims - 1); + + for( i = 0; i < count; i++, s += sstep1, d += dstep1 ) + { + double x = s[0]; + double y = s[sstep2]; + + d[wstep] = 1; + d[0] = x; + d[dstep2] = y; + + if( sdims >= 3 ) + { + d[dstep2*2] = s[sstep2*2]; + if( sdims == 4 ) + d[dstep2*3] = s[sstep2*3]; + } + } + } + else + { + int wstep = sstep2*(sdims - 1); + + for( i = 0; i < count; i++, s += sstep1, d += dstep1 ) + { + double w = s[wstep]; + double x = s[0]; + double y = s[sstep2]; + + w = w ? 1./w : 1; + + d[0] = x*w; + d[dstep2] = y*w; + + if( ddims == 3 ) + d[dstep2*2] = s[sstep2*2]*w; + } + } + + if( dst.data != _dst.data ) + dst.convertTo(_dst, _dst.depth()); +} + + +void +test_projectPoints( const Mat& _3d, const Mat& Rt, const Mat& A, Mat& _2d, RNG* rng, double sigma ) +{ + CV_Assert( _3d.isContinuous() ); + + double p[12]; + Mat P( 3, 4, CV_64F, p ); + gemm(A, Rt, 1, Mat(), 0, P); + + int i, count = _3d.cols; + + Mat noise; + if( rng ) + { + if( sigma == 0 ) + rng = 0; + else + { + noise.create( 1, _3d.cols, CV_64FC2 ); + rng->fill(noise, RNG::NORMAL, Scalar::all(0), Scalar::all(sigma) ); + } + } + + Mat temp( 1, count, CV_64FC3 ); + + for( i = 0; i < count; i++ ) + { + const double* M = _3d.ptr() + i*3; + double* m = temp.ptr() + i*3; + double X = M[0], Y = M[1], Z = M[2]; + double u = p[0]*X + p[1]*Y + p[2]*Z + p[3]; + double v = p[4]*X + p[5]*Y + p[6]*Z + p[7]; + double s = p[8]*X + p[9]*Y + p[10]*Z + p[11]; + + if( !noise.empty() ) + { + u += noise.at(i).x*s; + v += noise.at(i).y*s; + } + + m[0] = u; + m[1] = v; + m[2] = s; + } + + test_convertHomogeneous( temp, _2d ); +} + + +/********************************** Rodrigues transform ********************************/ + +class CV_RodriguesTest : public cvtest::ArrayTest +{ +public: + CV_RodriguesTest(); + +protected: + int read_params( CvFileStorage* fs ); + void fill_array( int test_case_idx, int i, int j, Mat& arr ); + int prepare_test_case( int test_case_idx ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int ); + + bool calc_jacobians; + bool test_cpp; +}; + + +CV_RodriguesTest::CV_RodriguesTest() +{ + test_array[INPUT].push_back(NULL); // rotation vector + test_array[OUTPUT].push_back(NULL); // rotation matrix + test_array[OUTPUT].push_back(NULL); // jacobian (J) + test_array[OUTPUT].push_back(NULL); // rotation vector (backward transform result) + test_array[OUTPUT].push_back(NULL); // inverse transform jacobian (J1) + test_array[OUTPUT].push_back(NULL); // J*J1 (or J1*J) == I(3x3) + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + + element_wise_relative_error = false; + calc_jacobians = false; + + test_cpp = false; +} + + +int CV_RodriguesTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::ArrayTest::read_params( fs ); + return code; +} + + +void CV_RodriguesTest::get_test_array_types_and_sizes( + int /*test_case_idx*/, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int depth = cvtest::randInt(rng) % 2 == 0 ? CV_32F : CV_64F; + int i, code; + + code = cvtest::randInt(rng) % 3; + types[INPUT][0] = CV_MAKETYPE(depth, 1); + + if( code == 0 ) + { + sizes[INPUT][0] = cvSize(1,1); + types[INPUT][0] = CV_MAKETYPE(depth, 3); + } + else if( code == 1 ) + sizes[INPUT][0] = cvSize(3,1); + else + sizes[INPUT][0] = cvSize(1,3); + + sizes[OUTPUT][0] = cvSize(3, 3); + types[OUTPUT][0] = CV_MAKETYPE(depth, 1); + + types[OUTPUT][1] = CV_MAKETYPE(depth, 1); + + if( cvtest::randInt(rng) % 2 ) + sizes[OUTPUT][1] = cvSize(3,9); + else + sizes[OUTPUT][1] = cvSize(9,3); + + types[OUTPUT][2] = types[INPUT][0]; + sizes[OUTPUT][2] = sizes[INPUT][0]; + + types[OUTPUT][3] = types[OUTPUT][1]; + sizes[OUTPUT][3] = cvSize(sizes[OUTPUT][1].height, sizes[OUTPUT][1].width); + + types[OUTPUT][4] = types[OUTPUT][1]; + sizes[OUTPUT][4] = cvSize(3,3); + + calc_jacobians = cvtest::randInt(rng) % 3 != 0; + if( !calc_jacobians ) + sizes[OUTPUT][1] = sizes[OUTPUT][3] = sizes[OUTPUT][4] = cvSize(0,0); + + for( i = 0; i < 5; i++ ) + { + types[REF_OUTPUT][i] = types[OUTPUT][i]; + sizes[REF_OUTPUT][i] = sizes[OUTPUT][i]; + } + test_cpp = (cvtest::randInt(rng) & 256) == 0; +} + + +double CV_RodriguesTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int j ) +{ + return j == 4 ? 1e-2 : 1e-2; +} + + +void CV_RodriguesTest::fill_array( int test_case_idx, int i, int j, Mat& arr ) +{ + if( i == INPUT && j == 0 ) + { + double r[3], theta0, theta1, f; + Mat _r( arr.rows, arr.cols, CV_MAKETYPE(CV_64F,arr.channels()), r ); + RNG& rng = ts->get_rng(); + + r[0] = cvtest::randReal(rng)*CV_PI*2; + r[1] = cvtest::randReal(rng)*CV_PI*2; + r[2] = cvtest::randReal(rng)*CV_PI*2; + + theta0 = sqrt(r[0]*r[0] + r[1]*r[1] + r[2]*r[2]); + theta1 = fmod(theta0, CV_PI*2); + + if( theta1 > CV_PI ) + theta1 = -(CV_PI*2 - theta1); + + f = theta1/(theta0 ? theta0 : 1); + r[0] *= f; + r[1] *= f; + r[2] *= f; + + cvtest::convert( _r, arr, arr.type() ); + } + else + cvtest::ArrayTest::fill_array( test_case_idx, i, j, arr ); +} + + +int CV_RodriguesTest::prepare_test_case( int test_case_idx ) +{ + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + return code; +} + + +void CV_RodriguesTest::run_func() +{ + CvMat v2m_jac, m2v_jac; + + if( calc_jacobians ) + { + v2m_jac = test_mat[OUTPUT][1]; + m2v_jac = test_mat[OUTPUT][3]; + } + + if( !test_cpp ) + { + CvMat _input = test_mat[INPUT][0], _output = test_mat[OUTPUT][0], _output2 = test_mat[OUTPUT][2]; + cvRodrigues2( &_input, &_output, calc_jacobians ? &v2m_jac : 0 ); + cvRodrigues2( &_output, &_output2, calc_jacobians ? &m2v_jac : 0 ); + } + else + { + cv::Mat v = test_mat[INPUT][0], M = test_mat[OUTPUT][0], v2 = test_mat[OUTPUT][2]; + cv::Mat M0 = M, v2_0 = v2; + if( !calc_jacobians ) + { + cv::Rodrigues(v, M); + cv::Rodrigues(M, v2); + } + else + { + cv::Mat J1 = test_mat[OUTPUT][1], J2 = test_mat[OUTPUT][3]; + cv::Mat J1_0 = J1, J2_0 = J2; + cv::Rodrigues(v, M, J1); + cv::Rodrigues(M, v2, J2); + if( J1.data != J1_0.data ) + { + if( J1.size() != J1_0.size() ) + J1 = J1.t(); + J1.convertTo(J1_0, J1_0.type()); + } + if( J2.data != J2_0.data ) + { + if( J2.size() != J2_0.size() ) + J2 = J2.t(); + J2.convertTo(J2_0, J2_0.type()); + } + } + if( M.data != M0.data ) + M.reshape(M0.channels(), M0.rows).convertTo(M0, M0.type()); + if( v2.data != v2_0.data ) + v2.reshape(v2_0.channels(), v2_0.rows).convertTo(v2_0, v2_0.type()); + } +} + + +void CV_RodriguesTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + const Mat& vec = test_mat[INPUT][0]; + Mat& m = test_mat[REF_OUTPUT][0]; + Mat& vec2 = test_mat[REF_OUTPUT][2]; + Mat* v2m_jac = 0, *m2v_jac = 0; + double theta0, theta1; + + if( calc_jacobians ) + { + v2m_jac = &test_mat[REF_OUTPUT][1]; + m2v_jac = &test_mat[REF_OUTPUT][3]; + } + + + cvtest::Rodrigues( vec, m, v2m_jac ); + cvtest::Rodrigues( m, vec2, m2v_jac ); + cvtest::copy( vec, vec2 ); + + theta0 = norm( vec2, CV_L2 ); + theta1 = fmod( theta0, CV_PI*2 ); + + if( theta1 > CV_PI ) + theta1 = -(CV_PI*2 - theta1); + vec2 *= theta1/(theta0 ? theta0 : 1); + + if( calc_jacobians ) + { + //cvInvert( v2m_jac, m2v_jac, CV_SVD ); + double nrm = norm(test_mat[REF_OUTPUT][3],CV_C); + if( FLT_EPSILON < nrm && nrm < 1000 ) + { + gemm( test_mat[OUTPUT][1], test_mat[OUTPUT][3], + 1, Mat(), 0, test_mat[OUTPUT][4], + v2m_jac->rows == 3 ? 0 : CV_GEMM_A_T + CV_GEMM_B_T ); + } + else + { + setIdentity(test_mat[OUTPUT][4], Scalar::all(1.)); + cvtest::copy( test_mat[REF_OUTPUT][2], test_mat[OUTPUT][2] ); + } + setIdentity(test_mat[REF_OUTPUT][4], Scalar::all(1.)); + } +} + + +/********************************** fundamental matrix *********************************/ + +class CV_FundamentalMatTest : public cvtest::ArrayTest +{ +public: + CV_FundamentalMatTest(); + +protected: + int read_params( CvFileStorage* fs ); + void fill_array( int test_case_idx, int i, int j, Mat& arr ); + int prepare_test_case( int test_case_idx ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int ); + + int method; + int img_size; + int cube_size; + int dims; + int f_result; + double min_f, max_f; + double sigma; + bool test_cpp; +}; + + +CV_FundamentalMatTest::CV_FundamentalMatTest() +{ + // input arrays: + // 0, 1 - arrays of 2d points that are passed to %func%. + // Can have different data type, layout, be stored in homogeneous coordinates or not. + // 2 - array of 3d points that are projected to both view planes + // 3 - [R|t] matrix for the second view plane (for the first one it is [I|0] + // 4, 5 - intrinsic matrices + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + test_array[TEMP].push_back(NULL); + test_array[TEMP].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + + element_wise_relative_error = false; + + method = 0; + img_size = 10; + cube_size = 10; + min_f = 1; + max_f = 3; + sigma = 0;//0.1; + f_result = 0; + + test_cpp = false; +} + + +int CV_FundamentalMatTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::ArrayTest::read_params( fs ); + return code; +} + + +void CV_FundamentalMatTest::get_test_array_types_and_sizes( int /*test_case_idx*/, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int pt_depth = cvtest::randInt(rng) % 2 == 0 ? CV_32F : CV_64F; + double pt_count_exp = cvtest::randReal(rng)*6 + 1; + int pt_count = cvRound(exp(pt_count_exp)); + + dims = cvtest::randInt(rng) % 2 + 2; + method = 1 << (cvtest::randInt(rng) % 4); + + if( method == CV_FM_7POINT ) + pt_count = 7; + else + { + pt_count = MAX( pt_count, 8 + (method == CV_FM_8POINT) ); + if( pt_count >= 8 && cvtest::randInt(rng) % 2 ) + method |= CV_FM_8POINT; + } + + types[INPUT][0] = CV_MAKETYPE(pt_depth, 1); + + if( cvtest::randInt(rng) % 2 ) + sizes[INPUT][0] = cvSize(pt_count, dims); + else + { + sizes[INPUT][0] = cvSize(dims, pt_count); + if( cvtest::randInt(rng) % 2 ) + { + types[INPUT][0] = CV_MAKETYPE(pt_depth, dims); + if( cvtest::randInt(rng) % 2 ) + sizes[INPUT][0] = cvSize(pt_count, 1); + else + sizes[INPUT][0] = cvSize(1, pt_count); + } + } + + sizes[INPUT][1] = sizes[INPUT][0]; + types[INPUT][1] = types[INPUT][0]; + + sizes[INPUT][2] = cvSize(pt_count, 1 ); + types[INPUT][2] = CV_64FC3; + + sizes[INPUT][3] = cvSize(4,3); + types[INPUT][3] = CV_64FC1; + + sizes[INPUT][4] = sizes[INPUT][5] = cvSize(3,3); + types[INPUT][4] = types[INPUT][5] = CV_MAKETYPE(CV_64F, 1); + + sizes[TEMP][0] = cvSize(3,3); + types[TEMP][0] = CV_64FC1; + sizes[TEMP][1] = cvSize(pt_count,1); + types[TEMP][1] = CV_8UC1; + + sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = cvSize(3,1); + types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_64FC1; + sizes[OUTPUT][1] = sizes[REF_OUTPUT][1] = cvSize(pt_count,1); + types[OUTPUT][1] = types[REF_OUTPUT][1] = CV_8UC1; + + test_cpp = (cvtest::randInt(rng) & 256) == 0; +} + + +double CV_FundamentalMatTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return 1e-2; +} + + +void CV_FundamentalMatTest::fill_array( int test_case_idx, int i, int j, Mat& arr ) +{ + double t[12]={0}; + RNG& rng = ts->get_rng(); + + if( i != INPUT ) + { + cvtest::ArrayTest::fill_array( test_case_idx, i, j, arr ); + return; + } + + switch( j ) + { + case 0: + case 1: + return; // fill them later in prepare_test_case + case 2: + { + double* p = arr.ptr(); + for( i = 0; i < arr.cols*3; i += 3 ) + { + p[i] = cvtest::randReal(rng)*cube_size; + p[i+1] = cvtest::randReal(rng)*cube_size; + p[i+2] = cvtest::randReal(rng)*cube_size + cube_size; + } + } + break; + case 3: + { + double r[3]; + Mat rot_vec( 3, 1, CV_64F, r ); + Mat rot_mat( 3, 3, CV_64F, t, 4*sizeof(t[0]) ); + r[0] = cvtest::randReal(rng)*CV_PI*2; + r[1] = cvtest::randReal(rng)*CV_PI*2; + r[2] = cvtest::randReal(rng)*CV_PI*2; + + cvtest::Rodrigues( rot_vec, rot_mat ); + t[3] = cvtest::randReal(rng)*cube_size; + t[7] = cvtest::randReal(rng)*cube_size; + t[11] = cvtest::randReal(rng)*cube_size; + Mat( 3, 4, CV_64F, t ).convertTo(arr, arr.type()); + } + break; + case 4: + case 5: + t[0] = t[4] = cvtest::randReal(rng)*(max_f - min_f) + min_f; + t[2] = (img_size*0.5 + cvtest::randReal(rng)*4. - 2.)*t[0]; + t[5] = (img_size*0.5 + cvtest::randReal(rng)*4. - 2.)*t[4]; + t[8] = 1.; + Mat( 3, 3, CV_64F, t ).convertTo( arr, arr.type() ); + break; + } +} + + +int CV_FundamentalMatTest::prepare_test_case( int test_case_idx ) +{ + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + const Mat& _3d = test_mat[INPUT][2]; + RNG& rng = ts->get_rng(); + double Idata[] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0 }; + Mat I( 3, 4, CV_64F, Idata ); + int k; + + for( k = 0; k < 2; k++ ) + { + const Mat& Rt = k == 0 ? I : test_mat[INPUT][3]; + const Mat& A = test_mat[INPUT][k == 0 ? 4 : 5]; + Mat& _2d = test_mat[INPUT][k]; + + test_projectPoints( _3d, Rt, A, _2d, &rng, sigma ); + } + } + + return code; +} + + +void CV_FundamentalMatTest::run_func() +{ + //if(!test_cpp) + { + CvMat _input0 = test_mat[INPUT][0], _input1 = test_mat[INPUT][1]; + CvMat F = test_mat[TEMP][0], mask = test_mat[TEMP][1]; + f_result = cvFindFundamentalMat( &_input0, &_input1, &F, method, MAX(sigma*3, 0.01), 0, &mask ); + } + /*else + { + cv::findFundamentalMat(const Mat& points1, const Mat& points2, + vector& mask, int method=FM_RANSAC, + double param1=3., double param2=0.99 ); + + CV_EXPORTS Mat findFundamentalMat( const Mat& points1, const Mat& points2, + int method=FM_RANSAC, + double param1=3., double param2=0.99 ); + }*/ + +} + + +void CV_FundamentalMatTest::prepare_to_validation( int test_case_idx ) +{ + const Mat& Rt = test_mat[INPUT][3]; + const Mat& A1 = test_mat[INPUT][4]; + const Mat& A2 = test_mat[INPUT][5]; + double f0[9], f[9]; + Mat F0(3, 3, CV_64FC1, f0), F(3, 3, CV_64F, f); + + Mat invA1, invA2, R=Rt.colRange(0, 3), T; + + cv::invert(A1, invA1, CV_SVD); + cv::invert(A2, invA2, CV_SVD); + + double tx = Rt.at(0, 3); + double ty = Rt.at(1, 3); + double tz = Rt.at(2, 3); + + double _t_x[] = { 0, -tz, ty, tz, 0, -tx, -ty, tx, 0 }; + + // F = (A2^-T)*[t]_x*R*(A1^-1) + cv::gemm( invA2, Mat( 3, 3, CV_64F, _t_x ), 1, Mat(), 0, T, CV_GEMM_A_T ); + cv::gemm( R, invA1, 1, Mat(), 0, invA2 ); + cv::gemm( T, invA2, 1, Mat(), 0, F0 ); + F0 *= 1./f0[8]; + + uchar* status = test_mat[TEMP][1].data; + double err_level = get_success_error_level( test_case_idx, OUTPUT, 1 ); + uchar* mtfm1 = test_mat[REF_OUTPUT][1].data; + uchar* mtfm2 = test_mat[OUTPUT][1].data; + double* f_prop1 = (double*)test_mat[REF_OUTPUT][0].data; + double* f_prop2 = (double*)test_mat[OUTPUT][0].data; + + int i, pt_count = test_mat[INPUT][2].cols; + Mat p1( 1, pt_count, CV_64FC2 ); + Mat p2( 1, pt_count, CV_64FC2 ); + + test_convertHomogeneous( test_mat[INPUT][0], p1 ); + test_convertHomogeneous( test_mat[INPUT][1], p2 ); + + cvtest::convert(test_mat[TEMP][0], F, F.type()); + + if( method <= CV_FM_8POINT ) + memset( status, 1, pt_count ); + + for( i = 0; i < pt_count; i++ ) + { + double x1 = p1.at(i).x; + double y1 = p1.at(i).y; + double x2 = p2.at(i).x; + double y2 = p2.at(i).y; + double n1 = 1./sqrt(x1*x1 + y1*y1 + 1); + double n2 = 1./sqrt(x2*x2 + y2*y2 + 1); + double t0 = fabs(f0[0]*x2*x1 + f0[1]*x2*y1 + f0[2]*x2 + + f0[3]*y2*x1 + f0[4]*y2*y1 + f0[5]*y2 + + f0[6]*x1 + f0[7]*y1 + f0[8])*n1*n2; + double t = fabs(f[0]*x2*x1 + f[1]*x2*y1 + f[2]*x2 + + f[3]*y2*x1 + f[4]*y2*y1 + f[5]*y2 + + f[6]*x1 + f[7]*y1 + f[8])*n1*n2; + mtfm1[i] = 1; + mtfm2[i] = !status[i] || t0 > err_level || t < err_level; + } + + f_prop1[0] = 1; + f_prop1[1] = 1; + f_prop1[2] = 0; + + f_prop2[0] = f_result != 0; + f_prop2[1] = f[8]; + f_prop2[2] = cv::determinant( F ); +} + + +/********************************** convert homogeneous *********************************/ + +class CV_ConvertHomogeneousTest : public cvtest::ArrayTest +{ +public: + CV_ConvertHomogeneousTest(); + +protected: + int read_params( CvFileStorage* fs ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void fill_array( int test_case_idx, int i, int j, Mat& arr ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int ); + + int dims1, dims2; + int pt_count; +}; + + +CV_ConvertHomogeneousTest::CV_ConvertHomogeneousTest() +{ + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + element_wise_relative_error = false; + + pt_count = dims1 = dims2 = 0; +} + + +int CV_ConvertHomogeneousTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::ArrayTest::read_params( fs ); + return code; +} + + +void CV_ConvertHomogeneousTest::get_test_array_types_and_sizes( int /*test_case_idx*/, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int pt_depth1 = cvtest::randInt(rng) % 2 == 0 ? CV_32F : CV_64F; + int pt_depth2 = cvtest::randInt(rng) % 2 == 0 ? CV_32F : CV_64F; + double pt_count_exp = cvtest::randReal(rng)*6 + 1; + int t; + + pt_count = cvRound(exp(pt_count_exp)); + pt_count = MAX( pt_count, 5 ); + + dims1 = 2 + (cvtest::randInt(rng) % 3); + dims2 = 2 + (cvtest::randInt(rng) % 3); + + if( dims1 == dims2 + 2 ) + dims1--; + else if( dims1 == dims2 - 2 ) + dims1++; + + if( cvtest::randInt(rng) % 2 ) + CV_SWAP( dims1, dims2, t ); + + types[INPUT][0] = CV_MAKETYPE(pt_depth1, 1); + + if( cvtest::randInt(rng) % 2 ) + sizes[INPUT][0] = cvSize(pt_count, dims1); + else + { + sizes[INPUT][0] = cvSize(dims1, pt_count); + if( cvtest::randInt(rng) % 2 ) + { + types[INPUT][0] = CV_MAKETYPE(pt_depth1, dims1); + if( cvtest::randInt(rng) % 2 ) + sizes[INPUT][0] = cvSize(pt_count, 1); + else + sizes[INPUT][0] = cvSize(1, pt_count); + } + } + + types[OUTPUT][0] = CV_MAKETYPE(pt_depth2, 1); + + if( cvtest::randInt(rng) % 2 ) + sizes[OUTPUT][0] = cvSize(pt_count, dims2); + else + { + sizes[OUTPUT][0] = cvSize(dims2, pt_count); + if( cvtest::randInt(rng) % 2 ) + { + types[OUTPUT][0] = CV_MAKETYPE(pt_depth2, dims2); + if( cvtest::randInt(rng) % 2 ) + sizes[OUTPUT][0] = cvSize(pt_count, 1); + else + sizes[OUTPUT][0] = cvSize(1, pt_count); + } + } + + types[REF_OUTPUT][0] = types[OUTPUT][0]; + sizes[REF_OUTPUT][0] = sizes[OUTPUT][0]; +} + + +double CV_ConvertHomogeneousTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return 1e-5; +} + + +void CV_ConvertHomogeneousTest::fill_array( int /*test_case_idx*/, int /*i*/, int /*j*/, Mat& arr ) +{ + Mat temp( 1, pt_count, CV_MAKETYPE(CV_64FC1,dims1) ); + RNG& rng = ts->get_rng(); + CvScalar low = cvScalarAll(0), high = cvScalarAll(10); + + if( dims1 > dims2 ) + low.val[dims1-1] = 1.; + + cvtest::randUni( rng, temp, low, high ); + test_convertHomogeneous( temp, arr ); +} + + +void CV_ConvertHomogeneousTest::run_func() +{ + CvMat _input = test_mat[INPUT][0], _output = test_mat[OUTPUT][0]; + cvConvertPointsHomogeneous( &_input, &_output ); +} + + +void CV_ConvertHomogeneousTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + test_convertHomogeneous( test_mat[INPUT][0], test_mat[REF_OUTPUT][0] ); +} + + +/************************** compute corresponding epipolar lines ************************/ + +class CV_ComputeEpilinesTest : public cvtest::ArrayTest +{ +public: + CV_ComputeEpilinesTest(); + +protected: + int read_params( CvFileStorage* fs ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void fill_array( int test_case_idx, int i, int j, Mat& arr ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int ); + + int which_image; + int dims; + int pt_count; +}; + + +CV_ComputeEpilinesTest::CV_ComputeEpilinesTest() +{ + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + element_wise_relative_error = false; + + pt_count = dims = which_image = 0; +} + + +int CV_ComputeEpilinesTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::ArrayTest::read_params( fs ); + return code; +} + + +void CV_ComputeEpilinesTest::get_test_array_types_and_sizes( int /*test_case_idx*/, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int fm_depth = cvtest::randInt(rng) % 2 == 0 ? CV_32F : CV_64F; + int pt_depth = cvtest::randInt(rng) % 2 == 0 ? CV_32F : CV_64F; + int ln_depth = cvtest::randInt(rng) % 2 == 0 ? CV_32F : CV_64F; + double pt_count_exp = cvtest::randReal(rng)*6 + 1; + + which_image = 1 + (cvtest::randInt(rng) % 2); + + pt_count = cvRound(exp(pt_count_exp)); + pt_count = MAX( pt_count, 5 ); + + dims = 2 + (cvtest::randInt(rng) % 2); + + types[INPUT][0] = CV_MAKETYPE(pt_depth, 1); + + if( cvtest::randInt(rng) % 2 ) + sizes[INPUT][0] = cvSize(pt_count, dims); + else + { + sizes[INPUT][0] = cvSize(dims, pt_count); + if( cvtest::randInt(rng) % 2 ) + { + types[INPUT][0] = CV_MAKETYPE(pt_depth, dims); + if( cvtest::randInt(rng) % 2 ) + sizes[INPUT][0] = cvSize(pt_count, 1); + else + sizes[INPUT][0] = cvSize(1, pt_count); + } + } + + types[INPUT][1] = CV_MAKETYPE(fm_depth, 1); + sizes[INPUT][1] = cvSize(3, 3); + + types[OUTPUT][0] = CV_MAKETYPE(ln_depth, 1); + + if( cvtest::randInt(rng) % 2 ) + sizes[OUTPUT][0] = cvSize(pt_count, 3); + else + { + sizes[OUTPUT][0] = cvSize(3, pt_count); + if( cvtest::randInt(rng) % 2 ) + { + types[OUTPUT][0] = CV_MAKETYPE(ln_depth, 3); + if( cvtest::randInt(rng) % 2 ) + sizes[OUTPUT][0] = cvSize(pt_count, 1); + else + sizes[OUTPUT][0] = cvSize(1, pt_count); + } + } + + types[REF_OUTPUT][0] = types[OUTPUT][0]; + sizes[REF_OUTPUT][0] = sizes[OUTPUT][0]; +} + + +double CV_ComputeEpilinesTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return 1e-5; +} + + +void CV_ComputeEpilinesTest::fill_array( int test_case_idx, int i, int j, Mat& arr ) +{ + RNG& rng = ts->get_rng(); + + if( i == INPUT && j == 0 ) + { + Mat temp( 1, pt_count, CV_MAKETYPE(CV_64FC1,dims) ); + cvtest::randUni( rng, temp, cvScalar(0,0,1), cvScalarAll(10) ); + test_convertHomogeneous( temp, arr ); + } + else if( i == INPUT && j == 1 ) + cvtest::randUni( rng, arr, cvScalarAll(0), cvScalarAll(10) ); + else + cvtest::ArrayTest::fill_array( test_case_idx, i, j, arr ); +} + + +void CV_ComputeEpilinesTest::run_func() +{ + CvMat _points = test_mat[INPUT][0], _F = test_mat[INPUT][1], _lines = test_mat[OUTPUT][0]; + cvComputeCorrespondEpilines( &_points, which_image, &_F, &_lines ); +} + + +void CV_ComputeEpilinesTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + Mat pt( 1, pt_count, CV_MAKETYPE(CV_64F, 3) ); + Mat lines( 1, pt_count, CV_MAKETYPE(CV_64F, 3) ); + double f[9]; + Mat F( 3, 3, CV_64F, f ); + + test_convertHomogeneous( test_mat[INPUT][0], pt ); + test_mat[INPUT][1].convertTo(F, CV_64F); + if( which_image == 2 ) + cv::transpose( F, F ); + + for( int i = 0; i < pt_count; i++ ) + { + double* p = pt.ptr() + i*3; + double* l = lines.ptr() + i*3; + double t0 = f[0]*p[0] + f[1]*p[1] + f[2]*p[2]; + double t1 = f[3]*p[0] + f[4]*p[1] + f[5]*p[2]; + double t2 = f[6]*p[0] + f[7]*p[1] + f[8]*p[2]; + double d = sqrt(t0*t0 + t1*t1); + d = d ? 1./d : 1.; + l[0] = t0*d; l[1] = t1*d; l[2] = t2*d; + } + + test_convertHomogeneous( lines, test_mat[REF_OUTPUT][0] ); +} + + +TEST(Calib3d_Rodrigues, accuracy) { CV_RodriguesTest test; test.safe_run(); } +TEST(Calib3d_FindFundamentalMat, accuracy) { CV_FundamentalMatTest test; test.safe_run(); } +TEST(Calib3d_ConvertHomogeneoous, accuracy) { CV_ConvertHomogeneousTest test; test.safe_run(); } +TEST(Calib3d_ComputeEpilines, accuracy) { CV_ComputeEpilinesTest test; test.safe_run(); } + +/* End of file. */ diff --git a/modules/calib3d/test/test_main.cpp b/modules/calib3d/test/test_main.cpp new file mode 100644 index 000000000..6b2499344 --- /dev/null +++ b/modules/calib3d/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("cv") diff --git a/modules/calib3d/test/test_posit.cpp b/modules/calib3d/test/test_posit.cpp new file mode 100644 index 000000000..7bf187af9 --- /dev/null +++ b/modules/calib3d/test/test_posit.cpp @@ -0,0 +1,221 @@ +/*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" + +using namespace cv; +using namespace std; + +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; + + /* Initilization */ + 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(); } + +/* End of file. */ diff --git a/modules/calib3d/test/test_precomp.cpp b/modules/calib3d/test/test_precomp.cpp new file mode 100644 index 000000000..5956e13e3 --- /dev/null +++ b/modules/calib3d/test/test_precomp.cpp @@ -0,0 +1 @@ +#include "test_precomp.hpp" diff --git a/modules/calib3d/test/test_precomp.hpp b/modules/calib3d/test/test_precomp.hpp new file mode 100644 index 000000000..43792d656 --- /dev/null +++ b/modules/calib3d/test/test_precomp.hpp @@ -0,0 +1,17 @@ +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/calib3d/calib3d.hpp" +#include "opencv2/highgui/highgui.hpp" +#include + +namespace cvtest +{ + void Rodrigues(const Mat& src, Mat& dst, Mat* jac=0); +} + +#endif + diff --git a/modules/calib3d/test/test_reproject_image_to_3d.cpp b/modules/calib3d/test/test_reproject_image_to_3d.cpp new file mode 100644 index 000000000..3e6c6c642 --- /dev/null +++ b/modules/calib3d/test/test_reproject_image_to_3d.cpp @@ -0,0 +1,175 @@ +/*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 +#include + +using namespace cv; +using namespace std; + +template double thres() { return 1.0; } +template<> double thres() { 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(++caseId, -100.f, 100.f); + progress = update_progress( progress, 2, 14, 0 ); + runCase(++caseId, -100, 100); + progress = update_progress( progress, 3, 14, 0 ); + runCase(++caseId, -100, 100); + progress = update_progress( progress, 4, 14, 0 ); + runCase(++caseId, 10, 100); + progress = update_progress( progress, 5, 14, 0 ); + + runCase(++caseId, -100.f, 100.f); + progress = update_progress( progress, 6, 14, 0 ); + runCase(++caseId, -100, 100); + progress = update_progress( progress, 7, 14, 0 ); + runCase(++caseId, -100, 100); + progress = update_progress( progress, 8, 14, 0 ); + runCase(++caseId, 10, 100); + progress = update_progress( progress, 10, 14, 0 ); + + runCase(++caseId, -100.f, 100.f); + progress = update_progress( progress, 11, 14, 0 ); + runCase(++caseId, -100, 100); + progress = update_progress( progress, 12, 14, 0 ); + runCase(++caseId, -100, 100); + progress = update_progress( progress, 13, 14, 0 ); + runCase(++caseId, 10, 100); + progress = update_progress( progress, 14, 14, 0 ); + } + + template double error(const Vec& v1, const Vec& 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 void runCase(int caseId, InT min, InT max) + { + typedef Vec out3d_t; + + bool handleMissingValues = (unsigned)theRNG() % 2 == 0; + + Mat_ disp(Size(320, 240)); + randu(disp, Scalar(min), Scalar(max)); + + if (handleMissingValues) + disp(disp.rows/2, disp.cols/2) = min - 1; + + Mat_ Q(4, 4); + randu(Q, Scalar(-5), Scalar(5)); + + Mat_ _3dImg(disp.size()); + + CvMat cvdisp = disp; CvMat cv_3dImg = _3dImg; CvMat cvQ = Q; + cvReprojectImageTo3D( &cvdisp, &cv_3dImg, &cvQ, handleMissingValues ); + + if (numeric_limits::max() == numeric_limits::max()) + 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] = { x, y, d, 1 }; + Mat_ res = Q * Mat_(4, 1, from); + res /= res(3, 0); + + out3d_t pixel_exp = *(Vec3d*)res.data; + 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(); + 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(); } + diff --git a/modules/calib3d/test/test_stereomatching.cpp b/modules/calib3d/test/test_stereomatching.cpp new file mode 100755 index 000000000..60f20de67 --- /dev/null +++ b/modules/calib3d/test/test_stereomatching.cpp @@ -0,0 +1,832 @@ +/*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 + discribed in "A Taxonomy and Evaluation of Dense Two-Frame Stereo Correspondence Algorithms". + Daniel Scharstein, Richard Szeliski +*/ + +#include "test_precomp.hpp" +#include +#include + +using namespace std; +using namespace cv; + +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( CV_StsBadArg, "img is empty" ); + + Mat img = _img; + if( _img.channels() > 1) + { + Mat tmp; cvtColor( _img, tmp, CV_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( CV_StsBadArg, "dispMap is empty" ); + if( dispMap.type() != CV_32FC1 ) + CV_Error( CV_StsBadArg, "dispMap must have CV_32FC1 type" ); + if( sz && (dispMap.rows != sz->height || dispMap.cols != sz->width) ) + CV_Error( CV_StsBadArg, "dispMap has incorrect size" ); +} + +void checkTypeAndSizeOfMask( const Mat& mask, Size sz ) +{ + if( mask.empty() ) + CV_Error( CV_StsBadArg, "mask is empty" ); + if( mask.type() != CV_8UC1 ) + CV_Error( CV_StsBadArg, "mask must have CV_8UC1 type" ); + if( mask.rows != sz.height || mask.cols != sz.width ) + CV_Error( CV_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( CV_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( CV_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(leftY,leftX) ) + continue; + float leftDispVal = leftDisp.at(leftY, leftX); + int rightX = leftX - cvRound(leftDispVal), rightY = leftY; + if( rightX >= 0) + rightDisp.at(rightY,rightX) = max(rightDisp.at(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(leftY,leftX) ) + continue; + float leftDispVal = leftDisp.at(leftY, leftX); + int rightX = leftX - cvRound(leftDispVal), rightY = leftY; + if( rightX < 0 && occludedMask ) + occludedMask->at(leftY, leftX) = 255; + else + { + if( !rightUnknDispMask.empty() && rightUnknDispMask.at(rightY,rightX) ) + continue; + float rightDispVal = rightDisp.at(rightY, rightX); + if( rightDispVal > leftDispVal + dispThresh ) + { + if( occludedMask ) + occludedMask->at(leftY, leftX) = 255; + } + else + { + if( nonOccludedMask ) + nonOccludedMask->at(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( CV_StsBadArg, "disp is empty" ); + if( disp.type() != CV_32FC1 ) + CV_Error( CV_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(numeric_limits::min()), unknDispMask ); + Mat maxNeighbDisp; dilate( curDisp, maxNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) ); + if( !unknDispMask.empty() ) + curDisp.setTo( Scalar(numeric_limits::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)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 + + +const string RMS_STR = "RMS"; +const string BAD_PXLS_FRACTION_STR = "BadPxlsFraction"; + +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, + 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& errors, FileStorage* fs = 0 ); + void readErrors( FileNode& fn, const string& errName, vector& errors ); + int compareErrors( const vector& calcErrors, const vector& validErrors, + const vector& eps, const string& errName ); + int processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite, + const Mat& leftImg, const Mat& rightImg, + const Mat& trueLeftDisp, const Mat& trueRightDisp, + const Mat& leftDisp, const Mat& rightDisp, + const QualityEvalParams& qualityEvalParams ); + void run( int ); + + vector rmsEps; + vector fracEps; + + struct DatasetParams + { + int dispScaleFactor; + int dispUnknVal; + }; + map datasetsParams; + + vector caseNames; + vector caseDatasets; +}; + +void CV_StereoMatchingTest::run(int) +{ + string dataPath = ts->get_data_path(); + 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 ); + + 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); + + 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, 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, 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& rms, vector& 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 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 && trueRightDisp.type() == CV_32FC1 ); + assert( leftDisp.type() == CV_32FC1 && 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 < numeric_limits::epsilon(); + assert(leftUnknMask.type() == CV_8UC1); + if( !trueRightDisp.empty() ) + { + absdiff( trueRightDisp, Scalar(params.dispUnknVal), rightUnknMask ); + rightUnknMask = rightUnknMask < numeric_limits::epsilon(); + assert(leftUnknMask.type() == CV_8UC1); + } + + // calculate errors + vector rmss, badPxlsFractions; + calcErrors( leftImg, rightImg, trueLeftDisp, trueRightDisp, leftUnknMask, rightUnknMask, + leftDisp, rightDisp, rmss, badPxlsFractions, qualityEvalParams ); + + if( isWrite ) + { + fs << caseNames[caseIdx] << "{"; + cvWriteComment( fs.fs, RMS_STR.c_str(), 0 ); + writeErrors( RMS_STR, rmss, &fs ); + cvWriteComment( fs.fs, BAD_PXLS_FRACTION_STR.c_str(), 0 ); + writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions, &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 ); + + FileNode fn = fs.getFirstTopLevelNode()[caseNames[caseIdx]]; + vector validRmss, validBadPxlsFractions; + + readErrors( fn, RMS_STR, validRmss ); + readErrors( fn, BAD_PXLS_FRACTION_STR, validBadPxlsFractions ); + 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; + } + 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& errors, FileStorage* fs ) +{ + assert( (int)errors.size() == ERROR_KINDS_COUNT ); + vector::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::readErrors( FileNode& fn, const string& errName, vector& errors ) +{ + errors.resize( ERROR_KINDS_COUNT ); + vector::iterator it = errors.begin(); + for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it ) + fn[ERROR_PREFIXES[i]+errName] >> *it; +} + +int CV_StereoMatchingTest::compareErrors( const vector& calcErrors, const vector& validErrors, + const vector& 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::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; +} + +//----------------------------------- 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 winSize; + }; + vector 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+=4 ) + { + 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()); + caseNames.push_back( caseName ); + caseDatasets.push_back( datasetName ); + caseRunParams.push_back( params ); + } + return code; + } + + virtual int runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg, + 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, CV_BGR2GRAY ); + Mat rightImg; cvtColor( _rightImg, rightImg, CV_BGR2GRAY ); + + StereoBM bm( StereoBM::BASIC_PRESET, params.ndisp, params.winSize ); + bm( leftImg, rightImg, leftDisp, CV_32F ); + return params.winSize/2; + } +}; + + +//----------------------------------- StereoGC test ----------------------------------------------------- + +class CV_StereoGCTest : public CV_StereoMatchingTest +{ +public: + CV_StereoGCTest() + { + name = "stereogc"; + fill(rmsEps.begin(), rmsEps.end(), 3.f); + fracEps[0] = 0.05f; // all + fracEps[1] = 0.05f; // noOccl + fracEps[2] = 0.25f; // occl + fracEps[3] = 0.05f; // textured + fracEps[4] = 0.10f; // textureless + fracEps[5] = 0.10f; // borderedDepthDiscont + } +protected: + struct RunParams + { + int ndisp; + int iterCount; + }; + vector 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+=4 ) + { + string caseName = fn[i], datasetName = fn[i+1]; + RunParams params; + string ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str()); + string iterCount = fn[i+3]; params.iterCount = atoi(iterCount.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, + Mat& leftDisp, Mat& rightDisp, int caseIdx ) + { + RunParams params = caseRunParams[caseIdx]; + assert( _leftImg.type() == CV_8UC3 && _rightImg.type() == CV_8UC3 ); + Mat leftImg, rightImg, tmp; + cvtColor( _leftImg, leftImg, CV_BGR2GRAY ); + cvtColor( _rightImg, rightImg, CV_BGR2GRAY ); + + leftDisp.create( leftImg.size(), CV_16SC1 ); + rightDisp.create( rightImg.size(), CV_16SC1 ); + + CvMat _limg = leftImg, _rimg = rightImg, _ldisp = leftDisp, _rdisp = rightDisp; + CvStereoGCState *state = cvCreateStereoGCState( params.ndisp, params.iterCount ); + cvFindStereoCorrespondenceGC( &_limg, &_rimg, &_ldisp, &_rdisp, state ); + cvReleaseStereoGCState( &state ); + + leftDisp = - leftDisp; + return 0; + } + +}; + + +//----------------------------------- 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; + bool fullDP; + }; + vector 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 fullDP = fn[i+4]; params.fullDP = atoi(fullDP.c_str()) == 0 ? false : true; + caseNames.push_back( caseName ); + caseDatasets.push_back( datasetName ); + caseRunParams.push_back( params ); + } + return code; + } + + virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg, + Mat& leftDisp, Mat& /*rightDisp*/, int caseIdx ) + { + RunParams params = caseRunParams[caseIdx]; + assert( params.ndisp%16 == 0 ); + StereoSGBM sgbm( 0, params.ndisp, params.winSize, 10*params.winSize*params.winSize, 40*params.winSize*params.winSize, + 1, 63, 10, 100, 32, params.fullDP ); + sgbm( leftImg, rightImg, leftDisp ); + assert( leftDisp.type() == CV_16SC1 ); + leftDisp/=16; + return 0; + } +}; + + +TEST(Calib3d_StereoBM, regression) { CV_StereoBMTest test; test.safe_run(); } +TEST(Calib3d_StereoGC, regression) { CV_StereoGCTest test; test.safe_run(); } +TEST(Calib3d_StereoSGBM, regression) { CV_StereoSGBMTest test; test.safe_run(); } diff --git a/modules/calib3d/test/test_undistort.cpp b/modules/calib3d/test/test_undistort.cpp new file mode 100644 index 000000000..82256269f --- /dev/null +++ b/modules/calib3d/test/test_undistort.cpp @@ -0,0 +1,928 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_DefaultNewCameraMatrixTest : public cvtest::ArrayTest +{ +public: + CV_DefaultNewCameraMatrixTest(); +protected: + int prepare_test_case (int test_case_idx); + void prepare_to_validation( int test_case_idx ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void run_func(); + +private: + cv::Size img_size; + cv::Mat camera_mat; + cv::Mat new_camera_mat; + + int matrix_type; + + bool center_principal_point; + + static const int MAX_X = 2048; + static const int MAX_Y = 2048; + static const int MAX_VAL = 10000; +}; + +CV_DefaultNewCameraMatrixTest::CV_DefaultNewCameraMatrixTest() +{ + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); +} + +void CV_DefaultNewCameraMatrixTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + cvtest::ArrayTest::get_test_array_types_and_sizes(test_case_idx,sizes,types); + RNG& rng = ts->get_rng(); + matrix_type = types[INPUT][0] = types[OUTPUT][0]= types[REF_OUTPUT][0] = cvtest::randInt(rng)%2 ? CV_64F : CV_32F; + sizes[INPUT][0] = sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = cvSize(3,3); +} + +int CV_DefaultNewCameraMatrixTest::prepare_test_case(int test_case_idx) +{ + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + + if (code <= 0) + return code; + + RNG& rng = ts->get_rng(); + + img_size.width = cvtest::randInt(rng) % MAX_X + 1; + img_size.height = cvtest::randInt(rng) % MAX_Y + 1; + + center_principal_point = ((cvtest::randInt(rng) % 2)!=0); + + // Generating camera_mat matrix + double sz = MAX(img_size.width, img_size.height); + double aspect_ratio = cvtest::randReal(rng)*0.6 + 0.7; + double a[9] = {0,0,0,0,0,0,0,0,1}; + Mat _a(3,3,CV_64F,a); + a[2] = (img_size.width - 1)*0.5 + cvtest::randReal(rng)*10 - 5; + a[5] = (img_size.height - 1)*0.5 + cvtest::randReal(rng)*10 - 5; + a[0] = sz/(0.9 - cvtest::randReal(rng)*0.6); + a[4] = aspect_ratio*a[0]; + + Mat& _a0 = test_mat[INPUT][0]; + cvtest::convert(_a, _a0, _a0.type()); + camera_mat = _a0; + + return code; + +} + +void CV_DefaultNewCameraMatrixTest::run_func() +{ + new_camera_mat = cv::getDefaultNewCameraMatrix(camera_mat,img_size,center_principal_point); +} + +void CV_DefaultNewCameraMatrixTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + const Mat& src = test_mat[INPUT][0]; + Mat& dst = test_mat[REF_OUTPUT][0]; + Mat& test_output = test_mat[OUTPUT][0]; + Mat& output = new_camera_mat; + cvtest::convert( output, test_output, test_output.type() ); + if (!center_principal_point) + { + cvtest::copy(src, dst); + } + else + { + double a[9] = {0,0,0,0,0,0,0,0,1}; + Mat _a(3,3,CV_64F,a); + if (matrix_type == CV_64F) + { + a[0] = src.at(0,0); + a[4] = src.at(1,1); + } + else + { + a[0] = src.at(0,0); + a[4] = src.at(1,1); + } + a[2] = (img_size.width - 1)*0.5; + a[5] = (img_size.height - 1)*0.5; + cvtest::convert( _a, dst, dst.type() ); + } +} + +//--------- + +class CV_UndistortPointsTest : public cvtest::ArrayTest +{ +public: + CV_UndistortPointsTest(); +protected: + int prepare_test_case (int test_case_idx); + void prepare_to_validation( int test_case_idx ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void distortPoints(const CvMat* _src, CvMat* _dst, const CvMat* _cameraMatrix, + const CvMat* _distCoeffs, const CvMat* matR, const CvMat* matP); + +private: + bool useCPlus; + bool useDstMat; + static const int N_POINTS = 10; + static const int MAX_X = 2048; + static const int MAX_Y = 2048; + + bool zero_new_cam; + bool zero_distortion; + bool zero_R; + + cv::Size img_size; + cv::Mat dst_points_mat; + + cv::Mat camera_mat; + cv::Mat R; + cv::Mat P; + cv::Mat distortion_coeffs; + cv::Mat src_points; + std::vector dst_points; +}; + +CV_UndistortPointsTest::CV_UndistortPointsTest() +{ + test_array[INPUT].push_back(NULL); // points matrix + test_array[INPUT].push_back(NULL); // camera matrix + test_array[INPUT].push_back(NULL); // distortion coeffs + test_array[INPUT].push_back(NULL); // R matrix + test_array[INPUT].push_back(NULL); // P matrix + test_array[OUTPUT].push_back(NULL); // distorted dst points + test_array[TEMP].push_back(NULL); // dst points + test_array[REF_OUTPUT].push_back(NULL); +} + +void CV_UndistortPointsTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + cvtest::ArrayTest::get_test_array_types_and_sizes(test_case_idx,sizes,types); + RNG& rng = ts->get_rng(); + useCPlus = ((cvtest::randInt(rng) % 2)!=0); + //useCPlus = 0; + if (useCPlus) + { + types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = types[TEMP][0]= CV_32FC2; + } + else + { + types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = types[TEMP][0]= cvtest::randInt(rng)%2 ? CV_64FC2 : CV_32FC2; + } + types[INPUT][1] = cvtest::randInt(rng)%2 ? CV_64F : CV_32F; + types[INPUT][2] = cvtest::randInt(rng)%2 ? CV_64F : CV_32F; + types[INPUT][3] = cvtest::randInt(rng)%2 ? CV_64F : CV_32F; + types[INPUT][4] = cvtest::randInt(rng)%2 ? CV_64F : CV_32F; + + sizes[INPUT][0] = sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = sizes[TEMP][0]= cvtest::randInt(rng)%2 ? cvSize(1,N_POINTS) : cvSize(N_POINTS,1); + sizes[INPUT][1] = sizes[INPUT][3] = cvSize(3,3); + sizes[INPUT][4] = cvtest::randInt(rng)%2 ? cvSize(3,3) : cvSize(4,3); + + if (cvtest::randInt(rng)%2) + { + if (cvtest::randInt(rng)%2) + { + sizes[INPUT][2] = cvSize(1,4); + } + else + { + sizes[INPUT][2] = cvSize(1,5); + } + } + else + { + if (cvtest::randInt(rng)%2) + { + sizes[INPUT][2] = cvSize(4,1); + } + else + { + sizes[INPUT][2] = cvSize(5,1); + } + } +} + +int CV_UndistortPointsTest::prepare_test_case(int test_case_idx) +{ + RNG& rng = ts->get_rng(); + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + + if (code <= 0) + return code; + + useDstMat = (cvtest::randInt(rng) % 2) == 0; + + img_size.width = cvtest::randInt(rng) % MAX_X + 1; + img_size.height = cvtest::randInt(rng) % MAX_Y + 1; + int dist_size = test_mat[INPUT][2].cols > test_mat[INPUT][2].rows ? test_mat[INPUT][2].cols : test_mat[INPUT][2].rows; + double cam[9] = {0,0,0,0,0,0,0,0,1}; + vector dist(dist_size); + vector proj(test_mat[INPUT][4].cols * test_mat[INPUT][4].rows); + vector points(N_POINTS); + + Mat _camera(3,3,CV_64F,cam); + Mat _distort(test_mat[INPUT][2].rows,test_mat[INPUT][2].cols,CV_64F,&dist[0]); + Mat _proj(test_mat[INPUT][4].size(), CV_64F, &proj[0]); + Mat _points(test_mat[INPUT][0].size(), CV_64FC2, &points[0]); + + _proj = Scalar::all(0); + + //Generating points + for( int i = 0; i < N_POINTS; i++ ) + { + points[i].x = cvtest::randReal(rng)*img_size.width; + points[i].y = cvtest::randReal(rng)*img_size.height; + } + + //Generating camera matrix + double sz = MAX(img_size.width,img_size.height); + double aspect_ratio = cvtest::randReal(rng)*0.6 + 0.7; + cam[2] = (img_size.width - 1)*0.5 + cvtest::randReal(rng)*10 - 5; + cam[5] = (img_size.height - 1)*0.5 + cvtest::randReal(rng)*10 - 5; + cam[0] = sz/(0.9 - cvtest::randReal(rng)*0.6); + cam[4] = aspect_ratio*cam[0]; + + //Generating distortion coeffs + dist[0] = cvtest::randReal(rng)*0.06 - 0.03; + dist[1] = cvtest::randReal(rng)*0.06 - 0.03; + if( dist[0]*dist[1] > 0 ) + dist[1] = -dist[1]; + if( cvtest::randInt(rng)%4 != 0 ) + { + dist[2] = cvtest::randReal(rng)*0.004 - 0.002; + dist[3] = cvtest::randReal(rng)*0.004 - 0.002; + if (dist_size > 4) + dist[4] = cvtest::randReal(rng)*0.004 - 0.002; + } + else + { + dist[2] = dist[3] = 0; + if (dist_size > 4) + dist[4] = 0; + } + + //Generating P matrix (projection) + if( test_mat[INPUT][4].cols != 4 ) + { + proj[8] = 1; + if (cvtest::randInt(rng)%2 == 0) // use identity new camera matrix + { + proj[0] = 1; + proj[4] = 1; + } + else + { + proj[0] = cam[0] + (cvtest::randReal(rng) - (double)0.5)*0.2*cam[0]; //10% + proj[4] = cam[4] + (cvtest::randReal(rng) - (double)0.5)*0.2*cam[4]; //10% + proj[2] = cam[2] + (cvtest::randReal(rng) - (double)0.5)*0.3*img_size.width; //15% + proj[5] = cam[5] + (cvtest::randReal(rng) - (double)0.5)*0.3*img_size.height; //15% + } + } + else + { + proj[10] = 1; + proj[0] = cam[0] + (cvtest::randReal(rng) - (double)0.5)*0.2*cam[0]; //10% + proj[5] = cam[4] + (cvtest::randReal(rng) - (double)0.5)*0.2*cam[4]; //10% + proj[2] = cam[2] + (cvtest::randReal(rng) - (double)0.5)*0.3*img_size.width; //15% + proj[6] = cam[5] + (cvtest::randReal(rng) - (double)0.5)*0.3*img_size.height; //15% + + proj[3] = (img_size.height + img_size.width - 1)*0.5 + cvtest::randReal(rng)*10 - 5; + proj[7] = (img_size.height + img_size.width - 1)*0.5 + cvtest::randReal(rng)*10 - 5; + proj[11] = (img_size.height + img_size.width - 1)*0.5 + cvtest::randReal(rng)*10 - 5; + } + + //Generating R matrix + Mat _rot(3,3,CV_64F); + Mat rotation(1,3,CV_64F); + rotation.at(0) = CV_PI*(cvtest::randReal(rng) - (double)0.5); // phi + rotation.at(1) = CV_PI*(cvtest::randReal(rng) - (double)0.5); // ksi + rotation.at(2) = CV_PI*(cvtest::randReal(rng) - (double)0.5); //khi + cvtest::Rodrigues(rotation, _rot); + + //copying data + //src_points = &_points; + _points.convertTo(test_mat[INPUT][0], test_mat[INPUT][0].type()); + _camera.convertTo(test_mat[INPUT][1], test_mat[INPUT][1].type()); + _distort.convertTo(test_mat[INPUT][2], test_mat[INPUT][2].type()); + _rot.convertTo(test_mat[INPUT][3], test_mat[INPUT][3].type()); + _proj.convertTo(test_mat[INPUT][4], test_mat[INPUT][4].type()); + + zero_distortion = (cvtest::randInt(rng)%2) == 0 ? false : true; + zero_new_cam = (cvtest::randInt(rng)%2) == 0 ? false : true; + zero_R = (cvtest::randInt(rng)%2) == 0 ? false : true; + + if (useCPlus) + { + _points.convertTo(src_points, CV_32F); + + camera_mat = test_mat[INPUT][1]; + distortion_coeffs = test_mat[INPUT][2]; + R = test_mat[INPUT][3]; + P = test_mat[INPUT][4]; + } + + return code; +} + +void CV_UndistortPointsTest::prepare_to_validation(int /*test_case_idx*/) +{ + int dist_size = test_mat[INPUT][2].cols > test_mat[INPUT][2].rows ? test_mat[INPUT][2].cols : test_mat[INPUT][2].rows; + double cam[9] = {0,0,0,0,0,0,0,0,1}; + double rot[9] = {1,0,0,0,1,0,0,0,1}; + + double* dist = new double[dist_size ]; + double* proj = new double[test_mat[INPUT][4].cols * test_mat[INPUT][4].rows]; + double* points = new double[N_POINTS*2]; + double* r_points = new double[N_POINTS*2]; + //Run reference calculations + CvMat ref_points= cvMat(test_mat[INPUT][0].rows,test_mat[INPUT][0].cols,CV_64FC2,r_points); + CvMat _camera = cvMat(3,3,CV_64F,cam); + CvMat _rot = cvMat(3,3,CV_64F,rot); + CvMat _distort = cvMat(test_mat[INPUT][2].rows,test_mat[INPUT][2].cols,CV_64F,dist); + CvMat _proj = cvMat(test_mat[INPUT][4].rows,test_mat[INPUT][4].cols,CV_64F,proj); + CvMat _points= cvMat(test_mat[TEMP][0].rows,test_mat[TEMP][0].cols,CV_64FC2,points); + + Mat __camera = cvarrToMat(&_camera); + Mat __distort = cvarrToMat(&_distort); + Mat __rot = cvarrToMat(&_rot); + Mat __proj = cvarrToMat(&_proj); + Mat __points = cvarrToMat(&_points); + Mat _ref_points = cvarrToMat(&ref_points); + + cvtest::convert(test_mat[INPUT][1], __camera, __camera.type()); + cvtest::convert(test_mat[INPUT][2], __distort, __distort.type()); + cvtest::convert(test_mat[INPUT][3], __rot, __rot.type()); + cvtest::convert(test_mat[INPUT][4], __proj, __proj.type()); + + if (useCPlus) + { + if (useDstMat) + { + CvMat temp = dst_points_mat; + for (int i=0;icols == 3)) + __P = cvCreateMat(3,3,CV_64F); + else + __P = cvCreateMat(3,4,CV_64F); + if (matP) + { + cvTsConvert(matP,__P); + } + else + { + cvZero(__P); + __P->data.db[0] = 1; + __P->data.db[4] = 1; + __P->data.db[8] = 1; + } + CvMat* __R = cvCreateMat(3,3,CV_64F);; + if (matR) + { + cvCopy(matR,__R); + } + else + { + cvZero(__R); + __R->data.db[0] = 1; + __R->data.db[4] = 1; + __R->data.db[8] = 1; + } + for (int i=0;icols > 3 ? 1 : 0; + double x = (_src->data.db[2*i]-__P->data.db[2])/__P->data.db[0]; + double y = (_src->data.db[2*i+1]-__P->data.db[5+movement])/__P->data.db[4+movement]; + CvMat inverse = cvMat(3,3,CV_64F,a); + cvInvert(__R,&inverse); + double w1 = x*inverse.data.db[6]+y*inverse.data.db[7]+inverse.data.db[8]; + double _x = (x*inverse.data.db[0]+y*inverse.data.db[1]+inverse.data.db[2])/w1; + double _y = (x*inverse.data.db[3]+y*inverse.data.db[4]+inverse.data.db[5])/w1; + + //Distortions + + double __x = _x; + double __y = _y; + if (_distCoeffs) + { + double r2 = _x*_x+_y*_y; + + __x = _x*(1+_distCoeffs->data.db[0]*r2+_distCoeffs->data.db[1]*r2*r2)+ + 2*_distCoeffs->data.db[2]*_x*_y+_distCoeffs->data.db[3]*(r2+2*_x*_x); + __y = _y*(1+_distCoeffs->data.db[0]*r2+_distCoeffs->data.db[1]*r2*r2)+ + 2*_distCoeffs->data.db[3]*_x*_y+_distCoeffs->data.db[2]*(r2+2*_y*_y); + if ((_distCoeffs->cols > 4) || (_distCoeffs->rows > 4)) + { + __x+=_x*_distCoeffs->data.db[4]*r2*r2*r2; + __y+=_y*_distCoeffs->data.db[4]*r2*r2*r2; + } + } + + + _dst->data.db[2*i] = __x*_cameraMatrix->data.db[0]+_cameraMatrix->data.db[2]; + _dst->data.db[2*i+1] = __y*_cameraMatrix->data.db[4]+_cameraMatrix->data.db[5]; + + } + + cvReleaseMat(&__R); + cvReleaseMat(&__P); + +} + + +double CV_UndistortPointsTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return 5e-2; +} + +//------------------------------------------------------ + +class CV_InitUndistortRectifyMapTest : public cvtest::ArrayTest +{ +public: + CV_InitUndistortRectifyMapTest(); +protected: + int prepare_test_case (int test_case_idx); + void prepare_to_validation( int test_case_idx ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + +private: + bool useCPlus; + static const int N_POINTS = 100; + static const int MAX_X = 2048; + static const int MAX_Y = 2048; + bool zero_new_cam; + bool zero_distortion; + bool zero_R; + + + 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; + CvMat* _mapx; + CvMat* _mapy; + int mat_type; +}; + +CV_InitUndistortRectifyMapTest::CV_InitUndistortRectifyMapTest() +{ + test_array[INPUT].push_back(NULL); // test points matrix + test_array[INPUT].push_back(NULL); // camera matrix + test_array[INPUT].push_back(NULL); // distortion coeffs + test_array[INPUT].push_back(NULL); // R matrix + test_array[INPUT].push_back(NULL); // new camera matrix + test_array[OUTPUT].push_back(NULL); // distorted dst points + test_array[REF_OUTPUT].push_back(NULL); +} + +void CV_InitUndistortRectifyMapTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + cvtest::ArrayTest::get_test_array_types_and_sizes(test_case_idx,sizes,types); + RNG& rng = ts->get_rng(); + useCPlus = ((cvtest::randInt(rng) % 2)!=0); + //useCPlus = 0; + types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_64FC2; + + types[INPUT][1] = cvtest::randInt(rng)%2 ? CV_64F : CV_32F; + types[INPUT][2] = cvtest::randInt(rng)%2 ? CV_64F : CV_32F; + types[INPUT][3] = cvtest::randInt(rng)%2 ? CV_64F : CV_32F; + types[INPUT][4] = cvtest::randInt(rng)%2 ? CV_64F : CV_32F; + + sizes[INPUT][0] = sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = cvSize(N_POINTS,1); + sizes[INPUT][1] = sizes[INPUT][3] = cvSize(3,3); + sizes[INPUT][4] = cvSize(3,3); + + if (cvtest::randInt(rng)%2) + { + if (cvtest::randInt(rng)%2) + { + sizes[INPUT][2] = cvSize(1,4); + } + else + { + sizes[INPUT][2] = cvSize(1,5); + } + } + else + { + if (cvtest::randInt(rng)%2) + { + sizes[INPUT][2] = cvSize(4,1); + } + else + { + sizes[INPUT][2] = cvSize(5,1); + } + } +} + + +int CV_InitUndistortRectifyMapTest::prepare_test_case(int test_case_idx) +{ + RNG& rng = ts->get_rng(); + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + + if (code <= 0) + return code; + + img_size.width = cvtest::randInt(rng) % MAX_X + 1; + img_size.height = cvtest::randInt(rng) % MAX_Y + 1; + + if (useCPlus) + { + mat_type = (cvtest::randInt(rng) % 2) == 0 ? CV_32FC1 : CV_16SC2; + if ((cvtest::randInt(rng) % 4) == 0) + mat_type = -1; + if ((cvtest::randInt(rng) % 4) == 0) + mat_type = CV_32FC2; + _mapx = 0; + _mapy = 0; + } + else + { + int typex = (cvtest::randInt(rng) % 2) == 0 ? CV_32FC1 : CV_16SC2; + //typex = CV_32FC1; ///!!!!!!!!!!!!!!!! + int typey = (typex == CV_32FC1) ? CV_32FC1 : CV_16UC1; + + _mapx = cvCreateMat(img_size.height,img_size.width,typex); + _mapy = cvCreateMat(img_size.height,img_size.width,typey); + + + } + + int dist_size = test_mat[INPUT][2].cols > test_mat[INPUT][2].rows ? test_mat[INPUT][2].cols : test_mat[INPUT][2].rows; + double cam[9] = {0,0,0,0,0,0,0,0,1}; + vector dist(dist_size); + vector new_cam(test_mat[INPUT][4].cols * test_mat[INPUT][4].rows); + vector points(N_POINTS); + + Mat _camera(3,3,CV_64F,cam); + Mat _distort(test_mat[INPUT][2].size(),CV_64F,&dist[0]); + Mat _new_cam(test_mat[INPUT][4].size(),CV_64F,&new_cam[0]); + Mat _points(test_mat[INPUT][0].size(),CV_64FC2, &points[0]); + + //Generating points + for (int i=0;i 0 ) + dist[1] = -dist[1]; + if( cvtest::randInt(rng)%4 != 0 ) + { + dist[2] = cvtest::randReal(rng)*0.004 - 0.002; + dist[3] = cvtest::randReal(rng)*0.004 - 0.002; + if (dist_size > 4) + dist[4] = cvtest::randReal(rng)*0.004 - 0.002; + } + else + { + dist[2] = dist[3] = 0; + if (dist_size > 4) + dist[4] = 0; + } + + //Generating new camera matrix + _new_cam = Scalar::all(0); + new_cam[8] = 1; + + //new_cam[0] = cam[0]; + //new_cam[4] = cam[4]; + //new_cam[2] = cam[2]; + //new_cam[5] = cam[5]; + + new_cam[0] = cam[0] + (cvtest::randReal(rng) - (double)0.5)*0.2*cam[0]; //10% + new_cam[4] = cam[4] + (cvtest::randReal(rng) - (double)0.5)*0.2*cam[4]; //10% + new_cam[2] = cam[2] + (cvtest::randReal(rng) - (double)0.5)*0.3*img_size.width; //15% + new_cam[5] = cam[5] + (cvtest::randReal(rng) - (double)0.5)*0.3*img_size.height; //15% + + + //Generating R matrix + Mat _rot(3,3,CV_64F); + Mat rotation(1,3,CV_64F); + rotation.at(0) = CV_PI/8*(cvtest::randReal(rng) - (double)0.5); // phi + rotation.at(1) = CV_PI/8*(cvtest::randReal(rng) - (double)0.5); // ksi + rotation.at(2) = CV_PI/3*(cvtest::randReal(rng) - (double)0.5); //khi + cvtest::Rodrigues(rotation, _rot); + + //cvSetIdentity(_rot); + //copying data + cvtest::convert( _points, test_mat[INPUT][0], test_mat[INPUT][0].type()); + cvtest::convert( _camera, test_mat[INPUT][1], test_mat[INPUT][1].type()); + cvtest::convert( _distort, test_mat[INPUT][2], test_mat[INPUT][2].type()); + cvtest::convert( _rot, test_mat[INPUT][3], test_mat[INPUT][3].type()); + cvtest::convert( _new_cam, test_mat[INPUT][4], test_mat[INPUT][4].type()); + + zero_distortion = (cvtest::randInt(rng)%2) == 0 ? false : true; + zero_new_cam = (cvtest::randInt(rng)%2) == 0 ? false : true; + zero_R = (cvtest::randInt(rng)%2) == 0 ? false : true; + + if (useCPlus) + { + camera_mat = test_mat[INPUT][1]; + distortion_coeffs = test_mat[INPUT][2]; + R = test_mat[INPUT][3]; + new_camera_mat = test_mat[INPUT][4]; + } + + return code; +} + +void CV_InitUndistortRectifyMapTest::prepare_to_validation(int/* test_case_idx*/) +{ +#if 0 + int dist_size = test_mat[INPUT][2].cols > test_mat[INPUT][2].rows ? test_mat[INPUT][2].cols : test_mat[INPUT][2].rows; + double cam[9] = {0,0,0,0,0,0,0,0,1}; + double rot[9] = {1,0,0,0,1,0,0,0,1}; + vector dist(dist_size); + vector new_cam(test_mat[INPUT][4].cols * test_mat[INPUT][4].rows); + vector points(N_POINTS); + vector r_points(N_POINTS); + //Run reference calculations + Mat ref_points(test_mat[INPUT][0].size(),CV_64FC2,&r_points[0]); + Mat _camera(3,3,CV_64F,cam); + Mat _rot(3,3,CV_64F,rot); + Mat _distort(test_mat[INPUT][2].size(),CV_64F,&dist[0]); + Mat _new_cam(test_mat[INPUT][4].size(),CV_64F,&new_cam[0]); + Mat _points(test_mat[INPUT][0].size(),CV_64FC2,&points[0]); + + cvtest::convert(test_mat[INPUT][1],_camera,_camera.type()); + cvtest::convert(test_mat[INPUT][2],_distort,_distort.type()); + cvtest::convert(test_mat[INPUT][3],_rot,_rot.type()); + cvtest::convert(test_mat[INPUT][4],_new_cam,_new_cam.type()); + + //Applying precalculated undistort rectify map + if (!useCPlus) + { + mapx = cv::Mat(_mapx); + mapy = cv::Mat(_mapy); + } + cv::Mat map1,map2; + cv::convertMaps(mapx,mapy,map1,map2,CV_32FC1); + CvMat _map1 = map1; + CvMat _map2 = map2; + const Point2d* sptr = (const Point2d*)test_mat[INPUT][0].data; + for( int i = 0;i < N_POINTS; i++ ) + { + int u = saturate_cast(sptr[i].x); + int v = saturate_cast(sptr[i].y); + points[i].x = _map1.data.fl[v*_map1.cols + u]; + points[i].y = _map2.data.fl[v*_map2.cols + u]; + } + + //--- + + cv::undistortPoints(_points, ref_points, _camera, + zero_distortion ? Mat() : _distort, + zero_R ? Mat::eye(3,3,CV_64F) : _rot, + zero_new_cam ? _camera : _new_cam); + //cvTsDistortPoints(&_points,&ref_points,&_camera,&_distort,&_rot,&_new_cam); + cvtest::convert(ref_points, test_mat[REF_OUTPUT][0], test_mat[REF_OUTPUT][0].type()); + cvtest::copy(test_mat[INPUT][0],test_mat[OUTPUT][0]); + + cvReleaseMat(&_mapx); + cvReleaseMat(&_mapy); +#else + int dist_size = test_mat[INPUT][2].cols > test_mat[INPUT][2].rows ? test_mat[INPUT][2].cols : test_mat[INPUT][2].rows; + double cam[9] = {0,0,0,0,0,0,0,0,1}; + double rot[9] = {1,0,0,0,1,0,0,0,1}; + double* dist = new double[dist_size ]; + double* new_cam = new double[test_mat[INPUT][4].cols * test_mat[INPUT][4].rows]; + double* points = new double[N_POINTS*2]; + double* r_points = new double[N_POINTS*2]; + //Run reference calculations + CvMat ref_points= cvMat(test_mat[INPUT][0].rows,test_mat[INPUT][0].cols,CV_64FC2,r_points); + CvMat _camera = cvMat(3,3,CV_64F,cam); + CvMat _rot = cvMat(3,3,CV_64F,rot); + CvMat _distort = cvMat(test_mat[INPUT][2].rows,test_mat[INPUT][2].cols,CV_64F,dist); + CvMat _new_cam = cvMat(test_mat[INPUT][4].rows,test_mat[INPUT][4].cols,CV_64F,new_cam); + CvMat _points= cvMat(test_mat[INPUT][0].rows,test_mat[INPUT][0].cols,CV_64FC2,points); + + CvMat _input1 = test_mat[INPUT][1]; + CvMat _input2 = test_mat[INPUT][2]; + CvMat _input3 = test_mat[INPUT][3]; + CvMat _input4 = test_mat[INPUT][4]; + + cvTsConvert(&_input1,&_camera); + cvTsConvert(&_input2,&_distort); + cvTsConvert(&_input3,&_rot); + cvTsConvert(&_input4,&_new_cam); + + //Applying precalculated undistort rectify map + if (!useCPlus) + { + mapx = cv::Mat(_mapx); + mapy = cv::Mat(_mapy); + } + cv::Mat map1,map2; + cv::convertMaps(mapx,mapy,map1,map2,CV_32FC1); + CvMat _map1 = map1; + CvMat _map2 = map2; + for (int i=0;i()[2*i]; + double v = test_mat[INPUT][0].ptr()[2*i+1]; + _points.data.db[2*i] = (double)_map1.data.fl[(int)v*_map1.cols+(int)u]; + _points.data.db[2*i+1] = (double)_map2.data.fl[(int)v*_map2.cols+(int)u]; + } + + //--- + + cvUndistortPoints(&_points,&ref_points,&_camera, + zero_distortion ? 0 : &_distort, zero_R ? 0 : &_rot, zero_new_cam ? &_camera : &_new_cam); + //cvTsDistortPoints(&_points,&ref_points,&_camera,&_distort,&_rot,&_new_cam); + CvMat dst = test_mat[REF_OUTPUT][0]; + cvTsConvert(&ref_points,&dst); + + cvtest::copy(test_mat[INPUT][0],test_mat[OUTPUT][0]); + + delete[] dist; + delete[] new_cam; + delete[] points; + delete[] r_points; + cvReleaseMat(&_mapx); + cvReleaseMat(&_mapy); +#endif +} + +void CV_InitUndistortRectifyMapTest::run_func() +{ + if (useCPlus) + { + cv::Mat input2,input3,input4; + input2 = zero_distortion ? cv::Mat() : test_mat[INPUT][2]; + input3 = zero_R ? cv::Mat() : test_mat[INPUT][3]; + input4 = zero_new_cam ? cv::Mat() : test_mat[INPUT][4]; + cv::initUndistortRectifyMap(camera_mat,input2,input3,input4,img_size,mat_type,mapx,mapy); + } + else + { + CvMat input1 = test_mat[INPUT][1], input2, input3, input4; + if( !zero_distortion ) + input2 = test_mat[INPUT][2]; + if( !zero_R ) + input3 = test_mat[INPUT][3]; + if( !zero_new_cam ) + input4 = test_mat[INPUT][4]; + cvInitUndistortRectifyMap(&input1, + zero_distortion ? 0 : &input2, + zero_R ? 0 : &input3, + zero_new_cam ? 0 : &input4, + _mapx,_mapy); + } +} + +double CV_InitUndistortRectifyMapTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return 8; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// + +TEST(Calib3d_DefaultNewCameraMatrix, accuracy) { CV_DefaultNewCameraMatrixTest test; test.safe_run(); } +TEST(Calib3d_UndistortPoints, accuracy) { CV_UndistortPointsTest test; test.safe_run(); } +TEST(Calib3d_InitUndistortRectifyMap, accuracy) { CV_InitUndistortRectifyMapTest test; test.safe_run(); } diff --git a/modules/calib3d/test/test_undistort_badarg.cpp b/modules/calib3d/test/test_undistort_badarg.cpp new file mode 100644 index 000000000..38aae65f7 --- /dev/null +++ b/modules/calib3d/test/test_undistort_badarg.cpp @@ -0,0 +1,518 @@ +/*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" + +class CV_UndistortPointsBadArgTest : public cvtest::BadArgTest +{ +public: + CV_UndistortPointsBadArgTest(); +protected: + void run(int); + void run_func(); + +private: + //common + cv::Size img_size; + bool useCPlus; + //static const int N_POINTS = 1; + static const int N_POINTS2 = 2; + + //C + CvMat* _camera_mat; + CvMat* matR; + CvMat* matP; + CvMat* _distortion_coeffs; + CvMat* _src_points; + CvMat* _dst_points; + + + //C++ + cv::Mat camera_mat; + cv::Mat R; + cv::Mat P; + cv::Mat distortion_coeffs; + cv::Mat src_points; + std::vector dst_points; + +}; + +CV_UndistortPointsBadArgTest::CV_UndistortPointsBadArgTest () +{ +} + +void CV_UndistortPointsBadArgTest::run_func() +{ + if (useCPlus) + { + cv::undistortPoints(src_points,dst_points,camera_mat,distortion_coeffs,R,P); + } + else + { + cvUndistortPoints(_src_points,_dst_points,_camera_mat,_distortion_coeffs,matR,matP); + } +} + +void CV_UndistortPointsBadArgTest::run(int) +{ + //RNG& rng = ts->get_rng(); + int errcount = 0; + useCPlus = false; +//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] = {img_size.width/4,img_size.height/4}; + double d_points[N_POINTS2]; + 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); + CvMat _dst_points_orig = cvMat(1,4,CV_64FC2,d_points); + + _camera_mat = &_camera_mat_orig; + _distortion_coeffs = &_distortion_coeffs_orig; + matP = &_P_orig; + matR = &_R_orig; + _src_points = &_src_points_orig; + _dst_points = &_dst_points_orig; + +//tests + CvMat* temp1; + CvMat* temp; + IplImage* temp_img = cvCreateImage(cvSize(img_size.width,img_size.height),8,3); + +//----------- + temp = (CvMat*)temp_img; + _src_points = temp; + errcount += run_test_case( CV_StsAssert, "Input data is not CvMat*" ); + _src_points = &_src_points_orig; + + temp = (CvMat*)temp_img; + _dst_points = temp; + errcount += run_test_case( CV_StsAssert, "Output data is not CvMat*" ); + _dst_points = &_dst_points_orig; + + temp = cvCreateMat(2,3,CV_64F); + _src_points = temp; + errcount += run_test_case( CV_StsAssert, "Invalid input data matrix size" ); + _src_points = &_src_points_orig; + cvReleaseMat(&temp); + + temp = cvCreateMat(2,3,CV_64F); + _dst_points = temp; + errcount += run_test_case(CV_StsAssert, "Invalid output data matrix size" ); + _dst_points = &_dst_points_orig; + cvReleaseMat(&temp); + + temp = cvCreateMat(1,3,CV_64F); + temp1 = cvCreateMat(4,1,CV_64F); + _dst_points = temp; + _src_points = temp1; + errcount += run_test_case(CV_StsAssert, "Output and input data sizes mismatch" ); + _dst_points = &_dst_points_orig; + _src_points = &_src_points_orig; + cvReleaseMat(&temp); + cvReleaseMat(&temp1); + + temp = cvCreateMat(1,3,CV_32S); + _dst_points = temp; + errcount += run_test_case(CV_StsAssert, "Invalid output data matrix type" ); + _dst_points = &_dst_points_orig; + cvReleaseMat(&temp); + + temp = cvCreateMat(1,3,CV_32S); + _src_points = temp; + errcount += run_test_case(CV_StsAssert, "Invalid input data matrix type" ); + _src_points = &_src_points_orig; + cvReleaseMat(&temp); +//------------ + temp = cvCreateMat(2,3,CV_64F); + _camera_mat = temp; + errcount += run_test_case( CV_StsAssert, "Invalid camera data matrix size" ); + _camera_mat = &_camera_mat_orig; + cvReleaseMat(&temp); + + temp = cvCreateMat(3,4,CV_64F); + _camera_mat = temp; + errcount += run_test_case( CV_StsAssert, "Invalid camera data matrix size" ); + _camera_mat = &_camera_mat_orig; + cvReleaseMat(&temp); + + temp = (CvMat*)temp_img; + _camera_mat = temp; + errcount += run_test_case( CV_StsAssert, "Camera data is not CvMat*" ); + _camera_mat = &_camera_mat_orig; +//---------- + + temp = (CvMat*)temp_img; + _distortion_coeffs = temp; + errcount += run_test_case( CV_StsAssert, "Distortion coefficients data is not CvMat*" ); + _distortion_coeffs = &_distortion_coeffs_orig; + + temp = cvCreateMat(1,6,CV_64F); + _distortion_coeffs = temp; + errcount += run_test_case( CV_StsAssert, "Invalid distortion coefficients data matrix size" ); + _distortion_coeffs = &_distortion_coeffs_orig; + cvReleaseMat(&temp); + + temp = cvCreateMat(3,3,CV_64F); + _distortion_coeffs = temp; + errcount += run_test_case( CV_StsAssert, "Invalid distortion coefficients data matrix size" ); + _distortion_coeffs = &_distortion_coeffs_orig; + cvReleaseMat(&temp); +//---------- + temp = (CvMat*)temp_img; + matR = temp; + errcount += run_test_case( CV_StsAssert, "R data is not CvMat*" ); + matR = &_R_orig; + + temp = cvCreateMat(4,3,CV_64F); + matR = temp; + errcount += run_test_case( CV_StsAssert, "Invalid R data matrix size" ); + matR = &_R_orig; + cvReleaseMat(&temp); + + temp = cvCreateMat(3,2,CV_64F); + matR = temp; + errcount += run_test_case( CV_StsAssert, "Invalid R data matrix size" ); + matR = &_R_orig; + cvReleaseMat(&temp); + +//----------- + temp = (CvMat*)temp_img; + matP = temp; + errcount += run_test_case( CV_StsAssert, "P data is not CvMat*" ); + matP = &_P_orig; + + temp = cvCreateMat(4,3,CV_64F); + matP = temp; + errcount += run_test_case( CV_StsAssert, "Invalid P data matrix size" ); + matP = &_P_orig; + cvReleaseMat(&temp); + + temp = cvCreateMat(3,2,CV_64F); + matP = temp; + errcount += run_test_case( CV_StsAssert, "Invalid P data matrix size" ); + matP = &_P_orig; + cvReleaseMat(&temp); +//------------ + //C++ tests + useCPlus = true; + + camera_mat = cv::Mat(&_camera_mat_orig); + distortion_coeffs = cv::Mat(&_distortion_coeffs_orig); + P = cv::Mat(&_P_orig); + R = cv::Mat(&_R_orig); + src_points = cv::Mat(&_src_points_orig); + + temp = cvCreateMat(2,2,CV_32FC2); + src_points = cv::Mat(temp); + errcount += run_test_case( CV_StsAssert, "Invalid input data matrix size" ); + src_points = cv::Mat(&_src_points_orig); + cvReleaseMat(&temp); + + temp = cvCreateMat(1,4,CV_64FC2); + src_points = cv::Mat(temp); + errcount += run_test_case( CV_StsAssert, "Invalid input data matrix type" ); + src_points = cv::Mat(&_src_points_orig); + cvReleaseMat(&temp); + + src_points = cv::Mat(); + errcount += run_test_case( CV_StsAssert, "Input data matrix is not continuous" ); + src_points = cv::Mat(&_src_points_orig); + cvReleaseMat(&temp); + + + +//------------ + cvReleaseImage(&temp_img); + 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: + //common + cv::Size img_size; + bool useCPlus; + + //C + CvMat* _camera_mat; + CvMat* matR; + CvMat* _new_camera_mat; + CvMat* _distortion_coeffs; + CvMat* _mapx; + CvMat* _mapy; + + + //C++ + 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() +{ + if (useCPlus) + { + cv::initUndistortRectifyMap(camera_mat,distortion_coeffs,R,new_camera_mat,img_size,mat_type,mapx,mapy); + } + else + { + cvInitUndistortRectifyMap(_camera_mat,_distortion_coeffs,matR,_new_camera_mat,_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}; + float* arr_mapx = new float[img_size.width*img_size.height]; + float* arr_mapy = new float[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); + CvMat _mapy_orig = cvMat(img_size.height,img_size.width,CV_32FC1,arr_mapy); + int mat_type_orig = CV_32FC1; + + _camera_mat = &_camera_mat_orig; + _distortion_coeffs = &_distortion_coeffs_orig; + _new_camera_mat = &_new_camera_mat_orig; + matR = &_R_orig; + _mapx = &_mapx_orig; + _mapy = &_mapy_orig; + mat_type = mat_type_orig; + +//tests + useCPlus = true; + CvMat* temp; + + //C++ tests + useCPlus = true; + + camera_mat = cv::Mat(&_camera_mat_orig); + distortion_coeffs = cv::Mat(&_distortion_coeffs_orig); + new_camera_mat = cv::Mat(&_new_camera_mat_orig); + R = cv::Mat(&_R_orig); + mapx = cv::Mat(&_mapx_orig); + mapy = cv::Mat(&_mapy_orig); + + + mat_type = CV_64F; + errcount += run_test_case( CV_StsAssert, "Invalid map matrix type" ); + mat_type = mat_type_orig; + + temp = cvCreateMat(3,2,CV_32FC1); + camera_mat = cv::Mat(temp); + errcount += run_test_case( CV_StsAssert, "Invalid camera data matrix size" ); + camera_mat = cv::Mat(&_camera_mat_orig); + cvReleaseMat(&temp); + + temp = cvCreateMat(4,3,CV_32FC1); + R = cv::Mat(temp); + errcount += run_test_case( CV_StsAssert, "Invalid R data matrix size" ); + R = cv::Mat(&_R_orig); + cvReleaseMat(&temp); + + temp = cvCreateMat(6,1,CV_32FC1); + distortion_coeffs = cv::Mat(temp); + errcount += run_test_case( CV_StsAssert, "Invalid distortion coefficients data matrix size" ); + distortion_coeffs = cv::Mat(&_distortion_coeffs_orig); + cvReleaseMat(&temp); + +//------------ + delete[] arr_mapx; + delete[] arr_mapy; + 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; + bool useCPlus; + + //C + CvMat* _camera_mat; + CvMat* _new_camera_mat; + CvMat* _distortion_coeffs; + CvMat* _src; + CvMat* _dst; + + + //C++ + 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() +{ + if (useCPlus) + { + cv::undistort(src,dst,camera_mat,distortion_coeffs,new_camera_mat); + } + else + { + cvUndistort2(_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}; + float* arr_src = new float[img_size.width*img_size.height]; + float* arr_dst = new float[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); + CvMat _dst_orig = cvMat(img_size.height,img_size.width,CV_32FC1,arr_dst); + + _camera_mat = &_camera_mat_orig; + _distortion_coeffs = &_distortion_coeffs_orig; + _new_camera_mat = &_new_camera_mat_orig; + _src = &_src_orig; + _dst = &_dst_orig; + +//tests + useCPlus = true; + CvMat* temp; + CvMat* temp1; + +//C tests + useCPlus = false; + + temp = cvCreateMat(800,600,CV_32F); + temp1 = cvCreateMat(800,601,CV_32F); + _src = temp; + _dst = temp1; + errcount += run_test_case( CV_StsAssert, "Input and output data matrix sizes mismatch" ); + _src = &_src_orig; + _dst = &_dst_orig; + cvReleaseMat(&temp); + cvReleaseMat(&temp1); + + temp = cvCreateMat(800,600,CV_32F); + temp1 = cvCreateMat(800,600,CV_64F); + _src = temp; + _dst = temp1; + errcount += run_test_case( CV_StsAssert, "Input and output data matrix types mismatch" ); + _src = &_src_orig; + _dst = &_dst_orig; + cvReleaseMat(&temp); + cvReleaseMat(&temp1); + + //C++ tests + useCPlus = true; + + camera_mat = cv::Mat(&_camera_mat_orig); + distortion_coeffs = cv::Mat(&_distortion_coeffs_orig); + new_camera_mat = cv::Mat(&_new_camera_mat_orig); + src = cv::Mat(&_src_orig); + dst = cv::Mat(&_dst_orig); + +//------------ + delete[] arr_src; + delete[] arr_dst; + 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(); } + +/* End of file. */ diff --git a/modules/contrib/include/opencv2/contrib/contrib.hpp b/modules/contrib/include/opencv2/contrib/contrib.hpp index f867a7613..4f56c3f09 100644 --- a/modules/contrib/include/opencv2/contrib/contrib.hpp +++ b/modules/contrib/include/opencv2/contrib/contrib.hpp @@ -549,8 +549,6 @@ namespace cv }; - CV_EXPORTS bool find4QuadCornerSubpix(const Mat& img, std::vector& corners, Size region_size); - CV_EXPORTS int chamerMatching( Mat& img, Mat& templ, vector >& results, vector& cost, double templScale=1, int maxMatches = 20, diff --git a/modules/core/include/opencv2/core/internal.hpp b/modules/core/include/opencv2/core/internal.hpp index c5aff62b0..cd19a0b41 100644 --- a/modules/core/include/opencv2/core/internal.hpp +++ b/modules/core/include/opencv2/core/internal.hpp @@ -288,14 +288,6 @@ CV_INLINE IppiSize ippiSize(int width, int height) /* ! DO NOT make it an inline function */ #define cvStackAlloc(size) cvAlignPtr( alloca((size) + CV_MALLOC_ALIGN), CV_MALLOC_ALIGN ) -#if defined _MSC_VER || defined __BORLANDC__ - #define CV_BIG_INT(n) n##I64 - #define CV_BIG_UINT(n) n##UI64 -#else - #define CV_BIG_INT(n) n##LL - #define CV_BIG_UINT(n) n##ULL -#endif - #ifndef CV_IMPL #define CV_IMPL CV_EXTERN_C #endif diff --git a/modules/core/include/opencv2/core/types_c.h b/modules/core/include/opencv2/core/types_c.h index d938d237e..53b910890 100644 --- a/modules/core/include/opencv2/core/types_c.h +++ b/modules/core/include/opencv2/core/types_c.h @@ -145,9 +145,13 @@ #if defined _MSC_VER || defined __BORLANDC__ typedef __int64 int64; typedef unsigned __int64 uint64; +#define CV_BIG_INT(n) n##I64 +#define CV_BIG_UINT(n) n##UI64 #else typedef int64_t int64; typedef uint64_t uint64; +#define CV_BIG_INT(n) n##LL +#define CV_BIG_UINT(n) n##ULL #endif #ifndef HAVE_IPL diff --git a/modules/core/src/arithm.cpp b/modules/core/src/arithm.cpp index bf6dde9f9..8675ddd33 100644 --- a/modules/core/src/arithm.cpp +++ b/modules/core/src/arithm.cpp @@ -1158,10 +1158,10 @@ div_( const Mat& srcmat1, const Mat& srcmat2, Mat& dstmat, double scale ) b *= d; a *= d; - T z0 = saturate_cast(src2[i+1] * src1[i] * b); - T z1 = saturate_cast(src2[i] * src1[i+1] * b); - T z2 = saturate_cast(src2[i+3] * src1[i+2] * a); - T z3 = saturate_cast(src2[i+2] * src1[i+3] * a); + T z0 = saturate_cast(src2[i+1] * ((double)src1[i] * b)); + T z1 = saturate_cast(src2[i] * ((double)src1[i+1] * b)); + T z2 = saturate_cast(src2[i+3] * ((double)src1[i+2] * a)); + T z3 = saturate_cast(src2[i+2] * ((double)src1[i+3] * a)); dst[i] = z0; dst[i+1] = z1; dst[i+2] = z2; dst[i+3] = z3; @@ -1193,7 +1193,7 @@ void divide(const Mat& src1, const Mat& src2, Mat& dst, double scale) }; MulDivFunc func = tab[src1.depth()]; - CV_Assert( src1.size() == src2.size() && src1.type() == src2.type() && func != 0 ); + CV_Assert( src1.type() == src2.type() && func != 0 ); if( src1.dims > 2 || src2.dims > 2 ) { @@ -1446,7 +1446,7 @@ void addWeighted( const Mat& src1, double alpha, const Mat& src2, addWeighted_, addWeighted_, addWeighted_, - addWeighted_, + addWeighted_, addWeighted_, 0 }; @@ -1952,7 +1952,7 @@ void compare( const Mat& src1, double value, Mat& dst, int cmpOp ) { func( it.planes[0], it.planes[1], value ); if( invflag ) - bitwise_not(it.planes[2], it.planes[2]); + bitwise_not(it.planes[1], it.planes[1]); } return; } diff --git a/modules/core/src/convert.cpp b/modules/core/src/convert.cpp index 5d4d70e2c..3dd95429b 100644 --- a/modules/core/src/convert.cpp +++ b/modules/core/src/convert.cpp @@ -619,7 +619,7 @@ static const int FBITS = 15; typedef void (*CvtFunc)( const Mat& src, Mat& dst ); typedef void (*CvtScaleFunc)( const Mat& src, Mat& dst, double scale, double shift ); -void convertScaleAbs( const Mat& src, Mat& dst, double scale, double shift ) +void convertScaleAbs( const Mat& src0, Mat& dst, double scale, double shift ) { static CvtScaleFunc tab[] = { @@ -632,15 +632,27 @@ void convertScaleAbs( const Mat& src, Mat& dst, double scale, double shift ) cvtScale_ >, 0 }; - Mat src0 = src; - dst.create( src.size(), CV_8UC(src.channels()) ); - CvtScaleFunc func = tab[src0.depth()]; + Mat src = src0; + dst.create( src.dims, src.size, CV_8UC(src.channels()) ); + CvtScaleFunc func = tab[src.depth()]; CV_Assert( func != 0 ); - func( src0, dst, scale, shift ); + + if( src.dims <= 2 ) + { + func( src, dst, scale, shift ); + } + else + { + const Mat* arrays[] = {&src, &dst, 0}; + Mat planes[2]; + NAryMatIterator it(arrays, planes); + + for( int i = 0; i < it.nplanes; i++, ++it ) + func(it.planes[0], it.planes[1], scale, shift); + } } - void Mat::convertTo(Mat& dst, int _type, double alpha, double beta) const { static CvtFunc tab[8][8] = @@ -699,7 +711,7 @@ void Mat::convertTo(Mat& dst, int _type, double alpha, double beta) const cvtScaleInt_, OpCvt, 0>, cvtScaleInt_, OpCvt, 0>, cvtScale_ >, - cvtScale_ >, + cvtScale_ >, cvtScale_ >, 0, }, @@ -709,7 +721,7 @@ void Mat::convertTo(Mat& dst, int _type, double alpha, double beta) const cvtScaleInt_, OpCvt, 1<<15>, cvtScaleInt_, OpCvt, 1<<15>, cvtScale_ >, - cvtScale_ >, + cvtScale_ >, cvtScale_ >, 0, }, @@ -719,7 +731,7 @@ void Mat::convertTo(Mat& dst, int _type, double alpha, double beta) const cvtScale_ >, cvtScale_ >, cvtScale_ >, - cvtScale_ >, + cvtScale_ >, cvtScale_ >, 0, }, diff --git a/modules/core/src/mathfuncs.cpp b/modules/core/src/mathfuncs.cpp index 2c0c8d676..7b63616dc 100644 --- a/modules/core/src/mathfuncs.cpp +++ b/modules/core/src/mathfuncs.cpp @@ -47,37 +47,17 @@ namespace cv { static const int MAX_BLOCK_SIZE = 1024; - typedef CvStatus (CV_STDCALL * MathFunc)(const void* src, void* dst, int len); -#define ICV_MATH_BLOCK_SIZE 256 - -#define _CV_SQRT_MAGIC 0xbe6f0000 - -#define _CV_SQRT_MAGIC_DBL CV_BIG_UINT(0xbfcd460000000000) - -#define _CV_ATAN_CF0 (-15.8131890796f) -#define _CV_ATAN_CF1 (61.0941945596f) -#define _CV_ATAN_CF2 0.f /*(-0.140500406322f)*/ - -static const float icvAtanTab[8] = { 0.f + _CV_ATAN_CF2, 90.f - _CV_ATAN_CF2, - 180.f - _CV_ATAN_CF2, 90.f + _CV_ATAN_CF2, - 360.f - _CV_ATAN_CF2, 270.f + _CV_ATAN_CF2, - 180.f + _CV_ATAN_CF2, 270.f - _CV_ATAN_CF2 -}; - -static const int icvAtanSign[8] = - { 0, 0x80000000, 0x80000000, 0, 0x80000000, 0, 0, 0x80000000 }; - float fastAtan2( float y, float x ) { double a, x2 = (double)x*x, y2 = (double)y*y; if( y2 <= x2 ) { - a = (180./CV_PI)*x*y/(x2 + 0.28*y2 + DBL_EPSILON); + a = (180./CV_PI)*x*y*(x2 + 0.43157974*y2)/(x2*x2 + y2*(0.76443945*x2 + 0.05831938*y2) + DBL_EPSILON); return (float)(x < 0 ? a + 180 : y >= 0 ? a : 360+a); } - a = (180./CV_PI)*x*y/(y2 + 0.28*x2 + DBL_EPSILON); + a = (180./CV_PI)*x*y*(y2 + 0.43157974*x2)/(y2*y2 + x2*(0.76443945*y2 + 0.05831938*x2) + DBL_EPSILON); return (float)(y >= 0 ? 90 - a : 270 - a); } @@ -95,15 +75,20 @@ static CvStatus CV_STDCALL FastAtan2_32f(const float *Y, const float *X, float * Cv32suf iabsmask; iabsmask.i = 0x7fffffff; __m128 eps = _mm_set1_ps((float)DBL_EPSILON), absmask = _mm_set1_ps(iabsmask.f); __m128 _90 = _mm_set1_ps((float)(CV_PI*0.5)), _180 = _mm_set1_ps((float)CV_PI), _360 = _mm_set1_ps((float)(CV_PI*2)); - __m128 zero = _mm_setzero_ps(), _0_28 = _mm_set1_ps(0.28f), scale4 = _mm_set1_ps(scale); + __m128 zero = _mm_setzero_ps(), scale4 = _mm_set1_ps(scale); + __m128 p0 = _mm_set1_ps(0.43157974f), q0 = _mm_set1_ps(0.76443945f), q1 = _mm_set1_ps(0.05831938f); for( ; i <= len - 4; i += 4 ) { __m128 x4 = _mm_loadu_ps(X + i), y4 = _mm_loadu_ps(Y + i); __m128 xq4 = _mm_mul_ps(x4, x4), yq4 = _mm_mul_ps(y4, y4); __m128 xly = _mm_cmplt_ps(xq4, yq4); - __m128 z4 = _mm_div_ps(_mm_mul_ps(x4, y4), _mm_add_ps(_mm_add_ps(_mm_max_ps(xq4, yq4), - _mm_mul_ps(_mm_min_ps(xq4, yq4), _0_28)), eps)); + __m128 t = _mm_min_ps(xq4, yq4); + xq4 = _mm_max_ps(xq4, yq4); yq4 = t; + __m128 z4 = _mm_div_ps(_mm_mul_ps(_mm_mul_ps(x4, y4), _mm_add_ps(xq4, _mm_mul_ps(yq4, p0))), + _mm_add_ps(eps, _mm_add_ps(_mm_mul_ps(xq4, xq4), + _mm_mul_ps(yq4, _mm_add_ps(_mm_mul_ps(xq4, q0), + _mm_mul_ps(yq4, q1)))))); // a4 <- x < y ? 90 : 0; __m128 a4 = _mm_and_ps(xly, _90); @@ -121,15 +106,19 @@ static CvStatus CV_STDCALL FastAtan2_32f(const float *Y, const float *X, float * } #endif - for( ; i < len; i++ ) + for( ; i < len; i++ ) { - float x = X[i], y = Y[i]; - float a, x2 = x*x, y2 = y*y; - if( y2 <= x2 ) - a = x*y/(x2 + 0.28f*y2 + (float)DBL_EPSILON) + (float)(x < 0 ? CV_PI : y >= 0 ? 0 : CV_PI*2); - else - a = (float)(y >= 0 ? CV_PI*0.5 : CV_PI*1.5) - x*y/(y2 + 0.28f*x2 + (float)DBL_EPSILON); - angle[i] = a*scale; + double x = X[i], y = Y[i], x2 = x*x, y2 = y*y, a; + + if( y2 <= x2 ) + a = (x < 0 ? CV_PI : y >= 0 ? 0 : CV_PI*2) + + x*y*(x2 + 0.43157974*y2)/(x2*x2 + y2*(0.76443945*x2 + 0.05831938*y2) + (float)DBL_EPSILON); + else + { + a = (y >= 0 ? CV_PI*0.5 : CV_PI*1.5) - + x*y*(y2 + 0.43157974*x2)/(y2*y2 + x2*(0.76443945*y2 + 0.05831938*x2) + (float)DBL_EPSILON); + } + angle[i] = a*scale; } return CV_OK; diff --git a/modules/core/src/matrix.cpp b/modules/core/src/matrix.cpp index c92019edc..e574d80af 100644 --- a/modules/core/src/matrix.cpp +++ b/modules/core/src/matrix.cpp @@ -102,6 +102,7 @@ static inline void setSize( Mat& m, int _dims, const int* _sz, m.step.p = (size_t*)fastMalloc(_dims*sizeof(m.step.p[0]) + (_dims+1)*sizeof(m.size.p[0])); m.size.p = (int*)(m.step.p + _dims) + 1; m.size.p[-1] = _dims; + m.rows = m.cols = -1; } } @@ -711,10 +712,19 @@ void insertImageCOI(const Mat& ch, CvArr* arr, int coi) Mat Mat::reshape(int new_cn, int new_rows) const { - CV_Assert( dims <= 2 ); - Mat hdr = *this; - int cn = channels(); + Mat hdr = *this; + + if( dims > 2 && new_rows == 0 && new_cn != 0 && size[dims-1]*cn % new_cn == 0 ) + { + hdr.flags = (hdr.flags & ~CV_MAT_CN_MASK) | ((new_cn-1) << CV_CN_SHIFT); + hdr.step[dims-1] = CV_ELEM_SIZE(hdr.flags); + hdr.size[dims-1] = hdr.size[dims-1]*cn / new_cn; + return hdr; + } + + CV_Assert( dims <= 2 ); + if( new_cn == 0 ) new_cn = cn; diff --git a/modules/core/src/stat.cpp b/modules/core/src/stat.cpp index 1000d8d24..44f981a37 100644 --- a/modules/core/src/stat.cpp +++ b/modules/core/src/stat.cpp @@ -409,7 +409,7 @@ template<> inline Vec SqrC4::operator() (const Vec static void -meanStdDev_( const Mat& srcmat, Scalar& _mean, Scalar& _stddev ) +meanStdDev_( const Mat& srcmat, Scalar& _sum, Scalar& _sqsum ) { SqrOp sqr; typedef typename SqrOp::type1 T; @@ -430,20 +430,13 @@ meanStdDev_( const Mat& srcmat, Scalar& _mean, Scalar& _stddev ) sq += sqr(v); } } - - _mean = _stddev = Scalar(); - double scale = 1./std::max(size.width*size.height, 1); - for( int i = 0; i < DataType::channels; i++ ) - { - double t = ((ST1*)&s)[i]*scale; - _mean.val[i] = t; - _stddev.val[i] = std::sqrt(std::max(((ST1*)&sq)[i]*scale - t*t, 0.)); - } + _sum = rawToScalar(s); + _sqsum = rawToScalar(sq); } template static void meanStdDevMask_( const Mat& srcmat, const Mat& maskmat, - Scalar& _mean, Scalar& _stddev ) + Scalar& _sum, Scalar& _sqsum, int& _nz ) { SqrOp sqr; typedef typename SqrOp::type1 T; @@ -470,20 +463,15 @@ meanStdDevMask_( const Mat& srcmat, const Mat& maskmat, pix++; } } - _mean = _stddev = Scalar(); - double scale = 1./std::max(pix, 1); - for( int i = 0; i < DataType::channels; i++ ) - { - double t = ((ST1*)&s)[i]*scale; - _mean.val[i] = t; - _stddev.val[i] = std::sqrt(std::max(((ST1*)&sq)[i]*scale - t*t, 0.)); - } + _sum = rawToScalar(s); + _sqsum = rawToScalar(sq); + _nz = pix; } -typedef void (*MeanStdDevFunc)(const Mat& src, Scalar& mean, Scalar& stddev); +typedef void (*MeanStdDevFunc)(const Mat& src, Scalar& s, Scalar& sq); typedef void (*MeanStdDevMaskFunc)(const Mat& src, const Mat& mask, - Scalar& mean, Scalar& stddev); + Scalar& s, Scalar& sq, int& nz); void meanStdDev( const Mat& m, Scalar& mean, Scalar& stddev, const Mat& mask ) { @@ -551,55 +539,53 @@ void meanStdDev( const Mat& m, Scalar& mean, Scalar& stddev, const Mat& mask ) CV_Assert( m.channels() <= 4 && (mask.empty() || mask.type() == CV_8U) ); + Scalar sum, sqsum; + int total = 0; MeanStdDevFunc func = tab[m.type()]; MeanStdDevMaskFunc mfunc = mtab[m.type()]; - CV_Assert( func != 0 || mfunc != 0 ); + CV_Assert( func != 0 && mfunc != 0 ); if( m.dims > 2 ) { - Scalar s, sq; - double total = 0; - const Mat* arrays[] = {&m, &mask, 0}; Mat planes[2]; NAryMatIterator it(arrays, planes); - int k, cn = m.channels(); + int nz = (int)planes[0].total(); for( int i = 0; i < it.nplanes; i++, ++it ) { - Scalar _mean, _stddev; - double nz = (double)(mask.data ? countNonZero(it.planes[1]) : it.planes[0].rows*it.planes[0].cols); + Scalar s, sq; - if( func ) - func(it.planes[0], _mean, _stddev); + if( mask.empty() ) + func(it.planes[0], s, sq); else - mfunc(it.planes[0], it.planes[1], _mean, _stddev); + mfunc(it.planes[0], it.planes[1], s, sq, nz); total += nz; - for( k = 0; k < cn; k++ ) - { - s[k] += _mean[k]*nz; - sq[k] += (_stddev[k]*_stddev[k] + _mean[k]*_mean[k])*nz; - } + sum += s; + sqsum += sq; } - - mean = stddev = Scalar(); - total = 1./std::max(total, 1.); - for( k = 0; k < cn; k++ ) - { - mean[k] = s[k]*total; - stddev[k] = std::sqrt(std::max(sq[k]*total - mean[k]*mean[k], 0.)); - } - return; - } - - if( mask.data ) - { - CV_Assert( mask.size() == m.size() ); - mfunc( m, mask, mean, stddev ); } else - func( m, mean, stddev ); + { + if( mask.data ) + { + CV_Assert( mask.size() == m.size() ); + mfunc( m, mask, sum, sqsum, total ); + } + else + { + func( m, sum, sqsum ); + total = (int)m.total(); + } + } + + double scale = 1./std::max(total, 1); + for( int k = 0; k < 4; k++ ) + { + mean[k] = sum[k]*scale; + stddev[k] = std::sqrt(std::max(sqsum[k]*scale - mean[k]*mean[k], 0.)); + } } @@ -607,45 +593,46 @@ void meanStdDev( const Mat& m, Scalar& mean, Scalar& stddev, const Mat& mask ) * minMaxLoc * \****************************************************************************************/ -template static void -minMaxIndx_( const Mat& srcmat, double* minVal, double* maxVal, int* minLoc, int* maxLoc ) +template static void +minMaxIndx_( const Mat& srcmat, double* _minVal, double* _maxVal, + size_t startIdx, size_t* _minIdx, size_t* _maxIdx ) { assert( DataType::type == srcmat.type() ); const T* src = (const T*)srcmat.data; size_t step = srcmat.step/sizeof(src[0]); - T min_val = src[0], max_val = min_val; - int min_loc = 0, max_loc = 0; - int x, loc = 0; + WT minVal = saturate_cast(*_minVal), maxVal = saturate_cast(*_maxVal); + size_t minIdx = *_minIdx, maxIdx = *_maxIdx; Size size = getContinuousSize( srcmat ); - for( ; size.height--; src += step, loc += size.width ) + for( ; size.height--; src += step, startIdx += size.width ) { - for( x = 0; x < size.width; x++ ) + for( int x = 0; x < size.width; x++ ) { T val = src[x]; - if( val < min_val ) + if( val < minVal ) { - min_val = val; - min_loc = loc + x; + minVal = val; + minIdx = startIdx + x; } - else if( val > max_val ) + if( val > maxVal ) { - max_val = val; - max_loc = loc + x; + maxVal = val; + maxIdx = startIdx + x; } } } - *minLoc = min_loc; - *maxLoc = max_loc; - *minVal = min_val; - *maxVal = max_val; + *_minIdx = minIdx; + *_maxIdx = maxIdx; + *_minVal = minVal; + *_maxVal = maxVal; } -template static void +template static void minMaxIndxMask_( const Mat& srcmat, const Mat& maskmat, - double* minVal, double* maxVal, int* minLoc, int* maxLoc ) + double* _minVal, double* _maxVal, + size_t startIdx, size_t* _minIdx, size_t* _maxIdx ) { assert( DataType::type == srcmat.type() && CV_8U == maskmat.type() && @@ -654,54 +641,40 @@ minMaxIndxMask_( const Mat& srcmat, const Mat& maskmat, const uchar* mask = maskmat.data; size_t step = srcmat.step/sizeof(src[0]); size_t maskstep = maskmat.step; - T min_val = 0, max_val = 0; - int min_loc = -1, max_loc = -1; - int x = 0, y, loc = 0; + WT minVal = saturate_cast(*_minVal), maxVal = saturate_cast(*_maxVal); + size_t minIdx = *_minIdx, maxIdx = *_maxIdx; Size size = getContinuousSize( srcmat, maskmat ); - for( y = 0; y < size.height; y++, src += step, mask += maskstep, loc += size.width ) + for( ; size.height--; src += step, mask += maskstep, startIdx += size.width ) { - for( x = 0; x < size.width; x++ ) - if( mask[x] != 0 ) - { - min_loc = max_loc = loc + x; - min_val = max_val = src[x]; - break; - } - if( x < size.width ) - break; - } - - for( ; y < size.height; x = 0, y++, src += step, mask += maskstep, loc += size.width ) - { - for( ; x < size.width; x++ ) + for( int x = 0; x < size.width; x++ ) { T val = src[x]; int m = mask[x]; - - if( val < min_val && m ) + + if( val < minVal && m ) { - min_val = val; - min_loc = loc + x; + minVal = val; + minIdx = startIdx + x; } - else if( val > max_val && m ) + if( val > maxVal && m ) { - max_val = val; - max_loc = loc + x; + maxVal = val; + maxIdx = startIdx + x; } } } - *minLoc = min_loc; - *maxLoc = max_loc; - *minVal = min_val; - *maxVal = max_val; + *_minIdx = minIdx; + *_maxIdx = maxIdx; + *_minVal = minVal; + *_maxVal = maxVal; } -typedef void (*MinMaxIndxFunc)(const Mat&, double*, double*, int*, int*); +typedef void (*MinMaxIndxFunc)(const Mat&, double*, double*, size_t, size_t*, size_t*); -typedef void (*MinMaxIndxMaskFunc)(const Mat&, const Mat&, - double*, double*, int*, int*); +typedef void (*MinMaxIndxMaskFunc)(const Mat&, const Mat&, double*, double*, + size_t, size_t*, size_t*); void minMaxLoc( const Mat& img, double* minVal, double* maxVal, Point* minLoc, Point* maxLoc, const Mat& mask ) @@ -709,15 +682,20 @@ void minMaxLoc( const Mat& img, double* minVal, double* maxVal, CV_Assert(img.dims <= 2); static MinMaxIndxFunc tab[] = - {minMaxIndx_, 0, minMaxIndx_, minMaxIndx_, - minMaxIndx_, minMaxIndx_, minMaxIndx_, 0}; + { + minMaxIndx_, 0, minMaxIndx_, minMaxIndx_, + minMaxIndx_, minMaxIndx_, minMaxIndx_, 0 + }; static MinMaxIndxMaskFunc tabm[] = - {minMaxIndxMask_, 0, minMaxIndxMask_, minMaxIndxMask_, - minMaxIndxMask_, minMaxIndxMask_, minMaxIndxMask_, 0}; + { + minMaxIndxMask_, 0, minMaxIndxMask_, minMaxIndxMask_, + minMaxIndxMask_, minMaxIndxMask_, minMaxIndxMask_, 0 + }; int depth = img.depth(); - double minval=0, maxval=0; - int minloc=0, maxloc=0; + double minval = depth < CV_32F ? INT_MAX : depth == CV_32F ? FLT_MAX : DBL_MAX; + double maxval = depth < CV_32F ? INT_MIN : depth == CV_32F ? -FLT_MAX : -DBL_MAX; + size_t minidx = 0, maxidx = 0, startidx = 1; CV_Assert( img.channels() == 1 ); @@ -725,36 +703,41 @@ void minMaxLoc( const Mat& img, double* minVal, double* maxVal, { MinMaxIndxFunc func = tab[depth]; CV_Assert( func != 0 ); - func( img, &minval, &maxval, &minloc, &maxloc ); + func( img, &minval, &maxval, startidx, &minidx, &maxidx ); } else { CV_Assert( img.size() == mask.size() && mask.type() == CV_8U ); MinMaxIndxMaskFunc func = tabm[depth]; CV_Assert( func != 0 ); - func( img, mask, &minval, &maxval, &minloc, &maxloc ); + func( img, mask, &minval, &maxval, startidx, &minidx, &maxidx ); } + if( minidx == 0 ) + minVal = maxVal = 0; + if( minVal ) *minVal = minval; if( maxVal ) *maxVal = maxval; if( minLoc ) { - if( minloc >= 0 ) + if( minidx > 0 ) { - minLoc->y = minloc/img.cols; - minLoc->x = minloc - minLoc->y*img.cols; + minidx--; + minLoc->y = minidx/img.cols; + minLoc->x = minidx - minLoc->y*img.cols; } else minLoc->x = minLoc->y = -1; } if( maxLoc ) { - if( maxloc >= 0 ) + if( maxidx > 0 ) { - maxLoc->y = maxloc/img.cols; - maxLoc->x = maxloc - maxLoc->y*img.cols; + maxidx--; + maxLoc->y = maxidx/img.cols; + maxLoc->x = maxidx - maxLoc->y*img.cols; } else maxLoc->x = maxLoc->y = -1; @@ -764,10 +747,20 @@ void minMaxLoc( const Mat& img, double* minVal, double* maxVal, static void ofs2idx(const Mat& a, size_t ofs, int* idx) { int i, d = a.dims; - for( i = 0; i < d; i++ ) + if( ofs > 0 ) { - idx[i] = (int)(ofs / a.step[i]); - ofs %= a.step[i]; + ofs--; + for( i = d-1; i >= 0; i-- ) + { + int sz = a.size[i]; + idx[i] = (int)(ofs % sz); + ofs /= sz; + } + } + else + { + for( i = d-1; i >= 0; i-- ) + idx[i] = -1; } } @@ -780,43 +773,60 @@ void minMaxIdx(const Mat& a, double* minVal, Point minLoc, maxLoc; minMaxLoc(a, minVal, maxVal, &minLoc, &maxLoc, mask); if( minIdx ) - minIdx[0] = minLoc.x, minIdx[1] = minLoc.y; + minIdx[0] = minLoc.y, minIdx[1] = minLoc.x; if( maxIdx ) - maxIdx[0] = maxLoc.x, maxIdx[1] = maxLoc.y; + maxIdx[0] = maxLoc.y, maxIdx[1] = maxLoc.x; return; } + + static MinMaxIndxFunc tab[] = + { + minMaxIndx_, 0, minMaxIndx_, minMaxIndx_, + minMaxIndx_, minMaxIndx_, minMaxIndx_, 0 + }; + static MinMaxIndxMaskFunc tabm[] = + { + minMaxIndxMask_, 0, minMaxIndxMask_, minMaxIndxMask_, + minMaxIndxMask_, minMaxIndxMask_, minMaxIndxMask_, 0 + }; const Mat* arrays[] = {&a, &mask, 0}; Mat planes[2]; NAryMatIterator it(arrays, planes); - double minval = DBL_MAX, maxval = -DBL_MAX; - size_t minofs = 0, maxofs = 0, esz = a.elemSize(); - for( int i = 0; i < it.nplanes; i++, ++it ) + int depth = a.depth(); + double minval = depth < CV_32F ? INT_MAX : depth == CV_32F ? FLT_MAX : DBL_MAX; + double maxval = depth < CV_32F ? INT_MIN : depth == CV_32F ? -FLT_MAX : -DBL_MAX; + size_t minidx = 0, maxidx = 0; + size_t startidx = 1, planeSize = planes[0].total(); + MinMaxIndxFunc func = 0; + MinMaxIndxMaskFunc mfunc = 0; + + if( mask.empty() ) + func = tab[depth]; + else + mfunc = tabm[depth]; + CV_Assert( func != 0 || mfunc != 0 ); + + for( int i = 0; i < it.nplanes; i++, ++it, startidx += planeSize ) { - double val0 = 0, val1 = 0; - Point pt0, pt1; - minMaxLoc( it.planes[0], &val0, &val1, &pt0, &pt1, it.planes[1] ); - if( val0 < minval ) - { - minval = val0; - minofs = (it.planes[0].data - a.data) + pt0.x*esz; - } - if( val1 > maxval ) - { - maxval = val1; - maxofs = (it.planes[0].data - a.data) + pt1.x*esz; - } + if( func ) + func( planes[0], &minval, &maxval, startidx, &minidx, &maxidx ); + else + mfunc( planes[0], planes[1], &minval, &maxval, startidx, &minidx, &maxidx ); } + if( minidx == 0 ) + minVal = maxVal = 0; + if( minVal ) *minVal = minval; if( maxVal ) *maxVal = maxval; if( minIdx ) - ofs2idx(a, minofs, minIdx); + ofs2idx(a, minidx, minIdx); if( maxIdx ) - ofs2idx(a, maxofs, maxIdx); + ofs2idx(a, maxidx, maxIdx); } /****************************************************************************************\ @@ -922,6 +932,7 @@ static double normMaskBlock_( const Mat& srcmat, const Mat& maskmat ) ST s0 = 0; WT s = 0; int y, remaining = BLOCK_SIZE; + int cn = srcmat.channels(); for( y = 0; y < size.height; y++ ) { @@ -933,21 +944,25 @@ static double normMaskBlock_( const Mat& srcmat, const Mat& maskmat ) int limit = std::min( remaining, size.width - x ); remaining -= limit; limit += x; - for( ; x <= limit - 4; x += 4 ) + int x0 = x; + for( int c = 0; c < cn; c++ ) { - if( mask[x] ) - s = update(s, (WT)f(src[x])); - if( mask[x+1] ) - s = update(s, (WT)f(src[x+1])); - if( mask[x+2] ) - s = update(s, (WT)f(src[x+2])); - if( mask[x+3] ) - s = update(s, (WT)f(src[x+3])); - } - for( ; x < limit; x++ ) - { - if( mask[x] ) - s = update(s, (WT)f(src[x])); + for( x = x0; x <= limit - 4; x += 4 ) + { + if( mask[x] ) + s = update(s, (WT)f(src[x*cn + c])); + if( mask[x+1] ) + s = update(s, (WT)f(src[(x+1)*cn + c])); + if( mask[x+2] ) + s = update(s, (WT)f(src[(x+2)*cn + c])); + if( mask[x+3] ) + s = update(s, (WT)f(src[(x+3)*cn + c])); + } + for( ; x < limit; x++ ) + { + if( mask[x] ) + s = update(s, (WT)f(src[x*cn + c])); + } } if( remaining == 0 || (x == size.width && y == size.height-1) ) { @@ -971,27 +986,31 @@ static double normMask_( const Mat& srcmat, const Mat& maskmat ) assert( DataType::depth == srcmat.depth() ); Size size = getContinuousSize( srcmat, maskmat ); ST s = 0; - + int cn = srcmat.channels(); + for( int y = 0; y < size.height; y++ ) { const T* src = (const T*)(srcmat.data + srcmat.step*y); const uchar* mask = maskmat.data + maskmat.step*y; - int x = 0; - for( ; x <= size.width - 4; x += 4 ) + for( int c = 0; c < cn; c++ ) { - if( mask[x] ) - s = update(s, (ST)f(src[x])); - if( mask[x+1] ) - s = update(s, (ST)f(src[x+1])); - if( mask[x+2] ) - s = update(s, (ST)f(src[x+2])); - if( mask[x+3] ) - s = update(s, (ST)f(src[x+3])); - } - for( ; x < size.width; x++ ) - { - if( mask[x] ) - s = update(s, (ST)f(src[x])); + int x = 0; + for( ; x <= size.width - 4; x += 4 ) + { + if( mask[x] ) + s = update(s, (ST)f(src[x*cn + c])); + if( mask[x+1] ) + s = update(s, (ST)f(src[(x+1)*cn + c])); + if( mask[x+2] ) + s = update(s, (ST)f(src[(x+2)*cn + c])); + if( mask[x+3] ) + s = update(s, (ST)f(src[(x+3)*cn + c])); + } + for( ; x < size.width; x++ ) + { + if( mask[x] ) + s = update(s, (ST)f(src[x*cn + c])); + } } } return s; @@ -1085,6 +1104,7 @@ static double normDiffMaskBlock_( const Mat& srcmat1, const Mat& srcmat2, const ST s0 = 0; WT s = 0; int y, remaining = BLOCK_SIZE; + int cn = srcmat1.channels(); for( y = 0; y < size.height; y++ ) { @@ -1097,20 +1117,24 @@ static double normDiffMaskBlock_( const Mat& srcmat1, const Mat& srcmat2, const int limit = std::min( remaining, size.width - x ); remaining -= limit; limit += x; - for( ; x <= limit - 4; x += 4 ) + int x0 = x; + for( int c = 0; c < cn; c++ ) { - if( mask[x] ) - s = update(s, (WT)f(src1[x] - src2[x])); - if( mask[x+1] ) - s = update(s, (WT)f(src1[x+1] - src2[x+1])); - if( mask[x+2] ) - s = update(s, (WT)f(src1[x+2] - src2[x+2])); - if( mask[x+3] ) - s = update(s, (WT)f(src1[x+3] - src2[x+3])); + for( x = x0; x <= limit - 4; x += 4 ) + { + if( mask[x] ) + s = update(s, (WT)f(src1[x*cn + c] - src2[x*cn + c])); + if( mask[x+1] ) + s = update(s, (WT)f(src1[(x+1)*cn + c] - src2[(x+1)*cn + c])); + if( mask[x+2] ) + s = update(s, (WT)f(src1[(x+2)*cn + c] - src2[(x+2)*cn + c])); + if( mask[x+3] ) + s = update(s, (WT)f(src1[(x+3)*cn + c] - src2[(x+3)*cn + c])); + } + for( ; x < limit; x++ ) + if( mask[x] ) + s = update(s, (WT)f(src1[x*cn + c] - src2[x*cn + c])); } - for( ; x < limit; x++ ) - if( mask[x] ) - s = update(s, (WT)f(src1[x] - src2[x])); if( remaining == 0 || (x == size.width && y == size.height-1) ) { s0 = globUpdate(s0, (ST)s); @@ -1132,28 +1156,31 @@ static double normDiffMask_( const Mat& srcmat1, const Mat& srcmat2, const Mat& assert( DataType::depth == srcmat1.depth() ); Size size = getContinuousSize( srcmat1, srcmat2, maskmat ); ST s = 0; + int cn = srcmat1.channels(); for( int y = 0; y < size.height; y++ ) { const T* src1 = (const T*)(srcmat1.data + srcmat1.step*y); const T* src2 = (const T*)(srcmat2.data + srcmat2.step*y); const uchar* mask = maskmat.data + maskmat.step*y; - int x = 0; - for( ; x <= size.width - 4; x += 4 ) + for( int c = 0; c < cn; c++ ) { - if( mask[x] ) - s = update(s, (ST)f(src1[x] - src2[x])); - if( mask[x+1] ) - s = update(s, (ST)f(src1[x+1] - src2[x+1])); - if( mask[x+2] ) - s = update(s, (ST)f(src1[x+2] - src2[x+2])); - if( mask[x+3] ) - s = update(s, (ST)f(src1[x+3] - src2[x+3])); + int x = 0; + for( ; x <= size.width - 4; x += 4 ) + { + if( mask[x] ) + s = update(s, (ST)f(src1[x*cn + c] - src2[x*cn + c])); + if( mask[x+1] ) + s = update(s, (ST)f(src1[(x+1)*cn + c] - src2[(x+1)*cn + c])); + if( mask[x+2] ) + s = update(s, (ST)f(src1[(x+2)*cn + c] - src2[(x+2)*cn + c])); + if( mask[x+3] ) + s = update(s, (ST)f(src1[(x+3)*cn + c] - src2[(x+3)*cn + c])); + } + for( ; x < size.width; x++ ) + if( mask[x] ) + s = update(s, (ST)f(src1[x*cn + c] - src2[x*cn + c])); } - for( ; x < size.width; x++ ) - if( mask[x] ) - s = update(s, (ST)f(src1[x] - src2[x])); - } return s; } @@ -1265,8 +1292,7 @@ double norm( const Mat& a, int normType, const Mat& mask ) return norm(a, normType); normType &= 7; - CV_Assert((normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2) && - mask.type() == CV_8U && a.channels() == 1); + CV_Assert((normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2) && mask.type() == CV_8U); NormMaskFunc func = tab[normType >> 1][a.depth()]; CV_Assert(func != 0); @@ -1405,7 +1431,7 @@ double norm( const Mat& a, const Mat& b, int normType, const Mat& mask ) if( !mask.data ) return norm(a, b, normType); - CV_Assert( a.type() == b.type() && mask.type() == CV_8U && a.channels() == 1); + CV_Assert( a.type() == b.type() && mask.type() == CV_8U); bool isRelative = (normType & NORM_RELATIVE) != 0; normType &= 7; CV_Assert(normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2); diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index c7b0252ec..c6022ce50 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -1,61 +1,1379 @@ #include "test_precomp.hpp" -#include using namespace cv; using namespace std; +namespace cvtest +{ + const int ARITHM_NTESTS = 1000; const int ARITHM_RNG_SEED = -1; +const int ARITHM_MAX_CHANNELS = 4; const int ARITHM_MAX_NDIMS = 4; const int ARITHM_MAX_SIZE_LOG = 10; -const int ARITHM_MAX_CHANNELS = 4; -static void getArithmValueRange(int depth, double& minval, double& maxval) +struct BaseElemWiseOp { - minval = depth < CV_32S ? cvtest::getMinVal(depth) : depth == CV_32S ? -1000000 : -1000.; - maxval = depth < CV_32S ? cvtest::getMinVal(depth) : depth == CV_32S ? 1000000 : 1000.; -} - -static double getArithmMaxErr(int depth) -{ - return depth < CV_32F ? 0 : 4; -} - -TEST(ArithmTest, add) -{ - int testIdx = 0; - RNG rng(ARITHM_RNG_SEED); - for( testIdx = 0; testIdx < ARITHM_NTESTS; testIdx++ ) + enum { FIX_ALPHA=1, FIX_BETA=2, FIX_GAMMA=4, REAL_GAMMA=8, SUPPORT_MASK=16, SCALAR_OUTPUT=32 }; + BaseElemWiseOp(int _ninputs, int _flags, double _alpha, double _beta, + Scalar _gamma=Scalar::all(0), int _context=1) + : ninputs(_ninputs), flags(_flags), alpha(_alpha), beta(_beta), gamma(_gamma), context(_context) {} + BaseElemWiseOp() { flags = alpha = beta = 0; gamma = Scalar::all(0); } + virtual ~BaseElemWiseOp() {} + virtual void op(const vector&, Mat&, const Mat&) {} + virtual void refop(const vector&, Mat&, const Mat&) {} + virtual void getValueRange(int depth, double& minval, double& maxval) { - double minval, maxval; - vector size; - cvtest::randomSize(rng, 2, ARITHM_MAX_NDIMS, ARITHM_MAX_SIZE_LOG, size); - int type = cvtest::randomType(rng, cvtest::TYPE_MASK_ALL, 1, ARITHM_MAX_CHANNELS); - int depth = CV_MAT_DEPTH(type); - bool haveMask = rng.uniform(0, 4) == 0; + minval = depth < CV_32S ? cvtest::getMinVal(depth) : depth == CV_32S ? -1000000 : -1000.; + maxval = depth < CV_32S ? cvtest::getMaxVal(depth) : depth == CV_32S ? 1000000 : 1000.; + } + + virtual void getRandomSize(RNG& rng, vector& size) + { + cvtest::randomSize(rng, 2, ARITHM_MAX_NDIMS, cvtest::ARITHM_MAX_SIZE_LOG, size); + } + + virtual int getRandomType(RNG& rng) + { + return cvtest::randomType(rng, cvtest::TYPE_MASK_ALL_BUT_8S, 1, + ninputs > 1 ? ARITHM_MAX_CHANNELS : 4); + } - getArithmValueRange(depth, minval, maxval); - Mat src1 = cvtest::randomMat(rng, size, type, minval, maxval, true); - Mat src2 = cvtest::randomMat(rng, size, type, minval, maxval, true); - Mat dst0 = cvtest::randomMat(rng, size, type, minval, maxval, false); - Mat dst = cvtest::randomMat(rng, size, type, minval, maxval, true); - Mat mask; - if( haveMask ) + virtual int getMaxErr(int depth) { return depth < CV_32F ? 1 : 256; } + virtual void generateScalars(int depth, RNG& rng) + { + const double m = 3.; + + if( !(flags & FIX_ALPHA) ) { - mask = cvtest::randomMat(rng, size, CV_8U, 0, 2, true); - cvtest::copy(dst0, dst); - cvtest::add(src1, 1, src2, 1, Scalar::all(0), dst0, dst.type()); - cvtest::copy(dst, dst0, mask, true); - add(src1, src2, dst, mask); + alpha = exp(rng.uniform(-0.5, 0.1)*m*2*CV_LOG2); + alpha *= rng.uniform(0, 2) ? 1 : -1; + } + if( !(flags & FIX_BETA) ) + { + beta = exp(rng.uniform(-0.5, 0.1)*m*2*CV_LOG2); + beta *= rng.uniform(0, 2) ? 1 : -1; + } + + if( !(flags & FIX_GAMMA) ) + { + for( int i = 0; i < 4; i++ ) + { + gamma[i] = exp(rng.uniform(-1, 6)*m*CV_LOG2); + gamma[i] *= rng.uniform(0, 2) ? 1 : -1; + } + if( flags & REAL_GAMMA ) + gamma = Scalar::all(gamma[0]); + } + + if( depth == CV_32F ) + { + Mat fl, db; + + db = Mat(1, 1, CV_64F, &alpha); + db.convertTo(fl, CV_32F); + fl.convertTo(db, CV_64F); + + db = Mat(1, 1, CV_64F, &beta); + db.convertTo(fl, CV_32F); + fl.convertTo(db, CV_64F); + + db = Mat(1, 4, CV_64F, &gamma[0]); + db.convertTo(fl, CV_32F); + fl.convertTo(db, CV_64F); + } + } + + int ninputs; + int flags; + double alpha; + double beta; + Scalar gamma; + int maxErr; + int context; +}; + + +struct BaseAddOp : public BaseElemWiseOp +{ + BaseAddOp(int _ninputs, int _flags, double _alpha, double _beta, Scalar _gamma=Scalar::all(0)) + : BaseElemWiseOp(_ninputs, _flags, _alpha, _beta, _gamma) {} + + void refop(const vector& src, Mat& dst, const Mat& mask) + { + Mat temp; + if( !mask.empty() ) + { + cvtest::add(src[0], alpha, src.size() > 1 ? src[1] : Mat(), beta, gamma, temp, src[0].type()); + cvtest::copy(temp, dst, mask); + } + else + cvtest::add(src[0], alpha, src.size() > 1 ? src[1] : Mat(), beta, gamma, dst, src[0].type()); + } +}; + + +struct AddOp : public BaseAddOp +{ + AddOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat& mask) + { + if( mask.empty() ) + add(src[0], src[1], dst); + else + add(src[0], src[1], dst, mask); + } +}; + + +struct SubOp : public BaseAddOp +{ + SubOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, -1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat& mask) + { + if( mask.empty() ) + subtract(src[0], src[1], dst); + else + subtract(src[0], src[1], dst, mask); + } +}; + + +struct AddSOp : public BaseAddOp +{ + AddSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, 1, 0, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat& mask) + { + if( mask.empty() ) + add(src[0], gamma, dst); + else + add(src[0], gamma, dst, mask); + } +}; + + +struct SubRSOp : public BaseAddOp +{ + SubRSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, -1, 0, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat& mask) + { + if( mask.empty() ) + subtract(gamma, src[0], dst); + else + subtract(gamma, src[0], dst, mask); + } +}; + + +struct ScaleAddOp : public BaseAddOp +{ + ScaleAddOp() : BaseAddOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + scaleAdd(src[0], alpha, src[1], dst); + } +}; + + +struct AddWeightedOp : public BaseAddOp +{ + AddWeightedOp() : BaseAddOp(2, REAL_GAMMA, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + addWeighted(src[0], alpha, src[1], beta, gamma[0], dst); + } + int getMaxErr(int depth) + { + return depth <= CV_32S ? 2 : depth < CV_64F ? (1 << 10) : (1 << 22); + } +}; + +struct MulOp : public BaseElemWiseOp +{ + MulOp() : BaseElemWiseOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + cv::multiply(src[0], src[1], dst, alpha); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::multiply(src[0], src[1], dst, alpha); + } + int getMaxErr(int depth) + { + return depth < CV_32S ? 2 : depth < CV_32F ? 4 : 16; + } +}; + +struct DivOp : public BaseElemWiseOp +{ + DivOp() : BaseElemWiseOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + cv::divide(src[0], src[1], dst, alpha); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::divide(src[0], src[1], dst, alpha); + } + int getMaxErr(int depth) + { + return depth < CV_32S ? 2 : depth < CV_32F ? 4 : 16; + } +}; + +struct RecipOp : public BaseElemWiseOp +{ + RecipOp() : BaseElemWiseOp(1, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + cv::divide(alpha, src[0], dst); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::divide(Mat(), src[0], dst, alpha); + } + int getMaxErr(int depth) + { + return depth < CV_32S ? 2 : depth < CV_32F ? 4 : 16; + } +}; + +struct AbsDiffOp : public BaseAddOp +{ + AbsDiffOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, -1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + absdiff(src[0], src[1], dst); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::add(src[0], 1, src[1], -1, Scalar::all(0), dst, src[0].type(), true); + } +}; + +struct AbsDiffSOp : public BaseAddOp +{ + AbsDiffSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA, 1, 0, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + absdiff(src[0], gamma, dst); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::add(src[0], 1, Mat(), 0, -gamma, dst, src[0].type(), true); + } +}; + +struct LogicOp : public BaseElemWiseOp +{ + LogicOp(char _opcode) : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)), opcode(_opcode) {}; + void op(const vector& src, Mat& dst, const Mat& mask) + { + if( opcode == '&' ) + bitwise_and(src[0], src[1], dst, mask); + else if( opcode == '|' ) + bitwise_or(src[0], src[1], dst, mask); + else + bitwise_xor(src[0], src[1], dst, mask); + } + void refop(const vector& src, Mat& dst, const Mat& mask) + { + Mat temp; + if( !mask.empty() ) + { + cvtest::logicOp(src[0], src[1], temp, opcode); + cvtest::copy(temp, dst, mask); + } + else + cvtest::logicOp(src[0], src[1], dst, opcode); + } + int getMaxErr(int depth) + { + return 0; + } + char opcode; +}; + +struct LogicSOp : public BaseElemWiseOp +{ + LogicSOp(char _opcode) + : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+(_opcode != '~' ? SUPPORT_MASK : 0), 1, 1, Scalar::all(0)), opcode(_opcode) {}; + void op(const vector& src, Mat& dst, const Mat& mask) + { + if( opcode == '&' ) + bitwise_and(src[0], gamma, dst, mask); + else if( opcode == '|' ) + bitwise_or(src[0], gamma, dst, mask); + else if( opcode == '^' ) + bitwise_xor(src[0], gamma, dst, mask); + else + bitwise_not(src[0], dst); + } + void refop(const vector& src, Mat& dst, const Mat& mask) + { + Mat temp; + if( !mask.empty() ) + { + cvtest::logicOp(src[0], gamma, temp, opcode); + cvtest::copy(temp, dst, mask); + } + else + cvtest::logicOp(src[0], gamma, dst, opcode); + } + int getMaxErr(int) + { + return 0; + } + char opcode; +}; + +struct MinOp : public BaseElemWiseOp +{ + MinOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + cv::min(src[0], src[1], dst); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::min(src[0], src[1], dst); + } + int getMaxErr(int depth) + { + return 0; + } +}; + +struct MaxOp : public BaseElemWiseOp +{ + MaxOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + cv::max(src[0], src[1], dst); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::max(src[0], src[1], dst); + } + int getMaxErr(int depth) + { + return 0; + } +}; + +struct MinSOp : public BaseElemWiseOp +{ + MinSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + cv::min(src[0], gamma[0], dst); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::min(src[0], gamma[0], dst); + } + int getMaxErr(int depth) + { + return 0; + } +}; + +struct MaxSOp : public BaseElemWiseOp +{ + MaxSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + cv::max(src[0], gamma[0], dst); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::max(src[0], gamma[0], dst); + } + int getMaxErr(int depth) + { + return 0; + } +}; + +struct CmpOp : public BaseElemWiseOp +{ + CmpOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + void generateScalars(int depth, RNG& rng) + { + BaseElemWiseOp::generateScalars(depth, rng); + cmpop = rng.uniform(0, 6); + } + void op(const vector& src, Mat& dst, const Mat&) + { + cv::compare(src[0], src[1], dst, cmpop); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::compare(src[0], src[1], dst, cmpop); + } + int getRandomType(RNG& rng) + { + return cvtest::randomType(rng, cvtest::TYPE_MASK_ALL_BUT_8S, 1, 1); + } + + int getMaxErr(int) + { + return 0; + } + int cmpop; +}; + +struct CmpSOp : public BaseElemWiseOp +{ + CmpSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {}; + void generateScalars(int depth, RNG& rng) + { + BaseElemWiseOp::generateScalars(depth, rng); + cmpop = rng.uniform(0, 6); + } + void op(const vector& src, Mat& dst, const Mat&) + { + cv::compare(src[0], gamma[0], dst, cmpop); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::compare(src[0], gamma[0], dst, cmpop); + } + int getRandomType(RNG& rng) + { + return cvtest::randomType(rng, cvtest::TYPE_MASK_ALL_BUT_8S, 1, 1); + } + int getMaxErr(int) + { + return 0; + } + int cmpop; +}; + + +struct CopyOp : public BaseElemWiseOp +{ + CopyOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat& mask) + { + src[0].copyTo(dst, mask); + } + void refop(const vector& src, Mat& dst, const Mat& mask) + { + cvtest::copy(src[0], dst, mask); + } + int getRandomType(RNG& rng) + { + return cvtest::randomType(rng, cvtest::TYPE_MASK_ALL, 1, ARITHM_MAX_CHANNELS); + } + int getMaxErr(int) + { + return 0; + } + int cmpop; +}; + + +struct SetOp : public BaseElemWiseOp +{ + SetOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {}; + void op(const vector&, Mat& dst, const Mat& mask) + { + dst.setTo(gamma, mask); + } + void refop(const vector& src, Mat& dst, const Mat& mask) + { + cvtest::set(dst, gamma, mask); + } + int getRandomType(RNG& rng) + { + return cvtest::randomType(rng, cvtest::TYPE_MASK_ALL, 1, ARITHM_MAX_CHANNELS); + } + int getMaxErr(int) + { + return 0; + } +}; + +template static void +inRangeS_(const _Tp* src, const _WTp* a, const _WTp* b, uchar* dst, size_t total, int cn) +{ + size_t i; + int c; + for( i = 0; i < total; i++ ) + { + _Tp val = src[i*cn]; + dst[i] = a[0] <= val && val < b[0] ? 255 : 0; + } + for( c = 1; c < cn; c++ ) + { + for( i = 0; i < total; i++ ) + { + _Tp val = src[i*cn + c]; + dst[i] = a[c] <= val && val < b[c] ? dst[i] : 0; + } + } +} + +template static void inRange_(const _Tp* src, const _Tp* a, const _Tp* b, uchar* dst, size_t total, int cn) +{ + size_t i; + int c; + for( i = 0; i < total; i++ ) + { + _Tp val = src[i*cn]; + dst[i] = a[i*cn] <= val && val < b[i*cn] ? 255 : 0; + } + for( c = 1; c < cn; c++ ) + { + for( i = 0; i < total; i++ ) + { + _Tp val = src[i*cn + c]; + dst[i] = a[i*cn + c] <= val && val < b[i*cn + c] ? dst[i] : 0; + } + } +} + + +static void inRange(const Mat& src, const Mat& lb, const Mat& rb, Mat& dst) +{ + CV_Assert( src.type() == lb.type() && src.type() == rb.type() && + src.size == lb.size && src.size == rb.size ); + dst.create( src.dims, &src.size[0], CV_8U ); + const Mat *arrays[]={&src, &lb, &rb, &dst, 0}; + Mat planes[4]; + + NAryMatIterator it(arrays, planes); + size_t total = planes[0].total(); + int i, nplanes = it.nplanes, depth = src.depth(), cn = src.channels(); + + for( i = 0; i < nplanes; i++, ++it ) + { + const uchar* sptr = planes[0].data; + const uchar* aptr = planes[1].data; + const uchar* bptr = planes[2].data; + uchar* dptr = planes[3].data; + + switch( depth ) + { + case CV_8U: + inRange_((const uchar*)sptr, (const uchar*)aptr, (const uchar*)bptr, dptr, total, cn); + break; + case CV_8S: + inRange_((const schar*)sptr, (const schar*)aptr, (const schar*)bptr, dptr, total, cn); + break; + case CV_16U: + inRange_((const ushort*)sptr, (const ushort*)aptr, (const ushort*)bptr, dptr, total, cn); + break; + case CV_16S: + inRange_((const short*)sptr, (const short*)aptr, (const short*)bptr, dptr, total, cn); + break; + case CV_32S: + inRange_((const int*)sptr, (const int*)aptr, (const int*)bptr, dptr, total, cn); + break; + case CV_32F: + inRange_((const float*)sptr, (const float*)aptr, (const float*)bptr, dptr, total, cn); + break; + case CV_64F: + inRange_((const double*)sptr, (const double*)aptr, (const double*)bptr, dptr, total, cn); + break; + default: + CV_Error(CV_StsUnsupportedFormat, ""); + } + } +} + + +static void inRangeS(const Mat& src, const Scalar& lb, const Scalar& rb, Mat& dst) +{ + dst.create( src.dims, &src.size[0], CV_8U ); + const Mat *arrays[]={&src, &dst, 0}; + Mat planes[2]; + + NAryMatIterator it(arrays, planes); + size_t total = planes[0].total(); + int i, nplanes = it.nplanes, depth = src.depth(), cn = src.channels(); + double lbuf[4], rbuf[4]; + int wtype = CV_MAKETYPE(depth <= CV_32S ? CV_32S : depth, cn); + scalarToRawData(lb, lbuf, wtype, cn); + scalarToRawData(rb, rbuf, wtype, cn); + + for( i = 0; i < nplanes; i++, ++it ) + { + const uchar* sptr = planes[0].data; + uchar* dptr = planes[1].data; + + switch( depth ) + { + case CV_8U: + inRangeS_((const uchar*)sptr, (const int*)lbuf, (const int*)rbuf, dptr, total, cn); + break; + case CV_8S: + inRangeS_((const schar*)sptr, (const int*)lbuf, (const int*)rbuf, dptr, total, cn); + break; + case CV_16U: + inRangeS_((const ushort*)sptr, (const int*)lbuf, (const int*)rbuf, dptr, total, cn); + break; + case CV_16S: + inRangeS_((const short*)sptr, (const int*)lbuf, (const int*)rbuf, dptr, total, cn); + break; + case CV_32S: + inRangeS_((const int*)sptr, (const int*)lbuf, (const int*)rbuf, dptr, total, cn); + break; + case CV_32F: + inRangeS_((const float*)sptr, (const float*)lbuf, (const float*)rbuf, dptr, total, cn); + break; + case CV_64F: + inRangeS_((const double*)sptr, (const double*)lbuf, (const double*)rbuf, dptr, total, cn); + break; + default: + CV_Error(CV_StsUnsupportedFormat, ""); + } + } +} + + +struct InRangeSOp : public BaseElemWiseOp +{ + InRangeSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + cv::inRange(src[0], gamma, gamma1, dst); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::inRangeS(src[0], gamma, gamma1, dst); + } + int getMaxErr(int) + { + return 0; + } + void generateScalars(int depth, RNG& rng) + { + BaseElemWiseOp::generateScalars(depth, rng); + Scalar temp = gamma; + BaseElemWiseOp::generateScalars(depth, rng); + for( int i = 0; i < 4; i++ ) + { + gamma1[i] = std::max(gamma[i], temp[i]); + gamma[i] = std::min(gamma[i], temp[i]); + } + } + Scalar gamma1; +}; + + +struct InRangeOp : public BaseElemWiseOp +{ + InRangeOp() : BaseElemWiseOp(3, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + Mat lb, rb; + cvtest::min(src[1], src[2], lb); + cvtest::max(src[1], src[2], rb); + + cv::inRange(src[0], lb, rb, dst); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + Mat lb, rb; + cvtest::min(src[1], src[2], lb); + cvtest::max(src[1], src[2], rb); + + cvtest::inRange(src[0], lb, rb, dst); + } + int getMaxErr(int) + { + return 0; + } +}; + + +struct ConvertScaleOp : public BaseElemWiseOp +{ + ConvertScaleOp() : BaseElemWiseOp(1, FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)), ddepth(0) { }; + void op(const vector& src, Mat& dst, const Mat&) + { + src[0].convertTo(dst, ddepth, alpha, gamma[0]); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::convert(src[0], dst, CV_MAKETYPE(ddepth, src[0].channels()), alpha, gamma[0]); + } + int getRandomType(RNG& rng) + { + int srctype = cvtest::randomType(rng, cvtest::TYPE_MASK_ALL, 1, ARITHM_MAX_CHANNELS); + ddepth = cvtest::randomType(rng, cvtest::TYPE_MASK_ALL, 1, 1); + return srctype; + } + int getMaxErr(int) + { + return ddepth <= CV_32S ? 2 : ddepth < CV_64F ? (1 << 14) : (1 << 18); + } + void generateScalars(int depth, RNG& rng) + { + if( rng.uniform(0, 2) ) + BaseElemWiseOp::generateScalars(depth, rng); + else + { + alpha = 1; + gamma = Scalar::all(0); + } + } + int ddepth; +}; + + +struct ConvertScaleAbsOp : public BaseElemWiseOp +{ + ConvertScaleAbsOp() : BaseElemWiseOp(1, FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {}; + void op(const vector& src, Mat& dst, const Mat&) + { + cv::convertScaleAbs(src[0], dst, alpha, gamma[0]); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::add(src[0], alpha, Mat(), 0, Scalar::all(gamma[0]), dst, CV_8UC(src[0].channels()), true); + } + void generateScalars(int depth, RNG& rng) + { + if( rng.uniform(0, 2) ) + BaseElemWiseOp::generateScalars(depth, rng); + else + { + alpha = 1; + gamma = Scalar::all(0); + } + } +}; + + +static void flip(const Mat& src, Mat& dst, int flipcode) +{ + CV_Assert(src.dims == 2); + dst.create(src.size(), src.type()); + int i, j, k, esz = (int)src.elemSize(), width = src.cols*esz; + + for( i = 0; i < dst.rows; i++ ) + { + const uchar* sptr = src.ptr(flipcode == 1 ? i : dst.rows - i - 1); + uchar* dptr = dst.ptr(i); + if( flipcode == 0 ) + memcpy(dptr, sptr, width); + else + { + for( j = 0; j < width; j += esz ) + for( k = 0; k < esz; k++ ) + dptr[j + k] = sptr[width - j - esz + k]; + } + } +} + + +static void setIdentity(Mat& dst, const Scalar& s) +{ + CV_Assert( dst.dims == 2 && dst.channels() <= 4 ); + double buf[4]; + scalarToRawData(s, buf, dst.type(), 0); + int i, k, esz = (int)dst.elemSize(), width = dst.cols*esz; + + for( i = 0; i < dst.rows; i++ ) + { + uchar* dptr = dst.ptr(i); + memset( dptr, 0, width ); + if( i < dst.cols ) + for( k = 0; k < esz; k++ ) + dptr[i*esz + k] = ((uchar*)buf)[k]; + } +} + + +struct FlipOp : public BaseElemWiseOp +{ + FlipOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + void getRandomSize(RNG& rng, vector& size) + { + cvtest::randomSize(rng, 2, 2, cvtest::ARITHM_MAX_SIZE_LOG, size); + } + void op(const vector& src, Mat& dst, const Mat&) + { + cv::flip(src[0], dst, flipcode); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::flip(src[0], dst, flipcode); + } + void generateScalars(int depth, RNG& rng) + { + flipcode = rng.uniform(0, 3) - 1; + } + int getMaxErr(int) + { + return 0; + } + int flipcode; +}; + +struct TransposeOp : public BaseElemWiseOp +{ + TransposeOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + void getRandomSize(RNG& rng, vector& size) + { + cvtest::randomSize(rng, 2, 2, cvtest::ARITHM_MAX_SIZE_LOG, size); + } + void op(const vector& src, Mat& dst, const Mat&) + { + cv::transpose(src[0], dst); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::transpose(src[0], dst); + } + int getMaxErr(int) + { + return 0; + } +}; + +struct SetIdentityOp : public BaseElemWiseOp +{ + SetIdentityOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA, 1, 1, Scalar::all(0)) {}; + void getRandomSize(RNG& rng, vector& size) + { + cvtest::randomSize(rng, 2, 2, cvtest::ARITHM_MAX_SIZE_LOG, size); + } + void op(const vector&, Mat& dst, const Mat&) + { + cv::setIdentity(dst, gamma); + } + void refop(const vector&, Mat& dst, const Mat&) + { + cvtest::setIdentity(dst, gamma); + } + int getMaxErr(int) + { + return 0; + } +}; + +struct SetZeroOp : public BaseElemWiseOp +{ + SetZeroOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + void op(const vector&, Mat& dst, const Mat&) + { + dst = Scalar::all(0); + } + void refop(const vector&, Mat& dst, const Mat&) + { + cvtest::set(dst, Scalar::all(0)); + } + int getMaxErr(int) + { + return 0; + } +}; + + +static void exp(const Mat& src, Mat& dst) +{ + dst.create( src.dims, &src.size[0], src.type() ); + const Mat *arrays[]={&src, &dst, 0}; + Mat planes[2]; + + NAryMatIterator it(arrays, planes); + size_t j, total = planes[0].total()*src.channels(); + int i, nplanes = it.nplanes, depth = src.depth(); + + for( i = 0; i < nplanes; i++, ++it ) + { + const uchar* sptr = planes[0].data; + uchar* dptr = planes[1].data; + + if( depth == CV_32F ) + { + for( j = 0; j < total; j++ ) + ((float*)dptr)[j] = std::exp(((const float*)sptr)[j]); + } + else if( depth == CV_64F ) + { + for( j = 0; j < total; j++ ) + ((double*)dptr)[j] = std::exp(((const double*)sptr)[j]); + } + } +} + +static void log(const Mat& src, Mat& dst) +{ + dst.create( src.dims, &src.size[0], src.type() ); + const Mat *arrays[]={&src, &dst, 0}; + Mat planes[2]; + + NAryMatIterator it(arrays, planes); + size_t j, total = planes[0].total()*src.channels(); + int i, nplanes = it.nplanes, depth = src.depth(); + + for( i = 0; i < nplanes; i++, ++it ) + { + const uchar* sptr = planes[0].data; + uchar* dptr = planes[1].data; + + if( depth == CV_32F ) + { + for( j = 0; j < total; j++ ) + ((float*)dptr)[j] = (float)std::log(fabs(((const float*)sptr)[j])); + } + else if( depth == CV_64F ) + { + for( j = 0; j < total; j++ ) + ((double*)dptr)[j] = std::log(fabs(((const double*)sptr)[j])); + } + } +} + +struct ExpOp : public BaseElemWiseOp +{ + ExpOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + int getRandomType(RNG& rng) + { + return cvtest::randomType(rng, cvtest::TYPE_MASK_FLT, 1, ARITHM_MAX_CHANNELS); + } + void getValueRange(int depth, double& minval, double& maxval) + { + maxval = depth == CV_32F ? 50 : 100; + minval = -maxval; + } + void op(const vector& src, Mat& dst, const Mat&) + { + cv::exp(src[0], dst); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + cvtest::exp(src[0], dst); + } + int getMaxErr(int) + { + return (1<<10); + } +}; + + +struct LogOp : public BaseElemWiseOp +{ + LogOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + int getRandomType(RNG& rng) + { + return cvtest::randomType(rng, cvtest::TYPE_MASK_FLT, 1, ARITHM_MAX_CHANNELS); + } + void getValueRange(int depth, double& minval, double& maxval) + { + maxval = depth == CV_32F ? 50 : 100; + minval = -maxval; + } + void op(const vector& src, Mat& dst, const Mat&) + { + Mat temp; + cvtest::exp(src[0], temp); + cv::log(temp, dst); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + Mat temp; + cvtest::exp(src[0], temp); + cvtest::log(temp, dst); + } + int getMaxErr(int) + { + return (1<<10); + } +}; + + +static void cartToPolar(const Mat& mx, const Mat& my, Mat& mmag, Mat& mangle, bool angleInDegrees) +{ + CV_Assert( (mx.type() == CV_32F || mx.type() == CV_64F) && + mx.type() == my.type() && mx.size == my.size ); + mmag.create( mx.dims, &mx.size[0], mx.type() ); + mangle.create( mx.dims, &mx.size[0], mx.type() ); + const Mat *arrays[]={&mx, &my, &mmag, &mangle, 0}; + Mat planes[4]; + + NAryMatIterator it(arrays, planes); + size_t j, total = planes[0].total(); + int i, nplanes = it.nplanes, depth = mx.depth(); + double scale = angleInDegrees ? 180/CV_PI : 1; + + for( i = 0; i < nplanes; i++, ++it ) + { + if( depth == CV_32F ) + { + const float* xptr = (const float*)planes[0].data; + const float* yptr = (const float*)planes[1].data; + float* mptr = (float*)planes[2].data; + float* aptr = (float*)planes[3].data; + + for( j = 0; j < total; j++ ) + { + mptr[j] = std::sqrt(xptr[j]*xptr[j] + yptr[j]*yptr[j]); + double a = atan2((double)yptr[j], (double)xptr[j]); + if( a < 0 ) a += CV_PI*2; + aptr[j] = (float)(a*scale); + } } else { - cvtest::add(src1, 1, src2, 1, Scalar::all(0), dst0, dst.type()); - add(src1, src2, dst); + const double* xptr = (const double*)planes[0].data; + const double* yptr = (const double*)planes[1].data; + double* mptr = (double*)planes[2].data; + double* aptr = (double*)planes[3].data; + + for( j = 0; j < total; j++ ) + { + mptr[j] = std::sqrt(xptr[j]*xptr[j] + yptr[j]*yptr[j]); + double a = atan2(yptr[j], xptr[j]); + if( a < 0 ) a += CV_PI*2; + aptr[j] = a*scale; + } } - - double maxErr = getArithmMaxErr(depth); - vector pos; - ASSERT_TRUE(cvtest::cmpEps(dst0, dst, maxErr, &pos)) << "position: " << Mat(pos); } } + + +struct CartToPolarToCartOp : public BaseElemWiseOp +{ + CartToPolarToCartOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) + { + context = 3; + angleInDegrees = true; + } + int getRandomType(RNG& rng) + { + return cvtest::randomType(rng, cvtest::TYPE_MASK_FLT, 1, 1); + } + void op(const vector& src, Mat& dst, const Mat&) + { + Mat mag, angle, x, y; + + cv::cartToPolar(src[0], src[1], mag, angle, angleInDegrees); + cv::polarToCart(mag, angle, x, y, angleInDegrees); + + Mat msrc[] = {mag, angle, x, y}; + int pairs[] = {0, 0, 1, 1, 2, 2, 3, 3}; + dst.create(src[0].dims, src[0].size, CV_MAKETYPE(src[0].depth(), 4)); + cv::mixChannels(msrc, 4, &dst, 1, pairs, 4); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + Mat mag, angle; + cvtest::cartToPolar(src[0], src[1], mag, angle, angleInDegrees); + Mat msrc[] = {mag, angle, src[0], src[1]}; + int pairs[] = {0, 0, 1, 1, 2, 2, 3, 3}; + dst.create(src[0].dims, src[0].size, CV_MAKETYPE(src[0].depth(), 4)); + cv::mixChannels(msrc, 4, &dst, 1, pairs, 4); + } + void generateScalars(int, RNG& rng) + { + angleInDegrees = rng.uniform(0, 2) != 0; + } + int getMaxErr(int) + { + return (1<<10); + } + bool angleInDegrees; +}; + + +struct MeanOp : public BaseElemWiseOp +{ + MeanOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK+SCALAR_OUTPUT, 1, 1, Scalar::all(0)) + { + context = 3; + }; + void op(const vector& src, Mat& dst, const Mat& mask) + { + dst.create(1, 1, CV_64FC4); + dst.at(0,0) = cv::mean(src[0], mask); + } + void refop(const vector& src, Mat& dst, const Mat& mask) + { + dst.create(1, 1, CV_64FC4); + dst.at(0,0) = cvtest::mean(src[0], mask); + } + int getMaxErr(int) + { + return (1<<13); + } +}; + + +struct SumOp : public BaseElemWiseOp +{ + SumOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SCALAR_OUTPUT, 1, 1, Scalar::all(0)) + { + context = 3; + }; + void op(const vector& src, Mat& dst, const Mat&) + { + dst.create(1, 1, CV_64FC4); + dst.at(0,0) = cv::sum(src[0]); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + dst.create(1, 1, CV_64FC4); + dst.at(0,0) = cvtest::mean(src[0])*(double)src[0].total(); + } + int getMaxErr(int) + { + return (1<<13); + } +}; + + +struct CountNonZeroOp : public BaseElemWiseOp +{ + CountNonZeroOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SCALAR_OUTPUT+SUPPORT_MASK, 1, 1, Scalar::all(0)) + {} + int getRandomType(RNG& rng) + { + return cvtest::randomType(rng, cvtest::TYPE_MASK_ALL, 1, 1); + } + void op(const vector& src, Mat& dst, const Mat& mask) + { + Mat temp; + src[0].copyTo(temp); + if( !mask.empty() ) + temp.setTo(Scalar::all(0), mask); + dst.create(1, 1, CV_32S); + dst.at(0,0) = cv::countNonZero(temp); + } + void refop(const vector& src, Mat& dst, const Mat& mask) + { + Mat temp; + cvtest::compare(src[0], 0, temp, CMP_NE); + if( !mask.empty() ) + cvtest::set(temp, Scalar::all(0), mask); + dst.create(1, 1, CV_32S); + dst.at(0,0) = saturate_cast(cvtest::mean(temp)[0]/255*temp.total()); + } + int getMaxErr(int) + { + return 0; + } +}; + + +struct MeanStdDevOp : public BaseElemWiseOp +{ + MeanStdDevOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK+SCALAR_OUTPUT, 1, 1, Scalar::all(0)) + { + context = 7; + }; + void op(const vector& src, Mat& dst, const Mat& mask) + { + dst.create(1, 2, CV_64FC4); + cv::meanStdDev(src[0], dst.at(0,0), dst.at(0,1), mask); + } + void refop(const vector& src, Mat& dst, const Mat& mask) + { + Mat temp; + cvtest::convert(src[0], temp, CV_64F); + cvtest::multiply(temp, temp, temp); + Scalar mean = cvtest::mean(src[0], mask); + Scalar sqmean = cvtest::mean(temp, mask); + + for( int c = 0; c < 4; c++ ) + sqmean[c] = std::sqrt(std::max(sqmean[c] - mean[c]*mean[c], 0.)); + + dst.create(1, 2, CV_64FC4); + dst.at(0,0) = mean; + dst.at(0,1) = sqmean; + } + int getMaxErr(int) + { + return (1<<13); + } +}; + + +struct NormOp : public BaseElemWiseOp +{ + NormOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK+SCALAR_OUTPUT, 1, 1, Scalar::all(0)) + { + context = 1; + normType = 0; + }; + int getRandomType(RNG& rng) + { + return cvtest::randomType(rng, cvtest::TYPE_MASK_ALL_BUT_8S, 1, 4); + } + void op(const vector& src, Mat& dst, const Mat& mask) + { + dst.create(1, 2, CV_64FC1); + dst.at(0,0) = cv::norm(src[0], normType, mask); + dst.at(0,1) = cv::norm(src[0], src[1], normType, mask); + } + void refop(const vector& src, Mat& dst, const Mat& mask) + { + dst.create(1, 2, CV_64FC1); + dst.at(0,0) = cvtest::norm(src[0], normType, mask); + dst.at(0,1) = cvtest::norm(src[0], src[1], normType, mask); + } + void generateScalars(int, RNG& rng) + { + normType = 1 << rng.uniform(0, 3); + } + int getMaxErr(int) + { + return (1<<13); + } + int normType; +}; + + +struct MinMaxLocOp : public BaseElemWiseOp +{ + MinMaxLocOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK+SCALAR_OUTPUT, 1, 1, Scalar::all(0)) + { + context = ARITHM_MAX_NDIMS*2 + 2; + }; + int getRandomType(RNG& rng) + { + return cvtest::randomType(rng, cvtest::TYPE_MASK_ALL_BUT_8S, 1, 1); + } + void saveOutput(const vector& minidx, const vector& maxidx, + double minval, double maxval, Mat& dst) + { + size_t i, ndims = minidx.size(); + dst.create(1, (int)(ndims*2 + 2), CV_64FC1); + + for( i = 0; i < ndims; i++ ) + { + dst.at(0,i) = minidx[i]; + dst.at(0,i+ndims) = maxidx[i]; + } + dst.at(0,ndims*2) = minval; + dst.at(0,ndims*2+1) = maxval; + } + void op(const vector& src, Mat& dst, const Mat& mask) + { + int ndims = src[0].dims; + vector minidx(ndims), maxidx(ndims); + double minval=0, maxval=0; + cv::minMaxIdx(src[0], &minval, &maxval, &minidx[0], &maxidx[0], mask); + saveOutput(minidx, maxidx, minval, maxval, dst); + } + void refop(const vector& src, Mat& dst, const Mat& mask) + { + int ndims=src[0].dims; + vector minidx(ndims), maxidx(ndims); + double minval=0, maxval=0; + cvtest::minMaxLoc(src[0], &minval, &maxval, &minidx, &maxidx, mask); + saveOutput(minidx, maxidx, minval, maxval, dst); + } + int getMaxErr(int) + { + return 0; + } +}; + + +} + +typedef Ptr ElemWiseOpPtr; +class ElemWiseTest : public ::testing::TestWithParam {}; + +TEST_P(ElemWiseTest, accuracy) +{ + ElemWiseOpPtr op = GetParam(); + + int testIdx = 0; + RNG rng(cvtest::ARITHM_RNG_SEED); + for( testIdx = 0; testIdx < cvtest::ARITHM_NTESTS; testIdx++ ) + { + vector size; + op->getRandomSize(rng, size); + int type = op->getRandomType(rng); + int depth = CV_MAT_DEPTH(type); + bool haveMask = (op->flags & cvtest::BaseElemWiseOp::SUPPORT_MASK) != 0 && rng.uniform(0, 4) == 0; + + double minval=0, maxval=0; + op->getValueRange(depth, minval, maxval); + int i, ninputs = op->ninputs; + vector src(ninputs); + for( i = 0; i < ninputs; i++ ) + src[i] = cvtest::randomMat(rng, size, type, minval, maxval, true); + Mat dst0, dst, mask; + if( haveMask ) + mask = cvtest::randomMat(rng, size, CV_8U, 0, 2, true); + + if( (haveMask || ninputs == 0) && !(op->flags & cvtest::BaseElemWiseOp::SCALAR_OUTPUT)) + { + dst0 = cvtest::randomMat(rng, size, type, minval, maxval, false); + dst = cvtest::randomMat(rng, size, type, minval, maxval, true); + cvtest::copy(dst, dst0); + } + op->generateScalars(depth, rng); + + op->refop(src, dst0, mask); + op->op(src, dst, mask); + + double maxErr = op->getMaxErr(depth); + vector pos; + ASSERT_PRED_FORMAT2(cvtest::MatComparator(maxErr, op->context), dst0, dst) << "\nsrc[0] ~ " << cvtest::MatInfo(!src.empty() ? src[0] : Mat()) << "\ntestCase #" << testIdx << "\n"; + } +} + + +INSTANTIATE_TEST_CASE_P(Core_Copy, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::CopyOp))); +INSTANTIATE_TEST_CASE_P(Core_Set, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::SetOp))); +INSTANTIATE_TEST_CASE_P(Core_SetZero, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::SetZeroOp))); +INSTANTIATE_TEST_CASE_P(Core_ConvertScale, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::ConvertScaleOp))); +INSTANTIATE_TEST_CASE_P(Core_ConvertScaleAbs, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::ConvertScaleAbsOp))); + +INSTANTIATE_TEST_CASE_P(Core_Add, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::AddOp))); +INSTANTIATE_TEST_CASE_P(Core_Sub, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::SubOp))); +INSTANTIATE_TEST_CASE_P(Core_AddS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::AddSOp))); +INSTANTIATE_TEST_CASE_P(Core_SubRS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::SubRSOp))); +INSTANTIATE_TEST_CASE_P(Core_ScaleAdd, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::ScaleAddOp))); +INSTANTIATE_TEST_CASE_P(Core_AddWeighted, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::AddWeightedOp))); +INSTANTIATE_TEST_CASE_P(Core_AbsDiff, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::AbsDiffOp))); +INSTANTIATE_TEST_CASE_P(Core_AbsDiffS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::AbsDiffSOp))); + +INSTANTIATE_TEST_CASE_P(Core_And, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicOp('&')))); +INSTANTIATE_TEST_CASE_P(Core_AndS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicSOp('&')))); +INSTANTIATE_TEST_CASE_P(Core_Or, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicOp('|')))); +INSTANTIATE_TEST_CASE_P(Core_OrS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicSOp('|')))); +INSTANTIATE_TEST_CASE_P(Core_Xor, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicOp('^')))); +INSTANTIATE_TEST_CASE_P(Core_XorS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicSOp('^')))); +INSTANTIATE_TEST_CASE_P(Core_Not, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicSOp('~')))); + +INSTANTIATE_TEST_CASE_P(Core_Max, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MaxOp))); +INSTANTIATE_TEST_CASE_P(Core_MaxS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MaxSOp))); +INSTANTIATE_TEST_CASE_P(Core_Min, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MinOp))); +INSTANTIATE_TEST_CASE_P(Core_MinS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MinSOp))); + +INSTANTIATE_TEST_CASE_P(Core_Mul, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MulOp))); +INSTANTIATE_TEST_CASE_P(Core_Div, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::DivOp))); +INSTANTIATE_TEST_CASE_P(Core_Recip, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::RecipOp))); + +INSTANTIATE_TEST_CASE_P(Core_Cmp, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::CmpOp))); +INSTANTIATE_TEST_CASE_P(Core_CmpS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::CmpSOp))); + +INSTANTIATE_TEST_CASE_P(Core_InRangeS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::InRangeSOp))); +INSTANTIATE_TEST_CASE_P(Core_InRange, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::InRangeOp))); + +INSTANTIATE_TEST_CASE_P(Core_Flip, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::FlipOp))); +INSTANTIATE_TEST_CASE_P(Core_Transpose, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::TransposeOp))); +INSTANTIATE_TEST_CASE_P(Core_SetIdentity, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::SetIdentityOp))); + +INSTANTIATE_TEST_CASE_P(Core_Exp, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::ExpOp))); +INSTANTIATE_TEST_CASE_P(Core_Log, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogOp))); + +INSTANTIATE_TEST_CASE_P(Core_CountNonZero, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::CountNonZeroOp))); +INSTANTIATE_TEST_CASE_P(Core_Mean, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MeanOp))); +INSTANTIATE_TEST_CASE_P(Core_MeanStdDev, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MeanStdDevOp))); +INSTANTIATE_TEST_CASE_P(Core_Sum, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::SumOp))); +INSTANTIATE_TEST_CASE_P(Core_Norm, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::NormOp))); +INSTANTIATE_TEST_CASE_P(Core_MinMaxLoc, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MinMaxLocOp))); + +INSTANTIATE_TEST_CASE_P(Core_CartToPolarToCart, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::CartToPolarToCartOp))); diff --git a/modules/core/test/test_ds.cpp b/modules/core/test/test_ds.cpp new file mode 100644 index 000000000..f19c6bb9d --- /dev/null +++ b/modules/core/test/test_ds.cpp @@ -0,0 +1,2122 @@ +#include "test_precomp.hpp" + +using namespace cv; +using namespace std; + +typedef struct CvTsSimpleSeq +{ + schar* array; + int count; + int max_count; + int elem_size; +} CvTsSimpleSeq; + + +static CvTsSimpleSeq* cvTsCreateSimpleSeq( int max_count, int elem_size ) +{ + CvTsSimpleSeq* seq = (CvTsSimpleSeq*)cvAlloc( sizeof(*seq) + max_count * elem_size ); + seq->elem_size = elem_size; + seq->max_count = max_count; + seq->count = 0; + seq->array = (schar*)(seq + 1); + return seq; +} + + +static void cvTsReleaseSimpleSeq( CvTsSimpleSeq** seq ) +{ + cvFree( seq ); +} + + +static schar* cvTsSimpleSeqElem( CvTsSimpleSeq* seq, int index ) +{ + assert( 0 <= index && index < seq->count ); + return seq->array + index * seq->elem_size; +} + + +static void cvTsClearSimpleSeq( CvTsSimpleSeq* seq ) +{ + seq->count = 0; +} + + +static void cvTsSimpleSeqShiftAndCopy( CvTsSimpleSeq* seq, int from_idx, int to_idx, void* elem=0 ) +{ + int elem_size = seq->elem_size; + + if( from_idx == to_idx ) + return; + assert( (from_idx > to_idx && !elem) || (from_idx < to_idx && elem) ); + + if( from_idx < seq->count ) + { + memmove( seq->array + to_idx*elem_size, seq->array + from_idx*elem_size, + (seq->count - from_idx)*elem_size ); + } + seq->count += to_idx - from_idx; + if( elem && to_idx > from_idx ) + memcpy( seq->array + from_idx*elem_size, elem, (to_idx - from_idx)*elem_size ); +} + +static void cvTsSimpleSeqInvert( CvTsSimpleSeq* seq ) +{ + int i, k, len = seq->count, elem_size = seq->elem_size; + schar *data = seq->array, t; + + for( i = 0; i < len/2; i++ ) + { + schar* a = data + i*elem_size; + schar* b = data + (len - i - 1)*elem_size; + for( k = 0; k < elem_size; k++ ) + CV_SWAP( a[k], b[k], t ); + } +} + +/****************************************************************************************\ + * simple cvset implementation * + \****************************************************************************************/ + +typedef struct CvTsSimpleSet +{ + schar* array; + int count, max_count; + int elem_size; + int* free_stack; + int free_count; +} CvTsSimpleSet; + + +static void cvTsClearSimpleSet( CvTsSimpleSet* set_header ) +{ + int i; + int elem_size = set_header->elem_size; + + for( i = 0; i < set_header->max_count; i++ ) + { + set_header->array[i*elem_size] = 0; + set_header->free_stack[i] = set_header->max_count - i - 1; + } + set_header->free_count = set_header->max_count; + set_header->count = 0; +} + + +static CvTsSimpleSet* cvTsCreateSimpleSet( int max_count, int elem_size ) +{ + CvTsSimpleSet* set_header = (CvTsSimpleSet*)cvAlloc( sizeof(*set_header) + max_count * + (elem_size + 1 + sizeof(int))); + set_header->elem_size = elem_size + 1; + set_header->max_count = max_count; + set_header->free_stack = (int*)(set_header + 1); + set_header->array = (schar*)(set_header->free_stack + max_count); + + cvTsClearSimpleSet( set_header ); + return set_header; +} + + +static void cvTsReleaseSimpleSet( CvTsSimpleSet** set_header ) +{ + cvFree( set_header ); +} + + +static schar* cvTsSimpleSetFind( CvTsSimpleSet* set_header, int index ) +{ + int idx = index * set_header->elem_size; + assert( 0 <= index && index < set_header->max_count ); + return set_header->array[idx] ? set_header->array + idx + 1 : 0; +} + + +static int cvTsSimpleSetAdd( CvTsSimpleSet* set_header, void* elem ) +{ + int idx, idx2; + assert( set_header->free_count > 0 ); + + idx = set_header->free_stack[--set_header->free_count]; + idx2 = idx * set_header->elem_size; + assert( set_header->array[idx2] == 0 ); + set_header->array[idx2] = 1; + if( set_header->elem_size > 1 ) + memcpy( set_header->array + idx2 + 1, elem, set_header->elem_size - 1 ); + set_header->count = MAX( set_header->count, idx + 1 ); + + return idx; +} + + +static void cvTsSimpleSetRemove( CvTsSimpleSet* set_header, int index ) +{ + assert( set_header->free_count < set_header->max_count && + 0 <= index && index < set_header->max_count ); + assert( set_header->array[index * set_header->elem_size] == 1 ); + + set_header->free_stack[set_header->free_count++] = index; + set_header->array[index * set_header->elem_size] = 0; +} + + +/****************************************************************************************\ + * simple graph implementation * + \****************************************************************************************/ + +typedef struct CvTsSimpleGraph +{ + char* matrix; + int edge_size; + int oriented; + CvTsSimpleSet* vtx; +} CvTsSimpleGraph; + + +static void cvTsClearSimpleGraph( CvTsSimpleGraph* graph ) +{ + int max_vtx_count = graph->vtx->max_count; + cvTsClearSimpleSet( graph->vtx ); + memset( graph->matrix, 0, max_vtx_count * max_vtx_count * graph->edge_size ); +} + + +static CvTsSimpleGraph* cvTsCreateSimpleGraph( int max_vtx_count, int vtx_size, + int edge_size, int oriented ) +{ + CvTsSimpleGraph* graph; + + assert( max_vtx_count > 1 && vtx_size >= 0 && edge_size >= 0 ); + graph = (CvTsSimpleGraph*)cvAlloc( sizeof(*graph) + + max_vtx_count * max_vtx_count * (edge_size + 1)); + graph->vtx = cvTsCreateSimpleSet( max_vtx_count, vtx_size ); + graph->edge_size = edge_size + 1; + graph->matrix = (char*)(graph + 1); + graph->oriented = oriented; + + cvTsClearSimpleGraph( graph ); + return graph; +} + + +static void cvTsReleaseSimpleGraph( CvTsSimpleGraph** graph ) +{ + if( *graph ) + { + cvTsReleaseSimpleSet( &(graph[0]->vtx) ); + cvFree( graph ); + } +} + + +static int cvTsSimpleGraphAddVertex( CvTsSimpleGraph* graph, void* vertex ) +{ + return cvTsSimpleSetAdd( graph->vtx, vertex ); +} + + +static void cvTsSimpleGraphRemoveVertex( CvTsSimpleGraph* graph, int index ) +{ + int i, max_vtx_count = graph->vtx->max_count; + int edge_size = graph->edge_size; + cvTsSimpleSetRemove( graph->vtx, index ); + + /* remove all the corresponding edges */ + for( i = 0; i < max_vtx_count; i++ ) + { + graph->matrix[(i*max_vtx_count + index)*edge_size] = + graph->matrix[(index*max_vtx_count + i)*edge_size] = 0; + } +} + + +static void cvTsSimpleGraphAddEdge( CvTsSimpleGraph* graph, int idx1, int idx2, void* edge ) +{ + int i, t, n = graph->oriented ? 1 : 2; + + assert( cvTsSimpleSetFind( graph->vtx, idx1 ) && + cvTsSimpleSetFind( graph->vtx, idx2 )); + + for( i = 0; i < n; i++ ) + { + int ofs = (idx1*graph->vtx->max_count + idx2)*graph->edge_size; + assert( graph->matrix[ofs] == 0 ); + graph->matrix[ofs] = 1; + if( graph->edge_size > 1 ) + memcpy( graph->matrix + ofs + 1, edge, graph->edge_size - 1 ); + + CV_SWAP( idx1, idx2, t ); + } +} + + +static void cvTsSimpleGraphRemoveEdge( CvTsSimpleGraph* graph, int idx1, int idx2 ) +{ + int i, t, n = graph->oriented ? 1 : 2; + + assert( cvTsSimpleSetFind( graph->vtx, idx1 ) && + cvTsSimpleSetFind( graph->vtx, idx2 )); + + for( i = 0; i < n; i++ ) + { + int ofs = (idx1*graph->vtx->max_count + idx2)*graph->edge_size; + assert( graph->matrix[ofs] == 1 ); + graph->matrix[ofs] = 0; + CV_SWAP( idx1, idx2, t ); + } +} + + +static schar* cvTsSimpleGraphFindVertex( CvTsSimpleGraph* graph, int index ) +{ + return cvTsSimpleSetFind( graph->vtx, index ); +} + + +static char* cvTsSimpleGraphFindEdge( CvTsSimpleGraph* graph, int idx1, int idx2 ) +{ + if( cvTsSimpleGraphFindVertex( graph, idx1 ) && + cvTsSimpleGraphFindVertex( graph, idx2 )) + { + char* edge = graph->matrix + (idx1 * graph->vtx->max_count + idx2)*graph->edge_size; + if( edge[0] ) return edge + 1; + } + return 0; +} + + +static int cvTsSimpleGraphVertexDegree( CvTsSimpleGraph* graph, int index ) +{ + int i, count = 0; + int edge_size = graph->edge_size; + int max_vtx_count = graph->vtx->max_count; + assert( cvTsSimpleGraphFindVertex( graph, index ) != 0 ); + + for( i = 0; i < max_vtx_count; i++ ) + { + count += graph->matrix[(i*max_vtx_count + index)*edge_size] + + graph->matrix[(index*max_vtx_count + i)*edge_size]; + } + + if( !graph->oriented ) + { + assert( count % 2 == 0 ); + count /= 2; + } + return count; +} + + +///////////////////////////////////// the tests ////////////////////////////////// + +#define CV_TS_SEQ_CHECK_CONDITION( expr, err_msg ) \ +if( !(expr) ) \ +{ \ +set_error_context( #expr, err_msg, __FILE__, __LINE__ ); \ +ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );\ +throw -1; \ +} + +class Core_DynStructBaseTest : public cvtest::BaseTest +{ +public: + Core_DynStructBaseTest(); + virtual ~Core_DynStructBaseTest(); + bool can_do_fast_forward(); + void clear(); + +protected: + int read_params( CvFileStorage* fs ); + void run_func(void); + void set_error_context( const char* condition, + const char* err_msg, + const char* file, int line ); + int test_seq_block_consistence( int _struct_idx, CvSeq* seq, int total ); + void update_progressbar(); + + int struct_count, max_struct_size, iterations, generations; + int min_log_storage_block_size, max_log_storage_block_size; + int min_log_elem_size, max_log_elem_size; + int gen, struct_idx, iter; + int test_progress; + int64 start_time; + double cpu_freq; + vector cxcore_struct; + vector simple_struct; + Ptr storage; +}; + + +Core_DynStructBaseTest::Core_DynStructBaseTest() +{ + struct_count = 2; + max_struct_size = 2000; + min_log_storage_block_size = 7; + max_log_storage_block_size = 12; + min_log_elem_size = 0; + max_log_elem_size = 8; + generations = 10; + iterations = max_struct_size*2; + gen = struct_idx = iter = -1; + test_progress = -1; + + storage = 0; +} + + +Core_DynStructBaseTest::~Core_DynStructBaseTest() +{ + clear(); +} + + +void Core_DynStructBaseTest::run_func() +{ +} + +bool Core_DynStructBaseTest::can_do_fast_forward() +{ + return false; +} + + +void Core_DynStructBaseTest::clear() +{ + cvtest::BaseTest::clear(); +} + + +int Core_DynStructBaseTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::BaseTest::read_params( fs ); + double sqrt_scale = sqrt(ts->get_test_case_count_scale()); + if( code < 0 ) + return code; + + struct_count = cvReadInt( find_param( fs, "struct_count" ), struct_count ); + max_struct_size = cvReadInt( find_param( fs, "max_struct_size" ), max_struct_size ); + generations = cvReadInt( find_param( fs, "generations" ), generations ); + iterations = cvReadInt( find_param( fs, "iterations" ), iterations ); + generations = cvRound(generations*sqrt_scale); + iterations = cvRound(iterations*sqrt_scale); + + min_log_storage_block_size = cvReadInt( find_param( fs, "min_log_storage_block_size" ), + min_log_storage_block_size ); + max_log_storage_block_size = cvReadInt( find_param( fs, "max_log_storage_block_size" ), + max_log_storage_block_size ); + min_log_elem_size = cvReadInt( find_param( fs, "min_log_elem_size" ), min_log_elem_size ); + max_log_elem_size = cvReadInt( find_param( fs, "max_log_elem_size" ), max_log_elem_size ); + + struct_count = cvtest::clipInt( struct_count, 1, 100 ); + max_struct_size = cvtest::clipInt( max_struct_size, 1, 1<<20 ); + generations = cvtest::clipInt( generations, 1, 100 ); + iterations = cvtest::clipInt( iterations, 100, 1<<20 ); + + min_log_storage_block_size = cvtest::clipInt( min_log_storage_block_size, 7, 20 ); + max_log_storage_block_size = cvtest::clipInt( max_log_storage_block_size, + min_log_storage_block_size, 20 ); + + min_log_elem_size = cvtest::clipInt( min_log_elem_size, 0, 8 ); + max_log_elem_size = cvtest::clipInt( max_log_elem_size, min_log_elem_size, 10 ); + + return 0; +} + + +void Core_DynStructBaseTest::update_progressbar() +{ + int64 t; + + if( test_progress < 0 ) + { + test_progress = 0; + cpu_freq = cv::getTickFrequency(); + start_time = cv::getTickCount(); + } + + t = cv::getTickCount(); + test_progress = update_progress( test_progress, 0, 0, (double)(t - start_time)/cpu_freq ); +} + + +void Core_DynStructBaseTest::set_error_context( const char* condition, + const char* err_msg, + const char* filename, int lineno ) +{ + ts->printf( cvtest::TS::LOG, "file %s, line %d: %s\n(\"%s\" failed).\n" + "generation = %d, struct_idx = %d, iter = %d\n", + filename, lineno, err_msg, condition, gen, struct_idx, iter ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); +} + + +int Core_DynStructBaseTest::test_seq_block_consistence( int _struct_idx, CvSeq* seq, int total ) +{ + int sum = 0; + struct_idx = _struct_idx; + + CV_TS_SEQ_CHECK_CONDITION( seq != 0, "Null sequence pointer" ); + + if( seq->first ) + { + CvSeqBlock* block = seq->first; + CvSeqBlock* prev_block = block->prev; + + int delta_idx = seq->first->start_index; + + for( ;; ) + { + CV_TS_SEQ_CHECK_CONDITION( sum == block->start_index - delta_idx && + block->count > 0 && block->prev == prev_block && + prev_block->next == block, + "sequence blocks are inconsistent" ); + sum += block->count; + prev_block = block; + block = block->next; + if( block == seq->first ) break; + } + + CV_TS_SEQ_CHECK_CONDITION( block->prev->count * seq->elem_size + + block->prev->data <= seq->block_max, + "block->data or block_max pointer are incorrect" ); + } + + CV_TS_SEQ_CHECK_CONDITION( seq->total == sum && sum == total, + "total number of elements is incorrect" ); + + return 0; +} + + +/////////////////////////////////// sequence tests //////////////////////////////////// + +class Core_SeqBaseTest : public Core_DynStructBaseTest +{ +public: + Core_SeqBaseTest(); + void clear(); + void run( int ); + +protected: + int test_multi_create(); + int test_get_seq_elem( int _struct_idx, int iters ); + int test_get_seq_reading( int _struct_idx, int iters ); + int test_seq_ops( int iters ); +}; + + +Core_SeqBaseTest::Core_SeqBaseTest() +{ +} + + +void Core_SeqBaseTest::clear() +{ + for( size_t i = 0; i < simple_struct.size(); i++ ) + cvTsReleaseSimpleSeq( (CvTsSimpleSeq**)&simple_struct[i] ); + Core_DynStructBaseTest::clear(); +} + + +int Core_SeqBaseTest::test_multi_create() +{ + vector writer(struct_count); + vector pos(struct_count); + vector index(struct_count); + int cur_count, elem_size; + RNG& rng = ts->get_rng(); + + for( int i = 0; i < struct_count; i++ ) + { + double t; + CvTsSimpleSeq* sseq; + + pos[i] = -1; + index[i] = i; + + t = cvtest::randReal(rng)*(max_log_elem_size - min_log_elem_size) + min_log_elem_size; + elem_size = cvRound( exp(t * CV_LOG2) ); + elem_size = MIN( elem_size, (int)(storage->block_size - sizeof(void*) - + sizeof(CvSeqBlock) - sizeof(CvMemBlock)) ); + + cvTsReleaseSimpleSeq( (CvTsSimpleSeq**)&simple_struct[i] ); + simple_struct[i] = sseq = cvTsCreateSimpleSeq( max_struct_size, elem_size ); + cxcore_struct[i] = 0; + sseq->count = cvtest::randInt( rng ) % max_struct_size; + Mat m( 1, MAX(sseq->count,1)*elem_size, CV_8UC1, sseq->array ); + cvtest::randUni( rng, m, Scalar::all(0), Scalar::all(256) ); + } + + for( cur_count = struct_count; cur_count > 0; cur_count-- ) + { + for(;;) + { + int k = cvtest::randInt( rng ) % cur_count; + struct_idx = index[k]; + CvTsSimpleSeq* sseq = (CvTsSimpleSeq*)simple_struct[struct_idx]; + + if( pos[struct_idx] < 0 ) + { + int hdr_size = (cvtest::randInt(rng) % 10)*4 + sizeof(CvSeq); + hdr_size = MIN( hdr_size, (int)(storage->block_size - sizeof(CvMemBlock)) ); + elem_size = sseq->elem_size; + + if( cvtest::randInt(rng) % 2 ) + { + cvStartWriteSeq( 0, hdr_size, elem_size, storage, &writer[struct_idx] ); + } + else + { + CvSeq* s; + s = cvCreateSeq( 0, hdr_size, elem_size, storage ); + cvStartAppendToSeq( s, &writer[struct_idx] ); + } + + cvSetSeqBlockSize( writer[struct_idx].seq, cvtest::randInt( rng ) % 10000 ); + pos[struct_idx] = 0; + } + + update_progressbar(); + if( pos[struct_idx] == sseq->count ) + { + cxcore_struct[struct_idx] = cvEndWriteSeq( &writer[struct_idx] ); + /* del index */ + for( ; k < cur_count-1; k++ ) + index[k] = index[k+1]; + break; + } + + { + schar* el = cvTsSimpleSeqElem( sseq, pos[struct_idx] ); + CV_WRITE_SEQ_ELEM_VAR( el, writer[struct_idx] ); + } + pos[struct_idx]++; + } + } + + return 0; +} + + +int Core_SeqBaseTest::test_get_seq_elem( int _struct_idx, int iters ) +{ + RNG& rng = ts->get_rng(); + + CvSeq* seq = (CvSeq*)cxcore_struct[_struct_idx]; + CvTsSimpleSeq* sseq = (CvTsSimpleSeq*)simple_struct[_struct_idx]; + struct_idx = _struct_idx; + + assert( seq->total == sseq->count ); + + if( sseq->count == 0 ) + return 0; + + for( int i = 0; i < iters; i++ ) + { + int idx = cvtest::randInt(rng) % (sseq->count*3) - sseq->count*3/2; + int idx0 = (unsigned)idx < (unsigned)(sseq->count) ? idx : idx < 0 ? + idx + sseq->count : idx - sseq->count; + int bad_range = (unsigned)idx0 >= (unsigned)(sseq->count); + schar* elem; + elem = cvGetSeqElem( seq, idx ); + + if( bad_range ) + { + CV_TS_SEQ_CHECK_CONDITION( elem == 0, + "cvGetSeqElem doesn't " + "handle \"out of range\" properly" ); + } + else + { + CV_TS_SEQ_CHECK_CONDITION( elem != 0 && + !memcmp( elem, cvTsSimpleSeqElem(sseq, idx0), sseq->elem_size ), + "cvGetSeqElem returns wrong element" ); + + idx = cvSeqElemIdx(seq, elem ); + CV_TS_SEQ_CHECK_CONDITION( idx >= 0 && idx == idx0, + "cvSeqElemIdx is incorrect" ); + } + } + + return 0; +} + + +int Core_SeqBaseTest::test_get_seq_reading( int _struct_idx, int iters ) +{ + const int max_val = 3*5 + 2; + CvSeq* seq = (CvSeq*)cxcore_struct[_struct_idx]; + CvTsSimpleSeq* sseq = (CvTsSimpleSeq*)simple_struct[_struct_idx]; + int total = seq->total; + RNG& rng = ts->get_rng(); + CvSeqReader reader; + vector _elem(sseq->elem_size); + schar* elem = &_elem[0]; + + assert( total == sseq->count ); + this->struct_idx = _struct_idx; + + int pos = cvtest::randInt(rng) % 2; + cvStartReadSeq( seq, &reader, pos ); + + if( total == 0 ) + { + CV_TS_SEQ_CHECK_CONDITION( reader.ptr == 0, "Empty sequence reader pointer is not NULL" ); + return 0; + } + + pos = pos ? seq->total - 1 : 0; + + CV_TS_SEQ_CHECK_CONDITION( pos == cvGetSeqReaderPos(&reader), + "initial reader position is wrong" ); + + for( iter = 0; iter < iters; iter++ ) + { + int op = cvtest::randInt(rng) % max_val; + + if( op >= max_val - 2 ) + { + int new_pos, new_pos0; + int bad_range; + int is_relative = op == max_val - 1; + + new_pos = cvtest::randInt(rng) % (total*2) - total; + new_pos0 = new_pos + (is_relative ? pos : 0 ); + + if( new_pos0 < 0 ) new_pos0 += total; + if( new_pos0 >= total ) new_pos0 -= total; + + bad_range = (unsigned)new_pos0 >= (unsigned)total; + cvSetSeqReaderPos( &reader, new_pos, is_relative ); + + if( !bad_range ) + { + CV_TS_SEQ_CHECK_CONDITION( new_pos0 == cvGetSeqReaderPos( &reader ), + "cvset reader position doesn't work" ); + pos = new_pos0; + } + else + { + CV_TS_SEQ_CHECK_CONDITION( pos == cvGetSeqReaderPos( &reader ), + "reader doesn't stay at the current position after wrong positioning" ); + } + } + else + { + int direction = (op % 3) - 1; + memcpy( elem, reader.ptr, sseq->elem_size ); + + if( direction > 0 ) + { + CV_NEXT_SEQ_ELEM( sseq->elem_size, reader ); + } + else if( direction < 0 ) + { + CV_PREV_SEQ_ELEM( sseq->elem_size, reader ); + } + + CV_TS_SEQ_CHECK_CONDITION( memcmp(elem, cvTsSimpleSeqElem(sseq, pos), + sseq->elem_size) == 0, "reading is incorrect" ); + pos += direction; + if( pos < 0 ) pos += total; + if( pos >= total ) pos -= total; + + CV_TS_SEQ_CHECK_CONDITION( pos == cvGetSeqReaderPos( &reader ), + "reader doesn't move correctly after reading" ); + } + } + + return 0; +} + + +int Core_SeqBaseTest::test_seq_ops( int iters ) +{ + const int max_op = 14; + int max_elem_size = 0; + schar* elem2 = 0; + RNG& rng = ts->get_rng(); + + for( int i = 0; i < struct_count; i++ ) + max_elem_size = MAX( max_elem_size, ((CvSeq*)cxcore_struct[i])->elem_size ); + + vector elem_buf(max_struct_size*max_elem_size); + schar* elem = (schar*)&elem_buf[0]; + Mat elem_mat; + + for( iter = 0; iter < iters; iter++ ) + { + struct_idx = cvtest::randInt(rng) % struct_count; + int op = cvtest::randInt(rng) % max_op; + CvSeq* seq = (CvSeq*)cxcore_struct[struct_idx]; + CvTsSimpleSeq* sseq = (CvTsSimpleSeq*)simple_struct[struct_idx]; + int elem_size = sseq->elem_size; + int whence = 0, pos = 0, count = 0; + + switch( op ) + { + case 0: + case 1: + case 2: // push/pushfront/insert + if( sseq->count == sseq->max_count ) + break; + + elem_mat = Mat(1, elem_size, CV_8U, elem); + cvtest::randUni( rng, elem_mat, cvScalarAll(0), cvScalarAll(255) ); + + whence = op - 1; + if( whence < 0 ) + { + pos = 0; + cvSeqPushFront( seq, elem ); + } + else if( whence > 0 ) + { + pos = sseq->count; + cvSeqPush( seq, elem ); + } + else + { + pos = cvtest::randInt(rng) % (sseq->count + 1); + cvSeqInsert( seq, pos, elem ); + } + + cvTsSimpleSeqShiftAndCopy( sseq, pos, pos + 1, elem ); + elem2 = cvGetSeqElem( seq, pos ); + CV_TS_SEQ_CHECK_CONDITION( elem2 != 0, "The inserted element could not be retrieved" ); + CV_TS_SEQ_CHECK_CONDITION( seq->total == sseq->count && + memcmp(elem2, cvTsSimpleSeqElem(sseq,pos), elem_size) == 0, + "The inserted sequence element is wrong" ); + break; + + case 3: + case 4: + case 5: // pop/popfront/remove + if( sseq->count == 0 ) + break; + + whence = op - 4; + if( whence < 0 ) + { + pos = 0; + cvSeqPopFront( seq, elem ); + } + else if( whence > 0 ) + { + pos = sseq->count-1; + cvSeqPop( seq, elem ); + } + else + { + pos = cvtest::randInt(rng) % sseq->count; + cvSeqRemove( seq, pos ); + } + + if( whence != 0 ) + CV_TS_SEQ_CHECK_CONDITION( seq->total == sseq->count - 1 && + memcmp( elem, cvTsSimpleSeqElem(sseq,pos), elem_size) == 0, + "The popped sequence element isn't correct" ); + + cvTsSimpleSeqShiftAndCopy( sseq, pos + 1, pos ); + + if( sseq->count > 0 ) + { + elem2 = cvGetSeqElem( seq, pos < sseq->count ? pos : -1 ); + CV_TS_SEQ_CHECK_CONDITION( elem2 != 0, "GetSeqElem fails after removing the element" ); + + CV_TS_SEQ_CHECK_CONDITION( memcmp( elem2, + cvTsSimpleSeqElem(sseq, pos - (pos == sseq->count)), elem_size) == 0, + "The first shifted element is not correct after removing another element" ); + } + else + { + CV_TS_SEQ_CHECK_CONDITION( seq->first == 0, + "The sequence doesn't become empty after the final remove" ); + } + break; + + case 6: + case 7: + case 8: // push [front] multi/insert slice + if( sseq->count == sseq->max_count ) + break; + + count = cvtest::randInt( rng ) % (sseq->max_count - sseq->count + 1); + elem_mat = Mat(1, MAX(count,1) * elem_size, CV_8U, elem); + cvtest::randUni( rng, elem_mat, cvScalarAll(0), cvScalarAll(255) ); + + whence = op - 7; + pos = whence < 0 ? 0 : whence > 0 ? sseq->count : cvtest::randInt(rng) % (sseq->count+1); + if( whence != 0 ) + { + cvSeqPushMulti( seq, elem, count, whence < 0 ); + } + else + { + CvSeq header; + CvSeqBlock block; + cvMakeSeqHeaderForArray( CV_SEQ_KIND_GENERIC, sizeof(CvSeq), + sseq->elem_size, + elem, count, + &header, &block ); + + cvSeqInsertSlice( seq, pos, &header ); + } + cvTsSimpleSeqShiftAndCopy( sseq, pos, pos + count, elem ); + + if( sseq->count > 0 ) + { + // choose the random element among the added + pos = count > 0 ? cvtest::randInt(rng) % count + pos : MAX(pos-1,0); + elem2 = cvGetSeqElem( seq, pos ); + CV_TS_SEQ_CHECK_CONDITION( elem2 != 0, "multi push operation doesn't add elements" ); + CV_TS_SEQ_CHECK_CONDITION( seq->total == sseq->count && + memcmp( elem2, cvTsSimpleSeqElem(sseq,pos), elem_size) == 0, + "One of the added elements is wrong" ); + } + else + { + CV_TS_SEQ_CHECK_CONDITION( seq->total == 0 && seq->first == 0, + "Adding no elements to empty sequence fails" ); + } + break; + + case 9: + case 10: + case 11: // pop [front] multi + if( sseq->count == 0 ) + break; + + count = cvtest::randInt(rng) % (sseq->count+1); + whence = op - 10; + pos = whence < 0 ? 0 : whence > 0 ? sseq->count - count : + cvtest::randInt(rng) % (sseq->count - count + 1); + + if( whence != 0 ) + { + cvSeqPopMulti( seq, elem, count, whence < 0 ); + + if( count > 0 ) + { + CV_TS_SEQ_CHECK_CONDITION( memcmp(elem, + cvTsSimpleSeqElem(sseq,pos), elem_size) == 0, + "The first (in the sequence order) removed element is wrong after popmulti" ); + } + } + else + { + cvSeqRemoveSlice( seq, cvSlice(pos, pos + count) ); + } + + CV_TS_SEQ_CHECK_CONDITION( seq->total == sseq->count - count, + "The popmulti left a wrong number of elements in the sequence" ); + + cvTsSimpleSeqShiftAndCopy( sseq, pos + count, pos, 0 ); + if( sseq->count > 0 ) + { + pos = whence < 0 ? 0 : MIN( pos, sseq->count - 1 ); + elem2 = cvGetSeqElem( seq, pos ); + CV_TS_SEQ_CHECK_CONDITION( elem2 && + memcmp( elem2, cvTsSimpleSeqElem(sseq,pos), elem_size) == 0, + "The last sequence element is wrong after POP" ); + } + else + { + CV_TS_SEQ_CHECK_CONDITION( seq->total == 0 && seq->first == 0, + "The sequence doesn't become empty after final POP" ); + } + break; + case 12: // seqslice + { + CvMemStoragePos storage_pos; + cvSaveMemStoragePos( storage, &storage_pos ); + + int copy_data = cvtest::randInt(rng) % 2; + count = cvtest::randInt(rng) % (seq->total + 1); + pos = cvtest::randInt(rng) % (seq->total - count + 1); + CvSeq* seq_slice = cvSeqSlice( seq, cvSlice(pos, pos + count), storage, copy_data ); + + CV_TS_SEQ_CHECK_CONDITION( seq_slice && seq_slice->total == count, + "cvSeqSlice returned incorrect slice" ); + + if( count > 0 ) + { + int test_idx = cvtest::randInt(rng) % count; + elem2 = cvGetSeqElem( seq_slice, test_idx ); + schar* elem3 = cvGetSeqElem( seq, pos + test_idx ); + CV_TS_SEQ_CHECK_CONDITION( elem2 && + memcmp( elem2, cvTsSimpleSeqElem(sseq,pos + test_idx), elem_size) == 0, + "The extracted slice elements are not correct" ); + CV_TS_SEQ_CHECK_CONDITION( (elem2 == elem3) ^ copy_data, + "copy_data flag is handled incorrectly" ); + } + + cvRestoreMemStoragePos( storage, &storage_pos ); + } + break; + case 13: // clear + cvTsClearSimpleSeq( sseq ); + cvClearSeq( seq ); + CV_TS_SEQ_CHECK_CONDITION( seq->total == 0 && seq->first == 0, + "The sequence doesn't become empty after clear" ); + break; + default: + assert(0); + return -1; + } + + if( test_seq_block_consistence(struct_idx, seq, sseq->count) < 0 ) + return -1; + + if( test_get_seq_elem(struct_idx, 7) < 0 ) + return -1; + + update_progressbar(); + } + + return 0; +} + + +void Core_SeqBaseTest::run( int ) +{ + try + { + RNG& rng = ts->get_rng(); + int i; + double t; + + clear(); + test_progress = -1; + + simple_struct.resize(struct_count, 0); + cxcore_struct.resize(struct_count, 0); + + for( gen = 0; gen < generations; gen++ ) + { + struct_idx = iter = -1; + + if( !storage ) + { + t = cvtest::randReal(rng)*(max_log_storage_block_size - min_log_storage_block_size) + + min_log_storage_block_size; + storage = cvCreateMemStorage( cvRound( exp(t * CV_LOG2) ) ); + } + + iter = struct_idx = -1; + test_multi_create(); + + for( i = 0; i < struct_count; i++ ) + { + if( test_seq_block_consistence(i, (CvSeq*)cxcore_struct[i], + ((CvTsSimpleSeq*)simple_struct[i])->count) < 0 ) + return; + + if( test_get_seq_elem( i, MAX(iterations/3,7) ) < 0 ) + return; + + if( test_get_seq_reading( i, MAX(iterations/3,7) ) < 0 ) + return; + update_progressbar(); + } + + if( test_seq_ops( iterations ) < 0 ) + return; + + if( cvtest::randInt(rng) % 2 ) + storage.release(); + else + cvClearMemStorage( storage ); + } + } + catch(int) + { + } +} + + +////////////////////////////// more sequence tests ////////////////////////////////////// + +class Core_SeqSortInvTest : public Core_SeqBaseTest +{ +public: + Core_SeqSortInvTest(); + void run( int ); + +protected: +}; + + +Core_SeqSortInvTest::Core_SeqSortInvTest() +{ +} + + +static int icvCmpSeqElems( const void* a, const void* b, void* userdata ) +{ + return memcmp( a, b, ((CvSeq*)userdata)->elem_size ); +} + +static int icvCmpSeqElems2_elem_size = 0; +static int icvCmpSeqElems2( const void* a, const void* b ) +{ + return memcmp( a, b, icvCmpSeqElems2_elem_size ); +} + + +void Core_SeqSortInvTest::run( int ) +{ + try + { + RNG& rng = ts->get_rng(); + int i, k; + double t; + schar *elem0, *elem, *elem2; + vector buffer; + + clear(); + test_progress = -1; + + simple_struct.resize(struct_count, 0); + cxcore_struct.resize(struct_count, 0); + + for( gen = 0; gen < generations; gen++ ) + { + struct_idx = iter = -1; + + if( storage.empty() ) + { + t = cvtest::randReal(rng)*(max_log_storage_block_size - min_log_storage_block_size) + + min_log_storage_block_size; + storage = cvCreateMemStorage( cvRound( exp(t * CV_LOG2) ) ); + } + + for( iter = 0; iter < iterations/10; iter++ ) + { + int max_size = 0; + test_multi_create(); + + for( i = 0; i < struct_count; i++ ) + { + CvTsSimpleSeq* sseq = (CvTsSimpleSeq*)simple_struct[i]; + max_size = MAX( max_size, sseq->count*sseq->elem_size ); + } + + buffer.resize(max_size); + + for( i = 0; i < struct_count; i++ ) + { + CvSeq* seq = (CvSeq*)cxcore_struct[i]; + CvTsSimpleSeq* sseq = (CvTsSimpleSeq*)simple_struct[i]; + CvSlice slice = CV_WHOLE_SEQ; + + //printf("%d. %d. %d-th size = %d\n", gen, iter, i, sseq->count ); + + cvSeqInvert( seq ); + cvTsSimpleSeqInvert( sseq ); + + if( test_seq_block_consistence( i, seq, sseq->count ) < 0 ) + return; + + if( sseq->count > 0 && cvtest::randInt(rng) % 2 == 0 ) + { + slice.end_index = cvtest::randInt(rng) % sseq->count + 1; + slice.start_index = cvtest::randInt(rng) % (sseq->count - slice.end_index + 1); + slice.end_index += slice.start_index; + } + + cvCvtSeqToArray( seq, &buffer[0], slice ); + + slice.end_index = MIN( slice.end_index, sseq->count ); + CV_TS_SEQ_CHECK_CONDITION( sseq->count == 0 || memcmp( &buffer[0], + sseq->array + slice.start_index*sseq->elem_size, + (slice.end_index - slice.start_index)*sseq->elem_size ) == 0, + "cvSeqInvert returned wrong result" ); + + for( k = 0; k < (sseq->count > 0 ? 10 : 0); k++ ) + { + int idx0 = cvtest::randInt(rng) % sseq->count, idx = 0; + elem0 = cvTsSimpleSeqElem( sseq, idx0 ); + elem = cvGetSeqElem( seq, idx0 ); + elem2 = cvSeqSearch( seq, elem0, k % 2 ? icvCmpSeqElems : 0, 0, &idx, seq ); + + CV_TS_SEQ_CHECK_CONDITION( elem != 0 && + memcmp( elem0, elem, seq->elem_size ) == 0, + "cvSeqInvert gives incorrect result" ); + CV_TS_SEQ_CHECK_CONDITION( elem2 != 0 && + memcmp( elem0, elem2, seq->elem_size ) == 0 && + elem2 == cvGetSeqElem( seq, idx ), + "cvSeqSearch failed (linear search)" ); + } + + cvSeqSort( seq, icvCmpSeqElems, seq ); + + if( test_seq_block_consistence( i, seq, sseq->count ) < 0 ) + return; + + if( sseq->count > 0 ) + { + // !!! This is not thread-safe !!! + icvCmpSeqElems2_elem_size = sseq->elem_size; + qsort( sseq->array, sseq->count, sseq->elem_size, icvCmpSeqElems2 ); + + if( cvtest::randInt(rng) % 2 == 0 ) + { + slice.end_index = cvtest::randInt(rng) % sseq->count + 1; + slice.start_index = cvtest::randInt(rng) % (sseq->count - slice.end_index + 1); + slice.end_index += slice.start_index; + } + } + + cvCvtSeqToArray( seq, &buffer[0], slice ); + CV_TS_SEQ_CHECK_CONDITION( sseq->count == 0 || memcmp( &buffer[0], + sseq->array + slice.start_index*sseq->elem_size, + (slice.end_index - slice.start_index)*sseq->elem_size ) == 0, + "cvSeqSort returned wrong result" ); + + for( k = 0; k < (sseq->count > 0 ? 10 : 0); k++ ) + { + int idx0 = cvtest::randInt(rng) % sseq->count, idx = 0; + elem0 = cvTsSimpleSeqElem( sseq, idx0 ); + elem = cvGetSeqElem( seq, idx0 ); + elem2 = cvSeqSearch( seq, elem0, icvCmpSeqElems, 1, &idx, seq ); + + CV_TS_SEQ_CHECK_CONDITION( elem != 0 && + memcmp( elem0, elem, seq->elem_size ) == 0, + "cvSeqSort gives incorrect result" ); + CV_TS_SEQ_CHECK_CONDITION( elem2 != 0 && + memcmp( elem0, elem2, seq->elem_size ) == 0 && + elem2 == cvGetSeqElem( seq, idx ), + "cvSeqSearch failed (binary search)" ); + } + } + + cvClearMemStorage( storage ); + } + + storage.release(); + } + } + catch (int) + { + } +} + + +/////////////////////////////////////// set tests /////////////////////////////////////// + +class Core_SetTest : public Core_DynStructBaseTest +{ +public: + Core_SetTest(); + void clear(); + void run( int ); + +protected: + //int test_seq_block_consistence( int struct_idx ); + int test_set_ops( int iters ); +}; + + +Core_SetTest::Core_SetTest() +{ +} + + +void Core_SetTest::clear() +{ + for( size_t i = 0; i < simple_struct.size(); i++ ) + cvTsReleaseSimpleSet( (CvTsSimpleSet**)&simple_struct[i] ); + Core_DynStructBaseTest::clear(); +} + + +int Core_SetTest::test_set_ops( int iters ) +{ + const int max_op = 4; + int max_elem_size = 0; + int idx, idx0; + CvSetElem *elem = 0, *elem2 = 0, *elem3 = 0; + schar* elem_data = 0; + RNG& rng = ts->get_rng(); + //int max_active_count = 0, mean_active_count = 0; + + for( int i = 0; i < struct_count; i++ ) + max_elem_size = MAX( max_elem_size, ((CvSeq*)cxcore_struct[i])->elem_size ); + + vector elem_buf(max_elem_size); + Mat elem_mat; + + for( iter = 0; iter < iters; iter++ ) + { + struct_idx = cvtest::randInt(rng) % struct_count; + + CvSet* cvset = (CvSet*)cxcore_struct[struct_idx]; + CvTsSimpleSet* sset = (CvTsSimpleSet*)simple_struct[struct_idx]; + int pure_elem_size = sset->elem_size - 1; + int prev_total = cvset->total, prev_count = cvset->active_count; + int op = cvtest::randInt(rng) % (iter <= iters/10 ? 2 : max_op); + int by_ptr = op % 2 == 0; + CvSetElem* first_free = cvset->free_elems; + CvSetElem* next_free = first_free ? first_free->next_free : 0; + int pass_data = 0; + + if( iter > iters/10 && cvtest::randInt(rng)%200 == 0 ) // clear set + { + int prev_count = cvset->total; + cvClearSet( cvset ); + cvTsClearSimpleSet( sset ); + + CV_TS_SEQ_CHECK_CONDITION( cvset->active_count == 0 && cvset->total == 0 && + cvset->first == 0 && cvset->free_elems == 0 && + (cvset->free_blocks != 0 || prev_count == 0), + "cvClearSet doesn't remove all the elements" ); + continue; + } + else if( op == 0 || op == 1 ) // add element + { + if( sset->free_count == 0 ) + continue; + + elem_mat = Mat(1, cvset->elem_size, CV_8U, &elem_buf[0]); + cvtest::randUni( rng, elem_mat, cvScalarAll(0), cvScalarAll(255) ); + elem = (CvSetElem*)&elem_buf[0]; + + if( by_ptr ) + { + elem2 = cvSetNew( cvset ); + CV_TS_SEQ_CHECK_CONDITION( elem2 != 0, "cvSetNew returned NULL pointer" ); + } + else + { + pass_data = cvtest::randInt(rng) % 2; + idx = cvSetAdd( cvset, pass_data ? elem : 0, &elem2 ); + CV_TS_SEQ_CHECK_CONDITION( elem2 != 0 && elem2->flags == idx, + "cvSetAdd returned NULL pointer or a wrong index" ); + } + + elem_data = (schar*)elem + sizeof(int); + + if( !pass_data ) + memcpy( (schar*)elem2 + sizeof(int), elem_data, pure_elem_size ); + + idx = elem2->flags; + idx0 = cvTsSimpleSetAdd( sset, elem_data ); + elem3 = cvGetSetElem( cvset, idx ); + + CV_TS_SEQ_CHECK_CONDITION( CV_IS_SET_ELEM(elem3) && + idx == idx0 && elem3 == elem2 && (!pass_data || + memcmp( (char*)elem3 + sizeof(int), elem_data, pure_elem_size) == 0), + "The added element is not correct" ); + + CV_TS_SEQ_CHECK_CONDITION( (!first_free || elem3 == first_free) && + (!next_free || cvset->free_elems == next_free) && + cvset->active_count == prev_count + 1, + "The free node list is modified incorrectly" ); + } + else if( op == 2 || op == 3 ) // remove element + { + idx = cvtest::randInt(rng) % sset->max_count; + + if( sset->free_count == sset->max_count || idx >= sset->count ) + continue; + + elem_data = cvTsSimpleSetFind(sset, idx); + if( elem_data == 0 ) + continue; + + elem = cvGetSetElem( cvset, idx ); + CV_TS_SEQ_CHECK_CONDITION( CV_IS_SET_ELEM(elem) && elem->flags == idx && + memcmp((char*)elem + sizeof(int), elem_data, pure_elem_size) == 0, + "cvGetSetElem returned wrong element" ); + + if( by_ptr ) + { + cvSetRemoveByPtr( cvset, elem ); + } + else + { + cvSetRemove( cvset, idx ); + } + + cvTsSimpleSetRemove( sset, idx ); + + CV_TS_SEQ_CHECK_CONDITION( !CV_IS_SET_ELEM(elem) && !cvGetSetElem(cvset, idx) && + (elem->flags & CV_SET_ELEM_IDX_MASK) == idx, + "cvSetRemove[ByPtr] didn't release the element properly" ); + + CV_TS_SEQ_CHECK_CONDITION( elem->next_free == first_free && + cvset->free_elems == elem && + cvset->active_count == prev_count - 1, + "The free node list has not been updated properly" ); + } + + //max_active_count = MAX( max_active_count, cvset->active_count ); + //mean_active_count += cvset->active_count; + CV_TS_SEQ_CHECK_CONDITION( cvset->active_count == sset->max_count - sset->free_count && + cvset->total >= cvset->active_count && + (cvset->total == 0 || cvset->total >= prev_total), + "The total number of cvset elements is not correct" ); + + // CvSet and simple set do not neccessary have the same "total" (active & free) number, + // so pass "set->total" to skip that check + test_seq_block_consistence( struct_idx, (CvSeq*)cvset, cvset->total ); + update_progressbar(); + } + + return 0; +} + + +void Core_SetTest::run( int ) +{ + try + { + RNG& rng = ts->get_rng(); + double t; + + clear(); + test_progress = -1; + + simple_struct.resize(struct_count, 0); + cxcore_struct.resize(struct_count, 0); + + for( gen = 0; gen < generations; gen++ ) + { + struct_idx = iter = -1; + t = cvtest::randReal(rng)*(max_log_storage_block_size - min_log_storage_block_size) + min_log_storage_block_size; + storage = cvCreateMemStorage( cvRound( exp(t * CV_LOG2) ) ); + + for( int i = 0; i < struct_count; i++ ) + { + t = cvtest::randReal(rng)*(max_log_elem_size - min_log_elem_size) + min_log_elem_size; + int pure_elem_size = cvRound( exp(t * CV_LOG2) ); + int elem_size = pure_elem_size + sizeof(int); + elem_size = (elem_size + sizeof(size_t) - 1) & ~(sizeof(size_t)-1); + elem_size = MAX( elem_size, (int)sizeof(CvSetElem) ); + elem_size = MIN( elem_size, (int)(storage->block_size - sizeof(void*) - sizeof(CvMemBlock) - sizeof(CvSeqBlock)) ); + pure_elem_size = MIN( pure_elem_size, elem_size-(int)sizeof(CvSetElem) ); + + cvTsReleaseSimpleSet( (CvTsSimpleSet**)&simple_struct[i] ); + simple_struct[i] = cvTsCreateSimpleSet( max_struct_size, pure_elem_size ); + cxcore_struct[i] = cvCreateSet( 0, sizeof(CvSet), elem_size, storage ); + } + + if( test_set_ops( iterations*100 ) < 0 ) + return; + + storage.release(); + } + } + catch(int) + { + } +} + + +/////////////////////////////////////// graph tests ////////////////////////////////// + +class Core_GraphTest : public Core_DynStructBaseTest +{ +public: + Core_GraphTest(); + void clear(); + void run( int ); + +protected: + //int test_seq_block_consistence( int struct_idx ); + int test_graph_ops( int iters ); +}; + + +Core_GraphTest::Core_GraphTest() +{ +} + + +void Core_GraphTest::clear() +{ + for( size_t i = 0; i < simple_struct.size(); i++ ) + cvTsReleaseSimpleGraph( (CvTsSimpleGraph**)&simple_struct[i] ); + Core_DynStructBaseTest::clear(); +} + + +int Core_GraphTest::test_graph_ops( int iters ) +{ + const int max_op = 4; + int i, k; + int max_elem_size = 0; + int idx, idx0; + CvGraphVtx *vtx = 0, *vtx2 = 0, *vtx3 = 0; + CvGraphEdge* edge = 0, *edge2 = 0; + RNG& rng = ts->get_rng(); + //int max_active_count = 0, mean_active_count = 0; + + for( i = 0; i < struct_count; i++ ) + { + CvGraph* graph = (CvGraph*)cxcore_struct[i]; + max_elem_size = MAX( max_elem_size, graph->elem_size ); + max_elem_size = MAX( max_elem_size, graph->edges->elem_size ); + } + + vector elem_buf(max_elem_size); + Mat elem_mat; + + for( iter = 0; iter < iters; iter++ ) + { + struct_idx = cvtest::randInt(rng) % struct_count; + CvGraph* graph = (CvGraph*)cxcore_struct[struct_idx]; + CvTsSimpleGraph* sgraph = (CvTsSimpleGraph*)simple_struct[struct_idx]; + CvSet* edges = graph->edges; + schar *vtx_data; + char *edge_data; + int pure_vtx_size = sgraph->vtx->elem_size - 1, + pure_edge_size = sgraph->edge_size - 1; + int prev_vtx_total = graph->total, + prev_edge_total = graph->edges->total, + prev_vtx_count = graph->active_count, + prev_edge_count = graph->edges->active_count; + int op = cvtest::randInt(rng) % max_op; + int pass_data = 0, vtx_degree0 = 0, vtx_degree = 0; + CvSetElem *first_free, *next_free; + + if( cvtest::randInt(rng) % 200 == 0 ) // clear graph + { + int prev_vtx_count = graph->total, prev_edge_count = graph->edges->total; + + cvClearGraph( graph ); + cvTsClearSimpleGraph( sgraph ); + + CV_TS_SEQ_CHECK_CONDITION( graph->active_count == 0 && graph->total == 0 && + graph->first == 0 && graph->free_elems == 0 && + (graph->free_blocks != 0 || prev_vtx_count == 0), + "The graph is not empty after clearing" ); + + CV_TS_SEQ_CHECK_CONDITION( edges->active_count == 0 && edges->total == 0 && + edges->first == 0 && edges->free_elems == 0 && + (edges->free_blocks != 0 || prev_edge_count == 0), + "The graph is not empty after clearing" ); + } + else if( op == 0 ) // add vertex + { + if( sgraph->vtx->free_count == 0 ) + continue; + + first_free = graph->free_elems; + next_free = first_free ? first_free->next_free : 0; + + if( pure_vtx_size ) + { + elem_mat = Mat(1, graph->elem_size, CV_8U, &elem_buf[0]); + cvtest::randUni( rng, elem_mat, cvScalarAll(0), cvScalarAll(255) ); + } + + vtx = (CvGraphVtx*)&elem_buf[0]; + idx0 = cvTsSimpleGraphAddVertex( sgraph, vtx + 1 ); + + pass_data = cvtest::randInt(rng) % 2; + idx = cvGraphAddVtx( graph, pass_data ? vtx : 0, &vtx2 ); + + if( !pass_data && pure_vtx_size > 0 ) + memcpy( vtx2 + 1, vtx + 1, pure_vtx_size ); + + vtx3 = cvGetGraphVtx( graph, idx ); + + CV_TS_SEQ_CHECK_CONDITION( (CV_IS_SET_ELEM(vtx3) && vtx3->flags == idx && + vtx3->first == 0) || (idx == idx0 && vtx3 == vtx2 && + (!pass_data || pure_vtx_size == 0 || + memcmp(vtx3 + 1, vtx + 1, pure_vtx_size) == 0)), + "The added element is not correct" ); + + CV_TS_SEQ_CHECK_CONDITION( (!first_free || first_free == (CvSetElem*)vtx3) && + (!next_free || graph->free_elems == next_free) && + graph->active_count == prev_vtx_count + 1, + "The free node list is modified incorrectly" ); + } + else if( op == 1 ) // remove vertex + { + idx = cvtest::randInt(rng) % sgraph->vtx->max_count; + if( sgraph->vtx->free_count == sgraph->vtx->max_count || idx >= sgraph->vtx->count ) + continue; + + vtx_data = cvTsSimpleGraphFindVertex(sgraph, idx); + if( vtx_data == 0 ) + continue; + + vtx_degree0 = cvTsSimpleGraphVertexDegree( sgraph, idx ); + first_free = graph->free_elems; + + vtx = cvGetGraphVtx( graph, idx ); + CV_TS_SEQ_CHECK_CONDITION( CV_IS_SET_ELEM(vtx) && vtx->flags == idx && + (pure_vtx_size == 0 || memcmp( vtx + 1, vtx_data, pure_vtx_size) == 0), + "cvGetGraphVtx returned wrong element" ); + + if( cvtest::randInt(rng) % 2 ) + { + vtx_degree = cvGraphVtxDegreeByPtr( graph, vtx ); + cvGraphRemoveVtxByPtr( graph, vtx ); + } + else + { + vtx_degree = cvGraphVtxDegree( graph, idx ); + cvGraphRemoveVtx( graph, idx ); + } + + cvTsSimpleGraphRemoveVertex( sgraph, idx ); + + CV_TS_SEQ_CHECK_CONDITION( vtx_degree == vtx_degree0, + "Number of incident edges is different in two graph representations" ); + + CV_TS_SEQ_CHECK_CONDITION( !CV_IS_SET_ELEM(vtx) && !cvGetGraphVtx(graph, idx) && + (vtx->flags & CV_SET_ELEM_IDX_MASK) == idx, + "cvGraphRemoveVtx[ByPtr] didn't release the vertex properly" ); + + CV_TS_SEQ_CHECK_CONDITION( graph->edges->active_count == prev_edge_count - vtx_degree, + "cvGraphRemoveVtx[ByPtr] didn't remove all the incident edges " + "(or removed some extra)" ); + + CV_TS_SEQ_CHECK_CONDITION( ((CvSetElem*)vtx)->next_free == first_free && + graph->free_elems == (CvSetElem*)vtx && + graph->active_count == prev_vtx_count - 1, + "The free node list has not been updated properly" ); + } + else if( op == 2 ) // add edge + { + int v_idx[2] = {0,0}, res = 0; + int v_prev_degree[2] = {0,0}, v_degree[2] = {0,0}; + + if( sgraph->vtx->free_count >= sgraph->vtx->max_count-1 ) + continue; + + for( i = 0, k = 0; i < 10; i++ ) + { + int j = cvtest::randInt(rng) % sgraph->vtx->count; + vtx_data = cvTsSimpleGraphFindVertex( sgraph, j ); + if( vtx_data ) + { + v_idx[k] = j; + if( k == 0 ) + k++; + else if( v_idx[0] != v_idx[1] && + cvTsSimpleGraphFindEdge( sgraph, v_idx[0], v_idx[1] ) == 0 ) + { + k++; + break; + } + } + } + + if( k < 2 ) + continue; + + first_free = graph->edges->free_elems; + next_free = first_free ? first_free->next_free : 0; + + edge = cvFindGraphEdge( graph, v_idx[0], v_idx[1] ); + CV_TS_SEQ_CHECK_CONDITION( edge == 0, "Extra edge appeared in the graph" ); + + if( pure_edge_size > 0 ) + { + elem_mat = Mat(1, graph->edges->elem_size, CV_8U, &elem_buf[0]); + cvtest::randUni( rng, elem_mat, cvScalarAll(0), cvScalarAll(255) ); + } + edge = (CvGraphEdge*)&elem_buf[0]; + + // assign some default weight that is easy to check for + // consistensy, 'cause an edge weight is not stored + // in the simple graph + edge->weight = (float)(v_idx[0] + v_idx[1]); + pass_data = cvtest::randInt(rng) % 2; + + vtx = cvGetGraphVtx( graph, v_idx[0] ); + vtx2 = cvGetGraphVtx( graph, v_idx[1] ); + CV_TS_SEQ_CHECK_CONDITION( vtx != 0 && vtx2 != 0 && vtx->flags == v_idx[0] && + vtx2->flags == v_idx[1], "Some of the vertices are missing" ); + + if( cvtest::randInt(rng) % 2 ) + { + v_prev_degree[0] = cvGraphVtxDegreeByPtr( graph, vtx ); + v_prev_degree[1] = cvGraphVtxDegreeByPtr( graph, vtx2 ); + res = cvGraphAddEdgeByPtr(graph, vtx, vtx2, pass_data ? edge : 0, &edge2); + v_degree[0] = cvGraphVtxDegreeByPtr( graph, vtx ); + v_degree[1] = cvGraphVtxDegreeByPtr( graph, vtx2 ); + } + else + { + v_prev_degree[0] = cvGraphVtxDegree( graph, v_idx[0] ); + v_prev_degree[1] = cvGraphVtxDegree( graph, v_idx[1] ); + res = cvGraphAddEdge(graph, v_idx[0], v_idx[1], pass_data ? edge : 0, &edge2); + v_degree[0] = cvGraphVtxDegree( graph, v_idx[0] ); + v_degree[1] = cvGraphVtxDegree( graph, v_idx[1] ); + } + + //edge3 = (CvGraphEdge*)cvGetSetElem( graph->edges, idx ); + CV_TS_SEQ_CHECK_CONDITION( res == 1 && edge2 != 0 && CV_IS_SET_ELEM(edge2) && + ((edge2->vtx[0] == vtx && edge2->vtx[1] == vtx2) || + (!CV_IS_GRAPH_ORIENTED(graph) && edge2->vtx[0] == vtx2 && edge2->vtx[1] == vtx)) && + (!pass_data || pure_edge_size == 0 || memcmp( edge2 + 1, edge + 1, pure_edge_size ) == 0), + "The edge has been added incorrectly" ); + + if( !pass_data ) + { + if( pure_edge_size > 0 ) + memcpy( edge2 + 1, edge + 1, pure_edge_size ); + edge2->weight = edge->weight; + } + + CV_TS_SEQ_CHECK_CONDITION( v_degree[0] == v_prev_degree[0] + 1 && + v_degree[1] == v_prev_degree[1] + 1, + "The vertices lists have not been updated properly" ); + + cvTsSimpleGraphAddEdge( sgraph, v_idx[0], v_idx[1], edge + 1 ); + + CV_TS_SEQ_CHECK_CONDITION( (!first_free || first_free == (CvSetElem*)edge2) && + (!next_free || graph->edges->free_elems == next_free) && + graph->edges->active_count == prev_edge_count + 1, + "The free node list is modified incorrectly" ); + } + else if( op == 3 ) // find & remove edge + { + int v_idx[2] = {0,0}, by_ptr; + int v_prev_degree[2] = {0,0}, v_degree[2] = {0,0}; + + if( sgraph->vtx->free_count >= sgraph->vtx->max_count-1 ) + continue; + + edge_data = 0; + for( i = 0, k = 0; i < 10; i++ ) + { + int j = cvtest::randInt(rng) % sgraph->vtx->count; + vtx_data = cvTsSimpleGraphFindVertex( sgraph, j ); + if( vtx_data ) + { + v_idx[k] = j; + if( k == 0 ) + k++; + else + { + edge_data = cvTsSimpleGraphFindEdge( sgraph, v_idx[0], v_idx[1] ); + if( edge_data ) + { + k++; + break; + } + } + } + } + + if( k < 2 ) + continue; + + by_ptr = cvtest::randInt(rng) % 2; + first_free = graph->edges->free_elems; + + vtx = cvGetGraphVtx( graph, v_idx[0] ); + vtx2 = cvGetGraphVtx( graph, v_idx[1] ); + CV_TS_SEQ_CHECK_CONDITION( vtx != 0 && vtx2 != 0 && vtx->flags == v_idx[0] && + vtx2->flags == v_idx[1], "Some of the vertices are missing" ); + + if( by_ptr ) + { + edge = cvFindGraphEdgeByPtr( graph, vtx, vtx2 ); + v_prev_degree[0] = cvGraphVtxDegreeByPtr( graph, vtx ); + v_prev_degree[1] = cvGraphVtxDegreeByPtr( graph, vtx2 ); + } + else + { + edge = cvFindGraphEdge( graph, v_idx[0], v_idx[1] ); + v_prev_degree[0] = cvGraphVtxDegree( graph, v_idx[0] ); + v_prev_degree[1] = cvGraphVtxDegree( graph, v_idx[1] ); + } + + idx = edge->flags; + + CV_TS_SEQ_CHECK_CONDITION( edge != 0 && edge->weight == v_idx[0] + v_idx[1] && + ((edge->vtx[0] == vtx && edge->vtx[1] == vtx2) || + (!CV_IS_GRAPH_ORIENTED(graph) && edge->vtx[1] == vtx && edge->vtx[0] == vtx2)) && + (pure_edge_size == 0 || memcmp(edge + 1, edge_data, pure_edge_size) == 0), + "An edge is missing or incorrect" ); + + if( by_ptr ) + { + cvGraphRemoveEdgeByPtr( graph, vtx, vtx2 ); + edge2 = cvFindGraphEdgeByPtr( graph, vtx, vtx2 ); + v_degree[0] = cvGraphVtxDegreeByPtr( graph, vtx ); + v_degree[1] = cvGraphVtxDegreeByPtr( graph, vtx2 ); + } + else + { + cvGraphRemoveEdge(graph, v_idx[0], v_idx[1] ); + edge2 = cvFindGraphEdge( graph, v_idx[0], v_idx[1] ); + v_degree[0] = cvGraphVtxDegree( graph, v_idx[0] ); + v_degree[1] = cvGraphVtxDegree( graph, v_idx[1] ); + } + + CV_TS_SEQ_CHECK_CONDITION( !edge2 && !CV_IS_SET_ELEM(edge), + "The edge has not been removed from the edge set" ); + + CV_TS_SEQ_CHECK_CONDITION( v_degree[0] == v_prev_degree[0] - 1 && + v_degree[1] == v_prev_degree[1] - 1, + "The vertices lists have not been updated properly" ); + + cvTsSimpleGraphRemoveEdge( sgraph, v_idx[0], v_idx[1] ); + + CV_TS_SEQ_CHECK_CONDITION( graph->edges->free_elems == (CvSetElem*)edge && + graph->edges->free_elems->next_free == first_free && + graph->edges->active_count == prev_edge_count - 1, + "The free edge list has not been modified properly" ); + } + + //max_active_count = MAX( max_active_count, graph->active_count ); + //mean_active_count += graph->active_count; + + CV_TS_SEQ_CHECK_CONDITION( graph->active_count == sgraph->vtx->max_count - sgraph->vtx->free_count && + graph->total >= graph->active_count && + (graph->total == 0 || graph->total >= prev_vtx_total), + "The total number of graph vertices is not correct" ); + + CV_TS_SEQ_CHECK_CONDITION( graph->edges->total >= graph->edges->active_count && + (graph->edges->total == 0 || graph->edges->total >= prev_edge_total), + "The total number of graph vertices is not correct" ); + + // CvGraph and simple graph do not neccessary have the same "total" (active & free) number, + // so pass "graph->total" (or "graph->edges->total") to skip that check + test_seq_block_consistence( struct_idx, (CvSeq*)graph, graph->total ); + test_seq_block_consistence( struct_idx, (CvSeq*)graph->edges, graph->edges->total ); + update_progressbar(); + } + + return 0; +} + + +void Core_GraphTest::run( int ) +{ + try + { + RNG& rng = ts->get_rng(); + int i, k; + double t; + + clear(); + test_progress = -1; + + simple_struct.resize(struct_count, 0); + cxcore_struct.resize(struct_count, 0); + + for( gen = 0; gen < generations; gen++ ) + { + struct_idx = iter = -1; + t = cvtest::randReal(rng)*(max_log_storage_block_size - min_log_storage_block_size) + min_log_storage_block_size; + int block_size = cvRound( exp(t * CV_LOG2) ); + block_size = MAX(block_size, (int)(sizeof(CvGraph) + sizeof(CvMemBlock) + sizeof(CvSeqBlock))); + + storage = cvCreateMemStorage(block_size); + + for( i = 0; i < struct_count; i++ ) + { + int pure_elem_size[2], elem_size[2]; + int is_oriented = (gen + i) % 2; + for( k = 0; k < 2; k++ ) + { + t = cvtest::randReal(rng)*(max_log_elem_size - min_log_elem_size) + min_log_elem_size; + int pe = cvRound( exp(t * CV_LOG2) ) - 1; // pure_elem_size==0 does also make sense + int delta = k == 0 ? sizeof(CvGraphVtx) : sizeof(CvGraphEdge); + int e = pe + delta; + e = (e + sizeof(size_t) - 1) & ~(sizeof(size_t)-1); + e = MIN( e, (int)(storage->block_size - sizeof(CvMemBlock) - + sizeof(CvSeqBlock) - sizeof(void*)) ); + pe = MIN(pe, e - delta); + pure_elem_size[k] = pe; + elem_size[k] = e; + } + + cvTsReleaseSimpleGraph( (CvTsSimpleGraph**)&simple_struct[i] ); + simple_struct[i] = cvTsCreateSimpleGraph( max_struct_size/4, pure_elem_size[0], + pure_elem_size[1], is_oriented ); + cxcore_struct[i] = cvCreateGraph( is_oriented ? CV_ORIENTED_GRAPH : CV_GRAPH, + sizeof(CvGraph), elem_size[0], elem_size[1], + storage ); + } + + if( test_graph_ops( iterations*10 ) < 0 ) + return; + + storage.release(); + } + } + catch(int) + { + } +} + + +//////////// graph scan test ////////////// + +class Core_GraphScanTest : public Core_DynStructBaseTest +{ +public: + Core_GraphScanTest(); + void run( int ); + +protected: + //int test_seq_block_consistence( int struct_idx ); + int create_random_graph( int ); +}; + + +Core_GraphScanTest::Core_GraphScanTest() +{ + iterations = 100; + struct_count = 1; +} + + +int Core_GraphScanTest::create_random_graph( int _struct_idx ) +{ + RNG& rng = ts->get_rng(); + int is_oriented = cvtest::randInt(rng) % 2; + int i, vtx_count = cvtest::randInt(rng) % max_struct_size; + int edge_count = cvtest::randInt(rng) % MAX(vtx_count*20, 1); + CvGraph* graph; + + struct_idx = _struct_idx; + cxcore_struct[_struct_idx] = graph = + cvCreateGraph(is_oriented ? CV_ORIENTED_GRAPH : CV_GRAPH, + sizeof(CvGraph), sizeof(CvGraphVtx), + sizeof(CvGraphEdge), storage ); + + for( i = 0; i < vtx_count; i++ ) + cvGraphAddVtx( graph ); + + assert( graph->active_count == vtx_count ); + + for( i = 0; i < edge_count; i++ ) + { + int j = cvtest::randInt(rng) % vtx_count; + int k = cvtest::randInt(rng) % vtx_count; + + if( j != k ) + cvGraphAddEdge( graph, j, k ); + } + + assert( graph->active_count == vtx_count && graph->edges->active_count <= edge_count ); + + return 0; +} + + +void Core_GraphScanTest::run( int ) +{ + CvGraphScanner* scanner = 0; + try + { + RNG& rng = ts->get_rng(); + vector vtx_mask, edge_mask; + double t; + int i; + + clear(); + test_progress = -1; + + cxcore_struct.resize(struct_count, 0); + + for( gen = 0; gen < generations; gen++ ) + { + struct_idx = iter = -1; + t = cvtest::randReal(rng)*(max_log_storage_block_size - min_log_storage_block_size) + min_log_storage_block_size; + int storage_blocksize = cvRound( exp(t * CV_LOG2) ); + storage_blocksize = MAX(storage_blocksize, (int)(sizeof(CvGraph) + sizeof(CvMemBlock) + sizeof(CvSeqBlock))); + storage_blocksize = MAX(storage_blocksize, (int)(sizeof(CvGraphEdge) + sizeof(CvMemBlock) + sizeof(CvSeqBlock))); + storage_blocksize = MAX(storage_blocksize, (int)(sizeof(CvGraphVtx) + sizeof(CvMemBlock) + sizeof(CvSeqBlock))); + storage = cvCreateMemStorage(storage_blocksize); + + if( gen == 0 ) + { + // special regression test for one sample graph. + // !!! ATTENTION !!! The test relies on the particular order of the inserted edges + // (LIFO: the edge inserted last goes first in the list of incident edges). + // if it is changed, the test will have to be modified. + + int vtx_count = -1, edge_count = 0, edges[][3] = + { + {0,4,'f'}, {0,1,'t'}, {1,4,'t'}, {1,2,'t'}, {2,3,'t'}, {4,3,'c'}, {3,1,'b'}, + {5,7,'t'}, {7,5,'b'}, {5,6,'t'}, {6,0,'c'}, {7,6,'c'}, {6,4,'c'}, {-1,-1,0} + }; + + CvGraph* graph = cvCreateGraph( CV_ORIENTED_GRAPH, sizeof(CvGraph), + sizeof(CvGraphVtx), sizeof(CvGraphEdge), storage ); + + for( i = 0; edges[i][0] >= 0; i++ ) + { + vtx_count = MAX( vtx_count, edges[i][0] ); + vtx_count = MAX( vtx_count, edges[i][1] ); + } + vtx_count++; + + for( i = 0; i < vtx_count; i++ ) + cvGraphAddVtx( graph ); + + for( i = 0; edges[i][0] >= 0; i++ ) + { + CvGraphEdge* edge; + cvGraphAddEdge( graph, edges[i][0], edges[i][1], 0, &edge ); + edge->weight = (float)edges[i][2]; + } + + edge_count = i; + scanner = cvCreateGraphScanner( graph, 0, CV_GRAPH_ALL_ITEMS ); + + for(;;) + { + int code, a = -1, b = -1; + const char* event = ""; + code = cvNextGraphItem( scanner ); + + switch( code ) + { + case CV_GRAPH_VERTEX: + event = "Vertex"; + vtx_count--; + a = cvGraphVtxIdx( graph, scanner->vtx ); + break; + case CV_GRAPH_TREE_EDGE: + event = "Tree Edge"; + edge_count--; + CV_TS_SEQ_CHECK_CONDITION( scanner->edge->weight == (float)'t', + "Invalid edge type" ); + a = cvGraphVtxIdx( graph, scanner->vtx ); + b = cvGraphVtxIdx( graph, scanner->dst ); + break; + case CV_GRAPH_BACK_EDGE: + event = "Back Edge"; + edge_count--; + CV_TS_SEQ_CHECK_CONDITION( scanner->edge->weight == (float)'b', + "Invalid edge type" ); + a = cvGraphVtxIdx( graph, scanner->vtx ); + b = cvGraphVtxIdx( graph, scanner->dst ); + break; + case CV_GRAPH_CROSS_EDGE: + event = "Cross Edge"; + edge_count--; + CV_TS_SEQ_CHECK_CONDITION( scanner->edge->weight == (float)'c', + "Invalid edge type" ); + a = cvGraphVtxIdx( graph, scanner->vtx ); + b = cvGraphVtxIdx( graph, scanner->dst ); + break; + case CV_GRAPH_FORWARD_EDGE: + event = "Forward Edge"; + edge_count--; + CV_TS_SEQ_CHECK_CONDITION( scanner->edge->weight == (float)'f', + "Invalid edge type" ); + a = cvGraphVtxIdx( graph, scanner->vtx ); + b = cvGraphVtxIdx( graph, scanner->dst ); + break; + case CV_GRAPH_BACKTRACKING: + event = "Backtracking"; + a = cvGraphVtxIdx( graph, scanner->vtx ); + break; + case CV_GRAPH_NEW_TREE: + event = "New search tree"; + break; + case CV_GRAPH_OVER: + event = "End of procedure"; + break; + default: + CV_TS_SEQ_CHECK_CONDITION( 0, "Invalid code appeared during graph scan" ); + } + + ts->printf( cvtest::TS::LOG, "%s", event ); + if( a >= 0 ) + { + if( b >= 0 ) + ts->printf( cvtest::TS::LOG, ": (%d,%d)", a, b ); + else + ts->printf( cvtest::TS::LOG, ": %d", a ); + } + + ts->printf( cvtest::TS::LOG, "\n" ); + + if( code < 0 ) + break; + } + + CV_TS_SEQ_CHECK_CONDITION( vtx_count == 0 && edge_count == 0, + "Not every vertex/edge has been visited" ); + update_progressbar(); + } + + // for a random graph the test just checks that every graph vertex and + // every edge is vitisted during the scan + for( iter = 0; iter < iterations; iter++ ) + { + create_random_graph(0); + CvGraph* graph = (CvGraph*)cxcore_struct[0]; + + // iterate twice to check that scanner doesn't damage the graph + for( i = 0; i < 2; i++ ) + { + CvGraphVtx* start_vtx = cvtest::randInt(rng) % 2 || graph->active_count == 0 ? 0 : + cvGetGraphVtx( graph, cvtest::randInt(rng) % graph->active_count ); + + scanner = cvCreateGraphScanner( graph, start_vtx, CV_GRAPH_ALL_ITEMS ); + + vtx_mask.resize(0); + vtx_mask.resize(graph->active_count, 0); + edge_mask.resize(0); + edge_mask.resize(graph->edges->active_count, 0); + + for(;;) + { + int code = cvNextGraphItem( scanner ); + + if( code == CV_GRAPH_OVER ) + break; + else if( code & CV_GRAPH_ANY_EDGE ) + { + int edge_idx = scanner->edge->flags & CV_SET_ELEM_IDX_MASK; + + CV_TS_SEQ_CHECK_CONDITION( edge_idx < graph->edges->active_count && + edge_mask[edge_idx] == 0, + "The edge is not found or visited for the second time" ); + edge_mask[edge_idx] = 1; + } + else if( code & CV_GRAPH_VERTEX ) + { + int vtx_idx = scanner->vtx->flags & CV_SET_ELEM_IDX_MASK; + + CV_TS_SEQ_CHECK_CONDITION( vtx_idx < graph->active_count && + vtx_mask[vtx_idx] == 0, + "The vtx is not found or visited for the second time" ); + vtx_mask[vtx_idx] = 1; + } + } + + cvReleaseGraphScanner( &scanner ); + + CV_TS_SEQ_CHECK_CONDITION( cvtest::norm(Mat(vtx_mask),CV_L1) == graph->active_count && + cvtest::norm(Mat(edge_mask),CV_L1) == graph->edges->active_count, + "Some vertices or edges have not been visited" ); + update_progressbar(); + } + cvClearMemStorage( storage ); + } + + storage.release(); + } + } + catch(int) + { + } + + cvReleaseGraphScanner( &scanner ); +} + + +TEST(Core_DS_Seq, basic_operations) { Core_SeqBaseTest test; test.safe_run(); } +TEST(Core_DS_Seq, sort_invert) { Core_SeqSortInvTest test; test.safe_run(); } +TEST(Core_DS_Set, basic_operations) { Core_SetTest test; test.safe_run(); } +TEST(Core_DS_Graph, basic_operations) { Core_GraphTest test; test.safe_run(); } +TEST(Core_DS_Graph, scan) { Core_GraphScanTest test; test.safe_run(); } + + diff --git a/modules/core/test/test_dxt.cpp b/modules/core/test/test_dxt.cpp new file mode 100644 index 000000000..b6f7078b3 --- /dev/null +++ b/modules/core/test/test_dxt.cpp @@ -0,0 +1,829 @@ +#include "test_precomp.hpp" + +using namespace cv; +using namespace std; + +namespace cvtest +{ + +static Mat initDFTWave( int n, bool inv ) +{ + int i; + double angle = (inv ? 1 : -1)*CV_PI*2/n; + Complexd wi, w1; + Mat wave(1, n, CV_64FC2); + Complexd* w = wave.ptr(); + + w1.re = cos(angle); + w1.im = sin(angle); + w[0].re = wi.re = 1.; + w[0].im = wi.im = 0.; + + for( i = 1; i < n; i++ ) + { + double t = wi.re*w1.re - wi.im*w1.im; + wi.im = wi.re*w1.im + wi.im*w1.re; + wi.re = t; + w[i] = wi; + } + + return wave; +} + + +static void DFT_1D( const Mat& _src, Mat& _dst, int flags, const Mat& _wave=Mat()) +{ + _dst.create(_src.size(), _src.type()); + int i, j, k, n = _dst.cols + _dst.rows - 1; + Mat wave = _wave; + double scale = (flags & DFT_SCALE) ? 1./n : 1.; + size_t esz = _src.elemSize(); + size_t srcstep = esz, dststep = esz; + const uchar* src0 = _src.data; + uchar* dst0 = _dst.data; + + CV_Assert( _src.cols + _src.rows - 1 == n ); + + if( wave.empty() ) + wave = initDFTWave( n, (flags & DFT_INVERSE) != 0 ); + + const Complexd* w = wave.ptr(); + if( !_src.isContinuous() ) + srcstep = _src.step; + if( !_dst.isContinuous() ) + dststep = _dst.step; + + if( _src.type() == CV_32FC2 ) + { + for( i = 0; i < n; i++ ) + { + Complexf* dst = (Complexf*)(dst0 + i*dststep); + Complexd sum(0,0); + int delta = i; + k = 0; + + for( j = 0; j < n; j++ ) + { + const Complexf* src = (const Complexf*)(src0 + j*srcstep); + sum.re += src->re*w[k].re - src->im*w[k].im; + sum.im += src->re*w[k].im + src->im*w[k].re; + k += delta; + k -= (k >= n ? n : 0); + } + + dst->re = (float)(sum.re*scale); + dst->im = (float)(sum.im*scale); + } + } + else if( _src.type() == CV_64FC2 ) + { + for( i = 0; i < n; i++ ) + { + Complexd* dst = (Complexd*)(dst0 + i*dststep); + Complexd sum(0,0); + int delta = i; + k = 0; + + for( j = 0; j < n; j++ ) + { + const Complexd* src = (const Complexd*)(src0 + j*srcstep); + sum.re += src->re*w[k].re - src->im*w[k].im; + sum.im += src->re*w[k].im + src->im*w[k].re; + k += delta; + k -= (k >= n ? n : 0); + } + + dst->re = sum.re*scale; + dst->im = sum.im*scale; + } + } + else + CV_Error(CV_StsUnsupportedFormat, ""); +} + + +static void DFT_2D( const Mat& src, Mat& dst, int flags ) +{ + const int cn = 2; + int i; + dst.create(src.size(), src.type()); + Mat tmp( src.cols, src.rows, src.type()); + Mat wave = initDFTWave( dst.cols, (flags & DFT_INVERSE) != 0 ); + + // 1. row-wise transform + for( i = 0; i < dst.rows; i++ ) + { + Mat srci = src.row(i).reshape(cn, src.cols), dsti = tmp.col(i); + DFT_1D(srci, dsti, flags, wave ); + } + + if( (flags & DFT_ROWS) == 0 ) + { + if( dst.cols != dst.rows ) + wave = initDFTWave( dst.rows, (flags & DFT_INVERSE) != 0 ); + + // 2. column-wise transform + for( i = 0; i < dst.cols; i++ ) + { + Mat srci = tmp.row(i).reshape(cn, tmp.cols), dsti = dst.col(i); + DFT_1D(srci, dsti, flags, wave ); + } + } + else + cvtest::transpose(tmp, dst); +} + + +static Mat initDCTWave( int n, bool inv ) +{ + int i, k; + double angle = CV_PI*0.5/n; + Mat wave(n, n, CV_64F); + + double scale = sqrt(1./n); + for( k = 0; k < n; k++ ) + wave.at(0, k) = scale; + scale *= sqrt(2.); + for( i = 1; i < n; i++ ) + for( k = 0; k < n; k++ ) + wave.at(i, k) = scale*cos( angle*i*(2*k + 1) ); + + if( inv ) + cv::transpose( wave, wave ); + + return wave; +} + + +static void DCT_1D( const Mat& _src, Mat& _dst, int flags, const Mat& _wave=Mat() ) +{ + _dst.create( _src.size(), _src.type() ); + int i, j, n = _dst.cols + _dst.rows - 1; + Mat wave = _wave; + int srcstep = 1, dststep = 1; + double* w; + + CV_Assert( _src.cols + _src.rows - 1 == n); + + if( wave.empty() ) + wave = initDCTWave( n, (flags & DFT_INVERSE) != 0 ); + w = wave.ptr(); + + if( !_src.isContinuous() ) + srcstep = _src.step/_src.elemSize(); + if( !_dst.isContinuous() ) + dststep = _dst.step/_dst.elemSize(); + + if( _src.type() == CV_32FC1 ) + { + float *dst = _dst.ptr(); + + for( i = 0; i < n; i++, dst += dststep ) + { + const float* src = _src.ptr(); + double sum = 0; + + for( j = 0; j < n; j++, src += srcstep ) + sum += src[0]*w[j]; + w += n; + dst[0] = (float)sum; + } + } + else if( _src.type() == CV_64FC1 ) + { + double *dst = _dst.ptr(); + + for( i = 0; i < n; i++, dst += dststep ) + { + const double* src = _src.ptr(); + double sum = 0; + + for( j = 0; j < n; j++, src += srcstep ) + sum += src[0]*w[j]; + w += n; + dst[0] = sum; + } + } + else + assert(0); +} + + +static void DCT_2D( const Mat& src, Mat& dst, int flags ) +{ + const int cn = 1; + int i; + dst.create( src.size(), src.type() ); + Mat tmp(dst.cols, dst.rows, dst.type() ); + Mat wave = initDCTWave( dst.cols, (flags & DCT_INVERSE) != 0 ); + + // 1. row-wise transform + for( i = 0; i < dst.rows; i++ ) + { + Mat srci = src.row(i).reshape(cn, src.cols); + Mat dsti = tmp.col(i); + DCT_1D(srci, dsti, flags, wave); + } + + if( (flags & DCT_ROWS) == 0 ) + { + if( dst.cols != dst.rows ) + wave = initDCTWave( dst.rows, (flags & DCT_INVERSE) != 0 ); + + // 2. column-wise transform + for( i = 0; i < dst.cols; i++ ) + { + Mat srci = tmp.row(i).reshape(cn, tmp.cols); + Mat dsti = dst.col(i); + DCT_1D( srci, dsti, flags, wave ); + } + } + else + cvtest::transpose( tmp, dst ); +} + + +static void convertFromCCS( const Mat& _src0, const Mat& _src1, Mat& _dst, int flags ) +{ + if( _dst.rows > 1 && (_dst.cols > 1 || (flags & DFT_ROWS)) ) + { + int i, count = _dst.rows, len = _dst.cols; + bool is2d = (flags & DFT_ROWS) == 0; + Mat src0row, src1row, dstrow; + for( i = 0; i < count; i++ ) + { + int j = !is2d || i == 0 ? i : count - i; + src0row = _src0.row(i); + src1row = _src1.row(j); + dstrow = _dst.row(i); + convertFromCCS( src0row, src1row, dstrow, 0 ); + } + + if( is2d ) + { + src0row = _src0.col(0); + dstrow = _dst.col(0); + convertFromCCS( src0row, src0row, dstrow, 0 ); + if( (len & 1) == 0 ) + { + src0row = _src0.col(_src0.cols - 1); + dstrow = _dst.col(len/2); + convertFromCCS( src0row, src0row, dstrow, 0 ); + } + } + } + else + { + int i, n = _dst.cols + _dst.rows - 1, n2 = (n+1) >> 1; + int cn = _src0.channels(); + int srcstep = cn, dststep = 1; + + if( !_dst.isContinuous() ) + dststep = _dst.step/_dst.elemSize(); + + if( !_src0.isContinuous() ) + srcstep = _src0.step/_src0.elemSize1(); + + if( _dst.depth() == CV_32F ) + { + Complexf* dst = _dst.ptr(); + const float* src0 = _src0.ptr(); + const float* src1 = _src1.ptr(); + int delta0, delta1; + + dst->re = src0[0]; + dst->im = 0; + + if( (n & 1) == 0 ) + { + dst[n2*dststep].re = src0[(cn == 1 ? n-1 : n2)*srcstep]; + dst[n2*dststep].im = 0; + } + + delta0 = srcstep; + delta1 = delta0 + (cn == 1 ? srcstep : 1); + if( cn == 1 ) + srcstep *= 2; + + for( i = 1; i < n2; i++, delta0 += srcstep, delta1 += srcstep ) + { + float t0 = src0[delta0]; + float t1 = src0[delta1]; + + dst[i*dststep].re = t0; + dst[i*dststep].im = t1; + + t0 = src1[delta0]; + t1 = -src1[delta1]; + + dst[(n-i)*dststep].re = t0; + dst[(n-i)*dststep].im = t1; + } + } + else + { + Complexd* dst = _dst.ptr(); + const double* src0 = _src0.ptr(); + const double* src1 = _src1.ptr(); + int delta0, delta1; + + dst->re = src0[0]; + dst->im = 0; + + if( (n & 1) == 0 ) + { + dst[n2*dststep].re = src0[(cn == 1 ? n-1 : n2)*srcstep]; + dst[n2*dststep].im = 0; + } + + delta0 = srcstep; + delta1 = delta0 + (cn == 1 ? srcstep : 1); + if( cn == 1 ) + srcstep *= 2; + + for( i = 1; i < n2; i++, delta0 += srcstep, delta1 += srcstep ) + { + double t0 = src0[delta0]; + double t1 = src0[delta1]; + + dst[i*dststep].re = t0; + dst[i*dststep].im = t1; + + t0 = src1[delta0]; + t1 = -src1[delta1]; + + dst[(n-i)*dststep].re = t0; + dst[(n-i)*dststep].im = t1; + } + } + } +} + + +static void fixCCS( Mat& mat, int cols, int flags ) +{ + int i, rows = mat.rows; + int rows2 = (flags & DFT_ROWS) ? rows : rows/2 + 1, cols2 = cols/2 + 1; + + CV_Assert( cols2 == mat.cols ); + + if( mat.type() == CV_32FC2 ) + { + for( i = 0; i < rows2; i++ ) + { + Complexf* row = mat.ptr(i); + if( (flags & DFT_ROWS) || i == 0 || (i == rows2 - 1 && rows % 2 == 0) ) + { + row[0].im = 0; + if( cols % 2 == 0 ) + row[cols2-1].im = 0; + } + else + { + Complexf* row2 = mat.ptr(rows-i); + row2[0].re = row[0].re; + row2[0].im = -row[0].im; + + if( cols % 2 == 0 ) + { + row2[cols2-1].re = row[cols2-1].re; + row2[cols2-1].im = -row[cols2-1].im; + } + } + } + } + else if( mat.type() == CV_64FC2 ) + { + for( i = 0; i < rows2; i++ ) + { + Complexd* row = mat.ptr(i); + if( (flags & DFT_ROWS) || i == 0 || (i == rows2 - 1 && rows % 2 == 0) ) + { + row[0].im = 0; + if( cols % 2 == 0 ) + row[cols2-1].im = 0; + } + else + { + Complexd* row2 = mat.ptr(rows-i); + row2[0].re = row[0].re; + row2[0].im = -row[0].im; + + if( cols % 2 == 0 ) + { + row2[cols2-1].re = row[cols2-1].re; + row2[cols2-1].im = -row[cols2-1].im; + } + } + } + } +} + + +static void mulComplex( const Mat& src1, const Mat& src2, Mat& dst, int flags ) +{ + dst.create(src1.rows, src1.cols, src1.type()); + int i, j, depth = src1.depth(), cols = src1.cols*2; + + CV_Assert( src1.size == src2.size && src1.type() == src2.type() && + (src1.type() == CV_32FC2 || src1.type() == CV_64FC2) ); + + for( i = 0; i < dst.rows; i++ ) + { + if( depth == CV_32F ) + { + const float* a = src1.ptr(i); + const float* b = src2.ptr(i); + float* c = dst.ptr(i); + + if( !(flags & CV_DXT_MUL_CONJ) ) + for( j = 0; j < cols; j += 2 ) + { + double re = (double)a[j]*b[j] - (double)a[j+1]*b[j+1]; + double im = (double)a[j+1]*b[j] + (double)a[j]*b[j+1]; + + c[j] = (float)re; + c[j+1] = (float)im; + } + else + for( j = 0; j < cols; j += 2 ) + { + double re = (double)a[j]*b[j] + (double)a[j+1]*b[j+1]; + double im = (double)a[j+1]*b[j] - (double)a[j]*b[j+1]; + + c[j] = (float)re; + c[j+1] = (float)im; + } + } + else + { + const double* a = src1.ptr(i); + const double* b = src2.ptr(i); + double* c = dst.ptr(i); + + if( !(flags & CV_DXT_MUL_CONJ) ) + for( j = 0; j < cols; j += 2 ) + { + double re = a[j]*b[j] - a[j+1]*b[j+1]; + double im = a[j+1]*b[j] + a[j]*b[j+1]; + + c[j] = re; + c[j+1] = im; + } + else + for( j = 0; j < cols; j += 2 ) + { + double re = a[j]*b[j] + a[j+1]*b[j+1]; + double im = a[j+1]*b[j] - a[j]*b[j+1]; + + c[j] = re; + c[j+1] = im; + } + } + } +} + +} + + +class CxCore_DXTBaseTest : public cvtest::ArrayTest +{ +public: + typedef cvtest::ArrayTest Base; + CxCore_DXTBaseTest( bool _allow_complex=false, bool _allow_odd=false, + bool _spectrum_mode=false ); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + int prepare_test_case( int test_case_idx ); + double get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ); + int flags; // transformation flags + bool allow_complex; // whether input/output may be complex or not: + // true for DFT and MulSpectrums, false for DCT + bool allow_odd; // whether input/output may be have odd (!=1) dimensions: + // true for DFT and MulSpectrums, false for DCT + bool spectrum_mode; // (2 complex/ccs inputs, 1 complex/ccs output): + // true for MulSpectrums, false for DFT and DCT + bool inplace; // inplace operation (set for each individual test case) + bool temp_dst; // use temporary destination (for real->ccs DFT and ccs MulSpectrums) +}; + + +CxCore_DXTBaseTest::CxCore_DXTBaseTest( bool _allow_complex, bool _allow_odd, bool _spectrum_mode ) +: Base(), flags(0), allow_complex(_allow_complex), allow_odd(_allow_odd), +spectrum_mode(_spectrum_mode), inplace(false), temp_dst(false) +{ + test_array[INPUT].push_back(NULL); + if( spectrum_mode ) + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[TEMP].push_back(NULL); + test_array[TEMP].push_back(NULL); + + max_log_array_size = 9; + element_wise_relative_error = spectrum_mode; +} + + +void CxCore_DXTBaseTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, + vector >& types ) +{ + RNG& rng = ts->get_rng(); + int bits = cvtest::randInt(rng); + int depth = cvtest::randInt(rng)%2 + CV_32F; + int cn = !allow_complex || !(bits & 256) ? 1 : 2; + Size size; + Base::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + flags = bits & (CV_DXT_INVERSE | CV_DXT_SCALE | CV_DXT_ROWS | CV_DXT_MUL_CONJ); + if( spectrum_mode ) + flags &= ~CV_DXT_INVERSE; + types[TEMP][0] = types[TEMP][1] = types[INPUT][0] = + types[OUTPUT][0] = CV_MAKETYPE(depth, cn); + size = sizes[INPUT][0]; + + temp_dst = false; + + if( flags & CV_DXT_ROWS && (bits&1024) ) + { + if( bits&16 ) + size.width = 1; + else + size.height = 1; + flags &= ~CV_DXT_ROWS; + } + + const int P2_MIN_SIZE = 32; + if( ((bits >> 10) & 1) == 0 ) + { + size.width = (size.width / P2_MIN_SIZE)*P2_MIN_SIZE; + size.width = MAX(size.width, 1); + size.height = (size.height / P2_MIN_SIZE)*P2_MIN_SIZE; + size.height = MAX(size.height, 1); + } + + if( !allow_odd ) + { + if( size.width > 1 && (size.width&1) != 0 ) + size.width = (size.width + 1) & -2; + + if( size.height > 1 && (size.height&1) != 0 && !(flags & CV_DXT_ROWS) ) + size.height = (size.height + 1) & -2; + } + + sizes[INPUT][0] = sizes[OUTPUT][0] = size; + sizes[TEMP][0] = sizes[TEMP][1] = cvSize(0,0); + + if( spectrum_mode ) + { + if( cn == 1 ) + { + types[OUTPUT][0] = depth + 8; + sizes[TEMP][0] = size; + } + sizes[INPUT][0] = sizes[INPUT][1] = size; + types[INPUT][1] = types[INPUT][0]; + } + else if( /*(cn == 2 && (bits&32)) ||*/ (cn == 1 && allow_complex) ) + { + types[TEMP][0] = depth + 8; // CV_??FC2 + sizes[TEMP][0] = size; + size = cvSize(size.width/2+1, size.height); + + if( flags & CV_DXT_INVERSE ) + { + if( cn == 2 ) + { + types[OUTPUT][0] = depth; + sizes[INPUT][0] = size; + } + types[TEMP][1] = types[TEMP][0]; + sizes[TEMP][1] = sizes[TEMP][0]; + } + else + { + if( allow_complex ) + types[OUTPUT][0] = depth + 8; + + if( cn == 2 ) + { + types[INPUT][0] = depth; + types[TEMP][1] = types[TEMP][0]; + sizes[TEMP][1] = size; + } + else + { + types[TEMP][1] = depth; + sizes[TEMP][1] = sizes[TEMP][0]; + } + temp_dst = true; + } + } + + inplace = false; + if( spectrum_mode || + (!temp_dst && types[INPUT][0] == types[OUTPUT][0]) || + (temp_dst && types[INPUT][0] == types[TEMP][1]) ) + inplace = (bits & 64) != 0; + + types[REF_OUTPUT][0] = types[OUTPUT][0]; + sizes[REF_OUTPUT][0] = sizes[OUTPUT][0]; +} + + +double CxCore_DXTBaseTest::get_success_error_level( int test_case_idx, int i, int j ) +{ + return Base::get_success_error_level( test_case_idx, i, j ); +} + + +int CxCore_DXTBaseTest::prepare_test_case( int test_case_idx ) +{ + int code = Base::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + int in_type = test_mat[INPUT][0].type(); + int out_type = test_mat[OUTPUT][0].type(); + + if( CV_MAT_CN(in_type) == 2 && CV_MAT_CN(out_type) == 1 ) + cvtest::fixCCS( test_mat[INPUT][0], test_mat[OUTPUT][0].cols, flags ); + + if( inplace ) + cvtest::copy( test_mat[INPUT][test_case_idx & (int)spectrum_mode], + temp_dst ? test_mat[TEMP][1] : + in_type == out_type ? test_mat[OUTPUT][0] : + test_mat[TEMP][0] ); + } + + return code; +} + + +////////////////////// FFT //////////////////////// +class CxCore_DFTTest : public CxCore_DXTBaseTest +{ +public: + CxCore_DFTTest(); +protected: + void run_func(); + void prepare_to_validation( int test_case_idx ); +}; + + +CxCore_DFTTest::CxCore_DFTTest() : CxCore_DXTBaseTest( true, true, false ) +{ +} + + +void CxCore_DFTTest::run_func() +{ + Mat& dst = temp_dst ? test_mat[TEMP][1] : test_mat[OUTPUT][0]; + const Mat& src = inplace ? dst : test_mat[INPUT][0]; + + if(!(flags & CV_DXT_INVERSE)) + cv::dft( src, dst, flags ); + else + cv::idft(src, dst, flags & ~CV_DXT_INVERSE); +} + + +void CxCore_DFTTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + Mat& src = test_mat[INPUT][0]; + Mat& dst = test_mat[REF_OUTPUT][0]; + Mat* tmp_src = &src; + Mat* tmp_dst = &dst; + int src_cn = src.channels(); + int dst_cn = dst.channels(); + + if( src_cn != 2 || dst_cn != 2 ) + { + tmp_src = &test_mat[TEMP][0]; + + if( !(flags & CV_DXT_INVERSE ) ) + { + Mat& cvdft_dst = test_mat[TEMP][1]; + cvtest::convertFromCCS( cvdft_dst, cvdft_dst, + test_mat[OUTPUT][0], flags ); + *tmp_src = Scalar::all(0); + cvtest::insert( src, *tmp_src, 0 ); + } + else + { + cvtest::convertFromCCS( src, src, *tmp_src, flags ); + tmp_dst = &test_mat[TEMP][1]; + } + } + + if( src.rows == 1 || (src.cols == 1 && !(flags & CV_DXT_ROWS)) ) + cvtest::DFT_1D( *tmp_src, *tmp_dst, flags ); + else + cvtest::DFT_2D( *tmp_src, *tmp_dst, flags ); + + if( tmp_dst != &dst ) + cvtest::extract( *tmp_dst, dst, 0 ); +} + +////////////////////// DCT //////////////////////// +class CxCore_DCTTest : public CxCore_DXTBaseTest +{ +public: + CxCore_DCTTest(); +protected: + void run_func(); + void prepare_to_validation( int test_case_idx ); +}; + + +CxCore_DCTTest::CxCore_DCTTest() : CxCore_DXTBaseTest( false, false, false ) +{ +} + + +void CxCore_DCTTest::run_func() +{ + Mat& dst = test_mat[OUTPUT][0]; + const Mat& src = inplace ? dst : test_mat[INPUT][0]; + + if(!(flags & CV_DXT_INVERSE)) + cv::dct( src, dst, flags ); + else + cv::idct( src, dst, flags & ~CV_DXT_INVERSE); +} + + +void CxCore_DCTTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + const Mat& src = test_mat[INPUT][0]; + Mat& dst = test_mat[REF_OUTPUT][0]; + + if( src.rows == 1 || (src.cols == 1 && !(flags & CV_DXT_ROWS)) ) + cvtest::DCT_1D( src, dst, flags ); + else + cvtest::DCT_2D( src, dst, flags ); +} + + +////////////////////// MulSpectrums //////////////////////// +class CxCore_MulSpectrumsTest : public CxCore_DXTBaseTest +{ +public: + CxCore_MulSpectrumsTest(); +protected: + void run_func(); + void prepare_to_validation( int test_case_idx ); +}; + + +CxCore_MulSpectrumsTest::CxCore_MulSpectrumsTest() : CxCore_DXTBaseTest( true, true, true ) +{ +} + + +void CxCore_MulSpectrumsTest::run_func() +{ + Mat& dst = !test_mat[TEMP].empty() && !test_mat[TEMP][0].empty() ? + test_mat[TEMP][0] : test_mat[OUTPUT][0]; + const Mat* src1 = &test_mat[INPUT][0], *src2 = &test_mat[INPUT][1]; + + if( inplace ) + { + if( ts->get_current_test_info()->test_case_idx & 1 ) + src2 = &dst; + else + src1 = &dst; + } + + cv::mulSpectrums( *src1, *src2, dst, flags, (flags & CV_DXT_MUL_CONJ) != 0 ); +} + + +void CxCore_MulSpectrumsTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + Mat* src1 = &test_mat[INPUT][0]; + Mat* src2 = &test_mat[INPUT][1]; + Mat& dst = test_mat[OUTPUT][0]; + Mat& dst0 = test_mat[REF_OUTPUT][0]; + int cn = src1->channels(); + + if( cn == 1 ) + { + cvtest::convertFromCCS( *src1, *src1, dst, flags ); + cvtest::convertFromCCS( *src2, *src2, dst0, flags ); + src1 = &dst; + src2 = &dst0; + } + + cvtest::mulComplex( *src1, *src2, dst0, flags ); + if( cn == 1 ) + { + Mat& temp = test_mat[TEMP][0]; + cvtest::convertFromCCS( temp, temp, dst, flags ); + } +} + +TEST(Core_DCT, accuracy) { CxCore_DCTTest test; test.safe_run(); } +TEST(Core_DFT, accuracy) { CxCore_DFTTest test; test.safe_run(); } +TEST(Core_MulSpectrums, accuracy) { CxCore_MulSpectrumsTest test; test.safe_run(); } + diff --git a/modules/core/test/test_io.cpp b/modules/core/test/test_io.cpp new file mode 100644 index 000000000..2edcfbd9a --- /dev/null +++ b/modules/core/test/test_io.cpp @@ -0,0 +1,382 @@ +#include "test_precomp.hpp" + +using namespace cv; +using namespace std; + +static SparseMat cvTsGetRandomSparseMat(int dims, const int* sz, int type, + int nzcount, double a, double b, RNG& rng) +{ + SparseMat m(dims, sz, type); + int i, j; + CV_Assert(CV_MAT_CN(type) == 1); + for( i = 0; i < nzcount; i++ ) + { + int idx[CV_MAX_DIM]; + for( j = 0; j < dims; j++ ) + idx[j] = cvtest::randInt(rng) % sz[j]; + double val = cvtest::randReal(rng)*(b - a) + a; + uchar* ptr = m.ptr(idx, true, 0); + if( type == CV_8U ) + *(uchar*)ptr = saturate_cast(val); + else if( type == CV_8S ) + *(schar*)ptr = saturate_cast(val); + else if( type == CV_16U ) + *(ushort*)ptr = saturate_cast(val); + else if( type == CV_16S ) + *(short*)ptr = saturate_cast(val); + else if( type == CV_32S ) + *(int*)ptr = saturate_cast(val); + else if( type == CV_32F ) + *(float*)ptr = saturate_cast(val); + else + *(double*)ptr = saturate_cast(val); + } + + return m; +} + +static bool cvTsCheckSparse(const CvSparseMat* m1, const CvSparseMat* m2, double eps) +{ + CvSparseMatIterator it1; + CvSparseNode* node1; + int depth = CV_MAT_DEPTH(m1->type); + + if( m1->heap->active_count != m2->heap->active_count || + m1->dims != m2->dims || CV_MAT_TYPE(m1->type) != CV_MAT_TYPE(m2->type) ) + return false; + + for( node1 = cvInitSparseMatIterator( m1, &it1 ); + node1 != 0; node1 = cvGetNextSparseNode( &it1 )) + { + uchar* v1 = (uchar*)CV_NODE_VAL(m1,node1); + uchar* v2 = cvPtrND( m2, CV_NODE_IDX(m1,node1), 0, 0, &node1->hashval ); + if( !v2 ) + return false; + if( depth == CV_8U || depth == CV_8S ) + { + if( *v1 != *v2 ) + return false; + } + else if( depth == CV_16U || depth == CV_16S ) + { + if( *(ushort*)v1 != *(ushort*)v2 ) + return false; + } + else if( depth == CV_32S ) + { + if( *(int*)v1 != *(int*)v2 ) + return false; + } + else if( depth == CV_32F ) + { + if( fabs(*(float*)v1 - *(float*)v2) > eps*(fabs(*(float*)v2) + 1) ) + return false; + } + else if( fabs(*(double*)v1 - *(double*)v2) > eps*(fabs(*(double*)v2) + 1) ) + return false; + } + + return true; +} + + +class Core_IOTest : public cvtest::BaseTest +{ +public: + Core_IOTest() {}; +protected: + void run(int) + { + double ranges[][2] = {{0, 256}, {-128, 128}, {0, 65536}, {-32768, 32768}, + {-1000000, 1000000}, {-10, 10}, {-10, 10}}; + RNG& rng = ts->get_rng(); + RNG rng0; + test_case_count = 2; + int progress = 0; + MemStorage storage(cvCreateMemStorage(0)); + + for( int idx = 0; idx < test_case_count; idx++ ) + { + ts->update_context( this, idx, false ); + progress = update_progress( progress, idx, test_case_count, 0 ); + + cvClearMemStorage(storage); + + char buf[L_tmpnam+16]; + char* filename = tmpnam(buf); + strcat(filename, idx % 2 ? ".yml" : ".xml"); + if(filename[0] == '\\') + filename++; + + FileStorage fs(filename, FileStorage::WRITE); + + int test_int = (int)cvtest::randInt(rng); + double test_real = (cvtest::randInt(rng)%2?1:-1)*exp(cvtest::randReal(rng)*18-9); + string test_string = "vw wv23424rt\"&<>&'@#$@$%$%&%IJUKYILFD@#$@%$&*&() "; + + int depth = cvtest::randInt(rng) % (CV_64F+1); + int cn = cvtest::randInt(rng) % 4 + 1; + Mat test_mat(cvtest::randInt(rng)%30+1, cvtest::randInt(rng)%30+1, CV_MAKETYPE(depth, cn)); + + rng0.fill(test_mat, CV_RAND_UNI, Scalar::all(ranges[depth][0]), Scalar::all(ranges[depth][1])); + if( depth >= CV_32F ) + { + exp(test_mat, test_mat); + Mat test_mat_scale(test_mat.size(), test_mat.type()); + rng0.fill(test_mat_scale, CV_RAND_UNI, Scalar::all(-1), Scalar::all(1)); + multiply(test_mat, test_mat_scale, test_mat); + } + + CvSeq* seq = cvCreateSeq(test_mat.type(), (int)sizeof(CvSeq), + (int)test_mat.elemSize(), storage); + cvSeqPushMulti(seq, test_mat.data, test_mat.cols*test_mat.rows); + + CvGraph* graph = cvCreateGraph( CV_ORIENTED_GRAPH, + sizeof(CvGraph), sizeof(CvGraphVtx), + sizeof(CvGraphEdge), storage ); + int edges[][2] = {{0,1},{1,2},{2,0},{0,3},{3,4},{4,1}}; + int i, vcount = 5, ecount = 6; + for( i = 0; i < vcount; i++ ) + cvGraphAddVtx(graph); + for( i = 0; i < ecount; i++ ) + { + CvGraphEdge* edge; + cvGraphAddEdge(graph, edges[i][0], edges[i][1], 0, &edge); + edge->weight = (float)(i+1); + } + + depth = cvtest::randInt(rng) % (CV_64F+1); + cn = cvtest::randInt(rng) % 4 + 1; + int sz[] = {cvtest::randInt(rng)%10+1, cvtest::randInt(rng)%10+1, cvtest::randInt(rng)%10+1}; + MatND test_mat_nd(3, sz, CV_MAKETYPE(depth, cn)); + + rng0.fill(test_mat_nd, CV_RAND_UNI, Scalar::all(ranges[depth][0]), Scalar::all(ranges[depth][1])); + if( depth >= CV_32F ) + { + exp(test_mat_nd, test_mat_nd); + MatND test_mat_scale(test_mat_nd.dims, test_mat_nd.size, test_mat_nd.type()); + rng0.fill(test_mat_scale, CV_RAND_UNI, Scalar::all(-1), Scalar::all(1)); + multiply(test_mat_nd, test_mat_scale, test_mat_nd); + } + + int ssz[] = {cvtest::randInt(rng)%10+1, cvtest::randInt(rng)%10+1, + cvtest::randInt(rng)%10+1,cvtest::randInt(rng)%10+1}; + SparseMat test_sparse_mat = cvTsGetRandomSparseMat(4, ssz, cvtest::randInt(rng)%(CV_64F+1), + cvtest::randInt(rng) % 10000, 0, 100, rng); + + fs << "test_int" << test_int << "test_real" << test_real << "test_string" << test_string; + fs << "test_mat" << test_mat; + fs << "test_mat_nd" << test_mat_nd; + fs << "test_sparse_mat" << test_sparse_mat; + + fs << "test_list" << "[" << 0.0000000000001 << 2 << CV_PI << -3435345 << "2-502 2-029 3egegeg" << + "{:" << "month" << 12 << "day" << 31 << "year" << 1969 << "}" << "]"; + fs << "test_map" << "{" << "x" << 1 << "y" << 2 << "width" << 100 << "height" << 200 << "lbp" << "[:"; + + const uchar arr[] = {0, 1, 1, 0, 1, 1, 0, 1}; + fs.writeRaw("u", arr, (int)(sizeof(arr)/sizeof(arr[0]))); + + fs << "]" << "}"; + cvWriteComment(*fs, "test comment", 0); + + fs.writeObj("test_seq", seq); + fs.writeObj("test_graph",graph); + CvGraph* graph2 = (CvGraph*)cvClone(graph); + + fs.release(); + + if(!fs.open(filename, FileStorage::READ)) + { + ts->printf( cvtest::TS::LOG, "filename %s can not be read\n", filename ); + ts->set_failed_test_info( cvtest::TS::FAIL_MISSING_TEST_DATA ); + return; + } + + int real_int = (int)fs["test_int"]; + double real_real = (double)fs["test_real"]; + string real_string = (string)fs["test_string"]; + + if( real_int != test_int || + fabs(real_real - test_real) > DBL_EPSILON*(fabs(test_real)+1) || + real_string != test_string ) + { + ts->printf( cvtest::TS::LOG, "the read scalars are not correct\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + CvMat* m = (CvMat*)fs["test_mat"].readObj(); + CvMat _test_mat = test_mat; + double max_diff = 0; + CvMat stub1, _test_stub1; + cvReshape(m, &stub1, 1, 0); + cvReshape(&_test_mat, &_test_stub1, 1, 0); + vector pt; + + if( !m || !CV_IS_MAT(m) || m->rows != test_mat.rows || m->cols != test_mat.cols || + cvtest::cmpEps( Mat(&stub1), Mat(&_test_stub1), &max_diff, 0, &pt, true) < 0 ) + { + ts->printf( cvtest::TS::LOG, "the read matrix is not correct: (%.20g vs %.20g) at (%d,%d)\n", + cvGetReal2D(&stub1, pt[0], pt[1]), cvGetReal2D(&_test_stub1, pt[0], pt[1]), + pt[0], pt[1] ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + if( m && CV_IS_MAT(m)) + cvReleaseMat(&m); + + CvMatND* m_nd = (CvMatND*)fs["test_mat_nd"].readObj(); + CvMatND _test_mat_nd = test_mat_nd; + + if( !m_nd || !CV_IS_MATND(m_nd) ) + { + ts->printf( cvtest::TS::LOG, "the read nd-matrix is not correct\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + CvMat stub, _test_stub; + cvGetMat(m_nd, &stub, 0, 1); + cvGetMat(&_test_mat_nd, &_test_stub, 0, 1); + cvReshape(&stub, &stub1, 1, 0); + cvReshape(&_test_stub, &_test_stub1, 1, 0); + + if( !CV_ARE_TYPES_EQ(&stub, &_test_stub) || + !CV_ARE_SIZES_EQ(&stub, &_test_stub) || + //cvNorm(&stub, &_test_stub, CV_L2) != 0 ) + cvtest::cmpEps( Mat(&stub1), Mat(&_test_stub1), &max_diff, 0, &pt, true) < 0 ) + { + ts->printf( cvtest::TS::LOG, "readObj method: the read nd matrix is not correct: (%.20g vs %.20g) vs at (%d,%d)\n", + cvGetReal2D(&stub1, pt[0], pt[1]), cvGetReal2D(&_test_stub1, pt[0], pt[1]), + pt[0], pt[1] ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + MatND mat_nd2; + fs["test_mat_nd"] >> mat_nd2; + CvMatND m_nd2 = mat_nd2; + cvGetMat(&m_nd2, &stub, 0, 1); + cvReshape(&stub, &stub1, 1, 0); + + if( !CV_ARE_TYPES_EQ(&stub, &_test_stub) || + !CV_ARE_SIZES_EQ(&stub, &_test_stub) || + //cvNorm(&stub, &_test_stub, CV_L2) != 0 ) + cvtest::cmpEps( Mat(&stub1), Mat(&_test_stub1), &max_diff, 0, &pt, true) < 0 ) + { + ts->printf( cvtest::TS::LOG, "C++ method: the read nd matrix is not correct: (%.20g vs %.20g) vs at (%d,%d)\n", + cvGetReal2D(&stub1, pt[0], pt[1]), cvGetReal2D(&_test_stub1, pt[1], pt[0]), + pt[0], pt[1] ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + cvRelease((void**)&m_nd); + + Ptr m_s = (CvSparseMat*)fs["test_sparse_mat"].readObj(); + Ptr _test_sparse_ = (CvSparseMat*)test_sparse_mat; + Ptr _test_sparse = (CvSparseMat*)cvClone(_test_sparse_); + SparseMat m_s2; + fs["test_sparse_mat"] >> m_s2; + Ptr _m_s2 = (CvSparseMat*)m_s2; + + if( !m_s || !CV_IS_SPARSE_MAT(m_s) || + !cvTsCheckSparse(m_s, _test_sparse,0) || + !cvTsCheckSparse(_m_s2, _test_sparse,0)) + { + ts->printf( cvtest::TS::LOG, "the read sparse matrix is not correct\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + FileNode tl = fs["test_list"]; + if( tl.type() != FileNode::SEQ || tl.size() != 6 || + fabs((double)tl[0] - 0.0000000000001) >= DBL_EPSILON || + (int)tl[1] != 2 || + fabs((double)tl[2] - CV_PI) >= DBL_EPSILON || + (int)tl[3] != -3435345 || + (string)tl[4] != "2-502 2-029 3egegeg" || + tl[5].type() != FileNode::MAP || tl[5].size() != 3 || + (int)tl[5]["month"] != 12 || + (int)tl[5]["day"] != 31 || + (int)tl[5]["year"] != 1969 ) + { + ts->printf( cvtest::TS::LOG, "the test list is incorrect\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + FileNode tm = fs["test_map"]; + FileNode tm_lbp = tm["lbp"]; + + int real_x = (int)tm["x"]; + int real_y = (int)tm["y"]; + int real_width = (int)tm["width"]; + int real_height = (int)tm["height"]; + + + int real_lbp_val = 0; + FileNodeIterator it; + it = tm_lbp.begin(); + real_lbp_val |= (int)*it << 0; + ++it; + real_lbp_val |= (int)*it << 1; + it++; + real_lbp_val |= (int)*it << 2; + it += 1; + real_lbp_val |= (int)*it << 3; + FileNodeIterator it2(it); + it2 += 4; + real_lbp_val |= (int)*it2 << 7; + --it2; + real_lbp_val |= (int)*it2 << 6; + it2--; + real_lbp_val |= (int)*it2 << 5; + it2 -= 1; + real_lbp_val |= (int)*it2 << 4; + it2 += -1; + CV_Assert( it == it2 ); + + if( tm.type() != FileNode::MAP || tm.size() != 5 || + real_x != 1 || + real_y != 2 || + real_width != 100 || + real_height != 200 || + tm_lbp.type() != FileNode::SEQ || + tm_lbp.size() != 8 || + real_lbp_val != 0xb6 ) + { + ts->printf( cvtest::TS::LOG, "the test map is incorrect\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + CvGraph* graph3 = (CvGraph*)fs["test_graph"].readObj(); + if(graph2->active_count != vcount || graph3->active_count != vcount || + graph2->edges->active_count != ecount || graph3->edges->active_count != ecount) + { + ts->printf( cvtest::TS::LOG, "the cloned or read graph have wrong number of vertices or edges\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + for( i = 0; i < ecount; i++ ) + { + CvGraphEdge* edge2 = cvFindGraphEdge(graph2, edges[i][0], edges[i][1]); + CvGraphEdge* edge3 = cvFindGraphEdge(graph3, edges[i][0], edges[i][1]); + if( !edge2 || edge2->weight != (float)(i+1) || + !edge3 || edge3->weight != (float)(i+1) ) + { + ts->printf( cvtest::TS::LOG, "the cloned or read graph do not have the edge (%d, %d)\n", edges[i][0], edges[i][1] ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + } + + fs.release(); + remove(filename); + } + } +}; + +TEST(Core_InputOutput, write_read_consistency) { Core_IOTest test; test.safe_run(); } diff --git a/modules/core/test/test_main.cpp b/modules/core/test/test_main.cpp index c2cc775a8..6b2499344 100644 --- a/modules/core/test/test_main.cpp +++ b/modules/core/test/test_main.cpp @@ -1,2 +1,3 @@ #include "test_precomp.hpp" -#include "opencv2/gtest/gtest_main.hpp" + +CV_TEST_MAIN("cv") diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp new file mode 100644 index 000000000..ae5f4429f --- /dev/null +++ b/modules/core/test/test_mat.cpp @@ -0,0 +1,811 @@ +#include "test_precomp.hpp" + +using namespace cv; +using namespace std; + + +class Core_ReduceTest : public cvtest::BaseTest +{ +public: + Core_ReduceTest() {}; +protected: + void run( int); + int checkOp( const Mat& src, int dstType, int opType, const Mat& opRes, int dim, double eps ); + int checkCase( int srcType, int dstType, int dim, Size sz ); + int checkDim( int dim, Size sz ); + int checkSize( Size sz ); +}; + +template +void testReduce( const Mat& src, Mat& sum, Mat& avg, Mat& max, Mat& min, int dim ) +{ + assert( src.channels() == 1 ); + if( dim == 0 ) // row + { + sum.create( 1, src.cols, CV_64FC1 ); + max.create( 1, src.cols, CV_64FC1 ); + min.create( 1, src.cols, CV_64FC1 ); + } + else + { + sum.create( src.rows, 1, CV_64FC1 ); + max.create( src.rows, 1, CV_64FC1 ); + min.create( src.rows, 1, CV_64FC1 ); + } + sum.setTo(Scalar(0)); + max.setTo(Scalar(-DBL_MAX)); + min.setTo(Scalar(DBL_MAX)); + + const Mat_& src_ = src; + Mat_& sum_ = (Mat_&)sum; + Mat_& min_ = (Mat_&)min; + Mat_& max_ = (Mat_&)max; + + if( dim == 0 ) + { + for( int ri = 0; ri < src.rows; ri++ ) + { + for( int ci = 0; ci < src.cols; ci++ ) + { + sum_(0, ci) += src_(ri, ci); + max_(0, ci) = std::max( max_(0, ci), (double)src_(ri, ci) ); + min_(0, ci) = std::min( min_(0, ci), (double)src_(ri, ci) ); + } + } + } + else + { + for( int ci = 0; ci < src.cols; ci++ ) + { + for( int ri = 0; ri < src.rows; ri++ ) + { + sum_(ri, 0) += src_(ri, ci); + max_(ri, 0) = std::max( max_(ri, 0), (double)src_(ri, ci) ); + min_(ri, 0) = std::min( min_(ri, 0), (double)src_(ri, ci) ); + } + } + } + sum.convertTo( avg, CV_64FC1 ); + avg = avg * (1.0 / (dim==0 ? (double)src.rows : (double)src.cols)); +} + +void getMatTypeStr( int type, string& str) +{ + str = type == CV_8UC1 ? "CV_8UC1" : + type == CV_8SC1 ? "CV_8SC1" : + type == CV_16UC1 ? "CV_16UC1" : + type == CV_16SC1 ? "CV_16SC1" : + type == CV_32SC1 ? "CV_32SC1" : + type == CV_32FC1 ? "CV_32FC1" : + type == CV_64FC1 ? "CV_64FC1" : "unsupported matrix type"; +} + +int Core_ReduceTest::checkOp( const Mat& src, int dstType, int opType, const Mat& opRes, int dim, double eps ) +{ + int srcType = src.type(); + bool support = false; + if( opType == CV_REDUCE_SUM || opType == CV_REDUCE_AVG ) + { + if( srcType == CV_8U && (dstType == CV_32S || dstType == CV_32F || dstType == CV_64F) ) + support = true; + if( srcType == CV_16U && (dstType == CV_32F || dstType == CV_64F) ) + support = true; + if( srcType == CV_16S && (dstType == CV_32F || dstType == CV_64F) ) + support = true; + if( srcType == CV_32F && (dstType == CV_32F || dstType == CV_64F) ) + support = true; + if( srcType == CV_64F && dstType == CV_64F) + support = true; + } + else if( opType == CV_REDUCE_MAX ) + { + if( srcType == CV_8U && dstType == CV_8U ) + support = true; + if( srcType == CV_32F && dstType == CV_32F ) + support = true; + if( srcType == CV_64F && dstType == CV_64F ) + support = true; + } + else if( opType == CV_REDUCE_MIN ) + { + if( srcType == CV_8U && dstType == CV_8U) + support = true; + if( srcType == CV_32F && dstType == CV_32F) + support = true; + if( srcType == CV_64F && dstType == CV_64F) + support = true; + } + if( !support ) + return cvtest::TS::OK; + + assert( opRes.type() == CV_64FC1 ); + Mat _dst, dst; + reduce( src, _dst, dim, opType, dstType ); + _dst.convertTo( dst, CV_64FC1 ); + if( norm( opRes, dst, NORM_INF ) > eps ) + { + char msg[100]; + const char* opTypeStr = opType == CV_REDUCE_SUM ? "CV_REDUCE_SUM" : + opType == CV_REDUCE_AVG ? "CV_REDUCE_AVG" : + opType == CV_REDUCE_MAX ? "CV_REDUCE_MAX" : + opType == CV_REDUCE_MIN ? "CV_REDUCE_MIN" : "unknown operation type"; + string srcTypeStr, dstTypeStr; + getMatTypeStr( src.type(), srcTypeStr ); + getMatTypeStr( dstType, dstTypeStr ); + const char* dimStr = dim == 0 ? "ROWS" : "COLS"; + + sprintf( msg, "bad accuracy with srcType = %s, dstType = %s, opType = %s, dim = %s", + srcTypeStr.c_str(), dstTypeStr.c_str(), opTypeStr, dimStr ); + ts->printf( cvtest::TS::LOG, msg ); + return cvtest::TS::FAIL_BAD_ACCURACY; + } + return cvtest::TS::OK; +} + +int Core_ReduceTest::checkCase( int srcType, int dstType, int dim, Size sz ) +{ + int code = cvtest::TS::OK, tempCode; + Mat src, sum, avg, max, min; + + src.create( sz, srcType ); + randu( src, Scalar(0), Scalar(100) ); + + if( srcType == CV_8UC1 ) + testReduce( src, sum, avg, max, min, dim ); + else if( srcType == CV_8SC1 ) + testReduce( src, sum, avg, max, min, dim ); + else if( srcType == CV_16UC1 ) + testReduce( src, sum, avg, max, min, dim ); + else if( srcType == CV_16SC1 ) + testReduce( src, sum, avg, max, min, dim ); + else if( srcType == CV_32SC1 ) + testReduce( src, sum, avg, max, min, dim ); + else if( srcType == CV_32FC1 ) + testReduce( src, sum, avg, max, min, dim ); + else if( srcType == CV_64FC1 ) + testReduce( src, sum, avg, max, min, dim ); + else + assert( 0 ); + + // 1. sum + tempCode = checkOp( src, dstType, CV_REDUCE_SUM, sum, dim, + srcType == CV_32FC1 && dstType == CV_32FC1 ? 0.05 : FLT_EPSILON ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + // 2. avg + tempCode = checkOp( src, dstType, CV_REDUCE_AVG, avg, dim, + dstType == CV_32SC1 ? 0.6 : 0.00007 ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + // 3. max + tempCode = checkOp( src, dstType, CV_REDUCE_MAX, max, dim, FLT_EPSILON ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + // 4. min + tempCode = checkOp( src, dstType, CV_REDUCE_MIN, min, dim, FLT_EPSILON ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + return code; +} + +int Core_ReduceTest::checkDim( int dim, Size sz ) +{ + int code = cvtest::TS::OK, tempCode; + + // CV_8UC1 + tempCode = checkCase( CV_8UC1, CV_8UC1, dim, sz ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + tempCode = checkCase( CV_8UC1, CV_32SC1, dim, sz ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + tempCode = checkCase( CV_8UC1, CV_32FC1, dim, sz ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + tempCode = checkCase( CV_8UC1, CV_64FC1, dim, sz ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + // CV_16UC1 + tempCode = checkCase( CV_16UC1, CV_32FC1, dim, sz ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + tempCode = checkCase( CV_16UC1, CV_64FC1, dim, sz ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + // CV_16SC1 + tempCode = checkCase( CV_16SC1, CV_32FC1, dim, sz ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + tempCode = checkCase( CV_16SC1, CV_64FC1, dim, sz ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + // CV_32FC1 + tempCode = checkCase( CV_32FC1, CV_32FC1, dim, sz ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + tempCode = checkCase( CV_32FC1, CV_64FC1, dim, sz ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + // CV_64FC1 + tempCode = checkCase( CV_64FC1, CV_64FC1, dim, sz ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + return code; +} + +int Core_ReduceTest::checkSize( Size sz ) +{ + int code = cvtest::TS::OK, tempCode; + + tempCode = checkDim( 0, sz ); // rows + code = tempCode != cvtest::TS::OK ? tempCode : code; + + tempCode = checkDim( 1, sz ); // cols + code = tempCode != cvtest::TS::OK ? tempCode : code; + + return code; +} + +void Core_ReduceTest::run( int ) +{ + int code = cvtest::TS::OK, tempCode; + + tempCode = checkSize( Size(1,1) ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + tempCode = checkSize( Size(1,100) ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + tempCode = checkSize( Size(100,1) ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + tempCode = checkSize( Size(1000,500) ); + code = tempCode != cvtest::TS::OK ? tempCode : code; + + ts->set_failed_test_info( code ); +} + + +#define CHECK_C + +Size sz(200, 500); + +class Core_PCATest : public cvtest::BaseTest +{ +public: + Core_PCATest() {} +protected: + void run(int) + { + int code = cvtest::TS::OK; + + double diffPrjEps, diffBackPrjEps, + prjEps, backPrjEps, + evalEps, evecEps; + int maxComponents = 100; + Mat rPoints(sz, CV_32FC1), rTestPoints(sz, CV_32FC1); + RNG& rng = ts->get_rng(); + + rng.fill( rPoints, RNG::UNIFORM, Scalar::all(0.0), Scalar::all(1.0) ); + rng.fill( rTestPoints, RNG::UNIFORM, Scalar::all(0.0), Scalar::all(1.0) ); + + PCA rPCA( rPoints, Mat(), CV_PCA_DATA_AS_ROW, maxComponents ), cPCA; + + // 1. check C++ PCA & ROW + Mat rPrjTestPoints = rPCA.project( rTestPoints ); + Mat rBackPrjTestPoints = rPCA.backProject( rPrjTestPoints ); + + Mat avg(1, sz.width, CV_32FC1 ); + reduce( rPoints, avg, 0, CV_REDUCE_AVG ); + Mat Q = rPoints - repeat( avg, rPoints.rows, 1 ), Qt = Q.t(), eval, evec; + Q = Qt * Q; + Q = Q /(float)rPoints.rows; + + eigen( Q, eval, evec ); + /*SVD svd(Q); + evec = svd.vt; + eval = svd.w;*/ + + Mat subEval( maxComponents, 1, eval.type(), eval.data ), + subEvec( maxComponents, evec.cols, evec.type(), evec.data ); + + #ifdef CHECK_C + Mat prjTestPoints, backPrjTestPoints, cPoints = rPoints.t(), cTestPoints = rTestPoints.t(); + CvMat _points, _testPoints, _avg, _eval, _evec, _prjTestPoints, _backPrjTestPoints; + #endif + + // check eigen() + double eigenEps = 1e-6; + double err; + for(int i = 0; i < Q.rows; i++ ) + { + Mat v = evec.row(i).t(); + Mat Qv = Q * v; + + Mat lv = eval.at(i,0) * v; + err = norm( Qv, lv ); + if( err > eigenEps ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy of eigen(); err = %f\n", err ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto exit_func; + } + } + // check pca eigenvalues + evalEps = 1e-6, evecEps = 1; + err = norm( rPCA.eigenvalues, subEval ); + if( err > evalEps ) + { + ts->printf( cvtest::TS::LOG, "pca.eigenvalues is incorrect (CV_PCA_DATA_AS_ROW); err = %f\n", err ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto exit_func; + } + // check pca eigenvectors + err = norm( rPCA.eigenvectors, subEvec, CV_RELATIVE_L2 ); + if( err > evecEps ) + { + ts->printf( cvtest::TS::LOG, "pca.eigenvectors is incorrect (CV_PCA_DATA_AS_ROW); err = %f\n", err ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto exit_func; + } + + prjEps = 1.265, backPrjEps = 1.265; + for( int i = 0; i < rTestPoints.rows; i++ ) + { + // check pca project + Mat subEvec_t = subEvec.t(); + Mat prj = rTestPoints.row(i) - avg; prj *= subEvec_t; + err = norm(rPrjTestPoints.row(i), prj, CV_RELATIVE_L2); + if( err > prjEps ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy of project() (CV_PCA_DATA_AS_ROW); err = %f\n", err ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto exit_func; + } + // check pca backProject + Mat backPrj = rPrjTestPoints.row(i) * subEvec + avg; + err = norm( rBackPrjTestPoints.row(i), backPrj, CV_RELATIVE_L2 ); + if( err > backPrjEps ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy of backProject() (CV_PCA_DATA_AS_ROW); err = %f\n", err ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto exit_func; + } + } + + // 2. check C++ PCA & COL + cPCA( rPoints.t(), Mat(), CV_PCA_DATA_AS_COL, maxComponents ); + diffPrjEps = 1, diffBackPrjEps = 1; + err = norm(cPCA.project(rTestPoints.t()), rPrjTestPoints.t(), CV_RELATIVE_L2 ); + if( err > diffPrjEps ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy of project() (CV_PCA_DATA_AS_COL); err = %f\n", err ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto exit_func; + } + err = norm(cPCA.backProject(rPrjTestPoints.t()), rBackPrjTestPoints.t(), CV_RELATIVE_L2 ); + if( err > diffBackPrjEps ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy of backProject() (CV_PCA_DATA_AS_COL); err = %f\n", err ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto exit_func; + } + + #ifdef CHECK_C + // 3. check C PCA & ROW + _points = rPoints; + _testPoints = rTestPoints; + _avg = avg; + _eval = eval; + _evec = evec; + prjTestPoints.create(rTestPoints.rows, maxComponents, rTestPoints.type() ); + backPrjTestPoints.create(rPoints.size(), rPoints.type() ); + _prjTestPoints = prjTestPoints; + _backPrjTestPoints = backPrjTestPoints; + + cvCalcPCA( &_points, &_avg, &_eval, &_evec, CV_PCA_DATA_AS_ROW ); + cvProjectPCA( &_testPoints, &_avg, &_evec, &_prjTestPoints ); + cvBackProjectPCA( &_prjTestPoints, &_avg, &_evec, &_backPrjTestPoints ); + + err = norm(prjTestPoints, rPrjTestPoints, CV_RELATIVE_L2); + if( err > diffPrjEps ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy of cvProjectPCA() (CV_PCA_DATA_AS_ROW); err = %f\n", err ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto exit_func; + } + err = norm(backPrjTestPoints, rBackPrjTestPoints, CV_RELATIVE_L2); + if( err > diffBackPrjEps ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy of cvBackProjectPCA() (CV_PCA_DATA_AS_ROW); err = %f\n", err ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto exit_func; + } + + // 3. check C PCA & COL + _points = cPoints; + _testPoints = cTestPoints; + avg = avg.t(); _avg = avg; + eval = eval.t(); _eval = eval; + evec = evec.t(); _evec = evec; + prjTestPoints = prjTestPoints.t(); _prjTestPoints = prjTestPoints; + backPrjTestPoints = backPrjTestPoints.t(); _backPrjTestPoints = backPrjTestPoints; + + cvCalcPCA( &_points, &_avg, &_eval, &_evec, CV_PCA_DATA_AS_COL ); + cvProjectPCA( &_testPoints, &_avg, &_evec, &_prjTestPoints ); + cvBackProjectPCA( &_prjTestPoints, &_avg, &_evec, &_backPrjTestPoints ); + + err = norm(prjTestPoints, rPrjTestPoints.t(), CV_RELATIVE_L2 ); + if( err > diffPrjEps ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy of cvProjectPCA() (CV_PCA_DATA_AS_COL); err = %f\n", err ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto exit_func; + } + err = norm(backPrjTestPoints, rBackPrjTestPoints.t(), CV_RELATIVE_L2); + if( err > diffBackPrjEps ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy of cvBackProjectPCA() (CV_PCA_DATA_AS_COL); err = %f\n", err ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto exit_func; + } + #endif + + exit_func: + + RNG& _rng = ts->get_rng(); + _rng = rng; + ts->set_failed_test_info( code ); + } +}; + +class Core_ArrayOpTest : public cvtest::BaseTest +{ +public: + Core_ArrayOpTest(); + ~Core_ArrayOpTest(); +protected: + void run(int); +}; + + +Core_ArrayOpTest::Core_ArrayOpTest() +{ +} +Core_ArrayOpTest::~Core_ArrayOpTest() {} + +static string idx2string(const int* idx, int dims) +{ + char buf[256]; + char* ptr = buf; + for( int k = 0; k < dims; k++ ) + { + sprintf(ptr, "%4d ", idx[k]); + ptr += strlen(ptr); + } + ptr[-1] = '\0'; + return string(buf); +} + +static const int* string2idx(const string& s, int* idx, int dims) +{ + const char* ptr = s.c_str(); + for( int k = 0; k < dims; k++ ) + { + int n = 0; + sscanf(ptr, "%d%n", idx + k, &n); + ptr += n; + } + return idx; +} + +static double getValue(SparseMat& M, const int* idx, RNG& rng) +{ + int d = M.dims(); + size_t hv = 0, *phv = 0; + if( (unsigned)rng % 2 ) + { + hv = d == 2 ? M.hash(idx[0], idx[1]) : + d == 3 ? M.hash(idx[0], idx[1], idx[2]) : M.hash(idx); + phv = &hv; + } + + const uchar* ptr = d == 2 ? M.ptr(idx[0], idx[1], false, phv) : + d == 3 ? M.ptr(idx[0], idx[1], idx[2], false, phv) : + M.ptr(idx, false, phv); + return !ptr ? 0 : M.type() == CV_32F ? *(float*)ptr : M.type() == CV_64F ? *(double*)ptr : 0; +} + +static double getValue(const CvSparseMat* M, const int* idx) +{ + int type = 0; + const uchar* ptr = cvPtrND(M, idx, &type, 0); + return !ptr ? 0 : type == CV_32F ? *(float*)ptr : type == CV_64F ? *(double*)ptr : 0; +} + +static void eraseValue(SparseMat& M, const int* idx, RNG& rng) +{ + int d = M.dims(); + size_t hv = 0, *phv = 0; + if( (unsigned)rng % 2 ) + { + hv = d == 2 ? M.hash(idx[0], idx[1]) : + d == 3 ? M.hash(idx[0], idx[1], idx[2]) : M.hash(idx); + phv = &hv; + } + + if( d == 2 ) + M.erase(idx[0], idx[1], phv); + else if( d == 3 ) + M.erase(idx[0], idx[1], idx[2], phv); + else + M.erase(idx, phv); +} + +static void eraseValue(CvSparseMat* M, const int* idx) +{ + cvClearND(M, idx); +} + +static void setValue(SparseMat& M, const int* idx, double value, RNG& rng) +{ + int d = M.dims(); + size_t hv = 0, *phv = 0; + if( (unsigned)rng % 2 ) + { + hv = d == 2 ? M.hash(idx[0], idx[1]) : + d == 3 ? M.hash(idx[0], idx[1], idx[2]) : M.hash(idx); + phv = &hv; + } + + uchar* ptr = d == 2 ? M.ptr(idx[0], idx[1], true, phv) : + d == 3 ? M.ptr(idx[0], idx[1], idx[2], true, phv) : + M.ptr(idx, true, phv); + if( M.type() == CV_32F ) + *(float*)ptr = (float)value; + else if( M.type() == CV_64F ) + *(double*)ptr = value; + else + CV_Error(CV_StsUnsupportedFormat, ""); +} + +void Core_ArrayOpTest::run( int /* start_from */) +{ + int errcount = 0; + + // dense matrix operations + { + int sz3[] = {5, 10, 15}; + MatND A(3, sz3, CV_32F), B(3, sz3, CV_16SC4); + CvMatND matA = A, matB = B; + RNG rng; + rng.fill(A, CV_RAND_UNI, Scalar::all(-10), Scalar::all(10)); + rng.fill(B, CV_RAND_UNI, Scalar::all(-10), Scalar::all(10)); + + int idx0[] = {3,4,5}, idx1[] = {0, 9, 7}; + float val0 = 130; + Scalar val1(-1000, 30, 3, 8); + cvSetRealND(&matA, idx0, val0); + cvSetReal3D(&matA, idx1[0], idx1[1], idx1[2], -val0); + cvSetND(&matB, idx0, val1); + cvSet3D(&matB, idx1[0], idx1[1], idx1[2], -val1); + Ptr matC = cvCloneMatND(&matB); + + if( A.at(idx0[0], idx0[1], idx0[2]) != val0 || + A.at(idx1[0], idx1[1], idx1[2]) != -val0 || + cvGetReal3D(&matA, idx0[0], idx0[1], idx0[2]) != val0 || + cvGetRealND(&matA, idx1) != -val0 || + + Scalar(B.at(idx0[0], idx0[1], idx0[2])) != val1 || + Scalar(B.at(idx1[0], idx1[1], idx1[2])) != -val1 || + Scalar(cvGet3D(matC, idx0[0], idx0[1], idx0[2])) != val1 || + Scalar(cvGetND(matC, idx1)) != -val1 ) + { + ts->printf(cvtest::TS::LOG, "one of cvSetReal3D, cvSetRealND, cvSet3D, cvSetND " + "or the corresponding *Get* functions is not correct\n"); + errcount++; + } + } + + RNG rng; + const int MAX_DIM = 5, MAX_DIM_SZ = 10; + // sparse matrix operations + for( int si = 0; si < 10; si++ ) + { + int depth = (unsigned)rng % 2 == 0 ? CV_32F : CV_64F; + int dims = ((unsigned)rng % MAX_DIM) + 1; + int i, k, size[MAX_DIM]={0}, idx[MAX_DIM]={0}; + vector all_idxs; + vector all_vals; + vector all_vals2; + string sidx, min_sidx, max_sidx; + double min_val=0, max_val=0; + + int p = 1; + for( k = 0; k < dims; k++ ) + { + size[k] = ((unsigned)rng % MAX_DIM_SZ) + 1; + p *= size[k]; + } + SparseMat M( dims, size, depth ); + map M0; + + int nz0 = (unsigned)rng % max(p/5,10); + nz0 = min(max(nz0, 1), p); + all_vals.resize(nz0); + all_vals2.resize(nz0); + Mat_ _all_vals(all_vals), _all_vals2(all_vals2); + rng.fill(_all_vals, CV_RAND_UNI, Scalar(-1000), Scalar(1000)); + if( depth == CV_32F ) + { + Mat _all_vals_f; + _all_vals.convertTo(_all_vals_f, CV_32F); + _all_vals_f.convertTo(_all_vals, CV_64F); + } + _all_vals.convertTo(_all_vals2, _all_vals2.type(), 2); + if( depth == CV_32F ) + { + Mat _all_vals2_f; + _all_vals2.convertTo(_all_vals2_f, CV_32F); + _all_vals2_f.convertTo(_all_vals2, CV_64F); + } + + minMaxLoc(_all_vals, &min_val, &max_val); + double _norm0 = norm(_all_vals, CV_C); + double _norm1 = norm(_all_vals, CV_L1); + double _norm2 = norm(_all_vals, CV_L2); + + for( i = 0; i < nz0; i++ ) + { + for(;;) + { + for( k = 0; k < dims; k++ ) + idx[k] = (unsigned)rng % size[k]; + sidx = idx2string(idx, dims); + if( M0.count(sidx) == 0 ) + break; + } + all_idxs.push_back(sidx); + M0[sidx] = all_vals[i]; + if( all_vals[i] == min_val ) + min_sidx = sidx; + if( all_vals[i] == max_val ) + max_sidx = sidx; + setValue(M, idx, all_vals[i], rng); + double v = getValue(M, idx, rng); + if( v != all_vals[i] ) + { + ts->printf(cvtest::TS::LOG, "%d. immediately after SparseMat[%s]=%.20g the current value is %.20g\n", + i, sidx.c_str(), all_vals[i], v); + errcount++; + break; + } + } + + Ptr M2 = (CvSparseMat*)M; + MatND Md; + M.copyTo(Md); + SparseMat M3; SparseMat(Md).convertTo(M3, Md.type(), 2); + + int nz1 = (int)M.nzcount(), nz2 = (int)M3.nzcount(); + double norm0 = norm(M, CV_C); + double norm1 = norm(M, CV_L1); + double norm2 = norm(M, CV_L2); + double eps = depth == CV_32F ? FLT_EPSILON*100 : DBL_EPSILON*1000; + + if( nz1 != nz0 || nz2 != nz0) + { + errcount++; + ts->printf(cvtest::TS::LOG, "%d: The number of non-zero elements before/after converting to/from dense matrix is not correct: %d/%d (while it should be %d)\n", + si, nz1, nz2, nz0 ); + break; + } + + if( fabs(norm0 - _norm0) > fabs(_norm0)*eps || + fabs(norm1 - _norm1) > fabs(_norm1)*eps || + fabs(norm2 - _norm2) > fabs(_norm2)*eps ) + { + errcount++; + ts->printf(cvtest::TS::LOG, "%d: The norms are different: %.20g/%.20g/%.20g vs %.20g/%.20g/%.20g\n", + si, norm0, norm1, norm2, _norm0, _norm1, _norm2 ); + break; + } + + int n = (unsigned)rng % max(p/5,10); + n = min(max(n, 1), p) + nz0; + + for( i = 0; i < n; i++ ) + { + double val1, val2, val3, val0; + if(i < nz0) + { + sidx = all_idxs[i]; + string2idx(sidx, idx, dims); + val0 = all_vals[i]; + } + else + { + for( k = 0; k < dims; k++ ) + idx[k] = (unsigned)rng % size[k]; + sidx = idx2string(idx, dims); + val0 = M0[sidx]; + } + val1 = getValue(M, idx, rng); + val2 = getValue(M2, idx); + val3 = getValue(M3, idx, rng); + + if( val1 != val0 || val2 != val0 || fabs(val3 - val0*2) > fabs(val0*2)*FLT_EPSILON ) + { + errcount++; + ts->printf(cvtest::TS::LOG, "SparseMat M[%s] = %g/%g/%g (while it should be %g)\n", sidx.c_str(), val1, val2, val3, val0 ); + break; + } + } + + for( i = 0; i < n; i++ ) + { + double val1, val2; + if(i < nz0) + { + sidx = all_idxs[i]; + string2idx(sidx, idx, dims); + } + else + { + for( k = 0; k < dims; k++ ) + idx[k] = (unsigned)rng % size[k]; + sidx = idx2string(idx, dims); + } + eraseValue(M, idx, rng); + eraseValue(M2, idx); + val1 = getValue(M, idx, rng); + val2 = getValue(M2, idx); + if( val1 != 0 || val2 != 0 ) + { + errcount++; + ts->printf(cvtest::TS::LOG, "SparseMat: after deleting M[%s], it is =%g/%g (while it should be 0)\n", sidx.c_str(), val1, val2 ); + break; + } + } + + int nz = (int)M.nzcount(); + if( nz != 0 ) + { + errcount++; + ts->printf(cvtest::TS::LOG, "The number of non-zero elements after removing all the elements = %d (while it should be 0)\n", nz ); + break; + } + + int idx1[MAX_DIM], idx2[MAX_DIM]; + double val1 = 0, val2 = 0; + M3 = SparseMat(Md); + minMaxLoc(M3, &val1, &val2, idx1, idx2); + string s1 = idx2string(idx1, dims), s2 = idx2string(idx2, dims); + if( val1 != min_val || val2 != max_val || s1 != min_sidx || s2 != max_sidx ) + { + errcount++; + ts->printf(cvtest::TS::LOG, "%d. Sparse: The value and positions of minimum/maximum elements are different from the reference values and positions:\n\t" + "(%g, %g, %s, %s) vs (%g, %g, %s, %s)\n", si, val1, val2, s1.c_str(), s2.c_str(), + min_val, max_val, min_sidx.c_str(), max_sidx.c_str()); + break; + } + + minMaxLoc(Md, &val1, &val2, idx1, idx2); + s1 = idx2string(idx1, dims), s2 = idx2string(idx2, dims); + if( (min_val < 0 && (val1 != min_val || s1 != min_sidx)) || + (max_val > 0 && (val2 != max_val || s2 != max_sidx)) ) + { + errcount++; + ts->printf(cvtest::TS::LOG, "%d. Dense: The value and positions of minimum/maximum elements are different from the reference values and positions:\n\t" + "(%g, %g, %s, %s) vs (%g, %g, %s, %s)\n", si, val1, val2, s1.c_str(), s2.c_str(), + min_val, max_val, min_sidx.c_str(), max_sidx.c_str()); + break; + } + } + + ts->set_failed_test_info(errcount == 0 ? cvtest::TS::OK : cvtest::TS::FAIL_INVALID_OUTPUT); +} + +TEST(Core_PCA, accuracy) { Core_PCATest test; test.safe_run(); } +TEST(Core_Reduce, accuracy) { Core_ReduceTest test; test.safe_run(); } +TEST(Core_Array, basic_operations) { Core_ArrayOpTest test; test.safe_run(); } diff --git a/modules/core/test/test_math.cpp b/modules/core/test/test_math.cpp new file mode 100644 index 000000000..1b68a6fb9 --- /dev/null +++ b/modules/core/test/test_math.cpp @@ -0,0 +1,2372 @@ +////////////////////////////////////////////////////////////////////////////////////////// +/////////////////// tests for matrix operations and math functions /////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +#include "test_precomp.hpp" +#include +#include + +using namespace cv; +using namespace std; + +/// !!! NOTE !!! These tests happily avoid overflow cases & out-of-range arguments +/// so that output arrays contain neigher Inf's nor Nan's. +/// Handling such cases would require special modification of check function +/// (validate_test_results) => TBD. +/// Also, need some logarithmic-scale generation of input data. Right now it is done (in some tests) +/// by generating min/max boundaries for random data in logarimithic scale, but +/// within the same test case all the input array elements are of the same order. + +class Core_MathTest : public cvtest::ArrayTest +{ +public: + typedef cvtest::ArrayTest Base; + Core_MathTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, + vector >& types); + double get_success_error_level( int /*test_case_idx*/, int i, int j ); + bool test_nd; +}; + + +Core_MathTest::Core_MathTest() +{ + optional_mask = false; + + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + + test_nd = false; +} + + +double Core_MathTest::get_success_error_level( int /*test_case_idx*/, int i, int j ) +{ + return test_mat[i][j].depth() == CV_32F ? FLT_EPSILON*128 : DBL_EPSILON*1024; +} + + +void Core_MathTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, + vector >& types) +{ + RNG& rng = ts->get_rng(); + int depth = cvtest::randInt(rng)%2 + CV_32F; + int cn = cvtest::randInt(rng) % 4 + 1, type = CV_MAKETYPE(depth, cn); + size_t i, j; + Base::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + for( i = 0; i < test_array.size(); i++ ) + { + size_t count = test_array[i].size(); + for( j = 0; j < count; j++ ) + types[i][j] = type; + } + test_nd = cvtest::randInt(rng)%3 == 0; +} + + +////////// pow ///////////// + +class Core_PowTest : public Core_MathTest +{ +public: + typedef Core_MathTest Base; + Core_PowTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, + vector >& types ); + void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); + void run_func(); + void prepare_to_validation( int test_case_idx ); + double get_success_error_level( int test_case_idx, int i, int j ); + double power; +}; + + +Core_PowTest::Core_PowTest() +{ + power = 0; +} + + +void Core_PowTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, + vector >& types ) +{ + RNG& rng = ts->get_rng(); + int depth = cvtest::randInt(rng) % (CV_64F+1); + int cn = cvtest::randInt(rng) % 4 + 1; + size_t i, j; + Base::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + depth += depth == CV_8S; + + if( depth < CV_32F || cvtest::randInt(rng)%8 == 0 ) + // integer power + power = (int)(cvtest::randInt(rng)%21 - 10); + else + { + i = cvtest::randInt(rng)%17; + power = i == 16 ? 1./3 : i == 15 ? 0.5 : i == 14 ? -0.5 : cvtest::randReal(rng)*10 - 5; + } + + for( i = 0; i < test_array.size(); i++ ) + { + size_t count = test_array[i].size(); + int type = CV_MAKETYPE(depth, cn); + for( j = 0; j < count; j++ ) + types[i][j] = type; + } + test_nd = cvtest::randInt(rng)%3 == 0; +} + + +double Core_PowTest::get_success_error_level( int test_case_idx, int i, int j ) +{ + int depth = test_mat[i][j].depth(); + if( depth < CV_32F ) + return power == cvRound(power) && power >= 0 ? 0 : 1; + else + return Base::get_success_error_level( test_case_idx, i, j ); +} + + +void Core_PowTest::get_minmax_bounds( int /*i*/, int /*j*/, int type, Scalar& low, Scalar& high ) +{ + double l, u = cvtest::randInt(ts->get_rng())%1000 + 1; + if( power > 0 ) + { + double mval = cvtest::getMaxVal(type); + double u1 = pow(mval,1./power)*2; + u = MIN(u,u1); + } + + l = power == cvRound(power) ? -u : FLT_EPSILON; + low = Scalar::all(l); + high = Scalar::all(u); +} + + +void Core_PowTest::run_func() +{ + if(!test_nd) + { + if( fabs(power-1./3) <= DBL_EPSILON && test_mat[INPUT][0].depth() == CV_32F ) + { + Mat a = test_mat[INPUT][0], b = test_mat[OUTPUT][0]; + + a = a.reshape(1); + b = b.reshape(1); + for( int i = 0; i < a.rows; i++ ) + { + b.at(i,0) = (float)fabs(cvCbrt(a.at(i,0))); + for( int j = 1; j < a.cols; j++ ) + b.at(i,j) = (float)fabs(cv::cubeRoot(a.at(i,j))); + } + } + else + cvPow( test_array[INPUT][0], test_array[OUTPUT][0], power ); + } + else + { + Mat& a = test_mat[INPUT][0]; + Mat& b = test_mat[OUTPUT][0]; + if(power == 0.5) + cv::sqrt(a, b); + else + cv::pow(a, power, b); + } +} + + +inline static int ipow( int a, int power ) +{ + int b = 1; + while( power > 0 ) + { + if( power&1 ) + b *= a, power--; + else + a *= a, power >>= 1; + } + return b; +} + + +inline static double ipow( double a, int power ) +{ + double b = 1.; + while( power > 0 ) + { + if( power&1 ) + b *= a, power--; + else + a *= a, power >>= 1; + } + return b; +} + + +void Core_PowTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + const Mat& a = test_mat[INPUT][0]; + Mat& b = test_mat[REF_OUTPUT][0]; + + int depth = a.depth(); + int ncols = a.cols*a.channels(); + int ipower = cvRound(power), apower = abs(ipower); + int i, j; + + for( i = 0; i < a.rows; i++ ) + { + const uchar* a_data = a.ptr(i); + uchar* b_data = b.ptr(i); + + switch( depth ) + { + case CV_8U: + if( ipower < 0 ) + for( j = 0; j < ncols; j++ ) + { + int val = ((uchar*)a_data)[j]; + ((uchar*)b_data)[j] = (uchar)(val <= 1 ? val : + val == 2 && ipower == -1 ? 1 : 0); + } + else + for( j = 0; j < ncols; j++ ) + { + int val = ((uchar*)a_data)[j]; + val = ipow( val, ipower ); + ((uchar*)b_data)[j] = saturate_cast(val); + } + break; + case CV_8S: + if( ipower < 0 ) + for( j = 0; j < ncols; j++ ) + { + int val = ((char*)a_data)[j]; + ((char*)b_data)[j] = (char)((val&~1)==0 ? val : + val ==-1 ? 1-2*(ipower&1) : + val == 2 && ipower == -1 ? 1 : 0); + } + else + for( j = 0; j < ncols; j++ ) + { + int val = ((char*)a_data)[j]; + val = ipow( val, ipower ); + ((char*)b_data)[j] = saturate_cast(val); + } + break; + case CV_16U: + if( ipower < 0 ) + for( j = 0; j < ncols; j++ ) + { + int val = ((ushort*)a_data)[j]; + ((ushort*)b_data)[j] = (ushort)((val&~1)==0 ? val : + val ==-1 ? 1-2*(ipower&1) : + val == 2 && ipower == -1 ? 1 : 0); + } + else + for( j = 0; j < ncols; j++ ) + { + int val = ((ushort*)a_data)[j]; + val = ipow( val, ipower ); + ((ushort*)b_data)[j] = saturate_cast(val); + } + break; + case CV_16S: + if( ipower < 0 ) + for( j = 0; j < ncols; j++ ) + { + int val = ((short*)a_data)[j]; + ((short*)b_data)[j] = (short)((val&~1)==0 ? val : + val ==-1 ? 1-2*(ipower&1) : + val == 2 && ipower == -1 ? 1 : 0); + } + else + for( j = 0; j < ncols; j++ ) + { + int val = ((short*)a_data)[j]; + val = ipow( val, ipower ); + ((short*)b_data)[j] = saturate_cast(val); + } + break; + case CV_32S: + if( ipower < 0 ) + for( j = 0; j < ncols; j++ ) + { + int val = ((int*)a_data)[j]; + ((int*)b_data)[j] = (val&~1)==0 ? val : + val ==-1 ? 1-2*(ipower&1) : + val == 2 && ipower == -1 ? 1 : 0; + } + else + for( j = 0; j < ncols; j++ ) + { + int val = ((int*)a_data)[j]; + val = ipow( val, ipower ); + ((int*)b_data)[j] = val; + } + break; + case CV_32F: + if( power != ipower ) + for( j = 0; j < ncols; j++ ) + { + double val = ((float*)a_data)[j]; + val = pow( fabs(val), power ); + ((float*)b_data)[j] = (float)val; + } + else + for( j = 0; j < ncols; j++ ) + { + double val = ((float*)a_data)[j]; + if( ipower < 0 ) + val = 1./val; + val = ipow( val, apower ); + ((float*)b_data)[j] = (float)val; + } + break; + case CV_64F: + if( power != ipower ) + for( j = 0; j < ncols; j++ ) + { + double val = ((double*)a_data)[j]; + val = pow( fabs(val), power ); + ((double*)b_data)[j] = (double)val; + } + else + for( j = 0; j < ncols; j++ ) + { + double val = ((double*)a_data)[j]; + if( ipower < 0 ) + val = 1./val; + val = ipow( val, apower ); + ((double*)b_data)[j] = (double)val; + } + break; + } + } +} + + + +///////////////////////////////////////// matrix tests //////////////////////////////////////////// + +class Core_MatrixTest : public cvtest::ArrayTest +{ +public: + typedef cvtest::ArrayTest Base; + Core_MatrixTest( int in_count, int out_count, + bool allow_int, bool scalar_output, int max_cn ); +protected: + void get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, + vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + bool allow_int; + bool scalar_output; + int max_cn; +}; + + +Core_MatrixTest::Core_MatrixTest( int in_count, int out_count, + bool _allow_int, bool _scalar_output, int _max_cn ) +: allow_int(_allow_int), scalar_output(_scalar_output), max_cn(_max_cn) +{ + int i; + for( i = 0; i < in_count; i++ ) + test_array[INPUT].push_back(NULL); + + for( i = 0; i < out_count; i++ ) + { + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + } + + element_wise_relative_error = false; +} + + +void Core_MatrixTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, + vector >& types ) +{ + RNG& rng = ts->get_rng(); + int depth = cvtest::randInt(rng) % (allow_int ? CV_64F+1 : 2); + int cn = cvtest::randInt(rng) % max_cn + 1; + size_t i, j; + + if( allow_int ) + depth += depth == CV_8S; + else + depth += CV_32F; + + Base::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + for( i = 0; i < test_array.size(); i++ ) + { + size_t count = test_array[i].size(); + int flag = (i == OUTPUT || i == REF_OUTPUT) && scalar_output; + int type = !flag ? CV_MAKETYPE(depth, cn) : CV_64FC1; + + for( j = 0; j < count; j++ ) + { + types[i][j] = type; + if( flag ) + sizes[i][j] = Size( 4, 1 ); + } + } +} + + +double Core_MatrixTest::get_success_error_level( int test_case_idx, int i, int j ) +{ + int input_depth = test_mat[INPUT][0].depth(); + double input_precision = input_depth < CV_32F ? 0 : input_depth == CV_32F ? 5e-5 : 5e-10; + double output_precision = Base::get_success_error_level( test_case_idx, i, j ); + return MAX(input_precision, output_precision); +} + + +///////////////// Trace ///////////////////// + +class Core_TraceTest : public Core_MatrixTest +{ +public: + Core_TraceTest(); +protected: + void run_func(); + void prepare_to_validation( int test_case_idx ); +}; + + +Core_TraceTest::Core_TraceTest() : Core_MatrixTest( 1, 1, true, true, 4 ) +{ +} + + +void Core_TraceTest::run_func() +{ + test_mat[OUTPUT][0].at(0,0) = cvTrace(test_array[INPUT][0]); +} + + +void Core_TraceTest::prepare_to_validation( int ) +{ + Mat& mat = test_mat[INPUT][0]; + int count = MIN( mat.rows, mat.cols ); + Mat diag(count, 1, mat.type(), mat.data, mat.step + mat.elemSize()); + Scalar r = cvtest::mean(diag); + r *= (double)count; + + test_mat[REF_OUTPUT][0].at(0,0) = r; +} + + +///////// dotproduct ////////// + +class Core_DotProductTest : public Core_MatrixTest +{ +public: + Core_DotProductTest(); +protected: + void run_func(); + void prepare_to_validation( int test_case_idx ); +}; + + +Core_DotProductTest::Core_DotProductTest() : Core_MatrixTest( 2, 1, true, true, 4 ) +{ +} + + +void Core_DotProductTest::run_func() +{ + test_mat[OUTPUT][0].at(0,0) = Scalar(cvDotProduct( test_array[INPUT][0], test_array[INPUT][1] )); +} + + +void Core_DotProductTest::prepare_to_validation( int ) +{ + test_mat[REF_OUTPUT][0].at(0,0) = Scalar(cvtest::crossCorr( test_mat[INPUT][0], test_mat[INPUT][1] )); +} + + +///////// crossproduct ////////// + +class Core_CrossProductTest : public Core_MatrixTest +{ +public: + Core_CrossProductTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, + vector >& types ); + void run_func(); + void prepare_to_validation( int test_case_idx ); +}; + + +Core_CrossProductTest::Core_CrossProductTest() : Core_MatrixTest( 2, 1, false, false, 1 ) +{ +} + + +void Core_CrossProductTest::get_test_array_types_and_sizes( int, + vector >& sizes, + vector >& types ) +{ + RNG& rng = ts->get_rng(); + int depth = cvtest::randInt(rng) % 2 + CV_32F; + int cn = cvtest::randInt(rng) & 1 ? 3 : 1, type = CV_MAKETYPE(depth, cn); + CvSize sz; + + types[INPUT][0] = types[INPUT][1] = types[OUTPUT][0] = types[REF_OUTPUT][0] = type; + + if( cn == 3 ) + sz = Size(1,1); + else if( cvtest::randInt(rng) & 1 ) + sz = Size(3,1); + else + sz = Size(1,3); + + sizes[INPUT][0] = sizes[INPUT][1] = sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = sz; +} + + +void Core_CrossProductTest::run_func() +{ + cvCrossProduct( test_array[INPUT][0], test_array[INPUT][1], test_array[OUTPUT][0] ); +} + + +void Core_CrossProductTest::prepare_to_validation( int ) +{ + CvScalar a = {{0,0,0,0}}, b = {{0,0,0,0}}, c = {{0,0,0,0}}; + + if( test_mat[INPUT][0].rows > 1 ) + { + a.val[0] = cvGetReal2D( test_array[INPUT][0], 0, 0 ); + a.val[1] = cvGetReal2D( test_array[INPUT][0], 1, 0 ); + a.val[2] = cvGetReal2D( test_array[INPUT][0], 2, 0 ); + + b.val[0] = cvGetReal2D( test_array[INPUT][1], 0, 0 ); + b.val[1] = cvGetReal2D( test_array[INPUT][1], 1, 0 ); + b.val[2] = cvGetReal2D( test_array[INPUT][1], 2, 0 ); + } + else if( test_mat[INPUT][0].cols > 1 ) + { + a.val[0] = cvGetReal1D( test_array[INPUT][0], 0 ); + a.val[1] = cvGetReal1D( test_array[INPUT][0], 1 ); + a.val[2] = cvGetReal1D( test_array[INPUT][0], 2 ); + + b.val[0] = cvGetReal1D( test_array[INPUT][1], 0 ); + b.val[1] = cvGetReal1D( test_array[INPUT][1], 1 ); + b.val[2] = cvGetReal1D( test_array[INPUT][1], 2 ); + } + else + { + a = cvGet1D( test_array[INPUT][0], 0 ); + b = cvGet1D( test_array[INPUT][1], 0 ); + } + + c.val[2] = a.val[0]*b.val[1] - a.val[1]*b.val[0]; + c.val[1] = -a.val[0]*b.val[2] + a.val[2]*b.val[0]; + c.val[0] = a.val[1]*b.val[2] - a.val[2]*b.val[1]; + + if( test_mat[REF_OUTPUT][0].rows > 1 ) + { + cvSetReal2D( test_array[REF_OUTPUT][0], 0, 0, c.val[0] ); + cvSetReal2D( test_array[REF_OUTPUT][0], 1, 0, c.val[1] ); + cvSetReal2D( test_array[REF_OUTPUT][0], 2, 0, c.val[2] ); + } + else if( test_mat[REF_OUTPUT][0].cols > 1 ) + { + cvSetReal1D( test_array[REF_OUTPUT][0], 0, c.val[0] ); + cvSetReal1D( test_array[REF_OUTPUT][0], 1, c.val[1] ); + cvSetReal1D( test_array[REF_OUTPUT][0], 2, c.val[2] ); + } + else + { + cvSet1D( test_array[REF_OUTPUT][0], 0, c ); + } +} + + +///////////////// gemm ///////////////////// + +class Core_GEMMTest : public Core_MatrixTest +{ +public: + typedef Core_MatrixTest Base; + Core_GEMMTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ); + int prepare_test_case( int test_case_idx ); + void run_func(); + void prepare_to_validation( int test_case_idx ); + int tabc_flag; + double alpha, beta; +}; + +Core_GEMMTest::Core_GEMMTest() : Core_MatrixTest( 5, 1, false, false, 2 ) +{ + test_case_count = 100; + max_log_array_size = 10; + alpha = beta = 0; +} + + +void Core_GEMMTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + Size sizeA; + Base::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + sizeA = sizes[INPUT][0]; + Base::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + sizes[INPUT][0] = sizeA; + sizes[INPUT][2] = sizes[INPUT][3] = Size(1,1); + types[INPUT][2] = types[INPUT][3] &= ~CV_MAT_CN_MASK; + + tabc_flag = cvtest::randInt(rng) & 7; + + switch( tabc_flag & (CV_GEMM_A_T|CV_GEMM_B_T) ) + { + case 0: + sizes[INPUT][1].height = sizes[INPUT][0].width; + sizes[OUTPUT][0].height = sizes[INPUT][0].height; + sizes[OUTPUT][0].width = sizes[INPUT][1].width; + break; + case CV_GEMM_B_T: + sizes[INPUT][1].width = sizes[INPUT][0].width; + sizes[OUTPUT][0].height = sizes[INPUT][0].height; + sizes[OUTPUT][0].width = sizes[INPUT][1].height; + break; + case CV_GEMM_A_T: + sizes[INPUT][1].height = sizes[INPUT][0].height; + sizes[OUTPUT][0].height = sizes[INPUT][0].width; + sizes[OUTPUT][0].width = sizes[INPUT][1].width; + break; + case CV_GEMM_A_T | CV_GEMM_B_T: + sizes[INPUT][1].width = sizes[INPUT][0].height; + sizes[OUTPUT][0].height = sizes[INPUT][0].width; + sizes[OUTPUT][0].width = sizes[INPUT][1].height; + break; + } + + sizes[REF_OUTPUT][0] = sizes[OUTPUT][0]; + + if( cvtest::randInt(rng) & 1 ) + sizes[INPUT][4] = Size(0,0); + else if( !(tabc_flag & CV_GEMM_C_T) ) + sizes[INPUT][4] = sizes[OUTPUT][0]; + else + { + sizes[INPUT][4].width = sizes[OUTPUT][0].height; + sizes[INPUT][4].height = sizes[OUTPUT][0].width; + } +} + + +int Core_GEMMTest::prepare_test_case( int test_case_idx ) +{ + int code = Base::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + alpha = cvGetReal2D( test_array[INPUT][2], 0, 0 ); + beta = cvGetReal2D( test_array[INPUT][3], 0, 0 ); + } + return code; +} + + +void Core_GEMMTest::get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ) +{ + low = Scalar::all(-10.); + high = Scalar::all(10.); +} + + +void Core_GEMMTest::run_func() +{ + cvGEMM( test_array[INPUT][0], test_array[INPUT][1], alpha, + test_array[INPUT][4], beta, test_array[OUTPUT][0], tabc_flag ); +} + + +void Core_GEMMTest::prepare_to_validation( int ) +{ + cvtest::gemm( test_mat[INPUT][0], test_mat[INPUT][1], alpha, + test_array[INPUT][4] ? test_mat[INPUT][4] : Mat(), + beta, test_mat[REF_OUTPUT][0], tabc_flag ); +} + + +///////////////// multransposed ///////////////////// + +class Core_MulTransposedTest : public Core_MatrixTest +{ +public: + Core_MulTransposedTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ); + void run_func(); + void prepare_to_validation( int test_case_idx ); + int order; +}; + + +Core_MulTransposedTest::Core_MulTransposedTest() : Core_MatrixTest( 2, 1, false, false, 1 ) +{ + test_case_count = 100; + order = 0; + test_array[TEMP].push_back(NULL); +} + + +void Core_MulTransposedTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int bits = cvtest::randInt(rng); + int src_type = cvtest::randInt(rng) % 5; + int dst_type = cvtest::randInt(rng) % 2; + + src_type = src_type == 0 ? CV_8U : src_type == 1 ? CV_16U : src_type == 2 ? CV_16S : + src_type == 3 ? CV_32F : CV_64F; + dst_type = dst_type == 0 ? CV_32F : CV_64F; + dst_type = MAX( dst_type, src_type ); + + Core_MatrixTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + if( bits & 1 ) + sizes[INPUT][1] = Size(0,0); + else + { + sizes[INPUT][1] = sizes[INPUT][0]; + if( bits & 2 ) + sizes[INPUT][1].height = 1; + if( bits & 4 ) + sizes[INPUT][1].width = 1; + } + + sizes[TEMP][0] = sizes[INPUT][0]; + types[INPUT][0] = src_type; + types[OUTPUT][0] = types[REF_OUTPUT][0] = types[INPUT][1] = types[TEMP][0] = dst_type; + + order = (bits & 8) != 0; + sizes[OUTPUT][0].width = sizes[OUTPUT][0].height = order == 0 ? + sizes[INPUT][0].height : sizes[INPUT][0].width; + sizes[REF_OUTPUT][0] = sizes[OUTPUT][0]; +} + + +void Core_MulTransposedTest::get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ) +{ + low = cvScalarAll(-10.); + high = cvScalarAll(10.); +} + + +void Core_MulTransposedTest::run_func() +{ + cvMulTransposed( test_array[INPUT][0], test_array[OUTPUT][0], + order, test_array[INPUT][1] ); +} + + +void Core_MulTransposedTest::prepare_to_validation( int ) +{ + const Mat& src = test_mat[INPUT][0]; + Mat delta = test_mat[INPUT][1]; + Mat& temp = test_mat[TEMP][0]; + if( !delta.empty() ) + { + if( delta.rows < src.rows || delta.cols < src.cols ) + { + cv::repeat( delta, src.rows/delta.rows, src.cols/delta.cols, temp); + delta = temp; + } + cvtest::add( src, 1, delta, -1, Scalar::all(0), temp, temp.type()); + } + else + src.convertTo(temp, temp.type()); + + cvtest::gemm( temp, temp, 1., Mat(), 0, test_mat[REF_OUTPUT][0], order == 0 ? GEMM_2_T : GEMM_1_T ); +} + + +///////////////// Transform ///////////////////// + +class Core_TransformTest : public Core_MatrixTest +{ +public: + typedef Core_MatrixTest Base; + Core_TransformTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + int prepare_test_case( int test_case_idx ); + void run_func(); + void prepare_to_validation( int test_case_idx ); + + double scale; + bool diagMtx; +}; + + +Core_TransformTest::Core_TransformTest() : Core_MatrixTest( 3, 1, true, false, 4 ) +{ +} + + +void Core_TransformTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int bits = cvtest::randInt(rng); + int depth, dst_cn, mat_cols, mattype; + Base::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + mat_cols = CV_MAT_CN(types[INPUT][0]); + depth = CV_MAT_DEPTH(types[INPUT][0]); + dst_cn = cvtest::randInt(rng) % 4 + 1; + types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(depth, dst_cn); + + mattype = depth < CV_32S ? CV_32F : depth == CV_64F ? CV_64F : bits & 1 ? CV_32F : CV_64F; + types[INPUT][1] = mattype; + types[INPUT][2] = CV_MAKETYPE(mattype, dst_cn); + + scale = 1./((cvtest::randInt(rng)%4)*50+1); + + if( bits & 2 ) + { + sizes[INPUT][2] = Size(0,0); + mat_cols += (bits & 4) != 0; + } + else if( bits & 4 ) + sizes[INPUT][2] = Size(1,1); + else + { + if( bits & 8 ) + sizes[INPUT][2] = Size(dst_cn,1); + else + sizes[INPUT][2] = Size(1,dst_cn); + types[INPUT][2] &= ~CV_MAT_CN_MASK; + } + diagMtx = (bits & 16) != 0; + + sizes[INPUT][1] = Size(mat_cols,dst_cn); +} + + +int Core_TransformTest::prepare_test_case( int test_case_idx ) +{ + int code = Base::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + Mat& m = test_mat[INPUT][1]; + cvtest::add(m, scale, m, 0, Scalar::all(0), m, m.type() ); + if(diagMtx) + { + Mat mask = Mat::eye(m.rows, m.cols, CV_8U)*255; + mask = ~mask; + m.setTo(Scalar::all(0), mask); + } + } + return code; +} + + +double Core_TransformTest::get_success_error_level( int test_case_idx, int i, int j ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth <= CV_8S ? 1 : depth <= CV_32S ? 9 : Base::get_success_error_level( test_case_idx, i, j ); +} + +void Core_TransformTest::run_func() +{ + CvMat _m = test_mat[INPUT][1], _shift = test_mat[INPUT][2]; + cvTransform( test_array[INPUT][0], test_array[OUTPUT][0], &_m, _shift.data.ptr ? &_shift : 0); +} + + +void Core_TransformTest::prepare_to_validation( int ) +{ + Mat transmat = test_mat[INPUT][1]; + Mat shift = test_mat[INPUT][2]; + + cvtest::transform( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], transmat, shift ); +} + + +///////////////// PerspectiveTransform ///////////////////// + +class Core_PerspectiveTransformTest : public Core_MatrixTest +{ +public: + Core_PerspectiveTransformTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int test_case_idx ); +}; + + +Core_PerspectiveTransformTest::Core_PerspectiveTransformTest() : Core_MatrixTest( 2, 1, false, false, 2 ) +{ +} + + +void Core_PerspectiveTransformTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int bits = cvtest::randInt(rng); + int depth, cn, mattype; + Core_MatrixTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + cn = CV_MAT_CN(types[INPUT][0]) + 1; + depth = CV_MAT_DEPTH(types[INPUT][0]); + types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(depth, cn); + + mattype = depth == CV_64F ? CV_64F : bits & 1 ? CV_32F : CV_64F; + types[INPUT][1] = mattype; + sizes[INPUT][1] = Size(cn + 1, cn + 1); +} + + +double Core_PerspectiveTransformTest::get_success_error_level( int test_case_idx, int i, int j ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth == CV_32F ? 1e-4 : depth == CV_64F ? 1e-8 : + Core_MatrixTest::get_success_error_level(test_case_idx, i, j); +} + + +void Core_PerspectiveTransformTest::run_func() +{ + CvMat _m = test_mat[INPUT][1]; + cvPerspectiveTransform( test_array[INPUT][0], test_array[OUTPUT][0], &_m ); +} + + +static void cvTsPerspectiveTransform( const CvArr* _src, CvArr* _dst, const CvMat* transmat ) +{ + int i, j, cols; + int cn, depth, mat_depth; + CvMat astub, bstub, *a, *b; + double mat[16]; + + a = cvGetMat( _src, &astub, 0, 0 ); + b = cvGetMat( _dst, &bstub, 0, 0 ); + + cn = CV_MAT_CN(a->type); + depth = CV_MAT_DEPTH(a->type); + mat_depth = CV_MAT_DEPTH(transmat->type); + cols = transmat->cols; + + // prepare cn x (cn + 1) transform matrix + if( mat_depth == CV_32F ) + { + for( i = 0; i < transmat->rows; i++ ) + for( j = 0; j < cols; j++ ) + mat[i*cols + j] = ((float*)(transmat->data.ptr + transmat->step*i))[j]; + } + else + { + assert( mat_depth == CV_64F ); + for( i = 0; i < transmat->rows; i++ ) + for( j = 0; j < cols; j++ ) + mat[i*cols + j] = ((double*)(transmat->data.ptr + transmat->step*i))[j]; + } + + // transform data + cols = a->cols * cn; + vector buf(cols); + + for( i = 0; i < a->rows; i++ ) + { + uchar* src = a->data.ptr + i*a->step; + uchar* dst = b->data.ptr + i*b->step; + + switch( depth ) + { + case CV_32F: + for( j = 0; j < cols; j++ ) + buf[j] = ((float*)src)[j]; + break; + case CV_64F: + for( j = 0; j < cols; j++ ) + buf[j] = ((double*)src)[j]; + break; + default: + assert(0); + } + + switch( cn ) + { + case 2: + for( j = 0; j < cols; j += 2 ) + { + double t0 = buf[j]*mat[0] + buf[j+1]*mat[1] + mat[2]; + double t1 = buf[j]*mat[3] + buf[j+1]*mat[4] + mat[5]; + double w = buf[j]*mat[6] + buf[j+1]*mat[7] + mat[8]; + w = w ? 1./w : 0; + buf[j] = t0*w; + buf[j+1] = t1*w; + } + break; + case 3: + for( j = 0; j < cols; j += 3 ) + { + double t0 = buf[j]*mat[0] + buf[j+1]*mat[1] + buf[j+2]*mat[2] + mat[3]; + double t1 = buf[j]*mat[4] + buf[j+1]*mat[5] + buf[j+2]*mat[6] + mat[7]; + double t2 = buf[j]*mat[8] + buf[j+1]*mat[9] + buf[j+2]*mat[10] + mat[11]; + double w = buf[j]*mat[12] + buf[j+1]*mat[13] + buf[j+2]*mat[14] + mat[15]; + w = w ? 1./w : 0; + buf[j] = t0*w; + buf[j+1] = t1*w; + buf[j+2] = t2*w; + } + break; + default: + assert(0); + } + + switch( depth ) + { + case CV_32F: + for( j = 0; j < cols; j++ ) + ((float*)dst)[j] = (float)buf[j]; + break; + case CV_64F: + for( j = 0; j < cols; j++ ) + ((double*)dst)[j] = buf[j]; + break; + default: + assert(0); + } + } +} + + +void Core_PerspectiveTransformTest::prepare_to_validation( int ) +{ + CvMat transmat = test_mat[INPUT][1]; + cvTsPerspectiveTransform( test_array[INPUT][0], test_array[REF_OUTPUT][0], &transmat ); +} + +///////////////// Mahalanobis ///////////////////// + +class Core_MahalanobisTest : public Core_MatrixTest +{ +public: + typedef Core_MatrixTest Base; + Core_MahalanobisTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + int prepare_test_case( int test_case_idx ); + void run_func(); + void prepare_to_validation( int test_case_idx ); +}; + + +Core_MahalanobisTest::Core_MahalanobisTest() : Core_MatrixTest( 3, 1, false, true, 1 ) +{ + test_case_count = 100; + test_array[TEMP].push_back(NULL); + test_array[TEMP].push_back(NULL); + test_array[TEMP].push_back(NULL); +} + + +void Core_MahalanobisTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + Core_MatrixTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + if( cvtest::randInt(rng) & 1 ) + sizes[INPUT][0].width = sizes[INPUT][1].width = 1; + else + sizes[INPUT][0].height = sizes[INPUT][1].height = 1; + + sizes[TEMP][0] = sizes[TEMP][1] = sizes[INPUT][0]; + sizes[INPUT][2].width = sizes[INPUT][2].height = sizes[INPUT][0].width + sizes[INPUT][0].height - 1; + sizes[TEMP][2] = sizes[INPUT][2]; + types[TEMP][0] = types[TEMP][1] = types[TEMP][2] = types[INPUT][0]; +} + +int Core_MahalanobisTest::prepare_test_case( int test_case_idx ) +{ + int code = Base::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + // make sure that the inverted "covariation" matrix is symmetrix and positively defined. + cvtest::gemm( test_mat[INPUT][2], test_mat[INPUT][2], 1., Mat(), 0., test_mat[TEMP][2], GEMM_2_T ); + cvtest::copy( test_mat[TEMP][2], test_mat[INPUT][2] ); + } + + return code; +} + + +void Core_MahalanobisTest::run_func() +{ + test_mat[OUTPUT][0].at(0,0) = + cvRealScalar(cvMahalanobis(test_array[INPUT][0], test_array[INPUT][1], test_array[INPUT][2])); +} + +void Core_MahalanobisTest::prepare_to_validation( int ) +{ + cvtest::add( test_mat[INPUT][0], 1., test_mat[INPUT][1], -1., + Scalar::all(0), test_mat[TEMP][0], test_mat[TEMP][0].type() ); + if( test_mat[INPUT][0].rows == 1 ) + cvtest::gemm( test_mat[TEMP][0], test_mat[INPUT][2], 1., + Mat(), 0., test_mat[TEMP][1], 0 ); + else + cvtest::gemm( test_mat[INPUT][2], test_mat[TEMP][0], 1., + Mat(), 0., test_mat[TEMP][1], 0 ); + + test_mat[REF_OUTPUT][0].at(0,0) = cvRealScalar(sqrt(cvtest::crossCorr(test_mat[TEMP][0], test_mat[TEMP][1]))); +} + + +///////////////// covarmatrix ///////////////////// + +class Core_CovarMatrixTest : public Core_MatrixTest +{ +public: + Core_CovarMatrixTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + int prepare_test_case( int test_case_idx ); + void run_func(); + void prepare_to_validation( int test_case_idx ); + vector temp_hdrs; + vector hdr_data; + int flags, t_flag, len, count; + bool are_images; +}; + + +Core_CovarMatrixTest::Core_CovarMatrixTest() : Core_MatrixTest( 1, 1, true, false, 1 ), +flags(0), t_flag(0), are_images(false) +{ + test_case_count = 100; + test_array[INPUT_OUTPUT].push_back(NULL); + test_array[REF_INPUT_OUTPUT].push_back(NULL); + test_array[TEMP].push_back(NULL); + test_array[TEMP].push_back(NULL); +} + + +void Core_CovarMatrixTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int bits = cvtest::randInt(rng); + int i, single_matrix; + Core_MatrixTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + flags = bits & (CV_COVAR_NORMAL | CV_COVAR_USE_AVG | CV_COVAR_SCALE | CV_COVAR_ROWS ); + single_matrix = flags & CV_COVAR_ROWS; + t_flag = (bits & 256) != 0; + + const int min_count = 2; + + if( !t_flag ) + { + len = sizes[INPUT][0].width; + count = sizes[INPUT][0].height; + count = MAX(count, min_count); + sizes[INPUT][0] = Size(len, count); + } + else + { + len = sizes[INPUT][0].height; + count = sizes[INPUT][0].width; + count = MAX(count, min_count); + sizes[INPUT][0] = Size(count, len); + } + + if( single_matrix && t_flag ) + flags = (flags & ~CV_COVAR_ROWS) | CV_COVAR_COLS; + + if( CV_MAT_DEPTH(types[INPUT][0]) == CV_32S ) + types[INPUT][0] = (types[INPUT][0] & ~CV_MAT_DEPTH_MASK) | CV_32F; + + sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = flags & CV_COVAR_NORMAL ? Size(len,len) : Size(count,count); + sizes[INPUT_OUTPUT][0] = sizes[REF_INPUT_OUTPUT][0] = !t_flag ? Size(len,1) : Size(1,len); + sizes[TEMP][0] = sizes[INPUT][0]; + + types[INPUT_OUTPUT][0] = types[REF_INPUT_OUTPUT][0] = + types[OUTPUT][0] = types[REF_OUTPUT][0] = types[TEMP][0] = + CV_MAT_DEPTH(types[INPUT][0]) == CV_64F || (bits & 512) ? CV_64F : CV_32F; + + are_images = (bits & 1024) != 0; + for( i = 0; i < (single_matrix ? 1 : count); i++ ) + temp_hdrs.push_back(NULL); +} + + +int Core_CovarMatrixTest::prepare_test_case( int test_case_idx ) +{ + int code = Core_MatrixTest::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + int i; + int single_matrix = flags & (CV_COVAR_ROWS|CV_COVAR_COLS); + int hdr_size = are_images ? sizeof(IplImage) : sizeof(CvMat); + + hdr_data.resize(count*hdr_size); + uchar* _hdr_data = &hdr_data[0]; + if( single_matrix ) + { + if( !are_images ) + *((CvMat*)_hdr_data) = test_mat[INPUT][0]; + else + *((IplImage*)_hdr_data) = test_mat[INPUT][0]; + temp_hdrs[0] = _hdr_data; + } + else + for( i = 0; i < count; i++ ) + { + Mat part; + void* ptr = _hdr_data + i*hdr_size; + + if( !t_flag ) + part = test_mat[INPUT][0].row(i); + else + part = test_mat[INPUT][0].col(i); + + if( !are_images ) + *((CvMat*)ptr) = part; + else + *((IplImage*)ptr) = part; + + temp_hdrs[i] = ptr; + } + } + + return code; +} + + +void Core_CovarMatrixTest::run_func() +{ + cvCalcCovarMatrix( (const void**)&temp_hdrs[0], count, + test_array[OUTPUT][0], test_array[INPUT_OUTPUT][0], flags ); +} + + +void Core_CovarMatrixTest::prepare_to_validation( int ) +{ + Mat& avg = test_mat[REF_INPUT_OUTPUT][0]; + double scale = 1.; + + if( !(flags & CV_COVAR_USE_AVG) ) + { + Mat hdrs0 = cvarrToMat(temp_hdrs[0]); + + int i; + avg = Scalar::all(0); + + for( i = 0; i < count; i++ ) + { + Mat vec; + if( flags & CV_COVAR_ROWS ) + vec = hdrs0.row(i); + else if( flags & CV_COVAR_COLS ) + vec = hdrs0.col(i); + else + vec = cvarrToMat(temp_hdrs[i]); + + cvtest::add(avg, 1, vec, 1, Scalar::all(0), avg, avg.type()); + } + + cvtest::add(avg, 1./count, avg, 0., Scalar::all(0), avg, avg.type()); + } + + if( flags & CV_COVAR_SCALE ) + { + scale = 1./count; + } + + Mat& temp0 = test_mat[TEMP][0]; + cv::repeat( avg, temp0.rows/avg.rows, temp0.cols/avg.cols, temp0 ); + cvtest::add( test_mat[INPUT][0], 1, temp0, -1, Scalar::all(0), temp0, temp0.type()); + + cvtest::gemm( temp0, temp0, scale, Mat(), 0., test_mat[REF_OUTPUT][0], + t_flag ^ ((flags & CV_COVAR_NORMAL) != 0) ? CV_GEMM_A_T : CV_GEMM_B_T ); + temp_hdrs.clear(); +} + + +static void cvTsFloodWithZeros( Mat& mat, RNG& rng ) +{ + int k, total = mat.rows*mat.cols, type = mat.type(); + int zero_total = cvtest::randInt(rng) % total; + CV_Assert( type == CV_32FC1 || type == CV_64FC1 ); + + for( k = 0; k < zero_total; k++ ) + { + int i = cvtest::randInt(rng) % mat.rows; + int j = cvtest::randInt(rng) % mat.cols; + + if( type == CV_32FC1 ) + mat.at(i,j) = 0.f; + else + mat.at(i,j) = 0.; + } +} + + +///////////////// determinant ///////////////////// + +class Core_DetTest : public Core_MatrixTest +{ +public: + typedef Core_MatrixTest Base; + Core_DetTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ); + int prepare_test_case( int test_case_idx ); + void run_func(); + void prepare_to_validation( int test_case_idx ); +}; + + +Core_DetTest::Core_DetTest() : Core_MatrixTest( 1, 1, false, true, 1 ) +{ + test_case_count = 100; + max_log_array_size = 7; + test_array[TEMP].push_back(NULL); +} + + +void Core_DetTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + Base::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + sizes[INPUT][0].width = sizes[INPUT][0].height = sizes[INPUT][0].height; + sizes[TEMP][0] = sizes[INPUT][0]; + types[TEMP][0] = CV_64FC1; +} + + +void Core_DetTest::get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ) +{ + low = cvScalarAll(-2.); + high = cvScalarAll(2.); +} + + +double Core_DetTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return CV_MAT_DEPTH(cvGetElemType(test_array[INPUT][0])) == CV_32F ? 1e-2 : 1e-5; +} + + +int Core_DetTest::prepare_test_case( int test_case_idx ) +{ + int code = Core_MatrixTest::prepare_test_case( test_case_idx ); + if( code > 0 ) + cvTsFloodWithZeros( test_mat[INPUT][0], ts->get_rng() ); + + return code; +} + + +void Core_DetTest::run_func() +{ + test_mat[OUTPUT][0].at(0,0) = cvRealScalar(cvDet(test_array[INPUT][0])); +} + + +// LU method that chooses the optimal in a column pivot element +static double cvTsLU( CvMat* a, CvMat* b=NULL, CvMat* x=NULL, int* rank=0 ) +{ + int i, j, k, N = a->rows, N1 = a->cols, Nm = MIN(N, N1), step = a->step/sizeof(double); + int M = b ? b->cols : 0, b_step = b ? b->step/sizeof(double) : 0; + int x_step = x ? x->step/sizeof(double) : 0; + double *a0 = a->data.db, *b0 = b ? b->data.db : 0; + double *x0 = x ? x->data.db : 0; + double t, det = 1.; + assert( CV_MAT_TYPE(a->type) == CV_64FC1 && + (!b || CV_ARE_TYPES_EQ(a,b)) && (!x || CV_ARE_TYPES_EQ(a,x))); + + for( i = 0; i < Nm; i++ ) + { + double max_val = fabs(a0[i*step + i]); + double *a1, *a2, *b1 = 0, *b2 = 0; + k = i; + + for( j = i+1; j < N; j++ ) + { + t = fabs(a0[j*step + i]); + if( max_val < t ) + { + max_val = t; + k = j; + } + } + + if( k != i ) + { + for( j = i; j < N1; j++ ) + CV_SWAP( a0[i*step + j], a0[k*step + j], t ); + + for( j = 0; j < M; j++ ) + CV_SWAP( b0[i*b_step + j], b0[k*b_step + j], t ); + det = -det; + } + + if( max_val == 0 ) + { + if( rank ) + *rank = i; + return 0.; + } + + a1 = a0 + i*step; + a2 = a1 + step; + b1 = b0 + i*b_step; + b2 = b1 + b_step; + + for( j = i+1; j < N; j++, a2 += step, b2 += b_step ) + { + t = a2[i]/a1[i]; + for( k = i+1; k < N1; k++ ) + a2[k] -= t*a1[k]; + + for( k = 0; k < M; k++ ) + b2[k] -= t*b1[k]; + } + + det *= a1[i]; + } + + if( x ) + { + assert( b ); + + for( i = N-1; i >= 0; i-- ) + { + double* a1 = a0 + i*step; + double* b1 = b0 + i*b_step; + for( j = 0; j < M; j++ ) + { + t = b1[j]; + for( k = i+1; k < N1; k++ ) + t -= a1[k]*x0[k*x_step + j]; + x0[i*x_step + j] = t/a1[i]; + } + } + } + + if( rank ) + *rank = i; + return det; +} + + +void Core_DetTest::prepare_to_validation( int ) +{ + test_mat[INPUT][0].convertTo(test_mat[TEMP][0], test_mat[TEMP][0].type()); + CvMat temp0 = test_mat[TEMP][0]; + test_mat[REF_OUTPUT][0].at(0,0) = cvRealScalar(cvTsLU(&temp0, 0, 0)); +} + + +///////////////// invert ///////////////////// + +class Core_InvertTest : public Core_MatrixTest +{ +public: + typedef Core_MatrixTest Base; + Core_InvertTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ); + double get_success_error_level( int test_case_idx, int i, int j ); + int prepare_test_case( int test_case_idx ); + void run_func(); + void prepare_to_validation( int test_case_idx ); + int method, rank; + double result; +}; + + +Core_InvertTest::Core_InvertTest() +: Core_MatrixTest( 1, 1, false, false, 1 ), method(0), rank(0), result(0.) +{ + test_case_count = 100; + max_log_array_size = 7; + test_array[TEMP].push_back(NULL); + test_array[TEMP].push_back(NULL); +} + + +void Core_InvertTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int bits = cvtest::randInt(rng); + Base::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int min_size = MIN( sizes[INPUT][0].width, sizes[INPUT][0].height ); + + if( (bits & 3) == 0 ) + { + method = CV_SVD; + if( bits & 4 ) + { + sizes[INPUT][0] = Size(min_size, min_size); + if( bits & 16 ) + method = CV_CHOLESKY; + } + } + else + { + method = CV_LU; + sizes[INPUT][0] = Size(min_size, min_size); + } + + sizes[TEMP][0].width = sizes[INPUT][0].height; + sizes[TEMP][0].height = sizes[INPUT][0].width; + sizes[TEMP][1] = sizes[INPUT][0]; + types[TEMP][0] = types[INPUT][0]; + types[TEMP][1] = CV_64FC1; + sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = Size(min_size, min_size); +} + + +double Core_InvertTest::get_success_error_level( int /*test_case_idx*/, int, int ) +{ + return CV_MAT_DEPTH(cvGetElemType(test_array[OUTPUT][0])) == CV_32F ? 1e-2 : 1e-6; +} + +int Core_InvertTest::prepare_test_case( int test_case_idx ) +{ + int code = Core_MatrixTest::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + cvTsFloodWithZeros( test_mat[INPUT][0], ts->get_rng() ); + + if( method == CV_CHOLESKY ) + { + cvtest::gemm( test_mat[INPUT][0], test_mat[INPUT][0], 1., + Mat(), 0., test_mat[TEMP][0], CV_GEMM_B_T ); + cvtest::copy( test_mat[TEMP][0], test_mat[INPUT][0] ); + } + } + + return code; +} + + + +void Core_InvertTest::get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ) +{ + low = cvScalarAll(-1.); + high = cvScalarAll(1.); +} + + +void Core_InvertTest::run_func() +{ + result = cvInvert(test_array[INPUT][0], test_array[TEMP][0], method); +} + + +static double cvTsSVDet( CvMat* mat, double* ratio ) +{ + int type = CV_MAT_TYPE(mat->type); + int i, nm = MIN( mat->rows, mat->cols ); + CvMat* w = cvCreateMat( nm, 1, type ); + double det = 1.; + + cvSVD( mat, w, 0, 0, 0 ); + + if( type == CV_32FC1 ) + { + for( i = 0; i < nm; i++ ) + det *= w->data.fl[i]; + *ratio = w->data.fl[nm-1] < FLT_EPSILON ? FLT_MAX : w->data.fl[nm-1]/w->data.fl[0]; + } + else + { + for( i = 0; i < nm; i++ ) + det *= w->data.db[i]; + *ratio = w->data.db[nm-1] < FLT_EPSILON ? DBL_MAX : w->data.db[nm-1]/w->data.db[0]; + } + + cvReleaseMat( &w ); + return det; +} + +void Core_InvertTest::prepare_to_validation( int ) +{ + Mat& input = test_mat[INPUT][0]; + Mat& temp0 = test_mat[TEMP][0]; + Mat& temp1 = test_mat[TEMP][1]; + Mat& dst0 = test_mat[REF_OUTPUT][0]; + Mat& dst = test_mat[OUTPUT][0]; + CvMat _input = input; + double ratio = 0, det = cvTsSVDet( &_input, &ratio ); + double threshold = (input.depth() == CV_32F ? FLT_EPSILON : DBL_EPSILON)*1000; + + cvtest::convert( input, temp1, temp1.type() ); + + if( det < threshold || + ((method == CV_LU || method == CV_CHOLESKY) && (result == 0 || ratio < threshold)) || + ((method == CV_SVD || method == CV_SVD_SYM) && result < threshold) ) + { + dst = Scalar::all(0); + dst0 = Scalar::all(0); + return; + } + + if( input.rows >= input.cols ) + cvtest::gemm( temp0, input, 1., Mat(), 0., dst, 0 ); + else + cvtest::gemm( input, temp0, 1., Mat(), 0., dst, 0 ); + + cv::setIdentity( dst0, Scalar::all(1) ); +} + + +///////////////// solve ///////////////////// + +class Core_SolveTest : public Core_MatrixTest +{ +public: + typedef Core_MatrixTest Base; + Core_SolveTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ); + double get_success_error_level( int test_case_idx, int i, int j ); + int prepare_test_case( int test_case_idx ); + void run_func(); + void prepare_to_validation( int test_case_idx ); + int method, rank; + double result; +}; + + +Core_SolveTest::Core_SolveTest() : Core_MatrixTest( 2, 1, false, false, 1 ), method(0), rank(0), result(0.) +{ + test_case_count = 100; + max_log_array_size = 7; + test_array[TEMP].push_back(NULL); + test_array[TEMP].push_back(NULL); +} + + +void Core_SolveTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int bits = cvtest::randInt(rng); + Base::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + CvSize in_sz = sizes[INPUT][0]; + Base::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + sizes[INPUT][0] = in_sz; + int min_size = MIN( sizes[INPUT][0].width, sizes[INPUT][0].height ); + + if( (bits & 3) == 0 ) + { + method = CV_SVD; + if( bits & 4 ) + { + sizes[INPUT][0] = Size(min_size, min_size); + /*if( bits & 8 ) + method = CV_SVD_SYM;*/ + } + } + else + { + method = CV_LU; + sizes[INPUT][0] = Size(min_size, min_size); + } + + sizes[INPUT][1].height = sizes[INPUT][0].height; + sizes[TEMP][0].width = sizes[INPUT][1].width; + sizes[TEMP][0].height = sizes[INPUT][0].width; + sizes[TEMP][1] = sizes[INPUT][0]; + types[TEMP][0] = types[INPUT][0]; + types[TEMP][1] = CV_64FC1; + sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = Size(sizes[INPUT][1].width, min_size); +} + + +int Core_SolveTest::prepare_test_case( int test_case_idx ) +{ + int code = Core_MatrixTest::prepare_test_case( test_case_idx ); + + /*if( method == CV_SVD_SYM ) + { + cvTsGEMM( test_array[INPUT][0], test_array[INPUT][0], 1., + 0, 0., test_array[TEMP][0], CV_GEMM_B_T ); + cvTsCopy( test_array[TEMP][0], test_array[INPUT][0] ); + }*/ + + return code; +} + + +void Core_SolveTest::get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ) +{ + low = cvScalarAll(-1.); + high = cvScalarAll(1.); +} + + +double Core_SolveTest::get_success_error_level( int /*test_case_idx*/, int, int ) +{ + return CV_MAT_DEPTH(cvGetElemType(test_array[OUTPUT][0])) == CV_32F ? 5e-2 : 1e-8; +} + + +void Core_SolveTest::run_func() +{ + result = cvSolve(test_array[INPUT][0], test_array[INPUT][1], test_array[TEMP][0], method); +} + +void Core_SolveTest::prepare_to_validation( int ) +{ + //int rank = test_mat[REF_OUTPUT][0].rows; + Mat& input = test_mat[INPUT][0]; + Mat& dst = test_mat[OUTPUT][0]; + Mat& dst0 = test_mat[REF_OUTPUT][0]; + + if( method == CV_LU ) + { + if( result == 0 ) + { + Mat& temp1 = test_mat[TEMP][1]; + cvtest::convert(input, temp1, temp1.type()); + dst = Scalar::all(0); + CvMat _temp1 = temp1; + double det = cvTsLU( &_temp1, 0, 0 ); + dst0 = Scalar::all(det != 0); + return; + } + + double threshold = (input.type() == CV_32F ? FLT_EPSILON : DBL_EPSILON)*1000; + CvMat _input = input; + double ratio = 0, det = cvTsSVDet( &_input, &ratio ); + if( det < threshold || ratio < threshold ) + { + dst = Scalar::all(0); + dst0 = Scalar::all(0); + return; + } + } + + Mat* pdst = input.rows <= input.cols ? &test_mat[OUTPUT][0] : &test_mat[INPUT][1]; + + cvtest::gemm( input, test_mat[TEMP][0], 1., test_mat[INPUT][1], -1., *pdst, 0 ); + if( pdst != &dst ) + cvtest::gemm( input, *pdst, 1., Mat(), 0., dst, CV_GEMM_A_T ); + dst0 = Scalar::all(0); +} + + +///////////////// SVD ///////////////////// + +class Core_SVDTest : public Core_MatrixTest +{ +public: + typedef Core_MatrixTest Base; + Core_SVDTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ); + int prepare_test_case( int test_case_idx ); + void run_func(); + void prepare_to_validation( int test_case_idx ); + int flags; + bool have_u, have_v, symmetric, compact, vector_w; +}; + + +Core_SVDTest::Core_SVDTest() : +Core_MatrixTest( 1, 4, false, false, 1 ), +flags(0), have_u(false), have_v(false), symmetric(false), compact(false), vector_w(false) +{ + test_case_count = 100; + test_array[TEMP].push_back(NULL); + test_array[TEMP].push_back(NULL); + test_array[TEMP].push_back(NULL); + test_array[TEMP].push_back(NULL); +} + + +void Core_SVDTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int bits = cvtest::randInt(rng); + Core_MatrixTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int min_size, i, m, n; + + min_size = MIN( sizes[INPUT][0].width, sizes[INPUT][0].height ); + + flags = bits & (CV_SVD_MODIFY_A+CV_SVD_U_T+CV_SVD_V_T); + have_u = (bits & 8) != 0; + have_v = (bits & 16) != 0; + symmetric = (bits & 32) != 0; + compact = (bits & 64) != 0; + vector_w = (bits & 128) != 0; + + if( symmetric ) + sizes[INPUT][0] = Size(min_size, min_size); + + m = sizes[INPUT][0].height; + n = sizes[INPUT][0].width; + + if( compact ) + sizes[TEMP][0] = Size(min_size, min_size); + else + sizes[TEMP][0] = sizes[INPUT][0]; + sizes[TEMP][3] = Size(0,0); + + if( vector_w ) + { + sizes[TEMP][3] = sizes[TEMP][0]; + if( bits & 256 ) + sizes[TEMP][0] = Size(1, min_size); + else + sizes[TEMP][0] = Size(min_size, 1); + } + + if( have_u ) + { + sizes[TEMP][1] = compact ? Size(min_size, m) : Size(m, m); + + if( flags & CV_SVD_U_T ) + CV_SWAP( sizes[TEMP][1].width, sizes[TEMP][1].height, i ); + } + else + sizes[TEMP][1] = Size(0,0); + + if( have_v ) + { + sizes[TEMP][2] = compact ? Size(n, min_size) : Size(n, n); + + if( !(flags & CV_SVD_V_T) ) + CV_SWAP( sizes[TEMP][2].width, sizes[TEMP][2].height, i ); + } + else + sizes[TEMP][2] = Size(0,0); + + types[TEMP][0] = types[TEMP][1] = types[TEMP][2] = types[TEMP][3] = types[INPUT][0]; + types[OUTPUT][0] = types[OUTPUT][1] = types[OUTPUT][2] = types[INPUT][0]; + types[OUTPUT][3] = CV_8UC1; + sizes[OUTPUT][0] = !have_u || !have_v ? Size(0,0) : sizes[INPUT][0]; + sizes[OUTPUT][1] = !have_u ? Size(0,0) : compact ? Size(min_size,min_size) : Size(m,m); + sizes[OUTPUT][2] = !have_v ? Size(0,0) : compact ? Size(min_size,min_size) : Size(n,n); + sizes[OUTPUT][3] = Size(min_size,1); + + for( i = 0; i < 4; i++ ) + { + sizes[REF_OUTPUT][i] = sizes[OUTPUT][i]; + types[REF_OUTPUT][i] = types[OUTPUT][i]; + } +} + + +int Core_SVDTest::prepare_test_case( int test_case_idx ) +{ + int code = Core_MatrixTest::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + Mat& input = test_mat[INPUT][0]; + cvTsFloodWithZeros( input, ts->get_rng() ); + + if( symmetric && (have_u || have_v) ) + { + Mat& temp = test_mat[TEMP][have_u ? 1 : 2]; + cvtest::gemm( input, input, 1., Mat(), 0., temp, CV_GEMM_B_T ); + cvtest::copy( temp, input ); + } + + if( (flags & CV_SVD_MODIFY_A) && test_array[OUTPUT][0] ) + cvtest::copy( input, test_mat[OUTPUT][0] ); + } + + return code; +} + + +void Core_SVDTest::get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ) +{ + low = cvScalarAll(-2.); + high = cvScalarAll(2.); +} + +double Core_SVDTest::get_success_error_level( int test_case_idx, int i, int j ) +{ + int input_depth = CV_MAT_DEPTH(cvGetElemType( test_array[INPUT][0] )); + double input_precision = input_depth < CV_32F ? 0 : input_depth == CV_32F ? 5e-5 : 5e-11; + double output_precision = Base::get_success_error_level( test_case_idx, i, j ); + return MAX(input_precision, output_precision); +} + +void Core_SVDTest::run_func() +{ + CvArr* src = test_array[!(flags & CV_SVD_MODIFY_A) ? INPUT : OUTPUT][0]; + if( !src ) + src = test_array[INPUT][0]; + cvSVD( src, test_array[TEMP][0], test_array[TEMP][1], test_array[TEMP][2], flags ); +} + + +void Core_SVDTest::prepare_to_validation( int ) +{ + Mat& input = test_mat[INPUT][0]; + int depth = input.depth(); + int m = input.rows, n = input.cols, min_size = MIN(m, n); + Mat *src, *dst, *w; + double prev = 0, threshold = depth == CV_32F ? FLT_EPSILON : DBL_EPSILON; + int i, step; + + if( have_u ) + { + src = &test_mat[TEMP][1]; + dst = &test_mat[OUTPUT][1]; + cvtest::gemm( *src, *src, 1., Mat(), 0., *dst, src->rows == dst->rows ? CV_GEMM_B_T : CV_GEMM_A_T ); + cv::setIdentity( test_mat[REF_OUTPUT][1], Scalar::all(1.) ); + } + + if( have_v ) + { + src = &test_mat[TEMP][2]; + dst = &test_mat[OUTPUT][2]; + cvtest::gemm( *src, *src, 1., Mat(), 0., *dst, src->rows == dst->rows ? CV_GEMM_B_T : CV_GEMM_A_T ); + cv::setIdentity( test_mat[REF_OUTPUT][2], Scalar::all(1.) ); + } + + w = &test_mat[TEMP][0]; + step = w->rows == 1 ? 1 : w->step1(); + for( i = 0; i < min_size; i++ ) + { + double normval = 0, aii; + if( w->rows > 1 && w->cols > 1 ) + { + normval = cvtest::norm( w->row(i), NORM_L1 ); + aii = depth == CV_32F ? w->at(i,i) : w->at(i,i); + } + else + { + normval = aii = depth == CV_32F ? w->at(i) : w->at(i); + } + + normval = fabs(normval - aii); + test_mat[OUTPUT][3].at(i) = aii >= 0 && normval < threshold && (i == 0 || aii <= prev); + prev = aii; + } + + test_mat[REF_OUTPUT][3] = Scalar::all(1); + + if( have_u && have_v ) + { + if( vector_w ) + { + test_mat[TEMP][3] = Scalar::all(0); + for( i = 0; i < min_size; i++ ) + { + double val = depth == CV_32F ? w->at(i) : w->at(i); + cvSetReal2D( test_array[TEMP][3], i, i, val ); + } + w = &test_mat[TEMP][3]; + } + + if( m >= n ) + { + cvtest::gemm( test_mat[TEMP][1], *w, 1., Mat(), 0., test_mat[REF_OUTPUT][0], + flags & CV_SVD_U_T ? CV_GEMM_A_T : 0 ); + cvtest::gemm( test_mat[REF_OUTPUT][0], test_mat[TEMP][2], 1., Mat(), 0., + test_mat[OUTPUT][0], flags & CV_SVD_V_T ? 0 : CV_GEMM_B_T ); + } + else + { + cvtest::gemm( *w, test_mat[TEMP][2], 1., Mat(), 0., test_mat[REF_OUTPUT][0], + flags & CV_SVD_V_T ? 0 : CV_GEMM_B_T ); + cvtest::gemm( test_mat[TEMP][1], test_mat[REF_OUTPUT][0], 1., Mat(), 0., + test_mat[OUTPUT][0], flags & CV_SVD_U_T ? CV_GEMM_A_T : 0 ); + } + + cvtest::copy( test_mat[INPUT][0], test_mat[REF_OUTPUT][0] ); + } +} + + + +///////////////// SVBkSb ///////////////////// + +class Core_SVBkSbTest : public Core_MatrixTest +{ +public: + typedef Core_MatrixTest Base; + Core_SVBkSbTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ); + int prepare_test_case( int test_case_idx ); + void run_func(); + void prepare_to_validation( int test_case_idx ); + int flags; + bool have_b, symmetric, compact, vector_w; +}; + + +Core_SVBkSbTest::Core_SVBkSbTest() : Core_MatrixTest( 2, 1, false, false, 1 ), +flags(0), have_b(false), symmetric(false), compact(false), vector_w(false) +{ + test_case_count = 100; + test_array[TEMP].push_back(NULL); + test_array[TEMP].push_back(NULL); + test_array[TEMP].push_back(NULL); +} + + +void Core_SVBkSbTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int bits = cvtest::randInt(rng); + Base::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int min_size, i, m, n; + CvSize b_size; + + min_size = MIN( sizes[INPUT][0].width, sizes[INPUT][0].height ); + + flags = bits & (CV_SVD_MODIFY_A+CV_SVD_U_T+CV_SVD_V_T); + have_b = (bits & 16) != 0; + symmetric = (bits & 32) != 0; + compact = (bits & 64) != 0; + vector_w = (bits & 128) != 0; + + if( symmetric ) + sizes[INPUT][0] = Size(min_size, min_size); + + m = sizes[INPUT][0].height; + n = sizes[INPUT][0].width; + + sizes[INPUT][1] = Size(0,0); + b_size = Size(m,m); + if( have_b ) + { + sizes[INPUT][1].height = sizes[INPUT][0].height; + sizes[INPUT][1].width = cvtest::randInt(rng) % 100 + 1; + b_size = sizes[INPUT][1]; + } + + if( compact ) + sizes[TEMP][0] = Size(min_size, min_size); + else + sizes[TEMP][0] = sizes[INPUT][0]; + + if( vector_w ) + { + if( bits & 256 ) + sizes[TEMP][0] = Size(1, min_size); + else + sizes[TEMP][0] = Size(min_size, 1); + } + + sizes[TEMP][1] = compact ? Size(min_size, m) : Size(m, m); + + if( flags & CV_SVD_U_T ) + CV_SWAP( sizes[TEMP][1].width, sizes[TEMP][1].height, i ); + + sizes[TEMP][2] = compact ? Size(n, min_size) : Size(n, n); + + if( !(flags & CV_SVD_V_T) ) + CV_SWAP( sizes[TEMP][2].width, sizes[TEMP][2].height, i ); + + types[TEMP][0] = types[TEMP][1] = types[TEMP][2] = types[INPUT][0]; + types[OUTPUT][0] = types[REF_OUTPUT][0] = types[INPUT][0]; + sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = Size( b_size.width, n ); +} + + +int Core_SVBkSbTest::prepare_test_case( int test_case_idx ) +{ + int code = Base::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + Mat& input = test_mat[INPUT][0]; + cvTsFloodWithZeros( input, ts->get_rng() ); + + if( symmetric ) + { + Mat& temp = test_mat[TEMP][1]; + cvtest::gemm( input, input, 1., Mat(), 0., temp, CV_GEMM_B_T ); + cvtest::copy( temp, input ); + } + + CvMat _input = input; + cvSVD( &_input, test_array[TEMP][0], test_array[TEMP][1], test_array[TEMP][2], flags ); + } + + return code; +} + + +void Core_SVBkSbTest::get_minmax_bounds( int /*i*/, int /*j*/, int /*type*/, Scalar& low, Scalar& high ) +{ + low = cvScalarAll(-2.); + high = cvScalarAll(2.); +} + + +double Core_SVBkSbTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return CV_MAT_DEPTH(cvGetElemType(test_array[INPUT][0])) == CV_32F ? 1e-3 : 1e-7; +} + + +void Core_SVBkSbTest::run_func() +{ + cvSVBkSb( test_array[TEMP][0], test_array[TEMP][1], test_array[TEMP][2], + test_array[INPUT][1], test_array[OUTPUT][0], flags ); +} + + +void Core_SVBkSbTest::prepare_to_validation( int ) +{ + Mat& input = test_mat[INPUT][0]; + int i, m = input.rows, n = input.cols, min_size = MIN(m, n); + bool is_float = input.type() == CV_32F; + Size w_size = compact ? Size(min_size,min_size) : Size(m,n); + Mat& w = test_mat[TEMP][0]; + Mat wdb( w_size.height, w_size.width, CV_64FC1 ); + CvMat _w = w, _wdb = wdb; + // use exactly the same threshold as in icvSVD... , + // so the changes in the library and here should be synchronized. + double threshold = cv::sum(w)[0]*2*(is_float ? FLT_EPSILON : DBL_EPSILON); + + wdb = Scalar::all(0); + for( i = 0; i < min_size; i++ ) + { + double wii = vector_w ? cvGetReal1D(&_w,i) : cvGetReal2D(&_w,i,i); + cvSetReal2D( &_wdb, i, i, wii > threshold ? 1./wii : 0. ); + } + + Mat u = test_mat[TEMP][1]; + Mat v = test_mat[TEMP][2]; + Mat b = test_mat[INPUT][1]; + + if( is_float ) + { + test_mat[TEMP][1].convertTo(u, CV_64F); + test_mat[TEMP][2].convertTo(v, CV_64F); + if( !b.empty() ) + test_mat[INPUT][1].convertTo(b, CV_64F); + } + + Mat t0, t1; + + if( !b.empty() ) + cvtest::gemm( u, b, 1., Mat(), 0., t0, !(flags & CV_SVD_U_T) ? CV_GEMM_A_T : 0 ); + else if( flags & CV_SVD_U_T ) + cvtest::copy( u, t0 ); + else + cvtest::transpose( u, t0 ); + + cvtest::gemm( wdb, t0, 1, Mat(), 0, t1, 0 ); + + cvtest::gemm( v, t1, 1, Mat(), 0, t0, flags & CV_SVD_V_T ? CV_GEMM_A_T : 0 ); + Mat& dst0 = test_mat[REF_OUTPUT][0]; + t0.convertTo(dst0, dst0.type() ); +} + + +typedef std::complex complex_type; + +struct pred_complex +{ + bool operator() (const complex_type& lhs, const complex_type& rhs) const + { + return fabs(lhs.real() - rhs.real()) > fabs(rhs.real())*FLT_EPSILON ? lhs.real() < rhs.real() : lhs.imag() < rhs.imag(); + } +}; + +struct pred_double +{ + bool operator() (const double& lhs, const double& rhs) const + { + return lhs < rhs; + } +}; + +class Core_SolvePolyTest : public cvtest::BaseTest +{ +public: + Core_SolvePolyTest(); + ~Core_SolvePolyTest(); +protected: + virtual void run( int start_from ); +}; + +Core_SolvePolyTest::Core_SolvePolyTest() {} + +Core_SolvePolyTest::~Core_SolvePolyTest() {} + +void Core_SolvePolyTest::run( int ) +{ + RNG& rng = ts->get_rng(); + int fig = 100; + double range = 50; + double err_eps = 1e-4; + + for (int idx = 0, max_idx = 1000, progress = 0; idx < max_idx; ++idx) + { + progress = update_progress(progress, idx-1, max_idx, 0); + int n = cvtest::randInt(rng) % 13 + 1; + std::vector r(n), ar(n), c(n + 1, 0); + std::vector a(n + 1), u(n * 2), ar1(n), ar2(n); + + int rr_odds = 3; // odds that we get a real root + for (int j = 0; j < n;) + { + if (cvtest::randInt(rng) % rr_odds == 0 || j == n - 1) + r[j++] = cvtest::randReal(rng) * range; + else + { + r[j] = complex_type(cvtest::randReal(rng) * range, + cvtest::randReal(rng) * range + 1); + r[j + 1] = std::conj(r[j]); + j += 2; + } + } + + for (int j = 0, k = 1 << n, jj, kk; j < k; ++j) + { + int p = 0; + complex_type v(1); + for (jj = 0, kk = 1; jj < n && !(j & kk); ++jj, ++p, kk <<= 1) + ; + for (; jj < n; ++jj, kk <<= 1) + { + if (j & kk) + v *= -r[jj]; + else + ++p; + } + c[p] += v; + } + + bool pass = false; + double div = 0, s = 0; + int cubic_case = idx & 1; + for (int maxiter = 100; !pass && maxiter < 10000; maxiter *= 2, cubic_case = (cubic_case + 1) % 2) + { + for (int j = 0; j < n + 1; ++j) + a[j] = c[j].real(); + + CvMat amat, umat; + cvInitMatHeader(&amat, n + 1, 1, CV_64FC1, &a[0]); + cvInitMatHeader(&umat, n, 1, CV_64FC2, &u[0]); + cvSolvePoly(&amat, &umat, maxiter, fig); + + for (int j = 0; j < n; ++j) + ar[j] = complex_type(u[j * 2], u[j * 2 + 1]); + + std::sort(r.begin(), r.end(), pred_complex()); + std::sort(ar.begin(), ar.end(), pred_complex()); + + pass = true; + if( n == 3 ) + { + ar2.resize(n); + cv::Mat _umat2(3, 1, CV_64F, &ar2[0]), umat2 = _umat2; + cvFlip(&amat, &amat, 0); + int nr2; + if( cubic_case == 0 ) + nr2 = cv::solveCubic(cv::Mat(&amat),umat2); + else + nr2 = cv::solveCubic(cv::Mat_(cv::Mat(&amat)), umat2); + cvFlip(&amat, &amat, 0); + if(nr2 > 0) + std::sort(ar2.begin(), ar2.begin()+nr2, pred_double()); + ar2.resize(nr2); + + int nr1 = 0; + for(int j = 0; j < n; j++) + if( fabs(r[j].imag()) < DBL_EPSILON ) + ar1[nr1++] = r[j].real(); + + pass = pass && nr1 == nr2; + if( nr2 > 0 ) + { + div = s = 0; + for(int j = 0; j < nr1; j++) + { + s += fabs(ar1[j]); + div += fabs(ar1[j] - ar2[j]); + } + div /= s; + pass = pass && div < err_eps; + } + } + + div = s = 0; + for (int j = 0; j < n; ++j) + { + s += fabs(r[j].real()) + fabs(r[j].imag()); + div += sqrt(pow(r[j].real() - ar[j].real(), 2) + pow(r[j].imag() - ar[j].imag(), 2)); + } + div /= s; + pass = pass && div < err_eps; + } + + if (!pass) + { + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + ts->printf( cvtest::TS::LOG, "too big diff = %g\n", div ); + + for (size_t j=0;jprintf( cvtest::TS::LOG, "ar2[%d]=%g\n", j, ar2[j]); + ts->printf(cvtest::TS::LOG, "\n"); + + for (size_t j=0;jprintf( cvtest::TS::LOG, "r[%d]=(%g, %g)\n", j, r[j].real(), r[j].imag()); + ts->printf( cvtest::TS::LOG, "\n" ); + for (size_t j=0;jprintf( cvtest::TS::LOG, "ar[%d]=(%g, %g)\n", j, ar[j].real(), ar[j].imag()); + break; + } + } +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +TEST(Core_CovarMatrix, accuracy) { Core_CovarMatrixTest test; test.safe_run(); } +TEST(Core_CrossProduct, accuracy) { Core_CrossProductTest test; test.safe_run(); } +TEST(Core_Determinant, accuracy) { Core_DetTest test; test.safe_run(); } +TEST(Core_DotProduct, accuracy) { Core_DotProductTest test; test.safe_run(); } +TEST(Core_GEMM, accuracy) { Core_GEMMTest test; test.safe_run(); } +TEST(Core_Invert, accuracy) { Core_InvertTest test; test.safe_run(); } +TEST(Core_Mahalanobis, accuracy) { Core_MahalanobisTest test; test.safe_run(); } +TEST(Core_MulTransposed, accuracy) { Core_MulTransposedTest test; test.safe_run(); } +TEST(Core_Transform, accuracy) { Core_TransformTest test; test.safe_run(); } +TEST(Core_PerspectiveTransform, accuracy) { Core_PerspectiveTransformTest test; test.safe_run(); } +TEST(Core_Pow, accuracy) { Core_PowTest test; test.safe_run(); } +TEST(Core_SolveLinearSystem, accuracy) { Core_SolveTest test; test.safe_run(); } +TEST(Core_SVD, accuracy) { Core_SVDTest test; test.safe_run(); } +TEST(Core_SVBkSb, accuracy) { Core_SVBkSbTest test; test.safe_run(); } +TEST(Core_Trace, accuracy) { Core_TraceTest test; test.safe_run(); } +TEST(Core_SolvePoly, accuracy) { Core_SolvePolyTest test; test.safe_run(); } + +// TODO: eigenvv, invsqrt, cbrt, fastarctan, (round, floor, ceil(?)), + +/* End of file. */ + diff --git a/modules/core/test/test_operations.cpp b/modules/core/test/test_operations.cpp new file mode 100644 index 000000000..1abaf1495 --- /dev/null +++ b/modules/core/test/test_operations.cpp @@ -0,0 +1,829 @@ +/*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 +#include +#include +#include +#include +#include + +using namespace cv; +using namespace std; + + +class CV_OperationsTest : public cvtest::BaseTest +{ +public: + CV_OperationsTest(); + ~CV_OperationsTest(); +protected: + void run(int); + + struct test_excep + { + test_excep(const string& _s=string("")) : s(_s) {}; + string s; + }; + + bool SomeMatFunctions(); + bool TestMat(); + bool TestTemplateMat(); + bool TestMatND(); + bool TestSparseMat(); + bool operations1(); + + void checkDiff(const Mat& m1, const Mat& m2, const string& s) { if (norm(m1, m2, NORM_INF) != 0) throw test_excep(s); } + void checkDiffF(const Mat& m1, const Mat& m2, const string& s) { if (norm(m1, m2, NORM_INF) > 1e-5) throw test_excep(s); } + +}; + +CV_OperationsTest::CV_OperationsTest() +{ +} + +CV_OperationsTest::~CV_OperationsTest() {} + +#define STR(a) STR2(a) +#define STR2(a) #a + +#define CHECK_DIFF(a, b) checkDiff(a, b, "(" #a ") != (" #b ") at l." STR(__LINE__)) +#define CHECK_DIFF_FLT(a, b) checkDiffF(a, b, "(" #a ") !=(eps) (" #b ") at l." STR(__LINE__)) + +#if defined _MSC_VER && _MSC_VER < 1400 +#define MSVC_OLD 1 +#else +#define MSVC_OLD 0 +#endif + +bool CV_OperationsTest::TestMat() +{ + try + { + Mat one_3x1(3, 1, CV_32F, Scalar(1.0)); + Mat shi_3x1(3, 1, CV_32F, Scalar(1.2)); + Mat shi_2x1(2, 1, CV_32F, Scalar(-1)); + Scalar shift = Scalar::all(15); + + float data[] = { sqrt(2.f)/2, -sqrt(2.f)/2, 1.f, sqrt(2.f)/2, sqrt(2.f)/2, 10.f }; + Mat rot_2x3(2, 3, CV_32F, data); + + Mat res = one_3x1 + shi_3x1 + shi_3x1 + shi_3x1; + res = Mat(Mat(2 * rot_2x3) * res - shi_2x1) + shift; + + Mat tmp, res2; + add(one_3x1, shi_3x1, tmp); + add(tmp, shi_3x1, tmp); + add(tmp, shi_3x1, tmp); + gemm(rot_2x3, tmp, 2, shi_2x1, -1, res2, 0); + add(res2, Mat(2, 1, CV_32F, shift), res2); + + CHECK_DIFF(res, res2); + + Mat mat4x4(4, 4, CV_32F); + randu(mat4x4, Scalar(0), Scalar(10)); + + Mat roi1 = mat4x4(Rect(Point(1, 1), Size(2, 2))); + Mat roi2 = mat4x4(Range(1, 3), Range(1, 3)); + + CHECK_DIFF(roi1, roi2); + CHECK_DIFF(mat4x4, mat4x4(Rect(Point(0,0), mat4x4.size()))); + + Mat intMat10(3, 3, CV_32S, Scalar(10)); + Mat intMat11(3, 3, CV_32S, Scalar(11)); + Mat resMat(3, 3, CV_8U, Scalar(255)); + + CHECK_DIFF(resMat, intMat10 == intMat10); + CHECK_DIFF(resMat, intMat10 < intMat11); + CHECK_DIFF(resMat, intMat11 > intMat10); + CHECK_DIFF(resMat, intMat10 <= intMat11); + CHECK_DIFF(resMat, intMat11 >= intMat10); + CHECK_DIFF(resMat, intMat11 != intMat10); + + CHECK_DIFF(resMat, intMat10 == 10.0); + CHECK_DIFF(resMat, 10.0 == intMat10); + CHECK_DIFF(resMat, intMat10 < 11.0); + CHECK_DIFF(resMat, 11.0 > intMat10); + CHECK_DIFF(resMat, 10.0 < intMat11); + CHECK_DIFF(resMat, 11.0 >= intMat10); + CHECK_DIFF(resMat, 10.0 <= intMat11); + CHECK_DIFF(resMat, 10.0 != intMat11); + CHECK_DIFF(resMat, intMat11 != 10.0); + + Mat eye = Mat::eye(3, 3, CV_16S); + Mat maskMat4(3, 3, CV_16S, Scalar(4)); + Mat maskMat1(3, 3, CV_16S, Scalar(1)); + Mat maskMat5(3, 3, CV_16S, Scalar(5)); + Mat maskMat0(3, 3, CV_16S, Scalar(0)); + + CHECK_DIFF(maskMat0, maskMat4 & maskMat1); + CHECK_DIFF(maskMat0, Scalar(1) & maskMat4); + CHECK_DIFF(maskMat0, maskMat4 & Scalar(1)); + + Mat m; + m = maskMat4.clone(); m &= maskMat1; CHECK_DIFF(maskMat0, m); + m = maskMat4.clone(); m &= maskMat1 | maskMat1; CHECK_DIFF(maskMat0, m); + m = maskMat4.clone(); m &= (2* maskMat1 - maskMat1); CHECK_DIFF(maskMat0, m); + + m = maskMat4.clone(); m &= Scalar(1); CHECK_DIFF(maskMat0, m); + m = maskMat4.clone(); m |= maskMat1; CHECK_DIFF(maskMat5, m); + m = maskMat5.clone(); m ^= maskMat1; CHECK_DIFF(maskMat4, m); + m = maskMat4.clone(); m |= (2* maskMat1 - maskMat1); CHECK_DIFF(maskMat5, m); + m = maskMat5.clone(); m ^= (2* maskMat1 - maskMat1); CHECK_DIFF(maskMat4, m); + + m = maskMat4.clone(); m |= Scalar(1); CHECK_DIFF(maskMat5, m); + m = maskMat5.clone(); m ^= Scalar(1); CHECK_DIFF(maskMat4, m); + + + + CHECK_DIFF(maskMat0, (maskMat4 | maskMat4) & (maskMat1 | maskMat1)); + CHECK_DIFF(maskMat0, (maskMat4 | maskMat4) & maskMat1); + CHECK_DIFF(maskMat0, maskMat4 & (maskMat1 | maskMat1)); + CHECK_DIFF(maskMat0, (maskMat1 | maskMat1) & Scalar(4)); + CHECK_DIFF(maskMat0, Scalar(4) & (maskMat1 | maskMat1)); + + CHECK_DIFF(maskMat0, maskMat5 ^ (maskMat4 | maskMat1)); + CHECK_DIFF(maskMat0, (maskMat4 | maskMat1) ^ maskMat5); + CHECK_DIFF(maskMat0, (maskMat4 + maskMat1) ^ (maskMat4 + maskMat1)); + CHECK_DIFF(maskMat0, Scalar(5) ^ (maskMat4 | Scalar(1))); + CHECK_DIFF(maskMat1, Scalar(5) ^ maskMat4); + CHECK_DIFF(maskMat0, Scalar(5) ^ (maskMat4 + maskMat1)); + CHECK_DIFF(maskMat5, Scalar(5) | (maskMat4 + maskMat1)); + CHECK_DIFF(maskMat0, (maskMat4 + maskMat1) ^ Scalar(5)); + + CHECK_DIFF(maskMat5, maskMat5 | (maskMat4 ^ maskMat1)); + CHECK_DIFF(maskMat5, (maskMat4 ^ maskMat1) | maskMat5); + CHECK_DIFF(maskMat5, maskMat5 | (maskMat4 ^ Scalar(1))); + CHECK_DIFF(maskMat5, (maskMat4 | maskMat4) | Scalar(1)); + CHECK_DIFF(maskMat5, Scalar(1) | (maskMat4 | maskMat4)); + CHECK_DIFF(maskMat5, Scalar(1) | maskMat4); + CHECK_DIFF(maskMat5, (maskMat5 | maskMat5) | (maskMat4 ^ maskMat1)); + + CHECK_DIFF(maskMat1, min(maskMat1, maskMat5)); + CHECK_DIFF(maskMat1, min(Mat(maskMat1 | maskMat1), maskMat5 | maskMat5)); + CHECK_DIFF(maskMat5, max(maskMat1, maskMat5)); + CHECK_DIFF(maskMat5, max(Mat(maskMat1 | maskMat1), maskMat5 | maskMat5)); + + CHECK_DIFF(maskMat1, min(maskMat1, maskMat5 | maskMat5)); + CHECK_DIFF(maskMat1, min(maskMat1 | maskMat1, maskMat5)); + CHECK_DIFF(maskMat5, max(maskMat1 | maskMat1, maskMat5)); + CHECK_DIFF(maskMat5, max(maskMat1, maskMat5 | maskMat5)); + + CHECK_DIFF(~maskMat1, maskMat1 ^ -1); + CHECK_DIFF(~(maskMat1 | maskMat1), maskMat1 ^ -1); + + CHECK_DIFF(maskMat1, maskMat4/4.0); + + ///////////////////////////// + + CHECK_DIFF(1.0 - (maskMat5 | maskMat5), -maskMat4); + CHECK_DIFF((maskMat4 | maskMat4) * 1.0 + 1.0, maskMat5); + CHECK_DIFF(1.0 + (maskMat4 | maskMat4) * 1.0, maskMat5); + CHECK_DIFF((maskMat5 | maskMat5) * 1.0 - 1.0, maskMat4); + CHECK_DIFF(5.0 - (maskMat4 | maskMat4) * 1.0, maskMat1); + CHECK_DIFF((maskMat4 | maskMat4) * 1.0 + 0.5 + 0.5, maskMat5); + CHECK_DIFF(0.5 + ((maskMat4 | maskMat4) * 1.0 + 0.5), maskMat5); + CHECK_DIFF(((maskMat4 | maskMat4) * 1.0 + 2.0) - 1.0, maskMat5); + CHECK_DIFF(5.0 - ((maskMat1 | maskMat1) * 1.0 + 3.0), maskMat1); + CHECK_DIFF( ( (maskMat1 | maskMat1) * 2.0 + 2.0) * 1.25, maskMat5); + CHECK_DIFF( 1.25 * ( (maskMat1 | maskMat1) * 2.0 + 2.0), maskMat5); + CHECK_DIFF( -( (maskMat1 | maskMat1) * (-2.0) + 1.0), maskMat1); + CHECK_DIFF( maskMat1 * 1.0 + maskMat4 * 0.5 + 2.0, maskMat5); + CHECK_DIFF( 1.0 + (maskMat1 * 1.0 + maskMat4 * 0.5 + 1.0), maskMat5); + CHECK_DIFF( (maskMat1 * 1.0 + maskMat4 * 0.5 + 2.0) - 1.0, maskMat4); + CHECK_DIFF(5.0 - (maskMat1 * 1.0 + maskMat4 * 0.5 + 1.0), maskMat1); + CHECK_DIFF((maskMat1 * 1.0 + maskMat4 * 0.5 + 1.0)*1.25, maskMat5); + CHECK_DIFF(1.25 * (maskMat1 * 1.0 + maskMat4 * 0.5 + 1.0), maskMat5); + CHECK_DIFF(-(maskMat1 * 2.0 + maskMat4 * (-1) + 1.0), maskMat1); + CHECK_DIFF((maskMat1 * 1.0 + maskMat4), maskMat5); + CHECK_DIFF((maskMat4 + maskMat1 * 1.0), maskMat5); + CHECK_DIFF((maskMat1 * 3.0 + 1.0) + maskMat1, maskMat5); + CHECK_DIFF(maskMat1 + (maskMat1 * 3.0 + 1.0), maskMat5); + CHECK_DIFF(maskMat1*4.0 + (maskMat1 | maskMat1), maskMat5); + CHECK_DIFF((maskMat1 | maskMat1) + maskMat1*4.0, maskMat5); + CHECK_DIFF((maskMat1*3.0 + 1.0) + (maskMat1 | maskMat1), maskMat5); + CHECK_DIFF((maskMat1 | maskMat1) + (maskMat1*3.0 + 1.0), maskMat5); + CHECK_DIFF(maskMat1*4.0 + maskMat4*2.0, maskMat1 * 12); + CHECK_DIFF((maskMat1*3.0 + 1.0) + maskMat4*2.0, maskMat1 * 12); + CHECK_DIFF(maskMat4*2.0 + (maskMat1*3.0 + 1.0), maskMat1 * 12); + CHECK_DIFF((maskMat1*3.0 + 1.0) + (maskMat1*2.0 + 2.0), maskMat1 * 8); + + CHECK_DIFF(maskMat5*1.0 - maskMat4, maskMat1); + CHECK_DIFF(maskMat5 - maskMat1 * 4.0, maskMat1); + CHECK_DIFF((maskMat4 * 1.0 + 4.0)- maskMat4, maskMat4); + CHECK_DIFF(maskMat5 - (maskMat1 * 2.0 + 2.0), maskMat1); + CHECK_DIFF(maskMat5*1.0 - (maskMat4 | maskMat4), maskMat1); + CHECK_DIFF((maskMat5 | maskMat5) - maskMat1 * 4.0, maskMat1); + CHECK_DIFF((maskMat4 * 1.0 + 4.0)- (maskMat4 | maskMat4), maskMat4); + CHECK_DIFF((maskMat5 | maskMat5) - (maskMat1 * 2.0 + 2.0), maskMat1); + CHECK_DIFF(maskMat1*5.0 - maskMat4 * 1.0, maskMat1); + CHECK_DIFF((maskMat1*5.0 + 3.0)- maskMat4 * 1.0, maskMat4); + CHECK_DIFF(maskMat4 * 2.0 - (maskMat1*4.0 + 3.0), maskMat1); + CHECK_DIFF((maskMat1 * 2.0 + 3.0) - (maskMat1*3.0 + 1.0), maskMat1); + + CHECK_DIFF((maskMat5 - maskMat4)* 4.0, maskMat4); + CHECK_DIFF(4.0 * (maskMat5 - maskMat4), maskMat4); + + CHECK_DIFF(-((maskMat4 | maskMat4) - (maskMat5 | maskMat5)), maskMat1); + + CHECK_DIFF(4.0 * (maskMat1 | maskMat1), maskMat4); + CHECK_DIFF((maskMat4 | maskMat4)/4.0, maskMat1); + +#if !MSVC_OLD + CHECK_DIFF(2.0 * (maskMat1 * 2.0) , maskMat4); +#endif + CHECK_DIFF((maskMat4 / 2.0) / 2.0 , maskMat1); + CHECK_DIFF(-(maskMat4 - maskMat5) , maskMat1); + CHECK_DIFF(-((maskMat4 - maskMat5) * 1.0), maskMat1); + + + ///////////////////////////// + CHECK_DIFF(maskMat4 / maskMat4, maskMat1); + + ///// Element-wise multiplication + + CHECK_DIFF(maskMat4.mul(maskMat4, 0.25), maskMat4); + CHECK_DIFF(maskMat4.mul(maskMat1 * 4, 0.25), maskMat4); + CHECK_DIFF(maskMat4.mul(maskMat4 / 4), maskMat4); + CHECK_DIFF(maskMat4.mul(maskMat4 / 4), maskMat4); + CHECK_DIFF(maskMat4.mul(maskMat4) * 0.25, maskMat4); + CHECK_DIFF(0.25 * maskMat4.mul(maskMat4), maskMat4); + + ////// Element-wise division + + CHECK_DIFF(maskMat4 / maskMat4, maskMat1); + CHECK_DIFF((maskMat4 & maskMat4) / (maskMat1 * 4), maskMat1); + + CHECK_DIFF((maskMat4 & maskMat4) / maskMat4, maskMat1); + CHECK_DIFF(maskMat4 / (maskMat4 & maskMat4), maskMat1); + CHECK_DIFF((maskMat1 * 4) / maskMat4, maskMat1); + + CHECK_DIFF(maskMat4 / (maskMat1 * 4), maskMat1); + CHECK_DIFF((maskMat4 * 0.5 )/ (maskMat1 * 2), maskMat1); + + CHECK_DIFF(maskMat4 / maskMat4.mul(maskMat1), maskMat1); + CHECK_DIFF((maskMat4 & maskMat4) / maskMat4.mul(maskMat1), maskMat1); + + CHECK_DIFF(4.0 / maskMat4, maskMat1); + CHECK_DIFF(4.0 / (maskMat4 | maskMat4), maskMat1); + CHECK_DIFF(4.0 / (maskMat1 * 4.0), maskMat1); + CHECK_DIFF(4.0 / (maskMat4 / maskMat1), maskMat1); + + m = maskMat4.clone(); m/=4.0; CHECK_DIFF(m, maskMat1); + m = maskMat4.clone(); m/=maskMat4; CHECK_DIFF(m, maskMat1); + m = maskMat4.clone(); m/=(maskMat1 * 4.0); CHECK_DIFF(m, maskMat1); + m = maskMat4.clone(); m/=(maskMat4 / maskMat1); CHECK_DIFF(m, maskMat1); + + ///////////////////////////// + float matrix_data[] = { 3, 1, -4, -5, 1, 0, 0, 1.1f, 1.5f}; + Mat mt(3, 3, CV_32F, matrix_data); + Mat mi = mt.inv(); + Mat d1 = Mat::eye(3, 3, CV_32F); + Mat d2 = d1 * 2; + MatExpr mt_tr = mt.t(); + MatExpr mi_tr = mi.t(); + Mat mi2 = mi * 2; + + + CHECK_DIFF_FLT( mi2 * mt, d2 ); + CHECK_DIFF_FLT( mi * mt, d1 ); + CHECK_DIFF_FLT( mt_tr * mi_tr, d1 ); + + m = mi.clone(); m*=mt; CHECK_DIFF_FLT(m, d1); + m = mi.clone(); m*= (2 * mt - mt) ; CHECK_DIFF_FLT(m, d1); + + m = maskMat4.clone(); m+=(maskMat1 * 1.0); CHECK_DIFF(m, maskMat5); + m = maskMat5.clone(); m-=(maskMat1 * 4.0); CHECK_DIFF(m, maskMat1); + + m = maskMat1.clone(); m+=(maskMat1 * 3.0 + 1.0); CHECK_DIFF(m, maskMat5); + m = maskMat5.clone(); m-=(maskMat1 * 3.0 + 1.0); CHECK_DIFF(m, maskMat1); +#if !MSVC_OLD + m = mi.clone(); m+=(3.0 * mi * mt + d1); CHECK_DIFF_FLT(m, mi + d1 * 4); + m = mi.clone(); m-=(3.0 * mi * mt + d1); CHECK_DIFF_FLT(m, mi - d1 * 4); + m = mi.clone(); m*=(mt * 1.0); CHECK_DIFF_FLT(m, d1); + m = mi.clone(); m*=(mt * 1.0 + Mat::eye(m.size(), m.type())); CHECK_DIFF_FLT(m, d1 + mi); + m = mi.clone(); m*=mt_tr.t(); CHECK_DIFF_FLT(m, d1); + + CHECK_DIFF_FLT( (mi * 2) * mt, d2); + CHECK_DIFF_FLT( mi * (2 * mt), d2); + CHECK_DIFF_FLT( mt.t() * mi_tr, d1 ); + CHECK_DIFF_FLT( mt_tr * mi.t(), d1 ); + CHECK_DIFF_FLT( (mi * 0.4) * (mt * 5), d2); + + CHECK_DIFF_FLT( mt.t() * (mi_tr * 2), d2 ); + CHECK_DIFF_FLT( (mt_tr * 2) * mi.t(), d2 ); + + CHECK_DIFF_FLT(mt.t() * mi.t(), d1); + CHECK_DIFF_FLT( (mi * mt) * 2.0, d2); + CHECK_DIFF_FLT( 2.0 * (mi * mt), d2); + CHECK_DIFF_FLT( -(mi * mt), -d1); + + CHECK_DIFF_FLT( (mi * mt) / 2.0, d1 / 2); + + Mat mt_mul_2_plus_1; + gemm(mt, d1, 2, Mat::ones(3, 3, CV_32F), 1, mt_mul_2_plus_1); + + CHECK_DIFF( (mt * 2.0 + 1.0) * mi, mt_mul_2_plus_1 * mi); // (A*alpha + beta)*B + CHECK_DIFF( mi * (mt * 2.0 + 1.0), mi * mt_mul_2_plus_1); // A*(B*alpha + beta) + CHECK_DIFF( (mt * 2.0 + 1.0) * (mi * 2), mt_mul_2_plus_1 * mi2); // (A*alpha + beta)*(B*gamma) + CHECK_DIFF( (mi *2)* (mt * 2.0 + 1.0), mi2 * mt_mul_2_plus_1); // (A*gamma)*(B*alpha + beta) + CHECK_DIFF_FLT( (mt * 2.0 + 1.0) * mi.t(), mt_mul_2_plus_1 * mi_tr); // (A*alpha + beta)*B^t + CHECK_DIFF_FLT( mi.t() * (mt * 2.0 + 1.0), mi_tr * mt_mul_2_plus_1); // A^t*(B*alpha + beta) + + CHECK_DIFF_FLT( (mi * mt + d2)*5, d1 * 3 * 5); + CHECK_DIFF_FLT( mi * mt + d2, d1 * 3); + CHECK_DIFF_FLT( -(mi * mt) + d2, d1); + CHECK_DIFF_FLT( (mi * mt) + d1, d2); + CHECK_DIFF_FLT( d1 + (mi * mt), d2); + CHECK_DIFF_FLT( (mi * mt) - d2, -d1); + CHECK_DIFF_FLT( d2 - (mi * mt), d1); + + CHECK_DIFF_FLT( (mi * mt) + d2 * 0.5, d2); + CHECK_DIFF_FLT( d2 * 0.5 + (mi * mt), d2); + CHECK_DIFF_FLT( (mi * mt) - d1 * 2, -d1); + CHECK_DIFF_FLT( d1 * 2 - (mi * mt), d1); + + CHECK_DIFF_FLT( (mi * mt) + mi.t(), mi_tr + d1); + CHECK_DIFF_FLT( mi.t() + (mi * mt), mi_tr + d1); + CHECK_DIFF_FLT( (mi * mt) - mi.t(), d1 - mi_tr); + CHECK_DIFF_FLT( mi.t() - (mi * mt), mi_tr - d1); + + CHECK_DIFF_FLT( 2.0 *(mi * mt + d2), d1 * 6); + CHECK_DIFF_FLT( -(mi * mt + d2), d1 * -3); + + CHECK_DIFF_FLT(mt.inv() * mt, d1); + + CHECK_DIFF_FLT(mt.inv() * (2*mt - mt), d1); +#endif + } + catch (const test_excep& e) + { + ts->printf(cvtest::TS::LOG, "%s\n", e.s.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return false; + } + return true; +} + +bool CV_OperationsTest::SomeMatFunctions() +{ + try + { + Mat rgba( 10, 10, CV_8UC4, Scalar(1,2,3,4) ); + Mat bgr( rgba.rows, rgba.cols, CV_8UC3 ); + Mat alpha( rgba.rows, rgba.cols, CV_8UC1 ); + Mat out[] = { bgr, alpha }; + // rgba[0] -> bgr[2], rgba[1] -> bgr[1], + // rgba[2] -> bgr[0], rgba[3] -> alpha[0] + int from_to[] = { 0,2, 1,1, 2,0, 3,3 }; + mixChannels( &rgba, 1, out, 2, from_to, 4 ); + + Mat bgr_exp( rgba.size(), CV_8UC3, Scalar(3,2,1)); + Mat alpha_exp( rgba.size(), CV_8UC1, Scalar(4)); + + CHECK_DIFF(bgr_exp, bgr); + CHECK_DIFF(alpha_exp, alpha); + } + catch (const test_excep& e) + { + ts->printf(cvtest::TS::LOG, "%s\n", e.s.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return false; + } + return true; + +} + + +bool CV_OperationsTest::TestTemplateMat() +{ + try + { + Mat_ one_3x1(3, 1, 1.0f); + Mat_ shi_3x1(3, 1, 1.2f); + Mat_ shi_2x1(2, 1, -2); + Scalar shift = Scalar::all(15); + + float data[] = { sqrt(2.f)/2, -sqrt(2.f)/2, 1.f, sqrt(2.f)/2, sqrt(2.f)/2, 10.f }; + Mat_ rot_2x3(2, 3, data); + + Mat_ res = Mat(Mat(2 * rot_2x3) * Mat(one_3x1 + shi_3x1 + shi_3x1 + shi_3x1) - shi_2x1) + shift; + Mat_ resS = rot_2x3 * one_3x1; + + Mat_ tmp, res2, resS2; + add(one_3x1, shi_3x1, tmp); + add(tmp, shi_3x1, tmp); + add(tmp, shi_3x1, tmp); + gemm(rot_2x3, tmp, 2, shi_2x1, -1, res2, 0); + add(res2, Mat(2, 1, CV_32F, shift), res2); + + gemm(rot_2x3, one_3x1, 1, shi_2x1, 0, resS2, 0); + CHECK_DIFF(res, res2); + CHECK_DIFF(resS, resS2); + + + Mat_ mat4x4(4, 4); + randu(mat4x4, Scalar(0), Scalar(10)); + + Mat_ roi1 = mat4x4(Rect(Point(1, 1), Size(2, 2))); + Mat_ roi2 = mat4x4(Range(1, 3), Range(1, 3)); + + CHECK_DIFF(roi1, roi2); + CHECK_DIFF(mat4x4, mat4x4(Rect(Point(0,0), mat4x4.size()))); + + Mat_ intMat10(3, 3, 10); + Mat_ intMat11(3, 3, 11); + Mat_ resMat(3, 3, 255); + + CHECK_DIFF(resMat, intMat10 == intMat10); + CHECK_DIFF(resMat, intMat10 < intMat11); + CHECK_DIFF(resMat, intMat11 > intMat10); + CHECK_DIFF(resMat, intMat10 <= intMat11); + CHECK_DIFF(resMat, intMat11 >= intMat10); + + CHECK_DIFF(resMat, intMat10 == 10.0); + CHECK_DIFF(resMat, intMat10 < 11.0); + CHECK_DIFF(resMat, intMat11 > 10.0); + CHECK_DIFF(resMat, intMat10 <= 11.0); + CHECK_DIFF(resMat, intMat11 >= 10.0); + + Mat_ maskMat4(3, 3, 4); + Mat_ maskMat1(3, 3, 1); + Mat_ maskMat5(3, 3, 5); + Mat_ maskMat0(3, 3, (uchar)0); + + CHECK_DIFF(maskMat0, maskMat4 & maskMat1); + CHECK_DIFF(maskMat0, Scalar(1) & maskMat4); + CHECK_DIFF(maskMat0, maskMat4 & Scalar(1)); + + Mat_ m; + m = maskMat4.clone(); m&=maskMat1; CHECK_DIFF(maskMat0, m); + m = maskMat4.clone(); m&=Scalar(1); CHECK_DIFF(maskMat0, m); + + m = maskMat4.clone(); m|=maskMat1; CHECK_DIFF(maskMat5, m); + m = maskMat4.clone(); m^=maskMat1; CHECK_DIFF(maskMat5, m); + + CHECK_DIFF(maskMat0, (maskMat4 | maskMat4) & (maskMat1 | maskMat1)); + CHECK_DIFF(maskMat0, (maskMat4 | maskMat4) & maskMat1); + CHECK_DIFF(maskMat0, maskMat4 & (maskMat1 | maskMat1)); + + CHECK_DIFF(maskMat0, maskMat5 ^ (maskMat4 | maskMat1)); + CHECK_DIFF(maskMat0, Scalar(5) ^ (maskMat4 | Scalar(1))); + + CHECK_DIFF(maskMat5, maskMat5 | (maskMat4 ^ maskMat1)); + CHECK_DIFF(maskMat5, maskMat5 | (maskMat4 ^ Scalar(1))); + + CHECK_DIFF(~maskMat1, maskMat1 ^ 0xFF); + CHECK_DIFF(~(maskMat1 | maskMat1), maskMat1 ^ 0xFF); + + CHECK_DIFF(maskMat1 + maskMat4, maskMat5); + CHECK_DIFF(maskMat1 + Scalar(4), maskMat5); + CHECK_DIFF(Scalar(4) + maskMat1, maskMat5); + CHECK_DIFF(Scalar(4) + (maskMat1 & maskMat1), maskMat5); + + CHECK_DIFF(maskMat1 + 4.0, maskMat5); + CHECK_DIFF((maskMat1 & 0xFF) + 4.0, maskMat5); + CHECK_DIFF(4.0 + maskMat1, maskMat5); + + m = maskMat4.clone(); m+=Scalar(1); CHECK_DIFF(m, maskMat5); + m = maskMat4.clone(); m+=maskMat1; CHECK_DIFF(m, maskMat5); + m = maskMat4.clone(); m+=(maskMat1 | maskMat1); CHECK_DIFF(m, maskMat5); + + CHECK_DIFF(maskMat5 - maskMat1, maskMat4); + CHECK_DIFF(maskMat5 - Scalar(1), maskMat4); + CHECK_DIFF((maskMat5 | maskMat5) - Scalar(1), maskMat4); + CHECK_DIFF(maskMat5 - 1, maskMat4); + CHECK_DIFF((maskMat5 | maskMat5) - 1, maskMat4); + CHECK_DIFF((maskMat5 | maskMat5) - (maskMat1 | maskMat1), maskMat4); + + CHECK_DIFF(maskMat1, min(maskMat1, maskMat5)); + CHECK_DIFF(maskMat5, max(maskMat1, maskMat5)); + + m = maskMat5.clone(); m-=Scalar(1); CHECK_DIFF(m, maskMat4); + m = maskMat5.clone(); m-=maskMat1; CHECK_DIFF(m, maskMat4); + m = maskMat5.clone(); m-=(maskMat1 | maskMat1); CHECK_DIFF(m, maskMat4); + + m = maskMat4.clone(); m |= Scalar(1); CHECK_DIFF(maskMat5, m); + m = maskMat5.clone(); m ^= Scalar(1); CHECK_DIFF(maskMat4, m); + + CHECK_DIFF(maskMat1, maskMat4/4.0); + + Mat_ negf(3, 3, -3.0); + Mat_ posf = -negf; + Mat_ posf2 = posf * 2; + Mat_ negi(3, 3, -3); + + CHECK_DIFF(abs(negf), -negf); + CHECK_DIFF(abs(posf - posf2), -negf); + CHECK_DIFF(abs(negi), -(negi & negi)); + + CHECK_DIFF(5.0 - maskMat4, maskMat1); + + + CHECK_DIFF(maskMat4.mul(maskMat4, 0.25), maskMat4); + CHECK_DIFF(maskMat4.mul(maskMat1 * 4, 0.25), maskMat4); + CHECK_DIFF(maskMat4.mul(maskMat4 / 4), maskMat4); + + + ////// Element-wise division + + CHECK_DIFF(maskMat4 / maskMat4, maskMat1); + CHECK_DIFF(4.0 / maskMat4, maskMat1); + m = maskMat4.clone(); m/=4.0; CHECK_DIFF(m, maskMat1); + + //////////////////////////////// + + typedef Mat_ TestMat_t; + + const TestMat_t cnegi = negi.clone(); + + TestMat_t::iterator beg = negi.begin(); + TestMat_t::iterator end = negi.end(); + + TestMat_t::const_iterator cbeg = cnegi.begin(); + TestMat_t::const_iterator cend = cnegi.end(); + + int sum = 0; + for(; beg!=end; ++beg) + sum+=*beg; + + for(; cbeg!=cend; ++cbeg) + sum-=*cbeg; + + if (sum != 0) throw test_excep(); + + CHECK_DIFF(negi.col(1), negi.col(2)); + CHECK_DIFF(negi.row(1), negi.row(2)); + CHECK_DIFF(negi.col(1), negi.diag()); + + if (Mat_(1, 1).elemSize1() != sizeof(float)) throw test_excep(); + if (Mat_(1, 1).elemSize() != 2 * sizeof(float)) throw test_excep(); + if (Mat_(1, 1).depth() != CV_32F) throw test_excep(); + if (Mat_(1, 1).depth() != CV_32F) throw test_excep(); + if (Mat_(1, 1).depth() != CV_32S) throw test_excep(); + if (Mat_(1, 1).depth() != CV_64F) throw test_excep(); + if (Mat_(1, 1).depth() != CV_64F) throw test_excep(); + if (Mat_(1, 1).depth() != CV_8S) throw test_excep(); + if (Mat_(1, 1).depth() != CV_16U) throw test_excep(); + if (Mat_(1, 1).channels() != 1) throw test_excep(); + if (Mat_(1, 1).channels() != 2) throw test_excep(); + if (Mat_(1, 1).channels() != 3) throw test_excep(); + if (Mat_(1, 1).channels() != 3) throw test_excep(); + + Mat_ eye = Mat_::zeros(2, 2); CHECK_DIFF(Mat_::zeros(Size(2, 2)), eye); + eye.at(Point(0,0)) = 1; eye.at(1, 1) = 1; + + CHECK_DIFF(Mat_::eye(2, 2), eye); + CHECK_DIFF(eye, Mat_::eye(Size(2,2))); + + Mat_ ones(2, 2, (uchar)1); + CHECK_DIFF(ones, Mat_::ones(Size(2,2))); + CHECK_DIFF(Mat_::ones(2, 2), ones); + + Mat_ pntMat(2, 2, Point2f(1, 0)); + if(pntMat.stepT() != 2) throw test_excep(); + + uchar uchar_data[] = {1, 0, 0, 1}; + + Mat_ matFromData(1, 4, uchar_data); + const Mat_ mat2 = matFromData.clone(); + CHECK_DIFF(matFromData, eye.reshape(1)); + if (matFromData(Point(0,0)) != uchar_data[0])throw test_excep(); + if (mat2(Point(0,0)) != uchar_data[0]) throw test_excep(); + + if (matFromData(0,0) != uchar_data[0])throw test_excep(); + if (mat2(0,0) != uchar_data[0]) throw test_excep(); + + Mat_ rect(eye, Rect(0, 0, 1, 1)); + if (rect.cols != 1 || rect.rows != 1 || rect(0,0) != uchar_data[0]) throw test_excep(); + + //cv::Mat_<_Tp>::adjustROI(int,int,int,int) + //cv::Mat_<_Tp>::cross(const Mat_&) const + //cv::Mat_<_Tp>::Mat_(const vector<_Tp>&,bool) + //cv::Mat_<_Tp>::Mat_(int,int,_Tp*,size_t) + //cv::Mat_<_Tp>::Mat_(int,int,const _Tp&) + //cv::Mat_<_Tp>::Mat_(Size,const _Tp&) + //cv::Mat_<_Tp>::mul(const Mat_<_Tp>&,double) const + //cv::Mat_<_Tp>::mul(const MatExpr_,double,Mat_<_Tp>,MatOp_DivRS_ >,Mat_<_Tp> >&,double) const + //cv::Mat_<_Tp>::mul(const MatExpr_,double,Mat_<_Tp>,MatOp_Scale_ >,Mat_<_Tp> >&,double) const + //cv::Mat_<_Tp>::operator Mat_() const + //cv::Mat_<_Tp>::operator MatExpr_,Mat_<_Tp> >() const + //cv::Mat_<_Tp>::operator()(const Range&,const Range&) const + //cv::Mat_<_Tp>::operator()(const Rect&) const + + //cv::Mat_<_Tp>::operator=(const MatExpr_Base&) + //cv::Mat_<_Tp>::operator[](int) const + + + /////////////////////////////// + + float matrix_data[] = { 3, 1, -4, -5, 1, 0, 0, 1.1f, 1.5f}; + Mat_ mt(3, 3, matrix_data); + Mat_ mi = mt.inv(); + Mat_ d1 = Mat_::eye(3, 3); + Mat_ d2 = d1 * 2; + Mat_ mt_tr = mt.t(); + Mat_ mi_tr = mi.t(); + Mat_ mi2 = mi * 2; + + CHECK_DIFF_FLT( mi2 * mt, d2 ); + CHECK_DIFF_FLT( mi * mt, d1 ); + CHECK_DIFF_FLT( mt_tr * mi_tr, d1 ); + + Mat_ mf; + mf = mi.clone(); mf*=mt; CHECK_DIFF_FLT(mf, d1); + + ////// typedefs ////// + + if (Mat1b(1, 1).elemSize() != sizeof(uchar)) throw test_excep(); + if (Mat2b(1, 1).elemSize() != 2 * sizeof(uchar)) throw test_excep(); + if (Mat3b(1, 1).elemSize() != 3 * sizeof(uchar)) throw test_excep(); + if (Mat1f(1, 1).elemSize() != sizeof(float)) throw test_excep(); + if (Mat2f(1, 1).elemSize() != 2 * sizeof(float)) throw test_excep(); + if (Mat3f(1, 1).elemSize() != 3 * sizeof(float)) throw test_excep(); + if (Mat1f(1, 1).depth() != CV_32F) throw test_excep(); + if (Mat3f(1, 1).depth() != CV_32F) throw test_excep(); + if (Mat3f(1, 1).type() != CV_32FC3) throw test_excep(); + if (Mat1i(1, 1).depth() != CV_32S) throw test_excep(); + if (Mat1d(1, 1).depth() != CV_64F) throw test_excep(); + if (Mat1b(1, 1).depth() != CV_8U) throw test_excep(); + if (Mat3b(1, 1).type() != CV_8UC3) throw test_excep(); + if (Mat1w(1, 1).depth() != CV_16U) throw test_excep(); + if (Mat1s(1, 1).depth() != CV_16S) throw test_excep(); + if (Mat1f(1, 1).channels() != 1) throw test_excep(); + if (Mat1b(1, 1).channels() != 1) throw test_excep(); + if (Mat1i(1, 1).channels() != 1) throw test_excep(); + if (Mat1w(1, 1).channels() != 1) throw test_excep(); + if (Mat1s(1, 1).channels() != 1) throw test_excep(); + if (Mat2f(1, 1).channels() != 2) throw test_excep(); + if (Mat2b(1, 1).channels() != 2) throw test_excep(); + if (Mat2i(1, 1).channels() != 2) throw test_excep(); + if (Mat2w(1, 1).channels() != 2) throw test_excep(); + if (Mat2s(1, 1).channels() != 2) throw test_excep(); + if (Mat3f(1, 1).channels() != 3) throw test_excep(); + if (Mat3b(1, 1).channels() != 3) throw test_excep(); + if (Mat3i(1, 1).channels() != 3) throw test_excep(); + if (Mat3w(1, 1).channels() != 3) throw test_excep(); + if (Mat3s(1, 1).channels() != 3) throw test_excep(); + + } + catch (const test_excep& e) + { + ts->printf(cvtest::TS::LOG, "%s\n", e.s.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return false; + } + return true; +} + +bool CV_OperationsTest::TestMatND() +{ + int sizes[] = { 3, 3, 3}; + cv::MatND nd(3, sizes, CV_32F); + + return true; +} + +bool CV_OperationsTest::TestSparseMat() +{ + try + { + int sizes[] = { 10, 10, 10}; + int dims = sizeof(sizes)/sizeof(sizes[0]); + SparseMat mat(dims, sizes, CV_32FC2); + + if (mat.dims() != dims) throw test_excep(); + if (mat.channels() != 2) throw test_excep(); + if (mat.depth() != CV_32F) throw test_excep(); + + SparseMat mat2 = mat.clone(); + } + catch (const test_excep&) + { + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return false; + } + return true; +} + +bool CV_OperationsTest::operations1() +{ + try + { + Point3d p1(1, 1, 1), p2(2, 2, 2), p4(4, 4, 4); + p1*=2; + if (!(p1 == p2)) throw test_excep(); + if (!(p2 * 2 == p4)) throw test_excep(); + if (!(p2 * 2.f == p4)) throw test_excep(); + if (!(p2 * 2.f == p4)) throw test_excep(); + + Point2d pi1(1, 1), pi2(2, 2), pi4(4, 4); + pi1*=2; + if (!(pi1 == pi2)) throw test_excep(); + if (!(pi2 * 2 == pi4)) throw test_excep(); + if (!(pi2 * 2.f == pi4)) throw test_excep(); + if (!(pi2 * 2.f == pi4)) throw test_excep(); + + Vec2d v12(1, 1), v22(2, 2); + v12*=2.0; + if (!(v12 == v22)) throw test_excep(); + + Vec3d v13(1, 1, 1), v23(2, 2, 2); + v13*=2.0; + if (!(v13 == v23)) throw test_excep(); + + Vec4d v14(1, 1, 1, 1), v24(2, 2, 2, 2); + v14*=2.0; + if (!(v14 == v24)) throw test_excep(); + + Size sz(10, 20); + if (sz.area() != 200) throw test_excep(); + if (sz.width != 10 || sz.height != 20) throw test_excep(); + if (((CvSize)sz).width != 10 || ((CvSize)sz).height != 20) throw test_excep(); + + Vec v5d(1, 1, 1, 1, 1); + Vec v6d(1, 1, 1, 1, 1, 1); + Vec v7d(1, 1, 1, 1, 1, 1, 1); + Vec v8d(1, 1, 1, 1, 1, 1, 1, 1); + Vec v9d(1, 1, 1, 1, 1, 1, 1, 1, 1); + Vec v10d(1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + Vec v10dzero; + for (int ii = 0; ii < 10; ++ii) { + if (!v10dzero[ii] == 0.0) + throw test_excep(); + } + } + catch(const test_excep&) + { + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return false; + } + return true; +} + +void CV_OperationsTest::run( int /* start_from */) +{ + if (!TestMat()) + return; + + if (!SomeMatFunctions()) + return; + + if (!TestTemplateMat()) + return; + + /* if (!TestMatND()) + return;*/ + + if (!TestSparseMat()) + return; + + if (!operations1()) + return; + + ts->set_failed_test_info(cvtest::TS::OK); +} + +TEST(Core_Array, expressions) { CV_OperationsTest test; test.safe_run(); } diff --git a/modules/core/test/test_precomp.hpp b/modules/core/test/test_precomp.hpp index 38e714a9c..887f91580 100644 --- a/modules/core/test/test_precomp.hpp +++ b/modules/core/test/test_precomp.hpp @@ -1,2 +1,8 @@ -#include "opencv2/gtest/gtestcv.hpp" -#include "opencv2/core/core.hpp" +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/core/core_c.h" +#include + +#endif diff --git a/modules/core/test/test_rand.cpp b/modules/core/test/test_rand.cpp new file mode 100644 index 000000000..d05b16a86 --- /dev/null +++ b/modules/core/test/test_rand.cpp @@ -0,0 +1,303 @@ +#include "test_precomp.hpp" + +using namespace cv; +using namespace std; + +class Core_RandTest : public cvtest::BaseTest +{ +public: + Core_RandTest(); +protected: + void run(int); + bool check_pdf(const Mat& hist, double scale, int dist_type, + double& refval, double& realval); +}; + + +Core_RandTest::Core_RandTest() +{ +} + +static double chi2_p95(int n) +{ + static float chi2_tab95[] = { + 3.841f, 5.991f, 7.815f, 9.488f, 11.07f, 12.59f, 14.07f, 15.51f, + 16.92f, 18.31f, 19.68f, 21.03f, 21.03f, 22.36f, 23.69f, 25.00f, + 26.30f, 27.59f, 28.87f, 30.14f, 31.41f, 32.67f, 33.92f, 35.17f, + 36.42f, 37.65f, 38.89f, 40.11f, 41.34f, 42.56f, 43.77f }; + static const double xp = 1.64; + CV_Assert(n >= 1); + + if( n <= 30 ) + return chi2_tab95[n-1]; + return n + sqrt((double)2*n)*xp + 0.6666666666666*(xp*xp - 1); +} + +bool Core_RandTest::check_pdf(const Mat& hist, double scale, + int dist_type, double& refval, double& realval) +{ + Mat hist0(hist.size(), CV_32F); + const int* H = (const int*)hist.data; + float* H0 = ((float*)hist0.data); + int i, hsz = hist.cols; + + double sum = 0; + for( i = 0; i < hsz; i++ ) + sum += H[i]; + CV_Assert( fabs(1./sum - scale) < FLT_EPSILON ); + + if( dist_type == CV_RAND_UNI ) + { + float scale0 = (float)(1./hsz); + for( i = 0; i < hsz; i++ ) + H0[i] = scale0; + } + else + { + double sum = 0, r = (hsz-1.)/2; + double alpha = 2*sqrt(2.)/r, beta = -alpha*r; + for( i = 0; i < hsz; i++ ) + { + double x = i*alpha + beta; + H0[i] = (float)exp(-x*x); + sum += H0[i]; + } + sum = 1./sum; + for( i = 0; i < hsz; i++ ) + H0[i] = (float)(H0[i]*sum); + } + + double chi2 = 0; + for( i = 0; i < hsz; i++ ) + { + double a = H0[i]; + double b = H[i]*scale; + if( a > DBL_EPSILON ) + chi2 += (a - b)*(a - b)/(a + b); + } + realval = chi2; + + double chi2_pval = chi2_p95(hsz - 1 - (dist_type == CV_RAND_NORMAL ? 2 : 0)); + refval = chi2_pval*0.01; + return realval <= refval; +} + +void Core_RandTest::run( int ) +{ + static int _ranges[][2] = + {{ 0, 256 }, { -128, 128 }, { 0, 65536 }, { -32768, 32768 }, + { -1000000, 1000000 }, { -1000, 1000 }, { -1000, 1000 }}; + + const int MAX_SDIM = 10; + const int N = 2000000; + const int maxSlice = 1000; + const int MAX_HIST_SIZE = 1000; + int progress = 0; + + RNG& rng = ts->get_rng(); + RNG tested_rng = theRNG(); + test_case_count = 200; + + for( int idx = 0; idx < test_case_count; idx++ ) + { + progress = update_progress( progress, idx, test_case_count, 0 ); + ts->update_context( this, idx, false ); + + int depth = cvtest::randInt(rng) % (CV_64F+1); + int c, cn = (cvtest::randInt(rng) % 4) + 1; + int type = CV_MAKETYPE(depth, cn); + int dist_type = cvtest::randInt(rng) % (CV_RAND_NORMAL+1); + int i, k, SZ = N/cn; + Scalar A, B; + + bool do_sphere_test = dist_type == CV_RAND_UNI; + Mat arr[2], hist[4]; + int W[] = {0,0,0,0}; + + arr[0].create(1, SZ, type); + arr[1].create(1, SZ, type); + bool fast_algo = dist_type == CV_RAND_UNI && depth < CV_32F; + + for( c = 0; c < cn; c++ ) + { + int a, b, hsz; + if( dist_type == CV_RAND_UNI ) + { + a = (int)(cvtest::randInt(rng) % (_ranges[depth][1] - + _ranges[depth][0])) + _ranges[depth][0]; + do + { + b = (int)(cvtest::randInt(rng) % (_ranges[depth][1] - + _ranges[depth][0])) + _ranges[depth][0]; + } + while( abs(a-b) <= 1 ); + if( a > b ) + std::swap(a, b); + + unsigned r = (unsigned)(b - a); + fast_algo = fast_algo && r <= 256 && (r & (r-1)) == 0; + hsz = min((unsigned)(b - a), (unsigned)MAX_HIST_SIZE); + do_sphere_test = do_sphere_test && b - a >= 100; + } + else + { + int vrange = _ranges[depth][1] - _ranges[depth][0]; + int meanrange = vrange/16; + int mindiv = MAX(vrange/20, 5); + int maxdiv = MIN(vrange/8, 10000); + + a = cvtest::randInt(rng) % meanrange - meanrange/2 + + (_ranges[depth][0] + _ranges[depth][1])/2; + b = cvtest::randInt(rng) % (maxdiv - mindiv) + mindiv; + hsz = min((unsigned)b*9, (unsigned)MAX_HIST_SIZE); + } + A[c] = a; + B[c] = b; + hist[c].create(1, hsz, CV_32S); + } + + cv::RNG saved_rng = tested_rng; + int maxk = fast_algo ? 0 : 1; + for( k = 0; k <= maxk; k++ ) + { + tested_rng = saved_rng; + int sz = 0, dsz = 0, slice; + for( slice = 0; slice < maxSlice; slice++, sz += dsz ) + { + dsz = slice+1 < maxSlice ? cvtest::randInt(rng) % (SZ - sz + 1) : SZ - sz; + Mat aslice = arr[k].colRange(sz, sz + dsz); + tested_rng.fill(aslice, dist_type, A, B); + } + } + + if( maxk >= 1 && norm(arr[0], arr[1], NORM_INF) != 0 ) + { + ts->printf( cvtest::TS::LOG, "RNG output depends on the array lengths (some generated numbers get lost?)" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + for( c = 0; c < cn; c++ ) + { + const uchar* data = arr[0].data; + int* H = hist[c].ptr(); + int HSZ = hist[c].cols; + double minVal = dist_type == CV_RAND_UNI ? A[c] : A[c] - B[c]*4; + double maxVal = dist_type == CV_RAND_UNI ? B[c] : A[c] + B[c]*4; + double scale = HSZ/(maxVal - minVal); + double delta = -minVal*scale; + + hist[c] = Scalar::all(0); + + for( i = c; i < SZ*cn; i += cn ) + { + double val = depth == CV_8U ? ((const uchar*)data)[i] : + depth == CV_8S ? ((const schar*)data)[i] : + depth == CV_16U ? ((const ushort*)data)[i] : + depth == CV_16S ? ((const short*)data)[i] : + depth == CV_32S ? ((const int*)data)[i] : + depth == CV_32F ? ((const float*)data)[i] : + ((const double*)data)[i]; + int ival = cvFloor(val*scale + delta); + if( (unsigned)ival < (unsigned)HSZ ) + { + H[ival]++; + W[c]++; + } + else if( dist_type == CV_RAND_UNI ) + { + if( (minVal <= val && val < maxVal) || (depth >= CV_32F && val == maxVal) ) + { + H[ival < 0 ? 0 : HSZ-1]++; + W[c]++; + } + else + { + putchar('^'); + } + } + } + + if( dist_type == CV_RAND_UNI && W[c] != SZ ) + { + ts->printf( cvtest::TS::LOG, "Uniform RNG gave values out of the range [%g,%g) on channel %d/%d\n", + A[c], B[c], c, cn); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + if( dist_type == CV_RAND_NORMAL && W[c] < SZ*.90) + { + ts->printf( cvtest::TS::LOG, "Normal RNG gave too many values out of the range (%g+4*%g,%g+4*%g) on channel %d/%d\n", + A[c], B[c], A[c], B[c], c, cn); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + double refval = 0, realval = 0; + + if( !check_pdf(hist[c], 1./W[c], dist_type, refval, realval) ) + { + ts->printf( cvtest::TS::LOG, "RNG failed Chi-square test " + "(got %g vs probable maximum %g) on channel %d/%d\n", + realval, refval, c, cn); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + } + + // Monte-Carlo test. Compute volume of SDIM-dimensional sphere + // inscribed in [-1,1]^SDIM cube. + if( do_sphere_test ) + { + int SDIM = cvtest::randInt(rng) % (MAX_SDIM-1) + 2; + int N0 = (SZ*cn/SDIM), N = 0; + double r2 = 0; + const uchar* data = arr[0].data; + double scale[4], delta[4]; + for( c = 0; c < cn; c++ ) + { + scale[c] = 2./(B[c] - A[c]); + delta[c] = -A[c]*scale[c] - 1; + } + + for( i = k = c = 0; i <= SZ*cn - SDIM; i++, k++, c++ ) + { + double val = depth == CV_8U ? ((const uchar*)data)[i] : + depth == CV_8S ? ((const schar*)data)[i] : + depth == CV_16U ? ((const ushort*)data)[i] : + depth == CV_16S ? ((const short*)data)[i] : + depth == CV_32S ? ((const int*)data)[i] : + depth == CV_32F ? ((const float*)data)[i] : ((const double*)data)[i]; + c &= c < cn ? -1 : 0; + val = val*scale[c] + delta[c]; + r2 += val*val; + if( k == SDIM-1 ) + { + N += r2 <= 1; + r2 = 0; + k = -1; + } + } + + double V = ((double)N/N0)*(1 << SDIM); + + // the theoretically computed volume + int sdim = SDIM % 2; + double V0 = sdim + 1; + for( sdim += 2; sdim <= SDIM; sdim += 2 ) + V0 *= 2*CV_PI/sdim; + + if( fabs(V - V0) > 0.3*fabs(V0) ) + { + ts->printf( cvtest::TS::LOG, "RNG failed %d-dim sphere volume test (got %g instead of %g)\n", + SDIM, V, V0); + ts->printf( cvtest::TS::LOG, "depth = %d, N0 = %d\n", depth, N0); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + } + } +} + +TEST(Core_Rand, quality) { Core_RandTest test; test.safe_run(); } + + diff --git a/modules/features2d/test/test_bruteforcematcher.cpp b/modules/features2d/test/test_bruteforcematcher.cpp new file mode 100644 index 000000000..f2fdca193 --- /dev/null +++ b/modules/features2d/test/test_bruteforcematcher.cpp @@ -0,0 +1,122 @@ +#include "test_precomp.hpp" + +#if 0 +using namespace cv; + +class BruteForceMatcherTest : public cvtest::BaseTest +{ +public: + BruteForceMatcherTest(); +protected: + void run( int ); +}; + +struct CV_EXPORTS L2Fake : public L2 +{ +}; + +BruteForceMatcherTest::BruteForceMatcherTest() : cvtest::BaseTest( "BruteForceMatcher", "BruteForceMatcher::matchImpl") +{ + support_testing_modes = cvtest::TS::TIMING_MODE; +} + +void BruteForceMatcherTest::run( int ) +{ + const int dimensions = 64; + const int descriptorsNumber = 5000; + + Mat train = Mat( descriptorsNumber, dimensions, CV_32FC1); + Mat query = Mat( descriptorsNumber, dimensions, CV_32FC1); + + Mat permutation( 1, descriptorsNumber, CV_32SC1 ); + for( int i=0;i( 0, i ) = i; + + //RNG rng = RNG( cvGetTickCount() ); + RNG rng = RNG( *ts->get_rng() ); + randShuffle( permutation, 1, &rng ); + + float boundary = 500.f; + for( int row=0;row( permutation.at( 0, row ), col ) = bit*boundary + rng.uniform( 0.f, boundary ); + query.at( row, col ) = bit*boundary + rng.uniform( 0.f, boundary ); + } + } + + vector specMatches, genericMatches; + BruteForceMatcher > specMatcher; + BruteForceMatcher genericMatcher; + + int64 time0 = cvGetTickCount(); + specMatcher.match( query, train, specMatches ); + int64 time1 = cvGetTickCount(); + genericMatcher.match( query, train, genericMatches ); + int64 time2 = cvGetTickCount(); + + float specMatcherTime = float(time1 - time0)/(float)cvGetTickFrequency(); + ts->printf( cvtest::TS::LOG, "Matching by matrix multiplication time s: %f, us per pair: %f\n", + specMatcherTime*1e-6, specMatcherTime/( descriptorsNumber*descriptorsNumber ) ); + + float genericMatcherTime = float(time2 - time1)/(float)cvGetTickFrequency(); + ts->printf( cvtest::TS::LOG, "Matching without matrix multiplication time s: %f, us per pair: %f\n", + genericMatcherTime*1e-6, genericMatcherTime/( descriptorsNumber*descriptorsNumber ) ); + + if( (int)specMatches.size() != descriptorsNumber || (int)genericMatches.size() != descriptorsNumber ) + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + for( int i=0;i( 0, i ) ) + { + ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH ); + break; + } + } + + + //Test mask + Mat mask( query.rows, train.rows, CV_8UC1 ); + rng.fill( mask, RNG::UNIFORM, 0, 2 ); + + + time0 = cvGetTickCount(); + specMatcher.match( query, train, specMatches, mask ); + time1 = cvGetTickCount(); + genericMatcher.match( query, train, genericMatches, mask ); + time2 = cvGetTickCount(); + + specMatcherTime = float(time1 - time0)/(float)cvGetTickFrequency(); + ts->printf( cvtest::TS::LOG, "Matching by matrix multiplication time with mask s: %f, us per pair: %f\n", + specMatcherTime*1e-6, specMatcherTime/( descriptorsNumber*descriptorsNumber ) ); + + genericMatcherTime = float(time2 - time1)/(float)cvGetTickFrequency(); + ts->printf( cvtest::TS::LOG, "Matching without matrix multiplication time with mask s: %f, us per pair: %f\n", + genericMatcherTime*1e-6, genericMatcherTime/( descriptorsNumber*descriptorsNumber ) ); + + if( specMatches.size() != genericMatches.size() ) + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + + for( size_t i=0;iset_failed_test_info( cvtest::TS::FAIL_MISMATCH ); + break; + } + } +} + +BruteForceMatcherTest taBruteForceMatcherTest; +#endif diff --git a/modules/features2d/test/test_detectordescriptor_evaluation.cpp b/modules/features2d/test/test_detectordescriptor_evaluation.cpp new file mode 100644 index 000000000..4980bb959 --- /dev/null +++ b/modules/features2d/test/test_detectordescriptor_evaluation.cpp @@ -0,0 +1,1186 @@ +/*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 +#include +#include +#include + +using namespace std; +using namespace cv; + +/****************************************************************************************\ +* Functions to evaluate affine covariant detectors and descriptors. * +\****************************************************************************************/ + +static inline Point2f applyHomography( const Mat_& H, const Point2f& pt ) +{ + double z = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2); + if( z ) + { + double w = 1./z; + return Point2f( (float)((H(0,0)*pt.x + H(0,1)*pt.y + H(0,2))*w), + (float)((H(1,0)*pt.x + H(1,1)*pt.y + H(1,2))*w) ); + } + return Point2f( numeric_limits::max(), numeric_limits::max() ); +} + +static inline void linearizeHomographyAt( const Mat_& H, const Point2f& pt, Mat_& A ) +{ + A.create(2,2); + double p1 = H(0,0)*pt.x + H(0,1)*pt.y + H(0,2), + p2 = H(1,0)*pt.x + H(1,1)*pt.y + H(1,2), + p3 = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2), + p3_2 = p3*p3; + if( p3 ) + { + A(0,0) = H(0,0)/p3 - p1*H(2,0)/p3_2; // fxdx + A(0,1) = H(0,1)/p3 - p1*H(2,1)/p3_2; // fxdy + + A(1,0) = H(1,0)/p3 - p2*H(2,0)/p3_2; // fydx + A(1,1) = H(1,1)/p3 - p2*H(2,1)/p3_2; // fydx + } + else + A.setTo(Scalar::all(numeric_limits::max())); +} + +static void calcKeyPointProjections( const vector& src, const Mat_& H, vector& dst ) +{ + if( !src.empty() ) + { + assert( !H.empty() && H.cols == 3 && H.rows == 3); + dst.resize(src.size()); + vector::const_iterator srcIt = src.begin(); + vector::iterator dstIt = dst.begin(); + for( ; srcIt != src.end(); ++srcIt, ++dstIt ) + { + Point2f dstPt = applyHomography(H, srcIt->pt); + + float srcSize2 = srcIt->size * srcIt->size; + Mat_ M(2, 2); + M(0,0) = M(1,1) = 1./srcSize2; + M(1,0) = M(0,1) = 0; + Mat_ invM; invert(M, invM); + Mat_ Aff; linearizeHomographyAt(H, srcIt->pt, Aff); + Mat_ dstM; invert(Aff*invM*Aff.t(), dstM); + Mat_ eval; eigen( dstM, eval ); + assert( eval(0,0) && eval(1,0) ); + float dstSize = (float)pow(1./(eval(0,0)*eval(1,0)), 0.25); + + // TODO: check angle projection + float srcAngleRad = (float)(srcIt->angle*CV_PI/180); + Point2f vec1(cos(srcAngleRad), sin(srcAngleRad)), vec2; + vec2.x = (float)(Aff(0,0)*vec1.x + Aff(0,1)*vec1.y); + vec2.y = (float)(Aff(1,0)*vec1.x + Aff(0,1)*vec1.y); + float dstAngleGrad = fastAtan2(vec2.y, vec2.x); + + *dstIt = KeyPoint( dstPt, dstSize, dstAngleGrad, srcIt->response, srcIt->octave, srcIt->class_id ); + } + } +} + +static void filterKeyPointsByImageSize( vector& keypoints, const Size& imgSize ) +{ + if( !keypoints.empty() ) + { + vector filtered; + filtered.reserve(keypoints.size()); + Rect r(0, 0, imgSize.width, imgSize.height); + vector::const_iterator it = keypoints.begin(); + for( int i = 0; it != keypoints.end(); ++it, i++ ) + if( r.contains(it->pt) ) + filtered.push_back(*it); + keypoints.assign(filtered.begin(), filtered.end()); + } +} + +/****************************************************************************************\ +* Detectors evaluation * +\****************************************************************************************/ +const int DATASETS_COUNT = 8; +const int TEST_CASE_COUNT = 5; + +const string IMAGE_DATASETS_DIR = "detectors_descriptors_evaluation/images_datasets/"; +const string DETECTORS_DIR = "detectors_descriptors_evaluation/detectors/"; +const string DESCRIPTORS_DIR = "detectors_descriptors_evaluation/descriptors/"; +const string KEYPOINTS_DIR = "detectors_descriptors_evaluation/keypoints_datasets/"; + +const string PARAMS_POSTFIX = "_params.xml"; +const string RES_POSTFIX = "_res.xml"; + +const string REPEAT = "repeatability"; +const string CORRESP_COUNT = "correspondence_count"; + +string DATASET_NAMES[DATASETS_COUNT] = { "bark", "bikes", "boat", "graf", "leuven", "trees", "ubc", "wall"}; + +string DEFAULT_PARAMS = "default"; + +string IS_ACTIVE_PARAMS = "isActiveParams"; +string IS_SAVE_KEYPOINTS = "isSaveKeypoints"; + + +class BaseQualityTest : public cvtest::BaseTest +{ +public: + BaseQualityTest( const char* _algName ) : algName(_algName) + { + //TODO: change this + isWriteGraphicsData = true; + } + +protected: + virtual string getRunParamsFilename() const = 0; + virtual string getResultsFilename() const = 0; + virtual string getPlotPath() const = 0; + + virtual void validQualityClear( int datasetIdx ) = 0; + virtual void calcQualityClear( int datasetIdx ) = 0; + virtual void validQualityCreate( int datasetIdx ) = 0; + virtual bool isValidQualityEmpty( int datasetIdx ) const = 0; + virtual bool isCalcQualityEmpty( int datasetIdx ) const = 0; + + void readAllDatasetsRunParams(); + virtual void readDatasetRunParams( FileNode& fn, int datasetIdx ) = 0; + void writeAllDatasetsRunParams() const; + virtual void writeDatasetRunParams( FileStorage& fs, int datasetIdx ) const = 0; + void setDefaultAllDatasetsRunParams(); + virtual void setDefaultDatasetRunParams( int datasetIdx ) = 0; + virtual void readDefaultRunParams( FileNode& /*fn*/ ) {} + virtual void writeDefaultRunParams( FileStorage& /*fs*/ ) const {} + + virtual void readResults(); + virtual void readResults( FileNode& fn, int datasetIdx, int caseIdx ) = 0; + void writeResults() const; + virtual void writeResults( FileStorage& fs, int datasetIdx, int caseIdx ) const = 0; + + bool readDataset( const string& datasetName, vector& Hs, vector& imgs ); + + virtual void readAlgorithm( ) {}; + virtual void processRunParamsFile () {}; + virtual void runDatasetTest( const vector& /*imgs*/, const vector& /*Hs*/, int /*di*/, int& /*progress*/ ) {} + void run( int ); + + virtual void processResults( int datasetIdx ); + virtual int processResults( int datasetIdx, int caseIdx ) = 0; + virtual void processResults(); + virtual void writePlotData( int /*datasetIdx*/ ) const {} + virtual void writeAveragePlotData() const {}; + + string algName; + bool isWriteParams, isWriteResults, isWriteGraphicsData; +}; + +void BaseQualityTest::readAllDatasetsRunParams() +{ + string filename = getRunParamsFilename(); + FileStorage fs( filename, FileStorage::READ ); + if( !fs.isOpened() ) + { + isWriteParams = true; + setDefaultAllDatasetsRunParams(); + ts->printf(cvtest::TS::LOG, "all runParams are default\n"); + } + else + { + isWriteParams = false; + FileNode topfn = fs.getFirstTopLevelNode(); + + FileNode fn = topfn[DEFAULT_PARAMS]; + readDefaultRunParams(fn); + + for( int i = 0; i < DATASETS_COUNT; i++ ) + { + FileNode fn = topfn[DATASET_NAMES[i]]; + if( fn.empty() ) + { + ts->printf( cvtest::TS::LOG, "%d-runParams is default\n", i); + setDefaultDatasetRunParams(i); + } + else + readDatasetRunParams(fn, i); + } + } +} + +void BaseQualityTest::writeAllDatasetsRunParams() const +{ + string filename = getRunParamsFilename(); + FileStorage fs( filename, FileStorage::WRITE ); + if( fs.isOpened() ) + { + fs << "run_params" << "{"; // top file node + fs << DEFAULT_PARAMS << "{"; + writeDefaultRunParams(fs); + fs << "}"; + for( int i = 0; i < DATASETS_COUNT; i++ ) + { + fs << DATASET_NAMES[i] << "{"; + writeDatasetRunParams(fs, i); + fs << "}"; + } + fs << "}"; + } + else + ts->printf(cvtest::TS::LOG, "file %s for writing run params can not be opened\n", filename.c_str() ); +} + +void BaseQualityTest::setDefaultAllDatasetsRunParams() +{ + for( int i = 0; i < DATASETS_COUNT; i++ ) + setDefaultDatasetRunParams(i); +} + +bool BaseQualityTest::readDataset( const string& datasetName, vector& Hs, vector& imgs ) +{ + Hs.resize( TEST_CASE_COUNT ); + imgs.resize( TEST_CASE_COUNT+1 ); + string dirname = string(ts->get_data_path()) + IMAGE_DATASETS_DIR + datasetName + "/"; + + for( int i = 0; i < (int)Hs.size(); i++ ) + { + stringstream filename; filename << "H1to" << i+2 << "p.xml"; + FileStorage fs( dirname + filename.str(), FileStorage::READ ); + if( !fs.isOpened() ) + return false; + fs.getFirstTopLevelNode() >> Hs[i]; + } + + for( int i = 0; i < (int)imgs.size(); i++ ) + { + stringstream filename; filename << "img" << i+1 << ".png"; + imgs[i] = imread( dirname + filename.str(), 0 ); + if( imgs[i].empty() ) + return false; + } + return true; +} + +void BaseQualityTest::readResults() +{ + string filename = getResultsFilename(); + FileStorage fs( filename, FileStorage::READ ); + if( fs.isOpened() ) + { + isWriteResults = false; + FileNode topfn = fs.getFirstTopLevelNode(); + for( int di = 0; di < DATASETS_COUNT; di++ ) + { + FileNode datafn = topfn[DATASET_NAMES[di]]; + if( datafn.empty() ) + { + validQualityClear(di); + ts->printf( cvtest::TS::LOG, "results for %s dataset were not read\n", + DATASET_NAMES[di].c_str() ); + } + else + { + validQualityCreate(di); + for( int ci = 0; ci < TEST_CASE_COUNT; ci++ ) + { + stringstream ss; ss << "case" << ci; + FileNode casefn = datafn[ss.str()]; + CV_Assert( !casefn.empty() ); + readResults( casefn , di, ci ); + } + } + } + } + else + isWriteResults = true; +} + +void BaseQualityTest::writeResults() const +{ + string filename = getResultsFilename();; + FileStorage fs( filename, FileStorage::WRITE ); + if( fs.isOpened() ) + { + fs << "results" << "{"; + for( int di = 0; di < DATASETS_COUNT; di++ ) + { + if( isCalcQualityEmpty(di) ) + { + ts->printf(cvtest::TS::LOG, "results on %s dataset were not write because of empty\n", + DATASET_NAMES[di].c_str()); + } + else + { + fs << DATASET_NAMES[di] << "{"; + for( int ci = 0; ci < TEST_CASE_COUNT; ci++ ) + { + stringstream ss; ss << "case" << ci; + fs << ss.str() << "{"; + writeResults( fs, di, ci ); + fs << "}"; //ss.str() + } + fs << "}"; //DATASET_NAMES[di] + } + } + fs << "}"; //results + } + else + ts->printf(cvtest::TS::LOG, "results were not written because file %s can not be opened\n", filename.c_str() ); +} + +void BaseQualityTest::processResults( int datasetIdx ) +{ + if( isWriteGraphicsData ) + writePlotData( datasetIdx ); +} + +void BaseQualityTest::processResults() +{ + if( isWriteParams ) + writeAllDatasetsRunParams(); + + if( isWriteGraphicsData ) + writeAveragePlotData(); + + int res = cvtest::TS::OK; + if( isWriteResults ) + writeResults(); + else + { + for( int di = 0; di < DATASETS_COUNT; di++ ) + { + if( isValidQualityEmpty(di) || isCalcQualityEmpty(di) ) + continue; + + ts->printf(cvtest::TS::LOG, "\nDataset: %s\n", DATASET_NAMES[di].c_str() ); + + for( int ci = 0; ci < TEST_CASE_COUNT; ci++ ) + { + ts->printf(cvtest::TS::LOG, "case%d\n", ci); + int currRes = processResults( di, ci ); + res = currRes == cvtest::TS::OK ? res : currRes; + } + } + } + + if( res != cvtest::TS::OK ) + ts->printf(cvtest::TS::LOG, "BAD ACCURACY\n"); + ts->set_failed_test_info( res ); +} + +void BaseQualityTest::run ( int ) +{ + readAlgorithm (); + processRunParamsFile (); + readResults(); + + int notReadDatasets = 0; + int progress = 0; + + FileStorage runParamsFS( getRunParamsFilename(), FileStorage::READ ); + isWriteParams = (! runParamsFS.isOpened()); + FileNode topfn = runParamsFS.getFirstTopLevelNode(); + FileNode defaultParams = topfn[DEFAULT_PARAMS]; + readDefaultRunParams (defaultParams); + + for(int di = 0; di < DATASETS_COUNT; di++ ) + { + vector imgs, Hs; + if( !readDataset( DATASET_NAMES[di], Hs, imgs ) ) + { + calcQualityClear (di); + ts->printf( cvtest::TS::LOG, "images or homography matrices of dataset named %s can not be read\n", + DATASET_NAMES[di].c_str()); + notReadDatasets++; + continue; + } + + FileNode fn = topfn[DATASET_NAMES[di]]; + readDatasetRunParams(fn, di); + + runDatasetTest (imgs, Hs, di, progress); + processResults( di ); + } + if( notReadDatasets == DATASETS_COUNT ) + { + ts->printf(cvtest::TS::LOG, "All datasets were not be read\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + } + else + processResults(); + runParamsFS.release(); +} + + + +class DetectorQualityTest : public BaseQualityTest +{ +public: + DetectorQualityTest( const char* _detectorName ) : + BaseQualityTest( _detectorName ) + { + validQuality.resize(DATASETS_COUNT); + calcQuality.resize(DATASETS_COUNT); + isSaveKeypoints.resize(DATASETS_COUNT); + isActiveParams.resize(DATASETS_COUNT); + + isSaveKeypointsDefault = false; + isActiveParamsDefault = false; + } + +protected: + using BaseQualityTest::readResults; + using BaseQualityTest::writeResults; + using BaseQualityTest::processResults; + + virtual string getRunParamsFilename() const; + virtual string getResultsFilename() const; + virtual string getPlotPath() const; + + virtual void validQualityClear( int datasetIdx ); + virtual void calcQualityClear( int datasetIdx ); + virtual void validQualityCreate( int datasetIdx ); + virtual bool isValidQualityEmpty( int datasetIdx ) const; + virtual bool isCalcQualityEmpty( int datasetIdx ) const; + + virtual void readResults( FileNode& fn, int datasetIdx, int caseIdx ); + virtual void writeResults( FileStorage& fs, int datasetIdx, int caseIdx ) const; + + virtual void readDatasetRunParams( FileNode& fn, int datasetIdx ); + virtual void writeDatasetRunParams( FileStorage& fs, int datasetIdx ) const; + virtual void setDefaultDatasetRunParams( int datasetIdx ); + virtual void readDefaultRunParams( FileNode &fn ); + virtual void writeDefaultRunParams( FileStorage &fs ) const; + + virtual void writePlotData( int di ) const; + virtual void writeAveragePlotData() const; + + void openToWriteKeypointsFile( FileStorage& fs, int datasetIdx ); + + virtual void readAlgorithm( ); + virtual void processRunParamsFile () {}; + virtual void runDatasetTest( const vector &imgs, const vector &Hs, int di, int &progress ); + virtual int processResults( int datasetIdx, int caseIdx ); + + Ptr specificDetector; + Ptr defaultDetector; + + struct Quality + { + float repeatability; + int correspondenceCount; + }; + vector > validQuality; + vector > calcQuality; + + vector isSaveKeypoints; + vector isActiveParams; + + bool isSaveKeypointsDefault; + bool isActiveParamsDefault; +}; + +string DetectorQualityTest::getRunParamsFilename() const +{ + return string(ts->get_data_path()) + DETECTORS_DIR + algName + PARAMS_POSTFIX; +} + +string DetectorQualityTest::getResultsFilename() const +{ + return string(ts->get_data_path()) + DETECTORS_DIR + algName + RES_POSTFIX; +} + +string DetectorQualityTest::getPlotPath() const +{ + return string(ts->get_data_path()) + DETECTORS_DIR + "plots/"; +} + +void DetectorQualityTest::validQualityClear( int datasetIdx ) +{ + validQuality[datasetIdx].clear(); +} + +void DetectorQualityTest::calcQualityClear( int datasetIdx ) +{ + calcQuality[datasetIdx].clear(); +} + +void DetectorQualityTest::validQualityCreate( int datasetIdx ) +{ + validQuality[datasetIdx].resize(TEST_CASE_COUNT); +} + +bool DetectorQualityTest::isValidQualityEmpty( int datasetIdx ) const +{ + return validQuality[datasetIdx].empty(); +} + +bool DetectorQualityTest::isCalcQualityEmpty( int datasetIdx ) const +{ + return calcQuality[datasetIdx].empty(); +} + +void DetectorQualityTest::readResults( FileNode& fn, int datasetIdx, int caseIdx ) +{ + validQuality[datasetIdx][caseIdx].repeatability = fn[REPEAT]; + validQuality[datasetIdx][caseIdx].correspondenceCount = fn[CORRESP_COUNT]; +} + +void DetectorQualityTest::writeResults( FileStorage& fs, int datasetIdx, int caseIdx ) const +{ + fs << REPEAT << calcQuality[datasetIdx][caseIdx].repeatability; + fs << CORRESP_COUNT << calcQuality[datasetIdx][caseIdx].correspondenceCount; +} + +void DetectorQualityTest::readDefaultRunParams (FileNode &fn) +{ + if (! fn.empty() ) + { + isSaveKeypointsDefault = (int)fn[IS_SAVE_KEYPOINTS] != 0; + defaultDetector->read (fn); + } +} + +void DetectorQualityTest::writeDefaultRunParams (FileStorage &fs) const +{ + fs << IS_SAVE_KEYPOINTS << isSaveKeypointsDefault; + defaultDetector->write (fs); +} + +void DetectorQualityTest::readDatasetRunParams( FileNode& fn, int datasetIdx ) +{ + isActiveParams[datasetIdx] = (int)fn[IS_ACTIVE_PARAMS] != 0; + if (isActiveParams[datasetIdx]) + { + isSaveKeypoints[datasetIdx] = (int)fn[IS_SAVE_KEYPOINTS] != 0; + specificDetector->read (fn); + } + else + { + setDefaultDatasetRunParams(datasetIdx); + } +} + +void DetectorQualityTest::writeDatasetRunParams( FileStorage& fs, int datasetIdx ) const +{ + fs << IS_ACTIVE_PARAMS << isActiveParams[datasetIdx]; + fs << IS_SAVE_KEYPOINTS << isSaveKeypoints[datasetIdx]; + defaultDetector->write (fs); +} + +void DetectorQualityTest::setDefaultDatasetRunParams( int datasetIdx ) +{ + isSaveKeypoints[datasetIdx] = isSaveKeypointsDefault; + isActiveParams[datasetIdx] = isActiveParamsDefault; +} + +void DetectorQualityTest::writePlotData(int di ) const +{ + int imgXVals[] = { 2, 3, 4, 5, 6 }; // if scale, blur or light changes + int viewpointXVals[] = { 20, 30, 40, 50, 60 }; // if viewpoint changes + int jpegXVals[] = { 60, 80, 90, 95, 98 }; // if jpeg compression + + int* xVals = 0; + if( !DATASET_NAMES[di].compare("ubc") ) + { + xVals = jpegXVals; + } + else if( !DATASET_NAMES[di].compare("graf") || !DATASET_NAMES[di].compare("wall") ) + { + xVals = viewpointXVals; + } + else + xVals = imgXVals; + + stringstream rFilename, cFilename; + rFilename << getPlotPath() << algName << "_" << DATASET_NAMES[di] << "_repeatability.csv"; + cFilename << getPlotPath() << algName << "_" << DATASET_NAMES[di] << "_correspondenceCount.csv"; + ofstream rfile(rFilename.str().c_str()), cfile(cFilename.str().c_str()); + for( int ci = 0; ci < TEST_CASE_COUNT; ci++ ) + { + rfile << xVals[ci] << ", " << calcQuality[di][ci].repeatability << endl; + cfile << xVals[ci] << ", " << calcQuality[di][ci].correspondenceCount << endl; + } +} + +void DetectorQualityTest::writeAveragePlotData() const +{ + stringstream rFilename, cFilename; + rFilename << getPlotPath() << algName << "_average_repeatability.csv"; + cFilename << getPlotPath() << algName << "_average_correspondenceCount.csv"; + ofstream rfile(rFilename.str().c_str()), cfile(cFilename.str().c_str()); + float avRep = 0, avCorCount = 0; + for( int di = 0; di < DATASETS_COUNT; di++ ) + { + for( int ci = 0; ci < TEST_CASE_COUNT; ci++ ) + { + avRep += calcQuality[di][ci].repeatability; + avCorCount += calcQuality[di][ci].correspondenceCount; + } + } + avRep /= DATASETS_COUNT*TEST_CASE_COUNT; + avCorCount /= DATASETS_COUNT*TEST_CASE_COUNT; + rfile << algName << ", " << avRep << endl; + cfile << algName << ", " << cvRound(avCorCount) << endl; +} + +void DetectorQualityTest::openToWriteKeypointsFile( FileStorage& fs, int datasetIdx ) +{ + string filename = string(ts->get_data_path()) + KEYPOINTS_DIR + algName + "_"+ + DATASET_NAMES[datasetIdx] + ".xml.gz" ; + + fs.open(filename, FileStorage::WRITE); + if( !fs.isOpened() ) + ts->printf( cvtest::TS::LOG, "keypoints can not be written in file %s because this file can not be opened\n", + filename.c_str()); +} + +inline void writeKeypoints( FileStorage& fs, const vector& keypoints, int imgIdx ) +{ + if( fs.isOpened() ) + { + stringstream imgName; imgName << "img" << imgIdx; + write( fs, imgName.str(), keypoints ); + } +} + +inline void readKeypoints( FileStorage& fs, vector& keypoints, int imgIdx ) +{ + assert( fs.isOpened() ); + stringstream imgName; imgName << "img" << imgIdx; + read( fs[imgName.str()], keypoints); +} + +void DetectorQualityTest::readAlgorithm () +{ + defaultDetector = FeatureDetector::create( algName ); + specificDetector = FeatureDetector::create( algName ); + if( defaultDetector == 0 ) + { + ts->printf(cvtest::TS::LOG, "Algorithm can not be read\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_GENERIC); + } +} + +void DetectorQualityTest::runDatasetTest (const vector &imgs, const vector &Hs, int di, int &progress) +{ + Ptr detector = isActiveParams[di] ? specificDetector : defaultDetector; + FileStorage keypontsFS; + if( isSaveKeypoints[di] ) + openToWriteKeypointsFile( keypontsFS, di ); + + calcQuality[di].resize(TEST_CASE_COUNT); + + vector keypoints1; + detector->detect( imgs[0], keypoints1 ); + writeKeypoints( keypontsFS, keypoints1, 0); + int progressCount = DATASETS_COUNT*TEST_CASE_COUNT; + for( int ci = 0; ci < TEST_CASE_COUNT; ci++ ) + { + progress = update_progress( progress, di*TEST_CASE_COUNT + ci, progressCount, 0 ); + vector keypoints2; + float rep; + evaluateFeatureDetector( imgs[0], imgs[ci+1], Hs[ci], &keypoints1, &keypoints2, + rep, calcQuality[di][ci].correspondenceCount, + detector ); + calcQuality[di][ci].repeatability = rep == -1 ? rep : 100.f*rep; + writeKeypoints( keypontsFS, keypoints2, ci+1); + } +} + +void testLog( cvtest::TS* ts, bool isBadAccuracy ) +{ + if( isBadAccuracy ) + ts->printf(cvtest::TS::LOG, " bad accuracy\n"); + else + ts->printf(cvtest::TS::LOG, "\n"); +} + +int DetectorQualityTest::processResults( int datasetIdx, int caseIdx ) +{ + int res = cvtest::TS::OK; + bool isBadAccuracy; + + Quality valid = validQuality[datasetIdx][caseIdx], calc = calcQuality[datasetIdx][caseIdx]; + + const int countEps = 1 + cvRound( 0.005f*(float)valid.correspondenceCount ); + const float rltvEps = 0.5f; + + ts->printf(cvtest::TS::LOG, "%s: calc=%f, valid=%f", REPEAT.c_str(), calc.repeatability, valid.repeatability ); + isBadAccuracy = (valid.repeatability - calc.repeatability) > rltvEps; + testLog( ts, isBadAccuracy ); + res = isBadAccuracy ? cvtest::TS::FAIL_BAD_ACCURACY : res; + + ts->printf(cvtest::TS::LOG, "%s: calc=%d, valid=%d", CORRESP_COUNT.c_str(), calc.correspondenceCount, valid.correspondenceCount ); + isBadAccuracy = (valid.correspondenceCount - calc.correspondenceCount) > countEps; + testLog( ts, isBadAccuracy ); + res = isBadAccuracy ? cvtest::TS::FAIL_BAD_ACCURACY : res; + return res; +} + +/****************************************************************************************\ +* Descriptors evaluation * +\****************************************************************************************/ + +const string RECALL = "recall"; +const string PRECISION = "precision"; + +const string KEYPOINTS_FILENAME = "keypointsFilename"; +const string PROJECT_KEYPOINTS_FROM_1IMAGE = "projectKeypointsFrom1Image"; +const string MATCH_FILTER = "matchFilter"; +const string RUN_PARAMS_IS_IDENTICAL = "runParamsIsIdentical"; + +const string ONE_WAY_TRAIN_DIR = "detectors_descriptors_evaluation/one_way_train_images/"; +const string ONE_WAY_IMAGES_LIST = "one_way_train_images.txt"; + +class DescriptorQualityTest : public BaseQualityTest +{ +public: + enum{ NO_MATCH_FILTER = 0 }; + DescriptorQualityTest( const char* _descriptorName, const char* _matcherName = 0 ) : + BaseQualityTest( _descriptorName ) + { + validQuality.resize(DATASETS_COUNT); + calcQuality.resize(DATASETS_COUNT); + calcDatasetQuality.resize(DATASETS_COUNT); + commRunParams.resize(DATASETS_COUNT); + + commRunParamsDefault.projectKeypointsFrom1Image = true; + commRunParamsDefault.matchFilter = NO_MATCH_FILTER; + commRunParamsDefault.isActiveParams = false; + + if( _matcherName ) + matcherName = _matcherName; + } + +protected: + using BaseQualityTest::readResults; + using BaseQualityTest::writeResults; + using BaseQualityTest::processResults; + + virtual string getRunParamsFilename() const; + virtual string getResultsFilename() const; + virtual string getPlotPath() const; + + virtual void validQualityClear( int datasetIdx ); + virtual void calcQualityClear( int datasetIdx ); + virtual void validQualityCreate( int datasetIdx ); + virtual bool isValidQualityEmpty( int datasetIdx ) const; + virtual bool isCalcQualityEmpty( int datasetIdx ) const; + + virtual void readResults( FileNode& fn, int datasetIdx, int caseIdx ); + virtual void writeResults( FileStorage& fs, int datasetIdx, int caseIdx ) const; + + virtual void readDatasetRunParams( FileNode& fn, int datasetIdx ); // + virtual void writeDatasetRunParams( FileStorage& fs, int datasetIdx ) const; + virtual void setDefaultDatasetRunParams( int datasetIdx ); + virtual void readDefaultRunParams( FileNode &fn ); + virtual void writeDefaultRunParams( FileStorage &fs ) const; + + virtual void readAlgorithm( ); + virtual void processRunParamsFile () {}; + virtual void runDatasetTest( const vector &imgs, const vector &Hs, int di, int &progress ); + + virtual int processResults( int datasetIdx, int caseIdx ); + + virtual void writePlotData( int di ) const; + void calculatePlotData( vector > &allMatches, vector > &allCorrectMatchesMask, int di ); + + struct Quality + { + float recall; + float precision; + }; + vector > validQuality; + vector > calcQuality; + vector > calcDatasetQuality; + + struct CommonRunParams + { + string keypontsFilename; + bool projectKeypointsFrom1Image; + int matchFilter; // not used now + bool isActiveParams; + }; + vector commRunParams; + + Ptr specificDescMatcher; + Ptr defaultDescMatcher; + + CommonRunParams commRunParamsDefault; + string matcherName; +}; + +string DescriptorQualityTest::getRunParamsFilename() const +{ + return string(ts->get_data_path()) + DESCRIPTORS_DIR + algName + PARAMS_POSTFIX; +} + +string DescriptorQualityTest::getResultsFilename() const +{ + return string(ts->get_data_path()) + DESCRIPTORS_DIR + algName + RES_POSTFIX; +} + +string DescriptorQualityTest::getPlotPath() const +{ + return string(ts->get_data_path()) + DESCRIPTORS_DIR + "plots/"; +} + +void DescriptorQualityTest::validQualityClear( int datasetIdx ) +{ + validQuality[datasetIdx].clear(); +} + +void DescriptorQualityTest::calcQualityClear( int datasetIdx ) +{ + calcQuality[datasetIdx].clear(); +} + +void DescriptorQualityTest::validQualityCreate( int datasetIdx ) +{ + validQuality[datasetIdx].resize(TEST_CASE_COUNT); +} + +bool DescriptorQualityTest::isValidQualityEmpty( int datasetIdx ) const +{ + return validQuality[datasetIdx].empty(); +} + +bool DescriptorQualityTest::isCalcQualityEmpty( int datasetIdx ) const +{ + return calcQuality[datasetIdx].empty(); +} + +void DescriptorQualityTest::readResults( FileNode& fn, int datasetIdx, int caseIdx ) +{ + validQuality[datasetIdx][caseIdx].recall = fn[RECALL]; + validQuality[datasetIdx][caseIdx].precision = fn[PRECISION]; +} + +void DescriptorQualityTest::writeResults( FileStorage& fs, int datasetIdx, int caseIdx ) const +{ + fs << RECALL << calcQuality[datasetIdx][caseIdx].recall; + fs << PRECISION << calcQuality[datasetIdx][caseIdx].precision; +} + +void DescriptorQualityTest::readDefaultRunParams (FileNode &fn) +{ + if (! fn.empty() ) + { + commRunParamsDefault.projectKeypointsFrom1Image = (int)fn[PROJECT_KEYPOINTS_FROM_1IMAGE] != 0; + commRunParamsDefault.matchFilter = (int)fn[MATCH_FILTER]; + defaultDescMatcher->read (fn); + } +} + +void DescriptorQualityTest::writeDefaultRunParams (FileStorage &fs) const +{ + fs << PROJECT_KEYPOINTS_FROM_1IMAGE << commRunParamsDefault.projectKeypointsFrom1Image; + fs << MATCH_FILTER << commRunParamsDefault.matchFilter; + defaultDescMatcher->write (fs); +} + +void DescriptorQualityTest::readDatasetRunParams( FileNode& fn, int datasetIdx ) +{ + commRunParams[datasetIdx].isActiveParams = (int)fn[IS_ACTIVE_PARAMS] != 0; + if (commRunParams[datasetIdx].isActiveParams) + { + commRunParams[datasetIdx].keypontsFilename = (string)fn[KEYPOINTS_FILENAME]; + commRunParams[datasetIdx].projectKeypointsFrom1Image = (int)fn[PROJECT_KEYPOINTS_FROM_1IMAGE] != 0; + commRunParams[datasetIdx].matchFilter = (int)fn[MATCH_FILTER]; + specificDescMatcher->read (fn); + } + else + { + setDefaultDatasetRunParams(datasetIdx); + } +} + +void DescriptorQualityTest::writeDatasetRunParams( FileStorage& fs, int datasetIdx ) const +{ + fs << IS_ACTIVE_PARAMS << commRunParams[datasetIdx].isActiveParams; + fs << KEYPOINTS_FILENAME << commRunParams[datasetIdx].keypontsFilename; + fs << PROJECT_KEYPOINTS_FROM_1IMAGE << commRunParams[datasetIdx].projectKeypointsFrom1Image; + fs << MATCH_FILTER << commRunParams[datasetIdx].matchFilter; + + defaultDescMatcher->write (fs); +} + +void DescriptorQualityTest::setDefaultDatasetRunParams( int datasetIdx ) +{ + commRunParams[datasetIdx] = commRunParamsDefault; + commRunParams[datasetIdx].keypontsFilename = "SURF_" + DATASET_NAMES[datasetIdx] + ".xml.gz"; +} + +void DescriptorQualityTest::writePlotData( int di ) const +{ + stringstream filename; + filename << getPlotPath() << algName << "_" << DATASET_NAMES[di] << ".csv"; + FILE *file = fopen (filename.str().c_str(), "w"); + size_t size = calcDatasetQuality[di].size(); + for (size_t i=0;i extractor = DescriptorExtractor::create( algName ); + Ptr matcher = DescriptorMatcher::create( matcherName ); + defaultDescMatcher = new VectorDescriptorMatch( extractor, matcher ); + specificDescMatcher = new VectorDescriptorMatch( extractor, matcher ); + + if( extractor == 0 || matcher == 0 ) + { + ts->printf(cvtest::TS::LOG, "Algorithm can not be read\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_GENERIC); + } + } +} + +void DescriptorQualityTest::calculatePlotData( vector > &allMatches, vector > &allCorrectMatchesMask, int di ) +{ + vector recallPrecisionCurve; + computeRecallPrecisionCurve( allMatches, allCorrectMatchesMask, recallPrecisionCurve ); + + calcDatasetQuality[di].clear(); + const float resultPrecision = 0.5; + bool isResultCalculated = false; + const double eps = 1e-2; + + Quality initQuality; + initQuality.recall = 0; + initQuality.precision = 0; + calcDatasetQuality[di].push_back( initQuality ); + + for( size_t i=0;i &imgs, const vector &Hs, int di, int &progress) +{ + FileStorage keypontsFS( string(ts->get_data_path()) + KEYPOINTS_DIR + commRunParams[di].keypontsFilename, + FileStorage::READ ); + if( !keypontsFS.isOpened()) + { + calcQuality[di].clear(); + ts->printf( cvtest::TS::LOG, "keypoints from file %s can not be read\n", commRunParams[di].keypontsFilename.c_str() ); + return; + } + + Ptr descMatch = commRunParams[di].isActiveParams ? specificDescMatcher : defaultDescMatcher; + calcQuality[di].resize(TEST_CASE_COUNT); + + vector keypoints1; + readKeypoints( keypontsFS, keypoints1, 0); + + int progressCount = DATASETS_COUNT*TEST_CASE_COUNT; + + vector > allMatches1to2; + vector > allCorrectMatchesMask; + for( int ci = 0; ci < TEST_CASE_COUNT; ci++ ) + { + progress = update_progress( progress, di*TEST_CASE_COUNT + ci, progressCount, 0 ); + + vector keypoints2; + if( commRunParams[di].projectKeypointsFrom1Image ) + { + // TODO need to test function calcKeyPointProjections + calcKeyPointProjections( keypoints1, Hs[ci], keypoints2 ); + filterKeyPointsByImageSize( keypoints2, imgs[ci+1].size() ); + } + else + readKeypoints( keypontsFS, keypoints2, ci+1 ); + // TODO if( commRunParams[di].matchFilter ) + + vector > matches1to2; + vector > correctMatchesMask; + vector recallPrecisionCurve; // not used because we need recallPrecisionCurve for + // all images in dataset + evaluateGenericDescriptorMatcher( imgs[0], imgs[ci+1], Hs[ci], keypoints1, keypoints2, + &matches1to2, &correctMatchesMask, recallPrecisionCurve, + descMatch ); + allMatches1to2.insert( allMatches1to2.end(), matches1to2.begin(), matches1to2.end() ); + allCorrectMatchesMask.insert( allCorrectMatchesMask.end(), correctMatchesMask.begin(), correctMatchesMask.end() ); + } + + calculatePlotData( allMatches1to2, allCorrectMatchesMask, di ); +} + +int DescriptorQualityTest::processResults( int datasetIdx, int caseIdx ) +{ + const float rltvEps = 0.001f; + + int res = cvtest::TS::OK; + bool isBadAccuracy; + + Quality valid = validQuality[datasetIdx][caseIdx], calc = calcQuality[datasetIdx][caseIdx]; + + ts->printf(cvtest::TS::LOG, "%s: calc=%f, valid=%f", RECALL.c_str(), calc.recall, valid.recall ); + isBadAccuracy = (valid.recall - calc.recall) > rltvEps; + testLog( ts, isBadAccuracy ); + res = isBadAccuracy ? cvtest::TS::FAIL_BAD_ACCURACY : res; + + ts->printf(cvtest::TS::LOG, "%s: calc=%f, valid=%f", PRECISION.c_str(), calc.precision, valid.precision ); + isBadAccuracy = (valid.precision - calc.precision) > rltvEps; + testLog( ts, isBadAccuracy ); + res = isBadAccuracy ? cvtest::TS::FAIL_BAD_ACCURACY : res; + + return res; +} + +//--------------------------------- Calonder descriptor test -------------------------------------------- +class CalonderDescriptorQualityTest : public DescriptorQualityTest +{ +public: + CalonderDescriptorQualityTest() : + DescriptorQualityTest( "Calonder", "quality-descriptor-calonder") {} + virtual void readAlgorithm( ) + { + string classifierFile = string(ts->get_data_path()) + "/features2d/calonder_classifier.rtc"; + defaultDescMatcher = new VectorDescriptorMatch( new CalonderDescriptorExtractor( classifierFile ), + new BruteForceMatcher > ); + specificDescMatcher = defaultDescMatcher; + } +}; + +//--------------------------------- One Way descriptor test -------------------------------------------- +class OneWayDescriptorQualityTest : public DescriptorQualityTest +{ +public: + OneWayDescriptorQualityTest() : + DescriptorQualityTest("ONEWAY", "quality-descriptor-one-way") + { + } +protected: + virtual void processRunParamsFile (); + virtual void writeDatasetRunParams( FileStorage& fs, int datasetIdx ) const; +}; + +void OneWayDescriptorQualityTest::processRunParamsFile () +{ + string filename = getRunParamsFilename(); + FileStorage fs = FileStorage (filename, FileStorage::READ); + FileNode fn = fs.getFirstTopLevelNode(); + fn = fn[DEFAULT_PARAMS]; + + string pcaFilename = string(ts->get_data_path()) + (string)fn["pcaFilename"]; + string trainPath = string(ts->get_data_path()) + (string)fn["trainPath"]; + string trainImagesList = (string)fn["trainImagesList"]; + int patch_width = fn["patchWidth"]; + int patch_height = fn["patchHeight"]; + Size patchSize = cvSize (patch_width, patch_height); + int poseCount = fn["poseCount"]; + + if (trainImagesList.length () == 0 ) + return; + + fs.release (); + + readAllDatasetsRunParams(); + + OneWayDescriptorBase *base = new OneWayDescriptorBase(patchSize, poseCount, pcaFilename, + trainPath, trainImagesList); + + OneWayDescriptorMatch *match = new OneWayDescriptorMatch (); + match->initialize( OneWayDescriptorMatch::Params (), base ); + defaultDescMatcher = match; + writeAllDatasetsRunParams(); +} + +void OneWayDescriptorQualityTest::writeDatasetRunParams( FileStorage& fs, int datasetIdx ) const +{ + fs << IS_ACTIVE_PARAMS << commRunParams[datasetIdx].isActiveParams; + fs << KEYPOINTS_FILENAME << commRunParams[datasetIdx].keypontsFilename; + fs << PROJECT_KEYPOINTS_FROM_1IMAGE << commRunParams[datasetIdx].projectKeypointsFrom1Image; + fs << MATCH_FILTER << commRunParams[datasetIdx].matchFilter; +} + +// Detectors +//DetectorQualityTest fastDetectorQuality = DetectorQualityTest( "FAST", "quality-detector-fast" ); +//DetectorQualityTest gfttDetectorQuality = DetectorQualityTest( "GFTT", "quality-detector-gftt" ); +//DetectorQualityTest harrisDetectorQuality = DetectorQualityTest( "HARRIS", "quality-detector-harris" ); +//DetectorQualityTest mserDetectorQuality = DetectorQualityTest( "MSER", "quality-detector-mser" ); +//DetectorQualityTest starDetectorQuality = DetectorQualityTest( "STAR", "quality-detector-star" ); +//DetectorQualityTest siftDetectorQuality = DetectorQualityTest( "SIFT", "quality-detector-sift" ); +//DetectorQualityTest surfDetectorQuality = DetectorQualityTest( "SURF", "quality-detector-surf" ); + +// Descriptors +//DescriptorQualityTest siftDescriptorQuality = DescriptorQualityTest( "SIFT", "quality-descriptor-sift", "BruteForce" ); +//DescriptorQualityTest surfDescriptorQuality = DescriptorQualityTest( "SURF", "quality-descriptor-surf", "BruteForce" ); +//DescriptorQualityTest fernDescriptorQualityTest( "FERN", "quality-descriptor-fern"); +//CalonderDescriptorQualityTest calonderDescriptorQualityTest; + + + +// Don't run it because of bug in OneWayDescriptorBase many to many matching. TODO: fix this bug. +//OneWayDescriptorQualityTest oneWayDescriptorQuality; + +// Don't run them (will validate and save results as "quality-descriptor-sift" and "quality-descriptor-surf" test data). +// TODO: differ result filenames. +//DescriptorQualityTest siftL1DescriptorQuality = DescriptorQualityTest( "SIFT", "quality-descriptor-sift-L1", "BruteForce-L1" ); +//DescriptorQualityTest surfL1DescriptorQuality = DescriptorQualityTest( "SURF", "quality-descriptor-surf-L1", "BruteForce-L1" ); +//DescriptorQualityTest oppSiftL1DescriptorQuality = DescriptorQualityTest( "SIFT", "quality-descriptor-opponent-sift-L1", "BruteForce-L1" ); +//DescriptorQualityTest oppSurfL1DescriptorQuality = DescriptorQualityTest( "SURF", "quality-descriptor-opponent-surf-L1", "BruteForce-L1" ); + diff --git a/modules/features2d/test/test_detectors.cpp b/modules/features2d/test/test_detectors.cpp new file mode 100644 index 000000000..75bc52180 --- /dev/null +++ b/modules/features2d/test/test_detectors.cpp @@ -0,0 +1,317 @@ +/*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 +#include +#include +#include +#include +#include +#include + +using namespace cv; +using namespace std; + +class CV_DetectorsTest : public cvtest::BaseTest +{ +public: + CV_DetectorsTest(); + ~CV_DetectorsTest(); +protected: + void run(int); + template bool testDetector(const Mat& img, const T& detector, vector& expected); + + void LoadExpected(const string& file, vector& out); +}; + +CV_DetectorsTest::CV_DetectorsTest() +{ +} +CV_DetectorsTest::~CV_DetectorsTest() {} + +void getRotation(const Mat& img, Mat& aff, Mat& out) +{ + Point center(img.cols/2, img.rows/2); + aff = getRotationMatrix2D(center, 30, 1); + warpAffine( img, out, aff, img.size()); +} + +void getZoom(const Mat& img, Mat& aff, Mat& out) +{ + const double mult = 1.2; + + aff.create(2, 3, CV_64F); + double *data = aff.ptr(); + data[0] = mult; data[1] = 0; data[2] = 0; + data[3] = 0; data[4] = mult; data[5] = 0; + + warpAffine( img, out, aff, img.size()); +} + +void getBlur(const Mat& img, Mat& aff, Mat& out) +{ + aff.create(2, 3, CV_64F); + double *data = aff.ptr(); + data[0] = 1; data[1] = 0; data[2] = 0; + data[3] = 0; data[4] = 1; data[5] = 0; + + GaussianBlur(img, out, Size(5, 5), 2); +} + +void getBrightness(const Mat& img, Mat& aff, Mat& out) +{ + aff.create(2, 3, CV_64F); + double *data = aff.ptr(); + data[0] = 1; data[1] = 0; data[2] = 0; + data[3] = 0; data[4] = 1; data[5] = 0; + + add(img, Mat(img.size(), img.type(), Scalar(15)), out); +} + +void showOrig(const Mat& img, const vector& orig_pts) +{ + + Mat img_color; + cvtColor(img, img_color, CV_GRAY2BGR); + + for(size_t i = 0; i < orig_pts.size(); ++i) + circle(img_color, orig_pts[i].pt, (int)orig_pts[i].size/2, CV_RGB(0, 255, 0)); + + namedWindow("O"); imshow("O", img_color); +} + +void show(const string& name, const Mat& new_img, const vector& new_pts, const vector& transf_pts) +{ + + Mat new_img_color; + cvtColor(new_img, new_img_color, CV_GRAY2BGR); + + for(size_t i = 0; i < transf_pts.size(); ++i) + circle(new_img_color, transf_pts[i].pt, (int)transf_pts[i].size/2, CV_RGB(255, 0, 0)); + + for(size_t i = 0; i < new_pts.size(); ++i) + circle(new_img_color, new_pts[i].pt, (int)new_pts[i].size/2, CV_RGB(0, 0, 255)); + + namedWindow(name + "_T"); imshow(name + "_T", new_img_color); +} + +struct WrapPoint +{ + const double* R; + WrapPoint(const Mat& rmat) : R(rmat.ptr()) { }; + + KeyPoint operator()(const KeyPoint& kp) const + { + KeyPoint res = kp; + res.pt.x = static_cast(kp.pt.x * R[0] + kp.pt.y * R[1] + R[2]); + res.pt.y = static_cast(kp.pt.x * R[3] + kp.pt.y * R[4] + R[5]); + return res; + } +}; + +struct sortByR { bool operator()(const KeyPoint& kp1, const KeyPoint& kp2) { return norm(kp1.pt) < norm(kp2.pt); } }; + +template bool CV_DetectorsTest::testDetector(const Mat& img, const T& detector, vector& exp) +{ + vector orig_kpts; + detector(img, orig_kpts); + + typedef void (*TransfFunc )(const Mat&, Mat&, Mat& FransfFunc); + const TransfFunc transfFunc[] = { getRotation, getZoom, getBlur, getBrightness }; + //const string names[] = { "Rotation", "Zoom", "Blur", "Brightness" }; + const size_t case_num = sizeof(transfFunc)/sizeof(transfFunc[0]); + + vector affs(case_num); + vector new_imgs(case_num); + + vector< vector > new_kpts(case_num); + vector< vector > transf_kpts(case_num); + + //showOrig(img, orig_kpts); + for(size_t i = 0; i < case_num; ++i) + { + transfFunc[i](img, affs[i], new_imgs[i]); + detector(new_imgs[i], new_kpts[i]); + transform(orig_kpts.begin(), orig_kpts.end(), back_inserter(transf_kpts[i]), WrapPoint(affs[i])); + //show(names[i], new_imgs[i], new_kpts[i], transf_kpts[i]); + } + + const float thres = 3; + const float nthres = 3; + + vector result; + for(size_t i = 0; i < orig_kpts.size(); ++i) + { + const KeyPoint& okp = orig_kpts[i]; + int foundCounter = 0; + for(size_t j = 0; j < case_num; ++j) + { + const KeyPoint& tkp = transf_kpts[j][i]; + + size_t k = 0; + + for(; k < new_kpts[j].size(); ++k) + if (norm(new_kpts[j][k].pt - tkp.pt) < nthres && fabs(new_kpts[j][k].size - tkp.size) < thres) + break; + + if (k != new_kpts[j].size()) + ++foundCounter; + + } + if (foundCounter == (int)case_num) + result.push_back(okp); + } + + sort(result.begin(), result.end(), sortByR()); + sort(exp.begin(), exp.end(), sortByR()); + + if (result.size() != exp.size()) + { + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return false; + } + + int foundCounter1 = 0; + for(size_t i = 0; i < exp.size(); ++i) + { + const KeyPoint& e = exp[i]; + size_t j = 0; + for(; j < result.size(); ++j) + { + const KeyPoint& r = result[i]; + if (norm(r.pt-e.pt) < nthres && fabs(r.size - e.size) < thres) + break; + } + if (j != result.size()) + ++foundCounter1; + } + + int foundCounter2 = 0; + for(size_t i = 0; i < result.size(); ++i) + { + const KeyPoint& r = result[i]; + size_t j = 0; + for(; j < exp.size(); ++j) + { + const KeyPoint& e = exp[i]; + if (norm(r.pt-e.pt) < nthres && fabs(r.size - e.size) < thres) + break; + } + if (j != exp.size()) + ++foundCounter2; + } + //showOrig(img, result); waitKey(); + + const float errorRate = 0.9f; + if (float(foundCounter1)/exp.size() < errorRate || float(foundCounter2)/result.size() < errorRate) + { + ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH); + return false; + } + return true; +} + +struct SurfNoMaskWrap +{ + const SURF& detector; + SurfNoMaskWrap(const SURF& surf) : detector(surf) {} + SurfNoMaskWrap& operator=(const SurfNoMaskWrap&); + void operator()(const Mat& img, vector& kpts) const { detector(img, Mat(), kpts); } +}; + +void CV_DetectorsTest::LoadExpected(const string& file, vector& out) +{ + Mat mat_exp; + FileStorage fs(file, FileStorage::READ); + if (fs.isOpened()) + { + read( fs["ResultVectorData"], mat_exp, Mat() ); + out.resize(mat_exp.cols / sizeof(KeyPoint)); + copy(mat_exp.ptr(), mat_exp.ptr() + out.size(), out.begin()); + } + else + { + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA); + out.clear(); + } +} + +void CV_DetectorsTest::run( int /*start_from*/ ) +{ + Mat img = imread(string(ts->get_data_path()) + "shared/graffiti.png", 0); + + if (img.empty()) + { + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + Mat to_test(img.size() * 2, img.type(), Scalar(0)); + Mat roi = to_test(Rect(img.rows/2, img.cols/2, img.cols, img.rows)); + img.copyTo(roi); + GaussianBlur(to_test, to_test, Size(3, 3), 1.5); + + vector exp; + LoadExpected(string(ts->get_data_path()) + "detectors/surf.xml", exp); + if (exp.empty()) + return; + + if (!testDetector(to_test, SurfNoMaskWrap(SURF(1536+512+512, 2)), exp)) + return; + + LoadExpected(string(ts->get_data_path()) + "detectors/star.xml", exp); + if (exp.empty()) + return; + + if (!testDetector(to_test, StarDetector(45, 30, 10, 8, 5), exp)) + return; + + ts->set_failed_test_info( cvtest::TS::OK); +} + + +TEST(Features2d_Detectors, regression) { CV_DetectorsTest test; test.safe_run(); } + + + diff --git a/modules/features2d/test/test_fast.cpp b/modules/features2d/test/test_fast.cpp new file mode 100644 index 000000000..416fe14c3 --- /dev/null +++ b/modules/features2d/test/test_fast.cpp @@ -0,0 +1,126 @@ +/*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" + +using namespace cv; + +class CV_FastTest : public cvtest::BaseTest +{ +public: + CV_FastTest(); + ~CV_FastTest(); +protected: + void run(int); +}; + +CV_FastTest::CV_FastTest() {} +CV_FastTest::~CV_FastTest() {} + +void CV_FastTest::run( int ) +{ + Mat image1 = imread(string(ts->get_data_path()) + "inpaint/orig.jpg"); + Mat image2 = imread(string(ts->get_data_path()) + "cameracalibration/chess9.jpg"); + string xml = string(ts->get_data_path()) + "fast/result.xml"; + + if (image1.empty() || image2.empty()) + { + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + Mat gray1, gray2; + cvtColor(image1, gray1, CV_BGR2GRAY); + cvtColor(image2, gray2, CV_BGR2GRAY); + + vector keypoints1; + vector keypoints2; + FAST(gray1, keypoints1, 30); + FAST(gray2, keypoints2, 30); + + for(size_t i = 0; i < keypoints1.size(); ++i) + { + const KeyPoint& kp = keypoints1[i]; + cv::circle(image1, kp.pt, cvRound(kp.size/2), CV_RGB(255, 0, 0)); + } + + for(size_t i = 0; i < keypoints2.size(); ++i) + { + const KeyPoint& kp = keypoints2[i]; + cv::circle(image2, kp.pt, cvRound(kp.size/2), CV_RGB(255, 0, 0)); + } + + Mat kps1(1, (int)(keypoints1.size() * sizeof(KeyPoint)), CV_8U, &keypoints1[0]); + Mat kps2(1, (int)(keypoints2.size() * sizeof(KeyPoint)), CV_8U, &keypoints2[0]); + + FileStorage fs(xml, FileStorage::READ); + if (!fs.isOpened()) + { + fs.open(xml, FileStorage::WRITE); + fs << "exp_kps1" << kps1; + fs << "exp_kps2" << kps2; + fs.release(); + } + + if (!fs.isOpened()) + fs.open(xml, FileStorage::READ); + + Mat exp_kps1, exp_kps2; + read( fs["exp_kps1"], exp_kps1, Mat() ); + read( fs["exp_kps2"], exp_kps2, Mat() ); + fs.release(); + + if ( 0 != norm(exp_kps1, kps1, NORM_L2) || 0 != norm(exp_kps2, kps2, NORM_L2)) + { + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + + /* cv::namedWindow("Img1"); cv::imshow("Img1", image1); + cv::namedWindow("Img2"); cv::imshow("Img2", image2); + cv::waitKey(0);*/ + + ts->set_failed_test_info(cvtest::TS::OK); +} + +TEST(Features2d_FAST, regression) { CV_FastTest test; test.safe_run(); } + diff --git a/modules/features2d/test/test_features2d.cpp b/modules/features2d/test/test_features2d.cpp new file mode 100644 index 000000000..cab7b0b55 --- /dev/null +++ b/modules/features2d/test/test_features2d.cpp @@ -0,0 +1,1038 @@ +/*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/highgui/highgui.hpp" + +using namespace std; +using namespace cv; + +const string FEATURES2D_DIR = "features2d"; +const string DETECTOR_DIR = FEATURES2D_DIR + "/feature_detectors"; +const string DESCRIPTOR_DIR = FEATURES2D_DIR + "/descriptor_extractors"; +const string IMAGE_FILENAME = "tsukuba.png"; + +/****************************************************************************************\ +* Regression tests for feature detectors comparing keypoints. * +\****************************************************************************************/ + +class CV_FeatureDetectorTest : public cvtest::BaseTest +{ +public: + CV_FeatureDetectorTest( const Ptr& _fdetector ) : + fdetector(_fdetector) {} + +protected: + bool isSimilarKeypoints( const KeyPoint& p1, const KeyPoint& p2 ); + void compareKeypointSets( const vector& validKeypoints, const vector& calcKeypoints ); + + void emptyDataTest(); + void regressionTest(); // TODO test of detect() with mask + + virtual void run( int ); + + Ptr fdetector; +}; + +void CV_FeatureDetectorTest::emptyDataTest() +{ + // One image. + Mat image; + vector keypoints; + try + { + fdetector->detect( image, keypoints ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "detect() on empty image must not generate exception (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + if( !keypoints.empty() ) + { + ts->printf( cvtest::TS::LOG, "detect() on empty image must return empty keypoints vector (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + // Several images. + vector images; + vector > keypointCollection; + try + { + fdetector->detect( images, keypointCollection ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "detect() on empty image vector must not generate exception (2).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } +} + +bool CV_FeatureDetectorTest::isSimilarKeypoints( const KeyPoint& p1, const KeyPoint& p2 ) +{ + const float maxPtDif = 1.f; + const float maxSizeDif = 1.f; + const float maxAngleDif = 2.f; + const float maxResponseDif = 0.1f; + + float dist = (float)norm( p1.pt - p2.pt ); + return (dist < maxPtDif && + fabs(p1.size - p2.size) < maxSizeDif && + abs(p1.angle - p2.angle) < maxAngleDif && + abs(p1.response - p2.response) < maxResponseDif && + p1.octave == p2.octave && + p1.class_id == p2.class_id ); +} + +void CV_FeatureDetectorTest::compareKeypointSets( const vector& validKeypoints, const vector& calcKeypoints ) +{ + const float maxCountRatioDif = 0.01f; + + // Compare counts of validation and calculated keypoints. + float countRatio = (float)validKeypoints.size() / (float)calcKeypoints.size(); + if( countRatio < 1 - maxCountRatioDif || countRatio > 1.f + maxCountRatioDif ) + { + ts->printf( cvtest::TS::LOG, "Bad keypoints count ratio (validCount = %d, calcCount = %d).\n", + validKeypoints.size(), calcKeypoints.size() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + int progress = 0, progressCount = validKeypoints.size() * calcKeypoints.size(); + int badPointCount = 0, commonPointCount = max(validKeypoints.size(), calcKeypoints.size()); + for( size_t v = 0; v < validKeypoints.size(); v++ ) + { + int nearestIdx = -1; + float minDist = std::numeric_limits::max(); + + for( size_t c = 0; c < calcKeypoints.size(); c++ ) + { + progress = update_progress( progress, v*calcKeypoints.size() + c, progressCount, 0 ); + float curDist = (float)norm( calcKeypoints[c].pt - validKeypoints[v].pt ); + if( curDist < minDist ) + { + minDist = curDist; + nearestIdx = c; + } + } + + assert( minDist >= 0 ); + if( !isSimilarKeypoints( validKeypoints[v], calcKeypoints[nearestIdx] ) ) + badPointCount++; + } + ts->printf( cvtest::TS::LOG, "badPointCount = %d; validPointCount = %d; calcPointCount = %d\n", + badPointCount, validKeypoints.size(), calcKeypoints.size() ); + if( badPointCount > 0.9 * commonPointCount ) + { + ts->printf( cvtest::TS::LOG, " - Bad accuracy!\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + return; + } + ts->printf( cvtest::TS::LOG, " - OK\n" ); +} + +void CV_FeatureDetectorTest::regressionTest() +{ + assert( !fdetector.empty() ); + string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; + string resFilename = string(ts->get_data_path()) + DETECTOR_DIR + "/" + string(name) + ".xml.gz"; + + // Read the test image. + Mat image = imread( imgFilename ); + if( image.empty() ) + { + ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + FileStorage fs( resFilename, FileStorage::READ ); + + // Compute keypoints. + vector calcKeypoints; + fdetector->detect( image, calcKeypoints ); + + if( fs.isOpened() ) // Compare computed and valid keypoints. + { + // TODO compare saved feature detector params with current ones + + // Read validation keypoints set. + vector validKeypoints; + read( fs["keypoints"], validKeypoints ); + if( validKeypoints.empty() ) + { + ts->printf( cvtest::TS::LOG, "Keypoints can not be read.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + compareKeypointSets( validKeypoints, calcKeypoints ); + } + else // Write detector parameters and computed keypoints as validation data. + { + fs.open( resFilename, FileStorage::WRITE ); + if( !fs.isOpened() ) + { + ts->printf( cvtest::TS::LOG, "File %s can not be opened to write.\n", resFilename.c_str() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + else + { + fs << "detector_params" << "{"; + fdetector->write( fs ); + fs << "}"; + + write( fs, "keypoints", calcKeypoints ); + } + } +} + +void CV_FeatureDetectorTest::run( int /*start_from*/ ) +{ + if( fdetector.empty() ) + { + ts->printf( cvtest::TS::LOG, "Feature detector is empty.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + emptyDataTest(); + regressionTest(); + + ts->set_failed_test_info( cvtest::TS::OK ); +} + +/****************************************************************************************\ +* Regression tests for descriptor extractors. * +\****************************************************************************************/ +static void writeMatInBin( const Mat& mat, const string& filename ) +{ + FILE* f = fopen( filename.c_str(), "wb"); + if( f ) + { + int type = mat.type(); + fwrite( (void*)&mat.rows, sizeof(int), 1, f ); + fwrite( (void*)&mat.cols, sizeof(int), 1, f ); + fwrite( (void*)&type, sizeof(int), 1, f ); + int dataSize = mat.step * mat.rows * mat.channels(); + fwrite( (void*)&dataSize, sizeof(int), 1, f ); + fwrite( (void*)mat.data, 1, dataSize, f ); + fclose(f); + } +} + +static Mat readMatFromBin( const string& filename ) +{ + FILE* f = fopen( filename.c_str(), "rb" ); + if( f ) + { + int rows, cols, type, dataSize; + fread( (void*)&rows, sizeof(int), 1, f ); + fread( (void*)&cols, sizeof(int), 1, f ); + fread( (void*)&type, sizeof(int), 1, f ); + fread( (void*)&dataSize, sizeof(int), 1, f ); + + uchar* data = (uchar*)cvAlloc(dataSize); + fread( (void*)data, 1, dataSize, f ); + fclose(f); + + return Mat( rows, cols, type, data ); + } + return Mat(); +} + +template +class CV_DescriptorExtractorTest : public cvtest::BaseTest +{ +public: + typedef typename Distance::ValueType ValueType; + typedef typename Distance::ResultType DistanceType; + + CV_DescriptorExtractorTest( DistanceType _maxDist, const Ptr& _dextractor, float _prevTime, + Distance d = Distance() ): + maxDist(_maxDist), prevTime(_prevTime), dextractor(_dextractor), distance(d) {} +protected: + virtual void createDescriptorExtractor() {} + + void compareDescriptors( const Mat& validDescriptors, const Mat& calcDescriptors ) + { + if( validDescriptors.size != calcDescriptors.size || validDescriptors.type() != calcDescriptors.type() ) + { + ts->printf(cvtest::TS::LOG, "Valid and computed descriptors matrices must have the same size and type.\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + CV_Assert( DataType::type == validDescriptors.type() ); + + int dimension = validDescriptors.cols; + DistanceType curMaxDist = std::numeric_limits::min(); + for( int y = 0; y < validDescriptors.rows; y++ ) + { + DistanceType dist = distance( validDescriptors.ptr(y), calcDescriptors.ptr(y), dimension ); + if( dist > curMaxDist ) + curMaxDist = dist; + } + + stringstream ss; + ss << "Max distance between valid and computed descriptors " << curMaxDist; + if( curMaxDist < maxDist ) + ss << "." << endl; + else + { + ss << ">" << maxDist << " - bad accuracy!"<< endl; + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + } + ts->printf(cvtest::TS::LOG, ss.str().c_str() ); + } + + void emptyDataTest() + { + assert( !dextractor.empty() ); + + // One image. + Mat image; + vector keypoints; + Mat descriptors; + + try + { + dextractor->compute( image, keypoints, descriptors ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "compute() on empty image and empty keypoints must not generate exception (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + } + + image.create( 50, 50, CV_8UC3 ); + try + { + dextractor->compute( image, keypoints, descriptors ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "compute() on nonempty image and empty keypoints must not generate exception (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + } + + // Several images. + vector images; + vector > keypointsCollection; + vector descriptorsCollection; + try + { + dextractor->compute( images, keypointsCollection, descriptorsCollection ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "compute() on empty images and empty keypoints collection must not generate exception (2).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + } + } + + void regressionTest() + { + assert( !dextractor.empty() ); + + // Read the test image. + string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; + + Mat img = imread( imgFilename ); + if( img.empty() ) + { + ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + vector keypoints; + FileStorage fs( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::READ ); + if( fs.isOpened() ) + { + read( fs.getFirstTopLevelNode(), keypoints ); + + Mat calcDescriptors; + double t = (double)getTickCount(); + dextractor->compute( img, keypoints, calcDescriptors ); + t = getTickCount() - t; + ts->printf(cvtest::TS::LOG, "\nAverage time of computiting one descriptor = %g ms (previous time = %g ms).\n", t/((double)cvGetTickFrequency()*1000.)/calcDescriptors.rows, prevTime ); + + if( calcDescriptors.rows != (int)keypoints.size() ) + { + ts->printf( cvtest::TS::LOG, "Count of computed descriptors and keypoints count must be equal.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + if( calcDescriptors.cols != dextractor->descriptorSize() || calcDescriptors.type() != dextractor->descriptorType() ) + { + ts->printf( cvtest::TS::LOG, "Incorrect descriptor size or descriptor type.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + // TODO read and write descriptor extractor parameters and check them + Mat validDescriptors = readDescriptors(); + if( !validDescriptors.empty() ) + compareDescriptors( validDescriptors, calcDescriptors ); + else + { + if( !writeDescriptors( calcDescriptors ) ) + { + ts->printf( cvtest::TS::LOG, "Descriptors can not be written.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + } + } + else + { + ts->printf( cvtest::TS::LOG, "Compute and write keypoints.\n" ); + fs.open( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::WRITE ); + if( fs.isOpened() ) + { + SurfFeatureDetector fd; + fd.detect(img, keypoints); + write( fs, "keypoints", keypoints ); + } + else + { + ts->printf(cvtest::TS::LOG, "File for writting keypoints can not be opened.\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + } + } + + void run(int) + { + createDescriptorExtractor(); + if( dextractor.empty() ) + { + ts->printf(cvtest::TS::LOG, "Descriptor extractor is empty.\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + emptyDataTest(); + regressionTest(); + + ts->set_failed_test_info( cvtest::TS::OK ); + } + + virtual Mat readDescriptors() + { + Mat res = readMatFromBin( string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) ); + return res; + } + + virtual bool writeDescriptors( Mat& descs ) + { + writeMatInBin( descs, string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) ); + return true; + } + + const DistanceType maxDist; + const float prevTime; + + Ptr dextractor; + Distance distance; + +private: + CV_DescriptorExtractorTest& operator=(const CV_DescriptorExtractorTest&) { return *this; } +}; + +template +class CV_CalonderDescriptorExtractorTest : public CV_DescriptorExtractorTest +{ +public: + CV_CalonderDescriptorExtractorTest( const char* testName, float _normDif, float _prevTime ) : + CV_DescriptorExtractorTest( testName, _normDif, Ptr(), _prevTime ) + {} + +protected: + virtual void createDescriptorExtractor() + { + CV_DescriptorExtractorTest::dextractor = + new CalonderDescriptorExtractor( string(CV_DescriptorExtractorTest::ts->get_data_path()) + + FEATURES2D_DIR + "/calonder_classifier.rtc"); + } +}; + +/****************************************************************************************\ +* Algorithmic tests for descriptor matchers * +\****************************************************************************************/ +class CV_DescriptorMatcherTest : public cvtest::BaseTest +{ +public: + CV_DescriptorMatcherTest( const Ptr& _dmatcher, float _badPart ) : + badPart(_badPart), dmatcher(_dmatcher) + {} +protected: + static const int dim = 500; + static const int queryDescCount = 300; // must be even number because we split train data in some cases in two + static const int countFactor = 4; // do not change it + const float badPart; + + virtual void run( int ); + void generateData( Mat& query, Mat& train ); + + void emptyDataTest(); + void matchTest( const Mat& query, const Mat& train ); + void knnMatchTest( const Mat& query, const Mat& train ); + void radiusMatchTest( const Mat& query, const Mat& train ); + + Ptr dmatcher; +private: + CV_DescriptorMatcherTest& operator=(const CV_DescriptorMatcherTest&) { return *this; } +}; + +void CV_DescriptorMatcherTest::emptyDataTest() +{ + assert( !dmatcher.empty() ); + Mat queryDescriptors, trainDescriptors, mask; + vector trainDescriptorCollection, masks; + vector matches; + vector > vmatches; + + try + { + dmatcher->match( queryDescriptors, trainDescriptors, matches, mask ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + try + { + dmatcher->knnMatch( queryDescriptors, trainDescriptors, vmatches, 2, mask ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + try + { + dmatcher->radiusMatch( queryDescriptors, trainDescriptors, vmatches, 10.f, mask ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + try + { + dmatcher->add( trainDescriptorCollection ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "add() on empty descriptors must not generate exception.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + try + { + dmatcher->match( queryDescriptors, matches, masks ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (2).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + try + { + dmatcher->knnMatch( queryDescriptors, vmatches, 2, masks ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (2).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + try + { + dmatcher->radiusMatch( queryDescriptors, vmatches, 10.f, masks ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (2).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + +} + +void CV_DescriptorMatcherTest::generateData( Mat& query, Mat& train ) +{ + RNG& rng = theRNG(); + + // Generate query descriptors randomly. + // Descriptor vector elements are integer values. + Mat buf( queryDescCount, dim, CV_32SC1 ); + rng.fill( buf, RNG::UNIFORM, Scalar::all(0), Scalar(3) ); + buf.convertTo( query, CV_32FC1 ); + + // Generate train decriptors as follows: + // copy each query descriptor to train set countFactor times + // and perturb some one element of the copied descriptors in + // in ascending order. General boundaries of the perturbation + // are (0.f, 1.f). + train.create( query.rows*countFactor, query.cols, CV_32FC1 ); + float step = 1.f / countFactor; + for( int qIdx = 0; qIdx < query.rows; qIdx++ ) + { + Mat queryDescriptor = query.row(qIdx); + for( int c = 0; c < countFactor; c++ ) + { + int tIdx = qIdx * countFactor + c; + Mat trainDescriptor = train.row(tIdx); + queryDescriptor.copyTo( trainDescriptor ); + int elem = rng(dim); + float diff = rng.uniform( step*c, step*(c+1) ); + trainDescriptor.at(0, elem) += diff; + } + } +} + +void CV_DescriptorMatcherTest::matchTest( const Mat& query, const Mat& train ) +{ + dmatcher->clear(); + + // test const version of match() + { + vector matches; + dmatcher->match( query, train, matches ); + + if( (int)matches.size() != queryDescCount ) + { + ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + else + { + int badCount = 0; + for( size_t i = 0; i < matches.size(); i++ ) + { + DMatch match = matches[i]; + if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor) || (match.imgIdx != 0) ) + badCount++; + } + if( (float)badCount > (float)queryDescCount*badPart ) + { + ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (1).\n", + (float)badCount/(float)queryDescCount ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + } + } + + // test version of match() with add() + { + vector matches; + // make add() twice to test such case + dmatcher->add( vector(1,train.rowRange(0, train.rows/2)) ); + dmatcher->add( vector(1,train.rowRange(train.rows/2, train.rows)) ); + // prepare masks (make first nearest match illegal) + vector masks(2); + for(int mi = 0; mi < 2; mi++ ) + { + masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1)); + for( int di = 0; di < queryDescCount/2; di++ ) + masks[mi].col(di*countFactor).setTo(Scalar::all(0)); + } + + dmatcher->match( query, matches, masks ); + + if( (int)matches.size() != queryDescCount ) + { + ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function (2).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + else + { + int badCount = 0; + for( size_t i = 0; i < matches.size(); i++ ) + { + DMatch match = matches[i]; + int shift = dmatcher->isMaskSupported() ? 1 : 0; + { + if( i < queryDescCount/2 ) + { + if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + shift) || (match.imgIdx != 0) ) + badCount++; + } + else + { + if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + shift) || (match.imgIdx != 1) ) + badCount++; + } + } + } + if( (float)badCount > (float)queryDescCount*badPart ) + { + ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (2).\n", + (float)badCount/(float)queryDescCount ); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + } + } + } +} + +void CV_DescriptorMatcherTest::knnMatchTest( const Mat& query, const Mat& train ) +{ + dmatcher->clear(); + + // test const version of knnMatch() + { + const int knn = 3; + + vector > matches; + dmatcher->knnMatch( query, train, matches, knn ); + + if( (int)matches.size() != queryDescCount ) + { + ts->printf(cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + else + { + int badCount = 0; + for( size_t i = 0; i < matches.size(); i++ ) + { + if( (int)matches[i].size() != knn ) + badCount++; + else + { + int localBadCount = 0; + for( int k = 0; k < knn; k++ ) + { + DMatch match = matches[i][k]; + if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor+k) || (match.imgIdx != 0) ) + localBadCount++; + } + badCount += localBadCount > 0 ? 1 : 0; + } + } + if( (float)badCount > (float)queryDescCount*badPart ) + { + ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (1).\n", + (float)badCount/(float)queryDescCount ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + } + } + + // test version of knnMatch() with add() + { + const int knn = 2; + vector > matches; + // make add() twice to test such case + dmatcher->add( vector(1,train.rowRange(0, train.rows/2)) ); + dmatcher->add( vector(1,train.rowRange(train.rows/2, train.rows)) ); + // prepare masks (make first nearest match illegal) + vector masks(2); + for(int mi = 0; mi < 2; mi++ ) + { + masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1)); + for( int di = 0; di < queryDescCount/2; di++ ) + masks[mi].col(di*countFactor).setTo(Scalar::all(0)); + } + + dmatcher->knnMatch( query, matches, knn, masks ); + + if( (int)matches.size() != queryDescCount ) + { + ts->printf(cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (2).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + else + { + int badCount = 0; + int shift = dmatcher->isMaskSupported() ? 1 : 0; + for( size_t i = 0; i < matches.size(); i++ ) + { + if( (int)matches[i].size() != knn ) + badCount++; + else + { + int localBadCount = 0; + for( int k = 0; k < knn; k++ ) + { + DMatch match = matches[i][k]; + { + if( i < queryDescCount/2 ) + { + if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + k + shift) || + (match.imgIdx != 0) ) + localBadCount++; + } + else + { + if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + k + shift) || + (match.imgIdx != 1) ) + localBadCount++; + } + } + } + badCount += localBadCount > 0 ? 1 : 0; + } + } + if( (float)badCount > (float)queryDescCount*badPart ) + { + ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (2).\n", + (float)badCount/(float)queryDescCount ); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + } + } + } +} + +void CV_DescriptorMatcherTest::radiusMatchTest( const Mat& query, const Mat& train ) +{ + dmatcher->clear(); + // test const version of match() + { + const float radius = 1.f/countFactor; + vector > matches; + dmatcher->radiusMatch( query, train, matches, radius ); + + if( (int)matches.size() != queryDescCount ) + { + ts->printf(cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + else + { + int badCount = 0; + for( size_t i = 0; i < matches.size(); i++ ) + { + if( (int)matches[i].size() != 1 ) + badCount++; + else + { + DMatch match = matches[i][0]; + if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor) || (match.imgIdx != 0) ) + badCount++; + } + } + if( (float)badCount > (float)queryDescCount*badPart ) + { + ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (1).\n", + (float)badCount/(float)queryDescCount ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + } + } + + // test version of match() with add() + { + int n = 3; + const float radius = 1.f/countFactor * n; + vector > matches; + // make add() twice to test such case + dmatcher->add( vector(1,train.rowRange(0, train.rows/2)) ); + dmatcher->add( vector(1,train.rowRange(train.rows/2, train.rows)) ); + // prepare masks (make first nearest match illegal) + vector masks(2); + for(int mi = 0; mi < 2; mi++ ) + { + masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1)); + for( int di = 0; di < queryDescCount/2; di++ ) + masks[mi].col(di*countFactor).setTo(Scalar::all(0)); + } + + dmatcher->radiusMatch( query, matches, radius, masks ); + + int curRes = cvtest::TS::OK; + if( (int)matches.size() != queryDescCount ) + { + ts->printf(cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + int badCount = 0; + int shift = dmatcher->isMaskSupported() ? 1 : 0; + int needMatchCount = dmatcher->isMaskSupported() ? n-1 : n; + for( size_t i = 0; i < matches.size(); i++ ) + { + if( (int)matches[i].size() != needMatchCount ) + badCount++; + else + { + int localBadCount = 0; + for( int k = 0; k < needMatchCount; k++ ) + { + DMatch match = matches[i][k]; + { + if( i < queryDescCount/2 ) + { + if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + k + shift) || + (match.imgIdx != 0) ) + localBadCount++; + } + else + { + if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + k + shift) || + (match.imgIdx != 1) ) + localBadCount++; + } + } + } + badCount += localBadCount > 0 ? 1 : 0; + } + } + if( (float)badCount > (float)queryDescCount*badPart ) + { + curRes = cvtest::TS::FAIL_INVALID_OUTPUT; + ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (2).\n", + (float)badCount/(float)queryDescCount ); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + } + } +} + +void CV_DescriptorMatcherTest::run( int ) +{ + Mat query, train; + generateData( query, train ); + + matchTest( query, train ); + + knnMatchTest( query, train ); + + radiusMatchTest( query, train ); +} + +/****************************************************************************************\ +* Tests registrations * +\****************************************************************************************/ + +/* + * Detectors + * "detector-fast, detector-gftt, detector-harris, detector-mser, detector-sift, detector-star, detector-surf, detector-grid-fast, detector-pyramid-fast" + */ + +/*CV_FeatureDetectorTest fastTest( "detector-fast", FeatureDetector::create("FAST") ); +CV_FeatureDetectorTest gfttTest( "detector-gftt", FeatureDetector::create("GFTT") ); +CV_FeatureDetectorTest harrisTest( "detector-harris", FeatureDetector::create("HARRIS") ); +CV_FeatureDetectorTest mserTest( "detector-mser", FeatureDetector::create("MSER") ); +CV_FeatureDetectorTest siftTest( "detector-sift", FeatureDetector::create("SIFT") ); +CV_FeatureDetectorTest starTest( "detector-star", FeatureDetector::create("STAR") ); +CV_FeatureDetectorTest surfTest( "detector-surf", FeatureDetector::create("SURF") ); +CV_FeatureDetectorTest gridFastfTest( "detector-grid-fast", FeatureDetector::create("GridFAST") ); +CV_FeatureDetectorTest pyramidFastTest( "detector-pyramid-fast", FeatureDetector::create("PyramidFAST") );*/ + +/* + * Descriptors + * "descriptor-sift, descriptor-surf, descriptor-calonder-uchar, descriptor-calonder-float, descriptor-brief, descriptor-opponent-sift, descriptor-opponent-surf" + */ +TEST( Features2d_Descriptor_SIFT, regression ) +{ + CV_DescriptorExtractorTest > test( 0.03f, DescriptorExtractor::create("SIFT"), 8.06652f ); + test.safe_run(); +} + +TEST( Features2d_Descriptor_SURF, regression ) +{ + CV_DescriptorExtractorTest > test( 0.035f, DescriptorExtractor::create("SURF"), 0.147372f ); + test.safe_run(); +} + +TEST( Features2d_Descriptor_BRIEF, regression ) +{ + CV_DescriptorExtractorTest test( 1, DescriptorExtractor::create("BRIEF"), 0.00527548f ); + test.safe_run(); +} + +TEST( Features2d_Descriptor_OpponentSIFT, regression ) +{ + CV_DescriptorExtractorTest > test( 0.18f, DescriptorExtractor::create("OpponentSIFT"), 8.06652f ); + test.safe_run(); +} + +TEST( Features2d_Descriptor_OpponentSURF, regression ) +{ + CV_DescriptorExtractorTest > test( 0.18f, DescriptorExtractor::create("OpponentSURF"), 0.147372f ); + test.safe_run(); +} + +#if CV_SSE2 +TEST( Features2d_Descriptor_Calonder_uchar, regression ) +{ + CV_CalonderDescriptorExtractorTest > test( + std::numeric_limits::epsilon() + 1, + 0.0132175f ); + test.safe_run(); +} + +TEST( Features2d_Descriptor_Calonder_float, regression ) +{ + CV_CalonderDescriptorExtractorTest > test( + std::numeric_limits::epsilon(), + 0.0221308f ); + test.safe_run(); +} +#endif // CV_SSE2 + +/* + * Matchers + * "descriptor-matcher-brute-force, descriptor-matcher-flann-based" + */ +TEST( Features2d_DescriptorMatcher_BruteForce_L2, accuracy ) +{ + CV_DescriptorMatcherTest test( new BruteForceMatcher >, 0.01f ); + test.safe_run(); +} + +TEST( Features2d_DescriptorMatcher_FLANN, accuracy ) +{ + CV_DescriptorMatcherTest test( new FlannBasedMatcher, 0.04f ); + test.safe_run(); +} + diff --git a/modules/features2d/test/test_main.cpp b/modules/features2d/test/test_main.cpp new file mode 100644 index 000000000..6b2499344 --- /dev/null +++ b/modules/features2d/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("cv") diff --git a/modules/features2d/test/test_mser.cpp b/modules/features2d/test/test_mser.cpp new file mode 100644 index 000000000..efb5b22fa --- /dev/null +++ b/modules/features2d/test/test_mser.cpp @@ -0,0 +1,203 @@ +/*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 +#include +using namespace std; +using namespace cv; + +class CV_MserTest : public cvtest::BaseTest +{ +public: + CV_MserTest(); +protected: + void run(int); + int LoadBoxes(const char* path, vector& boxes); + int SaveBoxes(const char* path, const vector& boxes); + int CompareBoxes(const vector& boxes1,const vector& boxes2, float max_rel_diff = 0.01f); +}; + +CV_MserTest::CV_MserTest() +{ +} + +int CV_MserTest::LoadBoxes(const char* path, vector& boxes) +{ + boxes.clear(); + FILE* f = fopen(path,"r"); + + if (f==NULL) + { + return 0; + } + + while (!feof(f)) + { + CvBox2D box; + fscanf(f,"%f,%f,%f,%f,%f\n",&box.angle,&box.center.x,&box.center.y,&box.size.width,&box.size.height); + boxes.push_back(box); + } + fclose(f); + return 1; +} + +int CV_MserTest::SaveBoxes(const char* path, const vector& boxes) +{ + FILE* f = fopen(path,"w"); + if (f==NULL) + { + return 0; + } + for (int i=0;i<(int)boxes.size();i++) + { + fprintf(f,"%f,%f,%f,%f,%f\n",boxes[i].angle,boxes[i].center.x,boxes[i].center.y,boxes[i].size.width,boxes[i].size.height); + } + fclose(f); + return 1; +} + +int CV_MserTest::CompareBoxes(const vector& boxes1,const vector& boxes2, float max_rel_diff) +{ + if (boxes1.size() != boxes2.size()) + return 0; + + for (int i=0; i<(int)boxes1.size();i++) + { + float rel_diff; + if (!((boxes1[i].angle == 0.0f) && (abs(boxes2[i].angle) < max_rel_diff))) + { + rel_diff = abs(boxes1[i].angle-boxes2[i].angle)/abs(boxes1[i].angle); + if (rel_diff > max_rel_diff) + return i; + } + + if (!((boxes1[i].center.x == 0.0f) && (abs(boxes2[i].center.x) < max_rel_diff))) + { + rel_diff = abs(boxes1[i].center.x-boxes2[i].center.x)/abs(boxes1[i].center.x); + if (rel_diff > max_rel_diff) + return i; + } + + if (!((boxes1[i].center.y == 0.0f) && (abs(boxes2[i].center.y) < max_rel_diff))) + { + rel_diff = abs(boxes1[i].center.y-boxes2[i].center.y)/abs(boxes1[i].center.y); + if (rel_diff > max_rel_diff) + return i; + } + if (!((boxes1[i].size.width == 0.0f) && (abs(boxes2[i].size.width) < max_rel_diff))) + { + rel_diff = abs(boxes1[i].size.width-boxes2[i].size.width)/abs(boxes1[i].size.width); + if (rel_diff > max_rel_diff) + return i; + } + + if (!((boxes1[i].size.height == 0.0f) && (abs(boxes2[i].size.height) < max_rel_diff))) + { + rel_diff = abs(boxes1[i].size.height-boxes2[i].size.height)/abs(boxes1[i].size.height); + if (rel_diff > max_rel_diff) + return i; + } + } + + return -1; +} + +void CV_MserTest::run(int) +{ + string image_path = string(ts->get_data_path()) + "mser/puzzle.png"; + + IplImage* img = cvLoadImage( image_path.c_str()); + if (!img) + { + ts->printf( cvtest::TS::LOG, "Unable to open image mser/puzzle.png\n"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA); + return; + } + + CvSeq* contours; + CvMemStorage* storage= cvCreateMemStorage(); + IplImage* hsv = cvCreateImage( cvGetSize( img ), IPL_DEPTH_8U, 3 ); + cvCvtColor( img, hsv, CV_BGR2YCrCb ); + CvMSERParams params = cvMSERParams();//cvMSERParams( 5, 60, cvRound(.2*img->width*img->height), .25, .2 ); + cvExtractMSER( hsv, NULL, &contours, storage, params ); + + vector boxes; + vector boxes_orig; + for ( int i = 0; i < contours->total; i++ ) + { + CvContour* r = *(CvContour**)cvGetSeqElem( contours, i ); + CvBox2D box = cvFitEllipse2( r ); + box.angle=(float)CV_PI/2-box.angle; + boxes.push_back(box); + } + + string boxes_path = string(ts->get_data_path()) + "mser/boxes.txt"; + + if (!LoadBoxes(boxes_path.c_str(),boxes_orig)) + { + SaveBoxes(boxes_path.c_str(),boxes); + ts->printf( cvtest::TS::LOG, "Unable to open data file mser/boxes.txt\n"); + ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA); + return; + } + + const float dissimularity = 0.01f; + int n_box = CompareBoxes(boxes_orig,boxes,dissimularity); + if (n_box < 0) + { + ts->set_failed_test_info(cvtest::TS::OK); + } + else + { + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + ts->printf( cvtest::TS::LOG, "Incorrect correspondence in %d box\n",n_box); + } + + cvReleaseMemStorage(&storage); + cvReleaseImage(&hsv); + cvReleaseImage(&img); +} + +TEST(Features2d_MSER, regression) { CV_MserTest test; test.safe_run(); } + diff --git a/modules/features2d/test/test_nearestneighbors.cpp b/modules/features2d/test/test_nearestneighbors.cpp new file mode 100644 index 000000000..b97f59775 --- /dev/null +++ b/modules/features2d/test/test_nearestneighbors.cpp @@ -0,0 +1,523 @@ +/*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 +#include +#include + +using namespace cv; +using namespace cv::flann; + +//-------------------------------------------------------------------------------- +class NearestNeighborTest : public cvtest::BaseTest +{ +public: + NearestNeighborTest() {} +protected: + static const int minValue = 0; + static const int maxValue = 1; + static const int dims = 30; + static const int featuresCount = 2000; + static const int K = 1; // * should also test 2nd nn etc.? + + + virtual void run( int start_from ); + virtual void createModel( const Mat& data ) = 0; + virtual int findNeighbors( Mat& points, Mat& neighbors ) = 0; + virtual int checkGetPoins( const Mat& data ); + virtual int checkFindBoxed(); + virtual int checkFind( const Mat& data ); + virtual void releaseModel() = 0; +}; + +int NearestNeighborTest::checkGetPoins( const Mat& ) +{ + return cvtest::TS::OK; +} + +int NearestNeighborTest::checkFindBoxed() +{ + return cvtest::TS::OK; +} + +int NearestNeighborTest::checkFind( const Mat& data ) +{ + int code = cvtest::TS::OK; + int pointsCount = 1000; + float noise = 0.2f; + + RNG rng; + Mat points( pointsCount, dims, CV_32FC1 ); + Mat results( pointsCount, K, CV_32SC1 ); + + std::vector fmap( pointsCount ); + for( int pi = 0; pi < pointsCount; pi++ ) + { + int fi = rng.next() % featuresCount; + fmap[pi] = fi; + for( int d = 0; d < dims; d++ ) + points.at(pi, d) = data.at(fi, d) + rng.uniform(0.0f, 1.0f) * noise; + } + + code = findNeighbors( points, results ); + + if( code == cvtest::TS::OK ) + { + int correctMatches = 0; + for( int pi = 0; pi < pointsCount; pi++ ) + { + if( fmap[pi] == results.at(pi, 0) ) + correctMatches++; + } + + double correctPerc = correctMatches / (double)pointsCount; + if (correctPerc < .75) + { + ts->printf( cvtest::TS::LOG, "correct_perc = %d\n", correctPerc ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + } + + return code; +} + +void NearestNeighborTest::run( int /*start_from*/ ) { + int code = cvtest::TS::OK, tempCode; + Mat desc( featuresCount, dims, CV_32FC1 ); + randu( desc, Scalar(minValue), Scalar(maxValue) ); + + createModel( desc ); + + tempCode = checkGetPoins( desc ); + if( tempCode != cvtest::TS::OK ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy of GetPoints \n" ); + code = tempCode; + } + + tempCode = checkFindBoxed(); + if( tempCode != cvtest::TS::OK ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy of FindBoxed \n" ); + code = tempCode; + } + + tempCode = checkFind( desc ); + if( tempCode != cvtest::TS::OK ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy of Find \n" ); + code = tempCode; + } + + releaseModel(); + + ts->set_failed_test_info( code ); +} + +//-------------------------------------------------------------------------------- +class CV_LSHTest : public NearestNeighborTest +{ +public: + CV_LSHTest() {} +protected: + virtual void createModel( const Mat& data ); + virtual int findNeighbors( Mat& points, Mat& neighbors ); + virtual void releaseModel(); + struct CvLSH* lsh; + CvMat desc; +}; + +void CV_LSHTest::createModel( const Mat& data ) +{ + desc = data; + lsh = cvCreateMemoryLSH( data.cols, data.rows, 70, 20, CV_32FC1 ); + cvLSHAdd( lsh, &desc ); +} + +int CV_LSHTest::findNeighbors( Mat& points, Mat& neighbors ) +{ + const int emax = 20; + Mat dist( points.rows, neighbors.cols, CV_64FC1); + CvMat _dist = dist, _points = points, _neighbors = neighbors; + cvLSHQuery( lsh, &_points, &_neighbors, &_dist, neighbors.cols, emax ); + return cvtest::TS::OK; +} + +void CV_LSHTest::releaseModel() +{ + cvReleaseLSH( &lsh ); +} + +//-------------------------------------------------------------------------------- +class CV_FeatureTreeTest_C : public NearestNeighborTest +{ +public: + CV_FeatureTreeTest_C() {} +protected: + virtual int findNeighbors( Mat& points, Mat& neighbors ); + virtual void releaseModel(); + CvFeatureTree* tr; + CvMat desc; +}; + +int CV_FeatureTreeTest_C::findNeighbors( Mat& points, Mat& neighbors ) +{ + const int emax = 20; + Mat dist( points.rows, neighbors.cols, CV_64FC1); + CvMat _dist = dist, _points = points, _neighbors = neighbors; + cvFindFeatures( tr, &_points, &_neighbors, &_dist, neighbors.cols, emax ); + return cvtest::TS::OK; +} + +void CV_FeatureTreeTest_C::releaseModel() +{ + cvReleaseFeatureTree( tr ); +} + +//-------------------------------------- +class CV_SpillTreeTest_C : public CV_FeatureTreeTest_C +{ +public: + CV_SpillTreeTest_C() {} +protected: + virtual void createModel( const Mat& data ); +}; + +void CV_SpillTreeTest_C::createModel( const Mat& data ) +{ + desc = data; + tr = cvCreateSpillTree( &desc ); +} + +//-------------------------------------- +class CV_KDTreeTest_C : public CV_FeatureTreeTest_C +{ +public: + CV_KDTreeTest_C() {} +protected: + virtual void createModel( const Mat& data ); + virtual int checkFindBoxed(); +}; + +void CV_KDTreeTest_C::createModel( const Mat& data ) +{ + desc = data; + tr = cvCreateKDTree( &desc ); +} + +int CV_KDTreeTest_C::checkFindBoxed() +{ + Mat min(1, dims, CV_32FC1 ), max(1, dims, CV_32FC1 ), indices( 1, 1, CV_32SC1 ); + float l = minValue, r = maxValue; + min.setTo(Scalar(l)), max.setTo(Scalar(r)); + CvMat _min = min, _max = max, _indices = indices; + // TODO check indices + if( cvFindFeaturesBoxed( tr, &_min, &_max, &_indices ) != featuresCount ) + return cvtest::TS::FAIL_BAD_ACCURACY; + return cvtest::TS::OK; +} + +//-------------------------------------------------------------------------------- +class CV_KDTreeTest_CPP : public NearestNeighborTest +{ +public: + CV_KDTreeTest_CPP() {} +protected: + virtual void createModel( const Mat& data ); + virtual int checkGetPoins( const Mat& data ); + virtual int findNeighbors( Mat& points, Mat& neighbors ); + virtual int checkFindBoxed(); + virtual void releaseModel(); + KDTree* tr; +}; + + +void CV_KDTreeTest_CPP::createModel( const Mat& data ) +{ + tr = new KDTree( data, false ); +} + +int CV_KDTreeTest_CPP::checkGetPoins( const Mat& data ) +{ + Mat res1( data.size(), data.type() ), + res2( data.size(), data.type() ), + res3( data.size(), data.type() ); + Mat idxs( 1, data.rows, CV_32SC1 ); + for( int pi = 0; pi < data.rows; pi++ ) + { + idxs.at(0, pi) = pi; + // 1st way + const float* point = tr->getPoint(pi); + for( int di = 0; di < data.cols; di++ ) + res1.at(pi, di) = point[di]; + } + // 2nd way + tr->getPoints( idxs.ptr(0), data.rows, res2 ); + + // 3d way + tr->getPoints( idxs, res3 ); + + if( norm( res1, data, NORM_L1) != 0 || + norm( res2, data, NORM_L1) != 0 || + norm( res3, data, NORM_L1) != 0) + return cvtest::TS::FAIL_BAD_ACCURACY; + return cvtest::TS::OK; +} + +int CV_KDTreeTest_CPP::checkFindBoxed() +{ + vector min( dims, minValue), max(dims, maxValue); + vector indices; + tr->findOrthoRange( &min[0], &max[0], &indices ); + // TODO check indices + if( (int)indices.size() != featuresCount) + return cvtest::TS::FAIL_BAD_ACCURACY; + return cvtest::TS::OK; +} + +int CV_KDTreeTest_CPP::findNeighbors( Mat& points, Mat& neighbors ) +{ + const int emax = 20; + Mat neighbors2( neighbors.size(), CV_32SC1 ); + int j; + vector min(points.cols, minValue); + vector max(points.cols, maxValue); + for( int pi = 0; pi < points.rows; pi++ ) + { + // 1st way + tr->findNearest( points.ptr(pi), neighbors.cols, emax, neighbors.ptr(pi) ); + + // 2nd way + vector neighborsIdx2( neighbors2.cols, 0 ); + tr->findNearest( points.ptr(pi), neighbors2.cols, emax, &neighborsIdx2 ); + vector::const_iterator it2 = neighborsIdx2.begin(); + for( j = 0; it2 != neighborsIdx2.end(); ++it2, j++ ) + neighbors2.at(pi,j) = *it2; + } + + // compare results + if( norm( neighbors, neighbors2, NORM_L1 ) != 0 ) + return cvtest::TS::FAIL_BAD_ACCURACY; + + return cvtest::TS::OK; +} + +void CV_KDTreeTest_CPP::releaseModel() +{ + delete tr; +} + +//-------------------------------------------------------------------------------- +class CV_FlannTest : public NearestNeighborTest +{ +public: + CV_FlannTest() {} +protected: + void createIndex( const Mat& data, const IndexParams& params ); + int knnSearch( Mat& points, Mat& neighbors ); + int radiusSearch( Mat& points, Mat& neighbors ); + virtual void releaseModel(); + Index* index; +}; + +void CV_FlannTest::createIndex( const Mat& data, const IndexParams& params ) +{ + index = new Index( data, params ); +} + +int CV_FlannTest::knnSearch( Mat& points, Mat& neighbors ) +{ + Mat dist( points.rows, neighbors.cols, CV_32FC1); + int knn = 1, j; + + // 1st way + index->knnSearch( points, neighbors, dist, knn, SearchParams() ); + + // 2nd way + Mat neighbors1( neighbors.size(), CV_32SC1 ); + for( int i = 0; i < points.rows; i++ ) + { + float* fltPtr = points.ptr(i); + vector query( fltPtr, fltPtr + points.cols ); + vector indices( neighbors1.cols, 0 ); + vector dists( dist.cols, 0 ); + index->knnSearch( query, indices, dists, knn, SearchParams() ); + vector::const_iterator it = indices.begin(); + for( j = 0; it != indices.end(); ++it, j++ ) + neighbors1.at(i,j) = *it; + } + + // compare results + if( norm( neighbors, neighbors1, NORM_L1 ) != 0 ) + return cvtest::TS::FAIL_BAD_ACCURACY; + + return cvtest::TS::OK; +} + +int CV_FlannTest::radiusSearch( Mat& points, Mat& neighbors ) +{ + Mat dist( 1, neighbors.cols, CV_32FC1); + Mat neighbors1( neighbors.size(), CV_32SC1 ); + float radius = 10.0f; + int j; + + // radiusSearch can only search one feature at a time for range search + for( int i = 0; i < points.rows; i++ ) + { + // 1st way + Mat p( 1, points.cols, CV_32FC1, points.ptr(i) ), + n( 1, neighbors.cols, CV_32SC1, neighbors.ptr(i) ); + index->radiusSearch( p, n, dist, radius, SearchParams() ); + + // 2nd way + float* fltPtr = points.ptr(i); + vector query( fltPtr, fltPtr + points.cols ); + vector indices( neighbors1.cols, 0 ); + vector dists( dist.cols, 0 ); + index->radiusSearch( query, indices, dists, radius, SearchParams() ); + vector::const_iterator it = indices.begin(); + for( j = 0; it != indices.end(); ++it, j++ ) + neighbors1.at(i,j) = *it; + } + // compare results + if( norm( neighbors, neighbors1, NORM_L1 ) != 0 ) + return cvtest::TS::FAIL_BAD_ACCURACY; + + return cvtest::TS::OK; +} + +void CV_FlannTest::releaseModel() +{ + delete index; +} + +//--------------------------------------- +class CV_FlannLinearIndexTest : public CV_FlannTest +{ +public: + CV_FlannLinearIndexTest() {} +protected: + virtual void createModel( const Mat& data ) { createIndex( data, LinearIndexParams() ); } + virtual int findNeighbors( Mat& points, Mat& neighbors ) { return knnSearch( points, neighbors ); } +}; + +//--------------------------------------- +class CV_FlannKMeansIndexTest : public CV_FlannTest +{ +public: + CV_FlannKMeansIndexTest() {} +protected: + virtual void createModel( const Mat& data ) { createIndex( data, KMeansIndexParams() ); } + virtual int findNeighbors( Mat& points, Mat& neighbors ) { return radiusSearch( points, neighbors ); } +}; + +//--------------------------------------- +class CV_FlannKDTreeIndexTest : public CV_FlannTest +{ +public: + CV_FlannKDTreeIndexTest() {} +protected: + virtual void createModel( const Mat& data ) { createIndex( data, KDTreeIndexParams() ); } + virtual int findNeighbors( Mat& points, Mat& neighbors ) { return radiusSearch( points, neighbors ); } +}; + +//---------------------------------------- +class CV_FlannCompositeIndexTest : public CV_FlannTest +{ +public: + CV_FlannCompositeIndexTest() {} +protected: + virtual void createModel( const Mat& data ) { createIndex( data, CompositeIndexParams() ); } + virtual int findNeighbors( Mat& points, Mat& neighbors ) { return knnSearch( points, neighbors ); } +}; + +//---------------------------------------- +class CV_FlannAutotunedIndexTest : public CV_FlannTest +{ +public: + CV_FlannAutotunedIndexTest() {} +protected: + virtual void createModel( const Mat& data ) { createIndex( data, AutotunedIndexParams() ); } + virtual int findNeighbors( Mat& points, Mat& neighbors ) { return knnSearch( points, neighbors ); } +}; +//---------------------------------------- +class CV_FlannSavedIndexTest : public CV_FlannTest +{ +public: + CV_FlannSavedIndexTest() {} +protected: + virtual void createModel( const Mat& data ); + virtual int findNeighbors( Mat& points, Mat& neighbors ) { return knnSearch( points, neighbors ); } +}; + +void CV_FlannSavedIndexTest::createModel(const cv::Mat &data) +{ + switch ( cvtest::randInt(ts->get_rng()) % 2 ) + { + //case 0: createIndex( data, LinearIndexParams() ); break; // nothing to save for linear search + case 0: createIndex( data, KMeansIndexParams() ); break; + case 1: createIndex( data, KDTreeIndexParams() ); break; + //case 2: createIndex( data, CompositeIndexParams() ); break; // nothing to save for linear search + //case 2: createIndex( data, AutotunedIndexParams() ); break; // possible linear index ! + default: assert(0); + } + char filename[50]; + tmpnam( filename ); + if(filename[0] == '\\') filename[0] = '_'; + index->save( filename ); + + createIndex( data, SavedIndexParams(filename)); + remove( filename ); +} + +TEST(Features2d_LSH, regression) { CV_LSHTest test; test.safe_run(); } +TEST(Features2d_SpillTree, regression) { CV_SpillTreeTest_C test; test.safe_run(); } +TEST(Features2d_KDTree_C, regression) { CV_KDTreeTest_C test; test.safe_run(); } +TEST(Features2d_KDTree_CPP, regression) { CV_KDTreeTest_CPP test; test.safe_run(); } +TEST(Features2d_FLANN_Linear, regression) { CV_FlannLinearIndexTest test; test.safe_run(); } +TEST(Features2d_FLANN_KMeans, regression) { CV_FlannKMeansIndexTest test; test.safe_run(); } +TEST(Features2d_FLANN_KDTree, regression) { CV_FlannKDTreeIndexTest test; test.safe_run(); } +TEST(Features2d_FLANN_Composite, regression) { CV_FlannCompositeIndexTest test; test.safe_run(); } +TEST(Features2d_FLANN_Auto, regression) { CV_FlannAutotunedIndexTest test; test.safe_run(); } +TEST(Features2d_FLANN_Saved, regression) { CV_FlannSavedIndexTest test; test.safe_run(); } diff --git a/modules/features2d/test/test_precomp.cpp b/modules/features2d/test/test_precomp.cpp new file mode 100644 index 000000000..5956e13e3 --- /dev/null +++ b/modules/features2d/test/test_precomp.cpp @@ -0,0 +1 @@ +#include "test_precomp.hpp" diff --git a/modules/features2d/test/test_precomp.hpp b/modules/features2d/test/test_precomp.hpp new file mode 100644 index 000000000..b787504f9 --- /dev/null +++ b/modules/features2d/test/test_precomp.hpp @@ -0,0 +1,11 @@ +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/features2d/features2d.hpp" +#include "opencv2/highgui/highgui.hpp" +#include + +#endif diff --git a/modules/gtest/CMakeLists.txt b/modules/gtest/CMakeLists.txt deleted file mode 100644 index 4a912b4a0..000000000 --- a/modules/gtest/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -if(BUILD_SHARED_LIBS) -add_definitions(-DGTEST_CREATE_SHARED_LIBRARY=1) -endif() -define_opencv_module(gtest opencv_core) diff --git a/modules/gtest/README b/modules/gtest/README deleted file mode 100644 index 8312bbcd2..000000000 --- a/modules/gtest/README +++ /dev/null @@ -1,422 +0,0 @@ -The new OpenCV test engine is based -on the Google C++ Testing Framework (GTest). -Below is the original GTest README. ------------------------------------ - -Google C++ Testing Framework -============================ - -http://code.google.com/p/googletest/ - -Overview --------- - -Google's framework for writing C++ tests on a variety of platforms -(Linux, Mac OS X, Windows, Windows CE, Symbian, etc). Based on the -xUnit architecture. Supports automatic test discovery, a rich set of -assertions, user-defined assertions, death tests, fatal and non-fatal -failures, various options for running the tests, and XML test report -generation. - -Please see the project page above for more information as well as the -mailing list for questions, discussions, and development. There is -also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please -join us! - -Requirements for End Users --------------------------- - -Google Test is designed to have fairly minimal requirements to build -and use with your projects, but there are some. Currently, we support -Linux, Windows, Mac OS X, and Cygwin. We will also make our best -effort to support other platforms (e.g. Solaris, AIX, and z/OS). -However, since core members of the Google Test project have no access -to these platforms, Google Test may have outstanding issues there. If -you notice any problems on your platform, please notify -googletestframework@googlegroups.com. Patches for fixing them are -even more welcome! - -### Linux Requirements ### - -These are the base requirements to build and use Google Test from a source -package (as described below): - * GNU-compatible Make or gmake - * POSIX-standard shell - * POSIX(-2) Regular Expressions (regex.h) - * A C++98-standard-compliant compiler - -### Windows Requirements ### - - * Microsoft Visual C++ 7.1 or newer - -### Cygwin Requirements ### - - * Cygwin 1.5.25-14 or newer - -### Mac OS X Requirements ### - - * Mac OS X 10.4 Tiger or newer - * Developer Tools Installed - -Also, you'll need CMake 2.6.4 or higher if you want to build the -samples using the provided CMake script, regardless of the platform. - -Requirements for Contributors ------------------------------ - -We welcome patches. If you plan to contribute a patch, you need to -build Google Test and its own tests from an SVN checkout (described -below), which has further requirements: - - * Python version 2.3 or newer (for running some of the tests and - re-generating certain source files from templates) - * CMake 2.6.4 or newer - -Getting the Source ------------------- - -There are two primary ways of getting Google Test's source code: you -can download a stable source release in your preferred archive format, -or directly check out the source from our Subversion (SVN) repositary. -The SVN checkout requires a few extra steps and some extra software -packages on your system, but lets you track the latest development and -make patches much more easily, so we highly encourage it. - -### Source Package ### - -Google Test is released in versioned source packages which can be -downloaded from the download page [1]. Several different archive -formats are provided, but the only difference is the tools used to -manipulate them, and the size of the resulting file. Download -whichever you are most comfortable with. - - [1] http://code.google.com/p/googletest/downloads/list - -Once the package is downloaded, expand it using whichever tools you -prefer for that type. This will result in a new directory with the -name "gtest-X.Y.Z" which contains all of the source code. Here are -some examples on Linux: - - tar -xvzf gtest-X.Y.Z.tar.gz - tar -xvjf gtest-X.Y.Z.tar.bz2 - unzip gtest-X.Y.Z.zip - -### SVN Checkout ### - -To check out the main branch (also known as the "trunk") of Google -Test, run the following Subversion command: - - svn checkout http://googletest.googlecode.com/svn/trunk/ gtest-svn - -Setting up the Build --------------------- - -To build Google Test and your tests that use it, you need to tell your -build system where to find its headers and source files. The exact -way to do it depends on which build system you use, and is usually -straightforward. - -### Generic Build Instructions ### - -Suppose you put Google Test in directory ${GTEST_DIR}. To build it, -create a library build target (or a project as called by Visual Studio -and Xcode) to compile - - ${GTEST_DIR}/src/gtest-all.cc - -with - - ${GTEST_DIR}/include and ${GTEST_DIR} - -in the header search path. Assuming a Linux-like system and gcc, -something like the following will do: - - g++ -I${GTEST_DIR}/include -I${GTEST_DIR} -c ${GTEST_DIR}/src/gtest-all.cc - ar -rv libgtest.a gtest-all.o - -Next, you should compile your test source file with -${GTEST_DIR}/include in the header search path, and link it with gtest -and any other necessary libraries: - - g++ -I${GTEST_DIR}/include path/to/your_test.cc libgtest.a -o your_test - -As an example, the make/ directory contains a Makefile that you can -use to build Google Test on systems where GNU make is available -(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google -Test's own tests. Instead, it just builds the Google Test library and -a sample test. You can use it as a starting point for your own build -script. - -If the default settings are correct for your environment, the -following commands should succeed: - - cd ${GTEST_DIR}/make - make - ./sample1_unittest - -If you see errors, try to tweak the contents of make/Makefile to make -them go away. There are instructions in make/Makefile on how to do -it. - -### Using CMake ### - -Google Test comes with a CMake build script (CMakeLists.txt) that can -be used on a wide range of platforms ("C" stands for cross-platofrm.). -If you don't have CMake installed already, you can download it for -free from http://www.cmake.org/. - -CMake works by generating native makefiles or build projects that can -be used in the compiler environment of your choice. The typical -workflow starts with: - - mkdir mybuild # Create a directory to hold the build output. - cd mybuild - cmake ${GTEST_DIR} # Generate native build scripts. - -If you want to build Google Test's samples, you should replace the -last command with - - cmake -Dbuild_gtest_samples=ON ${GTEST_DIR} - -If you are on a *nix system, you should now see a Makefile in the -current directory. Just type 'make' to build gtest. - -If you use Windows and have Vistual Studio installed, a gtest.sln file -and several .vcproj files will be created. You can then build them -using Visual Studio. - -On Mac OS X with Xcode installed, a .xcodeproj file will be generated. - -### Legacy Build Scripts ### - -Before settling on CMake, we have been providing hand-maintained build -projects/scripts for Visual Studio, Xcode, and Autotools. While we -continue to provide them for convenience, they are not actively -maintained any more. We highly recommend that you follow the -instructions in the previous two sections to integrate Google Test -with your existing build system. - -If you still need to use the legacy build scripts, here's how: - -The msvc\ folder contains two solutions with Visual C++ projects. -Open the gtest.sln or gtest-md.sln file using Visual Studio, and you -are ready to build Google Test the same way you build any Visual -Studio project. Files that have names ending with -md use DLL -versions of Microsoft runtime libraries (the /MD or the /MDd compiler -option). Files without that suffix use static versions of the runtime -libraries (the /MT or the /MTd option). Please note that one must use -the same option to compile both gtest and the test code. If you use -Visual Studio 2005 or above, we recommend the -md version as /MD is -the default for new projects in these versions of Visual Studio. - -On Mac OS X, open the gtest.xcodeproj in the xcode/ folder using -Xcode. Build the "gtest" target. The universal binary framework will -end up in your selected build directory (selected in the Xcode -"Preferences..." -> "Building" pane and defaults to xcode/build). -Alternatively, at the command line, enter: - - xcodebuild - -This will build the "Release" configuration of gtest.framework in your -default build location. See the "xcodebuild" man page for more -information about building different configurations and building in -different locations. - -Tweaking Google Test --------------------- - -Google Test can be used in diverse environments. The default -configuration may not work (or may not work well) out of the box in -some environments. However, you can easily tweak Google Test by -defining control macros on the compiler command line. Generally, -these macros are named like GTEST_XYZ and you define them to either 1 -or 0 to enable or disable a certain feature. - -We list the most frequently used macros below. For a complete list, -see file include/gtest/internal/gtest-port.h. - -### Choosing a TR1 Tuple Library ### - -Some Google Test features require the C++ Technical Report 1 (TR1) -tuple library, which is not yet available with all compilers. The -good news is that Google Test implements a subset of TR1 tuple that's -enough for its own need, and will automatically use this when the -compiler doesn't provide TR1 tuple. - -Usually you don't need to care about which tuple library Google Test -uses. However, if your project already uses TR1 tuple, you need to -tell Google Test to use the same TR1 tuple library the rest of your -project uses, or the two tuple implementations will clash. To do -that, add - - -DGTEST_USE_OWN_TR1_TUPLE=0 - -to the compiler flags while compiling Google Test and your tests. If -you want to force Google Test to use its own tuple library, just add - - -DGTEST_USE_OWN_TR1_TUPLE=1 - -to the compiler flags instead. - -If you don't want Google Test to use tuple at all, add - - -DGTEST_HAS_TR1_TUPLE=0 - -and all features using tuple will be disabled. - -### Multi-threaded Tests ### - -Google Test is thread-safe where the pthread library is available. -After #include , you can check the GTEST_IS_THREADSAFE -macro to see whether this is the case (yes if the macro is #defined to -1, no if it's undefined.). - -If Google Test doesn't correctly detect whether pthread is available -in your environment, you can force it with - - -DGTEST_HAS_PTHREAD=1 - -or - - -DGTEST_HAS_PTHREAD=0 - -When Google Test uses pthread, you may need to add flags to your -compiler and/or linker to select the pthread library, or you'll get -link errors. If you use the CMake script or the deprecated Autotools -script, this is taken care of for you. If you use your own build -script, you'll need to read your compiler and linker's manual to -figure out what flags to add. - -### As a Shared Library (DLL) ### - -Google Test is compact, so most users can build and link it as a -static library for the simplicity. You can choose to use Google Test -as a shared library (known as a DLL on Windows) if you prefer. - -To compile gtest as a shared library, add - - -DGTEST_CREATE_SHARED_LIBRARY=1 - -to the compiler flags. You'll also need to tell the linker to produce -a shared library instead - consult your linker's manual for how to do -it. - -To compile your tests that use the gtest shared library, add - - -DGTEST_LINKED_AS_SHARED_LIBRARY=1 - -to the compiler flags. - -### Avoiding Macro Name Clashes ### - -In C++, macros don't obey namespaces. Therefore two libraries that -both define a macro of the same name will clash if you #include both -definitions. In case a Google Test macro clashes with another -library, you can force Google Test to rename its macro to avoid the -conflict. - -Specifically, if both Google Test and some other code define macro -FOO, you can add - - -DGTEST_DONT_DEFINE_FOO=1 - -to the compiler flags to tell Google Test to change the macro's name -from FOO to GTEST_FOO. Currently FOO can be FAIL, SUCCEED, or TEST. -For example, with -DGTEST_DONT_DEFINE_TEST=1, you'll need to write - - GTEST_TEST(SomeTest, DoesThis) { ... } - -instead of - - TEST(SomeTest, DoesThis) { ... } - -in order to define a test. - -Upgrating from an Earlier Version ---------------------------------- - -We strive to keep Google Test releases backward compatible. -Sometimes, though, we have to make some breaking changes for the -users' long-term benefits. This section describes what you'll need to -do if you are upgrading from an earlier version of Google Test. - -### Upgrading from 1.3.0 or Earlier ### - -You may need to explicitly enable or disable Google Test's own TR1 -tuple library. See the instructions in section "Choosing a TR1 Tuple -Library". - -### Upgrading from 1.4.0 or Earlier ### - -The Autotools build script (configure + make) is no longer officially -supportted. You are encouraged to migrate to your own build system or -use CMake. If you still need to use Autotools, you can find -instructions in the README file from Google Test 1.4.0. - -On platforms where the pthread library is available, Google Test uses -it in order to be thread-safe. See the "Multi-threaded Tests" section -for what this means to your build script. - -If you use Microsoft Visual C++ 7.1 with exceptions disabled, Google -Test will no longer compile. This should affect very few people, as a -large portion of STL (including ) doesn't compile in this mode -anyway. We decided to stop supporting it in order to greatly simplify -Google Test's implementation. - -Developing Google Test ----------------------- - -This section discusses how to make your own changes to Google Test. - -### Testing Google Test Itself ### - -To make sure your changes work as intended and don't break existing -functionality, you'll want to compile and run Google Test's own tests. -For that you can use CMake: - - mkdir mybuild - cd mybuild - cmake -Dbuild_all_gtest_tests=ON ${GTEST_DIR} - -Make sure you have Python installed, as some of Google Test's tests -are written in Python. If the cmake command complains about not being -able to find Python ("Could NOT find PythonInterp (missing: -PYTHON_EXECUTABLE)"), try telling it explicitly where your Python -executable can be found: - - cmake -DPYTHON_EXECUTABLE=path/to/python -Dbuild_all_gtest_tests=ON \ - ${GTEST_DIR} - -Next, you can build Google Test and all of its own tests. On *nix, -this is usually done by 'make'. To run the tests, do - - make test - -All tests should pass. - -### Regenerating Source Files ### - -Some of Google Test's source files are generated from templates (not -in the C++ sense) using a script. A template file is named FOO.pump, -where FOO is the name of the file it will generate. For example, the -file include/gtest/internal/gtest-type-util.h.pump is used to generate -gtest-type-util.h in the same directory. - -Normally you don't need to worry about regenerating the source files, -unless you need to modify them. In that case, you should modify the -corresponding .pump files instead and run the pump.py Python script to -regenerate them. You can find pump.py in the scripts/ directory. -Read the Pump manual [2] for how to use it. - - [2] http://code.google.com/p/googletest/wiki/PumpManual - -### Contributing a Patch ### - -We welcome patches. Please read the Google Test developer's guide [3] -for how you can contribute. In particular, make sure you have signed -the Contributor License Agreement, or we won't be able to accept the -patch. - - [3] http://code.google.com/p/googletest/wiki/GoogleTestDevGuide - -Happy testing! diff --git a/modules/gtest/include/opencv2/gtest/gtest.h b/modules/gtest/include/opencv2/gtest/gtest.h deleted file mode 100644 index c0a1902e2..000000000 --- a/modules/gtest/include/opencv2/gtest/gtest.h +++ /dev/null @@ -1,18007 +0,0 @@ -// Copyright 2005, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: wan@google.com (Zhanyong Wan) -// -// The Google C++ Testing Framework (Google Test) -// -// This header file defines the public API for Google Test. It should be -// included by any test program that uses Google Test. -// -// IMPORTANT NOTE: Due to limitation of the C++ language, we have to -// leave some internal implementation details in this header file. -// They are clearly marked by comments like this: -// -// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -// -// Such code is NOT meant to be used by a user directly, and is subject -// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user -// program! -// -// Acknowledgment: Google Test borrowed the idea of automatic test -// registration from Barthelemy Dagenais' (barthelemy@prologique.com) -// easyUnit framework. - -#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ -#define GTEST_INCLUDE_GTEST_GTEST_H_ - -#include -#include - -// Copyright 2005, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) -// -// The Google C++ Testing Framework (Google Test) -// -// This header file declares functions and macros used internally by -// Google Test. They are subject to change without notice. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ - -// Copyright 2005, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Authors: wan@google.com (Zhanyong Wan) -// -// Low-level types and utilities for porting Google Test to various -// platforms. They are subject to change without notice. DO NOT USE -// THEM IN USER CODE. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ - -// The user can define the following macros in the build script to -// control Google Test's behavior. If the user doesn't define a macro -// in this list, Google Test will define it. -// -// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) -// is/isn't available. -// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions -// are enabled. -// GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string -// is/isn't available (some systems define -// ::string, which is different to std::string). -// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string -// is/isn't available (some systems define -// ::wstring, which is different to std::wstring). -// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that -// is/isn't available. -// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't -// enabled. -// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that -// std::wstring does/doesn't work (Google Test can -// be used where std::wstring is unavailable). -// GTEST_HAS_TR1_TUPLE - Define it to 1/0 to indicate tr1::tuple -// is/isn't available. -// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the -// compiler supports Microsoft's "Structured -// Exception Handling". -// GTEST_USE_OWN_TR1_TUPLE - Define it to 1/0 to indicate whether Google -// Test's own tr1 tuple implementation should be -// used. Unused when the user sets -// GTEST_HAS_TR1_TUPLE to 0. -// GTEST_LINKED_AS_SHARED_LIBRARY -// - Define to 1 when compiling tests that use -// Google Test as a shared library (known as -// DLL on Windows). -// GTEST_CREATE_SHARED_LIBRARY -// - Define to 1 when compiling Google Test itself -// as a shared library. - -// This header defines the following utilities: -// -// Macros indicating the current platform (defined to 1 if compiled on -// the given platform; otherwise undefined): -// GTEST_OS_AIX - IBM AIX -// GTEST_OS_CYGWIN - Cygwin -// GTEST_OS_LINUX - Linux -// GTEST_OS_MAC - Mac OS X -// GTEST_OS_SOLARIS - Sun Solaris -// GTEST_OS_SYMBIAN - Symbian -// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) -// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop -// GTEST_OS_WINDOWS_MINGW - MinGW -// GTEST_OS_WINDOWS_MOBILE - Windows Mobile -// GTEST_OS_ZOS - z/OS -// -// Among the platforms, Cygwin, Linux, Max OS X, and Windows have the -// most stable support. Since core members of the Google Test project -// don't have access to other platforms, support for them may be less -// stable. If you notice any problems on your platform, please notify -// googletestframework@googlegroups.com (patches for fixing them are -// even more welcome!). -// -// Note that it is possible that none of the GTEST_OS_* macros are defined. -// -// Macros indicating available Google Test features (defined to 1 if -// the corresponding feature is supported; otherwise undefined): -// GTEST_HAS_COMBINE - the Combine() function (for value-parameterized -// tests) -// GTEST_HAS_DEATH_TEST - death tests -// GTEST_HAS_PARAM_TEST - value-parameterized tests -// GTEST_HAS_TYPED_TEST - typed tests -// GTEST_HAS_TYPED_TEST_P - type-parameterized tests -// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. -// GTEST_USES_SIMPLE_RE - our own simple regex is used; -// the above two are mutually exclusive. -// GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ(). -// -// Macros for basic C++ coding: -// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. -// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a -// variable don't have to be used. -// GTEST_DISALLOW_ASSIGN_ - disables operator=. -// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=. -// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. -// -// Synchronization: -// Mutex, MutexLock, ThreadLocal, GetThreadCount() -// - synchronization primitives. -// GTEST_IS_THREADSAFE - defined to 1 to indicate that the above -// synchronization primitives have real implementations -// and Google Test is thread-safe; or 0 otherwise. -// -// Template meta programming: -// is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only. -// -// Smart pointers: -// scoped_ptr - as in TR2. -// -// Regular expressions: -// RE - a simple regular expression class using the POSIX -// Extended Regular Expression syntax. Not available on -// Windows. -// -// Logging: -// GTEST_LOG_() - logs messages at the specified severity level. -// LogToStderr() - directs all log messages to stderr. -// FlushInfoLog() - flushes informational log messages. -// -// Stdout and stderr capturing: -// CaptureStdout() - starts capturing stdout. -// GetCapturedStdout() - stops capturing stdout and returns the captured -// string. -// CaptureStderr() - starts capturing stderr. -// GetCapturedStderr() - stops capturing stderr and returns the captured -// string. -// -// Integer types: -// TypeWithSize - maps an integer to a int type. -// Int32, UInt32, Int64, UInt64, TimeInMillis -// - integers of known sizes. -// BiggestInt - the biggest signed integer type. -// -// Command-line utilities: -// GTEST_FLAG() - references a flag. -// GTEST_DECLARE_*() - declares a flag. -// GTEST_DEFINE_*() - defines a flag. -// GetArgvs() - returns the command line as a vector of strings. -// -// Environment variable utilities: -// GetEnv() - gets the value of an environment variable. -// BoolFromGTestEnv() - parses a bool environment variable. -// Int32FromGTestEnv() - parses an Int32 environment variable. -// StringFromGTestEnv() - parses a string environment variable. - -#include // For ptrdiff_t -#include -#include -#include -#ifndef _WIN32_WCE -#include -#endif // !_WIN32_WCE - -#include // NOLINT -#include // NOLINT -#include // NOLINT - -#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" -#define GTEST_FLAG_PREFIX_ "gtest_" -#define GTEST_FLAG_PREFIX_DASH_ "gtest-" -#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_" -#define GTEST_NAME_ "Google Test" -#define GTEST_PROJECT_URL_ "http://code.google.com/p/googletest/" - -// Determines the version of gcc that is used to compile this. -#ifdef __GNUC__ -// 40302 means version 4.3.2. -#define GTEST_GCC_VER_ \ - (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__) -#endif // __GNUC__ - -// Determines the platform on which Google Test is compiled. -#ifdef __CYGWIN__ -#define GTEST_OS_CYGWIN 1 -#elif defined __SYMBIAN32__ -#define GTEST_OS_SYMBIAN 1 -#elif defined _WIN32 -#define GTEST_OS_WINDOWS 1 -#ifdef _WIN32_WCE -#define GTEST_OS_WINDOWS_MOBILE 1 -#elif defined(__MINGW__) || defined(__MINGW32__) -#define GTEST_OS_WINDOWS_MINGW 1 -#else -#define GTEST_OS_WINDOWS_DESKTOP 1 -#endif // _WIN32_WCE -#elif defined __APPLE__ -#define GTEST_OS_MAC 1 -#elif defined __linux__ -#define GTEST_OS_LINUX 1 -#elif defined __MVS__ -#define GTEST_OS_ZOS 1 -#elif defined(__sun) && defined(__SVR4) -#define GTEST_OS_SOLARIS 1 -#elif defined(_AIX) -#define GTEST_OS_AIX 1 -#endif // __CYGWIN__ - -#if GTEST_OS_CYGWIN || GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_SYMBIAN || \ - GTEST_OS_SOLARIS || GTEST_OS_AIX - -// On some platforms, needs someone to define size_t, and -// won't compile otherwise. We can #include it here as we already -// included , which is guaranteed to define size_t through -// . -#include // NOLINT -#include // NOLINT -#include // NOLINT -#include // NOLINT -#include // NOLINT - -#define GTEST_USES_POSIX_RE 1 - -#elif GTEST_OS_WINDOWS - -#if !GTEST_OS_WINDOWS_MOBILE -#include // NOLINT -#include // NOLINT -#endif - -// is not available on Windows. Use our own simple regex -// implementation instead. -#define GTEST_USES_SIMPLE_RE 1 - -#else - -// may not be available on this platform. Use our own -// simple regex implementation instead. -#define GTEST_USES_SIMPLE_RE 1 - -#endif // GTEST_OS_CYGWIN || GTEST_OS_LINUX || GTEST_OS_MAC || - // GTEST_OS_SYMBIAN || GTEST_OS_SOLARIS || GTEST_OS_AIX - -#ifndef GTEST_HAS_EXCEPTIONS -// The user didn't tell us whether exceptions are enabled, so we need -// to figure it out. -#if defined(_MSC_VER) || defined(__BORLANDC__) -// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS -// macro to enable exceptions, so we'll do the same. -// Assumes that exceptions are enabled by default. -#ifndef _HAS_EXCEPTIONS -#define _HAS_EXCEPTIONS 1 -#endif // _HAS_EXCEPTIONS -#define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS -#elif defined(__GNUC__) && __EXCEPTIONS -// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled. -#define GTEST_HAS_EXCEPTIONS 1 -#elif defined(__SUNPRO_CC) -// Sun Pro CC supports exceptions. However, there is no compile-time way of -// detecting whether they are enabled or not. Therefore, we assume that -// they are enabled unless the user tells us otherwise. -#define GTEST_HAS_EXCEPTIONS 1 -#elif defined(__IBMCPP__) && __EXCEPTIONS -// xlC defines __EXCEPTIONS to 1 iff exceptions are enabled. -#define GTEST_HAS_EXCEPTIONS 1 -#else -// For other compilers, we assume exceptions are disabled to be -// conservative. -#define GTEST_HAS_EXCEPTIONS 0 -#endif // defined(_MSC_VER) || defined(__BORLANDC__) -#endif // GTEST_HAS_EXCEPTIONS - -#if !defined(GTEST_HAS_STD_STRING) -// Even though we don't use this macro any longer, we keep it in case -// some clients still depend on it. -#define GTEST_HAS_STD_STRING 1 -#elif !GTEST_HAS_STD_STRING -// The user told us that ::std::string isn't available. -#error "Google Test cannot be used where ::std::string isn't available." -#endif // !defined(GTEST_HAS_STD_STRING) - -#ifndef GTEST_HAS_GLOBAL_STRING -// The user didn't tell us whether ::string is available, so we need -// to figure it out. - -#define GTEST_HAS_GLOBAL_STRING 0 - -#endif // GTEST_HAS_GLOBAL_STRING - -#ifndef GTEST_HAS_STD_WSTRING -// The user didn't tell us whether ::std::wstring is available, so we need -// to figure it out. -// TODO(wan@google.com): uses autoconf to detect whether ::std::wstring -// is available. - -// Cygwin 1.5 and below doesn't support ::std::wstring. -// Cygwin 1.7 might add wstring support; this should be updated when clear. -// Solaris' libc++ doesn't support it either. -#define GTEST_HAS_STD_WSTRING (!(GTEST_OS_CYGWIN || GTEST_OS_SOLARIS)) - -#endif // GTEST_HAS_STD_WSTRING - -#ifndef GTEST_HAS_GLOBAL_WSTRING -// The user didn't tell us whether ::wstring is available, so we need -// to figure it out. -#define GTEST_HAS_GLOBAL_WSTRING \ - (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING) -#endif // GTEST_HAS_GLOBAL_WSTRING - -// Determines whether RTTI is available. -#ifndef GTEST_HAS_RTTI -// The user didn't tell us whether RTTI is enabled, so we need to -// figure it out. - -#ifdef _MSC_VER - -#ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled. -#define GTEST_HAS_RTTI 1 -#else -#define GTEST_HAS_RTTI 0 -#endif - -// Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled. -#elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302) - -#ifdef __GXX_RTTI -#define GTEST_HAS_RTTI 1 -#else -#define GTEST_HAS_RTTI 0 -#endif // __GXX_RTTI - -// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if -// both the typeid and dynamic_cast features are present. -#elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) - -#ifdef __RTTI_ALL__ -#define GTEST_HAS_RTTI 1 -#else -#define GTEST_HAS_RTTI 0 -#endif - -#else - -// For all other compilers, we assume RTTI is enabled. -#define GTEST_HAS_RTTI 1 - -#endif // _MSC_VER - -#endif // GTEST_HAS_RTTI - -// It's this header's responsibility to #include when RTTI -// is enabled. -#if GTEST_HAS_RTTI -#include -#endif - -// Determines whether Google Test can use the pthreads library. -#ifndef GTEST_HAS_PTHREAD -// The user didn't tell us explicitly, so we assume pthreads support is -// available on Linux and Mac. -// -// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 -// to your compiler flags. -#define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC) -#endif // GTEST_HAS_PTHREAD - -// Determines whether Google Test can use tr1/tuple. You can define -// this macro to 0 to prevent Google Test from using tuple (any -// feature depending on tuple with be disabled in this mode). -#ifndef GTEST_HAS_TR1_TUPLE -// The user didn't tell us not to do it, so we assume it's OK. -#define GTEST_HAS_TR1_TUPLE 1 -#endif // GTEST_HAS_TR1_TUPLE - -// Determines whether Google Test's own tr1 tuple implementation -// should be used. -#ifndef GTEST_USE_OWN_TR1_TUPLE -// The user didn't tell us, so we need to figure it out. - -// We use our own TR1 tuple if we aren't sure the user has an -// implementation of it already. At this time, GCC 4.0.0+ and MSVC -// 2010 are the only mainstream compilers that come with a TR1 tuple -// implementation. NVIDIA's CUDA NVCC compiler pretends to be GCC by -// defining __GNUC__ and friends, but cannot compile GCC's tuple -// implementation. MSVC 2008 (9.0) provides TR1 tuple in a 323 MB -// Feature Pack download, which we cannot assume the user has. -#if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000)) \ - || _MSC_VER >= 1600 -#define GTEST_USE_OWN_TR1_TUPLE 0 -#else -#define GTEST_USE_OWN_TR1_TUPLE 1 -#endif - -#endif // GTEST_USE_OWN_TR1_TUPLE - -// To avoid conditional compilation everywhere, we make it -// gtest-port.h's responsibility to #include the header implementing -// tr1/tuple. -#if GTEST_HAS_TR1_TUPLE - -#if GTEST_USE_OWN_TR1_TUPLE -// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! - -// Copyright 2009 Google Inc. -// All Rights Reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: wan@google.com (Zhanyong Wan) - -// Implements a subset of TR1 tuple needed by Google Test and Google Mock. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ - -#include // For ::std::pair. - -// The compiler used in Symbian has a bug that prevents us from declaring the -// tuple template as a friend (it complains that tuple is redefined). This -// hack bypasses the bug by declaring the members that should otherwise be -// private as public. -// Sun Studio versions < 12 also have the above bug. -#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) -#define GTEST_DECLARE_TUPLE_AS_FRIEND_ public: -#else -#define GTEST_DECLARE_TUPLE_AS_FRIEND_ \ - template friend class tuple; \ - private: -#endif - -// GTEST_n_TUPLE_(T) is the type of an n-tuple. -#define GTEST_0_TUPLE_(T) tuple<> -#define GTEST_1_TUPLE_(T) tuple -#define GTEST_2_TUPLE_(T) tuple -#define GTEST_3_TUPLE_(T) tuple -#define GTEST_4_TUPLE_(T) tuple -#define GTEST_5_TUPLE_(T) tuple -#define GTEST_6_TUPLE_(T) tuple -#define GTEST_7_TUPLE_(T) tuple -#define GTEST_8_TUPLE_(T) tuple -#define GTEST_9_TUPLE_(T) tuple -#define GTEST_10_TUPLE_(T) tuple - -// GTEST_n_TYPENAMES_(T) declares a list of n typenames. -#define GTEST_0_TYPENAMES_(T) -#define GTEST_1_TYPENAMES_(T) typename T##0 -#define GTEST_2_TYPENAMES_(T) typename T##0, typename T##1 -#define GTEST_3_TYPENAMES_(T) typename T##0, typename T##1, typename T##2 -#define GTEST_4_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3 -#define GTEST_5_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3, typename T##4 -#define GTEST_6_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3, typename T##4, typename T##5 -#define GTEST_7_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3, typename T##4, typename T##5, typename T##6 -#define GTEST_8_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3, typename T##4, typename T##5, typename T##6, typename T##7 -#define GTEST_9_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3, typename T##4, typename T##5, typename T##6, \ - typename T##7, typename T##8 -#define GTEST_10_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3, typename T##4, typename T##5, typename T##6, \ - typename T##7, typename T##8, typename T##9 - -// In theory, defining stuff in the ::std namespace is undefined -// behavior. We can do this as we are playing the role of a standard -// library vendor. -namespace std { -namespace tr1 { - -template -class tuple; - -// Anything in namespace gtest_internal is Google Test's INTERNAL -// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code. -namespace gtest_internal { - -// ByRef::type is T if T is a reference; otherwise it's const T&. -template -struct ByRef { typedef const T& type; }; // NOLINT -template -struct ByRef { typedef T& type; }; // NOLINT - -// A handy wrapper for ByRef. -#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef::type - -// AddRef::type is T if T is a reference; otherwise it's T&. This -// is the same as tr1::add_reference::type. -template -struct AddRef { typedef T& type; }; // NOLINT -template -struct AddRef { typedef T& type; }; // NOLINT - -// A handy wrapper for AddRef. -#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef::type - -// A helper for implementing get(). -template class Get; - -// A helper for implementing tuple_element. kIndexValid is true -// iff k < the number of fields in tuple type T. -template -struct TupleElement; - -template -struct TupleElement { typedef T0 type; }; - -template -struct TupleElement { typedef T1 type; }; - -template -struct TupleElement { typedef T2 type; }; - -template -struct TupleElement { typedef T3 type; }; - -template -struct TupleElement { typedef T4 type; }; - -template -struct TupleElement { typedef T5 type; }; - -template -struct TupleElement { typedef T6 type; }; - -template -struct TupleElement { typedef T7 type; }; - -template -struct TupleElement { typedef T8 type; }; - -template -struct TupleElement { typedef T9 type; }; - -} // namespace gtest_internal - -template <> -class tuple<> { - public: - tuple() {} - tuple(const tuple& /* t */) {} - tuple& operator=(const tuple& /* t */) { return *this; } -}; - -template -class GTEST_1_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0) : f0_(f0) {} - - tuple(const tuple& t) : f0_(t.f0_) {} - - template - tuple(const GTEST_1_TUPLE_(U)& t) : f0_(t.f0_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_1_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_1_TUPLE_(U)& t) { - f0_ = t.f0_; - return *this; - } - - T0 f0_; -}; - -template -class GTEST_2_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1) : f0_(f0), - f1_(f1) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_) {} - - template - tuple(const GTEST_2_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_) {} - template - tuple(const ::std::pair& p) : f0_(p.first), f1_(p.second) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_2_TUPLE_(U)& t) { - return CopyFrom(t); - } - template - tuple& operator=(const ::std::pair& p) { - f0_ = p.first; - f1_ = p.second; - return *this; - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_2_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - return *this; - } - - T0 f0_; - T1 f1_; -}; - -template -class GTEST_3_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2) : f0_(f0), f1_(f1), f2_(f2) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} - - template - tuple(const GTEST_3_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_3_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_3_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; -}; - -template -class GTEST_4_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3) : f0_(f0), f1_(f1), f2_(f2), - f3_(f3) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_) {} - - template - tuple(const GTEST_4_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_4_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_4_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; -}; - -template -class GTEST_5_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_(), f4_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, - GTEST_BY_REF_(T4) f4) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), - f4_(t.f4_) {} - - template - tuple(const GTEST_5_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_), f4_(t.f4_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_5_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_5_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - f4_ = t.f4_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; - T4 f4_; -}; - -template -class GTEST_6_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, - GTEST_BY_REF_(T5) f5) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), - f5_(f5) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), - f4_(t.f4_), f5_(t.f5_) {} - - template - tuple(const GTEST_6_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_), f4_(t.f4_), f5_(t.f5_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_6_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_6_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - f4_ = t.f4_; - f5_ = t.f5_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; - T4 f4_; - T5 f5_; -}; - -template -class GTEST_7_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, - GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6) : f0_(f0), f1_(f1), f2_(f2), - f3_(f3), f4_(f4), f5_(f5), f6_(f6) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), - f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} - - template - tuple(const GTEST_7_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_7_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_7_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - f4_ = t.f4_; - f5_ = t.f5_; - f6_ = t.f6_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; - T4 f4_; - T5 f5_; - T6 f6_; -}; - -template -class GTEST_8_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, - GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, - GTEST_BY_REF_(T7) f7) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), - f5_(f5), f6_(f6), f7_(f7) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), - f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} - - template - tuple(const GTEST_8_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_8_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_8_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - f4_ = t.f4_; - f5_ = t.f5_; - f6_ = t.f6_; - f7_ = t.f7_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; - T4 f4_; - T5 f5_; - T6 f6_; - T7 f7_; -}; - -template -class GTEST_9_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, - GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, - GTEST_BY_REF_(T8) f8) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), - f5_(f5), f6_(f6), f7_(f7), f8_(f8) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), - f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} - - template - tuple(const GTEST_9_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_9_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_9_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - f4_ = t.f4_; - f5_ = t.f5_; - f6_ = t.f6_; - f7_ = t.f7_; - f8_ = t.f8_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; - T4 f4_; - T5 f5_; - T6 f6_; - T7 f7_; - T8 f8_; -}; - -template -class tuple { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_(), - f9_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, - GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, - GTEST_BY_REF_(T8) f8, GTEST_BY_REF_(T9) f9) : f0_(f0), f1_(f1), f2_(f2), - f3_(f3), f4_(f4), f5_(f5), f6_(f6), f7_(f7), f8_(f8), f9_(f9) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), - f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), f9_(t.f9_) {} - - template - tuple(const GTEST_10_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), - f9_(t.f9_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_10_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_10_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - f4_ = t.f4_; - f5_ = t.f5_; - f6_ = t.f6_; - f7_ = t.f7_; - f8_ = t.f8_; - f9_ = t.f9_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; - T4 f4_; - T5 f5_; - T6 f6_; - T7 f7_; - T8 f8_; - T9 f9_; -}; - -// 6.1.3.2 Tuple creation functions. - -// Known limitations: we don't support passing an -// std::tr1::reference_wrapper to make_tuple(). And we don't -// implement tie(). - -inline tuple<> make_tuple() { return tuple<>(); } - -template -inline GTEST_1_TUPLE_(T) make_tuple(const T0& f0) { - return GTEST_1_TUPLE_(T)(f0); -} - -template -inline GTEST_2_TUPLE_(T) make_tuple(const T0& f0, const T1& f1) { - return GTEST_2_TUPLE_(T)(f0, f1); -} - -template -inline GTEST_3_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2) { - return GTEST_3_TUPLE_(T)(f0, f1, f2); -} - -template -inline GTEST_4_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3) { - return GTEST_4_TUPLE_(T)(f0, f1, f2, f3); -} - -template -inline GTEST_5_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3, const T4& f4) { - return GTEST_5_TUPLE_(T)(f0, f1, f2, f3, f4); -} - -template -inline GTEST_6_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3, const T4& f4, const T5& f5) { - return GTEST_6_TUPLE_(T)(f0, f1, f2, f3, f4, f5); -} - -template -inline GTEST_7_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3, const T4& f4, const T5& f5, const T6& f6) { - return GTEST_7_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6); -} - -template -inline GTEST_8_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7) { - return GTEST_8_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7); -} - -template -inline GTEST_9_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, - const T8& f8) { - return GTEST_9_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8); -} - -template -inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, - const T8& f8, const T9& f9) { - return GTEST_10_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9); -} - -// 6.1.3.3 Tuple helper classes. - -template struct tuple_size; - -template -struct tuple_size { static const int value = 0; }; - -template -struct tuple_size { static const int value = 1; }; - -template -struct tuple_size { static const int value = 2; }; - -template -struct tuple_size { static const int value = 3; }; - -template -struct tuple_size { static const int value = 4; }; - -template -struct tuple_size { static const int value = 5; }; - -template -struct tuple_size { static const int value = 6; }; - -template -struct tuple_size { static const int value = 7; }; - -template -struct tuple_size { static const int value = 8; }; - -template -struct tuple_size { static const int value = 9; }; - -template -struct tuple_size { static const int value = 10; }; - -template -struct tuple_element { - typedef typename gtest_internal::TupleElement< - k < (tuple_size::value), k, Tuple>::type type; -}; - -#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element::type - -// 6.1.3.4 Element access. - -namespace gtest_internal { - -template <> -class Get<0> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) - Field(Tuple& t) { return t.f0_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) - ConstField(const Tuple& t) { return t.f0_; } -}; - -template <> -class Get<1> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) - Field(Tuple& t) { return t.f1_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) - ConstField(const Tuple& t) { return t.f1_; } -}; - -template <> -class Get<2> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) - Field(Tuple& t) { return t.f2_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) - ConstField(const Tuple& t) { return t.f2_; } -}; - -template <> -class Get<3> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) - Field(Tuple& t) { return t.f3_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) - ConstField(const Tuple& t) { return t.f3_; } -}; - -template <> -class Get<4> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) - Field(Tuple& t) { return t.f4_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) - ConstField(const Tuple& t) { return t.f4_; } -}; - -template <> -class Get<5> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) - Field(Tuple& t) { return t.f5_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) - ConstField(const Tuple& t) { return t.f5_; } -}; - -template <> -class Get<6> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) - Field(Tuple& t) { return t.f6_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) - ConstField(const Tuple& t) { return t.f6_; } -}; - -template <> -class Get<7> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) - Field(Tuple& t) { return t.f7_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) - ConstField(const Tuple& t) { return t.f7_; } -}; - -template <> -class Get<8> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) - Field(Tuple& t) { return t.f8_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) - ConstField(const Tuple& t) { return t.f8_; } -}; - -template <> -class Get<9> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) - Field(Tuple& t) { return t.f9_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) - ConstField(const Tuple& t) { return t.f9_; } -}; - -} // namespace gtest_internal - -template -GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) -get(GTEST_10_TUPLE_(T)& t) { - return gtest_internal::Get::Field(t); -} - -template -GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) -get(const GTEST_10_TUPLE_(T)& t) { - return gtest_internal::Get::ConstField(t); -} - -// 6.1.3.5 Relational operators - -// We only implement == and !=, as we don't have a need for the rest yet. - -namespace gtest_internal { - -// SameSizeTuplePrefixComparator::Eq(t1, t2) returns true if the -// first k fields of t1 equals the first k fields of t2. -// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if -// k1 != k2. -template -struct SameSizeTuplePrefixComparator; - -template <> -struct SameSizeTuplePrefixComparator<0, 0> { - template - static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) { - return true; - } -}; - -template -struct SameSizeTuplePrefixComparator { - template - static bool Eq(const Tuple1& t1, const Tuple2& t2) { - return SameSizeTuplePrefixComparator::Eq(t1, t2) && - ::std::tr1::get(t1) == ::std::tr1::get(t2); - } -}; - -} // namespace gtest_internal - -template -inline bool operator==(const GTEST_10_TUPLE_(T)& t, - const GTEST_10_TUPLE_(U)& u) { - return gtest_internal::SameSizeTuplePrefixComparator< - tuple_size::value, - tuple_size::value>::Eq(t, u); -} - -template -inline bool operator!=(const GTEST_10_TUPLE_(T)& t, - const GTEST_10_TUPLE_(U)& u) { return !(t == u); } - -// 6.1.4 Pairs. -// Unimplemented. - -} // namespace tr1 -} // namespace std - -#undef GTEST_0_TUPLE_ -#undef GTEST_1_TUPLE_ -#undef GTEST_2_TUPLE_ -#undef GTEST_3_TUPLE_ -#undef GTEST_4_TUPLE_ -#undef GTEST_5_TUPLE_ -#undef GTEST_6_TUPLE_ -#undef GTEST_7_TUPLE_ -#undef GTEST_8_TUPLE_ -#undef GTEST_9_TUPLE_ -#undef GTEST_10_TUPLE_ - -#undef GTEST_0_TYPENAMES_ -#undef GTEST_1_TYPENAMES_ -#undef GTEST_2_TYPENAMES_ -#undef GTEST_3_TYPENAMES_ -#undef GTEST_4_TYPENAMES_ -#undef GTEST_5_TYPENAMES_ -#undef GTEST_6_TYPENAMES_ -#undef GTEST_7_TYPENAMES_ -#undef GTEST_8_TYPENAMES_ -#undef GTEST_9_TYPENAMES_ -#undef GTEST_10_TYPENAMES_ - -#undef GTEST_DECLARE_TUPLE_AS_FRIEND_ -#undef GTEST_BY_REF_ -#undef GTEST_ADD_REF_ -#undef GTEST_TUPLE_ELEMENT_ - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ -#elif GTEST_OS_SYMBIAN - -// On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to -// use STLport's tuple implementation, which unfortunately doesn't -// work as the copy of STLport distributed with Symbian is incomplete. -// By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to -// use its own tuple implementation. -#ifdef BOOST_HAS_TR1_TUPLE -#undef BOOST_HAS_TR1_TUPLE -#endif // BOOST_HAS_TR1_TUPLE - -// This prevents , which defines -// BOOST_HAS_TR1_TUPLE, from being #included by Boost's . -#define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED -#include - -#elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000) -// GCC 4.0+ implements tr1/tuple in the header. This does -// not conform to the TR1 spec, which requires the header to be . - -#if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 -// Until version 4.3.2, gcc has a bug that causes , -// which is #included by , to not compile when RTTI is -// disabled. _TR1_FUNCTIONAL is the header guard for -// . Hence the following #define is a hack to prevent -// from being included. -#define _TR1_FUNCTIONAL 1 -#include -#undef _TR1_FUNCTIONAL // Allows the user to #include - // if he chooses to. -#else -#include // NOLINT -#endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 - -#else -// If the compiler is not GCC 4.0+, we assume the user is using a -// spec-conforming TR1 implementation. -#include // NOLINT -#endif // GTEST_USE_OWN_TR1_TUPLE - -#endif // GTEST_HAS_TR1_TUPLE - -// Determines whether clone(2) is supported. -// Usually it will only be available on Linux, excluding -// Linux on the Itanium architecture. -// Also see http://linux.die.net/man/2/clone. -#ifndef GTEST_HAS_CLONE -// The user didn't tell us, so we need to figure it out. - -#if GTEST_OS_LINUX && !defined(__ia64__) -#define GTEST_HAS_CLONE 1 -#else -#define GTEST_HAS_CLONE 0 -#endif // GTEST_OS_LINUX && !defined(__ia64__) - -#endif // GTEST_HAS_CLONE - -// Determines whether to support stream redirection. This is used to test -// output correctness and to implement death tests. -#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN -#define GTEST_HAS_STREAM_REDIRECTION_ 1 -#endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN - -// Determines whether to support death tests. -// Google Test does not support death tests for VC 7.1 and earlier as -// abort() in a VC 7.1 application compiled as GUI in debug config -// pops up a dialog window that cannot be suppressed programmatically. -#if (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ - (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ - GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX) -#define GTEST_HAS_DEATH_TEST 1 -#include // NOLINT -#endif - -// We don't support MSVC 7.1 with exceptions disabled now. Therefore -// all the compilers we care about are adequate for supporting -// value-parameterized tests. -#define GTEST_HAS_PARAM_TEST 1 - -// Determines whether to support type-driven tests. - -// Typed tests need and variadic macros, which GCC, VC++ 8.0, -// Sun Pro CC, and IBM Visual Age support. -#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \ - defined(__IBMCPP__) -#define GTEST_HAS_TYPED_TEST 1 -#define GTEST_HAS_TYPED_TEST_P 1 -#endif - -// Determines whether to support Combine(). This only makes sense when -// value-parameterized tests are enabled. The implementation doesn't -// work on Sun Studio since it doesn't understand templated conversion -// operators. -#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC) -#define GTEST_HAS_COMBINE 1 -#endif - -// Determines whether the system compiler uses UTF-16 for encoding wide strings. -#define GTEST_WIDE_STRING_USES_UTF16_ \ - (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_SYMBIAN || GTEST_OS_AIX) - -// Defines some utility macros. - -// The GNU compiler emits a warning if nested "if" statements are followed by -// an "else" statement and braces are not used to explicitly disambiguate the -// "else" binding. This leads to problems with code like: -// -// if (gate) -// ASSERT_*(condition) << "Some message"; -// -// The "switch (0) case 0:" idiom is used to suppress this. -#ifdef __INTEL_COMPILER -#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ -#else -#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: // NOLINT -#endif - -// Use this annotation at the end of a struct/class definition to -// prevent the compiler from optimizing away instances that are never -// used. This is useful when all interesting logic happens inside the -// c'tor and / or d'tor. Example: -// -// struct Foo { -// Foo() { ... } -// } GTEST_ATTRIBUTE_UNUSED_; -// -// Also use it after a variable or parameter declaration to tell the -// compiler the variable/parameter does not have to be used. -#if defined(__GNUC__) && !defined(COMPILER_ICC) -#define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) -#else -#define GTEST_ATTRIBUTE_UNUSED_ -#endif - -// A macro to disallow operator= -// This should be used in the private: declarations for a class. -#define GTEST_DISALLOW_ASSIGN_(type)\ - void operator=(type const &) - -// A macro to disallow copy constructor and operator= -// This should be used in the private: declarations for a class. -#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)\ - type(type const &);\ - GTEST_DISALLOW_ASSIGN_(type) - -// Tell the compiler to warn about unused return values for functions declared -// with this macro. The macro should be used on function declarations -// following the argument list: -// -// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; -#if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC) -#define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result)) -#else -#define GTEST_MUST_USE_RESULT_ -#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC - -// Determine whether the compiler supports Microsoft's Structured Exception -// Handling. This is supported by several Windows compilers but generally -// does not exist on any other system. -#ifndef GTEST_HAS_SEH -// The user didn't tell us, so we need to figure it out. - -#if defined(_MSC_VER) || defined(__BORLANDC__) -// These two compilers are known to support SEH. -#define GTEST_HAS_SEH 1 -#else -// Assume no SEH. -#define GTEST_HAS_SEH 0 -#endif - -#endif // GTEST_HAS_SEH - -#ifdef _MSC_VER - -#if GTEST_LINKED_AS_SHARED_LIBRARY -#define GTEST_API_ __declspec(dllimport) -#elif GTEST_CREATE_SHARED_LIBRARY -#define GTEST_API_ __declspec(dllexport) -#endif - -#endif // _MSC_VER - -#ifndef GTEST_API_ -#define GTEST_API_ -#endif - -namespace testing { - -class Message; - -namespace internal { - -class String; - -typedef ::std::stringstream StrStream; - -// A helper for suppressing warnings on constant condition. It just -// returns 'condition'. -GTEST_API_ bool IsTrue(bool condition); - -// Defines scoped_ptr. - -// This implementation of scoped_ptr is PARTIAL - it only contains -// enough stuff to satisfy Google Test's need. -template -class scoped_ptr { - public: - typedef T element_type; - - explicit scoped_ptr(T* p = NULL) : ptr_(p) {} - ~scoped_ptr() { reset(); } - - T& operator*() const { return *ptr_; } - T* operator->() const { return ptr_; } - T* get() const { return ptr_; } - - T* release() { - T* const ptr = ptr_; - ptr_ = NULL; - return ptr; - } - - void reset(T* p = NULL) { - if (p != ptr_) { - if (IsTrue(sizeof(T) > 0)) { // Makes sure T is a complete type. - delete ptr_; - } - ptr_ = p; - } - } - private: - T* ptr_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr); -}; - -// Defines RE. - -// A simple C++ wrapper for . It uses the POSIX Extended -// Regular Expression syntax. -class GTEST_API_ RE { - public: - // A copy constructor is required by the Standard to initialize object - // references from r-values. - RE(const RE& other) { Init(other.pattern()); } - - // Constructs an RE from a string. - RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT - -#if GTEST_HAS_GLOBAL_STRING - RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT -#endif // GTEST_HAS_GLOBAL_STRING - - RE(const char* regex) { Init(regex); } // NOLINT - ~RE(); - - // Returns the string representation of the regex. - const char* pattern() const { return pattern_; } - - // FullMatch(str, re) returns true iff regular expression re matches - // the entire str. - // PartialMatch(str, re) returns true iff regular expression re - // matches a substring of str (including str itself). - // - // TODO(wan@google.com): make FullMatch() and PartialMatch() work - // when str contains NUL characters. - static bool FullMatch(const ::std::string& str, const RE& re) { - return FullMatch(str.c_str(), re); - } - static bool PartialMatch(const ::std::string& str, const RE& re) { - return PartialMatch(str.c_str(), re); - } - -#if GTEST_HAS_GLOBAL_STRING - static bool FullMatch(const ::string& str, const RE& re) { - return FullMatch(str.c_str(), re); - } - static bool PartialMatch(const ::string& str, const RE& re) { - return PartialMatch(str.c_str(), re); - } -#endif // GTEST_HAS_GLOBAL_STRING - - static bool FullMatch(const char* str, const RE& re); - static bool PartialMatch(const char* str, const RE& re); - - private: - void Init(const char* regex); - - // We use a const char* instead of a string, as Google Test may be used - // where string is not available. We also do not use Google Test's own - // String type here, in order to simplify dependencies between the - // files. - const char* pattern_; - bool is_valid_; -#if GTEST_USES_POSIX_RE - regex_t full_regex_; // For FullMatch(). - regex_t partial_regex_; // For PartialMatch(). -#else // GTEST_USES_SIMPLE_RE - const char* full_pattern_; // For FullMatch(); -#endif - - GTEST_DISALLOW_ASSIGN_(RE); -}; - -// Defines logging utilities: -// GTEST_LOG_(severity) - logs messages at the specified severity level. The -// message itself is streamed into the macro. -// LogToStderr() - directs all log messages to stderr. -// FlushInfoLog() - flushes informational log messages. - -enum GTestLogSeverity { - GTEST_INFO, - GTEST_WARNING, - GTEST_ERROR, - GTEST_FATAL -}; - -// Formats log entry severity, provides a stream object for streaming the -// log message, and terminates the message with a newline when going out of -// scope. -class GTEST_API_ GTestLog { - public: - GTestLog(GTestLogSeverity severity, const char* file, int line); - - // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. - ~GTestLog(); - - ::std::ostream& GetStream() { return ::std::cerr; } - - private: - const GTestLogSeverity severity_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog); -}; - -#define GTEST_LOG_(severity) \ - ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ - __FILE__, __LINE__).GetStream() - -inline void LogToStderr() {} -inline void FlushInfoLog() { fflush(NULL); } - -// INTERNAL IMPLEMENTATION - DO NOT USE. -// -// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition -// is not satisfied. -// Synopsys: -// GTEST_CHECK_(boolean_condition); -// or -// GTEST_CHECK_(boolean_condition) << "Additional message"; -// -// This checks the condition and if the condition is not satisfied -// it prints message about the condition violation, including the -// condition itself, plus additional message streamed into it, if any, -// and then it aborts the program. It aborts the program irrespective of -// whether it is built in the debug mode or not. -#define GTEST_CHECK_(condition) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::testing::internal::IsTrue(condition)) \ - ; \ - else \ - GTEST_LOG_(FATAL) << "Condition " #condition " failed. " - -// An all-mode assert to verify that the given POSIX-style function -// call returns 0 (indicating success). Known limitation: this -// doesn't expand to a balanced 'if' statement, so enclose the macro -// in {} if you need to use it as the only statement in an 'if' -// branch. -#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \ - if (const int gtest_error = (posix_call)) \ - GTEST_LOG_(FATAL) << #posix_call << "failed with error " \ - << gtest_error - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Downcasts the pointer of type Base to Derived. -// Derived must be a subclass of Base. The parameter MUST -// point to a class of type Derived, not any subclass of it. -// When RTTI is available, the function performs a runtime -// check to enforce this. -template -Derived* CheckedDowncastToActualType(Base* base) { -#if GTEST_HAS_RTTI - GTEST_CHECK_(typeid(*base) == typeid(Derived)); - return dynamic_cast(base); // NOLINT -#else - return static_cast(base); // Poor man's downcast. -#endif -} - -#if GTEST_HAS_STREAM_REDIRECTION_ - -// Defines the stderr capturer: -// CaptureStdout - starts capturing stdout. -// GetCapturedStdout - stops capturing stdout and returns the captured string. -// CaptureStderr - starts capturing stderr. -// GetCapturedStderr - stops capturing stderr and returns the captured string. -// -GTEST_API_ void CaptureStdout(); -GTEST_API_ String GetCapturedStdout(); -GTEST_API_ void CaptureStderr(); -GTEST_API_ String GetCapturedStderr(); - -#endif // GTEST_HAS_STREAM_REDIRECTION_ - - -#if GTEST_HAS_DEATH_TEST - -// A copy of all command line arguments. Set by InitGoogleTest(). -extern ::std::vector g_argvs; - -// GTEST_HAS_DEATH_TEST implies we have ::std::string. -const ::std::vector& GetArgvs(); - -#endif // GTEST_HAS_DEATH_TEST - -// Defines synchronization primitives. - -#if GTEST_HAS_PTHREAD - -// Sleeps for (roughly) n milli-seconds. This function is only for -// testing Google Test's own constructs. Don't use it in user tests, -// either directly or indirectly. -inline void SleepMilliseconds(int n) { - const timespec time = { - 0, // 0 seconds. - n * 1000L * 1000L, // And n ms. - }; - nanosleep(&time, NULL); -} - -// Allows a controller thread to pause execution of newly created -// threads until notified. Instances of this class must be created -// and destroyed in the controller thread. -// -// This class is only for testing Google Test's own constructs. Do not -// use it in user tests, either directly or indirectly. -class Notification { - public: - Notification() : notified_(false) {} - - // Notifies all threads created with this notification to start. Must - // be called from the controller thread. - void Notify() { notified_ = true; } - - // Blocks until the controller thread notifies. Must be called from a test - // thread. - void WaitForNotification() { - while(!notified_) { - SleepMilliseconds(10); - } - } - - private: - volatile bool notified_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); -}; - -// As a C-function, ThreadFuncWithCLinkage cannot be templated itself. -// Consequently, it cannot select a correct instantiation of ThreadWithParam -// in order to call its Run(). Introducing ThreadWithParamBase as a -// non-templated base class for ThreadWithParam allows us to bypass this -// problem. -class ThreadWithParamBase { - public: - virtual ~ThreadWithParamBase() {} - virtual void Run() = 0; -}; - -// pthread_create() accepts a pointer to a function type with the C linkage. -// According to the Standard (7.5/1), function types with different linkages -// are different even if they are otherwise identical. Some compilers (for -// example, SunStudio) treat them as different types. Since class methods -// cannot be defined with C-linkage we need to define a free C-function to -// pass into pthread_create(). -extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { - static_cast(thread)->Run(); - return NULL; -} - -// Helper class for testing Google Test's multi-threading constructs. -// To use it, write: -// -// void ThreadFunc(int param) { /* Do things with param */ } -// Notification thread_can_start; -// ... -// // The thread_can_start parameter is optional; you can supply NULL. -// ThreadWithParam thread(&ThreadFunc, 5, &thread_can_start); -// thread_can_start.Notify(); -// -// These classes are only for testing Google Test's own constructs. Do -// not use them in user tests, either directly or indirectly. -template -class ThreadWithParam : public ThreadWithParamBase { - public: - typedef void (*UserThreadFunc)(T); - - ThreadWithParam( - UserThreadFunc func, T param, Notification* thread_can_start) - : func_(func), - param_(param), - thread_can_start_(thread_can_start), - finished_(false) { - ThreadWithParamBase* const base = this; - // The thread can be created only after all fields except thread_ - // have been initialized. - GTEST_CHECK_POSIX_SUCCESS_( - pthread_create(&thread_, 0, &ThreadFuncWithCLinkage, base)); - } - ~ThreadWithParam() { Join(); } - - void Join() { - if (!finished_) { - GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, 0)); - finished_ = true; - } - } - - virtual void Run() { - if (thread_can_start_ != NULL) - thread_can_start_->WaitForNotification(); - func_(param_); - } - - private: - const UserThreadFunc func_; // User-supplied thread function. - const T param_; // User-supplied parameter to the thread function. - // When non-NULL, used to block execution until the controller thread - // notifies. - Notification* const thread_can_start_; - bool finished_; // true iff we know that the thread function has finished. - pthread_t thread_; // The native thread object. - - GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); -}; - -// gtest-port.h guarantees to #include when GTEST_HAS_PTHREAD is -// true. -#include - -// MutexBase and Mutex implement mutex on pthreads-based platforms. They -// are used in conjunction with class MutexLock: -// -// Mutex mutex; -// ... -// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end -// // of the current scope. -// -// MutexBase implements behavior for both statically and dynamically -// allocated mutexes. Do not use MutexBase directly. Instead, write -// the following to define a static mutex: -// -// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); -// -// You can forward declare a static mutex like this: -// -// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); -// -// To create a dynamic mutex, just define an object of type Mutex. -class MutexBase { - public: - // Acquires this mutex. - void Lock() { - GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); - owner_ = pthread_self(); - } - - // Releases this mutex. - void Unlock() { - // We don't protect writing to owner_ here, as it's the caller's - // responsibility to ensure that the current thread holds the - // mutex when this is called. - owner_ = 0; - GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); - } - - // Does nothing if the current thread holds the mutex. Otherwise, crashes - // with high probability. - void AssertHeld() const { - GTEST_CHECK_(owner_ == pthread_self()) - << "The current thread is not holding the mutex @" << this; - } - - // A static mutex may be used before main() is entered. It may even - // be used before the dynamic initialization stage. Therefore we - // must be able to initialize a static mutex object at link time. - // This means MutexBase has to be a POD and its member variables - // have to be public. - public: - pthread_mutex_t mutex_; // The underlying pthread mutex. - pthread_t owner_; // The thread holding the mutex; 0 means no one holds it. -}; - -// Forward-declares a static mutex. -#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ - extern ::testing::internal::MutexBase mutex - -// Defines and statically (i.e. at link time) initializes a static mutex. -#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ - ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, 0 } - -// The Mutex class can only be used for mutexes created at runtime. It -// shares its API with MutexBase otherwise. -class Mutex : public MutexBase { - public: - Mutex() { - GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); - owner_ = 0; - } - ~Mutex() { - GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); - } - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); -}; - -// We cannot name this class MutexLock as the ctor declaration would -// conflict with a macro named MutexLock, which is defined on some -// platforms. Hence the typedef trick below. -class GTestMutexLock { - public: - explicit GTestMutexLock(MutexBase* mutex) - : mutex_(mutex) { mutex_->Lock(); } - - ~GTestMutexLock() { mutex_->Unlock(); } - - private: - MutexBase* const mutex_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); -}; - -typedef GTestMutexLock MutexLock; - -// Helpers for ThreadLocal. - -// pthread_key_create() requires DeleteThreadLocalValue() to have -// C-linkage. Therefore it cannot be templatized to access -// ThreadLocal. Hence the need for class -// ThreadLocalValueHolderBase. -class ThreadLocalValueHolderBase { - public: - virtual ~ThreadLocalValueHolderBase() {} -}; - -// Called by pthread to delete thread-local data stored by -// pthread_setspecific(). -extern "C" inline void DeleteThreadLocalValue(void* value_holder) { - delete static_cast(value_holder); -} - -// Implements thread-local storage on pthreads-based systems. -// -// // Thread 1 -// ThreadLocal tl(100); // 100 is the default value for each thread. -// -// // Thread 2 -// tl.set(150); // Changes the value for thread 2 only. -// EXPECT_EQ(150, tl.get()); -// -// // Thread 1 -// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. -// tl.set(200); -// EXPECT_EQ(200, tl.get()); -// -// The template type argument T must have a public copy constructor. -// In addition, the default ThreadLocal constructor requires T to have -// a public default constructor. -// -// An object managed for a thread by a ThreadLocal instance is deleted -// when the thread exits. Or, if the ThreadLocal instance dies in -// that thread, when the ThreadLocal dies. It's the user's -// responsibility to ensure that all other threads using a ThreadLocal -// have exited when it dies, or the per-thread objects for those -// threads will not be deleted. -// -// Google Test only uses global ThreadLocal objects. That means they -// will die after main() has returned. Therefore, no per-thread -// object managed by Google Test will be leaked as long as all threads -// using Google Test have exited when main() returns. -template -class ThreadLocal { - public: - ThreadLocal() : key_(CreateKey()), - default_() {} - explicit ThreadLocal(const T& value) : key_(CreateKey()), - default_(value) {} - - ~ThreadLocal() { - // Destroys the managed object for the current thread, if any. - DeleteThreadLocalValue(pthread_getspecific(key_)); - - // Releases resources associated with the key. This will *not* - // delete managed objects for other threads. - GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_)); - } - - T* pointer() { return GetOrCreateValue(); } - const T* pointer() const { return GetOrCreateValue(); } - const T& get() const { return *pointer(); } - void set(const T& value) { *pointer() = value; } - - private: - // Holds a value of type T. - class ValueHolder : public ThreadLocalValueHolderBase { - public: - explicit ValueHolder(const T& value) : value_(value) {} - - T* pointer() { return &value_; } - - private: - T value_; - GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); - }; - - static pthread_key_t CreateKey() { - pthread_key_t key; - // When a thread exits, DeleteThreadLocalValue() will be called on - // the object managed for that thread. - GTEST_CHECK_POSIX_SUCCESS_( - pthread_key_create(&key, &DeleteThreadLocalValue)); - return key; - } - - T* GetOrCreateValue() const { - ThreadLocalValueHolderBase* const holder = - static_cast(pthread_getspecific(key_)); - if (holder != NULL) { - return CheckedDowncastToActualType(holder)->pointer(); - } - - ValueHolder* const new_holder = new ValueHolder(default_); - ThreadLocalValueHolderBase* const holder_base = new_holder; - GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); - return new_holder->pointer(); - } - - // A key pthreads uses for looking up per-thread values. - const pthread_key_t key_; - const T default_; // The default value for each thread. - - GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); -}; - -#define GTEST_IS_THREADSAFE 1 - -#else // GTEST_HAS_PTHREAD - -// A dummy implementation of synchronization primitives (mutex, lock, -// and thread-local variable). Necessary for compiling Google Test where -// mutex is not supported - using Google Test in multiple threads is not -// supported on such platforms. - -class Mutex { - public: - Mutex() {} - void AssertHeld() const {} -}; - -#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ - extern ::testing::internal::Mutex mutex - -#define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex - -class GTestMutexLock { - public: - explicit GTestMutexLock(Mutex*) {} // NOLINT -}; - -typedef GTestMutexLock MutexLock; - -template -class ThreadLocal { - public: - ThreadLocal() : value_() {} - explicit ThreadLocal(const T& value) : value_(value) {} - T* pointer() { return &value_; } - const T* pointer() const { return &value_; } - const T& get() const { return value_; } - void set(const T& value) { value_ = value; } - private: - T value_; -}; - -// The above synchronization primitives have dummy implementations. -// Therefore Google Test is not thread-safe. -#define GTEST_IS_THREADSAFE 0 - -#endif // GTEST_HAS_PTHREAD - -// Returns the number of threads running in the process, or 0 to indicate that -// we cannot detect it. -GTEST_API_ size_t GetThreadCount(); - -// Passing non-POD classes through ellipsis (...) crashes the ARM -// compiler and generates a warning in Sun Studio. The Nokia Symbian -// and the IBM XL C/C++ compiler try to instantiate a copy constructor -// for objects passed through ellipsis (...), failing for uncopyable -// objects. We define this to ensure that only POD is passed through -// ellipsis on these systems. -#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC) -// We lose support for NULL detection where the compiler doesn't like -// passing non-POD classes through ellipsis (...). -#define GTEST_ELLIPSIS_NEEDS_POD_ 1 -#else -#define GTEST_CAN_COMPARE_NULL 1 -#endif - -// The Nokia Symbian and IBM XL C/C++ compilers cannot decide between -// const T& and const T* in a function template. These compilers -// _can_ decide between class template specializations for T and T*, -// so a tr1::type_traits-like is_pointer works. -#if defined(__SYMBIAN32__) || defined(__IBMCPP__) -#define GTEST_NEEDS_IS_POINTER_ 1 -#endif - -template -struct bool_constant { - typedef bool_constant type; - static const bool value = bool_value; -}; -template const bool bool_constant::value; - -typedef bool_constant false_type; -typedef bool_constant true_type; - -template -struct is_pointer : public false_type {}; - -template -struct is_pointer : public true_type {}; - -#if GTEST_OS_WINDOWS -#define GTEST_PATH_SEP_ "\\" -#define GTEST_HAS_ALT_PATH_SEP_ 1 -// The biggest signed integer type the compiler supports. -typedef __int64 BiggestInt; -#else -#define GTEST_PATH_SEP_ "/" -#define GTEST_HAS_ALT_PATH_SEP_ 0 -typedef long long BiggestInt; // NOLINT -#endif // GTEST_OS_WINDOWS - -// The testing::internal::posix namespace holds wrappers for common -// POSIX functions. These wrappers hide the differences between -// Windows/MSVC and POSIX systems. Since some compilers define these -// standard functions as macros, the wrapper cannot have the same name -// as the wrapped function. - -namespace posix { - -// Functions with a different name on Windows. - -#if GTEST_OS_WINDOWS - -typedef struct _stat StatStruct; - -#ifdef __BORLANDC__ -inline int IsATTY(int fd) { return isatty(fd); } -inline int StrCaseCmp(const char* s1, const char* s2) { - return stricmp(s1, s2); -} -inline char* StrDup(const char* src) { return strdup(src); } -#else // !__BORLANDC__ -#if GTEST_OS_WINDOWS_MOBILE -inline int IsATTY(int /* fd */) { return 0; } -#else -inline int IsATTY(int fd) { return _isatty(fd); } -#endif // GTEST_OS_WINDOWS_MOBILE -inline int StrCaseCmp(const char* s1, const char* s2) { - return _stricmp(s1, s2); -} -inline char* StrDup(const char* src) { return _strdup(src); } -#endif // __BORLANDC__ - -#if GTEST_OS_WINDOWS_MOBILE -inline int FileNo(FILE* file) { return reinterpret_cast(_fileno(file)); } -// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this -// time and thus not defined there. -#else -inline int FileNo(FILE* file) { return _fileno(file); } -inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } -inline int RmDir(const char* dir) { return _rmdir(dir); } -inline bool IsDir(const StatStruct& st) { - return (_S_IFDIR & st.st_mode) != 0; -} -#endif // GTEST_OS_WINDOWS_MOBILE - -#else - -typedef struct stat StatStruct; - -inline int FileNo(FILE* file) { return fileno(file); } -inline int IsATTY(int fd) { return isatty(fd); } -inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } -inline int StrCaseCmp(const char* s1, const char* s2) { - return strcasecmp(s1, s2); -} -inline char* StrDup(const char* src) { return strdup(src); } -inline int RmDir(const char* dir) { return rmdir(dir); } -inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } - -#endif // GTEST_OS_WINDOWS - -// Functions deprecated by MSVC 8.0. - -#ifdef _MSC_VER -// Temporarily disable warning 4996 (deprecated function). -#pragma warning(push) -#pragma warning(disable:4996) -#endif - -inline const char* StrNCpy(char* dest, const char* src, size_t n) { - return strncpy(dest, src, n); -} - -// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and -// StrError() aren't needed on Windows CE at this time and thus not -// defined there. - -#if !GTEST_OS_WINDOWS_MOBILE -inline int ChDir(const char* dir) { return chdir(dir); } -#endif -inline FILE* FOpen(const char* path, const char* mode) { - return fopen(path, mode); -} -#if !GTEST_OS_WINDOWS_MOBILE -inline FILE *FReopen(const char* path, const char* mode, FILE* stream) { - return freopen(path, mode, stream); -} -inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } -#endif -inline int FClose(FILE* fp) { return fclose(fp); } -#if !GTEST_OS_WINDOWS_MOBILE -inline int Read(int fd, void* buf, unsigned int count) { - return static_cast(read(fd, buf, count)); -} -inline int Write(int fd, const void* buf, unsigned int count) { - return static_cast(write(fd, buf, count)); -} -inline int Close(int fd) { return close(fd); } -inline const char* StrError(int errnum) { return strerror(errnum); } -#endif -inline const char* GetEnv(const char* name) { -#if GTEST_OS_WINDOWS_MOBILE - // We are on Windows CE, which has no environment variables. - return NULL; -#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) - // Environment variables which we programmatically clear will be set to the - // empty string rather than unset (NULL). Handle that case. - const char* const env = getenv(name); - return (env != NULL && env[0] != '\0') ? env : NULL; -#else - return getenv(name); -#endif -} - -#ifdef _MSC_VER -#pragma warning(pop) // Restores the warning state. -#endif - -#if GTEST_OS_WINDOWS_MOBILE -// Windows CE has no C library. The abort() function is used in -// several places in Google Test. This implementation provides a reasonable -// imitation of standard behaviour. -void Abort(); -#else -inline void Abort() { abort(); } -#endif // GTEST_OS_WINDOWS_MOBILE - -} // namespace posix - -// The maximum number a BiggestInt can represent. This definition -// works no matter BiggestInt is represented in one's complement or -// two's complement. -// -// We cannot rely on numeric_limits in STL, as __int64 and long long -// are not part of standard C++ and numeric_limits doesn't need to be -// defined for them. -const BiggestInt kMaxBiggestInt = - ~(static_cast(1) << (8*sizeof(BiggestInt) - 1)); - -// This template class serves as a compile-time function from size to -// type. It maps a size in bytes to a primitive type with that -// size. e.g. -// -// TypeWithSize<4>::UInt -// -// is typedef-ed to be unsigned int (unsigned integer made up of 4 -// bytes). -// -// Such functionality should belong to STL, but I cannot find it -// there. -// -// Google Test uses this class in the implementation of floating-point -// comparison. -// -// For now it only handles UInt (unsigned int) as that's all Google Test -// needs. Other types can be easily added in the future if need -// arises. -template -class TypeWithSize { - public: - // This prevents the user from using TypeWithSize with incorrect - // values of N. - typedef void UInt; -}; - -// The specialization for size 4. -template <> -class TypeWithSize<4> { - public: - // unsigned int has size 4 in both gcc and MSVC. - // - // As base/basictypes.h doesn't compile on Windows, we cannot use - // uint32, uint64, and etc here. - typedef int Int; - typedef unsigned int UInt; -}; - -// The specialization for size 8. -template <> -class TypeWithSize<8> { - public: -#if GTEST_OS_WINDOWS - typedef __int64 Int; - typedef unsigned __int64 UInt; -#else - typedef long long Int; // NOLINT - typedef unsigned long long UInt; // NOLINT -#endif // GTEST_OS_WINDOWS -}; - -// Integer types of known sizes. -typedef TypeWithSize<4>::Int Int32; -typedef TypeWithSize<4>::UInt UInt32; -typedef TypeWithSize<8>::Int Int64; -typedef TypeWithSize<8>::UInt UInt64; -typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. - -// Utilities for command line flags and environment variables. - -// Macro for referencing flags. -#define GTEST_FLAG(name) FLAGS_gtest_##name - -// Macros for declaring flags. -#define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name) -#define GTEST_DECLARE_int32_(name) \ - GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name) -#define GTEST_DECLARE_string_(name) \ - GTEST_API_ extern ::testing::internal::String GTEST_FLAG(name) - -// Macros for defining flags. -#define GTEST_DEFINE_bool_(name, default_val, doc) \ - GTEST_API_ bool GTEST_FLAG(name) = (default_val) -#define GTEST_DEFINE_int32_(name, default_val, doc) \ - GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val) -#define GTEST_DEFINE_string_(name, default_val, doc) \ - GTEST_API_ ::testing::internal::String GTEST_FLAG(name) = (default_val) - -// Parses 'str' for a 32-bit signed integer. If successful, writes the result -// to *value and returns true; otherwise leaves *value unchanged and returns -// false. -// TODO(chandlerc): Find a better way to refactor flag and environment parsing -// out of both gtest-port.cc and gtest.cc to avoid exporting this utility -// function. -bool ParseInt32(const Message& src_text, const char* str, Int32* value); - -// Parses a bool/Int32/string from the environment variable -// corresponding to the given Google Test flag. -bool BoolFromGTestEnv(const char* flag, bool default_val); -GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val); -const char* StringFromGTestEnv(const char* flag, const char* default_val); - -} // namespace internal -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ - -#if GTEST_OS_LINUX -#include -#include -#include -#include -#endif // GTEST_OS_LINUX - -#include -#include -#include -#include -#include - -// Copyright 2005, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) -// -// The Google C++ Testing Framework (Google Test) -// -// This header file declares the String class and functions used internally by -// Google Test. They are subject to change without notice. They should not used -// by code external to Google Test. -// -// This header file is #included by . -// It should not be #included by other files. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ - -#ifdef __BORLANDC__ -// string.h is not guaranteed to provide strcpy on C++ Builder. -#include -#endif - -#include - -#include - -namespace testing { -namespace internal { - -// String - a UTF-8 string class. -// -// For historic reasons, we don't use std::string. -// -// TODO(wan@google.com): replace this class with std::string or -// implement it in terms of the latter. -// -// Note that String can represent both NULL and the empty string, -// while std::string cannot represent NULL. -// -// NULL and the empty string are considered different. NULL is less -// than anything (including the empty string) except itself. -// -// This class only provides minimum functionality necessary for -// implementing Google Test. We do not intend to implement a full-fledged -// string class here. -// -// Since the purpose of this class is to provide a substitute for -// std::string on platforms where it cannot be used, we define a copy -// constructor and assignment operators such that we don't need -// conditional compilation in a lot of places. -// -// In order to make the representation efficient, the d'tor of String -// is not virtual. Therefore DO NOT INHERIT FROM String. -class GTEST_API_ String { - public: - // Static utility methods - - // Returns the input enclosed in double quotes if it's not NULL; - // otherwise returns "(null)". For example, "\"Hello\"" is returned - // for input "Hello". - // - // This is useful for printing a C string in the syntax of a literal. - // - // Known issue: escape sequences are not handled yet. - static String ShowCStringQuoted(const char* c_str); - - // Clones a 0-terminated C string, allocating memory using new. The - // caller is responsible for deleting the return value using - // delete[]. Returns the cloned string, or NULL if the input is - // NULL. - // - // This is different from strdup() in string.h, which allocates - // memory using malloc(). - static const char* CloneCString(const char* c_str); - -#if GTEST_OS_WINDOWS_MOBILE - // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be - // able to pass strings to Win32 APIs on CE we need to convert them - // to 'Unicode', UTF-16. - - // Creates a UTF-16 wide string from the given ANSI string, allocating - // memory using new. The caller is responsible for deleting the return - // value using delete[]. Returns the wide string, or NULL if the - // input is NULL. - // - // The wide string is created using the ANSI codepage (CP_ACP) to - // match the behaviour of the ANSI versions of Win32 calls and the - // C runtime. - static LPCWSTR AnsiToUtf16(const char* c_str); - - // Creates an ANSI string from the given wide string, allocating - // memory using new. The caller is responsible for deleting the return - // value using delete[]. Returns the ANSI string, or NULL if the - // input is NULL. - // - // The returned string is created using the ANSI codepage (CP_ACP) to - // match the behaviour of the ANSI versions of Win32 calls and the - // C runtime. - static const char* Utf16ToAnsi(LPCWSTR utf16_str); -#endif - - // Compares two C strings. Returns true iff they have the same content. - // - // Unlike strcmp(), this function can handle NULL argument(s). A - // NULL C string is considered different to any non-NULL C string, - // including the empty string. - static bool CStringEquals(const char* lhs, const char* rhs); - - // Converts a wide C string to a String using the UTF-8 encoding. - // NULL will be converted to "(null)". If an error occurred during - // the conversion, "(failed to convert from wide string)" is - // returned. - static String ShowWideCString(const wchar_t* wide_c_str); - - // Similar to ShowWideCString(), except that this function encloses - // the converted string in double quotes. - static String ShowWideCStringQuoted(const wchar_t* wide_c_str); - - // Compares two wide C strings. Returns true iff they have the same - // content. - // - // Unlike wcscmp(), this function can handle NULL argument(s). A - // NULL C string is considered different to any non-NULL C string, - // including the empty string. - static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); - - // Compares two C strings, ignoring case. Returns true iff they - // have the same content. - // - // Unlike strcasecmp(), this function can handle NULL argument(s). - // A NULL C string is considered different to any non-NULL C string, - // including the empty string. - static bool CaseInsensitiveCStringEquals(const char* lhs, - const char* rhs); - - // Compares two wide C strings, ignoring case. Returns true iff they - // have the same content. - // - // Unlike wcscasecmp(), this function can handle NULL argument(s). - // A NULL C string is considered different to any non-NULL wide C string, - // including the empty string. - // NB: The implementations on different platforms slightly differ. - // On windows, this method uses _wcsicmp which compares according to LC_CTYPE - // environment variable. On GNU platform this method uses wcscasecmp - // which compares according to LC_CTYPE category of the current locale. - // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the - // current locale. - static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, - const wchar_t* rhs); - - // Formats a list of arguments to a String, using the same format - // spec string as for printf. - // - // We do not use the StringPrintf class as it is not universally - // available. - // - // The result is limited to 4096 characters (including the tailing - // 0). If 4096 characters are not enough to format the input, - // "" is returned. - static String Format(const char* format, ...); - - // C'tors - - // The default c'tor constructs a NULL string. - String() : c_str_(NULL), length_(0) {} - - // Constructs a String by cloning a 0-terminated C string. - String(const char* a_c_str) { // NOLINT - if (a_c_str == NULL) { - c_str_ = NULL; - length_ = 0; - } else { - ConstructNonNull(a_c_str, strlen(a_c_str)); - } - } - - // Constructs a String by copying a given number of chars from a - // buffer. E.g. String("hello", 3) creates the string "hel", - // String("a\0bcd", 4) creates "a\0bc", String(NULL, 0) creates "", - // and String(NULL, 1) results in access violation. - String(const char* buffer, size_t a_length) { - ConstructNonNull(buffer, a_length); - } - - // The copy c'tor creates a new copy of the string. The two - // String objects do not share content. - String(const String& str) : c_str_(NULL), length_(0) { *this = str; } - - // D'tor. String is intended to be a final class, so the d'tor - // doesn't need to be virtual. - ~String() { delete[] c_str_; } - - // Allows a String to be implicitly converted to an ::std::string or - // ::string, and vice versa. Converting a String containing a NULL - // pointer to ::std::string or ::string is undefined behavior. - // Converting a ::std::string or ::string containing an embedded NUL - // character to a String will result in the prefix up to the first - // NUL character. - String(const ::std::string& str) { - ConstructNonNull(str.c_str(), str.length()); - } - - operator ::std::string() const { return ::std::string(c_str(), length()); } - -#if GTEST_HAS_GLOBAL_STRING - String(const ::string& str) { - ConstructNonNull(str.c_str(), str.length()); - } - - operator ::string() const { return ::string(c_str(), length()); } -#endif // GTEST_HAS_GLOBAL_STRING - - // Returns true iff this is an empty string (i.e. ""). - bool empty() const { return (c_str() != NULL) && (length() == 0); } - - // Compares this with another String. - // Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 - // if this is greater than rhs. - int Compare(const String& rhs) const; - - // Returns true iff this String equals the given C string. A NULL - // string and a non-NULL string are considered not equal. - bool operator==(const char* a_c_str) const { return Compare(a_c_str) == 0; } - - // Returns true iff this String is less than the given String. A - // NULL string is considered less than "". - bool operator<(const String& rhs) const { return Compare(rhs) < 0; } - - // Returns true iff this String doesn't equal the given C string. A NULL - // string and a non-NULL string are considered not equal. - bool operator!=(const char* a_c_str) const { return !(*this == a_c_str); } - - // Returns true iff this String ends with the given suffix. *Any* - // String is considered to end with a NULL or empty suffix. - bool EndsWith(const char* suffix) const; - - // Returns true iff this String ends with the given suffix, not considering - // case. Any String is considered to end with a NULL or empty suffix. - bool EndsWithCaseInsensitive(const char* suffix) const; - - // Returns the length of the encapsulated string, or 0 if the - // string is NULL. - size_t length() const { return length_; } - - // Gets the 0-terminated C string this String object represents. - // The String object still owns the string. Therefore the caller - // should NOT delete the return value. - const char* c_str() const { return c_str_; } - - // Assigns a C string to this object. Self-assignment works. - const String& operator=(const char* a_c_str) { - return *this = String(a_c_str); - } - - // Assigns a String object to this object. Self-assignment works. - const String& operator=(const String& rhs) { - if (this != &rhs) { - delete[] c_str_; - if (rhs.c_str() == NULL) { - c_str_ = NULL; - length_ = 0; - } else { - ConstructNonNull(rhs.c_str(), rhs.length()); - } - } - - return *this; - } - - private: - // Constructs a non-NULL String from the given content. This - // function can only be called when data_ has not been allocated. - // ConstructNonNull(NULL, 0) results in an empty string (""). - // ConstructNonNull(NULL, non_zero) is undefined behavior. - void ConstructNonNull(const char* buffer, size_t a_length) { - char* const str = new char[a_length + 1]; - memcpy(str, buffer, a_length); - str[a_length] = '\0'; - c_str_ = str; - length_ = a_length; - } - - const char* c_str_; - size_t length_; -}; // class String - -// Streams a String to an ostream. Each '\0' character in the String -// is replaced with "\\0". -inline ::std::ostream& operator<<(::std::ostream& os, const String& str) { - if (str.c_str() == NULL) { - os << "(null)"; - } else { - const char* const c_str = str.c_str(); - for (size_t i = 0; i != str.length(); i++) { - if (c_str[i] == '\0') { - os << "\\0"; - } else { - os << c_str[i]; - } - } - } - return os; -} - -// Gets the content of the StrStream's buffer as a String. Each '\0' -// character in the buffer is replaced with "\\0". -GTEST_API_ String StrStreamToString(StrStream* stream); - -// Converts a streamable value to a String. A NULL pointer is -// converted to "(null)". When the input value is a ::string, -// ::std::string, ::wstring, or ::std::wstring object, each NUL -// character in it is replaced with "\\0". - -// Declared here but defined in gtest.h, so that it has access -// to the definition of the Message class, required by the ARM -// compiler. -template -String StreamableToString(const T& streamable); - -} // namespace internal -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ -// Copyright 2008, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: keith.ray@gmail.com (Keith Ray) -// -// Google Test filepath utilities -// -// This header file declares classes and functions used internally by -// Google Test. They are subject to change without notice. -// -// This file is #included in . -// Do not include this header file separately! - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ - - -namespace testing { -namespace internal { - -// FilePath - a class for file and directory pathname manipulation which -// handles platform-specific conventions (like the pathname separator). -// Used for helper functions for naming files in a directory for xml output. -// Except for Set methods, all methods are const or static, which provides an -// "immutable value object" -- useful for peace of mind. -// A FilePath with a value ending in a path separator ("like/this/") represents -// a directory, otherwise it is assumed to represent a file. In either case, -// it may or may not represent an actual file or directory in the file system. -// Names are NOT checked for syntax correctness -- no checking for illegal -// characters, malformed paths, etc. - -class GTEST_API_ FilePath { - public: - FilePath() : pathname_("") { } - FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } - - explicit FilePath(const char* pathname) : pathname_(pathname) { - Normalize(); - } - - explicit FilePath(const String& pathname) : pathname_(pathname) { - Normalize(); - } - - FilePath& operator=(const FilePath& rhs) { - Set(rhs); - return *this; - } - - void Set(const FilePath& rhs) { - pathname_ = rhs.pathname_; - } - - String ToString() const { return pathname_; } - const char* c_str() const { return pathname_.c_str(); } - - // Returns the current working directory, or "" if unsuccessful. - static FilePath GetCurrentDir(); - - // Given directory = "dir", base_name = "test", number = 0, - // extension = "xml", returns "dir/test.xml". If number is greater - // than zero (e.g., 12), returns "dir/test_12.xml". - // On Windows platform, uses \ as the separator rather than /. - static FilePath MakeFileName(const FilePath& directory, - const FilePath& base_name, - int number, - const char* extension); - - // Given directory = "dir", relative_path = "test.xml", - // returns "dir/test.xml". - // On Windows, uses \ as the separator rather than /. - static FilePath ConcatPaths(const FilePath& directory, - const FilePath& relative_path); - - // Returns a pathname for a file that does not currently exist. The pathname - // will be directory/base_name.extension or - // directory/base_name_.extension if directory/base_name.extension - // already exists. The number will be incremented until a pathname is found - // that does not already exist. - // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. - // There could be a race condition if two or more processes are calling this - // function at the same time -- they could both pick the same filename. - static FilePath GenerateUniqueFileName(const FilePath& directory, - const FilePath& base_name, - const char* extension); - - // Returns true iff the path is NULL or "". - bool IsEmpty() const { return c_str() == NULL || *c_str() == '\0'; } - - // If input name has a trailing separator character, removes it and returns - // the name, otherwise return the name string unmodified. - // On Windows platform, uses \ as the separator, other platforms use /. - FilePath RemoveTrailingPathSeparator() const; - - // Returns a copy of the FilePath with the directory part removed. - // Example: FilePath("path/to/file").RemoveDirectoryName() returns - // FilePath("file"). If there is no directory part ("just_a_file"), it returns - // the FilePath unmodified. If there is no file part ("just_a_dir/") it - // returns an empty FilePath (""). - // On Windows platform, '\' is the path separator, otherwise it is '/'. - FilePath RemoveDirectoryName() const; - - // RemoveFileName returns the directory path with the filename removed. - // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". - // If the FilePath is "a_file" or "/a_file", RemoveFileName returns - // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does - // not have a file, like "just/a/dir/", it returns the FilePath unmodified. - // On Windows platform, '\' is the path separator, otherwise it is '/'. - FilePath RemoveFileName() const; - - // Returns a copy of the FilePath with the case-insensitive extension removed. - // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns - // FilePath("dir/file"). If a case-insensitive extension is not - // found, returns a copy of the original FilePath. - FilePath RemoveExtension(const char* extension) const; - - // Creates directories so that path exists. Returns true if successful or if - // the directories already exist; returns false if unable to create - // directories for any reason. Will also return false if the FilePath does - // not represent a directory (that is, it doesn't end with a path separator). - bool CreateDirectoriesRecursively() const; - - // Create the directory so that path exists. Returns true if successful or - // if the directory already exists; returns false if unable to create the - // directory for any reason, including if the parent directory does not - // exist. Not named "CreateDirectory" because that's a macro on Windows. - bool CreateFolder() const; - - // Returns true if FilePath describes something in the file-system, - // either a file, directory, or whatever, and that something exists. - bool FileOrDirectoryExists() const; - - // Returns true if pathname describes a directory in the file-system - // that exists. - bool DirectoryExists() const; - - // Returns true if FilePath ends with a path separator, which indicates that - // it is intended to represent a directory. Returns false otherwise. - // This does NOT check that a directory (or file) actually exists. - bool IsDirectory() const; - - // Returns true if pathname describes a root directory. (Windows has one - // root directory per disk drive.) - bool IsRootDirectory() const; - - // Returns true if pathname describes an absolute path. - bool IsAbsolutePath() const; - - private: - // Replaces multiple consecutive separators with a single separator. - // For example, "bar///foo" becomes "bar/foo". Does not eliminate other - // redundancies that might be in a pathname involving "." or "..". - // - // A pathname with multiple consecutive separators may occur either through - // user error or as a result of some scripts or APIs that generate a pathname - // with a trailing separator. On other platforms the same API or script - // may NOT generate a pathname with a trailing "/". Then elsewhere that - // pathname may have another "/" and pathname components added to it, - // without checking for the separator already being there. - // The script language and operating system may allow paths like "foo//bar" - // but some of the functions in FilePath will not handle that correctly. In - // particular, RemoveTrailingPathSeparator() only removes one separator, and - // it is called in CreateDirectoriesRecursively() assuming that it will change - // a pathname from directory syntax (trailing separator) to filename syntax. - // - // On Windows this method also replaces the alternate path separator '/' with - // the primary path separator '\\', so that for example "bar\\/\\foo" becomes - // "bar\\foo". - - void Normalize(); - - // Returns a pointer to the last occurence of a valid path separator in - // the FilePath. On Windows, for example, both '/' and '\' are valid path - // separators. Returns NULL if no path separator was found. - const char* FindLastPathSeparator() const; - - String pathname_; -}; // class FilePath - -} // namespace internal -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ -// This file was GENERATED by command: -// pump.py gtest-type-util.h.pump -// DO NOT EDIT BY HAND!!! - -// Copyright 2008 Google Inc. -// All Rights Reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: wan@google.com (Zhanyong Wan) - -// Type utilities needed for implementing typed and type-parameterized -// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND! -// -// Currently we support at most 50 types in a list, and at most 50 -// type-parameterized tests in one type-parameterized test case. -// Please contact googletestframework@googlegroups.com if you need -// more. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ - - -#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P - -// #ifdef __GNUC__ is too general here. It is possible to use gcc without using -// libstdc++ (which is where cxxabi.h comes from). -#ifdef __GLIBCXX__ -#include -#endif // __GLIBCXX__ - -namespace testing { -namespace internal { - -// AssertyTypeEq::type is defined iff T1 and T2 are the same -// type. This can be used as a compile-time assertion to ensure that -// two types are equal. - -template -struct AssertTypeEq; - -template -struct AssertTypeEq { - typedef bool type; -}; - -// GetTypeName() returns a human-readable name of type T. -template -String GetTypeName() { -#if GTEST_HAS_RTTI - - const char* const name = typeid(T).name(); -#ifdef __GLIBCXX__ - int status = 0; - // gcc's implementation of typeid(T).name() mangles the type name, - // so we have to demangle it. - char* const readable_name = abi::__cxa_demangle(name, 0, 0, &status); - const String name_str(status == 0 ? readable_name : name); - free(readable_name); - return name_str; -#else - return name; -#endif // __GLIBCXX__ - -#else - return ""; -#endif // GTEST_HAS_RTTI -} - -// A unique type used as the default value for the arguments of class -// template Types. This allows us to simulate variadic templates -// (e.g. Types, Type, and etc), which C++ doesn't -// support directly. -struct None {}; - -// The following family of struct and struct templates are used to -// represent type lists. In particular, TypesN -// represents a type list with N types (T1, T2, ..., and TN) in it. -// Except for Types0, every struct in the family has two member types: -// Head for the first type in the list, and Tail for the rest of the -// list. - -// The empty type list. -struct Types0 {}; - -// Type lists of length 1, 2, 3, and so on. - -template -struct Types1 { - typedef T1 Head; - typedef Types0 Tail; -}; -template -struct Types2 { - typedef T1 Head; - typedef Types1 Tail; -}; - -template -struct Types3 { - typedef T1 Head; - typedef Types2 Tail; -}; - -template -struct Types4 { - typedef T1 Head; - typedef Types3 Tail; -}; - -template -struct Types5 { - typedef T1 Head; - typedef Types4 Tail; -}; - -template -struct Types6 { - typedef T1 Head; - typedef Types5 Tail; -}; - -template -struct Types7 { - typedef T1 Head; - typedef Types6 Tail; -}; - -template -struct Types8 { - typedef T1 Head; - typedef Types7 Tail; -}; - -template -struct Types9 { - typedef T1 Head; - typedef Types8 Tail; -}; - -template -struct Types10 { - typedef T1 Head; - typedef Types9 Tail; -}; - -template -struct Types11 { - typedef T1 Head; - typedef Types10 Tail; -}; - -template -struct Types12 { - typedef T1 Head; - typedef Types11 Tail; -}; - -template -struct Types13 { - typedef T1 Head; - typedef Types12 Tail; -}; - -template -struct Types14 { - typedef T1 Head; - typedef Types13 Tail; -}; - -template -struct Types15 { - typedef T1 Head; - typedef Types14 Tail; -}; - -template -struct Types16 { - typedef T1 Head; - typedef Types15 Tail; -}; - -template -struct Types17 { - typedef T1 Head; - typedef Types16 Tail; -}; - -template -struct Types18 { - typedef T1 Head; - typedef Types17 Tail; -}; - -template -struct Types19 { - typedef T1 Head; - typedef Types18 Tail; -}; - -template -struct Types20 { - typedef T1 Head; - typedef Types19 Tail; -}; - -template -struct Types21 { - typedef T1 Head; - typedef Types20 Tail; -}; - -template -struct Types22 { - typedef T1 Head; - typedef Types21 Tail; -}; - -template -struct Types23 { - typedef T1 Head; - typedef Types22 Tail; -}; - -template -struct Types24 { - typedef T1 Head; - typedef Types23 Tail; -}; - -template -struct Types25 { - typedef T1 Head; - typedef Types24 Tail; -}; - -template -struct Types26 { - typedef T1 Head; - typedef Types25 Tail; -}; - -template -struct Types27 { - typedef T1 Head; - typedef Types26 Tail; -}; - -template -struct Types28 { - typedef T1 Head; - typedef Types27 Tail; -}; - -template -struct Types29 { - typedef T1 Head; - typedef Types28 Tail; -}; - -template -struct Types30 { - typedef T1 Head; - typedef Types29 Tail; -}; - -template -struct Types31 { - typedef T1 Head; - typedef Types30 Tail; -}; - -template -struct Types32 { - typedef T1 Head; - typedef Types31 Tail; -}; - -template -struct Types33 { - typedef T1 Head; - typedef Types32 Tail; -}; - -template -struct Types34 { - typedef T1 Head; - typedef Types33 Tail; -}; - -template -struct Types35 { - typedef T1 Head; - typedef Types34 Tail; -}; - -template -struct Types36 { - typedef T1 Head; - typedef Types35 Tail; -}; - -template -struct Types37 { - typedef T1 Head; - typedef Types36 Tail; -}; - -template -struct Types38 { - typedef T1 Head; - typedef Types37 Tail; -}; - -template -struct Types39 { - typedef T1 Head; - typedef Types38 Tail; -}; - -template -struct Types40 { - typedef T1 Head; - typedef Types39 Tail; -}; - -template -struct Types41 { - typedef T1 Head; - typedef Types40 Tail; -}; - -template -struct Types42 { - typedef T1 Head; - typedef Types41 Tail; -}; - -template -struct Types43 { - typedef T1 Head; - typedef Types42 Tail; -}; - -template -struct Types44 { - typedef T1 Head; - typedef Types43 Tail; -}; - -template -struct Types45 { - typedef T1 Head; - typedef Types44 Tail; -}; - -template -struct Types46 { - typedef T1 Head; - typedef Types45 Tail; -}; - -template -struct Types47 { - typedef T1 Head; - typedef Types46 Tail; -}; - -template -struct Types48 { - typedef T1 Head; - typedef Types47 Tail; -}; - -template -struct Types49 { - typedef T1 Head; - typedef Types48 Tail; -}; - -template -struct Types50 { - typedef T1 Head; - typedef Types49 Tail; -}; - - -} // namespace internal - -// We don't want to require the users to write TypesN<...> directly, -// as that would require them to count the length. Types<...> is much -// easier to write, but generates horrible messages when there is a -// compiler error, as gcc insists on printing out each template -// argument, even if it has the default value (this means Types -// will appear as Types in the compiler -// errors). -// -// Our solution is to combine the best part of the two approaches: a -// user would write Types, and Google Test will translate -// that to TypesN internally to make error messages -// readable. The translation is done by the 'type' member of the -// Types template. -template -struct Types { - typedef internal::Types50 type; -}; - -template <> -struct Types { - typedef internal::Types0 type; -}; -template -struct Types { - typedef internal::Types1 type; -}; -template -struct Types { - typedef internal::Types2 type; -}; -template -struct Types { - typedef internal::Types3 type; -}; -template -struct Types { - typedef internal::Types4 type; -}; -template -struct Types { - typedef internal::Types5 type; -}; -template -struct Types { - typedef internal::Types6 type; -}; -template -struct Types { - typedef internal::Types7 type; -}; -template -struct Types { - typedef internal::Types8 type; -}; -template -struct Types { - typedef internal::Types9 type; -}; -template -struct Types { - typedef internal::Types10 type; -}; -template -struct Types { - typedef internal::Types11 type; -}; -template -struct Types { - typedef internal::Types12 type; -}; -template -struct Types { - typedef internal::Types13 type; -}; -template -struct Types { - typedef internal::Types14 type; -}; -template -struct Types { - typedef internal::Types15 type; -}; -template -struct Types { - typedef internal::Types16 type; -}; -template -struct Types { - typedef internal::Types17 type; -}; -template -struct Types { - typedef internal::Types18 type; -}; -template -struct Types { - typedef internal::Types19 type; -}; -template -struct Types { - typedef internal::Types20 type; -}; -template -struct Types { - typedef internal::Types21 type; -}; -template -struct Types { - typedef internal::Types22 type; -}; -template -struct Types { - typedef internal::Types23 type; -}; -template -struct Types { - typedef internal::Types24 type; -}; -template -struct Types { - typedef internal::Types25 type; -}; -template -struct Types { - typedef internal::Types26 type; -}; -template -struct Types { - typedef internal::Types27 type; -}; -template -struct Types { - typedef internal::Types28 type; -}; -template -struct Types { - typedef internal::Types29 type; -}; -template -struct Types { - typedef internal::Types30 type; -}; -template -struct Types { - typedef internal::Types31 type; -}; -template -struct Types { - typedef internal::Types32 type; -}; -template -struct Types { - typedef internal::Types33 type; -}; -template -struct Types { - typedef internal::Types34 type; -}; -template -struct Types { - typedef internal::Types35 type; -}; -template -struct Types { - typedef internal::Types36 type; -}; -template -struct Types { - typedef internal::Types37 type; -}; -template -struct Types { - typedef internal::Types38 type; -}; -template -struct Types { - typedef internal::Types39 type; -}; -template -struct Types { - typedef internal::Types40 type; -}; -template -struct Types { - typedef internal::Types41 type; -}; -template -struct Types { - typedef internal::Types42 type; -}; -template -struct Types { - typedef internal::Types43 type; -}; -template -struct Types { - typedef internal::Types44 type; -}; -template -struct Types { - typedef internal::Types45 type; -}; -template -struct Types { - typedef internal::Types46 type; -}; -template -struct Types { - typedef internal::Types47 type; -}; -template -struct Types { - typedef internal::Types48 type; -}; -template -struct Types { - typedef internal::Types49 type; -}; - -namespace internal { - -#define GTEST_TEMPLATE_ template class - -// The template "selector" struct TemplateSel is used to -// represent Tmpl, which must be a class template with one type -// parameter, as a type. TemplateSel::Bind::type is defined -// as the type Tmpl. This allows us to actually instantiate the -// template "selected" by TemplateSel. -// -// This trick is necessary for simulating typedef for class templates, -// which C++ doesn't support directly. -template -struct TemplateSel { - template - struct Bind { - typedef Tmpl type; - }; -}; - -#define GTEST_BIND_(TmplSel, T) \ - TmplSel::template Bind::type - -// A unique struct template used as the default value for the -// arguments of class template Templates. This allows us to simulate -// variadic templates (e.g. Templates, Templates, -// and etc), which C++ doesn't support directly. -template -struct NoneT {}; - -// The following family of struct and struct templates are used to -// represent template lists. In particular, TemplatesN represents a list of N templates (T1, T2, ..., and TN). Except -// for Templates0, every struct in the family has two member types: -// Head for the selector of the first template in the list, and Tail -// for the rest of the list. - -// The empty template list. -struct Templates0 {}; - -// Template lists of length 1, 2, 3, and so on. - -template -struct Templates1 { - typedef TemplateSel Head; - typedef Templates0 Tail; -}; -template -struct Templates2 { - typedef TemplateSel Head; - typedef Templates1 Tail; -}; - -template -struct Templates3 { - typedef TemplateSel Head; - typedef Templates2 Tail; -}; - -template -struct Templates4 { - typedef TemplateSel Head; - typedef Templates3 Tail; -}; - -template -struct Templates5 { - typedef TemplateSel Head; - typedef Templates4 Tail; -}; - -template -struct Templates6 { - typedef TemplateSel Head; - typedef Templates5 Tail; -}; - -template -struct Templates7 { - typedef TemplateSel Head; - typedef Templates6 Tail; -}; - -template -struct Templates8 { - typedef TemplateSel Head; - typedef Templates7 Tail; -}; - -template -struct Templates9 { - typedef TemplateSel Head; - typedef Templates8 Tail; -}; - -template -struct Templates10 { - typedef TemplateSel Head; - typedef Templates9 Tail; -}; - -template -struct Templates11 { - typedef TemplateSel Head; - typedef Templates10 Tail; -}; - -template -struct Templates12 { - typedef TemplateSel Head; - typedef Templates11 Tail; -}; - -template -struct Templates13 { - typedef TemplateSel Head; - typedef Templates12 Tail; -}; - -template -struct Templates14 { - typedef TemplateSel Head; - typedef Templates13 Tail; -}; - -template -struct Templates15 { - typedef TemplateSel Head; - typedef Templates14 Tail; -}; - -template -struct Templates16 { - typedef TemplateSel Head; - typedef Templates15 Tail; -}; - -template -struct Templates17 { - typedef TemplateSel Head; - typedef Templates16 Tail; -}; - -template -struct Templates18 { - typedef TemplateSel Head; - typedef Templates17 Tail; -}; - -template -struct Templates19 { - typedef TemplateSel Head; - typedef Templates18 Tail; -}; - -template -struct Templates20 { - typedef TemplateSel Head; - typedef Templates19 Tail; -}; - -template -struct Templates21 { - typedef TemplateSel Head; - typedef Templates20 Tail; -}; - -template -struct Templates22 { - typedef TemplateSel Head; - typedef Templates21 Tail; -}; - -template -struct Templates23 { - typedef TemplateSel Head; - typedef Templates22 Tail; -}; - -template -struct Templates24 { - typedef TemplateSel Head; - typedef Templates23 Tail; -}; - -template -struct Templates25 { - typedef TemplateSel Head; - typedef Templates24 Tail; -}; - -template -struct Templates26 { - typedef TemplateSel Head; - typedef Templates25 Tail; -}; - -template -struct Templates27 { - typedef TemplateSel Head; - typedef Templates26 Tail; -}; - -template -struct Templates28 { - typedef TemplateSel Head; - typedef Templates27 Tail; -}; - -template -struct Templates29 { - typedef TemplateSel Head; - typedef Templates28 Tail; -}; - -template -struct Templates30 { - typedef TemplateSel Head; - typedef Templates29 Tail; -}; - -template -struct Templates31 { - typedef TemplateSel Head; - typedef Templates30 Tail; -}; - -template -struct Templates32 { - typedef TemplateSel Head; - typedef Templates31 Tail; -}; - -template -struct Templates33 { - typedef TemplateSel Head; - typedef Templates32 Tail; -}; - -template -struct Templates34 { - typedef TemplateSel Head; - typedef Templates33 Tail; -}; - -template -struct Templates35 { - typedef TemplateSel Head; - typedef Templates34 Tail; -}; - -template -struct Templates36 { - typedef TemplateSel Head; - typedef Templates35 Tail; -}; - -template -struct Templates37 { - typedef TemplateSel Head; - typedef Templates36 Tail; -}; - -template -struct Templates38 { - typedef TemplateSel Head; - typedef Templates37 Tail; -}; - -template -struct Templates39 { - typedef TemplateSel Head; - typedef Templates38 Tail; -}; - -template -struct Templates40 { - typedef TemplateSel Head; - typedef Templates39 Tail; -}; - -template -struct Templates41 { - typedef TemplateSel Head; - typedef Templates40 Tail; -}; - -template -struct Templates42 { - typedef TemplateSel Head; - typedef Templates41 Tail; -}; - -template -struct Templates43 { - typedef TemplateSel Head; - typedef Templates42 Tail; -}; - -template -struct Templates44 { - typedef TemplateSel Head; - typedef Templates43 Tail; -}; - -template -struct Templates45 { - typedef TemplateSel Head; - typedef Templates44 Tail; -}; - -template -struct Templates46 { - typedef TemplateSel Head; - typedef Templates45 Tail; -}; - -template -struct Templates47 { - typedef TemplateSel Head; - typedef Templates46 Tail; -}; - -template -struct Templates48 { - typedef TemplateSel Head; - typedef Templates47 Tail; -}; - -template -struct Templates49 { - typedef TemplateSel Head; - typedef Templates48 Tail; -}; - -template -struct Templates50 { - typedef TemplateSel Head; - typedef Templates49 Tail; -}; - - -// We don't want to require the users to write TemplatesN<...> directly, -// as that would require them to count the length. Templates<...> is much -// easier to write, but generates horrible messages when there is a -// compiler error, as gcc insists on printing out each template -// argument, even if it has the default value (this means Templates -// will appear as Templates in the compiler -// errors). -// -// Our solution is to combine the best part of the two approaches: a -// user would write Templates, and Google Test will translate -// that to TemplatesN internally to make error messages -// readable. The translation is done by the 'type' member of the -// Templates template. -template -struct Templates { - typedef Templates50 type; -}; - -template <> -struct Templates { - typedef Templates0 type; -}; -template -struct Templates { - typedef Templates1 type; -}; -template -struct Templates { - typedef Templates2 type; -}; -template -struct Templates { - typedef Templates3 type; -}; -template -struct Templates { - typedef Templates4 type; -}; -template -struct Templates { - typedef Templates5 type; -}; -template -struct Templates { - typedef Templates6 type; -}; -template -struct Templates { - typedef Templates7 type; -}; -template -struct Templates { - typedef Templates8 type; -}; -template -struct Templates { - typedef Templates9 type; -}; -template -struct Templates { - typedef Templates10 type; -}; -template -struct Templates { - typedef Templates11 type; -}; -template -struct Templates { - typedef Templates12 type; -}; -template -struct Templates { - typedef Templates13 type; -}; -template -struct Templates { - typedef Templates14 type; -}; -template -struct Templates { - typedef Templates15 type; -}; -template -struct Templates { - typedef Templates16 type; -}; -template -struct Templates { - typedef Templates17 type; -}; -template -struct Templates { - typedef Templates18 type; -}; -template -struct Templates { - typedef Templates19 type; -}; -template -struct Templates { - typedef Templates20 type; -}; -template -struct Templates { - typedef Templates21 type; -}; -template -struct Templates { - typedef Templates22 type; -}; -template -struct Templates { - typedef Templates23 type; -}; -template -struct Templates { - typedef Templates24 type; -}; -template -struct Templates { - typedef Templates25 type; -}; -template -struct Templates { - typedef Templates26 type; -}; -template -struct Templates { - typedef Templates27 type; -}; -template -struct Templates { - typedef Templates28 type; -}; -template -struct Templates { - typedef Templates29 type; -}; -template -struct Templates { - typedef Templates30 type; -}; -template -struct Templates { - typedef Templates31 type; -}; -template -struct Templates { - typedef Templates32 type; -}; -template -struct Templates { - typedef Templates33 type; -}; -template -struct Templates { - typedef Templates34 type; -}; -template -struct Templates { - typedef Templates35 type; -}; -template -struct Templates { - typedef Templates36 type; -}; -template -struct Templates { - typedef Templates37 type; -}; -template -struct Templates { - typedef Templates38 type; -}; -template -struct Templates { - typedef Templates39 type; -}; -template -struct Templates { - typedef Templates40 type; -}; -template -struct Templates { - typedef Templates41 type; -}; -template -struct Templates { - typedef Templates42 type; -}; -template -struct Templates { - typedef Templates43 type; -}; -template -struct Templates { - typedef Templates44 type; -}; -template -struct Templates { - typedef Templates45 type; -}; -template -struct Templates { - typedef Templates46 type; -}; -template -struct Templates { - typedef Templates47 type; -}; -template -struct Templates { - typedef Templates48 type; -}; -template -struct Templates { - typedef Templates49 type; -}; - -// The TypeList template makes it possible to use either a single type -// or a Types<...> list in TYPED_TEST_CASE() and -// INSTANTIATE_TYPED_TEST_CASE_P(). - -template -struct TypeList { typedef Types1 type; }; - -template -struct TypeList > { - typedef typename Types::type type; -}; - -} // namespace internal -} // namespace testing - -#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ - -// Due to C++ preprocessor weirdness, we need double indirection to -// concatenate two tokens when one of them is __LINE__. Writing -// -// foo ## __LINE__ -// -// will result in the token foo__LINE__, instead of foo followed by -// the current line number. For more details, see -// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 -#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) -#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar - -// Google Test defines the testing::Message class to allow construction of -// test messages via the << operator. The idea is that anything -// streamable to std::ostream can be streamed to a testing::Message. -// This allows a user to use his own types in Google Test assertions by -// overloading the << operator. -// -// util/gtl/stl_logging-inl.h overloads << for STL containers. These -// overloads cannot be defined in the std namespace, as that will be -// undefined behavior. Therefore, they are defined in the global -// namespace instead. -// -// C++'s symbol lookup rule (i.e. Koenig lookup) says that these -// overloads are visible in either the std namespace or the global -// namespace, but not other namespaces, including the testing -// namespace which Google Test's Message class is in. -// -// To allow STL containers (and other types that has a << operator -// defined in the global namespace) to be used in Google Test assertions, -// testing::Message must access the custom << operator from the global -// namespace. Hence this helper function. -// -// Note: Jeffrey Yasskin suggested an alternative fix by "using -// ::operator<<;" in the definition of Message's operator<<. That fix -// doesn't require a helper function, but unfortunately doesn't -// compile with MSVC. -template -inline void GTestStreamToHelper(std::ostream* os, const T& val) { - *os << val; -} - -namespace testing { - -// Forward declaration of classes. - -class AssertionResult; // Result of an assertion. -class Message; // Represents a failure message. -class Test; // Represents a test. -class TestInfo; // Information about a test. -class TestPartResult; // Result of a test part. -class UnitTest; // A collection of test cases. - -namespace internal { - -struct TraceInfo; // Information about a trace point. -class ScopedTrace; // Implements scoped trace. -class TestInfoImpl; // Opaque implementation of TestInfo -class UnitTestImpl; // Opaque implementation of UnitTest - -// How many times InitGoogleTest() has been called. -extern int g_init_gtest_count; - -// The text used in failure messages to indicate the start of the -// stack trace. -GTEST_API_ extern const char kStackTraceMarker[]; - -// A secret type that Google Test users don't know about. It has no -// definition on purpose. Therefore it's impossible to create a -// Secret object, which is what we want. -class Secret; - -// Two overloaded helpers for checking at compile time whether an -// expression is a null pointer literal (i.e. NULL or any 0-valued -// compile-time integral constant). Their return values have -// different sizes, so we can use sizeof() to test which version is -// picked by the compiler. These helpers have no implementations, as -// we only need their signatures. -// -// Given IsNullLiteralHelper(x), the compiler will pick the first -// version if x can be implicitly converted to Secret*, and pick the -// second version otherwise. Since Secret is a secret and incomplete -// type, the only expression a user can write that has type Secret* is -// a null pointer literal. Therefore, we know that x is a null -// pointer literal if and only if the first version is picked by the -// compiler. -char IsNullLiteralHelper(Secret* p); -char (&IsNullLiteralHelper(...))[2]; // NOLINT - -// A compile-time bool constant that is true if and only if x is a -// null pointer literal (i.e. NULL or any 0-valued compile-time -// integral constant). -#ifdef GTEST_ELLIPSIS_NEEDS_POD_ -// We lose support for NULL detection where the compiler doesn't like -// passing non-POD classes through ellipsis (...). -#define GTEST_IS_NULL_LITERAL_(x) false -#else -#define GTEST_IS_NULL_LITERAL_(x) \ - (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1) -#endif // GTEST_ELLIPSIS_NEEDS_POD_ - -// Appends the user-supplied message to the Google-Test-generated message. -GTEST_API_ String AppendUserMessage(const String& gtest_msg, - const Message& user_msg); - -// A helper class for creating scoped traces in user programs. -class GTEST_API_ ScopedTrace { - public: - // The c'tor pushes the given source file location and message onto - // a trace stack maintained by Google Test. - ScopedTrace(const char* file, int line, const Message& message); - - // The d'tor pops the info pushed by the c'tor. - // - // Note that the d'tor is not virtual in order to be efficient. - // Don't inherit from ScopedTrace! - ~ScopedTrace(); - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace); -} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its - // c'tor and d'tor. Therefore it doesn't - // need to be used otherwise. - -// Converts a streamable value to a String. A NULL pointer is -// converted to "(null)". When the input value is a ::string, -// ::std::string, ::wstring, or ::std::wstring object, each NUL -// character in it is replaced with "\\0". -// Declared here but defined in gtest.h, so that it has access -// to the definition of the Message class, required by the ARM -// compiler. -template -String StreamableToString(const T& streamable); - -// Formats a value to be used in a failure message. - -#ifdef GTEST_NEEDS_IS_POINTER_ - -// These are needed as the Nokia Symbian and IBM XL C/C++ compilers -// cannot decide between const T& and const T* in a function template. -// These compilers _can_ decide between class template specializations -// for T and T*, so a tr1::type_traits-like is_pointer works, and we -// can overload on that. - -// This overload makes sure that all pointers (including -// those to char or wchar_t) are printed as raw pointers. -template -inline String FormatValueForFailureMessage(internal::true_type /*dummy*/, - T* pointer) { - return StreamableToString(static_cast(pointer)); -} - -template -inline String FormatValueForFailureMessage(internal::false_type /*dummy*/, - const T& value) { - return StreamableToString(value); -} - -template -inline String FormatForFailureMessage(const T& value) { - return FormatValueForFailureMessage( - typename internal::is_pointer::type(), value); -} - -#else - -// These are needed as the above solution using is_pointer has the -// limitation that T cannot be a type without external linkage, when -// compiled using MSVC. - -template -inline String FormatForFailureMessage(const T& value) { - return StreamableToString(value); -} - -// This overload makes sure that all pointers (including -// those to char or wchar_t) are printed as raw pointers. -template -inline String FormatForFailureMessage(T* pointer) { - return StreamableToString(static_cast(pointer)); -} - -#endif // GTEST_NEEDS_IS_POINTER_ - -// These overloaded versions handle narrow and wide characters. -GTEST_API_ String FormatForFailureMessage(char ch); -GTEST_API_ String FormatForFailureMessage(wchar_t wchar); - -// When this operand is a const char* or char*, and the other operand -// is a ::std::string or ::string, we print this operand as a C string -// rather than a pointer. We do the same for wide strings. - -// This internal macro is used to avoid duplicated code. -#define GTEST_FORMAT_IMPL_(operand2_type, operand1_printer)\ -inline String FormatForComparisonFailureMessage(\ - operand2_type::value_type* str, const operand2_type& /*operand2*/) {\ - return operand1_printer(str);\ -}\ -inline String FormatForComparisonFailureMessage(\ - const operand2_type::value_type* str, const operand2_type& /*operand2*/) {\ - return operand1_printer(str);\ -} - -GTEST_FORMAT_IMPL_(::std::string, String::ShowCStringQuoted) -#if GTEST_HAS_STD_WSTRING -GTEST_FORMAT_IMPL_(::std::wstring, String::ShowWideCStringQuoted) -#endif // GTEST_HAS_STD_WSTRING - -#if GTEST_HAS_GLOBAL_STRING -GTEST_FORMAT_IMPL_(::string, String::ShowCStringQuoted) -#endif // GTEST_HAS_GLOBAL_STRING -#if GTEST_HAS_GLOBAL_WSTRING -GTEST_FORMAT_IMPL_(::wstring, String::ShowWideCStringQuoted) -#endif // GTEST_HAS_GLOBAL_WSTRING - -#undef GTEST_FORMAT_IMPL_ - -// Constructs and returns the message for an equality assertion -// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. -// -// The first four parameters are the expressions used in the assertion -// and their values, as strings. For example, for ASSERT_EQ(foo, bar) -// where foo is 5 and bar is 6, we have: -// -// expected_expression: "foo" -// actual_expression: "bar" -// expected_value: "5" -// actual_value: "6" -// -// The ignoring_case parameter is true iff the assertion is a -// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will -// be inserted into the message. -GTEST_API_ AssertionResult EqFailure(const char* expected_expression, - const char* actual_expression, - const String& expected_value, - const String& actual_value, - bool ignoring_case); - -// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. -GTEST_API_ String GetBoolAssertionFailureMessage( - const AssertionResult& assertion_result, - const char* expression_text, - const char* actual_predicate_value, - const char* expected_predicate_value); - -// This template class represents an IEEE floating-point number -// (either single-precision or double-precision, depending on the -// template parameters). -// -// The purpose of this class is to do more sophisticated number -// comparison. (Due to round-off error, etc, it's very unlikely that -// two floating-points will be equal exactly. Hence a naive -// comparison by the == operation often doesn't work.) -// -// Format of IEEE floating-point: -// -// The most-significant bit being the leftmost, an IEEE -// floating-point looks like -// -// sign_bit exponent_bits fraction_bits -// -// Here, sign_bit is a single bit that designates the sign of the -// number. -// -// For float, there are 8 exponent bits and 23 fraction bits. -// -// For double, there are 11 exponent bits and 52 fraction bits. -// -// More details can be found at -// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. -// -// Template parameter: -// -// RawType: the raw floating-point type (either float or double) -template -class FloatingPoint { - public: - // Defines the unsigned integer type that has the same size as the - // floating point number. - typedef typename TypeWithSize::UInt Bits; - - // Constants. - - // # of bits in a number. - static const size_t kBitCount = 8*sizeof(RawType); - - // # of fraction bits in a number. - static const size_t kFractionBitCount = - std::numeric_limits::digits - 1; - - // # of exponent bits in a number. - static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; - - // The mask for the sign bit. - static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); - - // The mask for the fraction bits. - static const Bits kFractionBitMask = - ~static_cast(0) >> (kExponentBitCount + 1); - - // The mask for the exponent bits. - static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); - - // How many ULP's (Units in the Last Place) we want to tolerate when - // comparing two numbers. The larger the value, the more error we - // allow. A 0 value means that two numbers must be exactly the same - // to be considered equal. - // - // The maximum error of a single floating-point operation is 0.5 - // units in the last place. On Intel CPU's, all floating-point - // calculations are done with 80-bit precision, while double has 64 - // bits. Therefore, 4 should be enough for ordinary use. - // - // See the following article for more details on ULP: - // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm. - static const size_t kMaxUlps = 4; - - // Constructs a FloatingPoint from a raw floating-point number. - // - // On an Intel CPU, passing a non-normalized NAN (Not a Number) - // around may change its bits, although the new value is guaranteed - // to be also a NAN. Therefore, don't expect this constructor to - // preserve the bits in x when x is a NAN. - explicit FloatingPoint(const RawType& x) { u_.value_ = x; } - - // Static methods - - // Reinterprets a bit pattern as a floating-point number. - // - // This function is needed to test the AlmostEquals() method. - static RawType ReinterpretBits(const Bits bits) { - FloatingPoint fp(0); - fp.u_.bits_ = bits; - return fp.u_.value_; - } - - // Returns the floating-point number that represent positive infinity. - static RawType Infinity() { - return ReinterpretBits(kExponentBitMask); - } - - // Non-static methods - - // Returns the bits that represents this number. - const Bits &bits() const { return u_.bits_; } - - // Returns the exponent bits of this number. - Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } - - // Returns the fraction bits of this number. - Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } - - // Returns the sign bit of this number. - Bits sign_bit() const { return kSignBitMask & u_.bits_; } - - // Returns true iff this is NAN (not a number). - bool is_nan() const { - // It's a NAN if the exponent bits are all ones and the fraction - // bits are not entirely zeros. - return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); - } - - // Returns true iff this number is at most kMaxUlps ULP's away from - // rhs. In particular, this function: - // - // - returns false if either number is (or both are) NAN. - // - treats really large numbers as almost equal to infinity. - // - thinks +0.0 and -0.0 are 0 DLP's apart. - bool AlmostEquals(const FloatingPoint& rhs) const { - // The IEEE standard says that any comparison operation involving - // a NAN must return false. - if (is_nan() || rhs.is_nan()) return false; - - return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) - <= kMaxUlps; - } - - private: - // The data type used to store the actual floating-point number. - union FloatingPointUnion { - RawType value_; // The raw floating-point number. - Bits bits_; // The bits that represent the number. - }; - - // Converts an integer from the sign-and-magnitude representation to - // the biased representation. More precisely, let N be 2 to the - // power of (kBitCount - 1), an integer x is represented by the - // unsigned number x + N. - // - // For instance, - // - // -N + 1 (the most negative number representable using - // sign-and-magnitude) is represented by 1; - // 0 is represented by N; and - // N - 1 (the biggest number representable using - // sign-and-magnitude) is represented by 2N - 1. - // - // Read http://en.wikipedia.org/wiki/Signed_number_representations - // for more details on signed number representations. - static Bits SignAndMagnitudeToBiased(const Bits &sam) { - if (kSignBitMask & sam) { - // sam represents a negative number. - return ~sam + 1; - } else { - // sam represents a positive number. - return kSignBitMask | sam; - } - } - - // Given two numbers in the sign-and-magnitude representation, - // returns the distance between them as an unsigned number. - static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, - const Bits &sam2) { - const Bits biased1 = SignAndMagnitudeToBiased(sam1); - const Bits biased2 = SignAndMagnitudeToBiased(sam2); - return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); - } - - FloatingPointUnion u_; -}; - -// Typedefs the instances of the FloatingPoint template class that we -// care to use. -typedef FloatingPoint Float; -typedef FloatingPoint Double; - -// In order to catch the mistake of putting tests that use different -// test fixture classes in the same test case, we need to assign -// unique IDs to fixture classes and compare them. The TypeId type is -// used to hold such IDs. The user should treat TypeId as an opaque -// type: the only operation allowed on TypeId values is to compare -// them for equality using the == operator. -typedef const void* TypeId; - -template -class TypeIdHelper { - public: - // dummy_ must not have a const type. Otherwise an overly eager - // compiler (e.g. MSVC 7.1 & 8.0) may try to merge - // TypeIdHelper::dummy_ for different Ts as an "optimization". - static bool dummy_; -}; - -template -bool TypeIdHelper::dummy_ = false; - -// GetTypeId() returns the ID of type T. Different values will be -// returned for different types. Calling the function twice with the -// same type argument is guaranteed to return the same ID. -template -TypeId GetTypeId() { - // The compiler is required to allocate a different - // TypeIdHelper::dummy_ variable for each T used to instantiate - // the template. Therefore, the address of dummy_ is guaranteed to - // be unique. - return &(TypeIdHelper::dummy_); -} - -// Returns the type ID of ::testing::Test. Always call this instead -// of GetTypeId< ::testing::Test>() to get the type ID of -// ::testing::Test, as the latter may give the wrong result due to a -// suspected linker bug when compiling Google Test as a Mac OS X -// framework. -GTEST_API_ TypeId GetTestTypeId(); - -// Defines the abstract factory interface that creates instances -// of a Test object. -class TestFactoryBase { - public: - virtual ~TestFactoryBase() {} - - // Creates a test instance to run. The instance is both created and destroyed - // within TestInfoImpl::Run() - virtual Test* CreateTest() = 0; - - protected: - TestFactoryBase() {} - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase); -}; - -// This class provides implementation of TeastFactoryBase interface. -// It is used in TEST and TEST_F macros. -template -class TestFactoryImpl : public TestFactoryBase { - public: - virtual Test* CreateTest() { return new TestClass; } -}; - -#if GTEST_OS_WINDOWS - -// Predicate-formatters for implementing the HRESULT checking macros -// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} -// We pass a long instead of HRESULT to avoid causing an -// include dependency for the HRESULT type. -GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr, - long hr); // NOLINT -GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, - long hr); // NOLINT - -#endif // GTEST_OS_WINDOWS - -// Formats a source file path and a line number as they would appear -// in a compiler error message. -inline String FormatFileLocation(const char* file, int line) { - const char* const file_name = file == NULL ? "unknown file" : file; - if (line < 0) { - return String::Format("%s:", file_name); - } -#ifdef _MSC_VER - return String::Format("%s(%d):", file_name, line); -#else - return String::Format("%s:%d:", file_name, line); -#endif // _MSC_VER -} - -// Types of SetUpTestCase() and TearDownTestCase() functions. -typedef void (*SetUpTestCaseFunc)(); -typedef void (*TearDownTestCaseFunc)(); - -// Creates a new TestInfo object and registers it with Google Test; -// returns the created object. -// -// Arguments: -// -// test_case_name: name of the test case -// name: name of the test -// test_case_comment: a comment on the test case that will be included in -// the test output -// comment: a comment on the test that will be included in the -// test output -// fixture_class_id: ID of the test fixture class -// set_up_tc: pointer to the function that sets up the test case -// tear_down_tc: pointer to the function that tears down the test case -// factory: pointer to the factory that creates a test object. -// The newly created TestInfo instance will assume -// ownership of the factory object. -GTEST_API_ TestInfo* MakeAndRegisterTestInfo( - const char* test_case_name, const char* name, - const char* test_case_comment, const char* comment, - TypeId fixture_class_id, - SetUpTestCaseFunc set_up_tc, - TearDownTestCaseFunc tear_down_tc, - TestFactoryBase* factory); - -// If *pstr starts with the given prefix, modifies *pstr to be right -// past the prefix and returns true; otherwise leaves *pstr unchanged -// and returns false. None of pstr, *pstr, and prefix can be NULL. -bool SkipPrefix(const char* prefix, const char** pstr); - -#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P - -// State of the definition of a type-parameterized test case. -class GTEST_API_ TypedTestCasePState { - public: - TypedTestCasePState() : registered_(false) {} - - // Adds the given test name to defined_test_names_ and return true - // if the test case hasn't been registered; otherwise aborts the - // program. - bool AddTestName(const char* file, int line, const char* case_name, - const char* test_name) { - if (registered_) { - fprintf(stderr, "%s Test %s must be defined before " - "REGISTER_TYPED_TEST_CASE_P(%s, ...).\n", - FormatFileLocation(file, line).c_str(), test_name, case_name); - fflush(stderr); - posix::Abort(); - } - defined_test_names_.insert(test_name); - return true; - } - - // Verifies that registered_tests match the test names in - // defined_test_names_; returns registered_tests if successful, or - // aborts the program otherwise. - const char* VerifyRegisteredTestNames( - const char* file, int line, const char* registered_tests); - - private: - bool registered_; - ::std::set defined_test_names_; -}; - -// Skips to the first non-space char after the first comma in 'str'; -// returns NULL if no comma is found in 'str'. -inline const char* SkipComma(const char* str) { - const char* comma = strchr(str, ','); - if (comma == NULL) { - return NULL; - } - while (isspace(*(++comma))) {} - return comma; -} - -// Returns the prefix of 'str' before the first comma in it; returns -// the entire string if it contains no comma. -inline String GetPrefixUntilComma(const char* str) { - const char* comma = strchr(str, ','); - return comma == NULL ? String(str) : String(str, comma - str); -} - -// TypeParameterizedTest::Register() -// registers a list of type-parameterized tests with Google Test. The -// return value is insignificant - we just need to return something -// such that we can call this function in a namespace scope. -// -// Implementation note: The GTEST_TEMPLATE_ macro declares a template -// template parameter. It's defined in gtest-type-util.h. -template -class TypeParameterizedTest { - public: - // 'index' is the index of the test in the type list 'Types' - // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase, - // Types). Valid values for 'index' are [0, N - 1] where N is the - // length of Types. - static bool Register(const char* prefix, const char* case_name, - const char* test_names, int index) { - typedef typename Types::Head Type; - typedef Fixture FixtureClass; - typedef typename GTEST_BIND_(TestSel, Type) TestClass; - - // First, registers the first type-parameterized test in the type - // list. - MakeAndRegisterTestInfo( - String::Format("%s%s%s/%d", prefix, prefix[0] == '\0' ? "" : "/", - case_name, index).c_str(), - GetPrefixUntilComma(test_names).c_str(), - String::Format("TypeParam = %s", GetTypeName().c_str()).c_str(), - "", - GetTypeId(), - TestClass::SetUpTestCase, - TestClass::TearDownTestCase, - new TestFactoryImpl); - - // Next, recurses (at compile time) with the tail of the type list. - return TypeParameterizedTest - ::Register(prefix, case_name, test_names, index + 1); - } -}; - -// The base case for the compile time recursion. -template -class TypeParameterizedTest { - public: - static bool Register(const char* /*prefix*/, const char* /*case_name*/, - const char* /*test_names*/, int /*index*/) { - return true; - } -}; - -// TypeParameterizedTestCase::Register() -// registers *all combinations* of 'Tests' and 'Types' with Google -// Test. The return value is insignificant - we just need to return -// something such that we can call this function in a namespace scope. -template -class TypeParameterizedTestCase { - public: - static bool Register(const char* prefix, const char* case_name, - const char* test_names) { - typedef typename Tests::Head Head; - - // First, register the first test in 'Test' for each type in 'Types'. - TypeParameterizedTest::Register( - prefix, case_name, test_names, 0); - - // Next, recurses (at compile time) with the tail of the test list. - return TypeParameterizedTestCase - ::Register(prefix, case_name, SkipComma(test_names)); - } -}; - -// The base case for the compile time recursion. -template -class TypeParameterizedTestCase { - public: - static bool Register(const char* /*prefix*/, const char* /*case_name*/, - const char* /*test_names*/) { - return true; - } -}; - -#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P - -// Returns the current OS stack trace as a String. -// -// The maximum number of stack frames to be included is specified by -// the gtest_stack_trace_depth flag. The skip_count parameter -// specifies the number of top frames to be skipped, which doesn't -// count against the number of frames to be included. -// -// For example, if Foo() calls Bar(), which in turn calls -// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in -// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. -GTEST_API_ String GetCurrentOsStackTraceExceptTop(UnitTest* unit_test, - int skip_count); - -// Helpers for suppressing warnings on unreachable code or constant -// condition. - -// Always returns true. -GTEST_API_ bool AlwaysTrue(); - -// Always returns false. -inline bool AlwaysFalse() { return !AlwaysTrue(); } - -// A simple Linear Congruential Generator for generating random -// numbers with a uniform distribution. Unlike rand() and srand(), it -// doesn't use global state (and therefore can't interfere with user -// code). Unlike rand_r(), it's portable. An LCG isn't very random, -// but it's good enough for our purposes. -class GTEST_API_ Random { - public: - static const UInt32 kMaxRange = 1u << 31; - - explicit Random(UInt32 seed) : state_(seed) {} - - void Reseed(UInt32 seed) { state_ = seed; } - - // Generates a random number from [0, range). Crashes if 'range' is - // 0 or greater than kMaxRange. - UInt32 Generate(UInt32 range); - - private: - UInt32 state_; - GTEST_DISALLOW_COPY_AND_ASSIGN_(Random); -}; - -} // namespace internal -} // namespace testing - -#define GTEST_MESSAGE_(message, result_type) \ - ::testing::internal::AssertHelper(result_type, __FILE__, __LINE__, message) \ - = ::testing::Message() - -#define GTEST_FATAL_FAILURE_(message) \ - return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) - -#define GTEST_NONFATAL_FAILURE_(message) \ - GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) - -#define GTEST_SUCCESS_(message) \ - GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) - -// Suppresses MSVC warnings 4072 (unreachable code) for the code following -// statement if it returns or throws (or doesn't return or throw in some -// situations). -#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ - if (::testing::internal::AlwaysTrue()) { statement; } - -#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (const char* gtest_msg = "") { \ - bool gtest_caught_expected = false; \ - try { \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - } \ - catch (expected_exception const&) { \ - gtest_caught_expected = true; \ - } \ - catch (...) { \ - gtest_msg = "Expected: " #statement " throws an exception of type " \ - #expected_exception ".\n Actual: it throws a different " \ - "type."; \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ - } \ - if (!gtest_caught_expected) { \ - gtest_msg = "Expected: " #statement " throws an exception of type " \ - #expected_exception ".\n Actual: it throws nothing."; \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \ - fail(gtest_msg) - -#define GTEST_TEST_NO_THROW_(statement, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (const char* gtest_msg = "") { \ - try { \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - } \ - catch (...) { \ - gtest_msg = "Expected: " #statement " doesn't throw an exception.\n" \ - " Actual: it throws."; \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \ - fail(gtest_msg) - -#define GTEST_TEST_ANY_THROW_(statement, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (const char* gtest_msg = "") { \ - bool gtest_caught_any = false; \ - try { \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - } \ - catch (...) { \ - gtest_caught_any = true; \ - } \ - if (!gtest_caught_any) { \ - gtest_msg = "Expected: " #statement " throws an exception.\n" \ - " Actual: it doesn't."; \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \ - fail(gtest_msg) - - -// Implements Boolean test assertions such as EXPECT_TRUE. expression can be -// either a boolean expression or an AssertionResult. text is a textual -// represenation of expression as it was passed into the EXPECT_TRUE. -#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (const ::testing::AssertionResult gtest_ar_ = \ - ::testing::AssertionResult(expression)) \ - ; \ - else \ - fail(::testing::internal::GetBoolAssertionFailureMessage(\ - gtest_ar_, text, #actual, #expected).c_str()) - -#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (const char* gtest_msg = "") { \ - ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ - gtest_msg = "Expected: " #statement " doesn't generate new fatal " \ - "failures in the current thread.\n" \ - " Actual: it does."; \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \ - fail(gtest_msg) - -// Expands to the name of the class that implements the given test. -#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ - test_case_name##_##test_name##_Test - -// Helper macro for defining tests. -#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\ -class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ - public:\ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\ - private:\ - virtual void TestBody();\ - static ::testing::TestInfo* const test_info_;\ - GTEST_DISALLOW_COPY_AND_ASSIGN_(\ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\ -};\ -\ -::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\ - ::test_info_ =\ - ::testing::internal::MakeAndRegisterTestInfo(\ - #test_case_name, #test_name, "", "", \ - (parent_id), \ - parent_class::SetUpTestCase, \ - parent_class::TearDownTestCase, \ - new ::testing::internal::TestFactoryImpl<\ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\ -void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ -// Copyright 2005, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: wan@google.com (Zhanyong Wan) -// -// The Google C++ Testing Framework (Google Test) -// -// This header file defines the public API for death tests. It is -// #included by gtest.h so a user doesn't need to include this -// directly. - -#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ -#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ - -// Copyright 2005, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) -// -// The Google C++ Testing Framework (Google Test) -// -// This header file defines internal utilities needed for implementing -// death tests. They are subject to change without notice. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ - - -namespace testing { -namespace internal { - -GTEST_DECLARE_string_(internal_run_death_test); - -// Names of the flags (needed for parsing Google Test flags). -const char kDeathTestStyleFlag[] = "death_test_style"; -const char kDeathTestUseFork[] = "death_test_use_fork"; -const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; - -#if GTEST_HAS_DEATH_TEST - -// DeathTest is a class that hides much of the complexity of the -// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method -// returns a concrete class that depends on the prevailing death test -// style, as defined by the --gtest_death_test_style and/or -// --gtest_internal_run_death_test flags. - -// In describing the results of death tests, these terms are used with -// the corresponding definitions: -// -// exit status: The integer exit information in the format specified -// by wait(2) -// exit code: The integer code passed to exit(3), _exit(2), or -// returned from main() -class GTEST_API_ DeathTest { - public: - // Create returns false if there was an error determining the - // appropriate action to take for the current death test; for example, - // if the gtest_death_test_style flag is set to an invalid value. - // The LastMessage method will return a more detailed message in that - // case. Otherwise, the DeathTest pointer pointed to by the "test" - // argument is set. If the death test should be skipped, the pointer - // is set to NULL; otherwise, it is set to the address of a new concrete - // DeathTest object that controls the execution of the current test. - static bool Create(const char* statement, const RE* regex, - const char* file, int line, DeathTest** test); - DeathTest(); - virtual ~DeathTest() { } - - // A helper class that aborts a death test when it's deleted. - class ReturnSentinel { - public: - explicit ReturnSentinel(DeathTest* test) : test_(test) { } - ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } - private: - DeathTest* const test_; - GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel); - } GTEST_ATTRIBUTE_UNUSED_; - - // An enumeration of possible roles that may be taken when a death - // test is encountered. EXECUTE means that the death test logic should - // be executed immediately. OVERSEE means that the program should prepare - // the appropriate environment for a child process to execute the death - // test, then wait for it to complete. - enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; - - // An enumeration of the two reasons that a test might be aborted. - enum AbortReason { TEST_ENCOUNTERED_RETURN_STATEMENT, TEST_DID_NOT_DIE }; - - // Assumes one of the above roles. - virtual TestRole AssumeRole() = 0; - - // Waits for the death test to finish and returns its status. - virtual int Wait() = 0; - - // Returns true if the death test passed; that is, the test process - // exited during the test, its exit status matches a user-supplied - // predicate, and its stderr output matches a user-supplied regular - // expression. - // The user-supplied predicate may be a macro expression rather - // than a function pointer or functor, or else Wait and Passed could - // be combined. - virtual bool Passed(bool exit_status_ok) = 0; - - // Signals that the death test did not die as expected. - virtual void Abort(AbortReason reason) = 0; - - // Returns a human-readable outcome message regarding the outcome of - // the last death test. - static const char* LastMessage(); - - static void set_last_death_test_message(const String& message); - - private: - // A string containing a description of the outcome of the last death test. - static String last_death_test_message_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); -}; - -// Factory interface for death tests. May be mocked out for testing. -class DeathTestFactory { - public: - virtual ~DeathTestFactory() { } - virtual bool Create(const char* statement, const RE* regex, - const char* file, int line, DeathTest** test) = 0; -}; - -// A concrete DeathTestFactory implementation for normal use. -class DefaultDeathTestFactory : public DeathTestFactory { - public: - virtual bool Create(const char* statement, const RE* regex, - const char* file, int line, DeathTest** test); -}; - -// Returns true if exit_status describes a process that was terminated -// by a signal, or exited normally with a nonzero exit code. -GTEST_API_ bool ExitedUnsuccessfully(int exit_status); - -// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, -// ASSERT_EXIT*, and EXPECT_EXIT*. -#define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::testing::internal::AlwaysTrue()) { \ - const ::testing::internal::RE& gtest_regex = (regex); \ - ::testing::internal::DeathTest* gtest_dt; \ - if (!::testing::internal::DeathTest::Create(#statement, >est_regex, \ - __FILE__, __LINE__, >est_dt)) { \ - goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ - } \ - if (gtest_dt != NULL) { \ - ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \ - gtest_dt_ptr(gtest_dt); \ - switch (gtest_dt->AssumeRole()) { \ - case ::testing::internal::DeathTest::OVERSEE_TEST: \ - if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ - goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ - } \ - break; \ - case ::testing::internal::DeathTest::EXECUTE_TEST: { \ - ::testing::internal::DeathTest::ReturnSentinel \ - gtest_sentinel(gtest_dt); \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ - break; \ - } \ - } \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \ - fail(::testing::internal::DeathTest::LastMessage()) -// The symbol "fail" here expands to something into which a message -// can be streamed. - -// A class representing the parsed contents of the -// --gtest_internal_run_death_test flag, as it existed when -// RUN_ALL_TESTS was called. -class InternalRunDeathTestFlag { - public: - InternalRunDeathTestFlag(const String& a_file, - int a_line, - int an_index, - int a_write_fd) - : file_(a_file), line_(a_line), index_(an_index), - write_fd_(a_write_fd) {} - - ~InternalRunDeathTestFlag() { - if (write_fd_ >= 0) - posix::Close(write_fd_); - } - - String file() const { return file_; } - int line() const { return line_; } - int index() const { return index_; } - int write_fd() const { return write_fd_; } - - private: - String file_; - int line_; - int index_; - int write_fd_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag); -}; - -// Returns a newly created InternalRunDeathTestFlag object with fields -// initialized from the GTEST_FLAG(internal_run_death_test) flag if -// the flag is specified; otherwise returns NULL. -InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); - -#else // GTEST_HAS_DEATH_TEST - -// This macro is used for implementing macros such as -// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where -// death tests are not supported. Those macros must compile on such systems -// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on -// systems that support death tests. This allows one to write such a macro -// on a system that does not support death tests and be sure that it will -// compile on a death-test supporting system. -// -// Parameters: -// statement - A statement that a macro such as EXPECT_DEATH would test -// for program termination. This macro has to make sure this -// statement is compiled but not executed, to ensure that -// EXPECT_DEATH_IF_SUPPORTED compiles with a certain -// parameter iff EXPECT_DEATH compiles with it. -// regex - A regex that a macro such as EXPECT_DEATH would use to test -// the output of statement. This parameter has to be -// compiled but not evaluated by this macro, to ensure that -// this macro only accepts expressions that a macro such as -// EXPECT_DEATH would accept. -// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED -// and a return statement for ASSERT_DEATH_IF_SUPPORTED. -// This ensures that ASSERT_DEATH_IF_SUPPORTED will not -// compile inside functions where ASSERT_DEATH doesn't -// compile. -// -// The branch that has an always false condition is used to ensure that -// statement and regex are compiled (and thus syntactically correct) but -// never executed. The unreachable code macro protects the terminator -// statement from generating an 'unreachable code' warning in case -// statement unconditionally returns or throws. The Message constructor at -// the end allows the syntax of streaming additional messages into the -// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. -#define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::testing::internal::AlwaysTrue()) { \ - GTEST_LOG_(WARNING) \ - << "Death tests are not supported on this platform.\n" \ - << "Statement '" #statement "' cannot be verified."; \ - } else if (::testing::internal::AlwaysFalse()) { \ - ::testing::internal::RE::PartialMatch(".*", (regex)); \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - terminator; \ - } else \ - ::testing::Message() - -#endif // GTEST_HAS_DEATH_TEST - -} // namespace internal -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ - -namespace testing { - -// This flag controls the style of death tests. Valid values are "threadsafe", -// meaning that the death test child process will re-execute the test binary -// from the start, running only a single death test, or "fast", -// meaning that the child process will execute the test logic immediately -// after forking. -GTEST_DECLARE_string_(death_test_style); - -#if GTEST_HAS_DEATH_TEST - -// The following macros are useful for writing death tests. - -// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is -// executed: -// -// 1. It generates a warning if there is more than one active -// thread. This is because it's safe to fork() or clone() only -// when there is a single thread. -// -// 2. The parent process clone()s a sub-process and runs the death -// test in it; the sub-process exits with code 0 at the end of the -// death test, if it hasn't exited already. -// -// 3. The parent process waits for the sub-process to terminate. -// -// 4. The parent process checks the exit code and error message of -// the sub-process. -// -// Examples: -// -// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); -// for (int i = 0; i < 5; i++) { -// EXPECT_DEATH(server.ProcessRequest(i), -// "Invalid request .* in ProcessRequest()") -// << "Failed to die on request " << i); -// } -// -// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); -// -// bool KilledBySIGHUP(int exit_code) { -// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; -// } -// -// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); -// -// On the regular expressions used in death tests: -// -// On POSIX-compliant systems (*nix), we use the library, -// which uses the POSIX extended regex syntax. -// -// On other platforms (e.g. Windows), we only support a simple regex -// syntax implemented as part of Google Test. This limited -// implementation should be enough most of the time when writing -// death tests; though it lacks many features you can find in PCRE -// or POSIX extended regex syntax. For example, we don't support -// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and -// repetition count ("x{5,7}"), among others. -// -// Below is the syntax that we do support. We chose it to be a -// subset of both PCRE and POSIX extended regex, so it's easy to -// learn wherever you come from. In the following: 'A' denotes a -// literal character, period (.), or a single \\ escape sequence; -// 'x' and 'y' denote regular expressions; 'm' and 'n' are for -// natural numbers. -// -// c matches any literal character c -// \\d matches any decimal digit -// \\D matches any character that's not a decimal digit -// \\f matches \f -// \\n matches \n -// \\r matches \r -// \\s matches any ASCII whitespace, including \n -// \\S matches any character that's not a whitespace -// \\t matches \t -// \\v matches \v -// \\w matches any letter, _, or decimal digit -// \\W matches any character that \\w doesn't match -// \\c matches any literal character c, which must be a punctuation -// . matches any single character except \n -// A? matches 0 or 1 occurrences of A -// A* matches 0 or many occurrences of A -// A+ matches 1 or many occurrences of A -// ^ matches the beginning of a string (not that of each line) -// $ matches the end of a string (not that of each line) -// xy matches x followed by y -// -// If you accidentally use PCRE or POSIX extended regex features -// not implemented by us, you will get a run-time failure. In that -// case, please try to rewrite your regular expression within the -// above syntax. -// -// This implementation is *not* meant to be as highly tuned or robust -// as a compiled regex library, but should perform well enough for a -// death test, which already incurs significant overhead by launching -// a child process. -// -// Known caveats: -// -// A "threadsafe" style death test obtains the path to the test -// program from argv[0] and re-executes it in the sub-process. For -// simplicity, the current implementation doesn't search the PATH -// when launching the sub-process. This means that the user must -// invoke the test program via a path that contains at least one -// path separator (e.g. path/to/foo_test and -// /absolute/path/to/bar_test are fine, but foo_test is not). This -// is rarely a problem as people usually don't put the test binary -// directory in PATH. -// -// TODO(wan@google.com): make thread-safe death tests search the PATH. - -// Asserts that a given statement causes the program to exit, with an -// integer exit status that satisfies predicate, and emitting error output -// that matches regex. -#define ASSERT_EXIT(statement, predicate, regex) \ - GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_) - -// Like ASSERT_EXIT, but continues on to successive tests in the -// test case, if any: -#define EXPECT_EXIT(statement, predicate, regex) \ - GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_) - -// Asserts that a given statement causes the program to exit, either by -// explicitly exiting with a nonzero exit code or being killed by a -// signal, and emitting error output that matches regex. -#define ASSERT_DEATH(statement, regex) \ - ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) - -// Like ASSERT_DEATH, but continues on to successive tests in the -// test case, if any: -#define EXPECT_DEATH(statement, regex) \ - EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) - -// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: - -// Tests that an exit code describes a normal exit with a given exit code. -class GTEST_API_ ExitedWithCode { - public: - explicit ExitedWithCode(int exit_code); - bool operator()(int exit_status) const; - private: - // No implementation - assignment is unsupported. - void operator=(const ExitedWithCode& other); - - const int exit_code_; -}; - -#if !GTEST_OS_WINDOWS -// Tests that an exit code describes an exit due to termination by a -// given signal. -class GTEST_API_ KilledBySignal { - public: - explicit KilledBySignal(int signum); - bool operator()(int exit_status) const; - private: - const int signum_; -}; -#endif // !GTEST_OS_WINDOWS - -// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. -// The death testing framework causes this to have interesting semantics, -// since the sideeffects of the call are only visible in opt mode, and not -// in debug mode. -// -// In practice, this can be used to test functions that utilize the -// LOG(DFATAL) macro using the following style: -// -// int DieInDebugOr12(int* sideeffect) { -// if (sideeffect) { -// *sideeffect = 12; -// } -// LOG(DFATAL) << "death"; -// return 12; -// } -// -// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) { -// int sideeffect = 0; -// // Only asserts in dbg. -// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); -// -// #ifdef NDEBUG -// // opt-mode has sideeffect visible. -// EXPECT_EQ(12, sideeffect); -// #else -// // dbg-mode no visible sideeffect. -// EXPECT_EQ(0, sideeffect); -// #endif -// } -// -// This will assert that DieInDebugReturn12InOpt() crashes in debug -// mode, usually due to a DCHECK or LOG(DFATAL), but returns the -// appropriate fallback value (12 in this case) in opt mode. If you -// need to test that a function has appropriate side-effects in opt -// mode, include assertions against the side-effects. A general -// pattern for this is: -// -// EXPECT_DEBUG_DEATH({ -// // Side-effects here will have an effect after this statement in -// // opt mode, but none in debug mode. -// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); -// }, "death"); -// -#ifdef NDEBUG - -#define EXPECT_DEBUG_DEATH(statement, regex) \ - do { statement; } while (::testing::internal::AlwaysFalse()) - -#define ASSERT_DEBUG_DEATH(statement, regex) \ - do { statement; } while (::testing::internal::AlwaysFalse()) - -#else - -#define EXPECT_DEBUG_DEATH(statement, regex) \ - EXPECT_DEATH(statement, regex) - -#define ASSERT_DEBUG_DEATH(statement, regex) \ - ASSERT_DEATH(statement, regex) - -#endif // NDEBUG for EXPECT_DEBUG_DEATH -#endif // GTEST_HAS_DEATH_TEST - -// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and -// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if -// death tests are supported; otherwise they just issue a warning. This is -// useful when you are combining death test assertions with normal test -// assertions in one test. -#if GTEST_HAS_DEATH_TEST -#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ - EXPECT_DEATH(statement, regex) -#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ - ASSERT_DEATH(statement, regex) -#else -#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ - GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, ) -#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ - GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return) -#endif - -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ -// Copyright 2005, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: wan@google.com (Zhanyong Wan) -// -// The Google C++ Testing Framework (Google Test) -// -// This header file defines the Message class. -// -// IMPORTANT NOTE: Due to limitation of the C++ language, we have to -// leave some internal implementation details in this header file. -// They are clearly marked by comments like this: -// -// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -// -// Such code is NOT meant to be used by a user directly, and is subject -// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user -// program! - -#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ -#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ - -#include - - -namespace testing { - -// The Message class works like an ostream repeater. -// -// Typical usage: -// -// 1. You stream a bunch of values to a Message object. -// It will remember the text in a StrStream. -// 2. Then you stream the Message object to an ostream. -// This causes the text in the Message to be streamed -// to the ostream. -// -// For example; -// -// testing::Message foo; -// foo << 1 << " != " << 2; -// std::cout << foo; -// -// will print "1 != 2". -// -// Message is not intended to be inherited from. In particular, its -// destructor is not virtual. -// -// Note that StrStream behaves differently in gcc and in MSVC. You -// can stream a NULL char pointer to it in the former, but not in the -// latter (it causes an access violation if you do). The Message -// class hides this difference by treating a NULL char pointer as -// "(null)". -class GTEST_API_ Message { - private: - // The type of basic IO manipulators (endl, ends, and flush) for - // narrow streams. - typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); - - public: - // Constructs an empty Message. - // We allocate the StrStream separately because it otherwise each use of - // ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's - // stack frame leading to huge stack frames in some cases; gcc does not reuse - // the stack space. - Message() : ss_(new internal::StrStream) { - // By default, we want there to be enough precision when printing - // a double to a Message. - *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); - } - - // Copy constructor. - Message(const Message& msg) : ss_(new internal::StrStream) { // NOLINT - *ss_ << msg.GetString(); - } - - // Constructs a Message from a C-string. - explicit Message(const char* str) : ss_(new internal::StrStream) { - *ss_ << str; - } - - ~Message() { delete ss_; } -#if GTEST_OS_SYMBIAN - // Streams a value (either a pointer or not) to this object. - template - inline Message& operator <<(const T& value) { - StreamHelper(typename internal::is_pointer::type(), value); - return *this; - } -#else - // Streams a non-pointer value to this object. - template - inline Message& operator <<(const T& val) { - ::GTestStreamToHelper(ss_, val); - return *this; - } - - // Streams a pointer value to this object. - // - // This function is an overload of the previous one. When you - // stream a pointer to a Message, this definition will be used as it - // is more specialized. (The C++ Standard, section - // [temp.func.order].) If you stream a non-pointer, then the - // previous definition will be used. - // - // The reason for this overload is that streaming a NULL pointer to - // ostream is undefined behavior. Depending on the compiler, you - // may get "0", "(nil)", "(null)", or an access violation. To - // ensure consistent result across compilers, we always treat NULL - // as "(null)". - template - inline Message& operator <<(T* const& pointer) { // NOLINT - if (pointer == NULL) { - *ss_ << "(null)"; - } else { - ::GTestStreamToHelper(ss_, pointer); - } - return *this; - } -#endif // GTEST_OS_SYMBIAN - - // Since the basic IO manipulators are overloaded for both narrow - // and wide streams, we have to provide this specialized definition - // of operator <<, even though its body is the same as the - // templatized version above. Without this definition, streaming - // endl or other basic IO manipulators to Message will confuse the - // compiler. - Message& operator <<(BasicNarrowIoManip val) { - *ss_ << val; - return *this; - } - - // Instead of 1/0, we want to see true/false for bool values. - Message& operator <<(bool b) { - return *this << (b ? "true" : "false"); - } - - // These two overloads allow streaming a wide C string to a Message - // using the UTF-8 encoding. - Message& operator <<(const wchar_t* wide_c_str) { - return *this << internal::String::ShowWideCString(wide_c_str); - } - Message& operator <<(wchar_t* wide_c_str) { - return *this << internal::String::ShowWideCString(wide_c_str); - } - -#if GTEST_HAS_STD_WSTRING - // Converts the given wide string to a narrow string using the UTF-8 - // encoding, and streams the result to this Message object. - Message& operator <<(const ::std::wstring& wstr); -#endif // GTEST_HAS_STD_WSTRING - -#if GTEST_HAS_GLOBAL_WSTRING - // Converts the given wide string to a narrow string using the UTF-8 - // encoding, and streams the result to this Message object. - Message& operator <<(const ::wstring& wstr); -#endif // GTEST_HAS_GLOBAL_WSTRING - - // Gets the text streamed to this object so far as a String. - // Each '\0' character in the buffer is replaced with "\\0". - // - // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - internal::String GetString() const { - return internal::StrStreamToString(ss_); - } - - private: -#if GTEST_OS_SYMBIAN - // These are needed as the Nokia Symbian Compiler cannot decide between - // const T& and const T* in a function template. The Nokia compiler _can_ - // decide between class template specializations for T and T*, so a - // tr1::type_traits-like is_pointer works, and we can overload on that. - template - inline void StreamHelper(internal::true_type /*dummy*/, T* pointer) { - if (pointer == NULL) { - *ss_ << "(null)"; - } else { - ::GTestStreamToHelper(ss_, pointer); - } - } - template - inline void StreamHelper(internal::false_type /*dummy*/, const T& value) { - ::GTestStreamToHelper(ss_, value); - } -#endif // GTEST_OS_SYMBIAN - - // We'll hold the text streamed to this object here. - internal::StrStream* const ss_; - - // We declare (but don't implement) this to prevent the compiler - // from implementing the assignment operator. - void operator=(const Message&); -}; - -// Streams a Message to an ostream. -inline std::ostream& operator <<(std::ostream& os, const Message& sb) { - return os << sb.GetString(); -} - -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ -// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! - -// Copyright 2008, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Authors: vladl@google.com (Vlad Losev) -// -// Macros and functions for implementing parameterized tests -// in Google C++ Testing Framework (Google Test) -// -// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! -// -#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ -#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ - - -// Value-parameterized tests allow you to test your code with different -// parameters without writing multiple copies of the same test. -// -// Here is how you use value-parameterized tests: - -#if 0 - -// To write value-parameterized tests, first you should define a fixture -// class. It must be derived from testing::TestWithParam, where T is -// the type of your parameter values. TestWithParam is itself derived -// from testing::Test. T can be any copyable type. If it's a raw pointer, -// you are responsible for managing the lifespan of the pointed values. - -class FooTest : public ::testing::TestWithParam { - // You can implement all the usual class fixture members here. -}; - -// Then, use the TEST_P macro to define as many parameterized tests -// for this fixture as you want. The _P suffix is for "parameterized" -// or "pattern", whichever you prefer to think. - -TEST_P(FooTest, DoesBlah) { - // Inside a test, access the test parameter with the GetParam() method - // of the TestWithParam class: - EXPECT_TRUE(foo.Blah(GetParam())); - ... -} - -TEST_P(FooTest, HasBlahBlah) { - ... -} - -// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test -// case with any set of parameters you want. Google Test defines a number -// of functions for generating test parameters. They return what we call -// (surprise!) parameter generators. Here is a summary of them, which -// are all in the testing namespace: -// -// -// Range(begin, end [, step]) - Yields values {begin, begin+step, -// begin+step+step, ...}. The values do not -// include end. step defaults to 1. -// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. -// ValuesIn(container) - Yields values from a C-style array, an STL -// ValuesIn(begin,end) container, or an iterator range [begin, end). -// Bool() - Yields sequence {false, true}. -// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product -// for the math savvy) of the values generated -// by the N generators. -// -// For more details, see comments at the definitions of these functions below -// in this file. -// -// The following statement will instantiate tests from the FooTest test case -// each with parameter values "meeny", "miny", and "moe". - -INSTANTIATE_TEST_CASE_P(InstantiationName, - FooTest, - Values("meeny", "miny", "moe")); - -// To distinguish different instances of the pattern, (yes, you -// can instantiate it more then once) the first argument to the -// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the -// actual test case name. Remember to pick unique prefixes for different -// instantiations. The tests from the instantiation above will have -// these names: -// -// * InstantiationName/FooTest.DoesBlah/0 for "meeny" -// * InstantiationName/FooTest.DoesBlah/1 for "miny" -// * InstantiationName/FooTest.DoesBlah/2 for "moe" -// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" -// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" -// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" -// -// You can use these names in --gtest_filter. -// -// This statement will instantiate all tests from FooTest again, each -// with parameter values "cat" and "dog": - -const char* pets[] = {"cat", "dog"}; -INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); - -// The tests from the instantiation above will have these names: -// -// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" -// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" -// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" -// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" -// -// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests -// in the given test case, whether their definitions come before or -// AFTER the INSTANTIATE_TEST_CASE_P statement. -// -// Please also note that generator expressions (including parameters to the -// generators) are evaluated in InitGoogleTest(), after main() has started. -// This allows the user on one hand, to adjust generator parameters in order -// to dynamically determine a set of tests to run and on the other hand, -// give the user a chance to inspect the generated tests with Google Test -// reflection API before RUN_ALL_TESTS() is executed. -// -// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc -// for more examples. -// -// In the future, we plan to publish the API for defining new parameter -// generators. But for now this interface remains part of the internal -// implementation and is subject to change. - -#endif // 0 - - -#if !GTEST_OS_SYMBIAN -#include -#endif - -// scripts/fuse_gtest.py depends on gtest's own header being #included -// *unconditionally*. Therefore these #includes cannot be moved -// inside #if GTEST_HAS_PARAM_TEST. -// Copyright 2008 Google Inc. -// All Rights Reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: vladl@google.com (Vlad Losev) - -// Type and function utilities for implementing parameterized tests. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ - -#include -#include -#include - -// scripts/fuse_gtest.py depends on gtest's own header being #included -// *unconditionally*. Therefore these #includes cannot be moved -// inside #if GTEST_HAS_PARAM_TEST. -// Copyright 2003 Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Authors: Dan Egnor (egnor@google.com) -// -// A "smart" pointer type with reference tracking. Every pointer to a -// particular object is kept on a circular linked list. When the last pointer -// to an object is destroyed or reassigned, the object is deleted. -// -// Used properly, this deletes the object when the last reference goes away. -// There are several caveats: -// - Like all reference counting schemes, cycles lead to leaks. -// - Each smart pointer is actually two pointers (8 bytes instead of 4). -// - Every time a pointer is assigned, the entire list of pointers to that -// object is traversed. This class is therefore NOT SUITABLE when there -// will often be more than two or three pointers to a particular object. -// - References are only tracked as long as linked_ptr<> objects are copied. -// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS -// will happen (double deletion). -// -// A good use of this class is storing object references in STL containers. -// You can safely put linked_ptr<> in a vector<>. -// Other uses may not be as good. -// -// Note: If you use an incomplete type with linked_ptr<>, the class -// *containing* linked_ptr<> must have a constructor and destructor (even -// if they do nothing!). -// -// Bill Gibbons suggested we use something like this. -// -// Thread Safety: -// Unlike other linked_ptr implementations, in this implementation -// a linked_ptr object is thread-safe in the sense that: -// - it's safe to copy linked_ptr objects concurrently, -// - it's safe to copy *from* a linked_ptr and read its underlying -// raw pointer (e.g. via get()) concurrently, and -// - it's safe to write to two linked_ptrs that point to the same -// shared object concurrently. -// TODO(wan@google.com): rename this to safe_linked_ptr to avoid -// confusion with normal linked_ptr. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ - -#include -#include - - -namespace testing { -namespace internal { - -// Protects copying of all linked_ptr objects. -GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex); - -// This is used internally by all instances of linked_ptr<>. It needs to be -// a non-template class because different types of linked_ptr<> can refer to -// the same object (linked_ptr(obj) vs linked_ptr(obj)). -// So, it needs to be possible for different types of linked_ptr to participate -// in the same circular linked list, so we need a single class type here. -// -// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr. -class linked_ptr_internal { - public: - // Create a new circle that includes only this instance. - void join_new() { - next_ = this; - } - - // Many linked_ptr operations may change p.link_ for some linked_ptr - // variable p in the same circle as this object. Therefore we need - // to prevent two such operations from occurring concurrently. - // - // Note that different types of linked_ptr objects can coexist in a - // circle (e.g. linked_ptr, linked_ptr, and - // linked_ptr). Therefore we must use a single mutex to - // protect all linked_ptr objects. This can create serious - // contention in production code, but is acceptable in a testing - // framework. - - // Join an existing circle. - // L < g_linked_ptr_mutex - void join(linked_ptr_internal const* ptr) { - MutexLock lock(&g_linked_ptr_mutex); - - linked_ptr_internal const* p = ptr; - while (p->next_ != ptr) p = p->next_; - p->next_ = this; - next_ = ptr; - } - - // Leave whatever circle we're part of. Returns true if we were the - // last member of the circle. Once this is done, you can join() another. - // L < g_linked_ptr_mutex - bool depart() { - MutexLock lock(&g_linked_ptr_mutex); - - if (next_ == this) return true; - linked_ptr_internal const* p = next_; - while (p->next_ != this) p = p->next_; - p->next_ = next_; - return false; - } - - private: - mutable linked_ptr_internal const* next_; -}; - -template -class linked_ptr { - public: - typedef T element_type; - - // Take over ownership of a raw pointer. This should happen as soon as - // possible after the object is created. - explicit linked_ptr(T* ptr = NULL) { capture(ptr); } - ~linked_ptr() { depart(); } - - // Copy an existing linked_ptr<>, adding ourselves to the list of references. - template linked_ptr(linked_ptr const& ptr) { copy(&ptr); } - linked_ptr(linked_ptr const& ptr) { // NOLINT - assert(&ptr != this); - copy(&ptr); - } - - // Assignment releases the old value and acquires the new. - template linked_ptr& operator=(linked_ptr const& ptr) { - depart(); - copy(&ptr); - return *this; - } - - linked_ptr& operator=(linked_ptr const& ptr) { - if (&ptr != this) { - depart(); - copy(&ptr); - } - return *this; - } - - // Smart pointer members. - void reset(T* ptr = NULL) { - depart(); - capture(ptr); - } - T* get() const { return value_; } - T* operator->() const { return value_; } - T& operator*() const { return *value_; } - // Release ownership of the pointed object and returns it. - // Sole ownership by this linked_ptr object is required. - T* release() { - bool last = link_.depart(); - assert(last); - T* v = value_; - value_ = NULL; - return v; - } - - bool operator==(T* p) const { return value_ == p; } - bool operator!=(T* p) const { return value_ != p; } - template - bool operator==(linked_ptr const& ptr) const { - return value_ == ptr.get(); - } - template - bool operator!=(linked_ptr const& ptr) const { - return value_ != ptr.get(); - } - - private: - template - friend class linked_ptr; - - T* value_; - linked_ptr_internal link_; - - void depart() { - if (link_.depart()) delete value_; - } - - void capture(T* ptr) { - value_ = ptr; - link_.join_new(); - } - - template void copy(linked_ptr const* ptr) { - value_ = ptr->get(); - if (value_) - link_.join(&ptr->link_); - else - link_.join_new(); - } -}; - -template inline -bool operator==(T* ptr, const linked_ptr& x) { - return ptr == x.get(); -} - -template inline -bool operator!=(T* ptr, const linked_ptr& x) { - return ptr != x.get(); -} - -// A function to convert T* into linked_ptr -// Doing e.g. make_linked_ptr(new FooBarBaz(arg)) is a shorter notation -// for linked_ptr >(new FooBarBaz(arg)) -template -linked_ptr make_linked_ptr(T* ptr) { - return linked_ptr(ptr); -} - -} // namespace internal -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ - -#if GTEST_HAS_PARAM_TEST - -namespace testing { -namespace internal { - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Outputs a message explaining invalid registration of different -// fixture class for the same test case. This may happen when -// TEST_P macro is used to define two tests with the same name -// but in different namespaces. -GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name, - const char* file, int line); - -template class ParamGeneratorInterface; -template class ParamGenerator; - -// Interface for iterating over elements provided by an implementation -// of ParamGeneratorInterface. -template -class ParamIteratorInterface { - public: - virtual ~ParamIteratorInterface() {} - // A pointer to the base generator instance. - // Used only for the purposes of iterator comparison - // to make sure that two iterators belong to the same generator. - virtual const ParamGeneratorInterface* BaseGenerator() const = 0; - // Advances iterator to point to the next element - // provided by the generator. The caller is responsible - // for not calling Advance() on an iterator equal to - // BaseGenerator()->End(). - virtual void Advance() = 0; - // Clones the iterator object. Used for implementing copy semantics - // of ParamIterator. - virtual ParamIteratorInterface* Clone() const = 0; - // Dereferences the current iterator and provides (read-only) access - // to the pointed value. It is the caller's responsibility not to call - // Current() on an iterator equal to BaseGenerator()->End(). - // Used for implementing ParamGenerator::operator*(). - virtual const T* Current() const = 0; - // Determines whether the given iterator and other point to the same - // element in the sequence generated by the generator. - // Used for implementing ParamGenerator::operator==(). - virtual bool Equals(const ParamIteratorInterface& other) const = 0; -}; - -// Class iterating over elements provided by an implementation of -// ParamGeneratorInterface. It wraps ParamIteratorInterface -// and implements the const forward iterator concept. -template -class ParamIterator { - public: - typedef T value_type; - typedef const T& reference; - typedef ptrdiff_t difference_type; - - // ParamIterator assumes ownership of the impl_ pointer. - ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {} - ParamIterator& operator=(const ParamIterator& other) { - if (this != &other) - impl_.reset(other.impl_->Clone()); - return *this; - } - - const T& operator*() const { return *impl_->Current(); } - const T* operator->() const { return impl_->Current(); } - // Prefix version of operator++. - ParamIterator& operator++() { - impl_->Advance(); - return *this; - } - // Postfix version of operator++. - ParamIterator operator++(int /*unused*/) { - ParamIteratorInterface* clone = impl_->Clone(); - impl_->Advance(); - return ParamIterator(clone); - } - bool operator==(const ParamIterator& other) const { - return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_); - } - bool operator!=(const ParamIterator& other) const { - return !(*this == other); - } - - private: - friend class ParamGenerator; - explicit ParamIterator(ParamIteratorInterface* impl) : impl_(impl) {} - scoped_ptr > impl_; -}; - -// ParamGeneratorInterface is the binary interface to access generators -// defined in other translation units. -template -class ParamGeneratorInterface { - public: - typedef T ParamType; - - virtual ~ParamGeneratorInterface() {} - - // Generator interface definition - virtual ParamIteratorInterface* Begin() const = 0; - virtual ParamIteratorInterface* End() const = 0; -}; - -// Wraps ParamGeneratorInterface and provides general generator syntax -// compatible with the STL Container concept. -// This class implements copy initialization semantics and the contained -// ParamGeneratorInterface instance is shared among all copies -// of the original object. This is possible because that instance is immutable. -template -class ParamGenerator { - public: - typedef ParamIterator iterator; - - explicit ParamGenerator(ParamGeneratorInterface* impl) : impl_(impl) {} - ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} - - ParamGenerator& operator=(const ParamGenerator& other) { - impl_ = other.impl_; - return *this; - } - - iterator begin() const { return iterator(impl_->Begin()); } - iterator end() const { return iterator(impl_->End()); } - - private: - ::testing::internal::linked_ptr > impl_; -}; - -// Generates values from a range of two comparable values. Can be used to -// generate sequences of user-defined types that implement operator+() and -// operator<(). -// This class is used in the Range() function. -template -class RangeGenerator : public ParamGeneratorInterface { - public: - RangeGenerator(T begin, T end, IncrementT step) - : begin_(begin), end_(end), - step_(step), end_index_(CalculateEndIndex(begin, end, step)) {} - virtual ~RangeGenerator() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, begin_, 0, step_); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, end_, end_index_, step_); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, T value, int index, - IncrementT step) - : base_(base), value_(value), index_(index), step_(step) {} - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - virtual void Advance() { - value_ = value_ + step_; - index_++; - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const T* Current() const { return &value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const int other_index = - CheckedDowncastToActualType(&other)->index_; - return index_ == other_index; - } - - private: - Iterator(const Iterator& other) - : ParamIteratorInterface(), - base_(other.base_), value_(other.value_), index_(other.index_), - step_(other.step_) {} - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - T value_; - int index_; - const IncrementT step_; - }; // class RangeGenerator::Iterator - - static int CalculateEndIndex(const T& begin, - const T& end, - const IncrementT& step) { - int end_index = 0; - for (T i = begin; i < end; i = i + step) - end_index++; - return end_index; - } - - // No implementation - assignment is unsupported. - void operator=(const RangeGenerator& other); - - const T begin_; - const T end_; - const IncrementT step_; - // The index for the end() iterator. All the elements in the generated - // sequence are indexed (0-based) to aid iterator comparison. - const int end_index_; -}; // class RangeGenerator - - -// Generates values from a pair of STL-style iterators. Used in the -// ValuesIn() function. The elements are copied from the source range -// since the source can be located on the stack, and the generator -// is likely to persist beyond that stack frame. -template -class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface { - public: - template - ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) - : container_(begin, end) {} - virtual ~ValuesInIteratorRangeGenerator() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, container_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, container_.end()); - } - - private: - typedef typename ::std::vector ContainerType; - - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - typename ContainerType::const_iterator iterator) - : base_(base), iterator_(iterator) {} - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - virtual void Advance() { - ++iterator_; - value_.reset(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - // We need to use cached value referenced by iterator_ because *iterator_ - // can return a temporary object (and of type other then T), so just - // having "return &*iterator_;" doesn't work. - // value_ is updated here and not in Advance() because Advance() - // can advance iterator_ beyond the end of the range, and we cannot - // detect that fact. The client code, on the other hand, is - // responsible for not calling Current() on an out-of-range iterator. - virtual const T* Current() const { - if (value_.get() == NULL) - value_.reset(new T(*iterator_)); - return value_.get(); - } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - return iterator_ == - CheckedDowncastToActualType(&other)->iterator_; - } - - private: - Iterator(const Iterator& other) - // The explicit constructor call suppresses a false warning - // emitted by gcc when supplied with the -Wextra option. - : ParamIteratorInterface(), - base_(other.base_), - iterator_(other.iterator_) {} - - const ParamGeneratorInterface* const base_; - typename ContainerType::const_iterator iterator_; - // A cached value of *iterator_. We keep it here to allow access by - // pointer in the wrapping iterator's operator->(). - // value_ needs to be mutable to be accessed in Current(). - // Use of scoped_ptr helps manage cached value's lifetime, - // which is bound by the lifespan of the iterator itself. - mutable scoped_ptr value_; - }; // class ValuesInIteratorRangeGenerator::Iterator - - // No implementation - assignment is unsupported. - void operator=(const ValuesInIteratorRangeGenerator& other); - - const ContainerType container_; -}; // class ValuesInIteratorRangeGenerator - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Stores a parameter value and later creates tests parameterized with that -// value. -template -class ParameterizedTestFactory : public TestFactoryBase { - public: - typedef typename TestClass::ParamType ParamType; - explicit ParameterizedTestFactory(ParamType parameter) : - parameter_(parameter) {} - virtual Test* CreateTest() { - TestClass::SetParam(¶meter_); - return new TestClass(); - } - - private: - const ParamType parameter_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory); -}; - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// TestMetaFactoryBase is a base class for meta-factories that create -// test factories for passing into MakeAndRegisterTestInfo function. -template -class TestMetaFactoryBase { - public: - virtual ~TestMetaFactoryBase() {} - - virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; -}; - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// TestMetaFactory creates test factories for passing into -// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives -// ownership of test factory pointer, same factory object cannot be passed -// into that method twice. But ParameterizedTestCaseInfo is going to call -// it for each Test/Parameter value combination. Thus it needs meta factory -// creator class. -template -class TestMetaFactory - : public TestMetaFactoryBase { - public: - typedef typename TestCase::ParamType ParamType; - - TestMetaFactory() {} - - virtual TestFactoryBase* CreateTestFactory(ParamType parameter) { - return new ParameterizedTestFactory(parameter); - } - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory); -}; - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// ParameterizedTestCaseInfoBase is a generic interface -// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase -// accumulates test information provided by TEST_P macro invocations -// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations -// and uses that information to register all resulting test instances -// in RegisterTests method. The ParameterizeTestCaseRegistry class holds -// a collection of pointers to the ParameterizedTestCaseInfo objects -// and calls RegisterTests() on each of them when asked. -class ParameterizedTestCaseInfoBase { - public: - virtual ~ParameterizedTestCaseInfoBase() {} - - // Base part of test case name for display purposes. - virtual const String& GetTestCaseName() const = 0; - // Test case id to verify identity. - virtual TypeId GetTestCaseTypeId() const = 0; - // UnitTest class invokes this method to register tests in this - // test case right before running them in RUN_ALL_TESTS macro. - // This method should not be called more then once on any single - // instance of a ParameterizedTestCaseInfoBase derived class. - virtual void RegisterTests() = 0; - - protected: - ParameterizedTestCaseInfoBase() {} - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase); -}; - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P -// macro invocations for a particular test case and generators -// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that -// test case. It registers tests with all values generated by all -// generators when asked. -template -class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { - public: - // ParamType and GeneratorCreationFunc are private types but are required - // for declarations of public methods AddTestPattern() and - // AddTestCaseInstantiation(). - typedef typename TestCase::ParamType ParamType; - // A function that returns an instance of appropriate generator type. - typedef ParamGenerator(GeneratorCreationFunc)(); - - explicit ParameterizedTestCaseInfo(const char* name) - : test_case_name_(name) {} - - // Test case base name for display purposes. - virtual const String& GetTestCaseName() const { return test_case_name_; } - // Test case id to verify identity. - virtual TypeId GetTestCaseTypeId() const { return GetTypeId(); } - // TEST_P macro uses AddTestPattern() to record information - // about a single test in a LocalTestInfo structure. - // test_case_name is the base name of the test case (without invocation - // prefix). test_base_name is the name of an individual test without - // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is - // test case base name and DoBar is test base name. - void AddTestPattern(const char* test_case_name, - const char* test_base_name, - TestMetaFactoryBase* meta_factory) { - tests_.push_back(linked_ptr(new TestInfo(test_case_name, - test_base_name, - meta_factory))); - } - // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information - // about a generator. - int AddTestCaseInstantiation(const char* instantiation_name, - GeneratorCreationFunc* func, - const char* /* file */, - int /* line */) { - instantiations_.push_back(::std::make_pair(instantiation_name, func)); - return 0; // Return value used only to run this method in namespace scope. - } - // UnitTest class invokes this method to register tests in this test case - // test cases right before running tests in RUN_ALL_TESTS macro. - // This method should not be called more then once on any single - // instance of a ParameterizedTestCaseInfoBase derived class. - // UnitTest has a guard to prevent from calling this method more then once. - virtual void RegisterTests() { - for (typename TestInfoContainer::iterator test_it = tests_.begin(); - test_it != tests_.end(); ++test_it) { - linked_ptr test_info = *test_it; - for (typename InstantiationContainer::iterator gen_it = - instantiations_.begin(); gen_it != instantiations_.end(); - ++gen_it) { - const String& instantiation_name = gen_it->first; - ParamGenerator generator((*gen_it->second)()); - - Message test_case_name_stream; - if ( !instantiation_name.empty() ) - test_case_name_stream << instantiation_name.c_str() << "/"; - test_case_name_stream << test_info->test_case_base_name.c_str(); - - int i = 0; - for (typename ParamGenerator::iterator param_it = - generator.begin(); - param_it != generator.end(); ++param_it, ++i) { - Message test_name_stream; - test_name_stream << test_info->test_base_name.c_str() << "/" << i; - ::testing::internal::MakeAndRegisterTestInfo( - test_case_name_stream.GetString().c_str(), - test_name_stream.GetString().c_str(), - "", // test_case_comment - "", // comment; TODO(vladl@google.com): provide parameter value - // representation. - GetTestCaseTypeId(), - TestCase::SetUpTestCase, - TestCase::TearDownTestCase, - test_info->test_meta_factory->CreateTestFactory(*param_it)); - } // for param_it - } // for gen_it - } // for test_it - } // RegisterTests - - private: - // LocalTestInfo structure keeps information about a single test registered - // with TEST_P macro. - struct TestInfo { - TestInfo(const char* a_test_case_base_name, - const char* a_test_base_name, - TestMetaFactoryBase* a_test_meta_factory) : - test_case_base_name(a_test_case_base_name), - test_base_name(a_test_base_name), - test_meta_factory(a_test_meta_factory) {} - - const String test_case_base_name; - const String test_base_name; - const scoped_ptr > test_meta_factory; - }; - typedef ::std::vector > TestInfoContainer; - // Keeps pairs of - // received from INSTANTIATE_TEST_CASE_P macros. - typedef ::std::vector > - InstantiationContainer; - - const String test_case_name_; - TestInfoContainer tests_; - InstantiationContainer instantiations_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo); -}; // class ParameterizedTestCaseInfo - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase -// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P -// macros use it to locate their corresponding ParameterizedTestCaseInfo -// descriptors. -class ParameterizedTestCaseRegistry { - public: - ParameterizedTestCaseRegistry() {} - ~ParameterizedTestCaseRegistry() { - for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); - it != test_case_infos_.end(); ++it) { - delete *it; - } - } - - // Looks up or creates and returns a structure containing information about - // tests and instantiations of a particular test case. - template - ParameterizedTestCaseInfo* GetTestCasePatternHolder( - const char* test_case_name, - const char* file, - int line) { - ParameterizedTestCaseInfo* typed_test_info = NULL; - for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); - it != test_case_infos_.end(); ++it) { - if ((*it)->GetTestCaseName() == test_case_name) { - if ((*it)->GetTestCaseTypeId() != GetTypeId()) { - // Complain about incorrect usage of Google Test facilities - // and terminate the program since we cannot guaranty correct - // test case setup and tear-down in this case. - ReportInvalidTestCaseType(test_case_name, file, line); - abort(); - } else { - // At this point we are sure that the object we found is of the same - // type we are looking for, so we downcast it to that type - // without further checks. - typed_test_info = CheckedDowncastToActualType< - ParameterizedTestCaseInfo >(*it); - } - break; - } - } - if (typed_test_info == NULL) { - typed_test_info = new ParameterizedTestCaseInfo(test_case_name); - test_case_infos_.push_back(typed_test_info); - } - return typed_test_info; - } - void RegisterTests() { - for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); - it != test_case_infos_.end(); ++it) { - (*it)->RegisterTests(); - } - } - - private: - typedef ::std::vector TestCaseInfoContainer; - - TestCaseInfoContainer test_case_infos_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry); -}; - -} // namespace internal -} // namespace testing - -#endif // GTEST_HAS_PARAM_TEST - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ -// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! - -// Copyright 2008 Google Inc. -// All Rights Reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: vladl@google.com (Vlad Losev) - -// Type and function utilities for implementing parameterized tests. -// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! -// -// Currently Google Test supports at most 50 arguments in Values, -// and at most 10 arguments in Combine. Please contact -// googletestframework@googlegroups.com if you need more. -// Please note that the number of arguments to Combine is limited -// by the maximum arity of the implementation of tr1::tuple which is -// currently set at 10. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ - -// scripts/fuse_gtest.py depends on gtest's own header being #included -// *unconditionally*. Therefore these #includes cannot be moved -// inside #if GTEST_HAS_PARAM_TEST. - -#if GTEST_HAS_PARAM_TEST - -namespace testing { - -// Forward declarations of ValuesIn(), which is implemented in -// include/gtest/gtest-param-test.h. -template -internal::ParamGenerator< - typename ::std::iterator_traits::value_type> ValuesIn( - ForwardIterator begin, ForwardIterator end); - -template -internal::ParamGenerator ValuesIn(const T (&array)[N]); - -template -internal::ParamGenerator ValuesIn( - const Container& container); - -namespace internal { - -// Used in the Values() function to provide polymorphic capabilities. -template -class ValueArray1 { - public: - explicit ValueArray1(T1 v1) : v1_(v1) {} - - template - operator ParamGenerator() const { return ValuesIn(&v1_, &v1_ + 1); } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray1& other); - - const T1 v1_; -}; - -template -class ValueArray2 { - public: - ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray2& other); - - const T1 v1_; - const T2 v2_; -}; - -template -class ValueArray3 { - public: - ValueArray3(T1 v1, T2 v2, T3 v3) : v1_(v1), v2_(v2), v3_(v3) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray3& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; -}; - -template -class ValueArray4 { - public: - ValueArray4(T1 v1, T2 v2, T3 v3, T4 v4) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray4& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; -}; - -template -class ValueArray5 { - public: - ValueArray5(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4), v5_(v5) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray5& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; -}; - -template -class ValueArray6 { - public: - ValueArray6(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6) : v1_(v1), v2_(v2), - v3_(v3), v4_(v4), v5_(v5), v6_(v6) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray6& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; -}; - -template -class ValueArray7 { - public: - ValueArray7(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7) : v1_(v1), - v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray7& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; -}; - -template -class ValueArray8 { - public: - ValueArray8(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray8& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; -}; - -template -class ValueArray9 { - public: - ValueArray9(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, - T9 v9) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray9& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; -}; - -template -class ValueArray10 { - public: - ValueArray10(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray10& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; -}; - -template -class ValueArray11 { - public: - ValueArray11(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), - v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray11& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; -}; - -template -class ValueArray12 { - public: - ValueArray12(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), - v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray12& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; -}; - -template -class ValueArray13 { - public: - ValueArray13(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), - v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), - v12_(v12), v13_(v13) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray13& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; -}; - -template -class ValueArray14 { - public: - ValueArray14(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray14& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; -}; - -template -class ValueArray15 { - public: - ValueArray15(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) : v1_(v1), v2_(v2), - v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray15& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; -}; - -template -class ValueArray16 { - public: - ValueArray16(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16) : v1_(v1), - v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), - v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), - v16_(v16) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray16& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; -}; - -template -class ValueArray17 { - public: - ValueArray17(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, - T17 v17) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray17& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; -}; - -template -class ValueArray18 { - public: - ValueArray18(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray18& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; -}; - -template -class ValueArray19 { - public: - ValueArray19(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), - v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), - v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray19& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; -}; - -template -class ValueArray20 { - public: - ValueArray20(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), - v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), - v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), - v19_(v19), v20_(v20) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray20& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; -}; - -template -class ValueArray21 { - public: - ValueArray21(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), - v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), - v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), - v18_(v18), v19_(v19), v20_(v20), v21_(v21) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray21& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; -}; - -template -class ValueArray22 { - public: - ValueArray22(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray22& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; -}; - -template -class ValueArray23 { - public: - ValueArray23(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23) : v1_(v1), v2_(v2), - v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, - v23_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray23& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; -}; - -template -class ValueArray24 { - public: - ValueArray24(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24) : v1_(v1), - v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), - v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), - v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), - v22_(v22), v23_(v23), v24_(v24) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray24& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; -}; - -template -class ValueArray25 { - public: - ValueArray25(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, - T25 v25) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray25& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; -}; - -template -class ValueArray26 { - public: - ValueArray26(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray26& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; -}; - -template -class ValueArray27 { - public: - ValueArray27(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), - v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), - v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), - v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), - v26_(v26), v27_(v27) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray27& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; -}; - -template -class ValueArray28 { - public: - ValueArray28(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), - v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), - v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), - v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), - v25_(v25), v26_(v26), v27_(v27), v28_(v28) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray28& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; -}; - -template -class ValueArray29 { - public: - ValueArray29(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), - v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), - v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), - v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), - v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray29& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; -}; - -template -class ValueArray30 { - public: - ValueArray30(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), - v29_(v29), v30_(v30) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray30& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; -}; - -template -class ValueArray31 { - public: - ValueArray31(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) : v1_(v1), v2_(v2), - v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), - v29_(v29), v30_(v30), v31_(v31) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray31& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; -}; - -template -class ValueArray32 { - public: - ValueArray32(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32) : v1_(v1), - v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), - v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), - v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), - v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), - v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray32& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; -}; - -template -class ValueArray33 { - public: - ValueArray33(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, - T33 v33) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), - v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), - v33_(v33) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray33& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; -}; - -template -class ValueArray34 { - public: - ValueArray34(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), - v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), - v33_(v33), v34_(v34) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray34& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; -}; - -template -class ValueArray35 { - public: - ValueArray35(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), - v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), - v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), - v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), - v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), - v32_(v32), v33_(v33), v34_(v34), v35_(v35) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, - v35_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray35& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; -}; - -template -class ValueArray36 { - public: - ValueArray36(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), - v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), - v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), - v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), - v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), - v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray36& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; -}; - -template -class ValueArray37 { - public: - ValueArray37(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), - v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), - v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), - v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), - v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), - v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), - v36_(v36), v37_(v37) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray37& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; -}; - -template -class ValueArray38 { - public: - ValueArray38(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), - v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), - v35_(v35), v36_(v36), v37_(v37), v38_(v38) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray38& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; -}; - -template -class ValueArray39 { - public: - ValueArray39(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39) : v1_(v1), v2_(v2), - v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), - v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), - v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray39& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; -}; - -template -class ValueArray40 { - public: - ValueArray40(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) : v1_(v1), - v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), - v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), - v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), - v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), - v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), - v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), - v40_(v40) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray40& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; -}; - -template -class ValueArray41 { - public: - ValueArray41(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, - T41 v41) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), - v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), - v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), - v39_(v39), v40_(v40), v41_(v41) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray41& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; -}; - -template -class ValueArray42 { - public: - ValueArray42(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), - v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), - v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), - v39_(v39), v40_(v40), v41_(v41), v42_(v42) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray42& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; -}; - -template -class ValueArray43 { - public: - ValueArray43(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), - v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), - v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), - v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), - v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), - v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), - v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray43& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; -}; - -template -class ValueArray44 { - public: - ValueArray44(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), - v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), - v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), - v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), - v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), - v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), - v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), - v43_(v43), v44_(v44) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray44& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; -}; - -template -class ValueArray45 { - public: - ValueArray45(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44, T45 v45) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), - v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), - v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), - v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), - v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), - v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), - v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), - v42_(v42), v43_(v43), v44_(v44), v45_(v45) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray45& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; - const T45 v45_; -}; - -template -class ValueArray46 { - public: - ValueArray46(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), - v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), - v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), - v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray46& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; - const T45 v45_; - const T46 v46_; -}; - -template -class ValueArray47 { - public: - ValueArray47(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) : v1_(v1), v2_(v2), - v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), - v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), - v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), - v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46), - v47_(v47) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, - v47_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray47& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; - const T45 v45_; - const T46 v46_; - const T47 v47_; -}; - -template -class ValueArray48 { - public: - ValueArray48(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48) : v1_(v1), - v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), - v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), - v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), - v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), - v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), - v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), - v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), - v46_(v46), v47_(v47), v48_(v48) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, - v48_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray48& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; - const T45 v45_; - const T46 v46_; - const T47 v47_; - const T48 v48_; -}; - -template -class ValueArray49 { - public: - ValueArray49(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, - T49 v49) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), - v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), - v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), - v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), - v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, - v48_, v49_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray49& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; - const T45 v45_; - const T46 v46_; - const T47 v47_; - const T48 v48_; - const T49 v49_; -}; - -template -class ValueArray50 { - public: - ValueArray50(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, T49 v49, - T50 v50) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), - v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), - v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), - v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), - v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49), v50_(v50) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, - v48_, v49_, v50_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray50& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; - const T45 v45_; - const T46 v46_; - const T47 v47_; - const T48 v48_; - const T49 v49_; - const T50 v50_; -}; - -#if GTEST_HAS_COMBINE -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Generates values from the Cartesian product of values produced -// by the argument generators. -// -template -class CartesianProductGenerator2 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator2(const ParamGenerator& g1, - const ParamGenerator& g2) - : g1_(g1), g2_(g2) {} - virtual ~CartesianProductGenerator2() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current2_; - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - ParamType current_value_; - }; // class CartesianProductGenerator2::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator2& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; -}; // class CartesianProductGenerator2 - - -template -class CartesianProductGenerator3 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator3(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3) - : g1_(g1), g2_(g2), g3_(g3) {} - virtual ~CartesianProductGenerator3() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current3_; - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - ParamType current_value_; - }; // class CartesianProductGenerator3::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator3& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; -}; // class CartesianProductGenerator3 - - -template -class CartesianProductGenerator4 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator4(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} - virtual ~CartesianProductGenerator4() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current4_; - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - ParamType current_value_; - }; // class CartesianProductGenerator4::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator4& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; -}; // class CartesianProductGenerator4 - - -template -class CartesianProductGenerator5 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator5(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4, const ParamGenerator& g5) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} - virtual ~CartesianProductGenerator5() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end(), g5_, g5_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4, - const ParamGenerator& g5, - const typename ParamGenerator::iterator& current5) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4), - begin5_(g5.begin()), end5_(g5.end()), current5_(current5) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current5_; - if (current5_ == end5_) { - current5_ = begin5_; - ++current4_; - } - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_ && - current5_ == typed_other->current5_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_), - begin5_(other.begin5_), - end5_(other.end5_), - current5_(other.current5_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_ || - current5_ == end5_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - const typename ParamGenerator::iterator begin5_; - const typename ParamGenerator::iterator end5_; - typename ParamGenerator::iterator current5_; - ParamType current_value_; - }; // class CartesianProductGenerator5::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator5& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; - const ParamGenerator g5_; -}; // class CartesianProductGenerator5 - - -template -class CartesianProductGenerator6 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator6(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4, const ParamGenerator& g5, - const ParamGenerator& g6) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} - virtual ~CartesianProductGenerator6() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4, - const ParamGenerator& g5, - const typename ParamGenerator::iterator& current5, - const ParamGenerator& g6, - const typename ParamGenerator::iterator& current6) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4), - begin5_(g5.begin()), end5_(g5.end()), current5_(current5), - begin6_(g6.begin()), end6_(g6.end()), current6_(current6) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current6_; - if (current6_ == end6_) { - current6_ = begin6_; - ++current5_; - } - if (current5_ == end5_) { - current5_ = begin5_; - ++current4_; - } - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_ && - current5_ == typed_other->current5_ && - current6_ == typed_other->current6_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_), - begin5_(other.begin5_), - end5_(other.end5_), - current5_(other.current5_), - begin6_(other.begin6_), - end6_(other.end6_), - current6_(other.current6_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_, *current6_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_ || - current5_ == end5_ || - current6_ == end6_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - const typename ParamGenerator::iterator begin5_; - const typename ParamGenerator::iterator end5_; - typename ParamGenerator::iterator current5_; - const typename ParamGenerator::iterator begin6_; - const typename ParamGenerator::iterator end6_; - typename ParamGenerator::iterator current6_; - ParamType current_value_; - }; // class CartesianProductGenerator6::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator6& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; - const ParamGenerator g5_; - const ParamGenerator g6_; -}; // class CartesianProductGenerator6 - - -template -class CartesianProductGenerator7 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator7(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4, const ParamGenerator& g5, - const ParamGenerator& g6, const ParamGenerator& g7) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} - virtual ~CartesianProductGenerator7() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, - g7_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4, - const ParamGenerator& g5, - const typename ParamGenerator::iterator& current5, - const ParamGenerator& g6, - const typename ParamGenerator::iterator& current6, - const ParamGenerator& g7, - const typename ParamGenerator::iterator& current7) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4), - begin5_(g5.begin()), end5_(g5.end()), current5_(current5), - begin6_(g6.begin()), end6_(g6.end()), current6_(current6), - begin7_(g7.begin()), end7_(g7.end()), current7_(current7) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current7_; - if (current7_ == end7_) { - current7_ = begin7_; - ++current6_; - } - if (current6_ == end6_) { - current6_ = begin6_; - ++current5_; - } - if (current5_ == end5_) { - current5_ = begin5_; - ++current4_; - } - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_ && - current5_ == typed_other->current5_ && - current6_ == typed_other->current6_ && - current7_ == typed_other->current7_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_), - begin5_(other.begin5_), - end5_(other.end5_), - current5_(other.current5_), - begin6_(other.begin6_), - end6_(other.end6_), - current6_(other.current6_), - begin7_(other.begin7_), - end7_(other.end7_), - current7_(other.current7_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_, *current6_, *current7_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_ || - current5_ == end5_ || - current6_ == end6_ || - current7_ == end7_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - const typename ParamGenerator::iterator begin5_; - const typename ParamGenerator::iterator end5_; - typename ParamGenerator::iterator current5_; - const typename ParamGenerator::iterator begin6_; - const typename ParamGenerator::iterator end6_; - typename ParamGenerator::iterator current6_; - const typename ParamGenerator::iterator begin7_; - const typename ParamGenerator::iterator end7_; - typename ParamGenerator::iterator current7_; - ParamType current_value_; - }; // class CartesianProductGenerator7::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator7& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; - const ParamGenerator g5_; - const ParamGenerator g6_; - const ParamGenerator g7_; -}; // class CartesianProductGenerator7 - - -template -class CartesianProductGenerator8 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator8(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4, const ParamGenerator& g5, - const ParamGenerator& g6, const ParamGenerator& g7, - const ParamGenerator& g8) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), - g8_(g8) {} - virtual ~CartesianProductGenerator8() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, - g7_.begin(), g8_, g8_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, - g8_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4, - const ParamGenerator& g5, - const typename ParamGenerator::iterator& current5, - const ParamGenerator& g6, - const typename ParamGenerator::iterator& current6, - const ParamGenerator& g7, - const typename ParamGenerator::iterator& current7, - const ParamGenerator& g8, - const typename ParamGenerator::iterator& current8) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4), - begin5_(g5.begin()), end5_(g5.end()), current5_(current5), - begin6_(g6.begin()), end6_(g6.end()), current6_(current6), - begin7_(g7.begin()), end7_(g7.end()), current7_(current7), - begin8_(g8.begin()), end8_(g8.end()), current8_(current8) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current8_; - if (current8_ == end8_) { - current8_ = begin8_; - ++current7_; - } - if (current7_ == end7_) { - current7_ = begin7_; - ++current6_; - } - if (current6_ == end6_) { - current6_ = begin6_; - ++current5_; - } - if (current5_ == end5_) { - current5_ = begin5_; - ++current4_; - } - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_ && - current5_ == typed_other->current5_ && - current6_ == typed_other->current6_ && - current7_ == typed_other->current7_ && - current8_ == typed_other->current8_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_), - begin5_(other.begin5_), - end5_(other.end5_), - current5_(other.current5_), - begin6_(other.begin6_), - end6_(other.end6_), - current6_(other.current6_), - begin7_(other.begin7_), - end7_(other.end7_), - current7_(other.current7_), - begin8_(other.begin8_), - end8_(other.end8_), - current8_(other.current8_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_, *current6_, *current7_, *current8_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_ || - current5_ == end5_ || - current6_ == end6_ || - current7_ == end7_ || - current8_ == end8_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - const typename ParamGenerator::iterator begin5_; - const typename ParamGenerator::iterator end5_; - typename ParamGenerator::iterator current5_; - const typename ParamGenerator::iterator begin6_; - const typename ParamGenerator::iterator end6_; - typename ParamGenerator::iterator current6_; - const typename ParamGenerator::iterator begin7_; - const typename ParamGenerator::iterator end7_; - typename ParamGenerator::iterator current7_; - const typename ParamGenerator::iterator begin8_; - const typename ParamGenerator::iterator end8_; - typename ParamGenerator::iterator current8_; - ParamType current_value_; - }; // class CartesianProductGenerator8::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator8& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; - const ParamGenerator g5_; - const ParamGenerator g6_; - const ParamGenerator g7_; - const ParamGenerator g8_; -}; // class CartesianProductGenerator8 - - -template -class CartesianProductGenerator9 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator9(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4, const ParamGenerator& g5, - const ParamGenerator& g6, const ParamGenerator& g7, - const ParamGenerator& g8, const ParamGenerator& g9) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), - g9_(g9) {} - virtual ~CartesianProductGenerator9() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, - g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, - g8_.end(), g9_, g9_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4, - const ParamGenerator& g5, - const typename ParamGenerator::iterator& current5, - const ParamGenerator& g6, - const typename ParamGenerator::iterator& current6, - const ParamGenerator& g7, - const typename ParamGenerator::iterator& current7, - const ParamGenerator& g8, - const typename ParamGenerator::iterator& current8, - const ParamGenerator& g9, - const typename ParamGenerator::iterator& current9) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4), - begin5_(g5.begin()), end5_(g5.end()), current5_(current5), - begin6_(g6.begin()), end6_(g6.end()), current6_(current6), - begin7_(g7.begin()), end7_(g7.end()), current7_(current7), - begin8_(g8.begin()), end8_(g8.end()), current8_(current8), - begin9_(g9.begin()), end9_(g9.end()), current9_(current9) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current9_; - if (current9_ == end9_) { - current9_ = begin9_; - ++current8_; - } - if (current8_ == end8_) { - current8_ = begin8_; - ++current7_; - } - if (current7_ == end7_) { - current7_ = begin7_; - ++current6_; - } - if (current6_ == end6_) { - current6_ = begin6_; - ++current5_; - } - if (current5_ == end5_) { - current5_ = begin5_; - ++current4_; - } - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_ && - current5_ == typed_other->current5_ && - current6_ == typed_other->current6_ && - current7_ == typed_other->current7_ && - current8_ == typed_other->current8_ && - current9_ == typed_other->current9_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_), - begin5_(other.begin5_), - end5_(other.end5_), - current5_(other.current5_), - begin6_(other.begin6_), - end6_(other.end6_), - current6_(other.current6_), - begin7_(other.begin7_), - end7_(other.end7_), - current7_(other.current7_), - begin8_(other.begin8_), - end8_(other.end8_), - current8_(other.current8_), - begin9_(other.begin9_), - end9_(other.end9_), - current9_(other.current9_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_, *current6_, *current7_, *current8_, - *current9_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_ || - current5_ == end5_ || - current6_ == end6_ || - current7_ == end7_ || - current8_ == end8_ || - current9_ == end9_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - const typename ParamGenerator::iterator begin5_; - const typename ParamGenerator::iterator end5_; - typename ParamGenerator::iterator current5_; - const typename ParamGenerator::iterator begin6_; - const typename ParamGenerator::iterator end6_; - typename ParamGenerator::iterator current6_; - const typename ParamGenerator::iterator begin7_; - const typename ParamGenerator::iterator end7_; - typename ParamGenerator::iterator current7_; - const typename ParamGenerator::iterator begin8_; - const typename ParamGenerator::iterator end8_; - typename ParamGenerator::iterator current8_; - const typename ParamGenerator::iterator begin9_; - const typename ParamGenerator::iterator end9_; - typename ParamGenerator::iterator current9_; - ParamType current_value_; - }; // class CartesianProductGenerator9::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator9& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; - const ParamGenerator g5_; - const ParamGenerator g6_; - const ParamGenerator g7_; - const ParamGenerator g8_; - const ParamGenerator g9_; -}; // class CartesianProductGenerator9 - - -template -class CartesianProductGenerator10 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator10(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4, const ParamGenerator& g5, - const ParamGenerator& g6, const ParamGenerator& g7, - const ParamGenerator& g8, const ParamGenerator& g9, - const ParamGenerator& g10) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), - g9_(g9), g10_(g10) {} - virtual ~CartesianProductGenerator10() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, - g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin(), g10_, g10_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, - g8_.end(), g9_, g9_.end(), g10_, g10_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4, - const ParamGenerator& g5, - const typename ParamGenerator::iterator& current5, - const ParamGenerator& g6, - const typename ParamGenerator::iterator& current6, - const ParamGenerator& g7, - const typename ParamGenerator::iterator& current7, - const ParamGenerator& g8, - const typename ParamGenerator::iterator& current8, - const ParamGenerator& g9, - const typename ParamGenerator::iterator& current9, - const ParamGenerator& g10, - const typename ParamGenerator::iterator& current10) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4), - begin5_(g5.begin()), end5_(g5.end()), current5_(current5), - begin6_(g6.begin()), end6_(g6.end()), current6_(current6), - begin7_(g7.begin()), end7_(g7.end()), current7_(current7), - begin8_(g8.begin()), end8_(g8.end()), current8_(current8), - begin9_(g9.begin()), end9_(g9.end()), current9_(current9), - begin10_(g10.begin()), end10_(g10.end()), current10_(current10) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current10_; - if (current10_ == end10_) { - current10_ = begin10_; - ++current9_; - } - if (current9_ == end9_) { - current9_ = begin9_; - ++current8_; - } - if (current8_ == end8_) { - current8_ = begin8_; - ++current7_; - } - if (current7_ == end7_) { - current7_ = begin7_; - ++current6_; - } - if (current6_ == end6_) { - current6_ = begin6_; - ++current5_; - } - if (current5_ == end5_) { - current5_ = begin5_; - ++current4_; - } - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_ && - current5_ == typed_other->current5_ && - current6_ == typed_other->current6_ && - current7_ == typed_other->current7_ && - current8_ == typed_other->current8_ && - current9_ == typed_other->current9_ && - current10_ == typed_other->current10_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_), - begin5_(other.begin5_), - end5_(other.end5_), - current5_(other.current5_), - begin6_(other.begin6_), - end6_(other.end6_), - current6_(other.current6_), - begin7_(other.begin7_), - end7_(other.end7_), - current7_(other.current7_), - begin8_(other.begin8_), - end8_(other.end8_), - current8_(other.current8_), - begin9_(other.begin9_), - end9_(other.end9_), - current9_(other.current9_), - begin10_(other.begin10_), - end10_(other.end10_), - current10_(other.current10_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_, *current6_, *current7_, *current8_, - *current9_, *current10_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_ || - current5_ == end5_ || - current6_ == end6_ || - current7_ == end7_ || - current8_ == end8_ || - current9_ == end9_ || - current10_ == end10_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - const typename ParamGenerator::iterator begin5_; - const typename ParamGenerator::iterator end5_; - typename ParamGenerator::iterator current5_; - const typename ParamGenerator::iterator begin6_; - const typename ParamGenerator::iterator end6_; - typename ParamGenerator::iterator current6_; - const typename ParamGenerator::iterator begin7_; - const typename ParamGenerator::iterator end7_; - typename ParamGenerator::iterator current7_; - const typename ParamGenerator::iterator begin8_; - const typename ParamGenerator::iterator end8_; - typename ParamGenerator::iterator current8_; - const typename ParamGenerator::iterator begin9_; - const typename ParamGenerator::iterator end9_; - typename ParamGenerator::iterator current9_; - const typename ParamGenerator::iterator begin10_; - const typename ParamGenerator::iterator end10_; - typename ParamGenerator::iterator current10_; - ParamType current_value_; - }; // class CartesianProductGenerator10::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator10& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; - const ParamGenerator g5_; - const ParamGenerator g6_; - const ParamGenerator g7_; - const ParamGenerator g8_; - const ParamGenerator g9_; - const ParamGenerator g10_; -}; // class CartesianProductGenerator10 - - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Helper classes providing Combine() with polymorphic features. They allow -// casting CartesianProductGeneratorN to ParamGenerator if T is -// convertible to U. -// -template -class CartesianProductHolder2 { - public: -CartesianProductHolder2(const Generator1& g1, const Generator2& g2) - : g1_(g1), g2_(g2) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator2( - static_cast >(g1_), - static_cast >(g2_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder2& other); - - const Generator1 g1_; - const Generator2 g2_; -}; // class CartesianProductHolder2 - -template -class CartesianProductHolder3 { - public: -CartesianProductHolder3(const Generator1& g1, const Generator2& g2, - const Generator3& g3) - : g1_(g1), g2_(g2), g3_(g3) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator3( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder3& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; -}; // class CartesianProductHolder3 - -template -class CartesianProductHolder4 { - public: -CartesianProductHolder4(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator4( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder4& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; -}; // class CartesianProductHolder4 - -template -class CartesianProductHolder5 { - public: -CartesianProductHolder5(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4, const Generator5& g5) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator5( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_), - static_cast >(g5_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder5& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; - const Generator5 g5_; -}; // class CartesianProductHolder5 - -template -class CartesianProductHolder6 { - public: -CartesianProductHolder6(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4, const Generator5& g5, - const Generator6& g6) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator6( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_), - static_cast >(g5_), - static_cast >(g6_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder6& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; - const Generator5 g5_; - const Generator6 g6_; -}; // class CartesianProductHolder6 - -template -class CartesianProductHolder7 { - public: -CartesianProductHolder7(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4, const Generator5& g5, - const Generator6& g6, const Generator7& g7) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator7( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_), - static_cast >(g5_), - static_cast >(g6_), - static_cast >(g7_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder7& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; - const Generator5 g5_; - const Generator6 g6_; - const Generator7 g7_; -}; // class CartesianProductHolder7 - -template -class CartesianProductHolder8 { - public: -CartesianProductHolder8(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4, const Generator5& g5, - const Generator6& g6, const Generator7& g7, const Generator8& g8) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), - g8_(g8) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator8( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_), - static_cast >(g5_), - static_cast >(g6_), - static_cast >(g7_), - static_cast >(g8_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder8& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; - const Generator5 g5_; - const Generator6 g6_; - const Generator7 g7_; - const Generator8 g8_; -}; // class CartesianProductHolder8 - -template -class CartesianProductHolder9 { - public: -CartesianProductHolder9(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4, const Generator5& g5, - const Generator6& g6, const Generator7& g7, const Generator8& g8, - const Generator9& g9) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), - g9_(g9) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator9( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_), - static_cast >(g5_), - static_cast >(g6_), - static_cast >(g7_), - static_cast >(g8_), - static_cast >(g9_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder9& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; - const Generator5 g5_; - const Generator6 g6_; - const Generator7 g7_; - const Generator8 g8_; - const Generator9 g9_; -}; // class CartesianProductHolder9 - -template -class CartesianProductHolder10 { - public: -CartesianProductHolder10(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4, const Generator5& g5, - const Generator6& g6, const Generator7& g7, const Generator8& g8, - const Generator9& g9, const Generator10& g10) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), - g9_(g9), g10_(g10) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator10( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_), - static_cast >(g5_), - static_cast >(g6_), - static_cast >(g7_), - static_cast >(g8_), - static_cast >(g9_), - static_cast >(g10_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder10& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; - const Generator5 g5_; - const Generator6 g6_; - const Generator7 g7_; - const Generator8 g8_; - const Generator9 g9_; - const Generator10 g10_; -}; // class CartesianProductHolder10 - -#endif // GTEST_HAS_COMBINE - -} // namespace internal -} // namespace testing - -#endif // GTEST_HAS_PARAM_TEST - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ - -#if GTEST_HAS_PARAM_TEST - -namespace testing { - -// Functions producing parameter generators. -// -// Google Test uses these generators to produce parameters for value- -// parameterized tests. When a parameterized test case is instantiated -// with a particular generator, Google Test creates and runs tests -// for each element in the sequence produced by the generator. -// -// In the following sample, tests from test case FooTest are instantiated -// each three times with parameter values 3, 5, and 8: -// -// class FooTest : public TestWithParam { ... }; -// -// TEST_P(FooTest, TestThis) { -// } -// TEST_P(FooTest, TestThat) { -// } -// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8)); -// - -// Range() returns generators providing sequences of values in a range. -// -// Synopsis: -// Range(start, end) -// - returns a generator producing a sequence of values {start, start+1, -// start+2, ..., }. -// Range(start, end, step) -// - returns a generator producing a sequence of values {start, start+step, -// start+step+step, ..., }. -// Notes: -// * The generated sequences never include end. For example, Range(1, 5) -// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) -// returns a generator producing {1, 3, 5, 7}. -// * start and end must have the same type. That type may be any integral or -// floating-point type or a user defined type satisfying these conditions: -// * It must be assignable (have operator=() defined). -// * It must have operator+() (operator+(int-compatible type) for -// two-operand version). -// * It must have operator<() defined. -// Elements in the resulting sequences will also have that type. -// * Condition start < end must be satisfied in order for resulting sequences -// to contain any elements. -// -template -internal::ParamGenerator Range(T start, T end, IncrementT step) { - return internal::ParamGenerator( - new internal::RangeGenerator(start, end, step)); -} - -template -internal::ParamGenerator Range(T start, T end) { - return Range(start, end, 1); -} - -// ValuesIn() function allows generation of tests with parameters coming from -// a container. -// -// Synopsis: -// ValuesIn(const T (&array)[N]) -// - returns a generator producing sequences with elements from -// a C-style array. -// ValuesIn(const Container& container) -// - returns a generator producing sequences with elements from -// an STL-style container. -// ValuesIn(Iterator begin, Iterator end) -// - returns a generator producing sequences with elements from -// a range [begin, end) defined by a pair of STL-style iterators. These -// iterators can also be plain C pointers. -// -// Please note that ValuesIn copies the values from the containers -// passed in and keeps them to generate tests in RUN_ALL_TESTS(). -// -// Examples: -// -// This instantiates tests from test case StringTest -// each with C-string values of "foo", "bar", and "baz": -// -// const char* strings[] = {"foo", "bar", "baz"}; -// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings)); -// -// This instantiates tests from test case StlStringTest -// each with STL strings with values "a" and "b": -// -// ::std::vector< ::std::string> GetParameterStrings() { -// ::std::vector< ::std::string> v; -// v.push_back("a"); -// v.push_back("b"); -// return v; -// } -// -// INSTANTIATE_TEST_CASE_P(CharSequence, -// StlStringTest, -// ValuesIn(GetParameterStrings())); -// -// -// This will also instantiate tests from CharTest -// each with parameter values 'a' and 'b': -// -// ::std::list GetParameterChars() { -// ::std::list list; -// list.push_back('a'); -// list.push_back('b'); -// return list; -// } -// ::std::list l = GetParameterChars(); -// INSTANTIATE_TEST_CASE_P(CharSequence2, -// CharTest, -// ValuesIn(l.begin(), l.end())); -// -template -internal::ParamGenerator< - typename ::std::iterator_traits::value_type> ValuesIn( - ForwardIterator begin, - ForwardIterator end) { - typedef typename ::std::iterator_traits::value_type - ParamType; - return internal::ParamGenerator( - new internal::ValuesInIteratorRangeGenerator(begin, end)); -} - -template -internal::ParamGenerator ValuesIn(const T (&array)[N]) { - return ValuesIn(array, array + N); -} - -template -internal::ParamGenerator ValuesIn( - const Container& container) { - return ValuesIn(container.begin(), container.end()); -} - -// Values() allows generating tests from explicitly specified list of -// parameters. -// -// Synopsis: -// Values(T v1, T v2, ..., T vN) -// - returns a generator producing sequences with elements v1, v2, ..., vN. -// -// For example, this instantiates tests from test case BarTest each -// with values "one", "two", and "three": -// -// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three")); -// -// This instantiates tests from test case BazTest each with values 1, 2, 3.5. -// The exact type of values will depend on the type of parameter in BazTest. -// -// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); -// -// Currently, Values() supports from 1 to 50 parameters. -// -template -internal::ValueArray1 Values(T1 v1) { - return internal::ValueArray1(v1); -} - -template -internal::ValueArray2 Values(T1 v1, T2 v2) { - return internal::ValueArray2(v1, v2); -} - -template -internal::ValueArray3 Values(T1 v1, T2 v2, T3 v3) { - return internal::ValueArray3(v1, v2, v3); -} - -template -internal::ValueArray4 Values(T1 v1, T2 v2, T3 v3, T4 v4) { - return internal::ValueArray4(v1, v2, v3, v4); -} - -template -internal::ValueArray5 Values(T1 v1, T2 v2, T3 v3, T4 v4, - T5 v5) { - return internal::ValueArray5(v1, v2, v3, v4, v5); -} - -template -internal::ValueArray6 Values(T1 v1, T2 v2, T3 v3, - T4 v4, T5 v5, T6 v6) { - return internal::ValueArray6(v1, v2, v3, v4, v5, v6); -} - -template -internal::ValueArray7 Values(T1 v1, T2 v2, T3 v3, - T4 v4, T5 v5, T6 v6, T7 v7) { - return internal::ValueArray7(v1, v2, v3, v4, v5, - v6, v7); -} - -template -internal::ValueArray8 Values(T1 v1, T2 v2, - T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8) { - return internal::ValueArray8(v1, v2, v3, v4, - v5, v6, v7, v8); -} - -template -internal::ValueArray9 Values(T1 v1, T2 v2, - T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9) { - return internal::ValueArray9(v1, v2, v3, - v4, v5, v6, v7, v8, v9); -} - -template -internal::ValueArray10 Values(T1 v1, - T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10) { - return internal::ValueArray10(v1, - v2, v3, v4, v5, v6, v7, v8, v9, v10); -} - -template -internal::ValueArray11 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11) { - return internal::ValueArray11(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11); -} - -template -internal::ValueArray12 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12) { - return internal::ValueArray12(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12); -} - -template -internal::ValueArray13 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13) { - return internal::ValueArray13(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13); -} - -template -internal::ValueArray14 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) { - return internal::ValueArray14(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, - v14); -} - -template -internal::ValueArray15 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, - T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) { - return internal::ValueArray15(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); -} - -template -internal::ValueArray16 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16) { - return internal::ValueArray16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, - v12, v13, v14, v15, v16); -} - -template -internal::ValueArray17 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17) { - return internal::ValueArray17(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, - v11, v12, v13, v14, v15, v16, v17); -} - -template -internal::ValueArray18 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, - T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18) { - return internal::ValueArray18(v1, v2, v3, v4, v5, v6, v7, v8, v9, - v10, v11, v12, v13, v14, v15, v16, v17, v18); -} - -template -internal::ValueArray19 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, - T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, - T15 v15, T16 v16, T17 v17, T18 v18, T19 v19) { - return internal::ValueArray19(v1, v2, v3, v4, v5, v6, v7, v8, - v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19); -} - -template -internal::ValueArray20 Values(T1 v1, T2 v2, T3 v3, T4 v4, - T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, - T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20) { - return internal::ValueArray20(v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20); -} - -template -internal::ValueArray21 Values(T1 v1, T2 v2, T3 v3, T4 v4, - T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, - T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21) { - return internal::ValueArray21(v1, v2, v3, v4, v5, v6, - v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21); -} - -template -internal::ValueArray22 Values(T1 v1, T2 v2, T3 v3, - T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, - T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, - T21 v21, T22 v22) { - return internal::ValueArray22(v1, v2, v3, v4, - v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, - v20, v21, v22); -} - -template -internal::ValueArray23 Values(T1 v1, T2 v2, - T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, - T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, - T21 v21, T22 v22, T23 v23) { - return internal::ValueArray23(v1, v2, v3, - v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, - v20, v21, v22, v23); -} - -template -internal::ValueArray24 Values(T1 v1, T2 v2, - T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, - T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, - T21 v21, T22 v22, T23 v23, T24 v24) { - return internal::ValueArray24(v1, v2, - v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, - v19, v20, v21, v22, v23, v24); -} - -template -internal::ValueArray25 Values(T1 v1, - T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, - T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, - T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25) { - return internal::ValueArray25(v1, - v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, - v18, v19, v20, v21, v22, v23, v24, v25); -} - -template -internal::ValueArray26 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26) { - return internal::ValueArray26(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, - v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26); -} - -template -internal::ValueArray27 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27) { - return internal::ValueArray27(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, - v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27); -} - -template -internal::ValueArray28 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28) { - return internal::ValueArray28(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, - v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, - v28); -} - -template -internal::ValueArray29 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29) { - return internal::ValueArray29(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, - v27, v28, v29); -} - -template -internal::ValueArray30 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, - T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, - T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, - T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) { - return internal::ValueArray30(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, - v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, - v26, v27, v28, v29, v30); -} - -template -internal::ValueArray31 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, - T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) { - return internal::ValueArray31(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, - v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, - v25, v26, v27, v28, v29, v30, v31); -} - -template -internal::ValueArray32 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, - T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, - T32 v32) { - return internal::ValueArray32(v1, v2, v3, v4, v5, v6, v7, v8, v9, - v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, - v24, v25, v26, v27, v28, v29, v30, v31, v32); -} - -template -internal::ValueArray33 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, - T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, - T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, - T32 v32, T33 v33) { - return internal::ValueArray33(v1, v2, v3, v4, v5, v6, v7, v8, - v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, - v24, v25, v26, v27, v28, v29, v30, v31, v32, v33); -} - -template -internal::ValueArray34 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, - T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, - T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, - T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, - T31 v31, T32 v32, T33 v33, T34 v34) { - return internal::ValueArray34(v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, - v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34); -} - -template -internal::ValueArray35 Values(T1 v1, T2 v2, T3 v3, T4 v4, - T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, - T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, - T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, - T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35) { - return internal::ValueArray35(v1, v2, v3, v4, v5, v6, - v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, - v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35); -} - -template -internal::ValueArray36 Values(T1 v1, T2 v2, T3 v3, T4 v4, - T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, - T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, - T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, - T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36) { - return internal::ValueArray36(v1, v2, v3, v4, - v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, - v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, - v34, v35, v36); -} - -template -internal::ValueArray37 Values(T1 v1, T2 v2, T3 v3, - T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, - T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, - T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, - T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, - T37 v37) { - return internal::ValueArray37(v1, v2, v3, - v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, - v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, - v34, v35, v36, v37); -} - -template -internal::ValueArray38 Values(T1 v1, T2 v2, - T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, - T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, - T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, - T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, - T37 v37, T38 v38) { - return internal::ValueArray38(v1, v2, - v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, - v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, - v33, v34, v35, v36, v37, v38); -} - -template -internal::ValueArray39 Values(T1 v1, T2 v2, - T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, - T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, - T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, - T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, - T37 v37, T38 v38, T39 v39) { - return internal::ValueArray39(v1, - v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, - v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, - v32, v33, v34, v35, v36, v37, v38, v39); -} - -template -internal::ValueArray40 Values(T1 v1, - T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, - T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, - T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, - T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, - T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) { - return internal::ValueArray40(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, - v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, - v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40); -} - -template -internal::ValueArray41 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41) { - return internal::ValueArray41(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, - v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, - v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41); -} - -template -internal::ValueArray42 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42) { - return internal::ValueArray42(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, - v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, - v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, - v42); -} - -template -internal::ValueArray43 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43) { - return internal::ValueArray43(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, - v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, - v41, v42, v43); -} - -template -internal::ValueArray44 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44) { - return internal::ValueArray44(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, - v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, - v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, - v40, v41, v42, v43, v44); -} - -template -internal::ValueArray45 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, - T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, - T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, - T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, - T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, - T41 v41, T42 v42, T43 v43, T44 v44, T45 v45) { - return internal::ValueArray45(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, - v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, - v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, - v39, v40, v41, v42, v43, v44, v45); -} - -template -internal::ValueArray46 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, - T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, - T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, - T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) { - return internal::ValueArray46(v1, v2, v3, v4, v5, v6, v7, v8, v9, - v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, - v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, - v38, v39, v40, v41, v42, v43, v44, v45, v46); -} - -template -internal::ValueArray47 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, - T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, - T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, - T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) { - return internal::ValueArray47(v1, v2, v3, v4, v5, v6, v7, v8, - v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, - v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, - v38, v39, v40, v41, v42, v43, v44, v45, v46, v47); -} - -template -internal::ValueArray48 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, - T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, - T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, - T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, - T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, - T48 v48) { - return internal::ValueArray48(v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, - v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, - v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48); -} - -template -internal::ValueArray49 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, - T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, - T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, - T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, - T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, - T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, - T47 v47, T48 v48, T49 v49) { - return internal::ValueArray49(v1, v2, v3, v4, v5, v6, - v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, - v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, - v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49); -} - -template -internal::ValueArray50 Values(T1 v1, T2 v2, T3 v3, T4 v4, - T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, - T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, - T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, - T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, - T38 v38, T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, - T46 v46, T47 v47, T48 v48, T49 v49, T50 v50) { - return internal::ValueArray50(v1, v2, v3, v4, - v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, - v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, - v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, - v48, v49, v50); -} - -// Bool() allows generating tests with parameters in a set of (false, true). -// -// Synopsis: -// Bool() -// - returns a generator producing sequences with elements {false, true}. -// -// It is useful when testing code that depends on Boolean flags. Combinations -// of multiple flags can be tested when several Bool()'s are combined using -// Combine() function. -// -// In the following example all tests in the test case FlagDependentTest -// will be instantiated twice with parameters false and true. -// -// class FlagDependentTest : public testing::TestWithParam { -// virtual void SetUp() { -// external_flag = GetParam(); -// } -// } -// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool()); -// -inline internal::ParamGenerator Bool() { - return Values(false, true); -} - -#if GTEST_HAS_COMBINE -// Combine() allows the user to combine two or more sequences to produce -// values of a Cartesian product of those sequences' elements. -// -// Synopsis: -// Combine(gen1, gen2, ..., genN) -// - returns a generator producing sequences with elements coming from -// the Cartesian product of elements from the sequences generated by -// gen1, gen2, ..., genN. The sequence elements will have a type of -// tuple where T1, T2, ..., TN are the types -// of elements from sequences produces by gen1, gen2, ..., genN. -// -// Combine can have up to 10 arguments. This number is currently limited -// by the maximum number of elements in the tuple implementation used by Google -// Test. -// -// Example: -// -// This will instantiate tests in test case AnimalTest each one with -// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), -// tuple("dog", BLACK), and tuple("dog", WHITE): -// -// enum Color { BLACK, GRAY, WHITE }; -// class AnimalTest -// : public testing::TestWithParam > {...}; -// -// TEST_P(AnimalTest, AnimalLooksNice) {...} -// -// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest, -// Combine(Values("cat", "dog"), -// Values(BLACK, WHITE))); -// -// This will instantiate tests in FlagDependentTest with all variations of two -// Boolean flags: -// -// class FlagDependentTest -// : public testing::TestWithParam > { -// virtual void SetUp() { -// // Assigns external_flag_1 and external_flag_2 values from the tuple. -// tie(external_flag_1, external_flag_2) = GetParam(); -// } -// }; -// -// TEST_P(FlagDependentTest, TestFeature1) { -// // Test your code using external_flag_1 and external_flag_2 here. -// } -// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest, -// Combine(Bool(), Bool())); -// -template -internal::CartesianProductHolder2 Combine( - const Generator1& g1, const Generator2& g2) { - return internal::CartesianProductHolder2( - g1, g2); -} - -template -internal::CartesianProductHolder3 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3) { - return internal::CartesianProductHolder3( - g1, g2, g3); -} - -template -internal::CartesianProductHolder4 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4) { - return internal::CartesianProductHolder4( - g1, g2, g3, g4); -} - -template -internal::CartesianProductHolder5 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4, const Generator5& g5) { - return internal::CartesianProductHolder5( - g1, g2, g3, g4, g5); -} - -template -internal::CartesianProductHolder6 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4, const Generator5& g5, const Generator6& g6) { - return internal::CartesianProductHolder6( - g1, g2, g3, g4, g5, g6); -} - -template -internal::CartesianProductHolder7 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4, const Generator5& g5, const Generator6& g6, - const Generator7& g7) { - return internal::CartesianProductHolder7( - g1, g2, g3, g4, g5, g6, g7); -} - -template -internal::CartesianProductHolder8 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4, const Generator5& g5, const Generator6& g6, - const Generator7& g7, const Generator8& g8) { - return internal::CartesianProductHolder8( - g1, g2, g3, g4, g5, g6, g7, g8); -} - -template -internal::CartesianProductHolder9 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4, const Generator5& g5, const Generator6& g6, - const Generator7& g7, const Generator8& g8, const Generator9& g9) { - return internal::CartesianProductHolder9( - g1, g2, g3, g4, g5, g6, g7, g8, g9); -} - -template -internal::CartesianProductHolder10 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4, const Generator5& g5, const Generator6& g6, - const Generator7& g7, const Generator8& g8, const Generator9& g9, - const Generator10& g10) { - return internal::CartesianProductHolder10( - g1, g2, g3, g4, g5, g6, g7, g8, g9, g10); -} -#endif // GTEST_HAS_COMBINE - - - -#define TEST_P(test_case_name, test_name) \ - class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ - : public test_case_name { \ - public: \ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \ - virtual void TestBody(); \ - private: \ - static int AddToRegistry() { \ - ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ - GetTestCasePatternHolder(\ - #test_case_name, __FILE__, __LINE__)->AddTestPattern(\ - #test_case_name, \ - #test_name, \ - new ::testing::internal::TestMetaFactory< \ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \ - return 0; \ - } \ - static int gtest_registering_dummy_; \ - GTEST_DISALLOW_COPY_AND_ASSIGN_(\ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \ - }; \ - int GTEST_TEST_CLASS_NAME_(test_case_name, \ - test_name)::gtest_registering_dummy_ = \ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \ - void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() - -#define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \ - ::testing::internal::ParamGenerator \ - gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \ - int gtest_##prefix##test_case_name##_dummy_ = \ - ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ - GetTestCasePatternHolder(\ - #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\ - #prefix, \ - >est_##prefix##test_case_name##_EvalGenerator_, \ - __FILE__, __LINE__) - -} // namespace testing - -#endif // GTEST_HAS_PARAM_TEST - -#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ -// Copyright 2006, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: wan@google.com (Zhanyong Wan) -// -// Google C++ Testing Framework definitions useful in production code. - -#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_ -#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_ - -// When you need to test the private or protected members of a class, -// use the FRIEND_TEST macro to declare your tests as friends of the -// class. For example: -// -// class MyClass { -// private: -// void MyMethod(); -// FRIEND_TEST(MyClassTest, MyMethod); -// }; -// -// class MyClassTest : public testing::Test { -// // ... -// }; -// -// TEST_F(MyClassTest, MyMethod) { -// // Can call MyClass::MyMethod() here. -// } - -#define FRIEND_TEST(test_case_name, test_name)\ -friend class test_case_name##_##test_name##_Test - -#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_ -// Copyright 2008, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: mheule@google.com (Markus Heule) -// - -#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ -#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ - -#include -#include - -namespace testing { - -// A copyable object representing the result of a test part (i.e. an -// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). -// -// Don't inherit from TestPartResult as its destructor is not virtual. -class GTEST_API_ TestPartResult { - public: - // The possible outcomes of a test part (i.e. an assertion or an - // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). - enum Type { - kSuccess, // Succeeded. - kNonFatalFailure, // Failed but the test can continue. - kFatalFailure // Failed and the test should be terminated. - }; - - // C'tor. TestPartResult does NOT have a default constructor. - // Always use this constructor (with parameters) to create a - // TestPartResult object. - TestPartResult(Type a_type, - const char* a_file_name, - int a_line_number, - const char* a_message) - : type_(a_type), - file_name_(a_file_name), - line_number_(a_line_number), - summary_(ExtractSummary(a_message)), - message_(a_message) { - } - - // Gets the outcome of the test part. - Type type() const { return type_; } - - // Gets the name of the source file where the test part took place, or - // NULL if it's unknown. - const char* file_name() const { return file_name_.c_str(); } - - // Gets the line in the source file where the test part took place, - // or -1 if it's unknown. - int line_number() const { return line_number_; } - - // Gets the summary of the failure message. - const char* summary() const { return summary_.c_str(); } - - // Gets the message associated with the test part. - const char* message() const { return message_.c_str(); } - - // Returns true iff the test part passed. - bool passed() const { return type_ == kSuccess; } - - // Returns true iff the test part failed. - bool failed() const { return type_ != kSuccess; } - - // Returns true iff the test part non-fatally failed. - bool nonfatally_failed() const { return type_ == kNonFatalFailure; } - - // Returns true iff the test part fatally failed. - bool fatally_failed() const { return type_ == kFatalFailure; } - private: - Type type_; - - // Gets the summary of the failure message by omitting the stack - // trace in it. - static internal::String ExtractSummary(const char* message); - - // The name of the source file where the test part took place, or - // NULL if the source file is unknown. - internal::String file_name_; - // The line in the source file where the test part took place, or -1 - // if the line number is unknown. - int line_number_; - internal::String summary_; // The test failure summary. - internal::String message_; // The test failure message. -}; - -// Prints a TestPartResult object. -std::ostream& operator<<(std::ostream& os, const TestPartResult& result); - -// An array of TestPartResult objects. -// -// Don't inherit from TestPartResultArray as its destructor is not -// virtual. -class GTEST_API_ TestPartResultArray { - public: - TestPartResultArray() {} - - // Appends the given TestPartResult to the array. - void Append(const TestPartResult& result); - - // Returns the TestPartResult at the given index (0-based). - const TestPartResult& GetTestPartResult(int index) const; - - // Returns the number of TestPartResult objects in the array. - int size() const; - - private: - std::vector array_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray); -}; - -// This interface knows how to report a test part result. -class TestPartResultReporterInterface { - public: - virtual ~TestPartResultReporterInterface() {} - - virtual void ReportTestPartResult(const TestPartResult& result) = 0; -}; - -namespace internal { - -// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a -// statement generates new fatal failures. To do so it registers itself as the -// current test part result reporter. Besides checking if fatal failures were -// reported, it only delegates the reporting to the former result reporter. -// The original result reporter is restored in the destructor. -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -class GTEST_API_ HasNewFatalFailureHelper - : public TestPartResultReporterInterface { - public: - HasNewFatalFailureHelper(); - virtual ~HasNewFatalFailureHelper(); - virtual void ReportTestPartResult(const TestPartResult& result); - bool has_new_fatal_failure() const { return has_new_fatal_failure_; } - private: - bool has_new_fatal_failure_; - TestPartResultReporterInterface* original_reporter_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper); -}; - -} // namespace internal - -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ -// Copyright 2008 Google Inc. -// All Rights Reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: wan@google.com (Zhanyong Wan) - -#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ -#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ - -// This header implements typed tests and type-parameterized tests. - -// Typed (aka type-driven) tests repeat the same test for types in a -// list. You must know which types you want to test with when writing -// typed tests. Here's how you do it: - -#if 0 - -// First, define a fixture class template. It should be parameterized -// by a type. Remember to derive it from testing::Test. -template -class FooTest : public testing::Test { - public: - ... - typedef std::list List; - static T shared_; - T value_; -}; - -// Next, associate a list of types with the test case, which will be -// repeated for each type in the list. The typedef is necessary for -// the macro to parse correctly. -typedef testing::Types MyTypes; -TYPED_TEST_CASE(FooTest, MyTypes); - -// If the type list contains only one type, you can write that type -// directly without Types<...>: -// TYPED_TEST_CASE(FooTest, int); - -// Then, use TYPED_TEST() instead of TEST_F() to define as many typed -// tests for this test case as you want. -TYPED_TEST(FooTest, DoesBlah) { - // Inside a test, refer to TypeParam to get the type parameter. - // Since we are inside a derived class template, C++ requires use to - // visit the members of FooTest via 'this'. - TypeParam n = this->value_; - - // To visit static members of the fixture, add the TestFixture:: - // prefix. - n += TestFixture::shared_; - - // To refer to typedefs in the fixture, add the "typename - // TestFixture::" prefix. - typename TestFixture::List values; - values.push_back(n); - ... -} - -TYPED_TEST(FooTest, HasPropertyA) { ... } - -#endif // 0 - -// Type-parameterized tests are abstract test patterns parameterized -// by a type. Compared with typed tests, type-parameterized tests -// allow you to define the test pattern without knowing what the type -// parameters are. The defined pattern can be instantiated with -// different types any number of times, in any number of translation -// units. -// -// If you are designing an interface or concept, you can define a -// suite of type-parameterized tests to verify properties that any -// valid implementation of the interface/concept should have. Then, -// each implementation can easily instantiate the test suite to verify -// that it conforms to the requirements, without having to write -// similar tests repeatedly. Here's an example: - -#if 0 - -// First, define a fixture class template. It should be parameterized -// by a type. Remember to derive it from testing::Test. -template -class FooTest : public testing::Test { - ... -}; - -// Next, declare that you will define a type-parameterized test case -// (the _P suffix is for "parameterized" or "pattern", whichever you -// prefer): -TYPED_TEST_CASE_P(FooTest); - -// Then, use TYPED_TEST_P() to define as many type-parameterized tests -// for this type-parameterized test case as you want. -TYPED_TEST_P(FooTest, DoesBlah) { - // Inside a test, refer to TypeParam to get the type parameter. - TypeParam n = 0; - ... -} - -TYPED_TEST_P(FooTest, HasPropertyA) { ... } - -// Now the tricky part: you need to register all test patterns before -// you can instantiate them. The first argument of the macro is the -// test case name; the rest are the names of the tests in this test -// case. -REGISTER_TYPED_TEST_CASE_P(FooTest, - DoesBlah, HasPropertyA); - -// Finally, you are free to instantiate the pattern with the types you -// want. If you put the above code in a header file, you can #include -// it in multiple C++ source files and instantiate it multiple times. -// -// To distinguish different instances of the pattern, the first -// argument to the INSTANTIATE_* macro is a prefix that will be added -// to the actual test case name. Remember to pick unique prefixes for -// different instances. -typedef testing::Types MyTypes; -INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); - -// If the type list contains only one type, you can write that type -// directly without Types<...>: -// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int); - -#endif // 0 - - -// Implements typed tests. - -#if GTEST_HAS_TYPED_TEST - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Expands to the name of the typedef for the type parameters of the -// given test case. -#define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ - -// The 'Types' template argument below must have spaces around it -// since some compilers may choke on '>>' when passing a template -// instance (e.g. Types) -#define TYPED_TEST_CASE(CaseName, Types) \ - typedef ::testing::internal::TypeList< Types >::type \ - GTEST_TYPE_PARAMS_(CaseName) - -#define TYPED_TEST(CaseName, TestName) \ - template \ - class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ - : public CaseName { \ - private: \ - typedef CaseName TestFixture; \ - typedef gtest_TypeParam_ TypeParam; \ - virtual void TestBody(); \ - }; \ - bool gtest_##CaseName##_##TestName##_registered_ = \ - ::testing::internal::TypeParameterizedTest< \ - CaseName, \ - ::testing::internal::TemplateSel< \ - GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \ - GTEST_TYPE_PARAMS_(CaseName)>::Register(\ - "", #CaseName, #TestName, 0); \ - template \ - void GTEST_TEST_CLASS_NAME_(CaseName, TestName)::TestBody() - -#endif // GTEST_HAS_TYPED_TEST - -// Implements type-parameterized tests. - -#if GTEST_HAS_TYPED_TEST_P - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Expands to the namespace name that the type-parameterized tests for -// the given type-parameterized test case are defined in. The exact -// name of the namespace is subject to change without notice. -#define GTEST_CASE_NAMESPACE_(TestCaseName) \ - gtest_case_##TestCaseName##_ - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Expands to the name of the variable used to remember the names of -// the defined tests in the given test case. -#define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \ - gtest_typed_test_case_p_state_##TestCaseName##_ - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. -// -// Expands to the name of the variable used to remember the names of -// the registered tests in the given test case. -#define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \ - gtest_registered_test_names_##TestCaseName##_ - -// The variables defined in the type-parameterized test macros are -// static as typically these macros are used in a .h file that can be -// #included in multiple translation units linked together. -#define TYPED_TEST_CASE_P(CaseName) \ - static ::testing::internal::TypedTestCasePState \ - GTEST_TYPED_TEST_CASE_P_STATE_(CaseName) - -#define TYPED_TEST_P(CaseName, TestName) \ - namespace GTEST_CASE_NAMESPACE_(CaseName) { \ - template \ - class TestName : public CaseName { \ - private: \ - typedef CaseName TestFixture; \ - typedef gtest_TypeParam_ TypeParam; \ - virtual void TestBody(); \ - }; \ - static bool gtest_##TestName##_defined_ = \ - GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\ - __FILE__, __LINE__, #CaseName, #TestName); \ - } \ - template \ - void GTEST_CASE_NAMESPACE_(CaseName)::TestName::TestBody() - -#define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \ - namespace GTEST_CASE_NAMESPACE_(CaseName) { \ - typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \ - } \ - static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \ - GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\ - __FILE__, __LINE__, #__VA_ARGS__) - -// The 'Types' template argument below must have spaces around it -// since some compilers may choke on '>>' when passing a template -// instance (e.g. Types) -#define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \ - bool gtest_##Prefix##_##CaseName = \ - ::testing::internal::TypeParameterizedTestCase::type>::Register(\ - #Prefix, #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName)) - -#endif // GTEST_HAS_TYPED_TEST_P - -#endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ - -// Depending on the platform, different string classes are available. -// On Linux, in addition to ::std::string, Google also makes use of -// class ::string, which has the same interface as ::std::string, but -// has a different implementation. -// -// The user can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that -// ::string is available AND is a distinct type to ::std::string, or -// define it to 0 to indicate otherwise. -// -// If the user's ::std::string and ::string are the same class due to -// aliasing, he should define GTEST_HAS_GLOBAL_STRING to 0. -// -// If the user doesn't define GTEST_HAS_GLOBAL_STRING, it is defined -// heuristically. - -namespace testing { - -// Declares the flags. - -// This flag temporary enables the disabled tests. -GTEST_DECLARE_bool_(also_run_disabled_tests); - -// This flag brings the debugger on an assertion failure. -GTEST_DECLARE_bool_(break_on_failure); - -// This flag controls whether Google Test catches all test-thrown exceptions -// and logs them as failures. -GTEST_DECLARE_bool_(catch_exceptions); - -// This flag enables using colors in terminal output. Available values are -// "yes" to enable colors, "no" (disable colors), or "auto" (the default) -// to let Google Test decide. -GTEST_DECLARE_string_(color); - -// This flag sets up the filter to select by name using a glob pattern -// the tests to run. If the filter is not given all tests are executed. -GTEST_DECLARE_string_(filter); - -// This flag causes the Google Test to list tests. None of the tests listed -// are actually run if the flag is provided. -GTEST_DECLARE_bool_(list_tests); - -// This flag controls whether Google Test emits a detailed XML report to a file -// in addition to its normal textual output. -GTEST_DECLARE_string_(output); - -// This flags control whether Google Test prints the elapsed time for each -// test. -GTEST_DECLARE_bool_(print_time); - -// This flag specifies the random number seed. -GTEST_DECLARE_int32_(random_seed); - -// This flag sets how many times the tests are repeated. The default value -// is 1. If the value is -1 the tests are repeating forever. -GTEST_DECLARE_int32_(repeat); - -// This flag controls whether Google Test includes Google Test internal -// stack frames in failure stack traces. -GTEST_DECLARE_bool_(show_internal_stack_frames); - -// When this flag is specified, tests' order is randomized on every iteration. -GTEST_DECLARE_bool_(shuffle); - -// This flag specifies the maximum number of stack frames to be -// printed in a failure message. -GTEST_DECLARE_int32_(stack_trace_depth); - -// When this flag is specified, a failed assertion will throw an -// exception if exceptions are enabled, or exit the program with a -// non-zero code otherwise. -GTEST_DECLARE_bool_(throw_on_failure); - -// The upper limit for valid stack trace depths. -const int kMaxStackTraceDepth = 100; - -namespace internal { - -class AssertHelper; -class DefaultGlobalTestPartResultReporter; -class ExecDeathTest; -class NoExecDeathTest; -class FinalSuccessChecker; -class GTestFlagSaver; -class TestInfoImpl; -class TestResultAccessor; -class TestEventListenersAccessor; -class TestEventRepeater; -class WindowsDeathTest; -class UnitTestImpl* GetUnitTestImpl(); -void ReportFailureInUnknownLocation(TestPartResult::Type result_type, - const String& message); -class PrettyUnitTestResultPrinter; -class XmlUnitTestResultPrinter; - -// Converts a streamable value to a String. A NULL pointer is -// converted to "(null)". When the input value is a ::string, -// ::std::string, ::wstring, or ::std::wstring object, each NUL -// character in it is replaced with "\\0". -// Declared in gtest-internal.h but defined here, so that it has access -// to the definition of the Message class, required by the ARM -// compiler. -template -String StreamableToString(const T& streamable) { - return (Message() << streamable).GetString(); -} - -} // namespace internal - -// A class for indicating whether an assertion was successful. When -// the assertion wasn't successful, the AssertionResult object -// remembers a non-empty message that describes how it failed. -// -// To create an instance of this class, use one of the factory functions -// (AssertionSuccess() and AssertionFailure()). -// -// This class is useful for two purposes: -// 1. Defining predicate functions to be used with Boolean test assertions -// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts -// 2. Defining predicate-format functions to be -// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). -// -// For example, if you define IsEven predicate: -// -// testing::AssertionResult IsEven(int n) { -// if ((n % 2) == 0) -// return testing::AssertionSuccess(); -// else -// return testing::AssertionFailure() << n << " is odd"; -// } -// -// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5))) -// will print the message -// -// Value of: IsEven(Fib(5)) -// Actual: false (5 is odd) -// Expected: true -// -// instead of a more opaque -// -// Value of: IsEven(Fib(5)) -// Actual: false -// Expected: true -// -// in case IsEven is a simple Boolean predicate. -// -// If you expect your predicate to be reused and want to support informative -// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up -// about half as often as positive ones in our tests), supply messages for -// both success and failure cases: -// -// testing::AssertionResult IsEven(int n) { -// if ((n % 2) == 0) -// return testing::AssertionSuccess() << n << " is even"; -// else -// return testing::AssertionFailure() << n << " is odd"; -// } -// -// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print -// -// Value of: IsEven(Fib(6)) -// Actual: true (8 is even) -// Expected: false -// -// NB: Predicates that support negative Boolean assertions have reduced -// performance in positive ones so be careful not to use them in tests -// that have lots (tens of thousands) of positive Boolean assertions. -// -// To use this class with EXPECT_PRED_FORMAT assertions such as: -// -// // Verifies that Foo() returns an even number. -// EXPECT_PRED_FORMAT1(IsEven, Foo()); -// -// you need to define: -// -// testing::AssertionResult IsEven(const char* expr, int n) { -// if ((n % 2) == 0) -// return testing::AssertionSuccess(); -// else -// return testing::AssertionFailure() -// << "Expected: " << expr << " is even\n Actual: it's " << n; -// } -// -// If Foo() returns 5, you will see the following message: -// -// Expected: Foo() is even -// Actual: it's 5 -// -class GTEST_API_ AssertionResult { - public: - // Copy constructor. - // Used in EXPECT_TRUE/FALSE(assertion_result). - AssertionResult(const AssertionResult& other); - // Used in the EXPECT_TRUE/FALSE(bool_expression). - explicit AssertionResult(bool success) : success_(success) {} - - // Returns true iff the assertion succeeded. - operator bool() const { return success_; } // NOLINT - - // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. - AssertionResult operator!() const; - - // Returns the text streamed into this AssertionResult. Test assertions - // use it when they fail (i.e., the predicate's outcome doesn't match the - // assertion's expectation). When nothing has been streamed into the - // object, returns an empty string. - const char* message() const { - return message_.get() != NULL && message_->c_str() != NULL ? - message_->c_str() : ""; - } - // TODO(vladl@google.com): Remove this after making sure no clients use it. - // Deprecated; please use message() instead. - const char* failure_message() const { return message(); } - - // Streams a custom failure message into this object. - template AssertionResult& operator<<(const T& value); - - private: - // No implementation - we want AssertionResult to be - // copy-constructible but not assignable. - void operator=(const AssertionResult& other); - - // Stores result of the assertion predicate. - bool success_; - // Stores the message describing the condition in case the expectation - // construct is not satisfied with the predicate's outcome. - // Referenced via a pointer to avoid taking too much stack frame space - // with test assertions. - internal::scoped_ptr message_; -}; // class AssertionResult - -// Streams a custom failure message into this object. -template -AssertionResult& AssertionResult::operator<<(const T& value) { - Message msg; - if (message_.get() != NULL) - msg << *message_; - msg << value; - message_.reset(new internal::String(msg.GetString())); - return *this; -} - -// Makes a successful assertion result. -GTEST_API_ AssertionResult AssertionSuccess(); - -// Makes a failed assertion result. -GTEST_API_ AssertionResult AssertionFailure(); - -// Makes a failed assertion result with the given failure message. -// Deprecated; use AssertionFailure() << msg. -GTEST_API_ AssertionResult AssertionFailure(const Message& msg); - -// The abstract class that all tests inherit from. -// -// In Google Test, a unit test program contains one or many TestCases, and -// each TestCase contains one or many Tests. -// -// When you define a test using the TEST macro, you don't need to -// explicitly derive from Test - the TEST macro automatically does -// this for you. -// -// The only time you derive from Test is when defining a test fixture -// to be used a TEST_F. For example: -// -// class FooTest : public testing::Test { -// protected: -// virtual void SetUp() { ... } -// virtual void TearDown() { ... } -// ... -// }; -// -// TEST_F(FooTest, Bar) { ... } -// TEST_F(FooTest, Baz) { ... } -// -// Test is not copyable. -class GTEST_API_ Test { - public: - friend class internal::TestInfoImpl; - - // Defines types for pointers to functions that set up and tear down - // a test case. - typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc; - typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc; - - // The d'tor is virtual as we intend to inherit from Test. - virtual ~Test(); - - // Sets up the stuff shared by all tests in this test case. - // - // Google Test will call Foo::SetUpTestCase() before running the first - // test in test case Foo. Hence a sub-class can define its own - // SetUpTestCase() method to shadow the one defined in the super - // class. - static void SetUpTestCase() {} - - // Tears down the stuff shared by all tests in this test case. - // - // Google Test will call Foo::TearDownTestCase() after running the last - // test in test case Foo. Hence a sub-class can define its own - // TearDownTestCase() method to shadow the one defined in the super - // class. - static void TearDownTestCase() {} - - // Returns true iff the current test has a fatal failure. - static bool HasFatalFailure(); - - // Returns true iff the current test has a non-fatal failure. - static bool HasNonfatalFailure(); - - // Returns true iff the current test has a (either fatal or - // non-fatal) failure. - static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } - - // Logs a property for the current test. Only the last value for a given - // key is remembered. - // These are public static so they can be called from utility functions - // that are not members of the test fixture. - // The arguments are const char* instead strings, as Google Test is used - // on platforms where string doesn't compile. - // - // Note that a driving consideration for these RecordProperty methods - // was to produce xml output suited to the Greenspan charting utility, - // which at present will only chart values that fit in a 32-bit int. It - // is the user's responsibility to restrict their values to 32-bit ints - // if they intend them to be used with Greenspan. - static void RecordProperty(const char* key, const char* value); - static void RecordProperty(const char* key, int value); - - protected: - // Creates a Test object. - Test(); - - // Sets up the test fixture. - virtual void SetUp(); - - // Tears down the test fixture. - virtual void TearDown(); - - private: - // Returns true iff the current test has the same fixture class as - // the first test in the current test case. - static bool HasSameFixtureClass(); - - // Runs the test after the test fixture has been set up. - // - // A sub-class must implement this to define the test logic. - // - // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. - // Instead, use the TEST or TEST_F macro. - virtual void TestBody() = 0; - - // Sets up, executes, and tears down the test. - void Run(); - - // Uses a GTestFlagSaver to save and restore all Google Test flags. - const internal::GTestFlagSaver* const gtest_flag_saver_; - - // Often a user mis-spells SetUp() as Setup() and spends a long time - // wondering why it is never called by Google Test. The declaration of - // the following method is solely for catching such an error at - // compile time: - // - // - The return type is deliberately chosen to be not void, so it - // will be a conflict if a user declares void Setup() in his test - // fixture. - // - // - This method is private, so it will be another compiler error - // if a user calls it from his test fixture. - // - // DO NOT OVERRIDE THIS FUNCTION. - // - // If you see an error about overriding the following function or - // about it being private, you have mis-spelled SetUp() as Setup(). - struct Setup_should_be_spelled_SetUp {}; - virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } - - // We disallow copying Tests. - GTEST_DISALLOW_COPY_AND_ASSIGN_(Test); -}; - -typedef internal::TimeInMillis TimeInMillis; - -// A copyable object representing a user specified test property which can be -// output as a key/value string pair. -// -// Don't inherit from TestProperty as its destructor is not virtual. -class TestProperty { - public: - // C'tor. TestProperty does NOT have a default constructor. - // Always use this constructor (with parameters) to create a - // TestProperty object. - TestProperty(const char* a_key, const char* a_value) : - key_(a_key), value_(a_value) { - } - - // Gets the user supplied key. - const char* key() const { - return key_.c_str(); - } - - // Gets the user supplied value. - const char* value() const { - return value_.c_str(); - } - - // Sets a new value, overriding the one supplied in the constructor. - void SetValue(const char* new_value) { - value_ = new_value; - } - - private: - // The key supplied by the user. - internal::String key_; - // The value supplied by the user. - internal::String value_; -}; - -// The result of a single Test. This includes a list of -// TestPartResults, a list of TestProperties, a count of how many -// death tests there are in the Test, and how much time it took to run -// the Test. -// -// TestResult is not copyable. -class GTEST_API_ TestResult { - public: - // Creates an empty TestResult. - TestResult(); - - // D'tor. Do not inherit from TestResult. - ~TestResult(); - - // Gets the number of all test parts. This is the sum of the number - // of successful test parts and the number of failed test parts. - int total_part_count() const; - - // Returns the number of the test properties. - int test_property_count() const; - - // Returns true iff the test passed (i.e. no test part failed). - bool Passed() const { return !Failed(); } - - // Returns true iff the test failed. - bool Failed() const; - - // Returns true iff the test fatally failed. - bool HasFatalFailure() const; - - // Returns true iff the test has a non-fatal failure. - bool HasNonfatalFailure() const; - - // Returns the elapsed time, in milliseconds. - TimeInMillis elapsed_time() const { return elapsed_time_; } - - // Returns the i-th test part result among all the results. i can range - // from 0 to test_property_count() - 1. If i is not in that range, aborts - // the program. - const TestPartResult& GetTestPartResult(int i) const; - - // Returns the i-th test property. i can range from 0 to - // test_property_count() - 1. If i is not in that range, aborts the - // program. - const TestProperty& GetTestProperty(int i) const; - - private: - friend class TestInfo; - friend class UnitTest; - friend class internal::DefaultGlobalTestPartResultReporter; - friend class internal::ExecDeathTest; - friend class internal::TestInfoImpl; - friend class internal::TestResultAccessor; - friend class internal::UnitTestImpl; - friend class internal::WindowsDeathTest; - - // Gets the vector of TestPartResults. - const std::vector& test_part_results() const { - return test_part_results_; - } - - // Gets the vector of TestProperties. - const std::vector& test_properties() const { - return test_properties_; - } - - // Sets the elapsed time. - void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } - - // Adds a test property to the list. The property is validated and may add - // a non-fatal failure if invalid (e.g., if it conflicts with reserved - // key names). If a property is already recorded for the same key, the - // value will be updated, rather than storing multiple values for the same - // key. - void RecordProperty(const TestProperty& test_property); - - // Adds a failure if the key is a reserved attribute of Google Test - // testcase tags. Returns true if the property is valid. - // TODO(russr): Validate attribute names are legal and human readable. - static bool ValidateTestProperty(const TestProperty& test_property); - - // Adds a test part result to the list. - void AddTestPartResult(const TestPartResult& test_part_result); - - // Returns the death test count. - int death_test_count() const { return death_test_count_; } - - // Increments the death test count, returning the new count. - int increment_death_test_count() { return ++death_test_count_; } - - // Clears the test part results. - void ClearTestPartResults(); - - // Clears the object. - void Clear(); - - // Protects mutable state of the property vector and of owned - // properties, whose values may be updated. - internal::Mutex test_properites_mutex_; - - // The vector of TestPartResults - std::vector test_part_results_; - // The vector of TestProperties - std::vector test_properties_; - // Running count of death tests. - int death_test_count_; - // The elapsed time, in milliseconds. - TimeInMillis elapsed_time_; - - // We disallow copying TestResult. - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult); -}; // class TestResult - -// A TestInfo object stores the following information about a test: -// -// Test case name -// Test name -// Whether the test should be run -// A function pointer that creates the test object when invoked -// Test result -// -// The constructor of TestInfo registers itself with the UnitTest -// singleton such that the RUN_ALL_TESTS() macro knows which tests to -// run. -class GTEST_API_ TestInfo { - public: - // Destructs a TestInfo object. This function is not virtual, so - // don't inherit from TestInfo. - ~TestInfo(); - - // Returns the test case name. - const char* test_case_name() const; - - // Returns the test name. - const char* name() const; - - // Returns the test case comment. - const char* test_case_comment() const; - - // Returns the test comment. - const char* comment() const; - - // Returns true if this test should run, that is if the test is not disabled - // (or it is disabled but the also_run_disabled_tests flag has been specified) - // and its full name matches the user-specified filter. - // - // Google Test allows the user to filter the tests by their full names. - // The full name of a test Bar in test case Foo is defined as - // "Foo.Bar". Only the tests that match the filter will run. - // - // A filter is a colon-separated list of glob (not regex) patterns, - // optionally followed by a '-' and a colon-separated list of - // negative patterns (tests to exclude). A test is run if it - // matches one of the positive patterns and does not match any of - // the negative patterns. - // - // For example, *A*:Foo.* is a filter that matches any string that - // contains the character 'A' or starts with "Foo.". - bool should_run() const; - - // Returns the result of the test. - const TestResult* result() const; - - private: -#if GTEST_HAS_DEATH_TEST - friend class internal::DefaultDeathTestFactory; -#endif // GTEST_HAS_DEATH_TEST - friend class Test; - friend class TestCase; - friend class internal::TestInfoImpl; - friend class internal::UnitTestImpl; - friend TestInfo* internal::MakeAndRegisterTestInfo( - const char* test_case_name, const char* name, - const char* test_case_comment, const char* comment, - internal::TypeId fixture_class_id, - Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc, - internal::TestFactoryBase* factory); - - // Returns true if this test matches the user-specified filter. - bool matches_filter() const; - - // Increments the number of death tests encountered in this test so - // far. - int increment_death_test_count(); - - // Accessors for the implementation object. - internal::TestInfoImpl* impl() { return impl_; } - const internal::TestInfoImpl* impl() const { return impl_; } - - // Constructs a TestInfo object. The newly constructed instance assumes - // ownership of the factory object. - TestInfo(const char* test_case_name, const char* name, - const char* test_case_comment, const char* comment, - internal::TypeId fixture_class_id, - internal::TestFactoryBase* factory); - - // An opaque implementation object. - internal::TestInfoImpl* impl_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo); -}; - -// A test case, which consists of a vector of TestInfos. -// -// TestCase is not copyable. -class GTEST_API_ TestCase { - public: - // Creates a TestCase with the given name. - // - // TestCase does NOT have a default constructor. Always use this - // constructor to create a TestCase object. - // - // Arguments: - // - // name: name of the test case - // set_up_tc: pointer to the function that sets up the test case - // tear_down_tc: pointer to the function that tears down the test case - TestCase(const char* name, const char* comment, - Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc); - - // Destructor of TestCase. - virtual ~TestCase(); - - // Gets the name of the TestCase. - const char* name() const { return name_.c_str(); } - - // Returns the test case comment. - const char* comment() const { return comment_.c_str(); } - - // Returns true if any test in this test case should run. - bool should_run() const { return should_run_; } - - // Gets the number of successful tests in this test case. - int successful_test_count() const; - - // Gets the number of failed tests in this test case. - int failed_test_count() const; - - // Gets the number of disabled tests in this test case. - int disabled_test_count() const; - - // Get the number of tests in this test case that should run. - int test_to_run_count() const; - - // Gets the number of all tests in this test case. - int total_test_count() const; - - // Returns true iff the test case passed. - bool Passed() const { return !Failed(); } - - // Returns true iff the test case failed. - bool Failed() const { return failed_test_count() > 0; } - - // Returns the elapsed time, in milliseconds. - TimeInMillis elapsed_time() const { return elapsed_time_; } - - // Returns the i-th test among all the tests. i can range from 0 to - // total_test_count() - 1. If i is not in that range, returns NULL. - const TestInfo* GetTestInfo(int i) const; - - private: - friend class Test; - friend class internal::UnitTestImpl; - - // Gets the (mutable) vector of TestInfos in this TestCase. - std::vector& test_info_list() { return test_info_list_; } - - // Gets the (immutable) vector of TestInfos in this TestCase. - const std::vector& test_info_list() const { - return test_info_list_; - } - - // Returns the i-th test among all the tests. i can range from 0 to - // total_test_count() - 1. If i is not in that range, returns NULL. - TestInfo* GetMutableTestInfo(int i); - - // Sets the should_run member. - void set_should_run(bool should) { should_run_ = should; } - - // Adds a TestInfo to this test case. Will delete the TestInfo upon - // destruction of the TestCase object. - void AddTestInfo(TestInfo * test_info); - - // Clears the results of all tests in this test case. - void ClearResult(); - - // Clears the results of all tests in the given test case. - static void ClearTestCaseResult(TestCase* test_case) { - test_case->ClearResult(); - } - - // Runs every test in this TestCase. - void Run(); - - // Returns true iff test passed. - static bool TestPassed(const TestInfo * test_info); - - // Returns true iff test failed. - static bool TestFailed(const TestInfo * test_info); - - // Returns true iff test is disabled. - static bool TestDisabled(const TestInfo * test_info); - - // Returns true if the given test should run. - static bool ShouldRunTest(const TestInfo *test_info); - - // Shuffles the tests in this test case. - void ShuffleTests(internal::Random* random); - - // Restores the test order to before the first shuffle. - void UnshuffleTests(); - - // Name of the test case. - internal::String name_; - // Comment on the test case. - internal::String comment_; - // The vector of TestInfos in their original order. It owns the - // elements in the vector. - std::vector test_info_list_; - // Provides a level of indirection for the test list to allow easy - // shuffling and restoring the test order. The i-th element in this - // vector is the index of the i-th test in the shuffled test list. - std::vector test_indices_; - // Pointer to the function that sets up the test case. - Test::SetUpTestCaseFunc set_up_tc_; - // Pointer to the function that tears down the test case. - Test::TearDownTestCaseFunc tear_down_tc_; - // True iff any test in this test case should run. - bool should_run_; - // Elapsed time, in milliseconds. - TimeInMillis elapsed_time_; - - // We disallow copying TestCases. - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase); -}; - -// An Environment object is capable of setting up and tearing down an -// environment. The user should subclass this to define his own -// environment(s). -// -// An Environment object does the set-up and tear-down in virtual -// methods SetUp() and TearDown() instead of the constructor and the -// destructor, as: -// -// 1. You cannot safely throw from a destructor. This is a problem -// as in some cases Google Test is used where exceptions are enabled, and -// we may want to implement ASSERT_* using exceptions where they are -// available. -// 2. You cannot use ASSERT_* directly in a constructor or -// destructor. -class Environment { - public: - // The d'tor is virtual as we need to subclass Environment. - virtual ~Environment() {} - - // Override this to define how to set up the environment. - virtual void SetUp() {} - - // Override this to define how to tear down the environment. - virtual void TearDown() {} - private: - // If you see an error about overriding the following function or - // about it being private, you have mis-spelled SetUp() as Setup(). - struct Setup_should_be_spelled_SetUp {}; - virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } -}; - -// The interface for tracing execution of tests. The methods are organized in -// the order the corresponding events are fired. -class TestEventListener { - public: - virtual ~TestEventListener() {} - - // Fired before any test activity starts. - virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; - - // Fired before each iteration of tests starts. There may be more than - // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration - // index, starting from 0. - virtual void OnTestIterationStart(const UnitTest& unit_test, - int iteration) = 0; - - // Fired before environment set-up for each iteration of tests starts. - virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; - - // Fired after environment set-up for each iteration of tests ends. - virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; - - // Fired before the test case starts. - virtual void OnTestCaseStart(const TestCase& test_case) = 0; - - // Fired before the test starts. - virtual void OnTestStart(const TestInfo& test_info) = 0; - - // Fired after a failed assertion or a SUCCESS(). - virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; - - // Fired after the test ends. - virtual void OnTestEnd(const TestInfo& test_info) = 0; - - // Fired after the test case ends. - virtual void OnTestCaseEnd(const TestCase& test_case) = 0; - - // Fired before environment tear-down for each iteration of tests starts. - virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; - - // Fired after environment tear-down for each iteration of tests ends. - virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0; - - // Fired after each iteration of tests finishes. - virtual void OnTestIterationEnd(const UnitTest& unit_test, - int iteration) = 0; - - // Fired after all test activities have ended. - virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; -}; - -// The convenience class for users who need to override just one or two -// methods and are not concerned that a possible change to a signature of -// the methods they override will not be caught during the build. For -// comments about each method please see the definition of TestEventListener -// above. -class EmptyTestEventListener : public TestEventListener { - public: - virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} - virtual void OnTestIterationStart(const UnitTest& /*unit_test*/, - int /*iteration*/) {} - virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) {} - virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} - virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} - virtual void OnTestStart(const TestInfo& /*test_info*/) {} - virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) {} - virtual void OnTestEnd(const TestInfo& /*test_info*/) {} - virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} - virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) {} - virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} - virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/, - int /*iteration*/) {} - virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} -}; - -// TestEventListeners lets users add listeners to track events in Google Test. -class GTEST_API_ TestEventListeners { - public: - TestEventListeners(); - ~TestEventListeners(); - - // Appends an event listener to the end of the list. Google Test assumes - // the ownership of the listener (i.e. it will delete the listener when - // the test program finishes). - void Append(TestEventListener* listener); - - // Removes the given event listener from the list and returns it. It then - // becomes the caller's responsibility to delete the listener. Returns - // NULL if the listener is not found in the list. - TestEventListener* Release(TestEventListener* listener); - - // Returns the standard listener responsible for the default console - // output. Can be removed from the listeners list to shut down default - // console output. Note that removing this object from the listener list - // with Release transfers its ownership to the caller and makes this - // function return NULL the next time. - TestEventListener* default_result_printer() const { - return default_result_printer_; - } - - // Returns the standard listener responsible for the default XML output - // controlled by the --gtest_output=xml flag. Can be removed from the - // listeners list by users who want to shut down the default XML output - // controlled by this flag and substitute it with custom one. Note that - // removing this object from the listener list with Release transfers its - // ownership to the caller and makes this function return NULL the next - // time. - TestEventListener* default_xml_generator() const { - return default_xml_generator_; - } - - private: - friend class TestCase; - friend class internal::DefaultGlobalTestPartResultReporter; - friend class internal::NoExecDeathTest; - friend class internal::TestEventListenersAccessor; - friend class internal::TestInfoImpl; - friend class internal::UnitTestImpl; - - // Returns repeater that broadcasts the TestEventListener events to all - // subscribers. - TestEventListener* repeater(); - - // Sets the default_result_printer attribute to the provided listener. - // The listener is also added to the listener list and previous - // default_result_printer is removed from it and deleted. The listener can - // also be NULL in which case it will not be added to the list. Does - // nothing if the previous and the current listener objects are the same. - void SetDefaultResultPrinter(TestEventListener* listener); - - // Sets the default_xml_generator attribute to the provided listener. The - // listener is also added to the listener list and previous - // default_xml_generator is removed from it and deleted. The listener can - // also be NULL in which case it will not be added to the list. Does - // nothing if the previous and the current listener objects are the same. - void SetDefaultXmlGenerator(TestEventListener* listener); - - // Controls whether events will be forwarded by the repeater to the - // listeners in the list. - bool EventForwardingEnabled() const; - void SuppressEventForwarding(); - - // The actual list of listeners. - internal::TestEventRepeater* repeater_; - // Listener responsible for the standard result output. - TestEventListener* default_result_printer_; - // Listener responsible for the creation of the XML output file. - TestEventListener* default_xml_generator_; - - // We disallow copying TestEventListeners. - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners); -}; - -// A UnitTest consists of a vector of TestCases. -// -// This is a singleton class. The only instance of UnitTest is -// created when UnitTest::GetInstance() is first called. This -// instance is never deleted. -// -// UnitTest is not copyable. -// -// This class is thread-safe as long as the methods are called -// according to their specification. -class GTEST_API_ UnitTest { - public: - // Gets the singleton UnitTest object. The first time this method - // is called, a UnitTest object is constructed and returned. - // Consecutive calls will return the same object. - static UnitTest* GetInstance(); - - // Runs all tests in this UnitTest object and prints the result. - // Returns 0 if successful, or 1 otherwise. - // - // This method can only be called from the main thread. - // - // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - int Run() GTEST_MUST_USE_RESULT_; - - // Returns the working directory when the first TEST() or TEST_F() - // was executed. The UnitTest object owns the string. - const char* original_working_dir() const; - - // Returns the TestCase object for the test that's currently running, - // or NULL if no test is running. - const TestCase* current_test_case() const; - - // Returns the TestInfo object for the test that's currently running, - // or NULL if no test is running. - const TestInfo* current_test_info() const; - - // Returns the random seed used at the start of the current test run. - int random_seed() const; - -#if GTEST_HAS_PARAM_TEST - // Returns the ParameterizedTestCaseRegistry object used to keep track of - // value-parameterized tests and instantiate and register them. - // - // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - internal::ParameterizedTestCaseRegistry& parameterized_test_registry(); -#endif // GTEST_HAS_PARAM_TEST - - // Gets the number of successful test cases. - int successful_test_case_count() const; - - // Gets the number of failed test cases. - int failed_test_case_count() const; - - // Gets the number of all test cases. - int total_test_case_count() const; - - // Gets the number of all test cases that contain at least one test - // that should run. - int test_case_to_run_count() const; - - // Gets the number of successful tests. - int successful_test_count() const; - - // Gets the number of failed tests. - int failed_test_count() const; - - // Gets the number of disabled tests. - int disabled_test_count() const; - - // Gets the number of all tests. - int total_test_count() const; - - // Gets the number of tests that should run. - int test_to_run_count() const; - - // Gets the elapsed time, in milliseconds. - TimeInMillis elapsed_time() const; - - // Returns true iff the unit test passed (i.e. all test cases passed). - bool Passed() const; - - // Returns true iff the unit test failed (i.e. some test case failed - // or something outside of all tests failed). - bool Failed() const; - - // Gets the i-th test case among all the test cases. i can range from 0 to - // total_test_case_count() - 1. If i is not in that range, returns NULL. - const TestCase* GetTestCase(int i) const; - - // Returns the list of event listeners that can be used to track events - // inside Google Test. - TestEventListeners& listeners(); - - private: - // Registers and returns a global test environment. When a test - // program is run, all global test environments will be set-up in - // the order they were registered. After all tests in the program - // have finished, all global test environments will be torn-down in - // the *reverse* order they were registered. - // - // The UnitTest object takes ownership of the given environment. - // - // This method can only be called from the main thread. - Environment* AddEnvironment(Environment* env); - - // Adds a TestPartResult to the current TestResult object. All - // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) - // eventually call this to report their results. The user code - // should use the assertion macros instead of calling this directly. - void AddTestPartResult(TestPartResult::Type result_type, - const char* file_name, - int line_number, - const internal::String& message, - const internal::String& os_stack_trace); - - // Adds a TestProperty to the current TestResult object. If the result already - // contains a property with the same key, the value will be updated. - void RecordPropertyForCurrentTest(const char* key, const char* value); - - // Gets the i-th test case among all the test cases. i can range from 0 to - // total_test_case_count() - 1. If i is not in that range, returns NULL. - TestCase* GetMutableTestCase(int i); - - // Accessors for the implementation object. - internal::UnitTestImpl* impl() { return impl_; } - const internal::UnitTestImpl* impl() const { return impl_; } - - // These classes and funcions are friends as they need to access private - // members of UnitTest. - friend class Test; - friend class internal::AssertHelper; - friend class internal::ScopedTrace; - friend Environment* AddGlobalTestEnvironment(Environment* env); - friend internal::UnitTestImpl* internal::GetUnitTestImpl(); - friend void internal::ReportFailureInUnknownLocation( - TestPartResult::Type result_type, - const internal::String& message); - - // Creates an empty UnitTest. - UnitTest(); - - // D'tor - virtual ~UnitTest(); - - // Pushes a trace defined by SCOPED_TRACE() on to the per-thread - // Google Test trace stack. - void PushGTestTrace(const internal::TraceInfo& trace); - - // Pops a trace from the per-thread Google Test trace stack. - void PopGTestTrace(); - - // Protects mutable state in *impl_. This is mutable as some const - // methods need to lock it too. - mutable internal::Mutex mutex_; - - // Opaque implementation object. This field is never changed once - // the object is constructed. We don't mark it as const here, as - // doing so will cause a warning in the constructor of UnitTest. - // Mutable state in *impl_ is protected by mutex_. - internal::UnitTestImpl* impl_; - - // We disallow copying UnitTest. - GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest); -}; - -// A convenient wrapper for adding an environment for the test -// program. -// -// You should call this before RUN_ALL_TESTS() is called, probably in -// main(). If you use gtest_main, you need to call this before main() -// starts for it to take effect. For example, you can define a global -// variable like this: -// -// testing::Environment* const foo_env = -// testing::AddGlobalTestEnvironment(new FooEnvironment); -// -// However, we strongly recommend you to write your own main() and -// call AddGlobalTestEnvironment() there, as relying on initialization -// of global variables makes the code harder to read and may cause -// problems when you register multiple environments from different -// translation units and the environments have dependencies among them -// (remember that the compiler doesn't guarantee the order in which -// global variables from different translation units are initialized). -inline Environment* AddGlobalTestEnvironment(Environment* env) { - return UnitTest::GetInstance()->AddEnvironment(env); -} - -// Initializes Google Test. This must be called before calling -// RUN_ALL_TESTS(). In particular, it parses a command line for the -// flags that Google Test recognizes. Whenever a Google Test flag is -// seen, it is removed from argv, and *argc is decremented. -// -// No value is returned. Instead, the Google Test flag variables are -// updated. -// -// Calling the function for the second time has no user-visible effect. -GTEST_API_ void InitGoogleTest(int* argc, char** argv); - -// This overloaded version can be used in Windows programs compiled in -// UNICODE mode. -GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); - -namespace internal { - -// These overloaded versions handle ::std::string and ::std::wstring. -GTEST_API_ inline String FormatForFailureMessage(const ::std::string& str) { - return (Message() << '"' << str << '"').GetString(); -} - -#if GTEST_HAS_STD_WSTRING -GTEST_API_ inline String FormatForFailureMessage(const ::std::wstring& wstr) { - return (Message() << "L\"" << wstr << '"').GetString(); -} -#endif // GTEST_HAS_STD_WSTRING - -// These overloaded versions handle ::string and ::wstring. -#if GTEST_HAS_GLOBAL_STRING -GTEST_API_ inline String FormatForFailureMessage(const ::string& str) { - return (Message() << '"' << str << '"').GetString(); -} -#endif // GTEST_HAS_GLOBAL_STRING - -#if GTEST_HAS_GLOBAL_WSTRING -GTEST_API_ inline String FormatForFailureMessage(const ::wstring& wstr) { - return (Message() << "L\"" << wstr << '"').GetString(); -} -#endif // GTEST_HAS_GLOBAL_WSTRING - -// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) -// operand to be used in a failure message. The type (but not value) -// of the other operand may affect the format. This allows us to -// print a char* as a raw pointer when it is compared against another -// char*, and print it as a C string when it is compared against an -// std::string object, for example. -// -// The default implementation ignores the type of the other operand. -// Some specialized versions are used to handle formatting wide or -// narrow C strings. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -template -String FormatForComparisonFailureMessage(const T1& value, - const T2& /* other_operand */) { - return FormatForFailureMessage(value); -} - -// The helper function for {ASSERT|EXPECT}_EQ. -template -AssertionResult CmpHelperEQ(const char* expected_expression, - const char* actual_expression, - const T1& expected, - const T2& actual) { -#ifdef _MSC_VER -#pragma warning(push) // Saves the current warning state. -#pragma warning(disable:4389) // Temporarily disables warning on - // signed/unsigned mismatch. -#endif - - if (expected == actual) { - return AssertionSuccess(); - } - -#ifdef _MSC_VER -#pragma warning(pop) // Restores the warning state. -#endif - - return EqFailure(expected_expression, - actual_expression, - FormatForComparisonFailureMessage(expected, actual), - FormatForComparisonFailureMessage(actual, expected), - false); -} - -// With this overloaded version, we allow anonymous enums to be used -// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums -// can be implicitly cast to BiggestInt. -GTEST_API_ AssertionResult CmpHelperEQ(const char* expected_expression, - const char* actual_expression, - BiggestInt expected, - BiggestInt actual); - -// The helper class for {ASSERT|EXPECT}_EQ. The template argument -// lhs_is_null_literal is true iff the first argument to ASSERT_EQ() -// is a null pointer literal. The following default implementation is -// for lhs_is_null_literal being false. -template -class EqHelper { - public: - // This templatized version is for the general case. - template - static AssertionResult Compare(const char* expected_expression, - const char* actual_expression, - const T1& expected, - const T2& actual) { - return CmpHelperEQ(expected_expression, actual_expression, expected, - actual); - } - - // With this overloaded version, we allow anonymous enums to be used - // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous - // enums can be implicitly cast to BiggestInt. - // - // Even though its body looks the same as the above version, we - // cannot merge the two, as it will make anonymous enums unhappy. - static AssertionResult Compare(const char* expected_expression, - const char* actual_expression, - BiggestInt expected, - BiggestInt actual) { - return CmpHelperEQ(expected_expression, actual_expression, expected, - actual); - } -}; - -// This specialization is used when the first argument to ASSERT_EQ() -// is a null pointer literal. -template <> -class EqHelper { - public: - // We define two overloaded versions of Compare(). The first - // version will be picked when the second argument to ASSERT_EQ() is - // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or - // EXPECT_EQ(false, a_bool). - template - static AssertionResult Compare(const char* expected_expression, - const char* actual_expression, - const T1& expected, - const T2& actual) { - return CmpHelperEQ(expected_expression, actual_expression, expected, - actual); - } - - // This version will be picked when the second argument to - // ASSERT_EQ() is a pointer, e.g. ASSERT_EQ(NULL, a_pointer). - template - static AssertionResult Compare(const char* expected_expression, - const char* actual_expression, - const T1& /* expected */, - T2* actual) { - // We already know that 'expected' is a null pointer. - return CmpHelperEQ(expected_expression, actual_expression, - static_cast(NULL), actual); - } -}; - -// A macro for implementing the helper functions needed to implement -// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste -// of similar code. -// -// For each templatized helper function, we also define an overloaded -// version for BiggestInt in order to reduce code bloat and allow -// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled -// with gcc 4. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ -template \ -AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ - const T1& val1, const T2& val2) {\ - if (val1 op val2) {\ - return AssertionSuccess();\ - } else {\ - Message msg;\ - msg << "Expected: (" << expr1 << ") " #op " (" << expr2\ - << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ - << " vs " << FormatForComparisonFailureMessage(val2, val1);\ - return AssertionFailure(msg);\ - }\ -}\ -GTEST_API_ AssertionResult CmpHelper##op_name(\ - const char* expr1, const char* expr2, BiggestInt val1, BiggestInt val2) - -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - -// Implements the helper function for {ASSERT|EXPECT}_NE -GTEST_IMPL_CMP_HELPER_(NE, !=); -// Implements the helper function for {ASSERT|EXPECT}_LE -GTEST_IMPL_CMP_HELPER_(LE, <=); -// Implements the helper function for {ASSERT|EXPECT}_LT -GTEST_IMPL_CMP_HELPER_(LT, < ); -// Implements the helper function for {ASSERT|EXPECT}_GE -GTEST_IMPL_CMP_HELPER_(GE, >=); -// Implements the helper function for {ASSERT|EXPECT}_GT -GTEST_IMPL_CMP_HELPER_(GT, > ); - -#undef GTEST_IMPL_CMP_HELPER_ - -// The helper function for {ASSERT|EXPECT}_STREQ. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression, - const char* actual_expression, - const char* expected, - const char* actual); - -// The helper function for {ASSERT|EXPECT}_STRCASEEQ. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, - const char* actual_expression, - const char* expected, - const char* actual); - -// The helper function for {ASSERT|EXPECT}_STRNE. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, - const char* s2_expression, - const char* s1, - const char* s2); - -// The helper function for {ASSERT|EXPECT}_STRCASENE. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, - const char* s2_expression, - const char* s1, - const char* s2); - - -// Helper function for *_STREQ on wide strings. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression, - const char* actual_expression, - const wchar_t* expected, - const wchar_t* actual); - -// Helper function for *_STRNE on wide strings. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, - const char* s2_expression, - const wchar_t* s1, - const wchar_t* s2); - -} // namespace internal - -// IsSubstring() and IsNotSubstring() are intended to be used as the -// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by -// themselves. They check whether needle is a substring of haystack -// (NULL is considered a substring of itself only), and return an -// appropriate error message when they fail. -// -// The {needle,haystack}_expr arguments are the stringified -// expressions that generated the two real arguments. -GTEST_API_ AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const char* needle, const char* haystack); -GTEST_API_ AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const wchar_t* needle, const wchar_t* haystack); -GTEST_API_ AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const char* needle, const char* haystack); -GTEST_API_ AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const wchar_t* needle, const wchar_t* haystack); -GTEST_API_ AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::string& needle, const ::std::string& haystack); -GTEST_API_ AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::string& needle, const ::std::string& haystack); - -#if GTEST_HAS_STD_WSTRING -GTEST_API_ AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::wstring& needle, const ::std::wstring& haystack); -GTEST_API_ AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::wstring& needle, const ::std::wstring& haystack); -#endif // GTEST_HAS_STD_WSTRING - -namespace internal { - -// Helper template function for comparing floating-points. -// -// Template parameter: -// -// RawType: the raw floating-point type (either float or double) -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -template -AssertionResult CmpHelperFloatingPointEQ(const char* expected_expression, - const char* actual_expression, - RawType expected, - RawType actual) { - const FloatingPoint lhs(expected), rhs(actual); - - if (lhs.AlmostEquals(rhs)) { - return AssertionSuccess(); - } - - StrStream expected_ss; - expected_ss << std::setprecision(std::numeric_limits::digits10 + 2) - << expected; - - StrStream actual_ss; - actual_ss << std::setprecision(std::numeric_limits::digits10 + 2) - << actual; - - return EqFailure(expected_expression, - actual_expression, - StrStreamToString(&expected_ss), - StrStreamToString(&actual_ss), - false); -} - -// Helper function for implementing ASSERT_NEAR. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1, - const char* expr2, - const char* abs_error_expr, - double val1, - double val2, - double abs_error); - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// A class that enables one to stream messages to assertion macros -class GTEST_API_ AssertHelper { - public: - // Constructor. - AssertHelper(TestPartResult::Type type, - const char* file, - int line, - const char* message); - ~AssertHelper(); - - // Message assignment is a semantic trick to enable assertion - // streaming; see the GTEST_MESSAGE_ macro below. - void operator=(const Message& message) const; - - private: - // We put our data in a struct so that the size of the AssertHelper class can - // be as small as possible. This is important because gcc is incapable of - // re-using stack space even for temporary variables, so every EXPECT_EQ - // reserves stack space for another AssertHelper. - struct AssertHelperData { - AssertHelperData(TestPartResult::Type t, - const char* srcfile, - int line_num, - const char* msg) - : type(t), file(srcfile), line(line_num), message(msg) { } - - TestPartResult::Type const type; - const char* const file; - int const line; - String const message; - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData); - }; - - AssertHelperData* const data_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper); -}; - -} // namespace internal - -#if GTEST_HAS_PARAM_TEST -// The abstract base class that all value-parameterized tests inherit from. -// -// This class adds support for accessing the test parameter value via -// the GetParam() method. -// -// Use it with one of the parameter generator defining functions, like Range(), -// Values(), ValuesIn(), Bool(), and Combine(). -// -// class FooTest : public ::testing::TestWithParam { -// protected: -// FooTest() { -// // Can use GetParam() here. -// } -// virtual ~FooTest() { -// // Can use GetParam() here. -// } -// virtual void SetUp() { -// // Can use GetParam() here. -// } -// virtual void TearDown { -// // Can use GetParam() here. -// } -// }; -// TEST_P(FooTest, DoesBar) { -// // Can use GetParam() method here. -// Foo foo; -// ASSERT_TRUE(foo.DoesBar(GetParam())); -// } -// INSTANTIATE_TEST_CASE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); - -template -class TestWithParam : public Test { - public: - typedef T ParamType; - - // The current parameter value. Is also available in the test fixture's - // constructor. - const ParamType& GetParam() const { return *parameter_; } - - private: - // Sets parameter value. The caller is responsible for making sure the value - // remains alive and unchanged throughout the current test. - static void SetParam(const ParamType* parameter) { - parameter_ = parameter; - } - - // Static value used for accessing parameter during a test lifetime. - static const ParamType* parameter_; - - // TestClass must be a subclass of TestWithParam. - template friend class internal::ParameterizedTestFactory; -}; - -template -const T* TestWithParam::parameter_ = NULL; - -#endif // GTEST_HAS_PARAM_TEST - -// Macros for indicating success/failure in test code. - -// ADD_FAILURE unconditionally adds a failure to the current test. -// SUCCEED generates a success - it doesn't automatically make the -// current test successful, as a test is only successful when it has -// no failure. -// -// EXPECT_* verifies that a certain condition is satisfied. If not, -// it behaves like ADD_FAILURE. In particular: -// -// EXPECT_TRUE verifies that a Boolean condition is true. -// EXPECT_FALSE verifies that a Boolean condition is false. -// -// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except -// that they will also abort the current function on failure. People -// usually want the fail-fast behavior of FAIL and ASSERT_*, but those -// writing data-driven tests often find themselves using ADD_FAILURE -// and EXPECT_* more. -// -// Examples: -// -// EXPECT_TRUE(server.StatusIsOK()); -// ASSERT_FALSE(server.HasPendingRequest(port)) -// << "There are still pending requests " << "on port " << port; - -// Generates a nonfatal failure with a generic message. -#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") - -// Generates a fatal failure with a generic message. -#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") - -// Define this macro to 1 to omit the definition of FAIL(), which is a -// generic name and clashes with some other libraries. -#if !GTEST_DONT_DEFINE_FAIL -#define FAIL() GTEST_FAIL() -#endif - -// Generates a success with a generic message. -#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") - -// Define this macro to 1 to omit the definition of SUCCEED(), which -// is a generic name and clashes with some other libraries. -#if !GTEST_DONT_DEFINE_SUCCEED -#define SUCCEED() GTEST_SUCCEED() -#endif - -// Macros for testing exceptions. -// -// * {ASSERT|EXPECT}_THROW(statement, expected_exception): -// Tests that the statement throws the expected exception. -// * {ASSERT|EXPECT}_NO_THROW(statement): -// Tests that the statement doesn't throw any exception. -// * {ASSERT|EXPECT}_ANY_THROW(statement): -// Tests that the statement throws an exception. - -#define EXPECT_THROW(statement, expected_exception) \ - GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) -#define EXPECT_NO_THROW(statement) \ - GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) -#define EXPECT_ANY_THROW(statement) \ - GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) -#define ASSERT_THROW(statement, expected_exception) \ - GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) -#define ASSERT_NO_THROW(statement) \ - GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) -#define ASSERT_ANY_THROW(statement) \ - GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) - -// Boolean assertions. Condition can be either a Boolean expression or an -// AssertionResult. For more information on how to use AssertionResult with -// these macros see comments on that class. -#define EXPECT_TRUE(condition) \ - GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ - GTEST_NONFATAL_FAILURE_) -#define EXPECT_FALSE(condition) \ - GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ - GTEST_NONFATAL_FAILURE_) -#define ASSERT_TRUE(condition) \ - GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ - GTEST_FATAL_FAILURE_) -#define ASSERT_FALSE(condition) \ - GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ - GTEST_FATAL_FAILURE_) - -// Includes the auto-generated header that implements a family of -// generic predicate assertion macros. -// Copyright 2006, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. - -// This file is AUTOMATICALLY GENERATED on 10/02/2008 by command -// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! -// -// Implements a family of generic predicate assertion macros. - -#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ -#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ - -// Makes sure this header is not included before gtest.h. -#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ -#error Do not include gtest_pred_impl.h directly. Include gtest.h instead. -#endif // GTEST_INCLUDE_GTEST_GTEST_H_ - -// This header implements a family of generic predicate assertion -// macros: -// -// ASSERT_PRED_FORMAT1(pred_format, v1) -// ASSERT_PRED_FORMAT2(pred_format, v1, v2) -// ... -// -// where pred_format is a function or functor that takes n (in the -// case of ASSERT_PRED_FORMATn) values and their source expression -// text, and returns a testing::AssertionResult. See the definition -// of ASSERT_EQ in gtest.h for an example. -// -// If you don't care about formatting, you can use the more -// restrictive version: -// -// ASSERT_PRED1(pred, v1) -// ASSERT_PRED2(pred, v1, v2) -// ... -// -// where pred is an n-ary function or functor that returns bool, -// and the values v1, v2, ..., must support the << operator for -// streaming to std::ostream. -// -// We also define the EXPECT_* variations. -// -// For now we only support predicates whose arity is at most 5. -// Please email googletestframework@googlegroups.com if you need -// support for higher arities. - -// GTEST_ASSERT_ is the basic statement to which all of the assertions -// in this file reduce. Don't use this in your code. - -#define GTEST_ASSERT_(expression, on_failure) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (const ::testing::AssertionResult gtest_ar = (expression)) \ - ; \ - else \ - on_failure(gtest_ar.failure_message()) - - -// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use -// this in your code. -template -AssertionResult AssertPred1Helper(const char* pred_text, - const char* e1, - Pred pred, - const T1& v1) { - if (pred(v1)) return AssertionSuccess(); - - Message msg; - msg << pred_text << "(" - << e1 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1; - return AssertionFailure(msg); -} - -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. -// Don't use this in your code. -#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, v1),\ - on_failure) - -// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use -// this in your code. -#define GTEST_PRED1_(pred, v1, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \ - #v1, \ - pred, \ - v1), on_failure) - -// Unary predicate assertion macros. -#define EXPECT_PRED_FORMAT1(pred_format, v1) \ - GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED1(pred, v1) \ - GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT1(pred_format, v1) \ - GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED1(pred, v1) \ - GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) - - - -// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use -// this in your code. -template -AssertionResult AssertPred2Helper(const char* pred_text, - const char* e1, - const char* e2, - Pred pred, - const T1& v1, - const T2& v2) { - if (pred(v1, v2)) return AssertionSuccess(); - - Message msg; - msg << pred_text << "(" - << e1 << ", " - << e2 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2; - return AssertionFailure(msg); -} - -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. -// Don't use this in your code. -#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2),\ - on_failure) - -// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use -// this in your code. -#define GTEST_PRED2_(pred, v1, v2, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \ - #v1, \ - #v2, \ - pred, \ - v1, \ - v2), on_failure) - -// Binary predicate assertion macros. -#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ - GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED2(pred, v1, v2) \ - GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ - GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED2(pred, v1, v2) \ - GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) - - - -// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use -// this in your code. -template -AssertionResult AssertPred3Helper(const char* pred_text, - const char* e1, - const char* e2, - const char* e3, - Pred pred, - const T1& v1, - const T2& v2, - const T3& v3) { - if (pred(v1, v2, v3)) return AssertionSuccess(); - - Message msg; - msg << pred_text << "(" - << e1 << ", " - << e2 << ", " - << e3 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2 - << "\n" << e3 << " evaluates to " << v3; - return AssertionFailure(msg); -} - -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. -// Don't use this in your code. -#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3),\ - on_failure) - -// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use -// this in your code. -#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \ - #v1, \ - #v2, \ - #v3, \ - pred, \ - v1, \ - v2, \ - v3), on_failure) - -// Ternary predicate assertion macros. -#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ - GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED3(pred, v1, v2, v3) \ - GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ - GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED3(pred, v1, v2, v3) \ - GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) - - - -// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use -// this in your code. -template -AssertionResult AssertPred4Helper(const char* pred_text, - const char* e1, - const char* e2, - const char* e3, - const char* e4, - Pred pred, - const T1& v1, - const T2& v2, - const T3& v3, - const T4& v4) { - if (pred(v1, v2, v3, v4)) return AssertionSuccess(); - - Message msg; - msg << pred_text << "(" - << e1 << ", " - << e2 << ", " - << e3 << ", " - << e4 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2 - << "\n" << e3 << " evaluates to " << v3 - << "\n" << e4 << " evaluates to " << v4; - return AssertionFailure(msg); -} - -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. -// Don't use this in your code. -#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4),\ - on_failure) - -// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use -// this in your code. -#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \ - #v1, \ - #v2, \ - #v3, \ - #v4, \ - pred, \ - v1, \ - v2, \ - v3, \ - v4), on_failure) - -// 4-ary predicate assertion macros. -#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ - GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ - GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ - GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ - GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) - - - -// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use -// this in your code. -template -AssertionResult AssertPred5Helper(const char* pred_text, - const char* e1, - const char* e2, - const char* e3, - const char* e4, - const char* e5, - Pred pred, - const T1& v1, - const T2& v2, - const T3& v3, - const T4& v4, - const T5& v5) { - if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); - - Message msg; - msg << pred_text << "(" - << e1 << ", " - << e2 << ", " - << e3 << ", " - << e4 << ", " - << e5 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2 - << "\n" << e3 << " evaluates to " << v3 - << "\n" << e4 << " evaluates to " << v4 - << "\n" << e5 << " evaluates to " << v5; - return AssertionFailure(msg); -} - -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. -// Don't use this in your code. -#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5),\ - on_failure) - -// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use -// this in your code. -#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \ - #v1, \ - #v2, \ - #v3, \ - #v4, \ - #v5, \ - pred, \ - v1, \ - v2, \ - v3, \ - v4, \ - v5), on_failure) - -// 5-ary predicate assertion macros. -#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ - GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ - GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ - GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ - GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) - - - -#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ - -// Macros for testing equalities and inequalities. -// -// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual -// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 -// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 -// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 -// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 -// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 -// -// When they are not, Google Test prints both the tested expressions and -// their actual values. The values must be compatible built-in types, -// or you will get a compiler error. By "compatible" we mean that the -// values can be compared by the respective operator. -// -// Note: -// -// 1. It is possible to make a user-defined type work with -// {ASSERT|EXPECT}_??(), but that requires overloading the -// comparison operators and is thus discouraged by the Google C++ -// Usage Guide. Therefore, you are advised to use the -// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are -// equal. -// -// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on -// pointers (in particular, C strings). Therefore, if you use it -// with two C strings, you are testing how their locations in memory -// are related, not how their content is related. To compare two C -// strings by content, use {ASSERT|EXPECT}_STR*(). -// -// 3. {ASSERT|EXPECT}_EQ(expected, actual) is preferred to -// {ASSERT|EXPECT}_TRUE(expected == actual), as the former tells you -// what the actual value is when it fails, and similarly for the -// other comparisons. -// -// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() -// evaluate their arguments, which is undefined. -// -// 5. These macros evaluate their arguments exactly once. -// -// Examples: -// -// EXPECT_NE(5, Foo()); -// EXPECT_EQ(NULL, a_pointer); -// ASSERT_LT(i, array_size); -// ASSERT_GT(records.size(), 0) << "There is no record left."; - -#define EXPECT_EQ(expected, actual) \ - EXPECT_PRED_FORMAT2(::testing::internal:: \ - EqHelper::Compare, \ - expected, actual) -#define EXPECT_NE(expected, actual) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual) -#define EXPECT_LE(val1, val2) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) -#define EXPECT_LT(val1, val2) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) -#define EXPECT_GE(val1, val2) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) -#define EXPECT_GT(val1, val2) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) - -#define ASSERT_EQ(expected, actual) \ - ASSERT_PRED_FORMAT2(::testing::internal:: \ - EqHelper::Compare, \ - expected, actual) -#define ASSERT_NE(val1, val2) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) -#define ASSERT_LE(val1, val2) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) -#define ASSERT_LT(val1, val2) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) -#define ASSERT_GE(val1, val2) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) -#define ASSERT_GT(val1, val2) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) - -// C String Comparisons. All tests treat NULL and any non-NULL string -// as different. Two NULLs are equal. -// -// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 -// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 -// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case -// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case -// -// For wide or narrow string objects, you can use the -// {ASSERT|EXPECT}_??() macros. -// -// Don't depend on the order in which the arguments are evaluated, -// which is undefined. -// -// These macros evaluate their arguments exactly once. - -#define EXPECT_STREQ(expected, actual) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) -#define EXPECT_STRNE(s1, s2) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) -#define EXPECT_STRCASEEQ(expected, actual) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) -#define EXPECT_STRCASENE(s1, s2)\ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) - -#define ASSERT_STREQ(expected, actual) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) -#define ASSERT_STRNE(s1, s2) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) -#define ASSERT_STRCASEEQ(expected, actual) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) -#define ASSERT_STRCASENE(s1, s2)\ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) - -// Macros for comparing floating-point numbers. -// -// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual): -// Tests that two float values are almost equal. -// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual): -// Tests that two double values are almost equal. -// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): -// Tests that v1 and v2 are within the given distance to each other. -// -// Google Test uses ULP-based comparison to automatically pick a default -// error bound that is appropriate for the operands. See the -// FloatingPoint template class in gtest-internal.h if you are -// interested in the implementation details. - -#define EXPECT_FLOAT_EQ(expected, actual)\ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ - expected, actual) - -#define EXPECT_DOUBLE_EQ(expected, actual)\ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ - expected, actual) - -#define ASSERT_FLOAT_EQ(expected, actual)\ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ - expected, actual) - -#define ASSERT_DOUBLE_EQ(expected, actual)\ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ - expected, actual) - -#define EXPECT_NEAR(val1, val2, abs_error)\ - EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ - val1, val2, abs_error) - -#define ASSERT_NEAR(val1, val2, abs_error)\ - ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ - val1, val2, abs_error) - -// These predicate format functions work on floating-point values, and -// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. -// -// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); - -// Asserts that val1 is less than, or almost equal to, val2. Fails -// otherwise. In particular, it fails if either val1 or val2 is NaN. -GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2, - float val1, float val2); -GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, - double val1, double val2); - - -#if GTEST_OS_WINDOWS - -// Macros that test for HRESULT failure and success, these are only useful -// on Windows, and rely on Windows SDK macros and APIs to compile. -// -// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) -// -// When expr unexpectedly fails or succeeds, Google Test prints the -// expected result and the actual result with both a human-readable -// string representation of the error, if available, as well as the -// hex result code. -#define EXPECT_HRESULT_SUCCEEDED(expr) \ - EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) - -#define ASSERT_HRESULT_SUCCEEDED(expr) \ - ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) - -#define EXPECT_HRESULT_FAILED(expr) \ - EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) - -#define ASSERT_HRESULT_FAILED(expr) \ - ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) - -#endif // GTEST_OS_WINDOWS - -// Macros that execute statement and check that it doesn't generate new fatal -// failures in the current thread. -// -// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); -// -// Examples: -// -// EXPECT_NO_FATAL_FAILURE(Process()); -// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed"; -// -#define ASSERT_NO_FATAL_FAILURE(statement) \ - GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) -#define EXPECT_NO_FATAL_FAILURE(statement) \ - GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) - -// Causes a trace (including the source file path, the current line -// number, and the given message) to be included in every test failure -// message generated by code in the current scope. The effect is -// undone when the control leaves the current scope. -// -// The message argument can be anything streamable to std::ostream. -// -// In the implementation, we include the current line number as part -// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s -// to appear in the same block - as long as they are on different -// lines. -#define SCOPED_TRACE(message) \ - ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ - __FILE__, __LINE__, ::testing::Message() << (message)) - -namespace internal { - -// This template is declared, but intentionally undefined. -template -struct StaticAssertTypeEqHelper; - -template -struct StaticAssertTypeEqHelper {}; - -} // namespace internal - -// Compile-time assertion for type equality. -// StaticAssertTypeEq() compiles iff type1 and type2 are -// the same type. The value it returns is not interesting. -// -// Instead of making StaticAssertTypeEq a class template, we make it a -// function template that invokes a helper class template. This -// prevents a user from misusing StaticAssertTypeEq by -// defining objects of that type. -// -// CAVEAT: -// -// When used inside a method of a class template, -// StaticAssertTypeEq() is effective ONLY IF the method is -// instantiated. For example, given: -// -// template class Foo { -// public: -// void Bar() { testing::StaticAssertTypeEq(); } -// }; -// -// the code: -// -// void Test1() { Foo foo; } -// -// will NOT generate a compiler error, as Foo::Bar() is never -// actually instantiated. Instead, you need: -// -// void Test2() { Foo foo; foo.Bar(); } -// -// to cause a compiler error. -template -bool StaticAssertTypeEq() { - internal::StaticAssertTypeEqHelper(); - return true; -} - -// Defines a test. -// -// The first parameter is the name of the test case, and the second -// parameter is the name of the test within the test case. -// -// The convention is to end the test case name with "Test". For -// example, a test case for the Foo class can be named FooTest. -// -// The user should put his test code between braces after using this -// macro. Example: -// -// TEST(FooTest, InitializesCorrectly) { -// Foo foo; -// EXPECT_TRUE(foo.StatusIsOK()); -// } - -// Note that we call GetTestTypeId() instead of GetTypeId< -// ::testing::Test>() here to get the type ID of testing::Test. This -// is to work around a suspected linker bug when using Google Test as -// a framework on Mac OS X. The bug causes GetTypeId< -// ::testing::Test>() to return different values depending on whether -// the call is from the Google Test framework itself or from user test -// code. GetTestTypeId() is guaranteed to always return the same -// value, as it always calls GetTypeId<>() from the Google Test -// framework. -#define GTEST_TEST(test_case_name, test_name)\ - GTEST_TEST_(test_case_name, test_name, \ - ::testing::Test, ::testing::internal::GetTestTypeId()) - -// Define this macro to 1 to omit the definition of TEST(), which -// is a generic name and clashes with some other libraries. -#if !GTEST_DONT_DEFINE_TEST -#define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name) -#endif - -// Defines a test that uses a test fixture. -// -// The first parameter is the name of the test fixture class, which -// also doubles as the test case name. The second parameter is the -// name of the test within the test case. -// -// A test fixture class must be declared earlier. The user should put -// his test code between braces after using this macro. Example: -// -// class FooTest : public testing::Test { -// protected: -// virtual void SetUp() { b_.AddElement(3); } -// -// Foo a_; -// Foo b_; -// }; -// -// TEST_F(FooTest, InitializesCorrectly) { -// EXPECT_TRUE(a_.StatusIsOK()); -// } -// -// TEST_F(FooTest, ReturnsElementCountCorrectly) { -// EXPECT_EQ(0, a_.size()); -// EXPECT_EQ(1, b_.size()); -// } - -#define TEST_F(test_fixture, test_name)\ - GTEST_TEST_(test_fixture, test_name, test_fixture, \ - ::testing::internal::GetTypeId()) - -// Use this macro in main() to run all tests. It returns 0 if all -// tests are successful, or 1 otherwise. -// -// RUN_ALL_TESTS() should be invoked after the command line has been -// parsed by InitGoogleTest(). - -#define RUN_ALL_TESTS()\ - (::testing::UnitTest::GetInstance()->Run()) - -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_GTEST_H_ diff --git a/modules/gtest/include/opencv2/gtest/gtest_main.hpp b/modules/gtest/include/opencv2/gtest/gtest_main.hpp deleted file mode 100644 index c1ab63294..000000000 --- a/modules/gtest/include/opencv2/gtest/gtest_main.hpp +++ /dev/null @@ -1,5 +0,0 @@ -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/modules/gtest/include/opencv2/gtest/gtestcv.hpp b/modules/gtest/include/opencv2/gtest/gtestcv.hpp deleted file mode 100644 index 2da7febea..000000000 --- a/modules/gtest/include/opencv2/gtest/gtestcv.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef __OPENCV_GTESTCV_HPP__ -#define __OPENCV_GTESTCV_HPP__ - -#include "opencv2/gtest/gtest.h" -#include "opencv2/core/core.hpp" - -namespace cvtest -{ - -using std::vector; -using cv::RNG; -using cv::Mat; -using cv::Scalar; -using cv::Size; -using cv::Point; -using cv::Rect; - -enum -{ - TYPE_MASK_8U = 1 << CV_8U, - TYPE_MASK_8S = 1 << CV_8S, - TYPE_MASK_16U = 1 << CV_16U, - TYPE_MASK_16S = 1 << CV_16S, - TYPE_MASK_32S = 1 << CV_32S, - TYPE_MASK_32F = 1 << CV_32F, - TYPE_MASK_64F = 1 << CV_64F, - TYPE_MASK_ALL = (TYPE_MASK_64F<<1)-1, - TYPE_MASK_ALL_BUT_8S = TYPE_MASK_ALL & ~TYPE_MASK_8S -}; - -CV_EXPORTS double getMinVal(int depth); -CV_EXPORTS double getMaxVal(int depth); - -CV_EXPORTS Size randomSize(RNG& rng, double maxSizeLog); -CV_EXPORTS void randomSize(RNG& rng, int minDims, int maxDims, double maxSizeLog, vector& sz); -CV_EXPORTS int randomType(RNG& rng, int typeMask, int minChannels, int maxChannels); -CV_EXPORTS Mat randomMat(RNG& rng, Size size, int type, double minVal, double maxVal, bool useRoi); -CV_EXPORTS Mat randomMat(RNG& rng, const vector& size, int type, double minVal, double maxVal, bool useRoi); -CV_EXPORTS void add(const Mat& a, double alpha, const Mat& b, double beta, - Scalar gamma, Mat& c, int ctype, bool calcAbs=false); -CV_EXPORTS void convert(const Mat& src, Mat& dst, int dtype, double alpha=1, double beta=0); -CV_EXPORTS void copy(const Mat& src, Mat& dst, const Mat& mask=Mat(), bool invertMask=false); -CV_EXPORTS void set(Mat& dst, const Scalar& gamma, const Mat& mask=Mat()); -CV_EXPORTS void erode(const Mat& src, Mat& dst, const Mat& _kernel, Point anchor=Point(-1,-1), - int borderType=IPL_BORDER_CONSTANT, const Scalar& borderValue=Scalar()); -CV_EXPORTS void dilate(const Mat& src, Mat& dst, const Mat& _kernel, Point anchor=Point(-1,-1), - int borderType=IPL_BORDER_CONSTANT, const Scalar& borderValue=Scalar()); -CV_EXPORTS void filter2D(const Mat& src, Mat& dst, int ddepth, const Mat& kernel, - Point anchor, double delta, int borderType, - const Scalar& borderValue=Scalar()); -CV_EXPORTS void copyMakeBorder(const Mat& src, Mat& dst, int top, int bottom, int left, int right, - int borderType, const Scalar& borderValue=Scalar()); -CV_EXPORTS void minMaxLoc(const Mat& src, double* maxval, double* minval, - vector* maxloc, vector* minloc, const Mat& mask=Mat()); -CV_EXPORTS double norm(const Mat& src, int normType, const Mat& mask=Mat()); -CV_EXPORTS double norm(const Mat& src1, const Mat& src2, int normType, const Mat& mask=Mat()); -CV_EXPORTS bool cmpEps(const Mat& src1, const Mat& src2, int maxDiff, vector* loc); -CV_EXPORTS void logicOp(const Mat& src1, const Mat& src2, Mat& dst, char c); -CV_EXPORTS void logicOp(const Mat& src, const Scalar& s, Mat& dst, char c); -CV_EXPORTS void compare(const Mat& src1, const Mat& src2, Mat& dst, int cmpop); -CV_EXPORTS void compare(const Mat& src, double s, Mat& dst, int cmpop); -CV_EXPORTS void gemm(const Mat& src1, const Mat& src2, double alpha, - const Mat& src3, double beta, Mat& dst, int flags); -CV_EXPORTS void crosscorr(const Mat& src1, const Mat& src2, Mat& dst, int dtype); - -} - -#endif - diff --git a/modules/gtest/src/gtest.cpp b/modules/gtest/src/gtest.cpp deleted file mode 100644 index 43a5b5cc5..000000000 --- a/modules/gtest/src/gtest.cpp +++ /dev/null @@ -1,8510 +0,0 @@ -// Copyright 2008, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: mheule@google.com (Markus Heule) -// -// Google C++ Testing Framework (Google Test) -// -// Sometimes it's desirable to build Google Test by compiling a single file. -// This file serves this purpose. - -// This line ensures that gtest.h can be compiled on its own, even -// when it's fused. -#include "precomp.hpp" - -// The following lines pull in the real gtest *.cc files. -// Copyright 2005, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: wan@google.com (Zhanyong Wan) -// -// The Google C++ Testing Framework (Google Test) - -// Copyright 2007, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: wan@google.com (Zhanyong Wan) -// -// Utilities for testing Google Test itself and code that uses Google Test -// (e.g. frameworks built on top of Google Test). - -#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ -#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ - - -namespace testing { - -// This helper class can be used to mock out Google Test failure reporting -// so that we can test Google Test or code that builds on Google Test. -// -// An object of this class appends a TestPartResult object to the -// TestPartResultArray object given in the constructor whenever a Google Test -// failure is reported. It can either intercept only failures that are -// generated in the same thread that created this object or it can intercept -// all generated failures. The scope of this mock object can be controlled with -// the second argument to the two arguments constructor. -class GTEST_API_ ScopedFakeTestPartResultReporter - : public TestPartResultReporterInterface { - public: - // The two possible mocking modes of this object. - enum InterceptMode { - INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. - INTERCEPT_ALL_THREADS // Intercepts all failures. - }; - - // The c'tor sets this object as the test part result reporter used - // by Google Test. The 'result' parameter specifies where to report the - // results. This reporter will only catch failures generated in the current - // thread. DEPRECATED - explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); - - // Same as above, but you can choose the interception scope of this object. - ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, - TestPartResultArray* result); - - // The d'tor restores the previous test part result reporter. - virtual ~ScopedFakeTestPartResultReporter(); - - // Appends the TestPartResult object to the TestPartResultArray - // received in the constructor. - // - // This method is from the TestPartResultReporterInterface - // interface. - virtual void ReportTestPartResult(const TestPartResult& result); - private: - void Init(); - - const InterceptMode intercept_mode_; - TestPartResultReporterInterface* old_reporter_; - TestPartResultArray* const result_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter); -}; - -namespace internal { - -// A helper class for implementing EXPECT_FATAL_FAILURE() and -// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given -// TestPartResultArray contains exactly one failure that has the given -// type and contains the given substring. If that's not the case, a -// non-fatal failure will be generated. -class GTEST_API_ SingleFailureChecker { - public: - // The constructor remembers the arguments. - SingleFailureChecker(const TestPartResultArray* results, - TestPartResult::Type type, - const char* substr); - ~SingleFailureChecker(); - private: - const TestPartResultArray* const results_; - const TestPartResult::Type type_; - const String substr_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); -}; - -} // namespace internal - -} // namespace testing - -// A set of macros for testing Google Test assertions or code that's expected -// to generate Google Test fatal failures. It verifies that the given -// statement will cause exactly one fatal Google Test failure with 'substr' -// being part of the failure message. -// -// There are two different versions of this macro. EXPECT_FATAL_FAILURE only -// affects and considers failures generated in the current thread and -// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. -// -// The verification of the assertion is done correctly even when the statement -// throws an exception or aborts the current function. -// -// Known restrictions: -// - 'statement' cannot reference local non-static variables or -// non-static members of the current object. -// - 'statement' cannot return a value. -// - You cannot stream a failure message to this macro. -// -// Note that even though the implementations of the following two -// macros are much alike, we cannot refactor them to use a common -// helper macro, due to some peculiarity in how the preprocessor -// works. The AcceptsMacroThatExpandsToUnprotectedComma test in -// gtest_unittest.cc will fail to compile if we do that. -#define EXPECT_FATAL_FAILURE(statement, substr) \ - do { \ - class GTestExpectFatalFailureHelper {\ - public:\ - static void Execute() { statement; }\ - };\ - ::testing::TestPartResultArray gtest_failures;\ - ::testing::internal::SingleFailureChecker gtest_checker(\ - >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ - {\ - ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ - ::testing::ScopedFakeTestPartResultReporter:: \ - INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ - GTestExpectFatalFailureHelper::Execute();\ - }\ - } while (::testing::internal::AlwaysFalse()) - -#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ - do { \ - class GTestExpectFatalFailureHelper {\ - public:\ - static void Execute() { statement; }\ - };\ - ::testing::TestPartResultArray gtest_failures;\ - ::testing::internal::SingleFailureChecker gtest_checker(\ - >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ - {\ - ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ - ::testing::ScopedFakeTestPartResultReporter:: \ - INTERCEPT_ALL_THREADS, >est_failures);\ - GTestExpectFatalFailureHelper::Execute();\ - }\ - } while (::testing::internal::AlwaysFalse()) - -// A macro for testing Google Test assertions or code that's expected to -// generate Google Test non-fatal failures. It asserts that the given -// statement will cause exactly one non-fatal Google Test failure with 'substr' -// being part of the failure message. -// -// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only -// affects and considers failures generated in the current thread and -// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. -// -// 'statement' is allowed to reference local variables and members of -// the current object. -// -// The verification of the assertion is done correctly even when the statement -// throws an exception or aborts the current function. -// -// Known restrictions: -// - You cannot stream a failure message to this macro. -// -// Note that even though the implementations of the following two -// macros are much alike, we cannot refactor them to use a common -// helper macro, due to some peculiarity in how the preprocessor -// works. If we do that, the code won't compile when the user gives -// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that -// expands to code containing an unprotected comma. The -// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc -// catches that. -// -// For the same reason, we have to write -// if (::testing::internal::AlwaysTrue()) { statement; } -// instead of -// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) -// to avoid an MSVC warning on unreachable code. -#define EXPECT_NONFATAL_FAILURE(statement, substr) \ - do {\ - ::testing::TestPartResultArray gtest_failures;\ - ::testing::internal::SingleFailureChecker gtest_checker(\ - >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ - (substr));\ - {\ - ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ - ::testing::ScopedFakeTestPartResultReporter:: \ - INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ - if (::testing::internal::AlwaysTrue()) { statement; }\ - }\ - } while (::testing::internal::AlwaysFalse()) - -#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ - do {\ - ::testing::TestPartResultArray gtest_failures;\ - ::testing::internal::SingleFailureChecker gtest_checker(\ - >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ - (substr));\ - {\ - ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ - ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS,\ - >est_failures);\ - if (::testing::internal::AlwaysTrue()) { statement; }\ - }\ - } while (::testing::internal::AlwaysFalse()) - -#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if GTEST_OS_LINUX - -// TODO(kenton@google.com): Use autoconf to detect availability of -// gettimeofday(). -#define GTEST_HAS_GETTIMEOFDAY_ 1 - -#include -#include -#include -// Declares vsnprintf(). This header is not available on Windows. -#include -#include -#include -#include -#include -#include - -#elif GTEST_OS_SYMBIAN -#define GTEST_HAS_GETTIMEOFDAY_ 1 -#include // NOLINT - -#elif GTEST_OS_ZOS -#define GTEST_HAS_GETTIMEOFDAY_ 1 -#include // NOLINT - -// On z/OS we additionally need strings.h for strcasecmp. -#include // NOLINT - -#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. - -#include // NOLINT - -#elif GTEST_OS_WINDOWS // We are on Windows proper. - -#include // NOLINT -#include // NOLINT -#include // NOLINT -#include // NOLINT - -#if GTEST_OS_WINDOWS_MINGW -// MinGW has gettimeofday() but not _ftime64(). -// TODO(kenton@google.com): Use autoconf to detect availability of -// gettimeofday(). -// TODO(kenton@google.com): There are other ways to get the time on -// Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW -// supports these. consider using them instead. -#define GTEST_HAS_GETTIMEOFDAY_ 1 -#include // NOLINT -#endif // GTEST_OS_WINDOWS_MINGW - -// cpplint thinks that the header is already included, so we want to -// silence it. -#include // NOLINT - -#else - -// Assume other platforms have gettimeofday(). -// TODO(kenton@google.com): Use autoconf to detect availability of -// gettimeofday(). -#define GTEST_HAS_GETTIMEOFDAY_ 1 - -// cpplint thinks that the header is already included, so we want to -// silence it. -#include // NOLINT -#include // NOLINT - -#endif // GTEST_OS_LINUX - -#if GTEST_HAS_EXCEPTIONS -#include -#endif - -// Indicates that this translation unit is part of Google Test's -// implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick is to -// prevent a user from accidentally including gtest-internal-inl.h in -// his code. -#define GTEST_IMPLEMENTATION_ 1 -// Copyright 2005, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. - -// Utility functions and classes used by the Google C++ testing framework. -// -// Author: wan@google.com (Zhanyong Wan) -// -// This file contains purely Google Test's internal implementation. Please -// DO NOT #INCLUDE IT IN A USER PROGRAM. - -#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_ -#define GTEST_SRC_GTEST_INTERNAL_INL_H_ - -// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is -// part of Google Test's implementation; otherwise it's undefined. -#if !GTEST_IMPLEMENTATION_ -// A user is trying to include this from his code - just say no. -#error "gtest-internal-inl.h is part of Google Test's internal implementation." -#error "It must not be included except by Google Test itself." -#endif // GTEST_IMPLEMENTATION_ - -#ifndef _WIN32_WCE -#include -#endif // !_WIN32_WCE -#include -#include // For strtoll/_strtoul64/malloc/free. -#include // For memmove. - -#include -#include -#include - - -#if GTEST_OS_WINDOWS -#include // For DWORD. -#endif // GTEST_OS_WINDOWS - - -namespace testing { - -// Declares the flags. -// -// We don't want the users to modify this flag in the code, but want -// Google Test's own unit tests to be able to access it. Therefore we -// declare it here as opposed to in gtest.h. -GTEST_DECLARE_bool_(death_test_use_fork); - -namespace internal { - -// The value of GetTestTypeId() as seen from within the Google Test -// library. This is solely for testing GetTestTypeId(). -GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest; - -// Names of the flags (needed for parsing Google Test flags). -const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests"; -const char kBreakOnFailureFlag[] = "break_on_failure"; -const char kCatchExceptionsFlag[] = "catch_exceptions"; -const char kColorFlag[] = "color"; -const char kFilterFlag[] = "filter"; -const char kListTestsFlag[] = "list_tests"; -const char kOutputFlag[] = "output"; -const char kPrintTimeFlag[] = "print_time"; -const char kRandomSeedFlag[] = "random_seed"; -const char kRepeatFlag[] = "repeat"; -const char kShuffleFlag[] = "shuffle"; -const char kStackTraceDepthFlag[] = "stack_trace_depth"; -const char kThrowOnFailureFlag[] = "throw_on_failure"; - -// A valid random seed must be in [1, kMaxRandomSeed]. -const int kMaxRandomSeed = 99999; - -// g_help_flag is true iff the --help flag or an equivalent form is -// specified on the command line. -GTEST_API_ extern bool g_help_flag; - -// Returns the current time in milliseconds. -GTEST_API_ TimeInMillis GetTimeInMillis(); - -// Returns true iff Google Test should use colors in the output. -GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); - -// Formats the given time in milliseconds as seconds. -GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); - -// Parses a string for an Int32 flag, in the form of "--flag=value". -// -// On success, stores the value of the flag in *value, and returns -// true. On failure, returns false without changing *value. -GTEST_API_ bool ParseInt32Flag( - const char* str, const char* flag, Int32* value); - -// Returns a random seed in range [1, kMaxRandomSeed] based on the -// given --gtest_random_seed flag value. -inline int GetRandomSeedFromFlag(Int32 random_seed_flag) { - const unsigned int raw_seed = (random_seed_flag == 0) ? - static_cast(GetTimeInMillis()) : - static_cast(random_seed_flag); - - // Normalizes the actual seed to range [1, kMaxRandomSeed] such that - // it's easy to type. - const int normalized_seed = - static_cast((raw_seed - 1U) % - static_cast(kMaxRandomSeed)) + 1; - return normalized_seed; -} - -// Returns the first valid random seed after 'seed'. The behavior is -// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is -// considered to be 1. -inline int GetNextRandomSeed(int seed) { - GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed) - << "Invalid random seed " << seed << " - must be in [1, " - << kMaxRandomSeed << "]."; - const int next_seed = seed + 1; - return (next_seed > kMaxRandomSeed) ? 1 : next_seed; -} - -// This class saves the values of all Google Test flags in its c'tor, and -// restores them in its d'tor. -class GTestFlagSaver { - public: - // The c'tor. - GTestFlagSaver() { - also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests); - break_on_failure_ = GTEST_FLAG(break_on_failure); - catch_exceptions_ = GTEST_FLAG(catch_exceptions); - color_ = GTEST_FLAG(color); - death_test_style_ = GTEST_FLAG(death_test_style); - death_test_use_fork_ = GTEST_FLAG(death_test_use_fork); - filter_ = GTEST_FLAG(filter); - internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); - list_tests_ = GTEST_FLAG(list_tests); - output_ = GTEST_FLAG(output); - print_time_ = GTEST_FLAG(print_time); - random_seed_ = GTEST_FLAG(random_seed); - repeat_ = GTEST_FLAG(repeat); - shuffle_ = GTEST_FLAG(shuffle); - stack_trace_depth_ = GTEST_FLAG(stack_trace_depth); - throw_on_failure_ = GTEST_FLAG(throw_on_failure); - } - - // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS. - ~GTestFlagSaver() { - GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_; - GTEST_FLAG(break_on_failure) = break_on_failure_; - GTEST_FLAG(catch_exceptions) = catch_exceptions_; - GTEST_FLAG(color) = color_; - GTEST_FLAG(death_test_style) = death_test_style_; - GTEST_FLAG(death_test_use_fork) = death_test_use_fork_; - GTEST_FLAG(filter) = filter_; - GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; - GTEST_FLAG(list_tests) = list_tests_; - GTEST_FLAG(output) = output_; - GTEST_FLAG(print_time) = print_time_; - GTEST_FLAG(random_seed) = random_seed_; - GTEST_FLAG(repeat) = repeat_; - GTEST_FLAG(shuffle) = shuffle_; - GTEST_FLAG(stack_trace_depth) = stack_trace_depth_; - GTEST_FLAG(throw_on_failure) = throw_on_failure_; - } - private: - // Fields for saving the original values of flags. - bool also_run_disabled_tests_; - bool break_on_failure_; - bool catch_exceptions_; - String color_; - String death_test_style_; - bool death_test_use_fork_; - String filter_; - String internal_run_death_test_; - bool list_tests_; - String output_; - bool print_time_; - bool pretty_; - internal::Int32 random_seed_; - internal::Int32 repeat_; - bool shuffle_; - internal::Int32 stack_trace_depth_; - bool throw_on_failure_; -} GTEST_ATTRIBUTE_UNUSED_; - -// Converts a Unicode code point to a narrow string in UTF-8 encoding. -// code_point parameter is of type UInt32 because wchar_t may not be -// wide enough to contain a code point. -// The output buffer str must containt at least 32 characters. -// The function returns the address of the output buffer. -// If the code_point is not a valid Unicode code point -// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output -// as '(Invalid Unicode 0xXXXXXXXX)'. -GTEST_API_ char* CodePointToUtf8(UInt32 code_point, char* str); - -// Converts a wide string to a narrow string in UTF-8 encoding. -// The wide string is assumed to have the following encoding: -// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) -// UTF-32 if sizeof(wchar_t) == 4 (on Linux) -// Parameter str points to a null-terminated wide string. -// Parameter num_chars may additionally limit the number -// of wchar_t characters processed. -1 is used when the entire string -// should be processed. -// If the string contains code points that are not valid Unicode code points -// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output -// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding -// and contains invalid UTF-16 surrogate pairs, values in those pairs -// will be encoded as individual Unicode characters from Basic Normal Plane. -GTEST_API_ String WideStringToUtf8(const wchar_t* str, int num_chars); - -// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file -// if the variable is present. If a file already exists at this location, this -// function will write over it. If the variable is present, but the file cannot -// be created, prints an error and exits. -void WriteToShardStatusFileIfNeeded(); - -// Checks whether sharding is enabled by examining the relevant -// environment variable values. If the variables are present, -// but inconsistent (e.g., shard_index >= total_shards), prints -// an error and exits. If in_subprocess_for_death_test, sharding is -// disabled because it must only be applied to the original test -// process. Otherwise, we could filter out death tests we intended to execute. -GTEST_API_ bool ShouldShard(const char* total_shards_str, - const char* shard_index_str, - bool in_subprocess_for_death_test); - -// Parses the environment variable var as an Int32. If it is unset, -// returns default_val. If it is not an Int32, prints an error and -// and aborts. -GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val); - -// Given the total number of shards, the shard index, and the test id, -// returns true iff the test should be run on this shard. The test id is -// some arbitrary but unique non-negative integer assigned to each test -// method. Assumes that 0 <= shard_index < total_shards. -GTEST_API_ bool ShouldRunTestOnShard( - int total_shards, int shard_index, int test_id); - -// STL container utilities. - -// Returns the number of elements in the given container that satisfy -// the given predicate. -template -inline int CountIf(const Container& c, Predicate predicate) { - return static_cast(std::count_if(c.begin(), c.end(), predicate)); -} - -// Applies a function/functor to each element in the container. -template -void ForEach(const Container& c, Functor functor) { - std::for_each(c.begin(), c.end(), functor); -} - -// Returns the i-th element of the vector, or default_value if i is not -// in range [0, v.size()). -template -inline E GetElementOr(const std::vector& v, int i, E default_value) { - return (i < 0 || i >= static_cast(v.size())) ? default_value : v[i]; -} - -// Performs an in-place shuffle of a range of the vector's elements. -// 'begin' and 'end' are element indices as an STL-style range; -// i.e. [begin, end) are shuffled, where 'end' == size() means to -// shuffle to the end of the vector. -template -void ShuffleRange(internal::Random* random, int begin, int end, - std::vector* v) { - const int size = static_cast(v->size()); - GTEST_CHECK_(0 <= begin && begin <= size) - << "Invalid shuffle range start " << begin << ": must be in range [0, " - << size << "]."; - GTEST_CHECK_(begin <= end && end <= size) - << "Invalid shuffle range finish " << end << ": must be in range [" - << begin << ", " << size << "]."; - - // Fisher-Yates shuffle, from - // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle - for (int range_width = end - begin; range_width >= 2; range_width--) { - const int last_in_range = begin + range_width - 1; - const int selected = begin + random->Generate(range_width); - std::swap((*v)[selected], (*v)[last_in_range]); - } -} - -// Performs an in-place shuffle of the vector's elements. -template -inline void Shuffle(internal::Random* random, std::vector* v) { - ShuffleRange(random, 0, static_cast(v->size()), v); -} - -// A function for deleting an object. Handy for being used as a -// functor. -template -static void Delete(T* x) { - delete x; -} - -// A predicate that checks the key of a TestProperty against a known key. -// -// TestPropertyKeyIs is copyable. -class TestPropertyKeyIs { - public: - // Constructor. - // - // TestPropertyKeyIs has NO default constructor. - explicit TestPropertyKeyIs(const char* key) - : key_(key) {} - - // Returns true iff the test name of test property matches on key_. - bool operator()(const TestProperty& test_property) const { - return String(test_property.key()).Compare(key_) == 0; - } - - private: - String key_; -}; - -class TestInfoImpl { - public: - TestInfoImpl(TestInfo* parent, const char* test_case_name, - const char* name, const char* test_case_comment, - const char* comment, TypeId fixture_class_id, - internal::TestFactoryBase* factory); - ~TestInfoImpl(); - - // Returns true if this test should run. - bool should_run() const { return should_run_; } - - // Sets the should_run member. - void set_should_run(bool should) { should_run_ = should; } - - // Returns true if this test is disabled. Disabled tests are not run. - bool is_disabled() const { return is_disabled_; } - - // Sets the is_disabled member. - void set_is_disabled(bool is) { is_disabled_ = is; } - - // Returns true if this test matches the filter specified by the user. - bool matches_filter() const { return matches_filter_; } - - // Sets the matches_filter member. - void set_matches_filter(bool matches) { matches_filter_ = matches; } - - // Returns the test case name. - const char* test_case_name() const { return test_case_name_.c_str(); } - - // Returns the test name. - const char* name() const { return name_.c_str(); } - - // Returns the test case comment. - const char* test_case_comment() const { return test_case_comment_.c_str(); } - - // Returns the test comment. - const char* comment() const { return comment_.c_str(); } - - // Returns the ID of the test fixture class. - TypeId fixture_class_id() const { return fixture_class_id_; } - - // Returns the test result. - TestResult* result() { return &result_; } - const TestResult* result() const { return &result_; } - - // Creates the test object, runs it, records its result, and then - // deletes it. - void Run(); - - // Clears the test result. - void ClearResult() { result_.Clear(); } - - // Clears the test result in the given TestInfo object. - static void ClearTestResult(TestInfo * test_info) { - test_info->impl()->ClearResult(); - } - - private: - // These fields are immutable properties of the test. - TestInfo* const parent_; // The owner of this object - const String test_case_name_; // Test case name - const String name_; // Test name - const String test_case_comment_; // Test case comment - const String comment_; // Test comment - const TypeId fixture_class_id_; // ID of the test fixture class - bool should_run_; // True iff this test should run - bool is_disabled_; // True iff this test is disabled - bool matches_filter_; // True if this test matches the - // user-specified filter. - internal::TestFactoryBase* const factory_; // The factory that creates - // the test object - - // This field is mutable and needs to be reset before running the - // test for the second time. - TestResult result_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfoImpl); -}; - -// Class UnitTestOptions. -// -// This class contains functions for processing options the user -// specifies when running the tests. It has only static members. -// -// In most cases, the user can specify an option using either an -// environment variable or a command line flag. E.g. you can set the -// test filter using either GTEST_FILTER or --gtest_filter. If both -// the variable and the flag are present, the latter overrides the -// former. -class GTEST_API_ UnitTestOptions { - public: - // Functions for processing the gtest_output flag. - - // Returns the output format, or "" for normal printed output. - static String GetOutputFormat(); - - // Returns the absolute path of the requested output file, or the - // default (test_detail.xml in the original working directory) if - // none was explicitly specified. - static String GetAbsolutePathToOutputFile(); - - // Functions for processing the gtest_filter flag. - - // Returns true iff the wildcard pattern matches the string. The - // first ':' or '\0' character in pattern marks the end of it. - // - // This recursive algorithm isn't very efficient, but is clear and - // works well enough for matching test names, which are short. - static bool PatternMatchesString(const char *pattern, const char *str); - - // Returns true iff the user-specified filter matches the test case - // name and the test name. - static bool FilterMatchesTest(const String &test_case_name, - const String &test_name); - -#if GTEST_OS_WINDOWS - // Function for supporting the gtest_catch_exception flag. - - // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the - // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. - // This function is useful as an __except condition. - static int GTestShouldProcessSEH(DWORD exception_code); -#endif // GTEST_OS_WINDOWS - - // Returns true if "name" matches the ':' separated list of glob-style - // filters in "filter". - static bool MatchesFilter(const String& name, const char* filter); -}; - -// Returns the current application's name, removing directory path if that -// is present. Used by UnitTestOptions::GetOutputFile. -GTEST_API_ FilePath GetCurrentExecutableName(); - -// The role interface for getting the OS stack trace as a string. -class OsStackTraceGetterInterface { - public: - OsStackTraceGetterInterface() {} - virtual ~OsStackTraceGetterInterface() {} - - // Returns the current OS stack trace as a String. Parameters: - // - // max_depth - the maximum number of stack frames to be included - // in the trace. - // skip_count - the number of top frames to be skipped; doesn't count - // against max_depth. - virtual String CurrentStackTrace(int max_depth, int skip_count) = 0; - - // UponLeavingGTest() should be called immediately before Google Test calls - // user code. It saves some information about the current stack that - // CurrentStackTrace() will use to find and hide Google Test stack frames. - virtual void UponLeavingGTest() = 0; - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface); -}; - -// A working implementation of the OsStackTraceGetterInterface interface. -class OsStackTraceGetter : public OsStackTraceGetterInterface { - public: - OsStackTraceGetter() : caller_frame_(NULL) {} - virtual String CurrentStackTrace(int max_depth, int skip_count); - virtual void UponLeavingGTest(); - - // This string is inserted in place of stack frames that are part of - // Google Test's implementation. - static const char* const kElidedFramesMarker; - - private: - Mutex mutex_; // protects all internal state - - // We save the stack frame below the frame that calls user code. - // We do this because the address of the frame immediately below - // the user code changes between the call to UponLeavingGTest() - // and any calls to CurrentStackTrace() from within the user code. - void* caller_frame_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter); -}; - -// Information about a Google Test trace point. -struct TraceInfo { - const char* file; - int line; - String message; -}; - -// This is the default global test part result reporter used in UnitTestImpl. -// This class should only be used by UnitTestImpl. -class DefaultGlobalTestPartResultReporter - : public TestPartResultReporterInterface { - public: - explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test); - // Implements the TestPartResultReporterInterface. Reports the test part - // result in the current test. - virtual void ReportTestPartResult(const TestPartResult& result); - - private: - UnitTestImpl* const unit_test_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter); -}; - -// This is the default per thread test part result reporter used in -// UnitTestImpl. This class should only be used by UnitTestImpl. -class DefaultPerThreadTestPartResultReporter - : public TestPartResultReporterInterface { - public: - explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test); - // Implements the TestPartResultReporterInterface. The implementation just - // delegates to the current global test part result reporter of *unit_test_. - virtual void ReportTestPartResult(const TestPartResult& result); - - private: - UnitTestImpl* const unit_test_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter); -}; - -// The private implementation of the UnitTest class. We don't protect -// the methods under a mutex, as this class is not accessible by a -// user and the UnitTest class that delegates work to this class does -// proper locking. -class GTEST_API_ UnitTestImpl { - public: - explicit UnitTestImpl(UnitTest* parent); - virtual ~UnitTestImpl(); - - // There are two different ways to register your own TestPartResultReporter. - // You can register your own repoter to listen either only for test results - // from the current thread or for results from all threads. - // By default, each per-thread test result repoter just passes a new - // TestPartResult to the global test result reporter, which registers the - // test part result for the currently running test. - - // Returns the global test part result reporter. - TestPartResultReporterInterface* GetGlobalTestPartResultReporter(); - - // Sets the global test part result reporter. - void SetGlobalTestPartResultReporter( - TestPartResultReporterInterface* reporter); - - // Returns the test part result reporter for the current thread. - TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread(); - - // Sets the test part result reporter for the current thread. - void SetTestPartResultReporterForCurrentThread( - TestPartResultReporterInterface* reporter); - - // Gets the number of successful test cases. - int successful_test_case_count() const; - - // Gets the number of failed test cases. - int failed_test_case_count() const; - - // Gets the number of all test cases. - int total_test_case_count() const; - - // Gets the number of all test cases that contain at least one test - // that should run. - int test_case_to_run_count() const; - - // Gets the number of successful tests. - int successful_test_count() const; - - // Gets the number of failed tests. - int failed_test_count() const; - - // Gets the number of disabled tests. - int disabled_test_count() const; - - // Gets the number of all tests. - int total_test_count() const; - - // Gets the number of tests that should run. - int test_to_run_count() const; - - // Gets the elapsed time, in milliseconds. - TimeInMillis elapsed_time() const { return elapsed_time_; } - - // Returns true iff the unit test passed (i.e. all test cases passed). - bool Passed() const { return !Failed(); } - - // Returns true iff the unit test failed (i.e. some test case failed - // or something outside of all tests failed). - bool Failed() const { - return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed(); - } - - // Gets the i-th test case among all the test cases. i can range from 0 to - // total_test_case_count() - 1. If i is not in that range, returns NULL. - const TestCase* GetTestCase(int i) const { - const int index = GetElementOr(test_case_indices_, i, -1); - return index < 0 ? NULL : test_cases_[i]; - } - - // Gets the i-th test case among all the test cases. i can range from 0 to - // total_test_case_count() - 1. If i is not in that range, returns NULL. - TestCase* GetMutableTestCase(int i) { - const int index = GetElementOr(test_case_indices_, i, -1); - return index < 0 ? NULL : test_cases_[index]; - } - - // Provides access to the event listener list. - TestEventListeners* listeners() { return &listeners_; } - - // Returns the TestResult for the test that's currently running, or - // the TestResult for the ad hoc test if no test is running. - TestResult* current_test_result(); - - // Returns the TestResult for the ad hoc test. - const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; } - - // Sets the OS stack trace getter. - // - // Does nothing if the input and the current OS stack trace getter - // are the same; otherwise, deletes the old getter and makes the - // input the current getter. - void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter); - - // Returns the current OS stack trace getter if it is not NULL; - // otherwise, creates an OsStackTraceGetter, makes it the current - // getter, and returns it. - OsStackTraceGetterInterface* os_stack_trace_getter(); - - // Returns the current OS stack trace as a String. - // - // The maximum number of stack frames to be included is specified by - // the gtest_stack_trace_depth flag. The skip_count parameter - // specifies the number of top frames to be skipped, which doesn't - // count against the number of frames to be included. - // - // For example, if Foo() calls Bar(), which in turn calls - // CurrentOsStackTraceExceptTop(1), Foo() will be included in the - // trace but Bar() and CurrentOsStackTraceExceptTop() won't. - String CurrentOsStackTraceExceptTop(int skip_count); - - // Finds and returns a TestCase with the given name. If one doesn't - // exist, creates one and returns it. - // - // Arguments: - // - // test_case_name: name of the test case - // set_up_tc: pointer to the function that sets up the test case - // tear_down_tc: pointer to the function that tears down the test case - TestCase* GetTestCase(const char* test_case_name, - const char* comment, - Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc); - - // Adds a TestInfo to the unit test. - // - // Arguments: - // - // set_up_tc: pointer to the function that sets up the test case - // tear_down_tc: pointer to the function that tears down the test case - // test_info: the TestInfo object - void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc, - TestInfo * test_info) { - // In order to support thread-safe death tests, we need to - // remember the original working directory when the test program - // was first invoked. We cannot do this in RUN_ALL_TESTS(), as - // the user may have changed the current directory before calling - // RUN_ALL_TESTS(). Therefore we capture the current directory in - // AddTestInfo(), which is called to register a TEST or TEST_F - // before main() is reached. - if (original_working_dir_.IsEmpty()) { - original_working_dir_.Set(FilePath::GetCurrentDir()); - GTEST_CHECK_(!original_working_dir_.IsEmpty()) - << "Failed to get the current working directory."; - } - - GetTestCase(test_info->test_case_name(), - test_info->test_case_comment(), - set_up_tc, - tear_down_tc)->AddTestInfo(test_info); - } - -#if GTEST_HAS_PARAM_TEST - // Returns ParameterizedTestCaseRegistry object used to keep track of - // value-parameterized tests and instantiate and register them. - internal::ParameterizedTestCaseRegistry& parameterized_test_registry() { - return parameterized_test_registry_; - } -#endif // GTEST_HAS_PARAM_TEST - - // Sets the TestCase object for the test that's currently running. - void set_current_test_case(TestCase* a_current_test_case) { - current_test_case_ = a_current_test_case; - } - - // Sets the TestInfo object for the test that's currently running. If - // current_test_info is NULL, the assertion results will be stored in - // ad_hoc_test_result_. - void set_current_test_info(TestInfo* a_current_test_info) { - current_test_info_ = a_current_test_info; - } - - // Registers all parameterized tests defined using TEST_P and - // INSTANTIATE_TEST_P, creating regular tests for each test/parameter - // combination. This method can be called more then once; it has - // guards protecting from registering the tests more then once. - // If value-parameterized tests are disabled, RegisterParameterizedTests - // is present but does nothing. - void RegisterParameterizedTests(); - - // Runs all tests in this UnitTest object, prints the result, and - // returns 0 if all tests are successful, or 1 otherwise. If any - // exception is thrown during a test on Windows, this test is - // considered to be failed, but the rest of the tests will still be - // run. (We disable exceptions on Linux and Mac OS X, so the issue - // doesn't apply there.) - int RunAllTests(); - - // Clears the results of all tests, including the ad hoc test. - void ClearResult() { - ForEach(test_cases_, TestCase::ClearTestCaseResult); - ad_hoc_test_result_.Clear(); - } - - enum ReactionToSharding { - HONOR_SHARDING_PROTOCOL, - IGNORE_SHARDING_PROTOCOL - }; - - // Matches the full name of each test against the user-specified - // filter to decide whether the test should run, then records the - // result in each TestCase and TestInfo object. - // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests - // based on sharding variables in the environment. - // Returns the number of tests that should run. - int FilterTests(ReactionToSharding shard_tests); - - // Prints the names of the tests matching the user-specified filter flag. - void ListTestsMatchingFilter(); - - const TestCase* current_test_case() const { return current_test_case_; } - TestInfo* current_test_info() { return current_test_info_; } - const TestInfo* current_test_info() const { return current_test_info_; } - - // Returns the vector of environments that need to be set-up/torn-down - // before/after the tests are run. - std::vector& environments() { return environments_; } - - // Getters for the per-thread Google Test trace stack. - std::vector& gtest_trace_stack() { - return *(gtest_trace_stack_.pointer()); - } - const std::vector& gtest_trace_stack() const { - return gtest_trace_stack_.get(); - } - -#if GTEST_HAS_DEATH_TEST - void InitDeathTestSubprocessControlInfo() { - internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); - } - // Returns a pointer to the parsed --gtest_internal_run_death_test - // flag, or NULL if that flag was not specified. - // This information is useful only in a death test child process. - // Must not be called before a call to InitGoogleTest. - const InternalRunDeathTestFlag* internal_run_death_test_flag() const { - return internal_run_death_test_flag_.get(); - } - - // Returns a pointer to the current death test factory. - internal::DeathTestFactory* death_test_factory() { - return death_test_factory_.get(); - } - - void SuppressTestEventsIfInSubprocess(); - - friend class ReplaceDeathTestFactory; -#endif // GTEST_HAS_DEATH_TEST - - // Initializes the event listener performing XML output as specified by - // UnitTestOptions. Must not be called before InitGoogleTest. - void ConfigureXmlOutput(); - - // Performs initialization dependent upon flag values obtained in - // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to - // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest - // this function is also called from RunAllTests. Since this function can be - // called more than once, it has to be idempotent. - void PostFlagParsingInit(); - - // Gets the random seed used at the start of the current test iteration. - int random_seed() const { return random_seed_; } - - // Gets the random number generator. - internal::Random* random() { return &random_; } - - // Shuffles all test cases, and the tests within each test case, - // making sure that death tests are still run first. - void ShuffleTests(); - - // Restores the test cases and tests to their order before the first shuffle. - void UnshuffleTests(); - - private: - friend class ::testing::UnitTest; - - // The UnitTest object that owns this implementation object. - UnitTest* const parent_; - - // The working directory when the first TEST() or TEST_F() was - // executed. - internal::FilePath original_working_dir_; - - // The default test part result reporters. - DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_; - DefaultPerThreadTestPartResultReporter - default_per_thread_test_part_result_reporter_; - - // Points to (but doesn't own) the global test part result reporter. - TestPartResultReporterInterface* global_test_part_result_repoter_; - - // Protects read and write access to global_test_part_result_reporter_. - internal::Mutex global_test_part_result_reporter_mutex_; - - // Points to (but doesn't own) the per-thread test part result reporter. - internal::ThreadLocal - per_thread_test_part_result_reporter_; - - // The vector of environments that need to be set-up/torn-down - // before/after the tests are run. - std::vector environments_; - - // The vector of TestCases in their original order. It owns the - // elements in the vector. - std::vector test_cases_; - - // Provides a level of indirection for the test case list to allow - // easy shuffling and restoring the test case order. The i-th - // element of this vector is the index of the i-th test case in the - // shuffled order. - std::vector test_case_indices_; - -#if GTEST_HAS_PARAM_TEST - // ParameterizedTestRegistry object used to register value-parameterized - // tests. - internal::ParameterizedTestCaseRegistry parameterized_test_registry_; - - // Indicates whether RegisterParameterizedTests() has been called already. - bool parameterized_tests_registered_; -#endif // GTEST_HAS_PARAM_TEST - - // Index of the last death test case registered. Initially -1. - int last_death_test_case_; - - // This points to the TestCase for the currently running test. It - // changes as Google Test goes through one test case after another. - // When no test is running, this is set to NULL and Google Test - // stores assertion results in ad_hoc_test_result_. Initially NULL. - TestCase* current_test_case_; - - // This points to the TestInfo for the currently running test. It - // changes as Google Test goes through one test after another. When - // no test is running, this is set to NULL and Google Test stores - // assertion results in ad_hoc_test_result_. Initially NULL. - TestInfo* current_test_info_; - - // Normally, a user only writes assertions inside a TEST or TEST_F, - // or inside a function called by a TEST or TEST_F. Since Google - // Test keeps track of which test is current running, it can - // associate such an assertion with the test it belongs to. - // - // If an assertion is encountered when no TEST or TEST_F is running, - // Google Test attributes the assertion result to an imaginary "ad hoc" - // test, and records the result in ad_hoc_test_result_. - TestResult ad_hoc_test_result_; - - // The list of event listeners that can be used to track events inside - // Google Test. - TestEventListeners listeners_; - - // The OS stack trace getter. Will be deleted when the UnitTest - // object is destructed. By default, an OsStackTraceGetter is used, - // but the user can set this field to use a custom getter if that is - // desired. - OsStackTraceGetterInterface* os_stack_trace_getter_; - - // True iff PostFlagParsingInit() has been called. - bool post_flag_parse_init_performed_; - - // The random number seed used at the beginning of the test run. - int random_seed_; - - // Our random number generator. - internal::Random random_; - - // How long the test took to run, in milliseconds. - TimeInMillis elapsed_time_; - -#if GTEST_HAS_DEATH_TEST - // The decomposed components of the gtest_internal_run_death_test flag, - // parsed when RUN_ALL_TESTS is called. - internal::scoped_ptr internal_run_death_test_flag_; - internal::scoped_ptr death_test_factory_; -#endif // GTEST_HAS_DEATH_TEST - - // A per-thread stack of traces created by the SCOPED_TRACE() macro. - internal::ThreadLocal > gtest_trace_stack_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl); -}; // class UnitTestImpl - -// Convenience function for accessing the global UnitTest -// implementation object. -inline UnitTestImpl* GetUnitTestImpl() { - return UnitTest::GetInstance()->impl(); -} - -// Internal helper functions for implementing the simple regular -// expression matcher. -GTEST_API_ bool IsInSet(char ch, const char* str); -GTEST_API_ bool IsDigit(char ch); -GTEST_API_ bool IsPunct(char ch); -GTEST_API_ bool IsRepeat(char ch); -GTEST_API_ bool IsWhiteSpace(char ch); -GTEST_API_ bool IsWordChar(char ch); -GTEST_API_ bool IsValidEscape(char ch); -GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch); -GTEST_API_ bool ValidateRegex(const char* regex); -GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str); -GTEST_API_ bool MatchRepetitionAndRegexAtHead( - bool escaped, char ch, char repeat, const char* regex, const char* str); -GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str); - -// Parses the command line for Google Test flags, without initializing -// other parts of Google Test. -GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv); -GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); - -#if GTEST_HAS_DEATH_TEST - -// Returns the message describing the last system error, regardless of the -// platform. -String GetLastErrnoDescription(); - -#if GTEST_OS_WINDOWS -// Provides leak-safe Windows kernel handle ownership. -class AutoHandle { - public: - AutoHandle() : handle_(INVALID_HANDLE_VALUE) {} - explicit AutoHandle(HANDLE handle) : handle_(handle) {} - - ~AutoHandle() { Reset(); } - - HANDLE Get() const { return handle_; } - void Reset() { Reset(INVALID_HANDLE_VALUE); } - void Reset(HANDLE handle) { - if (handle != handle_) { - if (handle_ != INVALID_HANDLE_VALUE) - ::CloseHandle(handle_); - handle_ = handle; - } - } - - private: - HANDLE handle_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); -}; -#endif // GTEST_OS_WINDOWS - -// Attempts to parse a string into a positive integer pointed to by the -// number parameter. Returns true if that is possible. -// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use -// it here. -template -bool ParseNaturalNumber(const ::std::string& str, Integer* number) { - // Fail fast if the given string does not begin with a digit; - // this bypasses strtoXXX's "optional leading whitespace and plus - // or minus sign" semantics, which are undesirable here. - if (str.empty() || !isdigit(str[0])) { - return false; - } - errno = 0; - - char* end; - // BiggestConvertible is the largest integer type that system-provided - // string-to-number conversion routines can return. -#if GTEST_OS_WINDOWS && !defined(__GNUC__) - // MSVC and C++ Builder define __int64 instead of the standard long long. - typedef unsigned __int64 BiggestConvertible; - const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10); -#else - typedef unsigned long long BiggestConvertible; // NOLINT - const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10); -#endif // GTEST_OS_WINDOWS && !defined(__GNUC__) - const bool parse_success = *end == '\0' && errno == 0; - - // TODO(vladl@google.com): Convert this to compile time assertion when it is - // available. - GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed)); - - const Integer result = static_cast(parsed); - if (parse_success && static_cast(result) == parsed) { - *number = result; - return true; - } - return false; -} -#endif // GTEST_HAS_DEATH_TEST - -// TestResult contains some private methods that should be hidden from -// Google Test user but are required for testing. This class allow our tests -// to access them. -// -// This class is supplied only for the purpose of testing Google Test's own -// constructs. Do not use it in user tests, either directly or indirectly. -class TestResultAccessor { - public: - static void RecordProperty(TestResult* test_result, - const TestProperty& property) { - test_result->RecordProperty(property); - } - - static void ClearTestPartResults(TestResult* test_result) { - test_result->ClearTestPartResults(); - } - - static const std::vector& test_part_results( - const TestResult& test_result) { - return test_result.test_part_results(); - } -}; - -} // namespace internal -} // namespace testing - -#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_ -#undef GTEST_IMPLEMENTATION_ - -#if GTEST_OS_WINDOWS -#define vsnprintf _vsnprintf -#endif // GTEST_OS_WINDOWS - -namespace testing { - -using internal::CountIf; -using internal::ForEach; -using internal::GetElementOr; -using internal::Shuffle; - -// Constants. - -// A test whose test case name or test name matches this filter is -// disabled and not run. -static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*"; - -// A test case whose name matches this filter is considered a death -// test case and will be run before test cases whose name doesn't -// match this filter. -static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*"; - -// A test filter that matches everything. -static const char kUniversalFilter[] = "*"; - -// The default output file for XML output. -static const char kDefaultOutputFile[] = "test_detail.xml"; - -// The environment variable name for the test shard index. -static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; -// The environment variable name for the total number of test shards. -static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; -// The environment variable name for the test shard status file. -static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; - -namespace internal { - -// The text used in failure messages to indicate the start of the -// stack trace. -const char kStackTraceMarker[] = "\nStack trace:\n"; - -// g_help_flag is true iff the --help flag or an equivalent form is -// specified on the command line. -bool g_help_flag = false; - -} // namespace internal - -GTEST_DEFINE_bool_( - also_run_disabled_tests, - internal::BoolFromGTestEnv("also_run_disabled_tests", false), - "Run disabled tests too, in addition to the tests normally being run."); - -GTEST_DEFINE_bool_( - break_on_failure, - internal::BoolFromGTestEnv("break_on_failure", false), - "True iff a failed assertion should be a debugger break-point."); - -GTEST_DEFINE_bool_( - catch_exceptions, - internal::BoolFromGTestEnv("catch_exceptions", false), - "True iff " GTEST_NAME_ - " should catch exceptions and treat them as test failures."); - -GTEST_DEFINE_string_( - color, - internal::StringFromGTestEnv("color", "auto"), - "Whether to use colors in the output. Valid values: yes, no, " - "and auto. 'auto' means to use colors if the output is " - "being sent to a terminal and the TERM environment variable " - "is set to xterm, xterm-color, xterm-256color, linux or cygwin."); - -GTEST_DEFINE_string_( - filter, - internal::StringFromGTestEnv("filter", kUniversalFilter), - "A colon-separated list of glob (not regex) patterns " - "for filtering the tests to run, optionally followed by a " - "'-' and a : separated list of negative patterns (tests to " - "exclude). A test is run if it matches one of the positive " - "patterns and does not match any of the negative patterns."); - -GTEST_DEFINE_bool_(list_tests, false, - "List all tests without running them."); - -GTEST_DEFINE_string_( - output, - internal::StringFromGTestEnv("output", ""), - "A format (currently must be \"xml\"), optionally followed " - "by a colon and an output file name or directory. A directory " - "is indicated by a trailing pathname separator. " - "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " - "If a directory is specified, output files will be created " - "within that directory, with file-names based on the test " - "executable's name and, if necessary, made unique by adding " - "digits."); - -GTEST_DEFINE_bool_( - print_time, - internal::BoolFromGTestEnv("print_time", true), - "True iff " GTEST_NAME_ - " should display elapsed time in text output."); - -GTEST_DEFINE_int32_( - random_seed, - internal::Int32FromGTestEnv("random_seed", 0), - "Random number seed to use when shuffling test orders. Must be in range " - "[1, 99999], or 0 to use a seed based on the current time."); - -GTEST_DEFINE_int32_( - repeat, - internal::Int32FromGTestEnv("repeat", 1), - "How many times to repeat each test. Specify a negative number " - "for repeating forever. Useful for shaking out flaky tests."); - -GTEST_DEFINE_bool_( - show_internal_stack_frames, false, - "True iff " GTEST_NAME_ " should include internal stack frames when " - "printing test failure stack traces."); - -GTEST_DEFINE_bool_( - shuffle, - internal::BoolFromGTestEnv("shuffle", false), - "True iff " GTEST_NAME_ - " should randomize tests' order on every run."); - -GTEST_DEFINE_int32_( - stack_trace_depth, - internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth), - "The maximum number of stack frames to print when an " - "assertion fails. The valid range is 0 through 100, inclusive."); - -GTEST_DEFINE_bool_( - throw_on_failure, - internal::BoolFromGTestEnv("throw_on_failure", false), - "When this flag is specified, a failed assertion will throw an exception " - "if exceptions are enabled or exit the program with a non-zero code " - "otherwise."); - -namespace internal { - -// Generates a random number from [0, range), using a Linear -// Congruential Generator (LCG). Crashes if 'range' is 0 or greater -// than kMaxRange. -UInt32 Random::Generate(UInt32 range) { - // These constants are the same as are used in glibc's rand(3). - state_ = (1103515245U*state_ + 12345U) % kMaxRange; - - GTEST_CHECK_(range > 0) - << "Cannot generate a number in the range [0, 0)."; - GTEST_CHECK_(range <= kMaxRange) - << "Generation of a number in [0, " << range << ") was requested, " - << "but this can only generate numbers in [0, " << kMaxRange << ")."; - - // Converting via modulus introduces a bit of downward bias, but - // it's simple, and a linear congruential generator isn't too good - // to begin with. - return state_ % range; -} - -// GTestIsInitialized() returns true iff the user has initialized -// Google Test. Useful for catching the user mistake of not initializing -// Google Test before calling RUN_ALL_TESTS(). -// -// A user must call testing::InitGoogleTest() to initialize Google -// Test. g_init_gtest_count is set to the number of times -// InitGoogleTest() has been called. We don't protect this variable -// under a mutex as it is only accessed in the main thread. -int g_init_gtest_count = 0; -static bool GTestIsInitialized() { return g_init_gtest_count != 0; } - -// Iterates over a vector of TestCases, keeping a running sum of the -// results of calling a given int-returning method on each. -// Returns the sum. -static int SumOverTestCaseList(const std::vector& case_list, - int (TestCase::*method)() const) { - int sum = 0; - for (size_t i = 0; i < case_list.size(); i++) { - sum += (case_list[i]->*method)(); - } - return sum; -} - -// Returns true iff the test case passed. -static bool TestCasePassed(const TestCase* test_case) { - return test_case->should_run() && test_case->Passed(); -} - -// Returns true iff the test case failed. -static bool TestCaseFailed(const TestCase* test_case) { - return test_case->should_run() && test_case->Failed(); -} - -// Returns true iff test_case contains at least one test that should -// run. -static bool ShouldRunTestCase(const TestCase* test_case) { - return test_case->should_run(); -} - -// AssertHelper constructor. -AssertHelper::AssertHelper(TestPartResult::Type type, - const char* file, - int line, - const char* message) - : data_(new AssertHelperData(type, file, line, message)) { -} - -AssertHelper::~AssertHelper() { - delete data_; -} - -// Message assignment, for assertion streaming support. -void AssertHelper::operator=(const Message& message) const { - UnitTest::GetInstance()-> - AddTestPartResult(data_->type, data_->file, data_->line, - AppendUserMessage(data_->message, message), - UnitTest::GetInstance()->impl() - ->CurrentOsStackTraceExceptTop(1) - // Skips the stack frame for this function itself. - ); // NOLINT -} - -// Mutex for linked pointers. -GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); - -// Application pathname gotten in InitGoogleTest. -String g_executable_path; - -// Returns the current application's name, removing directory path if that -// is present. -FilePath GetCurrentExecutableName() { - FilePath result; - -#if GTEST_OS_WINDOWS - result.Set(FilePath(g_executable_path).RemoveExtension("exe")); -#else - result.Set(FilePath(g_executable_path)); -#endif // GTEST_OS_WINDOWS - - return result.RemoveDirectoryName(); -} - -// Functions for processing the gtest_output flag. - -// Returns the output format, or "" for normal printed output. -String UnitTestOptions::GetOutputFormat() { - const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); - if (gtest_output_flag == NULL) return String(""); - - const char* const colon = strchr(gtest_output_flag, ':'); - return (colon == NULL) ? - String(gtest_output_flag) : - String(gtest_output_flag, colon - gtest_output_flag); -} - -// Returns the name of the requested output file, or the default if none -// was explicitly specified. -String UnitTestOptions::GetAbsolutePathToOutputFile() { - const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); - if (gtest_output_flag == NULL) - return String(""); - - const char* const colon = strchr(gtest_output_flag, ':'); - if (colon == NULL) - return String(internal::FilePath::ConcatPaths( - internal::FilePath( - UnitTest::GetInstance()->original_working_dir()), - internal::FilePath(kDefaultOutputFile)).ToString() ); - - internal::FilePath output_name(colon + 1); - if (!output_name.IsAbsolutePath()) - // TODO(wan@google.com): on Windows \some\path is not an absolute - // path (as its meaning depends on the current drive), yet the - // following logic for turning it into an absolute path is wrong. - // Fix it. - output_name = internal::FilePath::ConcatPaths( - internal::FilePath(UnitTest::GetInstance()->original_working_dir()), - internal::FilePath(colon + 1)); - - if (!output_name.IsDirectory()) - return output_name.ToString(); - - internal::FilePath result(internal::FilePath::GenerateUniqueFileName( - output_name, internal::GetCurrentExecutableName(), - GetOutputFormat().c_str())); - return result.ToString(); -} - -// Returns true iff the wildcard pattern matches the string. The -// first ':' or '\0' character in pattern marks the end of it. -// -// This recursive algorithm isn't very efficient, but is clear and -// works well enough for matching test names, which are short. -bool UnitTestOptions::PatternMatchesString(const char *pattern, - const char *str) { - switch (*pattern) { - case '\0': - case ':': // Either ':' or '\0' marks the end of the pattern. - return *str == '\0'; - case '?': // Matches any single character. - return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); - case '*': // Matches any string (possibly empty) of characters. - return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || - PatternMatchesString(pattern + 1, str); - default: // Non-special character. Matches itself. - return *pattern == *str && - PatternMatchesString(pattern + 1, str + 1); - } -} - -bool UnitTestOptions::MatchesFilter(const String& name, const char* filter) { - const char *cur_pattern = filter; - for (;;) { - if (PatternMatchesString(cur_pattern, name.c_str())) { - return true; - } - - // Finds the next pattern in the filter. - cur_pattern = strchr(cur_pattern, ':'); - - // Returns if no more pattern can be found. - if (cur_pattern == NULL) { - return false; - } - - // Skips the pattern separater (the ':' character). - cur_pattern++; - } -} - -// TODO(keithray): move String function implementations to gtest-string.cc. - -// Returns true iff the user-specified filter matches the test case -// name and the test name. -bool UnitTestOptions::FilterMatchesTest(const String &test_case_name, - const String &test_name) { - const String& full_name = String::Format("%s.%s", - test_case_name.c_str(), - test_name.c_str()); - - // Split --gtest_filter at '-', if there is one, to separate into - // positive filter and negative filter portions - const char* const p = GTEST_FLAG(filter).c_str(); - const char* const dash = strchr(p, '-'); - String positive; - String negative; - if (dash == NULL) { - positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter - negative = String(""); - } else { - positive = String(p, dash - p); // Everything up to the dash - negative = String(dash+1); // Everything after the dash - if (positive.empty()) { - // Treat '-test1' as the same as '*-test1' - positive = kUniversalFilter; - } - } - - // A filter is a colon-separated list of patterns. It matches a - // test if any pattern in it matches the test. - return (MatchesFilter(full_name, positive.c_str()) && - !MatchesFilter(full_name, negative.c_str())); -} - -#if GTEST_OS_WINDOWS -// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the -// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. -// This function is useful as an __except condition. -int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { - // Google Test should handle an exception if: - // 1. the user wants it to, AND - // 2. this is not a breakpoint exception. - return (GTEST_FLAG(catch_exceptions) && - exception_code != EXCEPTION_BREAKPOINT) ? - EXCEPTION_EXECUTE_HANDLER : - EXCEPTION_CONTINUE_SEARCH; -} -#endif // GTEST_OS_WINDOWS - -} // namespace internal - -// The c'tor sets this object as the test part result reporter used by -// Google Test. The 'result' parameter specifies where to report the -// results. Intercepts only failures from the current thread. -ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( - TestPartResultArray* result) - : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD), - result_(result) { - Init(); -} - -// The c'tor sets this object as the test part result reporter used by -// Google Test. The 'result' parameter specifies where to report the -// results. -ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( - InterceptMode intercept_mode, TestPartResultArray* result) - : intercept_mode_(intercept_mode), - result_(result) { - Init(); -} - -void ScopedFakeTestPartResultReporter::Init() { - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - if (intercept_mode_ == INTERCEPT_ALL_THREADS) { - old_reporter_ = impl->GetGlobalTestPartResultReporter(); - impl->SetGlobalTestPartResultReporter(this); - } else { - old_reporter_ = impl->GetTestPartResultReporterForCurrentThread(); - impl->SetTestPartResultReporterForCurrentThread(this); - } -} - -// The d'tor restores the test part result reporter used by Google Test -// before. -ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() { - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - if (intercept_mode_ == INTERCEPT_ALL_THREADS) { - impl->SetGlobalTestPartResultReporter(old_reporter_); - } else { - impl->SetTestPartResultReporterForCurrentThread(old_reporter_); - } -} - -// Increments the test part result count and remembers the result. -// This method is from the TestPartResultReporterInterface interface. -void ScopedFakeTestPartResultReporter::ReportTestPartResult( - const TestPartResult& result) { - result_->Append(result); -} - -namespace internal { - -// Returns the type ID of ::testing::Test. We should always call this -// instead of GetTypeId< ::testing::Test>() to get the type ID of -// testing::Test. This is to work around a suspected linker bug when -// using Google Test as a framework on Mac OS X. The bug causes -// GetTypeId< ::testing::Test>() to return different values depending -// on whether the call is from the Google Test framework itself or -// from user test code. GetTestTypeId() is guaranteed to always -// return the same value, as it always calls GetTypeId<>() from the -// gtest.cc, which is within the Google Test framework. -TypeId GetTestTypeId() { - return GetTypeId(); -} - -// The value of GetTestTypeId() as seen from within the Google Test -// library. This is solely for testing GetTestTypeId(). -extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId(); - -// This predicate-formatter checks that 'results' contains a test part -// failure of the given type and that the failure message contains the -// given substring. -AssertionResult HasOneFailure(const char* /* results_expr */, - const char* /* type_expr */, - const char* /* substr_expr */, - const TestPartResultArray& results, - TestPartResult::Type type, - const char* substr) { - const String expected(type == TestPartResult::kFatalFailure ? - "1 fatal failure" : - "1 non-fatal failure"); - Message msg; - if (results.size() != 1) { - msg << "Expected: " << expected << "\n" - << " Actual: " << results.size() << " failures"; - for (int i = 0; i < results.size(); i++) { - msg << "\n" << results.GetTestPartResult(i); - } - return AssertionFailure(msg); - } - - const TestPartResult& r = results.GetTestPartResult(0); - if (r.type() != type) { - msg << "Expected: " << expected << "\n" - << " Actual:\n" - << r; - return AssertionFailure(msg); - } - - if (strstr(r.message(), substr) == NULL) { - msg << "Expected: " << expected << " containing \"" - << substr << "\"\n" - << " Actual:\n" - << r; - return AssertionFailure(msg); - } - - return AssertionSuccess(); -} - -// The constructor of SingleFailureChecker remembers where to look up -// test part results, what type of failure we expect, and what -// substring the failure message should contain. -SingleFailureChecker:: SingleFailureChecker( - const TestPartResultArray* results, - TestPartResult::Type type, - const char* substr) - : results_(results), - type_(type), - substr_(substr) {} - -// The destructor of SingleFailureChecker verifies that the given -// TestPartResultArray contains exactly one failure that has the given -// type and contains the given substring. If that's not the case, a -// non-fatal failure will be generated. -SingleFailureChecker::~SingleFailureChecker() { - EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_.c_str()); -} - -DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter( - UnitTestImpl* unit_test) : unit_test_(unit_test) {} - -void DefaultGlobalTestPartResultReporter::ReportTestPartResult( - const TestPartResult& result) { - unit_test_->current_test_result()->AddTestPartResult(result); - unit_test_->listeners()->repeater()->OnTestPartResult(result); -} - -DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter( - UnitTestImpl* unit_test) : unit_test_(unit_test) {} - -void DefaultPerThreadTestPartResultReporter::ReportTestPartResult( - const TestPartResult& result) { - unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result); -} - -// Returns the global test part result reporter. -TestPartResultReporterInterface* -UnitTestImpl::GetGlobalTestPartResultReporter() { - internal::MutexLock lock(&global_test_part_result_reporter_mutex_); - return global_test_part_result_repoter_; -} - -// Sets the global test part result reporter. -void UnitTestImpl::SetGlobalTestPartResultReporter( - TestPartResultReporterInterface* reporter) { - internal::MutexLock lock(&global_test_part_result_reporter_mutex_); - global_test_part_result_repoter_ = reporter; -} - -// Returns the test part result reporter for the current thread. -TestPartResultReporterInterface* -UnitTestImpl::GetTestPartResultReporterForCurrentThread() { - return per_thread_test_part_result_reporter_.get(); -} - -// Sets the test part result reporter for the current thread. -void UnitTestImpl::SetTestPartResultReporterForCurrentThread( - TestPartResultReporterInterface* reporter) { - per_thread_test_part_result_reporter_.set(reporter); -} - -// Gets the number of successful test cases. -int UnitTestImpl::successful_test_case_count() const { - return CountIf(test_cases_, TestCasePassed); -} - -// Gets the number of failed test cases. -int UnitTestImpl::failed_test_case_count() const { - return CountIf(test_cases_, TestCaseFailed); -} - -// Gets the number of all test cases. -int UnitTestImpl::total_test_case_count() const { - return static_cast(test_cases_.size()); -} - -// Gets the number of all test cases that contain at least one test -// that should run. -int UnitTestImpl::test_case_to_run_count() const { - return CountIf(test_cases_, ShouldRunTestCase); -} - -// Gets the number of successful tests. -int UnitTestImpl::successful_test_count() const { - return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count); -} - -// Gets the number of failed tests. -int UnitTestImpl::failed_test_count() const { - return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); -} - -// Gets the number of disabled tests. -int UnitTestImpl::disabled_test_count() const { - return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); -} - -// Gets the number of all tests. -int UnitTestImpl::total_test_count() const { - return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); -} - -// Gets the number of tests that should run. -int UnitTestImpl::test_to_run_count() const { - return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); -} - -// Returns the current OS stack trace as a String. -// -// The maximum number of stack frames to be included is specified by -// the gtest_stack_trace_depth flag. The skip_count parameter -// specifies the number of top frames to be skipped, which doesn't -// count against the number of frames to be included. -// -// For example, if Foo() calls Bar(), which in turn calls -// CurrentOsStackTraceExceptTop(1), Foo() will be included in the -// trace but Bar() and CurrentOsStackTraceExceptTop() won't. -String UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { - (void)skip_count; - return String(""); -} - -// Returns the current time in milliseconds. -TimeInMillis GetTimeInMillis() { -#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__) - // Difference between 1970-01-01 and 1601-01-01 in milliseconds. - // http://analogous.blogspot.com/2005/04/epoch.html - const TimeInMillis kJavaEpochToWinFileTimeDelta = - static_cast(116444736UL) * 100000UL; - const DWORD kTenthMicrosInMilliSecond = 10000; - - SYSTEMTIME now_systime; - FILETIME now_filetime; - ULARGE_INTEGER now_int64; - // TODO(kenton@google.com): Shouldn't this just use - // GetSystemTimeAsFileTime()? - GetSystemTime(&now_systime); - if (SystemTimeToFileTime(&now_systime, &now_filetime)) { - now_int64.LowPart = now_filetime.dwLowDateTime; - now_int64.HighPart = now_filetime.dwHighDateTime; - now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) - - kJavaEpochToWinFileTimeDelta; - return now_int64.QuadPart; - } - return 0; -#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_ - __timeb64 now; -#ifdef _MSC_VER - // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 - // (deprecated function) there. - // TODO(kenton@google.com): Use GetTickCount()? Or use - // SystemTimeToFileTime() -#pragma warning(push) // Saves the current warning state. -#pragma warning(disable:4996) // Temporarily disables warning 4996. - _ftime64(&now); -#pragma warning(pop) // Restores the warning state. -#else - _ftime64(&now); -#endif // _MSC_VER - return static_cast(now.time) * 1000 + now.millitm; -#elif GTEST_HAS_GETTIMEOFDAY_ - struct timeval now; - gettimeofday(&now, NULL); - return static_cast(now.tv_sec) * 1000 + now.tv_usec / 1000; -#else -#error "Don't know how to get the current time on your system." -#endif -} - -// Utilities - -// class String - -// Returns the input enclosed in double quotes if it's not NULL; -// otherwise returns "(null)". For example, "\"Hello\"" is returned -// for input "Hello". -// -// This is useful for printing a C string in the syntax of a literal. -// -// Known issue: escape sequences are not handled yet. -String String::ShowCStringQuoted(const char* c_str) { - return c_str ? String::Format("\"%s\"", c_str) : String("(null)"); -} - -// Copies at most length characters from str into a newly-allocated -// piece of memory of size length+1. The memory is allocated with new[]. -// A terminating null byte is written to the memory, and a pointer to it -// is returned. If str is NULL, NULL is returned. -static char* CloneString(const char* str, size_t length) { - if (str == NULL) { - return NULL; - } else { - char* const clone = new char[length + 1]; - posix::StrNCpy(clone, str, length); - clone[length] = '\0'; - return clone; - } -} - -// Clones a 0-terminated C string, allocating memory using new. The -// caller is responsible for deleting[] the return value. Returns the -// cloned string, or NULL if the input is NULL. -const char * String::CloneCString(const char* c_str) { - return (c_str == NULL) ? - NULL : CloneString(c_str, strlen(c_str)); -} - -#if GTEST_OS_WINDOWS_MOBILE -// Creates a UTF-16 wide string from the given ANSI string, allocating -// memory using new. The caller is responsible for deleting the return -// value using delete[]. Returns the wide string, or NULL if the -// input is NULL. -LPCWSTR String::AnsiToUtf16(const char* ansi) { - if (!ansi) return NULL; - const int length = strlen(ansi); - const int unicode_length = - MultiByteToWideChar(CP_ACP, 0, ansi, length, - NULL, 0); - WCHAR* unicode = new WCHAR[unicode_length + 1]; - MultiByteToWideChar(CP_ACP, 0, ansi, length, - unicode, unicode_length); - unicode[unicode_length] = 0; - return unicode; -} - -// Creates an ANSI string from the given wide string, allocating -// memory using new. The caller is responsible for deleting the return -// value using delete[]. Returns the ANSI string, or NULL if the -// input is NULL. -const char* String::Utf16ToAnsi(LPCWSTR utf16_str) { - if (!utf16_str) return NULL; - const int ansi_length = - WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, - NULL, 0, NULL, NULL); - char* ansi = new char[ansi_length + 1]; - WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, - ansi, ansi_length, NULL, NULL); - ansi[ansi_length] = 0; - return ansi; -} - -#endif // GTEST_OS_WINDOWS_MOBILE - -// Compares two C strings. Returns true iff they have the same content. -// -// Unlike strcmp(), this function can handle NULL argument(s). A NULL -// C string is considered different to any non-NULL C string, -// including the empty string. -bool String::CStringEquals(const char * lhs, const char * rhs) { - if ( lhs == NULL ) return rhs == NULL; - - if ( rhs == NULL ) return false; - - return strcmp(lhs, rhs) == 0; -} - -#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING - -// Converts an array of wide chars to a narrow string using the UTF-8 -// encoding, and streams the result to the given Message object. -static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, - Message* msg) { - // TODO(wan): consider allowing a testing::String object to - // contain '\0'. This will make it behave more like std::string, - // and will allow ToUtf8String() to return the correct encoding - // for '\0' s.t. we can get rid of the conditional here (and in - // several other places). - for (size_t i = 0; i != length; ) { // NOLINT - if (wstr[i] != L'\0') { - *msg << WideStringToUtf8(wstr + i, static_cast(length - i)); - while (i != length && wstr[i] != L'\0') - i++; - } else { - *msg << '\0'; - i++; - } - } -} - -#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING - -} // namespace internal - -#if GTEST_HAS_STD_WSTRING -// Converts the given wide string to a narrow string using the UTF-8 -// encoding, and streams the result to this Message object. -Message& Message::operator <<(const ::std::wstring& wstr) { - internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); - return *this; -} -#endif // GTEST_HAS_STD_WSTRING - -#if GTEST_HAS_GLOBAL_WSTRING -// Converts the given wide string to a narrow string using the UTF-8 -// encoding, and streams the result to this Message object. -Message& Message::operator <<(const ::wstring& wstr) { - internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); - return *this; -} -#endif // GTEST_HAS_GLOBAL_WSTRING - -namespace internal { - -// Formats a value to be used in a failure message. - -// For a char value, we print it as a C++ char literal and as an -// unsigned integer (both in decimal and in hexadecimal). -String FormatForFailureMessage(char ch) { - const unsigned int ch_as_uint = ch; - // A String object cannot contain '\0', so we print "\\0" when ch is - // '\0'. - return String::Format("'%s' (%u, 0x%X)", - ch ? String::Format("%c", ch).c_str() : "\\0", - ch_as_uint, ch_as_uint); -} - -// For a wchar_t value, we print it as a C++ wchar_t literal and as an -// unsigned integer (both in decimal and in hexidecimal). -String FormatForFailureMessage(wchar_t wchar) { - // The C++ standard doesn't specify the exact size of the wchar_t - // type. It just says that it shall have the same size as another - // integral type, called its underlying type. - // - // Therefore, in order to print a wchar_t value in the numeric form, - // we first convert it to the largest integral type (UInt64) and - // then print the converted value. - // - // We use streaming to print the value as "%llu" doesn't work - // correctly with MSVC 7.1. - const UInt64 wchar_as_uint64 = wchar; - Message msg; - // A String object cannot contain '\0', so we print "\\0" when wchar is - // L'\0'. - char buffer[32]; // CodePointToUtf8 requires a buffer that big. - msg << "L'" - << (wchar ? CodePointToUtf8(static_cast(wchar), buffer) : "\\0") - << "' (" << wchar_as_uint64 << ", 0x" << ::std::setbase(16) - << wchar_as_uint64 << ")"; - return msg.GetString(); -} - -} // namespace internal - -// AssertionResult constructors. -// Used in EXPECT_TRUE/FALSE(assertion_result). -AssertionResult::AssertionResult(const AssertionResult& other) - : success_(other.success_), - message_(other.message_.get() != NULL ? - new internal::String(*other.message_) : - static_cast(NULL)) { -} - -// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. -AssertionResult AssertionResult::operator!() const { - AssertionResult negation(!success_); - if (message_.get() != NULL) - negation << *message_; - return negation; -} - -// Makes a successful assertion result. -AssertionResult AssertionSuccess() { - return AssertionResult(true); -} - -// Makes a failed assertion result. -AssertionResult AssertionFailure() { - return AssertionResult(false); -} - -// Makes a failed assertion result with the given failure message. -// Deprecated; use AssertionFailure() << message. -AssertionResult AssertionFailure(const Message& message) { - return AssertionFailure() << message; -} - -namespace internal { - -// Constructs and returns the message for an equality assertion -// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. -// -// The first four parameters are the expressions used in the assertion -// and their values, as strings. For example, for ASSERT_EQ(foo, bar) -// where foo is 5 and bar is 6, we have: -// -// expected_expression: "foo" -// actual_expression: "bar" -// expected_value: "5" -// actual_value: "6" -// -// The ignoring_case parameter is true iff the assertion is a -// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will -// be inserted into the message. -AssertionResult EqFailure(const char* expected_expression, - const char* actual_expression, - const String& expected_value, - const String& actual_value, - bool ignoring_case) { - Message msg; - msg << "Value of: " << actual_expression; - if (actual_value != actual_expression) { - msg << "\n Actual: " << actual_value; - } - - msg << "\nExpected: " << expected_expression; - if (ignoring_case) { - msg << " (ignoring case)"; - } - if (expected_value != expected_expression) { - msg << "\nWhich is: " << expected_value; - } - - return AssertionFailure(msg); -} - -// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. -String GetBoolAssertionFailureMessage(const AssertionResult& assertion_result, - const char* expression_text, - const char* actual_predicate_value, - const char* expected_predicate_value) { - const char* actual_message = assertion_result.message(); - Message msg; - msg << "Value of: " << expression_text - << "\n Actual: " << actual_predicate_value; - if (actual_message[0] != '\0') - msg << " (" << actual_message << ")"; - msg << "\nExpected: " << expected_predicate_value; - return msg.GetString(); -} - -// Helper function for implementing ASSERT_NEAR. -AssertionResult DoubleNearPredFormat(const char* expr1, - const char* expr2, - const char* abs_error_expr, - double val1, - double val2, - double abs_error) { - const double diff = fabs(val1 - val2); - if (diff <= abs_error) return AssertionSuccess(); - - // TODO(wan): do not print the value of an expression if it's - // already a literal. - Message msg; - msg << "The difference between " << expr1 << " and " << expr2 - << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" - << expr1 << " evaluates to " << val1 << ",\n" - << expr2 << " evaluates to " << val2 << ", and\n" - << abs_error_expr << " evaluates to " << abs_error << "."; - return AssertionFailure(msg); -} - - -// Helper template for implementing FloatLE() and DoubleLE(). -template -AssertionResult FloatingPointLE(const char* expr1, - const char* expr2, - RawType val1, - RawType val2) { - // Returns success if val1 is less than val2, - if (val1 < val2) { - return AssertionSuccess(); - } - - // or if val1 is almost equal to val2. - const FloatingPoint lhs(val1), rhs(val2); - if (lhs.AlmostEquals(rhs)) { - return AssertionSuccess(); - } - - // Note that the above two checks will both fail if either val1 or - // val2 is NaN, as the IEEE floating-point standard requires that - // any predicate involving a NaN must return false. - - StrStream val1_ss; - val1_ss << std::setprecision(std::numeric_limits::digits10 + 2) - << val1; - - StrStream val2_ss; - val2_ss << std::setprecision(std::numeric_limits::digits10 + 2) - << val2; - - Message msg; - msg << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" - << " Actual: " << StrStreamToString(&val1_ss) << " vs " - << StrStreamToString(&val2_ss); - - return AssertionFailure(msg); -} - -} // namespace internal - -// Asserts that val1 is less than, or almost equal to, val2. Fails -// otherwise. In particular, it fails if either val1 or val2 is NaN. -AssertionResult FloatLE(const char* expr1, const char* expr2, - float val1, float val2) { - return internal::FloatingPointLE(expr1, expr2, val1, val2); -} - -// Asserts that val1 is less than, or almost equal to, val2. Fails -// otherwise. In particular, it fails if either val1 or val2 is NaN. -AssertionResult DoubleLE(const char* expr1, const char* expr2, - double val1, double val2) { - return internal::FloatingPointLE(expr1, expr2, val1, val2); -} - -namespace internal { - -// The helper function for {ASSERT|EXPECT}_EQ with int or enum -// arguments. -AssertionResult CmpHelperEQ(const char* expected_expression, - const char* actual_expression, - BiggestInt expected, - BiggestInt actual) { - if (expected == actual) { - return AssertionSuccess(); - } - - return EqFailure(expected_expression, - actual_expression, - FormatForComparisonFailureMessage(expected, actual), - FormatForComparisonFailureMessage(actual, expected), - false); -} - -// A macro for implementing the helper functions needed to implement -// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here -// just to avoid copy-and-paste of similar code. -#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ -AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ - BiggestInt val1, BiggestInt val2) {\ - if (val1 op val2) {\ - return AssertionSuccess();\ - } else {\ - Message msg;\ - msg << "Expected: (" << expr1 << ") " #op " (" << expr2\ - << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ - << " vs " << FormatForComparisonFailureMessage(val2, val1);\ - return AssertionFailure(msg);\ - }\ -} - -// Implements the helper function for {ASSERT|EXPECT}_NE with int or -// enum arguments. -GTEST_IMPL_CMP_HELPER_(NE, !=) -// Implements the helper function for {ASSERT|EXPECT}_LE with int or -// enum arguments. -GTEST_IMPL_CMP_HELPER_(LE, <=) -// Implements the helper function for {ASSERT|EXPECT}_LT with int or -// enum arguments. -GTEST_IMPL_CMP_HELPER_(LT, < ) -// Implements the helper function for {ASSERT|EXPECT}_GE with int or -// enum arguments. -GTEST_IMPL_CMP_HELPER_(GE, >=) -// Implements the helper function for {ASSERT|EXPECT}_GT with int or -// enum arguments. -GTEST_IMPL_CMP_HELPER_(GT, > ) - -#undef GTEST_IMPL_CMP_HELPER_ - -// The helper function for {ASSERT|EXPECT}_STREQ. -AssertionResult CmpHelperSTREQ(const char* expected_expression, - const char* actual_expression, - const char* expected, - const char* actual) { - if (String::CStringEquals(expected, actual)) { - return AssertionSuccess(); - } - - return EqFailure(expected_expression, - actual_expression, - String::ShowCStringQuoted(expected), - String::ShowCStringQuoted(actual), - false); -} - -// The helper function for {ASSERT|EXPECT}_STRCASEEQ. -AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, - const char* actual_expression, - const char* expected, - const char* actual) { - if (String::CaseInsensitiveCStringEquals(expected, actual)) { - return AssertionSuccess(); - } - - return EqFailure(expected_expression, - actual_expression, - String::ShowCStringQuoted(expected), - String::ShowCStringQuoted(actual), - true); -} - -// The helper function for {ASSERT|EXPECT}_STRNE. -AssertionResult CmpHelperSTRNE(const char* s1_expression, - const char* s2_expression, - const char* s1, - const char* s2) { - if (!String::CStringEquals(s1, s2)) { - return AssertionSuccess(); - } else { - Message msg; - msg << "Expected: (" << s1_expression << ") != (" - << s2_expression << "), actual: \"" - << s1 << "\" vs \"" << s2 << "\""; - return AssertionFailure(msg); - } -} - -// The helper function for {ASSERT|EXPECT}_STRCASENE. -AssertionResult CmpHelperSTRCASENE(const char* s1_expression, - const char* s2_expression, - const char* s1, - const char* s2) { - if (!String::CaseInsensitiveCStringEquals(s1, s2)) { - return AssertionSuccess(); - } else { - Message msg; - msg << "Expected: (" << s1_expression << ") != (" - << s2_expression << ") (ignoring case), actual: \"" - << s1 << "\" vs \"" << s2 << "\""; - return AssertionFailure(msg); - } -} - -} // namespace internal - -namespace { - -// Helper functions for implementing IsSubString() and IsNotSubstring(). - -// This group of overloaded functions return true iff needle is a -// substring of haystack. NULL is considered a substring of itself -// only. - -bool IsSubstringPred(const char* needle, const char* haystack) { - if (needle == NULL || haystack == NULL) - return needle == haystack; - - return strstr(haystack, needle) != NULL; -} - -bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) { - if (needle == NULL || haystack == NULL) - return needle == haystack; - - return wcsstr(haystack, needle) != NULL; -} - -// StringType here can be either ::std::string or ::std::wstring. -template -bool IsSubstringPred(const StringType& needle, - const StringType& haystack) { - return haystack.find(needle) != StringType::npos; -} - -// This function implements either IsSubstring() or IsNotSubstring(), -// depending on the value of the expected_to_be_substring parameter. -// StringType here can be const char*, const wchar_t*, ::std::string, -// or ::std::wstring. -template -AssertionResult IsSubstringImpl( - bool expected_to_be_substring, - const char* needle_expr, const char* haystack_expr, - const StringType& needle, const StringType& haystack) { - if (IsSubstringPred(needle, haystack) == expected_to_be_substring) - return AssertionSuccess(); - - const bool is_wide_string = sizeof(needle[0]) > 1; - const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; - return AssertionFailure( - Message() - << "Value of: " << needle_expr << "\n" - << " Actual: " << begin_string_quote << needle << "\"\n" - << "Expected: " << (expected_to_be_substring ? "" : "not ") - << "a substring of " << haystack_expr << "\n" - << "Which is: " << begin_string_quote << haystack << "\""); -} - -} // namespace - -// IsSubstring() and IsNotSubstring() check whether needle is a -// substring of haystack (NULL is considered a substring of itself -// only), and return an appropriate error message when they fail. - -AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const char* needle, const char* haystack) { - return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); -} - -AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const wchar_t* needle, const wchar_t* haystack) { - return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); -} - -AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const char* needle, const char* haystack) { - return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); -} - -AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const wchar_t* needle, const wchar_t* haystack) { - return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); -} - -AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::string& needle, const ::std::string& haystack) { - return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); -} - -AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::string& needle, const ::std::string& haystack) { - return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); -} - -#if GTEST_HAS_STD_WSTRING -AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::wstring& needle, const ::std::wstring& haystack) { - return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); -} - -AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::wstring& needle, const ::std::wstring& haystack) { - return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); -} -#endif // GTEST_HAS_STD_WSTRING - -namespace internal { - -#if GTEST_OS_WINDOWS - -namespace { - -// Helper function for IsHRESULT{SuccessFailure} predicates -AssertionResult HRESULTFailureHelper(const char* expr, - const char* expected, - long hr) { // NOLINT -#if GTEST_OS_WINDOWS_MOBILE - // Windows CE doesn't support FormatMessage. - const char error_text[] = ""; -#else - // Looks up the human-readable system message for the HRESULT code - // and since we're not passing any params to FormatMessage, we don't - // want inserts expanded. - const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS; - const DWORD kBufSize = 4096; // String::Format can't exceed this length. - // Gets the system's human readable message string for this HRESULT. - char error_text[kBufSize] = { '\0' }; - DWORD message_length = ::FormatMessageA(kFlags, - 0, // no source, we're asking system - hr, // the error - 0, // no line width restrictions - error_text, // output buffer - kBufSize, // buf size - NULL); // no arguments for inserts - // Trims tailing white space (FormatMessage leaves a trailing cr-lf) - for (; message_length && isspace(error_text[message_length - 1]); - --message_length) { - error_text[message_length - 1] = '\0'; - } -#endif // GTEST_OS_WINDOWS_MOBILE - - const String error_hex(String::Format("0x%08X ", hr)); - Message msg; - msg << "Expected: " << expr << " " << expected << ".\n" - << " Actual: " << error_hex << error_text << "\n"; - - return ::testing::AssertionFailure(msg); -} - -} // namespace - -AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT - if (SUCCEEDED(hr)) { - return AssertionSuccess(); - } - return HRESULTFailureHelper(expr, "succeeds", hr); -} - -AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT - if (FAILED(hr)) { - return AssertionSuccess(); - } - return HRESULTFailureHelper(expr, "fails", hr); -} - -#endif // GTEST_OS_WINDOWS - -// Utility functions for encoding Unicode text (wide strings) in -// UTF-8. - -// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8 -// like this: -// -// Code-point length Encoding -// 0 - 7 bits 0xxxxxxx -// 8 - 11 bits 110xxxxx 10xxxxxx -// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx -// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - -// The maximum code-point a one-byte UTF-8 sequence can represent. -const UInt32 kMaxCodePoint1 = (static_cast(1) << 7) - 1; - -// The maximum code-point a two-byte UTF-8 sequence can represent. -const UInt32 kMaxCodePoint2 = (static_cast(1) << (5 + 6)) - 1; - -// The maximum code-point a three-byte UTF-8 sequence can represent. -const UInt32 kMaxCodePoint3 = (static_cast(1) << (4 + 2*6)) - 1; - -// The maximum code-point a four-byte UTF-8 sequence can represent. -const UInt32 kMaxCodePoint4 = (static_cast(1) << (3 + 3*6)) - 1; - -// Chops off the n lowest bits from a bit pattern. Returns the n -// lowest bits. As a side effect, the original bit pattern will be -// shifted to the right by n bits. -inline UInt32 ChopLowBits(UInt32* bits, int n) { - const UInt32 low_bits = *bits & ((static_cast(1) << n) - 1); - *bits >>= n; - return low_bits; -} - -// Converts a Unicode code point to a narrow string in UTF-8 encoding. -// code_point parameter is of type UInt32 because wchar_t may not be -// wide enough to contain a code point. -// The output buffer str must containt at least 32 characters. -// The function returns the address of the output buffer. -// If the code_point is not a valid Unicode code point -// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output -// as '(Invalid Unicode 0xXXXXXXXX)'. -char* CodePointToUtf8(UInt32 code_point, char* str) { - if (code_point <= kMaxCodePoint1) { - str[1] = '\0'; - str[0] = static_cast(code_point); // 0xxxxxxx - } else if (code_point <= kMaxCodePoint2) { - str[2] = '\0'; - str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx - str[0] = static_cast(0xC0 | code_point); // 110xxxxx - } else if (code_point <= kMaxCodePoint3) { - str[3] = '\0'; - str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx - str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx - str[0] = static_cast(0xE0 | code_point); // 1110xxxx - } else if (code_point <= kMaxCodePoint4) { - str[4] = '\0'; - str[3] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx - str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx - str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx - str[0] = static_cast(0xF0 | code_point); // 11110xxx - } else { - // The longest string String::Format can produce when invoked - // with these parameters is 28 character long (not including - // the terminating nul character). We are asking for 32 character - // buffer just in case. This is also enough for strncpy to - // null-terminate the destination string. - posix::StrNCpy( - str, String::Format("(Invalid Unicode 0x%X)", code_point).c_str(), 32); - str[31] = '\0'; // Makes sure no change in the format to strncpy leaves - // the result unterminated. - } - return str; -} - -// The following two functions only make sense if the the system -// uses UTF-16 for wide string encoding. All supported systems -// with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16. - -// Determines if the arguments constitute UTF-16 surrogate pair -// and thus should be combined into a single Unicode code point -// using CreateCodePointFromUtf16SurrogatePair. -inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) { - return sizeof(wchar_t) == 2 && - (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00; -} - -// Creates a Unicode code point from UTF16 surrogate pair. -inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first, - wchar_t second) { - const UInt32 mask = (1 << 10) - 1; - return (sizeof(wchar_t) == 2) ? - (((first & mask) << 10) | (second & mask)) + 0x10000 : - // This function should not be called when the condition is - // false, but we provide a sensible default in case it is. - static_cast(first); -} - -// Converts a wide string to a narrow string in UTF-8 encoding. -// The wide string is assumed to have the following encoding: -// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) -// UTF-32 if sizeof(wchar_t) == 4 (on Linux) -// Parameter str points to a null-terminated wide string. -// Parameter num_chars may additionally limit the number -// of wchar_t characters processed. -1 is used when the entire string -// should be processed. -// If the string contains code points that are not valid Unicode code points -// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output -// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding -// and contains invalid UTF-16 surrogate pairs, values in those pairs -// will be encoded as individual Unicode characters from Basic Normal Plane. -String WideStringToUtf8(const wchar_t* str, int num_chars) { - if (num_chars == -1) - num_chars = static_cast(wcslen(str)); - - StrStream stream; - for (int i = 0; i < num_chars; ++i) { - UInt32 unicode_code_point; - - if (str[i] == L'\0') { - break; - } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) { - unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i], - str[i + 1]); - i++; - } else { - unicode_code_point = static_cast(str[i]); - } - - char buffer[32]; // CodePointToUtf8 requires a buffer this big. - stream << CodePointToUtf8(unicode_code_point, buffer); - } - return StrStreamToString(&stream); -} - -// Converts a wide C string to a String using the UTF-8 encoding. -// NULL will be converted to "(null)". -String String::ShowWideCString(const wchar_t * wide_c_str) { - if (wide_c_str == NULL) return String("(null)"); - - return String(internal::WideStringToUtf8(wide_c_str, -1).c_str()); -} - -// Similar to ShowWideCString(), except that this function encloses -// the converted string in double quotes. -String String::ShowWideCStringQuoted(const wchar_t* wide_c_str) { - if (wide_c_str == NULL) return String("(null)"); - - return String::Format("L\"%s\"", - String::ShowWideCString(wide_c_str).c_str()); -} - -// Compares two wide C strings. Returns true iff they have the same -// content. -// -// Unlike wcscmp(), this function can handle NULL argument(s). A NULL -// C string is considered different to any non-NULL C string, -// including the empty string. -bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) { - if (lhs == NULL) return rhs == NULL; - - if (rhs == NULL) return false; - - return wcscmp(lhs, rhs) == 0; -} - -// Helper function for *_STREQ on wide strings. -AssertionResult CmpHelperSTREQ(const char* expected_expression, - const char* actual_expression, - const wchar_t* expected, - const wchar_t* actual) { - if (String::WideCStringEquals(expected, actual)) { - return AssertionSuccess(); - } - - return EqFailure(expected_expression, - actual_expression, - String::ShowWideCStringQuoted(expected), - String::ShowWideCStringQuoted(actual), - false); -} - -// Helper function for *_STRNE on wide strings. -AssertionResult CmpHelperSTRNE(const char* s1_expression, - const char* s2_expression, - const wchar_t* s1, - const wchar_t* s2) { - if (!String::WideCStringEquals(s1, s2)) { - return AssertionSuccess(); - } - - Message msg; - msg << "Expected: (" << s1_expression << ") != (" - << s2_expression << "), actual: " - << String::ShowWideCStringQuoted(s1) - << " vs " << String::ShowWideCStringQuoted(s2); - return AssertionFailure(msg); -} - -// Compares two C strings, ignoring case. Returns true iff they have -// the same content. -// -// Unlike strcasecmp(), this function can handle NULL argument(s). A -// NULL C string is considered different to any non-NULL C string, -// including the empty string. -bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { - if (lhs == NULL) - return rhs == NULL; - if (rhs == NULL) - return false; - return posix::StrCaseCmp(lhs, rhs) == 0; -} - - // Compares two wide C strings, ignoring case. Returns true iff they - // have the same content. - // - // Unlike wcscasecmp(), this function can handle NULL argument(s). - // A NULL C string is considered different to any non-NULL wide C string, - // including the empty string. - // NB: The implementations on different platforms slightly differ. - // On windows, this method uses _wcsicmp which compares according to LC_CTYPE - // environment variable. On GNU platform this method uses wcscasecmp - // which compares according to LC_CTYPE category of the current locale. - // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the - // current locale. -bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, - const wchar_t* rhs) { - if ( lhs == NULL ) return rhs == NULL; - - if ( rhs == NULL ) return false; - -#if GTEST_OS_WINDOWS - return _wcsicmp(lhs, rhs) == 0; -#elif GTEST_OS_LINUX - return wcscasecmp(lhs, rhs) == 0; -#else - // Mac OS X and Cygwin don't define wcscasecmp. Other unknown OSes - // may not define it either. - wint_t left, right; - do { - left = towlower(*lhs++); - right = towlower(*rhs++); - } while (left && left == right); - return left == right; -#endif // OS selector -} - -// Compares this with another String. -// Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 -// if this is greater than rhs. -int String::Compare(const String & rhs) const { - const char* const lhs_c_str = c_str(); - const char* const rhs_c_str = rhs.c_str(); - - if (lhs_c_str == NULL) { - return rhs_c_str == NULL ? 0 : -1; // NULL < anything except NULL - } else if (rhs_c_str == NULL) { - return 1; - } - - const size_t shorter_str_len = - length() <= rhs.length() ? length() : rhs.length(); - for (size_t i = 0; i != shorter_str_len; i++) { - if (lhs_c_str[i] < rhs_c_str[i]) { - return -1; - } else if (lhs_c_str[i] > rhs_c_str[i]) { - return 1; - } - } - return (length() < rhs.length()) ? -1 : - (length() > rhs.length()) ? 1 : 0; -} - -// Returns true iff this String ends with the given suffix. *Any* -// String is considered to end with a NULL or empty suffix. -bool String::EndsWith(const char* suffix) const { - if (suffix == NULL || CStringEquals(suffix, "")) return true; - - if (c_str() == NULL) return false; - - const size_t this_len = strlen(c_str()); - const size_t suffix_len = strlen(suffix); - return (this_len >= suffix_len) && - CStringEquals(c_str() + this_len - suffix_len, suffix); -} - -// Returns true iff this String ends with the given suffix, ignoring case. -// Any String is considered to end with a NULL or empty suffix. -bool String::EndsWithCaseInsensitive(const char* suffix) const { - if (suffix == NULL || CStringEquals(suffix, "")) return true; - - if (c_str() == NULL) return false; - - const size_t this_len = strlen(c_str()); - const size_t suffix_len = strlen(suffix); - return (this_len >= suffix_len) && - CaseInsensitiveCStringEquals(c_str() + this_len - suffix_len, suffix); -} - -// Formats a list of arguments to a String, using the same format -// spec string as for printf. -// -// We do not use the StringPrintf class as it is not universally -// available. -// -// The result is limited to 4096 characters (including the tailing 0). -// If 4096 characters are not enough to format the input, or if -// there's an error, "" is -// returned. -String String::Format(const char * format, ...) { - va_list args; - va_start(args, format); - - char buffer[4096]; - const int kBufferSize = sizeof(buffer)/sizeof(buffer[0]); - - // MSVC 8 deprecates vsnprintf(), so we want to suppress warning - // 4996 (deprecated function) there. -#ifdef _MSC_VER // We are using MSVC. -#pragma warning(push) // Saves the current warning state. -#pragma warning(disable:4996) // Temporarily disables warning 4996. - const int size = vsnprintf(buffer, kBufferSize, format, args); -#pragma warning(pop) // Restores the warning state. -#else // We are not using MSVC. - const int size = vsnprintf(buffer, kBufferSize, format, args); -#endif // _MSC_VER - va_end(args); - - // vsnprintf()'s behavior is not portable. When the buffer is not - // big enough, it returns a negative value in MSVC, and returns the - // needed buffer size on Linux. When there is an output error, it - // always returns a negative value. For simplicity, we lump the two - // error cases together. - if (size < 0 || size >= kBufferSize) { - return String(""); - } else { - return String(buffer, size); - } -} - -// Converts the buffer in a StrStream to a String, converting NUL -// bytes to "\\0" along the way. -String StrStreamToString(StrStream* ss) { - const ::std::string& str = ss->str(); - const char* const start = str.c_str(); - const char* const end = start + str.length(); - - // We need to use a helper StrStream to do this transformation - // because String doesn't support push_back(). - StrStream helper; - for (const char* ch = start; ch != end; ++ch) { - if (*ch == '\0') { - helper << "\\0"; // Replaces NUL with "\\0"; - } else { - helper.put(*ch); - } - } - - return String(helper.str().c_str()); -} - -// Appends the user-supplied message to the Google-Test-generated message. -String AppendUserMessage(const String& gtest_msg, - const Message& user_msg) { - // Appends the user message if it's non-empty. - const String user_msg_string = user_msg.GetString(); - if (user_msg_string.empty()) { - return gtest_msg; - } - - Message msg; - msg << gtest_msg << "\n" << user_msg_string; - - return msg.GetString(); -} - -} // namespace internal - -// class TestResult - -// Creates an empty TestResult. -TestResult::TestResult() - : death_test_count_(0), - elapsed_time_(0) { -} - -// D'tor. -TestResult::~TestResult() { -} - -// Returns the i-th test part result among all the results. i can -// range from 0 to total_part_count() - 1. If i is not in that range, -// aborts the program. -const TestPartResult& TestResult::GetTestPartResult(int i) const { - if (i < 0 || i >= total_part_count()) - internal::posix::Abort(); - return test_part_results_.at(i); -} - -// Returns the i-th test property. i can range from 0 to -// test_property_count() - 1. If i is not in that range, aborts the -// program. -const TestProperty& TestResult::GetTestProperty(int i) const { - if (i < 0 || i >= test_property_count()) - internal::posix::Abort(); - return test_properties_.at(i); -} - -// Clears the test part results. -void TestResult::ClearTestPartResults() { - test_part_results_.clear(); -} - -// Adds a test part result to the list. -void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { - test_part_results_.push_back(test_part_result); -} - -// Adds a test property to the list. If a property with the same key as the -// supplied property is already represented, the value of this test_property -// replaces the old value for that key. -void TestResult::RecordProperty(const TestProperty& test_property) { - if (!ValidateTestProperty(test_property)) { - return; - } - internal::MutexLock lock(&test_properites_mutex_); - const std::vector::iterator property_with_matching_key = - std::find_if(test_properties_.begin(), test_properties_.end(), - internal::TestPropertyKeyIs(test_property.key())); - if (property_with_matching_key == test_properties_.end()) { - test_properties_.push_back(test_property); - return; - } - property_with_matching_key->SetValue(test_property.value()); -} - -// Adds a failure if the key is a reserved attribute of Google Test -// testcase tags. Returns true if the property is valid. -bool TestResult::ValidateTestProperty(const TestProperty& test_property) { - internal::String key(test_property.key()); - if (key == "name" || key == "status" || key == "time" || key == "classname") { - ADD_FAILURE() - << "Reserved key used in RecordProperty(): " - << key - << " ('name', 'status', 'time', and 'classname' are reserved by " - << GTEST_NAME_ << ")"; - return false; - } - return true; -} - -// Clears the object. -void TestResult::Clear() { - test_part_results_.clear(); - test_properties_.clear(); - death_test_count_ = 0; - elapsed_time_ = 0; -} - -// Returns true iff the test failed. -bool TestResult::Failed() const { - for (int i = 0; i < total_part_count(); ++i) { - if (GetTestPartResult(i).failed()) - return true; - } - return false; -} - -// Returns true iff the test part fatally failed. -static bool TestPartFatallyFailed(const TestPartResult& result) { - return result.fatally_failed(); -} - -// Returns true iff the test fatally failed. -bool TestResult::HasFatalFailure() const { - return CountIf(test_part_results_, TestPartFatallyFailed) > 0; -} - -// Returns true iff the test part non-fatally failed. -static bool TestPartNonfatallyFailed(const TestPartResult& result) { - return result.nonfatally_failed(); -} - -// Returns true iff the test has a non-fatal failure. -bool TestResult::HasNonfatalFailure() const { - return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0; -} - -// Gets the number of all test parts. This is the sum of the number -// of successful test parts and the number of failed test parts. -int TestResult::total_part_count() const { - return static_cast(test_part_results_.size()); -} - -// Returns the number of the test properties. -int TestResult::test_property_count() const { - return static_cast(test_properties_.size()); -} - -// class Test - -// Creates a Test object. - -// The c'tor saves the values of all Google Test flags. -Test::Test() - : gtest_flag_saver_(new internal::GTestFlagSaver) { -} - -// The d'tor restores the values of all Google Test flags. -Test::~Test() { - delete gtest_flag_saver_; -} - -// Sets up the test fixture. -// -// A sub-class may override this. -void Test::SetUp() { -} - -// Tears down the test fixture. -// -// A sub-class may override this. -void Test::TearDown() { -} - -// Allows user supplied key value pairs to be recorded for later output. -void Test::RecordProperty(const char* key, const char* value) { - UnitTest::GetInstance()->RecordPropertyForCurrentTest(key, value); -} - -// Allows user supplied key value pairs to be recorded for later output. -void Test::RecordProperty(const char* key, int value) { - Message value_message; - value_message << value; - RecordProperty(key, value_message.GetString().c_str()); -} - -namespace internal { - -void ReportFailureInUnknownLocation(TestPartResult::Type result_type, - const String& message) { - // This function is a friend of UnitTest and as such has access to - // AddTestPartResult. - UnitTest::GetInstance()->AddTestPartResult( - result_type, - NULL, // No info about the source file where the exception occurred. - -1, // We have no info on which line caused the exception. - message, - String()); // No stack trace, either. -} - -} // namespace internal - -#if GTEST_OS_WINDOWS -// We are on Windows. - -// Adds an "exception thrown" fatal failure to the current test. -static void AddExceptionThrownFailure(DWORD exception_code, - const char* location) { - Message message; - message << "Exception thrown with code 0x" << std::setbase(16) << - exception_code << std::setbase(10) << " in " << location << "."; - - internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, - message.GetString()); -} - -#endif // GTEST_OS_WINDOWS - -// Google Test requires all tests in the same test case to use the same test -// fixture class. This function checks if the current test has the -// same fixture class as the first test in the current test case. If -// yes, it returns true; otherwise it generates a Google Test failure and -// returns false. -bool Test::HasSameFixtureClass() { - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - const TestCase* const test_case = impl->current_test_case(); - - // Info about the first test in the current test case. - const internal::TestInfoImpl* const first_test_info = - test_case->test_info_list()[0]->impl(); - const internal::TypeId first_fixture_id = first_test_info->fixture_class_id(); - const char* const first_test_name = first_test_info->name(); - - // Info about the current test. - const internal::TestInfoImpl* const this_test_info = - impl->current_test_info()->impl(); - const internal::TypeId this_fixture_id = this_test_info->fixture_class_id(); - const char* const this_test_name = this_test_info->name(); - - if (this_fixture_id != first_fixture_id) { - // Is the first test defined using TEST? - const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId(); - // Is this test defined using TEST? - const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId(); - - if (first_is_TEST || this_is_TEST) { - // The user mixed TEST and TEST_F in this test case - we'll tell - // him/her how to fix it. - - // Gets the name of the TEST and the name of the TEST_F. Note - // that first_is_TEST and this_is_TEST cannot both be true, as - // the fixture IDs are different for the two tests. - const char* const TEST_name = - first_is_TEST ? first_test_name : this_test_name; - const char* const TEST_F_name = - first_is_TEST ? this_test_name : first_test_name; - - ADD_FAILURE() - << "All tests in the same test case must use the same test fixture\n" - << "class, so mixing TEST_F and TEST in the same test case is\n" - << "illegal. In test case " << this_test_info->test_case_name() - << ",\n" - << "test " << TEST_F_name << " is defined using TEST_F but\n" - << "test " << TEST_name << " is defined using TEST. You probably\n" - << "want to change the TEST to TEST_F or move it to another test\n" - << "case."; - } else { - // The user defined two fixture classes with the same name in - // two namespaces - we'll tell him/her how to fix it. - ADD_FAILURE() - << "All tests in the same test case must use the same test fixture\n" - << "class. However, in test case " - << this_test_info->test_case_name() << ",\n" - << "you defined test " << first_test_name - << " and test " << this_test_name << "\n" - << "using two different test fixture classes. This can happen if\n" - << "the two classes are from different namespaces or translation\n" - << "units and have the same name. You should probably rename one\n" - << "of the classes to put the tests into different test cases."; - } - return false; - } - - return true; -} - -// Runs the test and updates the test result. -void Test::Run() { - if (!HasSameFixtureClass()) return; - - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); -#if GTEST_HAS_SEH - // Catch SEH-style exceptions. - impl->os_stack_trace_getter()->UponLeavingGTest(); - __try { - SetUp(); - } __except(internal::UnitTestOptions::GTestShouldProcessSEH( - GetExceptionCode())) { - AddExceptionThrownFailure(GetExceptionCode(), "SetUp()"); - } - - // We will run the test only if SetUp() had no fatal failure. - if (!HasFatalFailure()) { - impl->os_stack_trace_getter()->UponLeavingGTest(); - __try { - TestBody(); - } __except(internal::UnitTestOptions::GTestShouldProcessSEH( - GetExceptionCode())) { - AddExceptionThrownFailure(GetExceptionCode(), "the test body"); - } - } - - // However, we want to clean up as much as possible. Hence we will - // always call TearDown(), even if SetUp() or the test body has - // failed. - impl->os_stack_trace_getter()->UponLeavingGTest(); - __try { - TearDown(); - } __except(internal::UnitTestOptions::GTestShouldProcessSEH( - GetExceptionCode())) { - AddExceptionThrownFailure(GetExceptionCode(), "TearDown()"); - } - -#else // We are on a compiler or platform that doesn't support SEH. - impl->os_stack_trace_getter()->UponLeavingGTest(); - SetUp(); - - // We will run the test only if SetUp() was successful. - if (!HasFatalFailure()) { - impl->os_stack_trace_getter()->UponLeavingGTest(); - TestBody(); - } - - // However, we want to clean up as much as possible. Hence we will - // always call TearDown(), even if SetUp() or the test body has - // failed. - impl->os_stack_trace_getter()->UponLeavingGTest(); - TearDown(); -#endif // GTEST_HAS_SEH -} - - -// Returns true iff the current test has a fatal failure. -bool Test::HasFatalFailure() { - return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); -} - -// Returns true iff the current test has a non-fatal failure. -bool Test::HasNonfatalFailure() { - return internal::GetUnitTestImpl()->current_test_result()-> - HasNonfatalFailure(); -} - -// class TestInfo - -// Constructs a TestInfo object. It assumes ownership of the test factory -// object via impl_. -TestInfo::TestInfo(const char* a_test_case_name, - const char* a_name, - const char* a_test_case_comment, - const char* a_comment, - internal::TypeId fixture_class_id, - internal::TestFactoryBase* factory) { - impl_ = new internal::TestInfoImpl(this, a_test_case_name, a_name, - a_test_case_comment, a_comment, - fixture_class_id, factory); -} - -// Destructs a TestInfo object. -TestInfo::~TestInfo() { - delete impl_; -} - -namespace internal { - -// Creates a new TestInfo object and registers it with Google Test; -// returns the created object. -// -// Arguments: -// -// test_case_name: name of the test case -// name: name of the test -// test_case_comment: a comment on the test case that will be included in -// the test output -// comment: a comment on the test that will be included in the -// test output -// fixture_class_id: ID of the test fixture class -// set_up_tc: pointer to the function that sets up the test case -// tear_down_tc: pointer to the function that tears down the test case -// factory: pointer to the factory that creates a test object. -// The newly created TestInfo instance will assume -// ownership of the factory object. -TestInfo* MakeAndRegisterTestInfo( - const char* test_case_name, const char* name, - const char* test_case_comment, const char* comment, - TypeId fixture_class_id, - SetUpTestCaseFunc set_up_tc, - TearDownTestCaseFunc tear_down_tc, - TestFactoryBase* factory) { - TestInfo* const test_info = - new TestInfo(test_case_name, name, test_case_comment, comment, - fixture_class_id, factory); - GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); - return test_info; -} - -#if GTEST_HAS_PARAM_TEST -void ReportInvalidTestCaseType(const char* test_case_name, - const char* file, int line) { - Message errors; - errors - << "Attempted redefinition of test case " << test_case_name << ".\n" - << "All tests in the same test case must use the same test fixture\n" - << "class. However, in test case " << test_case_name << ", you tried\n" - << "to define a test using a fixture class different from the one\n" - << "used earlier. This can happen if the two fixture classes are\n" - << "from different namespaces and have the same name. You should\n" - << "probably rename one of the classes to put the tests into different\n" - << "test cases."; - - fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), - errors.GetString().c_str()); -} -#endif // GTEST_HAS_PARAM_TEST - -} // namespace internal - -// Returns the test case name. -const char* TestInfo::test_case_name() const { - return impl_->test_case_name(); -} - -// Returns the test name. -const char* TestInfo::name() const { - return impl_->name(); -} - -// Returns the test case comment. -const char* TestInfo::test_case_comment() const { - return impl_->test_case_comment(); -} - -// Returns the test comment. -const char* TestInfo::comment() const { - return impl_->comment(); -} - -// Returns true if this test should run. -bool TestInfo::should_run() const { return impl_->should_run(); } - -// Returns true if this test matches the user-specified filter. -bool TestInfo::matches_filter() const { return impl_->matches_filter(); } - -// Returns the result of the test. -const TestResult* TestInfo::result() const { return impl_->result(); } - -// Increments the number of death tests encountered in this test so -// far. -int TestInfo::increment_death_test_count() { - return impl_->result()->increment_death_test_count(); -} - -namespace { - -// A predicate that checks the test name of a TestInfo against a known -// value. -// -// This is used for implementation of the TestCase class only. We put -// it in the anonymous namespace to prevent polluting the outer -// namespace. -// -// TestNameIs is copyable. -class TestNameIs { - public: - // Constructor. - // - // TestNameIs has NO default constructor. - explicit TestNameIs(const char* name) - : name_(name) {} - - // Returns true iff the test name of test_info matches name_. - bool operator()(const TestInfo * test_info) const { - return test_info && internal::String(test_info->name()).Compare(name_) == 0; - } - - private: - internal::String name_; -}; - -} // namespace - -namespace internal { - -// This method expands all parameterized tests registered with macros TEST_P -// and INSTANTIATE_TEST_CASE_P into regular tests and registers those. -// This will be done just once during the program runtime. -void UnitTestImpl::RegisterParameterizedTests() { -#if GTEST_HAS_PARAM_TEST - if (!parameterized_tests_registered_) { - parameterized_test_registry_.RegisterTests(); - parameterized_tests_registered_ = true; - } -#endif -} - -// Creates the test object, runs it, records its result, and then -// deletes it. -void TestInfoImpl::Run() { - if (!should_run_) return; - - // Tells UnitTest where to store test result. - UnitTestImpl* const impl = internal::GetUnitTestImpl(); - impl->set_current_test_info(parent_); - - TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); - - // Notifies the unit test event listeners that a test is about to start. - repeater->OnTestStart(*parent_); - - const TimeInMillis start = GetTimeInMillis(); - - impl->os_stack_trace_getter()->UponLeavingGTest(); -#if GTEST_HAS_SEH - // Catch SEH-style exceptions. - Test* test = NULL; - - __try { - // Creates the test object. - test = factory_->CreateTest(); - } __except(internal::UnitTestOptions::GTestShouldProcessSEH( - GetExceptionCode())) { - AddExceptionThrownFailure(GetExceptionCode(), - "the test fixture's constructor"); - return; - } -#else // We are on a compiler or platform that doesn't support SEH. - - // TODO(wan): If test->Run() throws, test won't be deleted. This is - // not a problem now as we don't use exceptions. If we were to - // enable exceptions, we should revise the following to be - // exception-safe. - - // Creates the test object. - Test* test = factory_->CreateTest(); -#endif // GTEST_HAS_SEH - - // Runs the test only if the constructor of the test fixture didn't - // generate a fatal failure. - if (!Test::HasFatalFailure()) { - test->Run(); - } - - // Deletes the test object. - impl->os_stack_trace_getter()->UponLeavingGTest(); - delete test; - test = NULL; - - result_.set_elapsed_time(GetTimeInMillis() - start); - - // Notifies the unit test event listener that a test has just finished. - repeater->OnTestEnd(*parent_); - - // Tells UnitTest to stop associating assertion results to this - // test. - impl->set_current_test_info(NULL); -} - -} // namespace internal - -// class TestCase - -// Gets the number of successful tests in this test case. -int TestCase::successful_test_count() const { - return CountIf(test_info_list_, TestPassed); -} - -// Gets the number of failed tests in this test case. -int TestCase::failed_test_count() const { - return CountIf(test_info_list_, TestFailed); -} - -int TestCase::disabled_test_count() const { - return CountIf(test_info_list_, TestDisabled); -} - -// Get the number of tests in this test case that should run. -int TestCase::test_to_run_count() const { - return CountIf(test_info_list_, ShouldRunTest); -} - -// Gets the number of all tests. -int TestCase::total_test_count() const { - return static_cast(test_info_list_.size()); -} - -// Creates a TestCase with the given name. -// -// Arguments: -// -// name: name of the test case -// set_up_tc: pointer to the function that sets up the test case -// tear_down_tc: pointer to the function that tears down the test case -TestCase::TestCase(const char* a_name, const char* a_comment, - Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc) - : name_(a_name), - comment_(a_comment), - set_up_tc_(set_up_tc), - tear_down_tc_(tear_down_tc), - should_run_(false), - elapsed_time_(0) { -} - -// Destructor of TestCase. -TestCase::~TestCase() { - // Deletes every Test in the collection. - ForEach(test_info_list_, internal::Delete); -} - -// Returns the i-th test among all the tests. i can range from 0 to -// total_test_count() - 1. If i is not in that range, returns NULL. -const TestInfo* TestCase::GetTestInfo(int i) const { - const int index = GetElementOr(test_indices_, i, -1); - return index < 0 ? NULL : test_info_list_[index]; -} - -// Returns the i-th test among all the tests. i can range from 0 to -// total_test_count() - 1. If i is not in that range, returns NULL. -TestInfo* TestCase::GetMutableTestInfo(int i) { - const int index = GetElementOr(test_indices_, i, -1); - return index < 0 ? NULL : test_info_list_[index]; -} - -// Adds a test to this test case. Will delete the test upon -// destruction of the TestCase object. -void TestCase::AddTestInfo(TestInfo * test_info) { - test_info_list_.push_back(test_info); - test_indices_.push_back(static_cast(test_indices_.size())); -} - -// Runs every test in this TestCase. -void TestCase::Run() { - if (!should_run_) return; - - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - impl->set_current_test_case(this); - - TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); - - repeater->OnTestCaseStart(*this); - impl->os_stack_trace_getter()->UponLeavingGTest(); - set_up_tc_(); - - const internal::TimeInMillis start = internal::GetTimeInMillis(); - for (int i = 0; i < total_test_count(); i++) { - GetMutableTestInfo(i)->impl()->Run(); - } - elapsed_time_ = internal::GetTimeInMillis() - start; - - impl->os_stack_trace_getter()->UponLeavingGTest(); - tear_down_tc_(); - repeater->OnTestCaseEnd(*this); - impl->set_current_test_case(NULL); -} - -// Clears the results of all tests in this test case. -void TestCase::ClearResult() { - ForEach(test_info_list_, internal::TestInfoImpl::ClearTestResult); -} - -// Returns true iff test passed. -bool TestCase::TestPassed(const TestInfo * test_info) { - const internal::TestInfoImpl* const impl = test_info->impl(); - return impl->should_run() && impl->result()->Passed(); -} - -// Returns true iff test failed. -bool TestCase::TestFailed(const TestInfo * test_info) { - const internal::TestInfoImpl* const impl = test_info->impl(); - return impl->should_run() && impl->result()->Failed(); -} - -// Returns true iff test is disabled. -bool TestCase::TestDisabled(const TestInfo * test_info) { - return test_info->impl()->is_disabled(); -} - -// Returns true if the given test should run. -bool TestCase::ShouldRunTest(const TestInfo *test_info) { - return test_info->impl()->should_run(); -} - -// Shuffles the tests in this test case. -void TestCase::ShuffleTests(internal::Random* random) { - Shuffle(random, &test_indices_); -} - -// Restores the test order to before the first shuffle. -void TestCase::UnshuffleTests() { - for (size_t i = 0; i < test_indices_.size(); i++) { - test_indices_[i] = static_cast(i); - } -} - -// Formats a countable noun. Depending on its quantity, either the -// singular form or the plural form is used. e.g. -// -// FormatCountableNoun(1, "formula", "formuli") returns "1 formula". -// FormatCountableNoun(5, "book", "books") returns "5 books". -static internal::String FormatCountableNoun(int count, - const char * singular_form, - const char * plural_form) { - return internal::String::Format("%d %s", count, - count == 1 ? singular_form : plural_form); -} - -// Formats the count of tests. -static internal::String FormatTestCount(int test_count) { - return FormatCountableNoun(test_count, "test", "tests"); -} - -// Formats the count of test cases. -static internal::String FormatTestCaseCount(int test_case_count) { - return FormatCountableNoun(test_case_count, "test case", "test cases"); -} - -// Converts a TestPartResult::Type enum to human-friendly string -// representation. Both kNonFatalFailure and kFatalFailure are translated -// to "Failure", as the user usually doesn't care about the difference -// between the two when viewing the test result. -static const char * TestPartResultTypeToString(TestPartResult::Type type) { - switch (type) { - case TestPartResult::kSuccess: - return "Success"; - - case TestPartResult::kNonFatalFailure: - case TestPartResult::kFatalFailure: -#ifdef _MSC_VER - return "error: "; -#else - return "Failure\n"; -#endif - } - - return "Unknown result type"; -} - -// Prints a TestPartResult to a String. -static internal::String PrintTestPartResultToString( - const TestPartResult& test_part_result) { - return (Message() - << internal::FormatFileLocation(test_part_result.file_name(), - test_part_result.line_number()) - << " " << TestPartResultTypeToString(test_part_result.type()) - << test_part_result.message()).GetString(); -} - -// Prints a TestPartResult. -static void PrintTestPartResult(const TestPartResult& test_part_result) { - const internal::String& result = - PrintTestPartResultToString(test_part_result); - printf("%s\n", result.c_str()); - fflush(stdout); - // If the test program runs in Visual Studio or a debugger, the - // following statements add the test part result message to the Output - // window such that the user can double-click on it to jump to the - // corresponding source code location; otherwise they do nothing. -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE - // We don't call OutputDebugString*() on Windows Mobile, as printing - // to stdout is done by OutputDebugString() there already - we don't - // want the same message printed twice. - ::OutputDebugStringA(result.c_str()); - ::OutputDebugStringA("\n"); -#endif -} - -// class PrettyUnitTestResultPrinter - -namespace internal { - -enum GTestColor { - COLOR_DEFAULT, - COLOR_RED, - COLOR_GREEN, - COLOR_YELLOW -}; - -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE - -// Returns the character attribute for the given color. -WORD GetColorAttribute(GTestColor color) { - switch (color) { - case COLOR_RED: return FOREGROUND_RED; - case COLOR_GREEN: return FOREGROUND_GREEN; - case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; - default: return 0; - } -} - -#else - -// Returns the ANSI color code for the given color. COLOR_DEFAULT is -// an invalid input. -const char* GetAnsiColorCode(GTestColor color) { - switch (color) { - case COLOR_RED: return "1"; - case COLOR_GREEN: return "2"; - case COLOR_YELLOW: return "3"; - default: return NULL; - }; -} - -#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE - -// Returns true iff Google Test should use colors in the output. -bool ShouldUseColor(bool stdout_is_tty) { - const char* const gtest_color = GTEST_FLAG(color).c_str(); - - if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { -#if GTEST_OS_WINDOWS - // On Windows the TERM variable is usually not set, but the - // console there does support colors. - return stdout_is_tty; -#else - // On non-Windows platforms, we rely on the TERM variable. - const char* const term = posix::GetEnv("TERM"); - const bool term_supports_color = - String::CStringEquals(term, "xterm") || - String::CStringEquals(term, "xterm-color") || - String::CStringEquals(term, "xterm-256color") || - String::CStringEquals(term, "linux") || - String::CStringEquals(term, "cygwin"); - return stdout_is_tty && term_supports_color; -#endif // GTEST_OS_WINDOWS - } - - return String::CaseInsensitiveCStringEquals(gtest_color, "yes") || - String::CaseInsensitiveCStringEquals(gtest_color, "true") || - String::CaseInsensitiveCStringEquals(gtest_color, "t") || - String::CStringEquals(gtest_color, "1"); - // We take "yes", "true", "t", and "1" as meaning "yes". If the - // value is neither one of these nor "auto", we treat it as "no" to - // be conservative. -} - -// Helpers for printing colored strings to stdout. Note that on Windows, we -// cannot simply emit special characters and have the terminal change colors. -// This routine must actually emit the characters rather than return a string -// that would be colored when printed, as can be done on Linux. -void ColoredPrintf(GTestColor color, const char* fmt, ...) { - va_list args; - va_start(args, fmt); - -#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS - const bool use_color = false; -#else - static const bool in_color_mode = - ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); - const bool use_color = in_color_mode && (color != COLOR_DEFAULT); -#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS - // The '!= 0' comparison is necessary to satisfy MSVC 7.1. - - if (!use_color) { - vprintf(fmt, args); - va_end(args); - return; - } - -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE - const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); - - // Gets the current text color. - CONSOLE_SCREEN_BUFFER_INFO buffer_info; - GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); - const WORD old_color_attrs = buffer_info.wAttributes; - - // We need to flush the stream buffers into the console before each - // SetConsoleTextAttribute call lest it affect the text that is already - // printed but has not yet reached the console. - fflush(stdout); - SetConsoleTextAttribute(stdout_handle, - GetColorAttribute(color) | FOREGROUND_INTENSITY); - vprintf(fmt, args); - - fflush(stdout); - // Restores the text color. - SetConsoleTextAttribute(stdout_handle, old_color_attrs); -#else - printf("\033[0;3%sm", GetAnsiColorCode(color)); - vprintf(fmt, args); - printf("\033[m"); // Resets the terminal to default. -#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE - va_end(args); -} - -// This class implements the TestEventListener interface. -// -// Class PrettyUnitTestResultPrinter is copyable. -class PrettyUnitTestResultPrinter : public TestEventListener { - public: - PrettyUnitTestResultPrinter() {} - static void PrintTestName(const char * test_case, const char * test) { - printf("%s.%s", test_case, test); - } - - // The following methods override what's in the TestEventListener class. - virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} - virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); - virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); - virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} - virtual void OnTestCaseStart(const TestCase& test_case); - virtual void OnTestStart(const TestInfo& test_info); - virtual void OnTestPartResult(const TestPartResult& result); - virtual void OnTestEnd(const TestInfo& test_info); - virtual void OnTestCaseEnd(const TestCase& test_case); - virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); - virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} - virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); - virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} - - private: - static void PrintFailedTests(const UnitTest& unit_test); - - internal::String test_case_name_; -}; - - // Fired before each iteration of tests starts. -void PrettyUnitTestResultPrinter::OnTestIterationStart( - const UnitTest& unit_test, int iteration) { - if (GTEST_FLAG(repeat) != 1) - printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1); - - const char* const filter = GTEST_FLAG(filter).c_str(); - - // Prints the filter if it's not *. This reminds the user that some - // tests may be skipped. - if (!internal::String::CStringEquals(filter, kUniversalFilter)) { - ColoredPrintf(COLOR_YELLOW, - "Note: %s filter = %s\n", GTEST_NAME_, filter); - } - - if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { - ColoredPrintf(COLOR_YELLOW, - "Note: This is test shard %s of %s.\n", - internal::posix::GetEnv(kTestShardIndex), - internal::posix::GetEnv(kTestTotalShards)); - } - - if (GTEST_FLAG(shuffle)) { - ColoredPrintf(COLOR_YELLOW, - "Note: Randomizing tests' orders with a seed of %d .\n", - unit_test.random_seed()); - } - - ColoredPrintf(COLOR_GREEN, "[==========] "); - printf("Running %s from %s.\n", - FormatTestCount(unit_test.test_to_run_count()).c_str(), - FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); - fflush(stdout); -} - -void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( - const UnitTest& /*unit_test*/) { - ColoredPrintf(COLOR_GREEN, "[----------] "); - printf("Global test environment set-up.\n"); - fflush(stdout); -} - -void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { - test_case_name_ = test_case.name(); - const internal::String counts = - FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); - ColoredPrintf(COLOR_GREEN, "[----------] "); - printf("%s from %s", counts.c_str(), test_case_name_.c_str()); - if (test_case.comment()[0] == '\0') { - printf("\n"); - } else { - printf(", where %s\n", test_case.comment()); - } - fflush(stdout); -} - -void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { - ColoredPrintf(COLOR_GREEN, "[ RUN ] "); - PrintTestName(test_case_name_.c_str(), test_info.name()); - if (test_info.comment()[0] == '\0') { - printf("\n"); - } else { - printf(", where %s\n", test_info.comment()); - } - fflush(stdout); -} - -// Called after an assertion failure. -void PrettyUnitTestResultPrinter::OnTestPartResult( - const TestPartResult& result) { - // If the test part succeeded, we don't need to do anything. - if (result.type() == TestPartResult::kSuccess) - return; - - // Print failure message from the assertion (e.g. expected this and got that). - PrintTestPartResult(result); - fflush(stdout); -} - -void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { - if (test_info.result()->Passed()) { - ColoredPrintf(COLOR_GREEN, "[ OK ] "); - } else { - ColoredPrintf(COLOR_RED, "[ FAILED ] "); - } - PrintTestName(test_case_name_.c_str(), test_info.name()); - if (GTEST_FLAG(print_time)) { - printf(" (%s ms)\n", internal::StreamableToString( - test_info.result()->elapsed_time()).c_str()); - } else { - printf("\n"); - } - fflush(stdout); -} - -void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { - if (!GTEST_FLAG(print_time)) return; - - test_case_name_ = test_case.name(); - const internal::String counts = - FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); - ColoredPrintf(COLOR_GREEN, "[----------] "); - printf("%s from %s (%s ms total)\n\n", - counts.c_str(), test_case_name_.c_str(), - internal::StreamableToString(test_case.elapsed_time()).c_str()); - fflush(stdout); -} - -void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart( - const UnitTest& /*unit_test*/) { - ColoredPrintf(COLOR_GREEN, "[----------] "); - printf("Global test environment tear-down\n"); - fflush(stdout); -} - -// Internal helper for printing the list of failed tests. -void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) { - const int failed_test_count = unit_test.failed_test_count(); - if (failed_test_count == 0) { - return; - } - - for (int i = 0; i < unit_test.total_test_case_count(); ++i) { - const TestCase& test_case = *unit_test.GetTestCase(i); - if (!test_case.should_run() || (test_case.failed_test_count() == 0)) { - continue; - } - for (int j = 0; j < test_case.total_test_count(); ++j) { - const TestInfo& test_info = *test_case.GetTestInfo(j); - if (!test_info.should_run() || test_info.result()->Passed()) { - continue; - } - ColoredPrintf(COLOR_RED, "[ FAILED ] "); - printf("%s.%s", test_case.name(), test_info.name()); - if (test_case.comment()[0] != '\0' || - test_info.comment()[0] != '\0') { - printf(", where %s", test_case.comment()); - if (test_case.comment()[0] != '\0' && - test_info.comment()[0] != '\0') { - printf(" and "); - } - } - printf("%s\n", test_info.comment()); - } - } -} - - void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, - int /*iteration*/) { - ColoredPrintf(COLOR_GREEN, "[==========] "); - printf("%s from %s ran.", - FormatTestCount(unit_test.test_to_run_count()).c_str(), - FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); - if (GTEST_FLAG(print_time)) { - printf(" (%s ms total)", - internal::StreamableToString(unit_test.elapsed_time()).c_str()); - } - printf("\n"); - ColoredPrintf(COLOR_GREEN, "[ PASSED ] "); - printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str()); - - int num_failures = unit_test.failed_test_count(); - if (!unit_test.Passed()) { - const int failed_test_count = unit_test.failed_test_count(); - ColoredPrintf(COLOR_RED, "[ FAILED ] "); - printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str()); - PrintFailedTests(unit_test); - printf("\n%2d FAILED %s\n", num_failures, - num_failures == 1 ? "TEST" : "TESTS"); - } - - int num_disabled = unit_test.disabled_test_count(); - if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { - if (!num_failures) { - printf("\n"); // Add a spacer if no FAILURE banner is displayed. - } - ColoredPrintf(COLOR_YELLOW, - " YOU HAVE %d DISABLED %s\n\n", - num_disabled, - num_disabled == 1 ? "TEST" : "TESTS"); - } - // Ensure that Google Test output is printed before, e.g., heapchecker output. - fflush(stdout); -} - -// End PrettyUnitTestResultPrinter - -// class TestEventRepeater -// -// This class forwards events to other event listeners. -class TestEventRepeater : public TestEventListener { - public: - TestEventRepeater() : forwarding_enabled_(true) {} - virtual ~TestEventRepeater(); - void Append(TestEventListener *listener); - TestEventListener* Release(TestEventListener* listener); - - // Controls whether events will be forwarded to listeners_. Set to false - // in death test child processes. - bool forwarding_enabled() const { return forwarding_enabled_; } - void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; } - - virtual void OnTestProgramStart(const UnitTest& unit_test); - virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); - virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); - virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test); - virtual void OnTestCaseStart(const TestCase& test_case); - virtual void OnTestStart(const TestInfo& test_info); - virtual void OnTestPartResult(const TestPartResult& result); - virtual void OnTestEnd(const TestInfo& test_info); - virtual void OnTestCaseEnd(const TestCase& test_case); - virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); - virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test); - virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); - virtual void OnTestProgramEnd(const UnitTest& unit_test); - - private: - // Controls whether events will be forwarded to listeners_. Set to false - // in death test child processes. - bool forwarding_enabled_; - // The list of listeners that receive events. - std::vector listeners_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater); -}; - -TestEventRepeater::~TestEventRepeater() { - ForEach(listeners_, Delete); -} - -void TestEventRepeater::Append(TestEventListener *listener) { - listeners_.push_back(listener); -} - -// TODO(vladl@google.com): Factor the search functionality into Vector::Find. -TestEventListener* TestEventRepeater::Release(TestEventListener *listener) { - for (size_t i = 0; i < listeners_.size(); ++i) { - if (listeners_[i] == listener) { - listeners_.erase(listeners_.begin() + i); - return listener; - } - } - - return NULL; -} - -// Since most methods are very similar, use macros to reduce boilerplate. -// This defines a member that forwards the call to all listeners. -#define GTEST_REPEATER_METHOD_(Name, Type) \ -void TestEventRepeater::Name(const Type& parameter) { \ - if (forwarding_enabled_) { \ - for (size_t i = 0; i < listeners_.size(); i++) { \ - listeners_[i]->Name(parameter); \ - } \ - } \ -} -// This defines a member that forwards the call to all listeners in reverse -// order. -#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \ -void TestEventRepeater::Name(const Type& parameter) { \ - if (forwarding_enabled_) { \ - for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { \ - listeners_[i]->Name(parameter); \ - } \ - } \ -} - -GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest) -GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest) -GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase) -GTEST_REPEATER_METHOD_(OnTestStart, TestInfo) -GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult) -GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest) -GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest) -GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest) -GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo) -GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase) -GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest) - -#undef GTEST_REPEATER_METHOD_ -#undef GTEST_REVERSE_REPEATER_METHOD_ - -void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test, - int iteration) { - if (forwarding_enabled_) { - for (size_t i = 0; i < listeners_.size(); i++) { - listeners_[i]->OnTestIterationStart(unit_test, iteration); - } - } -} - -void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test, - int iteration) { - if (forwarding_enabled_) { - for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { - listeners_[i]->OnTestIterationEnd(unit_test, iteration); - } - } -} - -// End TestEventRepeater - -// This class generates an XML output file. -class XmlUnitTestResultPrinter : public EmptyTestEventListener { - public: - explicit XmlUnitTestResultPrinter(const char* output_file); - - virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); - - private: - // Is c a whitespace character that is normalized to a space character - // when it appears in an XML attribute value? - static bool IsNormalizableWhitespace(char c) { - return c == 0x9 || c == 0xA || c == 0xD; - } - - // May c appear in a well-formed XML document? - static bool IsValidXmlCharacter(char c) { - return IsNormalizableWhitespace(c) || c >= 0x20; - } - - // Returns an XML-escaped copy of the input string str. If - // is_attribute is true, the text is meant to appear as an attribute - // value, and normalizable whitespace is preserved by replacing it - // with character references. - static String EscapeXml(const char* str, bool is_attribute); - - // Returns the given string with all characters invalid in XML removed. - static String RemoveInvalidXmlCharacters(const char* str); - - // Convenience wrapper around EscapeXml when str is an attribute value. - static String EscapeXmlAttribute(const char* str) { - return EscapeXml(str, true); - } - - // Convenience wrapper around EscapeXml when str is not an attribute value. - static String EscapeXmlText(const char* str) { return EscapeXml(str, false); } - - // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. - static void OutputXmlCDataSection(::std::ostream* stream, const char* data); - - // Streams an XML representation of a TestInfo object. - static void OutputXmlTestInfo(::std::ostream* stream, - const char* test_case_name, - const TestInfo& test_info); - - // Prints an XML representation of a TestCase object - static void PrintXmlTestCase(FILE* out, const TestCase& test_case); - - // Prints an XML summary of unit_test to output stream out. - static void PrintXmlUnitTest(FILE* out, const UnitTest& unit_test); - - // Produces a string representing the test properties in a result as space - // delimited XML attributes based on the property key="value" pairs. - // When the String is not empty, it includes a space at the beginning, - // to delimit this attribute from prior attributes. - static String TestPropertiesAsXmlAttributes(const TestResult& result); - - // The output file. - const String output_file_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); -}; - -// Creates a new XmlUnitTestResultPrinter. -XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) - : output_file_(output_file) { - if (output_file_.c_str() == NULL || output_file_.empty()) { - fprintf(stderr, "XML output file may not be null\n"); - fflush(stderr); - exit(EXIT_FAILURE); - } -} - -// Called after the unit test ends. -void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, - int /*iteration*/) { - FILE* xmlout = NULL; - FilePath output_file(output_file_); - FilePath output_dir(output_file.RemoveFileName()); - - if (output_dir.CreateDirectoriesRecursively()) { - xmlout = posix::FOpen(output_file_.c_str(), "w"); - } - if (xmlout == NULL) { - // TODO(wan): report the reason of the failure. - // - // We don't do it for now as: - // - // 1. There is no urgent need for it. - // 2. It's a bit involved to make the errno variable thread-safe on - // all three operating systems (Linux, Windows, and Mac OS). - // 3. To interpret the meaning of errno in a thread-safe way, - // we need the strerror_r() function, which is not available on - // Windows. - fprintf(stderr, - "Unable to open file \"%s\"\n", - output_file_.c_str()); - fflush(stderr); - exit(EXIT_FAILURE); - } - PrintXmlUnitTest(xmlout, unit_test); - fclose(xmlout); -} - -// Returns an XML-escaped copy of the input string str. If is_attribute -// is true, the text is meant to appear as an attribute value, and -// normalizable whitespace is preserved by replacing it with character -// references. -// -// Invalid XML characters in str, if any, are stripped from the output. -// It is expected that most, if not all, of the text processed by this -// module will consist of ordinary English text. -// If this module is ever modified to produce version 1.1 XML output, -// most invalid characters can be retained using character references. -// TODO(wan): It might be nice to have a minimally invasive, human-readable -// escaping scheme for invalid characters, rather than dropping them. -String XmlUnitTestResultPrinter::EscapeXml(const char* str, bool is_attribute) { - Message m; - - if (str != NULL) { - for (const char* src = str; *src; ++src) { - switch (*src) { - case '<': - m << "<"; - break; - case '>': - m << ">"; - break; - case '&': - m << "&"; - break; - case '\'': - if (is_attribute) - m << "'"; - else - m << '\''; - break; - case '"': - if (is_attribute) - m << """; - else - m << '"'; - break; - default: - if (IsValidXmlCharacter(*src)) { - if (is_attribute && IsNormalizableWhitespace(*src)) - m << String::Format("&#x%02X;", unsigned(*src)); - else - m << *src; - } - break; - } - } - } - - return m.GetString(); -} - -// Returns the given string with all characters invalid in XML removed. -// Currently invalid characters are dropped from the string. An -// alternative is to replace them with certain characters such as . or ?. -String XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(const char* str) { - char* const output = new char[strlen(str) + 1]; - char* appender = output; - for (char ch = *str; ch != '\0'; ch = *++str) - if (IsValidXmlCharacter(ch)) - *appender++ = ch; - *appender = '\0'; - - String ret_value(output); - delete[] output; - return ret_value; -} - -// The following routines generate an XML representation of a UnitTest -// object. -// -// This is how Google Test concepts map to the DTD: -// -// <-- corresponds to a UnitTest object -// <-- corresponds to a TestCase object -// <-- corresponds to a TestInfo object -// ... -// ... -// ... -// <-- individual assertion failures -// -// -// - -// Formats the given time in milliseconds as seconds. -std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { - ::std::stringstream ss; - ss << ms/1000.0; - return ss.str(); -} - -// Streams an XML CDATA section, escaping invalid CDATA sequences as needed. -void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, - const char* data) { - const char* segment = data; - *stream << ""); - if (next_segment != NULL) { - stream->write( - segment, static_cast(next_segment - segment)); - *stream << "]]>]]>"); - } else { - *stream << segment; - break; - } - } - *stream << "]]>"; -} - -// Prints an XML representation of a TestInfo object. -// TODO(wan): There is also value in printing properties with the plain printer. -void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, - const char* test_case_name, - const TestInfo& test_info) { - const TestResult& result = *test_info.result(); - *stream << " \n"; - *stream << " "; - const String message = RemoveInvalidXmlCharacters(String::Format( - "%s:%d\n%s", - part.file_name(), part.line_number(), - part.message()).c_str()); - OutputXmlCDataSection(stream, message.c_str()); - *stream << "\n"; - } - } - - if (failures == 0) - *stream << " />\n"; - else - *stream << " \n"; -} - -// Prints an XML representation of a TestCase object -void XmlUnitTestResultPrinter::PrintXmlTestCase(FILE* out, - const TestCase& test_case) { - fprintf(out, - " \n", - FormatTimeInMillisAsSeconds(test_case.elapsed_time()).c_str()); - for (int i = 0; i < test_case.total_test_count(); ++i) { - StrStream stream; - OutputXmlTestInfo(&stream, test_case.name(), *test_case.GetTestInfo(i)); - fprintf(out, "%s", StrStreamToString(&stream).c_str()); - } - fprintf(out, " \n"); -} - -// Prints an XML summary of unit_test to output stream out. -void XmlUnitTestResultPrinter::PrintXmlUnitTest(FILE* out, - const UnitTest& unit_test) { - fprintf(out, "\n"); - fprintf(out, - "\n"); - for (int i = 0; i < unit_test.total_test_case_count(); ++i) - PrintXmlTestCase(out, *unit_test.GetTestCase(i)); - fprintf(out, "\n"); -} - -// Produces a string representing the test properties in a result as space -// delimited XML attributes based on the property key="value" pairs. -String XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( - const TestResult& result) { - Message attributes; - for (int i = 0; i < result.test_property_count(); ++i) { - const TestProperty& property = result.GetTestProperty(i); - attributes << " " << property.key() << "=" - << "\"" << EscapeXmlAttribute(property.value()) << "\""; - } - return attributes.GetString(); -} - -// End XmlUnitTestResultPrinter - -// Class ScopedTrace - -// Pushes the given source file location and message onto a per-thread -// trace stack maintained by Google Test. -// L < UnitTest::mutex_ -ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) { - TraceInfo trace; - trace.file = file; - trace.line = line; - trace.message = message.GetString(); - - UnitTest::GetInstance()->PushGTestTrace(trace); -} - -// Pops the info pushed by the c'tor. -// L < UnitTest::mutex_ -ScopedTrace::~ScopedTrace() { - UnitTest::GetInstance()->PopGTestTrace(); -} - - -// class OsStackTraceGetter - -// Returns the current OS stack trace as a String. Parameters: -// -// max_depth - the maximum number of stack frames to be included -// in the trace. -// skip_count - the number of top frames to be skipped; doesn't count -// against max_depth. -// -// L < mutex_ -// We use "L < mutex_" to denote that the function may acquire mutex_. -String OsStackTraceGetter::CurrentStackTrace(int, int) { - return String(""); -} - -// L < mutex_ -void OsStackTraceGetter::UponLeavingGTest() { -} - -const char* const -OsStackTraceGetter::kElidedFramesMarker = - "... " GTEST_NAME_ " internal frames ..."; - -} // namespace internal - -// class TestEventListeners - -TestEventListeners::TestEventListeners() - : repeater_(new internal::TestEventRepeater()), - default_result_printer_(NULL), - default_xml_generator_(NULL) { -} - -TestEventListeners::~TestEventListeners() { delete repeater_; } - -// Returns the standard listener responsible for the default console -// output. Can be removed from the listeners list to shut down default -// console output. Note that removing this object from the listener list -// with Release transfers its ownership to the user. -void TestEventListeners::Append(TestEventListener* listener) { - repeater_->Append(listener); -} - -// Removes the given event listener from the list and returns it. It then -// becomes the caller's responsibility to delete the listener. Returns -// NULL if the listener is not found in the list. -TestEventListener* TestEventListeners::Release(TestEventListener* listener) { - if (listener == default_result_printer_) - default_result_printer_ = NULL; - else if (listener == default_xml_generator_) - default_xml_generator_ = NULL; - return repeater_->Release(listener); -} - -// Returns repeater that broadcasts the TestEventListener events to all -// subscribers. -TestEventListener* TestEventListeners::repeater() { return repeater_; } - -// Sets the default_result_printer attribute to the provided listener. -// The listener is also added to the listener list and previous -// default_result_printer is removed from it and deleted. The listener can -// also be NULL in which case it will not be added to the list. Does -// nothing if the previous and the current listener objects are the same. -void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) { - if (default_result_printer_ != listener) { - // It is an error to pass this method a listener that is already in the - // list. - delete Release(default_result_printer_); - default_result_printer_ = listener; - if (listener != NULL) - Append(listener); - } -} - -// Sets the default_xml_generator attribute to the provided listener. The -// listener is also added to the listener list and previous -// default_xml_generator is removed from it and deleted. The listener can -// also be NULL in which case it will not be added to the list. Does -// nothing if the previous and the current listener objects are the same. -void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) { - if (default_xml_generator_ != listener) { - // It is an error to pass this method a listener that is already in the - // list. - delete Release(default_xml_generator_); - default_xml_generator_ = listener; - if (listener != NULL) - Append(listener); - } -} - -// Controls whether events will be forwarded by the repeater to the -// listeners in the list. -bool TestEventListeners::EventForwardingEnabled() const { - return repeater_->forwarding_enabled(); -} - -void TestEventListeners::SuppressEventForwarding() { - repeater_->set_forwarding_enabled(false); -} - -// class UnitTest - -// Gets the singleton UnitTest object. The first time this method is -// called, a UnitTest object is constructed and returned. Consecutive -// calls will return the same object. -// -// We don't protect this under mutex_ as a user is not supposed to -// call this before main() starts, from which point on the return -// value will never change. -UnitTest * UnitTest::GetInstance() { - // When compiled with MSVC 7.1 in optimized mode, destroying the - // UnitTest object upon exiting the program messes up the exit code, - // causing successful tests to appear failed. We have to use a - // different implementation in this case to bypass the compiler bug. - // This implementation makes the compiler happy, at the cost of - // leaking the UnitTest object. - - // CodeGear C++Builder insists on a public destructor for the - // default implementation. Use this implementation to keep good OO - // design with private destructor. - -#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) - static UnitTest* const instance = new UnitTest; - return instance; -#else - static UnitTest instance; - return &instance; -#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) -} - -// Gets the number of successful test cases. -int UnitTest::successful_test_case_count() const { - return impl()->successful_test_case_count(); -} - -// Gets the number of failed test cases. -int UnitTest::failed_test_case_count() const { - return impl()->failed_test_case_count(); -} - -// Gets the number of all test cases. -int UnitTest::total_test_case_count() const { - return impl()->total_test_case_count(); -} - -// Gets the number of all test cases that contain at least one test -// that should run. -int UnitTest::test_case_to_run_count() const { - return impl()->test_case_to_run_count(); -} - -// Gets the number of successful tests. -int UnitTest::successful_test_count() const { - return impl()->successful_test_count(); -} - -// Gets the number of failed tests. -int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } - -// Gets the number of disabled tests. -int UnitTest::disabled_test_count() const { - return impl()->disabled_test_count(); -} - -// Gets the number of all tests. -int UnitTest::total_test_count() const { return impl()->total_test_count(); } - -// Gets the number of tests that should run. -int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } - -// Gets the elapsed time, in milliseconds. -internal::TimeInMillis UnitTest::elapsed_time() const { - return impl()->elapsed_time(); -} - -// Returns true iff the unit test passed (i.e. all test cases passed). -bool UnitTest::Passed() const { return impl()->Passed(); } - -// Returns true iff the unit test failed (i.e. some test case failed -// or something outside of all tests failed). -bool UnitTest::Failed() const { return impl()->Failed(); } - -// Gets the i-th test case among all the test cases. i can range from 0 to -// total_test_case_count() - 1. If i is not in that range, returns NULL. -const TestCase* UnitTest::GetTestCase(int i) const { - return impl()->GetTestCase(i); -} - -// Gets the i-th test case among all the test cases. i can range from 0 to -// total_test_case_count() - 1. If i is not in that range, returns NULL. -TestCase* UnitTest::GetMutableTestCase(int i) { - return impl()->GetMutableTestCase(i); -} - -// Returns the list of event listeners that can be used to track events -// inside Google Test. -TestEventListeners& UnitTest::listeners() { - return *impl()->listeners(); -} - -// Registers and returns a global test environment. When a test -// program is run, all global test environments will be set-up in the -// order they were registered. After all tests in the program have -// finished, all global test environments will be torn-down in the -// *reverse* order they were registered. -// -// The UnitTest object takes ownership of the given environment. -// -// We don't protect this under mutex_, as we only support calling it -// from the main thread. -Environment* UnitTest::AddEnvironment(Environment* env) { - if (env == NULL) { - return NULL; - } - - impl_->environments().push_back(env); - return env; -} - -#if GTEST_HAS_EXCEPTIONS -// A failed Google Test assertion will throw an exception of this type -// when exceptions are enabled. We derive it from std::runtime_error, -// which is for errors presumably detectable only at run time. Since -// std::runtime_error inherits from std::exception, many testing -// frameworks know how to extract and print the message inside it. -class GoogleTestFailureException : public ::std::runtime_error { - public: - explicit GoogleTestFailureException(const TestPartResult& failure) - : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} -}; -#endif - -// Adds a TestPartResult to the current TestResult object. All Google Test -// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call -// this to report their results. The user code should use the -// assertion macros instead of calling this directly. -// L < mutex_ -void UnitTest::AddTestPartResult(TestPartResult::Type result_type, - const char* file_name, - int line_number, - const internal::String& message, - const internal::String& os_stack_trace) { - Message msg; - msg << message; - - internal::MutexLock lock(&mutex_); - if (impl_->gtest_trace_stack().size() > 0) { - msg << "\n" << GTEST_NAME_ << " trace:"; - - for (int i = static_cast(impl_->gtest_trace_stack().size()); - i > 0; --i) { - const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1]; - msg << "\n" << internal::FormatFileLocation(trace.file, trace.line) - << " " << trace.message; - } - } - - if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) { - msg << internal::kStackTraceMarker << os_stack_trace; - } - - const TestPartResult result = - TestPartResult(result_type, file_name, line_number, - msg.GetString().c_str()); - impl_->GetTestPartResultReporterForCurrentThread()-> - ReportTestPartResult(result); - - if (result_type != TestPartResult::kSuccess) { - // gtest_break_on_failure takes precedence over - // gtest_throw_on_failure. This allows a user to set the latter - // in the code (perhaps in order to use Google Test assertions - // with another testing framework) and specify the former on the - // command line for debugging. - if (GTEST_FLAG(break_on_failure)) { -#if GTEST_OS_WINDOWS - // Using DebugBreak on Windows allows gtest to still break into a debugger - // when a failure happens and both the --gtest_break_on_failure and - // the --gtest_catch_exceptions flags are specified. - DebugBreak(); -#else - *static_cast(NULL) = 1; -#endif // GTEST_OS_WINDOWS - } else if (GTEST_FLAG(throw_on_failure)) { -#if GTEST_HAS_EXCEPTIONS - throw GoogleTestFailureException(result); -#else - // We cannot call abort() as it generates a pop-up in debug mode - // that cannot be suppressed in VC 7.1 or below. - exit(1); -#endif - } - } -} - -// Creates and adds a property to the current TestResult. If a property matching -// the supplied value already exists, updates its value instead. -void UnitTest::RecordPropertyForCurrentTest(const char* key, - const char* value) { - const TestProperty test_property(key, value); - impl_->current_test_result()->RecordProperty(test_property); -} - -// Runs all tests in this UnitTest object and prints the result. -// Returns 0 if successful, or 1 otherwise. -// -// We don't protect this under mutex_, as we only support calling it -// from the main thread. -int UnitTest::Run() { -#if GTEST_HAS_SEH - // Catch SEH-style exceptions. - - const bool in_death_test_child_process = - internal::GTEST_FLAG(internal_run_death_test).length() > 0; - - // Either the user wants Google Test to catch exceptions thrown by the - // tests or this is executing in the context of death test child - // process. In either case the user does not want to see pop-up dialogs - // about crashes - they are expected.. - if (GTEST_FLAG(catch_exceptions) || in_death_test_child_process) { -#if !GTEST_OS_WINDOWS_MOBILE - // SetErrorMode doesn't exist on CE. - SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | - SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); -#endif // !GTEST_OS_WINDOWS_MOBILE - -#if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE - // Death test children can be terminated with _abort(). On Windows, - // _abort() can show a dialog with a warning message. This forces the - // abort message to go to stderr instead. - _set_error_mode(_OUT_TO_STDERR); -#endif - -#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE - // In the debug version, Visual Studio pops up a separate dialog - // offering a choice to debug the aborted program. We need to suppress - // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement - // executed. Google Test will notify the user of any unexpected - // failure via stderr. - // - // VC++ doesn't define _set_abort_behavior() prior to the version 8.0. - // Users of prior VC versions shall suffer the agony and pain of - // clicking through the countless debug dialogs. - // TODO(vladl@google.com): find a way to suppress the abort dialog() in the - // debug mode when compiled with VC 7.1 or lower. - if (!GTEST_FLAG(break_on_failure)) - _set_abort_behavior( - 0x0, // Clear the following flags: - _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. -#endif - } - - __try { - return impl_->RunAllTests(); - } __except(internal::UnitTestOptions::GTestShouldProcessSEH( - GetExceptionCode())) { - printf("Exception thrown with code 0x%x.\nFAIL\n", GetExceptionCode()); - fflush(stdout); - return 1; - } - -#else // We are on a compiler or platform that doesn't support SEH. - - return impl_->RunAllTests(); -#endif // GTEST_HAS_SEH -} - -// Returns the working directory when the first TEST() or TEST_F() was -// executed. -const char* UnitTest::original_working_dir() const { - return impl_->original_working_dir_.c_str(); -} - -// Returns the TestCase object for the test that's currently running, -// or NULL if no test is running. -// L < mutex_ -const TestCase* UnitTest::current_test_case() const { - internal::MutexLock lock(&mutex_); - return impl_->current_test_case(); -} - -// Returns the TestInfo object for the test that's currently running, -// or NULL if no test is running. -// L < mutex_ -const TestInfo* UnitTest::current_test_info() const { - internal::MutexLock lock(&mutex_); - return impl_->current_test_info(); -} - -// Returns the random seed used at the start of the current test run. -int UnitTest::random_seed() const { return impl_->random_seed(); } - -#if GTEST_HAS_PARAM_TEST -// Returns ParameterizedTestCaseRegistry object used to keep track of -// value-parameterized tests and instantiate and register them. -// L < mutex_ -internal::ParameterizedTestCaseRegistry& - UnitTest::parameterized_test_registry() { - return impl_->parameterized_test_registry(); -} -#endif // GTEST_HAS_PARAM_TEST - -// Creates an empty UnitTest. -UnitTest::UnitTest() { - impl_ = new internal::UnitTestImpl(this); -} - -// Destructor of UnitTest. -UnitTest::~UnitTest() { - delete impl_; -} - -// Pushes a trace defined by SCOPED_TRACE() on to the per-thread -// Google Test trace stack. -// L < mutex_ -void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) { - internal::MutexLock lock(&mutex_); - impl_->gtest_trace_stack().push_back(trace); -} - -// Pops a trace from the per-thread Google Test trace stack. -// L < mutex_ -void UnitTest::PopGTestTrace() { - internal::MutexLock lock(&mutex_); - impl_->gtest_trace_stack().pop_back(); -} - -namespace internal { - -UnitTestImpl::UnitTestImpl(UnitTest* parent) - : parent_(parent), -#ifdef _MSC_VER -#pragma warning(push) // Saves the current warning state. -#pragma warning(disable:4355) // Temporarily disables warning 4355 - // (using this in initializer). - default_global_test_part_result_reporter_(this), - default_per_thread_test_part_result_reporter_(this), -#pragma warning(pop) // Restores the warning state again. -#else - default_global_test_part_result_reporter_(this), - default_per_thread_test_part_result_reporter_(this), -#endif // _MSC_VER - global_test_part_result_repoter_( - &default_global_test_part_result_reporter_), - per_thread_test_part_result_reporter_( - &default_per_thread_test_part_result_reporter_), -#if GTEST_HAS_PARAM_TEST - parameterized_test_registry_(), - parameterized_tests_registered_(false), -#endif // GTEST_HAS_PARAM_TEST - last_death_test_case_(-1), - current_test_case_(NULL), - current_test_info_(NULL), - ad_hoc_test_result_(), - os_stack_trace_getter_(NULL), - post_flag_parse_init_performed_(false), - random_seed_(0), // Will be overridden by the flag before first use. - random_(0), // Will be reseeded before first use. -#if GTEST_HAS_DEATH_TEST - elapsed_time_(0), - internal_run_death_test_flag_(NULL), - death_test_factory_(new DefaultDeathTestFactory) { -#else - elapsed_time_(0) { -#endif // GTEST_HAS_DEATH_TEST - listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter); -} - -UnitTestImpl::~UnitTestImpl() { - // Deletes every TestCase. - ForEach(test_cases_, internal::Delete); - - // Deletes every Environment. - ForEach(environments_, internal::Delete); - - delete os_stack_trace_getter_; -} - -#if GTEST_HAS_DEATH_TEST -// Disables event forwarding if the control is currently in a death test -// subprocess. Must not be called before InitGoogleTest. -void UnitTestImpl::SuppressTestEventsIfInSubprocess() { - if (internal_run_death_test_flag_.get() != NULL) - listeners()->SuppressEventForwarding(); -} -#endif // GTEST_HAS_DEATH_TEST - -// Initializes event listeners performing XML output as specified by -// UnitTestOptions. Must not be called before InitGoogleTest. -void UnitTestImpl::ConfigureXmlOutput() { - const String& output_format = UnitTestOptions::GetOutputFormat(); - if (output_format == "xml") { - listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( - UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); - } else if (output_format != "") { - printf("WARNING: unrecognized output format \"%s\" ignored.\n", - output_format.c_str()); - fflush(stdout); - } -} - -// Performs initialization dependent upon flag values obtained in -// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to -// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest -// this function is also called from RunAllTests. Since this function can be -// called more than once, it has to be idempotent. -void UnitTestImpl::PostFlagParsingInit() { - // Ensures that this function does not execute more than once. - if (!post_flag_parse_init_performed_) { - post_flag_parse_init_performed_ = true; - -#if GTEST_HAS_DEATH_TEST - InitDeathTestSubprocessControlInfo(); - SuppressTestEventsIfInSubprocess(); -#endif // GTEST_HAS_DEATH_TEST - - // Registers parameterized tests. This makes parameterized tests - // available to the UnitTest reflection API without running - // RUN_ALL_TESTS. - RegisterParameterizedTests(); - - // Configures listeners for XML output. This makes it possible for users - // to shut down the default XML output before invoking RUN_ALL_TESTS. - ConfigureXmlOutput(); - } -} - -// A predicate that checks the name of a TestCase against a known -// value. -// -// This is used for implementation of the UnitTest class only. We put -// it in the anonymous namespace to prevent polluting the outer -// namespace. -// -// TestCaseNameIs is copyable. -class TestCaseNameIs { - public: - // Constructor. - explicit TestCaseNameIs(const String& name) - : name_(name) {} - - // Returns true iff the name of test_case matches name_. - bool operator()(const TestCase* test_case) const { - return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0; - } - - private: - String name_; -}; - -// Finds and returns a TestCase with the given name. If one doesn't -// exist, creates one and returns it. It's the CALLER'S -// RESPONSIBILITY to ensure that this function is only called WHEN THE -// TESTS ARE NOT SHUFFLED. -// -// Arguments: -// -// test_case_name: name of the test case -// set_up_tc: pointer to the function that sets up the test case -// tear_down_tc: pointer to the function that tears down the test case -TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, - const char* comment, - Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc) { - // Can we find a TestCase with the given name? - const std::vector::const_iterator test_case = - std::find_if(test_cases_.begin(), test_cases_.end(), - TestCaseNameIs(test_case_name)); - - if (test_case != test_cases_.end()) - return *test_case; - - // No. Let's create one. - TestCase* const new_test_case = - new TestCase(test_case_name, comment, set_up_tc, tear_down_tc); - - // Is this a death test case? - if (internal::UnitTestOptions::MatchesFilter(String(test_case_name), - kDeathTestCaseFilter)) { - // Yes. Inserts the test case after the last death test case - // defined so far. This only works when the test cases haven't - // been shuffled. Otherwise we may end up running a death test - // after a non-death test. - ++last_death_test_case_; - test_cases_.insert(test_cases_.begin() + last_death_test_case_, - new_test_case); - } else { - // No. Appends to the end of the list. - test_cases_.push_back(new_test_case); - } - - test_case_indices_.push_back(static_cast(test_case_indices_.size())); - return new_test_case; -} - -// Helpers for setting up / tearing down the given environment. They -// are for use in the ForEach() function. -static void SetUpEnvironment(Environment* env) { env->SetUp(); } -static void TearDownEnvironment(Environment* env) { env->TearDown(); } - -// Runs all tests in this UnitTest object, prints the result, and -// returns 0 if all tests are successful, or 1 otherwise. If any -// exception is thrown during a test on Windows, this test is -// considered to be failed, but the rest of the tests will still be -// run. (We disable exceptions on Linux and Mac OS X, so the issue -// doesn't apply there.) -// When parameterized tests are enabled, it expands and registers -// parameterized tests first in RegisterParameterizedTests(). -// All other functions called from RunAllTests() may safely assume that -// parameterized tests are ready to be counted and run. -int UnitTestImpl::RunAllTests() { - // Makes sure InitGoogleTest() was called. - if (!GTestIsInitialized()) { - printf("%s", - "\nThis test program did NOT call ::testing::InitGoogleTest " - "before calling RUN_ALL_TESTS(). Please fix it.\n"); - return 1; - } - - // Do not run any test if the --help flag was specified. - if (g_help_flag) - return 0; - - // Repeats the call to the post-flag parsing initialization in case the - // user didn't call InitGoogleTest. - PostFlagParsingInit(); - - // Even if sharding is not on, test runners may want to use the - // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding - // protocol. - internal::WriteToShardStatusFileIfNeeded(); - - // True iff we are in a subprocess for running a thread-safe-style - // death test. - bool in_subprocess_for_death_test = false; - -#if GTEST_HAS_DEATH_TEST - in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL); -#endif // GTEST_HAS_DEATH_TEST - - const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex, - in_subprocess_for_death_test); - - // Compares the full test names with the filter to decide which - // tests to run. - const bool has_tests_to_run = FilterTests(should_shard - ? HONOR_SHARDING_PROTOCOL - : IGNORE_SHARDING_PROTOCOL) > 0; - - // Lists the tests and exits if the --gtest_list_tests flag was specified. - if (GTEST_FLAG(list_tests)) { - // This must be called *after* FilterTests() has been called. - ListTestsMatchingFilter(); - return 0; - } - - random_seed_ = GTEST_FLAG(shuffle) ? - GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0; - - // True iff at least one test has failed. - bool failed = false; - - TestEventListener* repeater = listeners()->repeater(); - - repeater->OnTestProgramStart(*parent_); - - // How many times to repeat the tests? We don't want to repeat them - // when we are inside the subprocess of a death test. - const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); - // Repeats forever if the repeat count is negative. - const bool forever = repeat < 0; - for (int i = 0; forever || i != repeat; i++) { - ClearResult(); - - const TimeInMillis start = GetTimeInMillis(); - - // Shuffles test cases and tests if requested. - if (has_tests_to_run && GTEST_FLAG(shuffle)) { - random()->Reseed(random_seed_); - // This should be done before calling OnTestIterationStart(), - // such that a test event listener can see the actual test order - // in the event. - ShuffleTests(); - } - - // Tells the unit test event listeners that the tests are about to start. - repeater->OnTestIterationStart(*parent_, i); - - // Runs each test case if there is at least one test to run. - if (has_tests_to_run) { - // Sets up all environments beforehand. - repeater->OnEnvironmentsSetUpStart(*parent_); - ForEach(environments_, SetUpEnvironment); - repeater->OnEnvironmentsSetUpEnd(*parent_); - - // Runs the tests only if there was no fatal failure during global - // set-up. - if (!Test::HasFatalFailure()) { - for (int test_index = 0; test_index < total_test_case_count(); - test_index++) { - GetMutableTestCase(test_index)->Run(); - } - } - - // Tears down all environments in reverse order afterwards. - repeater->OnEnvironmentsTearDownStart(*parent_); - std::for_each(environments_.rbegin(), environments_.rend(), - TearDownEnvironment); - repeater->OnEnvironmentsTearDownEnd(*parent_); - } - - elapsed_time_ = GetTimeInMillis() - start; - - // Tells the unit test event listener that the tests have just finished. - repeater->OnTestIterationEnd(*parent_, i); - - // Gets the result and clears it. - if (!Passed()) { - failed = true; - } - - // Restores the original test order after the iteration. This - // allows the user to quickly repro a failure that happens in the - // N-th iteration without repeating the first (N - 1) iterations. - // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in - // case the user somehow changes the value of the flag somewhere - // (it's always safe to unshuffle the tests). - UnshuffleTests(); - - if (GTEST_FLAG(shuffle)) { - // Picks a new random seed for each iteration. - random_seed_ = GetNextRandomSeed(random_seed_); - } - } - - repeater->OnTestProgramEnd(*parent_); - - // Returns 0 if all tests passed, or 1 other wise. - return failed ? 1 : 0; -} - -// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file -// if the variable is present. If a file already exists at this location, this -// function will write over it. If the variable is present, but the file cannot -// be created, prints an error and exits. -void WriteToShardStatusFileIfNeeded() { - const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile); - if (test_shard_file != NULL) { - FILE* const file = posix::FOpen(test_shard_file, "w"); - if (file == NULL) { - ColoredPrintf(COLOR_RED, - "Could not write to the test shard status file \"%s\" " - "specified by the %s environment variable.\n", - test_shard_file, kTestShardStatusFile); - fflush(stdout); - exit(EXIT_FAILURE); - } - fclose(file); - } -} - -// Checks whether sharding is enabled by examining the relevant -// environment variable values. If the variables are present, -// but inconsistent (i.e., shard_index >= total_shards), prints -// an error and exits. If in_subprocess_for_death_test, sharding is -// disabled because it must only be applied to the original test -// process. Otherwise, we could filter out death tests we intended to execute. -bool ShouldShard(const char* total_shards_env, - const char* shard_index_env, - bool in_subprocess_for_death_test) { - if (in_subprocess_for_death_test) { - return false; - } - - const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1); - const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1); - - if (total_shards == -1 && shard_index == -1) { - return false; - } else if (total_shards == -1 && shard_index != -1) { - const Message msg = Message() - << "Invalid environment variables: you have " - << kTestShardIndex << " = " << shard_index - << ", but have left " << kTestTotalShards << " unset.\n"; - ColoredPrintf(COLOR_RED, msg.GetString().c_str()); - fflush(stdout); - exit(EXIT_FAILURE); - } else if (total_shards != -1 && shard_index == -1) { - const Message msg = Message() - << "Invalid environment variables: you have " - << kTestTotalShards << " = " << total_shards - << ", but have left " << kTestShardIndex << " unset.\n"; - ColoredPrintf(COLOR_RED, msg.GetString().c_str()); - fflush(stdout); - exit(EXIT_FAILURE); - } else if (shard_index < 0 || shard_index >= total_shards) { - const Message msg = Message() - << "Invalid environment variables: we require 0 <= " - << kTestShardIndex << " < " << kTestTotalShards - << ", but you have " << kTestShardIndex << "=" << shard_index - << ", " << kTestTotalShards << "=" << total_shards << ".\n"; - ColoredPrintf(COLOR_RED, msg.GetString().c_str()); - fflush(stdout); - exit(EXIT_FAILURE); - } - - return total_shards > 1; -} - -// Parses the environment variable var as an Int32. If it is unset, -// returns default_val. If it is not an Int32, prints an error -// and aborts. -Int32 Int32FromEnvOrDie(const char* const var, Int32 default_val) { - const char* str_val = posix::GetEnv(var); - if (str_val == NULL) { - return default_val; - } - - Int32 result; - if (!ParseInt32(Message() << "The value of environment variable " << var, - str_val, &result)) { - exit(EXIT_FAILURE); - } - return result; -} - -// Given the total number of shards, the shard index, and the test id, -// returns true iff the test should be run on this shard. The test id is -// some arbitrary but unique non-negative integer assigned to each test -// method. Assumes that 0 <= shard_index < total_shards. -bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) { - return (test_id % total_shards) == shard_index; -} - -// Compares the name of each test with the user-specified filter to -// decide whether the test should be run, then records the result in -// each TestCase and TestInfo object. -// If shard_tests == true, further filters tests based on sharding -// variables in the environment - see -// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide. -// Returns the number of tests that should run. -int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { - const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ? - Int32FromEnvOrDie(kTestTotalShards, -1) : -1; - const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ? - Int32FromEnvOrDie(kTestShardIndex, -1) : -1; - - // num_runnable_tests are the number of tests that will - // run across all shards (i.e., match filter and are not disabled). - // num_selected_tests are the number of tests to be run on - // this shard. - int num_runnable_tests = 0; - int num_selected_tests = 0; - for (size_t i = 0; i < test_cases_.size(); i++) { - TestCase* const test_case = test_cases_[i]; - const String &test_case_name = test_case->name(); - test_case->set_should_run(false); - - for (size_t j = 0; j < test_case->test_info_list().size(); j++) { - TestInfo* const test_info = test_case->test_info_list()[j]; - const String test_name(test_info->name()); - // A test is disabled if test case name or test name matches - // kDisableTestFilter. - const bool is_disabled = - internal::UnitTestOptions::MatchesFilter(test_case_name, - kDisableTestFilter) || - internal::UnitTestOptions::MatchesFilter(test_name, - kDisableTestFilter); - test_info->impl()->set_is_disabled(is_disabled); - - const bool matches_filter = - internal::UnitTestOptions::FilterMatchesTest(test_case_name, - test_name); - test_info->impl()->set_matches_filter(matches_filter); - - const bool is_runnable = - (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && - matches_filter; - - const bool is_selected = is_runnable && - (shard_tests == IGNORE_SHARDING_PROTOCOL || - ShouldRunTestOnShard(total_shards, shard_index, - num_runnable_tests)); - - num_runnable_tests += is_runnable; - num_selected_tests += is_selected; - - test_info->impl()->set_should_run(is_selected); - test_case->set_should_run(test_case->should_run() || is_selected); - } - } - return num_selected_tests; -} - -// Prints the names of the tests matching the user-specified filter flag. -void UnitTestImpl::ListTestsMatchingFilter() { - for (size_t i = 0; i < test_cases_.size(); i++) { - const TestCase* const test_case = test_cases_[i]; - bool printed_test_case_name = false; - - for (size_t j = 0; j < test_case->test_info_list().size(); j++) { - const TestInfo* const test_info = - test_case->test_info_list()[j]; - if (test_info->matches_filter()) { - if (!printed_test_case_name) { - printed_test_case_name = true; - printf("%s.\n", test_case->name()); - } - printf(" %s\n", test_info->name()); - } - } - } - fflush(stdout); -} - -// Sets the OS stack trace getter. -// -// Does nothing if the input and the current OS stack trace getter are -// the same; otherwise, deletes the old getter and makes the input the -// current getter. -void UnitTestImpl::set_os_stack_trace_getter( - OsStackTraceGetterInterface* getter) { - if (os_stack_trace_getter_ != getter) { - delete os_stack_trace_getter_; - os_stack_trace_getter_ = getter; - } -} - -// Returns the current OS stack trace getter if it is not NULL; -// otherwise, creates an OsStackTraceGetter, makes it the current -// getter, and returns it. -OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { - if (os_stack_trace_getter_ == NULL) { - os_stack_trace_getter_ = new OsStackTraceGetter; - } - - return os_stack_trace_getter_; -} - -// Returns the TestResult for the test that's currently running, or -// the TestResult for the ad hoc test if no test is running. -TestResult* UnitTestImpl::current_test_result() { - return current_test_info_ ? - current_test_info_->impl()->result() : &ad_hoc_test_result_; -} - -// Shuffles all test cases, and the tests within each test case, -// making sure that death tests are still run first. -void UnitTestImpl::ShuffleTests() { - // Shuffles the death test cases. - ShuffleRange(random(), 0, last_death_test_case_ + 1, &test_case_indices_); - - // Shuffles the non-death test cases. - ShuffleRange(random(), last_death_test_case_ + 1, - static_cast(test_cases_.size()), &test_case_indices_); - - // Shuffles the tests inside each test case. - for (size_t i = 0; i < test_cases_.size(); i++) { - test_cases_[i]->ShuffleTests(random()); - } -} - -// Restores the test cases and tests to their order before the first shuffle. -void UnitTestImpl::UnshuffleTests() { - for (size_t i = 0; i < test_cases_.size(); i++) { - // Unshuffles the tests in each test case. - test_cases_[i]->UnshuffleTests(); - // Resets the index of each test case. - test_case_indices_[i] = static_cast(i); - } -} - -// TestInfoImpl constructor. The new instance assumes ownership of the test -// factory object. -TestInfoImpl::TestInfoImpl(TestInfo* parent, - const char* a_test_case_name, - const char* a_name, - const char* a_test_case_comment, - const char* a_comment, - TypeId a_fixture_class_id, - internal::TestFactoryBase* factory) : - parent_(parent), - test_case_name_(String(a_test_case_name)), - name_(String(a_name)), - test_case_comment_(String(a_test_case_comment)), - comment_(String(a_comment)), - fixture_class_id_(a_fixture_class_id), - should_run_(false), - is_disabled_(false), - matches_filter_(false), - factory_(factory) { -} - -// TestInfoImpl destructor. -TestInfoImpl::~TestInfoImpl() { - delete factory_; -} - -// Returns the current OS stack trace as a String. -// -// The maximum number of stack frames to be included is specified by -// the gtest_stack_trace_depth flag. The skip_count parameter -// specifies the number of top frames to be skipped, which doesn't -// count against the number of frames to be included. -// -// For example, if Foo() calls Bar(), which in turn calls -// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in -// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. -String GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, - int skip_count) { - // We pass skip_count + 1 to skip this wrapper function in addition - // to what the user really wants to skip. - return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); -} - -// Used by the GTEST_HIDE_UNREACHABLE_CODE_ macro to suppress unreachable -// code warnings. -namespace { -class ClassUniqueToAlwaysTrue {}; -} - -bool IsTrue(bool condition) { return condition; } - -bool AlwaysTrue() { -#if GTEST_HAS_EXCEPTIONS - // This condition is always false so AlwaysTrue() never actually throws, - // but it makes the compiler think that it may throw. - if (IsTrue(false)) - throw ClassUniqueToAlwaysTrue(); -#endif // GTEST_HAS_EXCEPTIONS - return true; -} - -// If *pstr starts with the given prefix, modifies *pstr to be right -// past the prefix and returns true; otherwise leaves *pstr unchanged -// and returns false. None of pstr, *pstr, and prefix can be NULL. -bool SkipPrefix(const char* prefix, const char** pstr) { - const size_t prefix_len = strlen(prefix); - if (strncmp(*pstr, prefix, prefix_len) == 0) { - *pstr += prefix_len; - return true; - } - return false; -} - -// Parses a string as a command line flag. The string should have -// the format "--flag=value". When def_optional is true, the "=value" -// part can be omitted. -// -// Returns the value of the flag, or NULL if the parsing failed. -const char* ParseFlagValue(const char* str, - const char* flag, - bool def_optional) { - // str and flag must not be NULL. - if (str == NULL || flag == NULL) return NULL; - - // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. - const String flag_str = String::Format("--%s%s", GTEST_FLAG_PREFIX_, flag); - const size_t flag_len = flag_str.length(); - if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; - - // Skips the flag name. - const char* flag_end = str + flag_len; - - // When def_optional is true, it's OK to not have a "=value" part. - if (def_optional && (flag_end[0] == '\0')) { - return flag_end; - } - - // If def_optional is true and there are more characters after the - // flag name, or if def_optional is false, there must be a '=' after - // the flag name. - if (flag_end[0] != '=') return NULL; - - // Returns the string after "=". - return flag_end + 1; -} - -// Parses a string for a bool flag, in the form of either -// "--flag=value" or "--flag". -// -// In the former case, the value is taken as true as long as it does -// not start with '0', 'f', or 'F'. -// -// In the latter case, the value is taken as true. -// -// On success, stores the value of the flag in *value, and returns -// true. On failure, returns false without changing *value. -bool ParseBoolFlag(const char* str, const char* flag, bool* value) { - // Gets the value of the flag as a string. - const char* const value_str = ParseFlagValue(str, flag, true); - - // Aborts if the parsing failed. - if (value_str == NULL) return false; - - // Converts the string value to a bool. - *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); - return true; -} - -// Parses a string for an Int32 flag, in the form of -// "--flag=value". -// -// On success, stores the value of the flag in *value, and returns -// true. On failure, returns false without changing *value. -bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { - // Gets the value of the flag as a string. - const char* const value_str = ParseFlagValue(str, flag, false); - - // Aborts if the parsing failed. - if (value_str == NULL) return false; - - // Sets *value to the value of the flag. - return ParseInt32(Message() << "The value of flag --" << flag, - value_str, value); -} - -// Parses a string for a string flag, in the form of -// "--flag=value". -// -// On success, stores the value of the flag in *value, and returns -// true. On failure, returns false without changing *value. -bool ParseStringFlag(const char* str, const char* flag, String* value) { - // Gets the value of the flag as a string. - const char* const value_str = ParseFlagValue(str, flag, false); - - // Aborts if the parsing failed. - if (value_str == NULL) return false; - - // Sets *value to the value of the flag. - *value = value_str; - return true; -} - -// Determines whether a string has a prefix that Google Test uses for its -// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_. -// If Google Test detects that a command line flag has its prefix but is not -// recognized, it will print its help message. Flags starting with -// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test -// internal flags and do not trigger the help message. -static bool HasGoogleTestFlagPrefix(const char* str) { - return (SkipPrefix("--", &str) || - SkipPrefix("-", &str) || - SkipPrefix("/", &str)) && - !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) && - (SkipPrefix(GTEST_FLAG_PREFIX_, &str) || - SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str)); -} - -// Prints a string containing code-encoded text. The following escape -// sequences can be used in the string to control the text color: -// -// @@ prints a single '@' character. -// @R changes the color to red. -// @G changes the color to green. -// @Y changes the color to yellow. -// @D changes to the default terminal text color. -// -// TODO(wan@google.com): Write tests for this once we add stdout -// capturing to Google Test. -static void PrintColorEncoded(const char* str) { - GTestColor color = COLOR_DEFAULT; // The current color. - - // Conceptually, we split the string into segments divided by escape - // sequences. Then we print one segment at a time. At the end of - // each iteration, the str pointer advances to the beginning of the - // next segment. - for (;;) { - const char* p = strchr(str, '@'); - if (p == NULL) { - ColoredPrintf(color, "%s", str); - return; - } - - ColoredPrintf(color, "%s", String(str, p - str).c_str()); - - const char ch = p[1]; - str = p + 2; - if (ch == '@') { - ColoredPrintf(color, "@"); - } else if (ch == 'D') { - color = COLOR_DEFAULT; - } else if (ch == 'R') { - color = COLOR_RED; - } else if (ch == 'G') { - color = COLOR_GREEN; - } else if (ch == 'Y') { - color = COLOR_YELLOW; - } else { - --str; - } - } -} - -static const char kColorEncodedHelpMessage[] = -"This program contains tests written using " GTEST_NAME_ ". You can use the\n" -"following command line flags to control its behavior:\n" -"\n" -"Test Selection:\n" -" @G--" GTEST_FLAG_PREFIX_ "list_tests@D\n" -" List the names of all tests instead of running them. The name of\n" -" TEST(Foo, Bar) is \"Foo.Bar\".\n" -" @G--" GTEST_FLAG_PREFIX_ "filter=@YPOSTIVE_PATTERNS" - "[@G-@YNEGATIVE_PATTERNS]@D\n" -" Run only the tests whose name matches one of the positive patterns but\n" -" none of the negative patterns. '?' matches any single character; '*'\n" -" matches any substring; ':' separates two patterns.\n" -" @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests@D\n" -" Run all disabled tests too.\n" -"\n" -"Test Execution:\n" -" @G--" GTEST_FLAG_PREFIX_ "repeat=@Y[COUNT]@D\n" -" Run the tests repeatedly; use a negative count to repeat forever.\n" -" @G--" GTEST_FLAG_PREFIX_ "shuffle@D\n" -" Randomize tests' orders on every iteration.\n" -" @G--" GTEST_FLAG_PREFIX_ "random_seed=@Y[NUMBER]@D\n" -" Random number seed to use for shuffling test orders (between 1 and\n" -" 99999, or 0 to use a seed based on the current time).\n" -"\n" -"Test Output:\n" -" @G--" GTEST_FLAG_PREFIX_ "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n" -" Enable/disable colored output. The default is @Gauto@D.\n" -" -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n" -" Don't print the elapsed time of each test.\n" -" @G--" GTEST_FLAG_PREFIX_ "output=xml@Y[@G:@YDIRECTORY_PATH@G" - GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n" -" Generate an XML report in the given directory or with the given file\n" -" name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n" -"\n" -"Assertion Behavior:\n" -#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS -" @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" -" Set the default death test style.\n" -#endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS -" @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n" -" Turn assertion failures into debugger break-points.\n" -" @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n" -" Turn assertion failures into C++ exceptions.\n" -#if GTEST_OS_WINDOWS -" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions@D\n" -" Suppress pop-ups caused by exceptions.\n" -#endif // GTEST_OS_WINDOWS -"\n" -"Except for @G--" GTEST_FLAG_PREFIX_ "list_tests@D, you can alternatively set " - "the corresponding\n" -"environment variable of a flag (all letters in upper-case). For example, to\n" -"disable colored text output, you can either specify @G--" GTEST_FLAG_PREFIX_ - "color=no@D or set\n" -"the @G" GTEST_FLAG_PREFIX_UPPER_ "COLOR@D environment variable to @Gno@D.\n" -"\n" -"For more information, please read the " GTEST_NAME_ " documentation at\n" -"@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ "\n" -"(not one in your own code or tests), please report it to\n" -"@G<" GTEST_DEV_EMAIL_ ">@D.\n"; - -// Parses the command line for Google Test flags, without initializing -// other parts of Google Test. The type parameter CharType can be -// instantiated to either char or wchar_t. -template -void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { - for (int i = 1; i < *argc; i++) { - const String arg_string = StreamableToString(argv[i]); - const char* const arg = arg_string.c_str(); - - using internal::ParseBoolFlag; - using internal::ParseInt32Flag; - using internal::ParseStringFlag; - - // Do we see a Google Test flag? - if (ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag, - >EST_FLAG(also_run_disabled_tests)) || - ParseBoolFlag(arg, kBreakOnFailureFlag, - >EST_FLAG(break_on_failure)) || - ParseBoolFlag(arg, kCatchExceptionsFlag, - >EST_FLAG(catch_exceptions)) || - ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) || - ParseStringFlag(arg, kDeathTestStyleFlag, - >EST_FLAG(death_test_style)) || - ParseBoolFlag(arg, kDeathTestUseFork, - >EST_FLAG(death_test_use_fork)) || - ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || - ParseStringFlag(arg, kInternalRunDeathTestFlag, - >EST_FLAG(internal_run_death_test)) || - ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || - ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || - ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) || - ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) || - ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) || - ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || - ParseInt32Flag(arg, kStackTraceDepthFlag, - >EST_FLAG(stack_trace_depth)) || - ParseBoolFlag(arg, kThrowOnFailureFlag, >EST_FLAG(throw_on_failure)) - ) { - // Yes. Shift the remainder of the argv list left by one. Note - // that argv has (*argc + 1) elements, the last one always being - // NULL. The following loop moves the trailing NULL element as - // well. - for (int j = i; j != *argc; j++) { - argv[j] = argv[j + 1]; - } - - // Decrements the argument count. - (*argc)--; - - // We also need to decrement the iterator as we just removed - // an element. - i--; - } else if (arg_string == "--help" || arg_string == "-h" || - arg_string == "-?" || arg_string == "/?" || - HasGoogleTestFlagPrefix(arg)) { - // Both help flag and unrecognized Google Test flags (excluding - // internal ones) trigger help display. - g_help_flag = true; - } - } - - if (g_help_flag) { - // We print the help here instead of in RUN_ALL_TESTS(), as the - // latter may not be called at all if the user is using Google - // Test with another testing framework. - PrintColorEncoded(kColorEncodedHelpMessage); - } -} - -// Parses the command line for Google Test flags, without initializing -// other parts of Google Test. -void ParseGoogleTestFlagsOnly(int* argc, char** argv) { - ParseGoogleTestFlagsOnlyImpl(argc, argv); -} -void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) { - ParseGoogleTestFlagsOnlyImpl(argc, argv); -} - -// The internal implementation of InitGoogleTest(). -// -// The type parameter CharType can be instantiated to either char or -// wchar_t. -template -void InitGoogleTestImpl(int* argc, CharType** argv) { - g_init_gtest_count++; - - // We don't want to run the initialization code twice. - if (g_init_gtest_count != 1) return; - - if (*argc <= 0) return; - - internal::g_executable_path = internal::StreamableToString(argv[0]); - -#if GTEST_HAS_DEATH_TEST - g_argvs.clear(); - for (int i = 0; i != *argc; i++) { - g_argvs.push_back(StreamableToString(argv[i])); - } -#endif // GTEST_HAS_DEATH_TEST - - ParseGoogleTestFlagsOnly(argc, argv); - GetUnitTestImpl()->PostFlagParsingInit(); -} - -} // namespace internal - -// Initializes Google Test. This must be called before calling -// RUN_ALL_TESTS(). In particular, it parses a command line for the -// flags that Google Test recognizes. Whenever a Google Test flag is -// seen, it is removed from argv, and *argc is decremented. -// -// No value is returned. Instead, the Google Test flag variables are -// updated. -// -// Calling the function for the second time has no user-visible effect. -void InitGoogleTest(int* argc, char** argv) { - internal::InitGoogleTestImpl(argc, argv); -} - -// This overloaded version can be used in Windows programs compiled in -// UNICODE mode. -void InitGoogleTest(int* argc, wchar_t** argv) { - internal::InitGoogleTestImpl(argc, argv); -} - -} // namespace testing -// Copyright 2005, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev) -// -// This file implements death tests. - - -#if GTEST_HAS_DEATH_TEST - -#if GTEST_OS_MAC -#include -#endif // GTEST_OS_MAC - -#include -#include -#include -#include - -#if GTEST_OS_WINDOWS -#include -#else -#include -#include -#endif // GTEST_OS_WINDOWS - -#endif // GTEST_HAS_DEATH_TEST - - -// Indicates that this translation unit is part of Google Test's -// implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick is to -// prevent a user from accidentally including gtest-internal-inl.h in -// his code. -#define GTEST_IMPLEMENTATION_ 1 -#undef GTEST_IMPLEMENTATION_ - -namespace testing { - -// Constants. - -// The default death test style. -static const char kDefaultDeathTestStyle[] = "fast"; - -GTEST_DEFINE_string_( - death_test_style, - internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle), - "Indicates how to run a death test in a forked child process: " - "\"threadsafe\" (child process re-executes the test binary " - "from the beginning, running only the specific death test) or " - "\"fast\" (child process runs the death test immediately " - "after forking)."); - -GTEST_DEFINE_bool_( - death_test_use_fork, - internal::BoolFromGTestEnv("death_test_use_fork", false), - "Instructs to use fork()/_exit() instead of clone() in death tests. " - "Ignored and always uses fork() on POSIX systems where clone() is not " - "implemented. Useful when running under valgrind or similar tools if " - "those do not support clone(). Valgrind 3.3.1 will just fail if " - "it sees an unsupported combination of clone() flags. " - "It is not recommended to use this flag w/o valgrind though it will " - "work in 99% of the cases. Once valgrind is fixed, this flag will " - "most likely be removed."); - -namespace internal { -GTEST_DEFINE_string_( - internal_run_death_test, "", - "Indicates the file, line number, temporal index of " - "the single death test to run, and a file descriptor to " - "which a success code may be sent, all separated by " - "colons. This flag is specified if and only if the current " - "process is a sub-process launched for running a thread-safe " - "death test. FOR INTERNAL USE ONLY."); -} // namespace internal - -#if GTEST_HAS_DEATH_TEST - -// ExitedWithCode constructor. -ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { -} - -// ExitedWithCode function-call operator. -bool ExitedWithCode::operator()(int exit_status) const { -#if GTEST_OS_WINDOWS - return exit_status == exit_code_; -#else - return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; -#endif // GTEST_OS_WINDOWS -} - -#if !GTEST_OS_WINDOWS -// KilledBySignal constructor. -KilledBySignal::KilledBySignal(int signum) : signum_(signum) { -} - -// KilledBySignal function-call operator. -bool KilledBySignal::operator()(int exit_status) const { - return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; -} -#endif // !GTEST_OS_WINDOWS - -namespace internal { - -// Utilities needed for death tests. - -// Generates a textual description of a given exit code, in the format -// specified by wait(2). -static String ExitSummary(int exit_code) { - Message m; -#if GTEST_OS_WINDOWS - m << "Exited with exit status " << exit_code; -#else - if (WIFEXITED(exit_code)) { - m << "Exited with exit status " << WEXITSTATUS(exit_code); - } else if (WIFSIGNALED(exit_code)) { - m << "Terminated by signal " << WTERMSIG(exit_code); - } -#ifdef WCOREDUMP - if (WCOREDUMP(exit_code)) { - m << " (core dumped)"; - } -#endif -#endif // GTEST_OS_WINDOWS - return m.GetString(); -} - -// Returns true if exit_status describes a process that was terminated -// by a signal, or exited normally with a nonzero exit code. -bool ExitedUnsuccessfully(int exit_status) { - return !ExitedWithCode(0)(exit_status); -} - -#if !GTEST_OS_WINDOWS -// Generates a textual failure message when a death test finds more than -// one thread running, or cannot determine the number of threads, prior -// to executing the given statement. It is the responsibility of the -// caller not to pass a thread_count of 1. -static String DeathTestThreadWarning(size_t thread_count) { - Message msg; - msg << "Death tests use fork(), which is unsafe particularly" - << " in a threaded context. For this test, " << GTEST_NAME_ << " "; - if (thread_count == 0) - msg << "couldn't detect the number of threads."; - else - msg << "detected " << thread_count << " threads."; - return msg.GetString(); -} -#endif // !GTEST_OS_WINDOWS - -// Flag characters for reporting a death test that did not die. -static const char kDeathTestLived = 'L'; -static const char kDeathTestReturned = 'R'; -static const char kDeathTestInternalError = 'I'; - -// An enumeration describing all of the possible ways that a death test -// can conclude. DIED means that the process died while executing the -// test code; LIVED means that process lived beyond the end of the test -// code; and RETURNED means that the test statement attempted a "return," -// which is not allowed. IN_PROGRESS means the test has not yet -// concluded. -enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED }; - -// Routine for aborting the program which is safe to call from an -// exec-style death test child process, in which case the error -// message is propagated back to the parent process. Otherwise, the -// message is simply printed to stderr. In either case, the program -// then exits with status 1. -void DeathTestAbort(const String& message) { - // On a POSIX system, this function may be called from a threadsafe-style - // death test child process, which operates on a very small stack. Use - // the heap for any additional non-minuscule memory requirements. - const InternalRunDeathTestFlag* const flag = - GetUnitTestImpl()->internal_run_death_test_flag(); - if (flag != NULL) { - FILE* parent = posix::FDOpen(flag->write_fd(), "w"); - fputc(kDeathTestInternalError, parent); - fprintf(parent, "%s", message.c_str()); - fflush(parent); - _exit(1); - } else { - fprintf(stderr, "%s", message.c_str()); - fflush(stderr); - abort(); - } -} - -// A replacement for CHECK that calls DeathTestAbort if the assertion -// fails. -#define GTEST_DEATH_TEST_CHECK_(expression) \ - do { \ - if (!::testing::internal::IsTrue(expression)) { \ - DeathTestAbort(::testing::internal::String::Format( \ - "CHECK failed: File %s, line %d: %s", \ - __FILE__, __LINE__, #expression)); \ - } \ - } while (::testing::internal::AlwaysFalse()) - -// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for -// evaluating any system call that fulfills two conditions: it must return -// -1 on failure, and set errno to EINTR when it is interrupted and -// should be tried again. The macro expands to a loop that repeatedly -// evaluates the expression as long as it evaluates to -1 and sets -// errno to EINTR. If the expression evaluates to -1 but errno is -// something other than EINTR, DeathTestAbort is called. -#define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \ - do { \ - int gtest_retval; \ - do { \ - gtest_retval = (expression); \ - } while (gtest_retval == -1 && errno == EINTR); \ - if (gtest_retval == -1) { \ - DeathTestAbort(::testing::internal::String::Format( \ - "CHECK failed: File %s, line %d: %s != -1", \ - __FILE__, __LINE__, #expression)); \ - } \ - } while (::testing::internal::AlwaysFalse()) - -// Returns the message describing the last system error in errno. -String GetLastErrnoDescription() { - return String(errno == 0 ? "" : posix::StrError(errno)); -} - -// This is called from a death test parent process to read a failure -// message from the death test child process and log it with the FATAL -// severity. On Windows, the message is read from a pipe handle. On other -// platforms, it is read from a file descriptor. -static void FailFromInternalError(int fd) { - Message error; - char buffer[256]; - int num_read; - - do { - while ((num_read = posix::Read(fd, buffer, 255)) > 0) { - buffer[num_read] = '\0'; - error << buffer; - } - } while (num_read == -1 && errno == EINTR); - - if (num_read == 0) { - GTEST_LOG_(FATAL) << error.GetString(); - } else { - const int last_error = errno; - GTEST_LOG_(FATAL) << "Error while reading death test internal: " - << GetLastErrnoDescription() << " [" << last_error << "]"; - } -} - -// Death test constructor. Increments the running death test count -// for the current test. -DeathTest::DeathTest() { - TestInfo* const info = GetUnitTestImpl()->current_test_info(); - if (info == NULL) { - DeathTestAbort("Cannot run a death test outside of a TEST or " - "TEST_F construct"); - } -} - -// Creates and returns a death test by dispatching to the current -// death test factory. -bool DeathTest::Create(const char* statement, const RE* regex, - const char* file, int line, DeathTest** test) { - return GetUnitTestImpl()->death_test_factory()->Create( - statement, regex, file, line, test); -} - -const char* DeathTest::LastMessage() { - return last_death_test_message_.c_str(); -} - -void DeathTest::set_last_death_test_message(const String& message) { - last_death_test_message_ = message; -} - -String DeathTest::last_death_test_message_; - -// Provides cross platform implementation for some death functionality. -class DeathTestImpl : public DeathTest { - protected: - DeathTestImpl(const char* a_statement, const RE* a_regex) - : statement_(a_statement), - regex_(a_regex), - spawned_(false), - status_(-1), - outcome_(IN_PROGRESS), - read_fd_(-1), - write_fd_(-1) {} - - // read_fd_ is expected to be closed and cleared by a derived class. - ~DeathTestImpl() { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); } - - void Abort(AbortReason reason); - virtual bool Passed(bool status_ok); - - const char* statement() const { return statement_; } - const RE* regex() const { return regex_; } - bool spawned() const { return spawned_; } - void set_spawned(bool is_spawned) { spawned_ = is_spawned; } - int status() const { return status_; } - void set_status(int a_status) { status_ = a_status; } - DeathTestOutcome outcome() const { return outcome_; } - void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; } - int read_fd() const { return read_fd_; } - void set_read_fd(int fd) { read_fd_ = fd; } - int write_fd() const { return write_fd_; } - void set_write_fd(int fd) { write_fd_ = fd; } - - // Called in the parent process only. Reads the result code of the death - // test child process via a pipe, interprets it to set the outcome_ - // member, and closes read_fd_. Outputs diagnostics and terminates in - // case of unexpected codes. - void ReadAndInterpretStatusByte(); - - private: - // The textual content of the code this object is testing. This class - // doesn't own this string and should not attempt to delete it. - const char* const statement_; - // The regular expression which test output must match. DeathTestImpl - // doesn't own this object and should not attempt to delete it. - const RE* const regex_; - // True if the death test child process has been successfully spawned. - bool spawned_; - // The exit status of the child process. - int status_; - // How the death test concluded. - DeathTestOutcome outcome_; - // Descriptor to the read end of the pipe to the child process. It is - // always -1 in the child process. The child keeps its write end of the - // pipe in write_fd_. - int read_fd_; - // Descriptor to the child's write end of the pipe to the parent process. - // It is always -1 in the parent process. The parent keeps its end of the - // pipe in read_fd_. - int write_fd_; -}; - -// Called in the parent process only. Reads the result code of the death -// test child process via a pipe, interprets it to set the outcome_ -// member, and closes read_fd_. Outputs diagnostics and terminates in -// case of unexpected codes. -void DeathTestImpl::ReadAndInterpretStatusByte() { - char flag; - int bytes_read; - - // The read() here blocks until data is available (signifying the - // failure of the death test) or until the pipe is closed (signifying - // its success), so it's okay to call this in the parent before - // the child process has exited. - do { - bytes_read = posix::Read(read_fd(), &flag, 1); - } while (bytes_read == -1 && errno == EINTR); - - if (bytes_read == 0) { - set_outcome(DIED); - } else if (bytes_read == 1) { - switch (flag) { - case kDeathTestReturned: - set_outcome(RETURNED); - break; - case kDeathTestLived: - set_outcome(LIVED); - break; - case kDeathTestInternalError: - FailFromInternalError(read_fd()); // Does not return. - break; - default: - GTEST_LOG_(FATAL) << "Death test child process reported " - << "unexpected status byte (" - << static_cast(flag) << ")"; - } - } else { - GTEST_LOG_(FATAL) << "Read from death test child process failed: " - << GetLastErrnoDescription(); - } - GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd())); - set_read_fd(-1); -} - -// Signals that the death test code which should have exited, didn't. -// Should be called only in a death test child process. -// Writes a status byte to the child's status file descriptor, then -// calls _exit(1). -void DeathTestImpl::Abort(AbortReason reason) { - // The parent process considers the death test to be a failure if - // it finds any data in our pipe. So, here we write a single flag byte - // to the pipe, then exit. - const char status_ch = - reason == TEST_DID_NOT_DIE ? kDeathTestLived : kDeathTestReturned; - GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1)); - GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(write_fd())); - _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) -} - -// Assesses the success or failure of a death test, using both private -// members which have previously been set, and one argument: -// -// Private data members: -// outcome: An enumeration describing how the death test -// concluded: DIED, LIVED, or RETURNED. The death test fails -// in the latter two cases. -// status: The exit status of the child process. On *nix, it is in the -// in the format specified by wait(2). On Windows, this is the -// value supplied to the ExitProcess() API or a numeric code -// of the exception that terminated the program. -// regex: A regular expression object to be applied to -// the test's captured standard error output; the death test -// fails if it does not match. -// -// Argument: -// status_ok: true if exit_status is acceptable in the context of -// this particular death test, which fails if it is false -// -// Returns true iff all of the above conditions are met. Otherwise, the -// first failing condition, in the order given above, is the one that is -// reported. Also sets the last death test message string. -bool DeathTestImpl::Passed(bool status_ok) { - if (!spawned()) - return false; - - const String error_message = GetCapturedStderr(); - - bool success = false; - Message buffer; - - buffer << "Death test: " << statement() << "\n"; - switch (outcome()) { - case LIVED: - buffer << " Result: failed to die.\n" - << " Error msg: " << error_message; - break; - case RETURNED: - buffer << " Result: illegal return in test statement.\n" - << " Error msg: " << error_message; - break; - case DIED: - if (status_ok) { - const bool matched = RE::PartialMatch(error_message.c_str(), *regex()); - if (matched) { - success = true; - } else { - buffer << " Result: died but not with expected error.\n" - << " Expected: " << regex()->pattern() << "\n" - << "Actual msg: " << error_message; - } - } else { - buffer << " Result: died but not with expected exit code:\n" - << " " << ExitSummary(status()) << "\n"; - } - break; - case IN_PROGRESS: - default: - GTEST_LOG_(FATAL) - << "DeathTest::Passed somehow called before conclusion of test"; - } - - DeathTest::set_last_death_test_message(buffer.GetString()); - return success; -} - -#if GTEST_OS_WINDOWS -// WindowsDeathTest implements death tests on Windows. Due to the -// specifics of starting new processes on Windows, death tests there are -// always threadsafe, and Google Test considers the -// --gtest_death_test_style=fast setting to be equivalent to -// --gtest_death_test_style=threadsafe there. -// -// A few implementation notes: Like the Linux version, the Windows -// implementation uses pipes for child-to-parent communication. But due to -// the specifics of pipes on Windows, some extra steps are required: -// -// 1. The parent creates a communication pipe and stores handles to both -// ends of it. -// 2. The parent starts the child and provides it with the information -// necessary to acquire the handle to the write end of the pipe. -// 3. The child acquires the write end of the pipe and signals the parent -// using a Windows event. -// 4. Now the parent can release the write end of the pipe on its side. If -// this is done before step 3, the object's reference count goes down to -// 0 and it is destroyed, preventing the child from acquiring it. The -// parent now has to release it, or read operations on the read end of -// the pipe will not return when the child terminates. -// 5. The parent reads child's output through the pipe (outcome code and -// any possible error messages) from the pipe, and its stderr and then -// determines whether to fail the test. -// -// Note: to distinguish Win32 API calls from the local method and function -// calls, the former are explicitly resolved in the global namespace. -// -class WindowsDeathTest : public DeathTestImpl { - public: - WindowsDeathTest(const char* statement, - const RE* regex, - const char* file, - int line) - : DeathTestImpl(statement, regex), file_(file), line_(line) {} - - // All of these virtual functions are inherited from DeathTest. - virtual int Wait(); - virtual TestRole AssumeRole(); - - private: - // The name of the file in which the death test is located. - const char* const file_; - // The line number on which the death test is located. - const int line_; - // Handle to the write end of the pipe to the child process. - AutoHandle write_handle_; - // Child process handle. - AutoHandle child_handle_; - // Event the child process uses to signal the parent that it has - // acquired the handle to the write end of the pipe. After seeing this - // event the parent can release its own handles to make sure its - // ReadFile() calls return when the child terminates. - AutoHandle event_handle_; -}; - -// Waits for the child in a death test to exit, returning its exit -// status, or 0 if no child process exists. As a side effect, sets the -// outcome data member. -int WindowsDeathTest::Wait() { - if (!spawned()) - return 0; - - // Wait until the child either signals that it has acquired the write end - // of the pipe or it dies. - const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() }; - switch (::WaitForMultipleObjects(2, - wait_handles, - FALSE, // Waits for any of the handles. - INFINITE)) { - case WAIT_OBJECT_0: - case WAIT_OBJECT_0 + 1: - break; - default: - GTEST_DEATH_TEST_CHECK_(false); // Should not get here. - } - - // The child has acquired the write end of the pipe or exited. - // We release the handle on our side and continue. - write_handle_.Reset(); - event_handle_.Reset(); - - ReadAndInterpretStatusByte(); - - // Waits for the child process to exit if it haven't already. This - // returns immediately if the child has already exited, regardless of - // whether previous calls to WaitForMultipleObjects synchronized on this - // handle or not. - GTEST_DEATH_TEST_CHECK_( - WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(), - INFINITE)); - DWORD status; - GTEST_DEATH_TEST_CHECK_(::GetExitCodeProcess(child_handle_.Get(), &status) - != FALSE); - child_handle_.Reset(); - set_status(static_cast(status)); - return this->status(); -} - -// The AssumeRole process for a Windows death test. It creates a child -// process with the same executable as the current process to run the -// death test. The child process is given the --gtest_filter and -// --gtest_internal_run_death_test flags such that it knows to run the -// current death test only. -DeathTest::TestRole WindowsDeathTest::AssumeRole() { - const UnitTestImpl* const impl = GetUnitTestImpl(); - const InternalRunDeathTestFlag* const flag = - impl->internal_run_death_test_flag(); - const TestInfo* const info = impl->current_test_info(); - const int death_test_index = info->result()->death_test_count(); - - if (flag != NULL) { - // ParseInternalRunDeathTestFlag() has performed all the necessary - // processing. - set_write_fd(flag->write_fd()); - return EXECUTE_TEST; - } - - // WindowsDeathTest uses an anonymous pipe to communicate results of - // a death test. - SECURITY_ATTRIBUTES handles_are_inheritable = { - sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; - HANDLE read_handle, write_handle; - GTEST_DEATH_TEST_CHECK_( - ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable, - 0) // Default buffer size. - != FALSE); - set_read_fd(::_open_osfhandle(reinterpret_cast(read_handle), - O_RDONLY)); - write_handle_.Reset(write_handle); - event_handle_.Reset(::CreateEvent( - &handles_are_inheritable, - TRUE, // The event will automatically reset to non-signaled state. - FALSE, // The initial state is non-signalled. - NULL)); // The even is unnamed. - GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL); - const String filter_flag = String::Format("--%s%s=%s.%s", - GTEST_FLAG_PREFIX_, kFilterFlag, - info->test_case_name(), - info->name()); - const String internal_flag = String::Format( - "--%s%s=%s|%d|%d|%u|%Iu|%Iu", - GTEST_FLAG_PREFIX_, - kInternalRunDeathTestFlag, - file_, line_, - death_test_index, - static_cast(::GetCurrentProcessId()), - // size_t has the same with as pointers on both 32-bit and 64-bit - // Windows platforms. - // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. - reinterpret_cast(write_handle), - reinterpret_cast(event_handle_.Get())); - - char executable_path[_MAX_PATH + 1]; // NOLINT - GTEST_DEATH_TEST_CHECK_( - _MAX_PATH + 1 != ::GetModuleFileNameA(NULL, - executable_path, - _MAX_PATH)); - - String command_line = String::Format("%s %s \"%s\"", - ::GetCommandLineA(), - filter_flag.c_str(), - internal_flag.c_str()); - - DeathTest::set_last_death_test_message(""); - - CaptureStderr(); - // Flush the log buffers since the log streams are shared with the child. - FlushInfoLog(); - - // The child process will share the standard handles with the parent. - STARTUPINFOA startup_info; - memset(&startup_info, 0, sizeof(STARTUPINFO)); - startup_info.dwFlags = STARTF_USESTDHANDLES; - startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); - startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); - startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); - - PROCESS_INFORMATION process_info; - GTEST_DEATH_TEST_CHECK_(::CreateProcessA( - executable_path, - const_cast(command_line.c_str()), - NULL, // Retuned process handle is not inheritable. - NULL, // Retuned thread handle is not inheritable. - TRUE, // Child inherits all inheritable handles (for write_handle_). - 0x0, // Default creation flags. - NULL, // Inherit the parent's environment. - UnitTest::GetInstance()->original_working_dir(), - &startup_info, - &process_info) != FALSE); - child_handle_.Reset(process_info.hProcess); - ::CloseHandle(process_info.hThread); - set_spawned(true); - return OVERSEE_TEST; -} -#else // We are not on Windows. - -// ForkingDeathTest provides implementations for most of the abstract -// methods of the DeathTest interface. Only the AssumeRole method is -// left undefined. -class ForkingDeathTest : public DeathTestImpl { - public: - ForkingDeathTest(const char* statement, const RE* regex); - - // All of these virtual functions are inherited from DeathTest. - virtual int Wait(); - - protected: - void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; } - - private: - // PID of child process during death test; 0 in the child process itself. - pid_t child_pid_; -}; - -// Constructs a ForkingDeathTest. -ForkingDeathTest::ForkingDeathTest(const char* a_statement, const RE* a_regex) - : DeathTestImpl(a_statement, a_regex), - child_pid_(-1) {} - -// Waits for the child in a death test to exit, returning its exit -// status, or 0 if no child process exists. As a side effect, sets the -// outcome data member. -int ForkingDeathTest::Wait() { - if (!spawned()) - return 0; - - ReadAndInterpretStatusByte(); - - int status_value; - GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0)); - set_status(status_value); - return status_value; -} - -// A concrete death test class that forks, then immediately runs the test -// in the child process. -class NoExecDeathTest : public ForkingDeathTest { - public: - NoExecDeathTest(const char* a_statement, const RE* a_regex) : - ForkingDeathTest(a_statement, a_regex) { } - virtual TestRole AssumeRole(); -}; - -// The AssumeRole process for a fork-and-run death test. It implements a -// straightforward fork, with a simple pipe to transmit the status byte. -DeathTest::TestRole NoExecDeathTest::AssumeRole() { - const size_t thread_count = GetThreadCount(); - if (thread_count != 1) { - GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count); - } - - int pipe_fd[2]; - GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); - - DeathTest::set_last_death_test_message(""); - CaptureStderr(); - // When we fork the process below, the log file buffers are copied, but the - // file descriptors are shared. We flush all log files here so that closing - // the file descriptors in the child process doesn't throw off the - // synchronization between descriptors and buffers in the parent process. - // This is as close to the fork as possible to avoid a race condition in case - // there are multiple threads running before the death test, and another - // thread writes to the log file. - FlushInfoLog(); - - const pid_t child_pid = fork(); - GTEST_DEATH_TEST_CHECK_(child_pid != -1); - set_child_pid(child_pid); - if (child_pid == 0) { - GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0])); - set_write_fd(pipe_fd[1]); - // Redirects all logging to stderr in the child process to prevent - // concurrent writes to the log files. We capture stderr in the parent - // process and append the child process' output to a log. - LogToStderr(); - // Event forwarding to the listeners of event listener API mush be shut - // down in death test subprocesses. - GetUnitTestImpl()->listeners()->SuppressEventForwarding(); - return EXECUTE_TEST; - } else { - GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); - set_read_fd(pipe_fd[0]); - set_spawned(true); - return OVERSEE_TEST; - } -} - -// A concrete death test class that forks and re-executes the main -// program from the beginning, with command-line flags set that cause -// only this specific death test to be run. -class ExecDeathTest : public ForkingDeathTest { - public: - ExecDeathTest(const char* a_statement, const RE* a_regex, - const char* file, int line) : - ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { } - virtual TestRole AssumeRole(); - private: - // The name of the file in which the death test is located. - const char* const file_; - // The line number on which the death test is located. - const int line_; -}; - -// Utility class for accumulating command-line arguments. -class Arguments { - public: - Arguments() { - args_.push_back(NULL); - } - - ~Arguments() { - for (std::vector::iterator i = args_.begin(); i != args_.end(); - ++i) { - free(*i); - } - } - void AddArgument(const char* argument) { - args_.insert(args_.end() - 1, posix::StrDup(argument)); - } - - template - void AddArguments(const ::std::vector& arguments) { - for (typename ::std::vector::const_iterator i = arguments.begin(); - i != arguments.end(); - ++i) { - args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); - } - } - char* const* Argv() { - return &args_[0]; - } - private: - std::vector args_; -}; - -// A struct that encompasses the arguments to the child process of a -// threadsafe-style death test process. -struct ExecDeathTestArgs { - char* const* argv; // Command-line arguments for the child's call to exec - int close_fd; // File descriptor to close; the read end of a pipe -}; - -#if GTEST_OS_MAC -inline char** GetEnviron() { - // When Google Test is built as a framework on MacOS X, the environ variable - // is unavailable. Apple's documentation (man environ) recommends using - // _NSGetEnviron() instead. - return *_NSGetEnviron(); -} -#else -// Some POSIX platforms expect you to declare environ. extern "C" makes -// it reside in the global namespace. -extern "C" char** environ; -inline char** GetEnviron() { return environ; } -#endif // GTEST_OS_MAC - -// The main function for a threadsafe-style death test child process. -// This function is called in a clone()-ed process and thus must avoid -// any potentially unsafe operations like malloc or libc functions. -static int ExecDeathTestChildMain(void* child_arg) { - ExecDeathTestArgs* const args = static_cast(child_arg); - GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd)); - - // We need to execute the test program in the same environment where - // it was originally invoked. Therefore we change to the original - // working directory first. - const char* const original_dir = - UnitTest::GetInstance()->original_working_dir(); - // We can safely call chdir() as it's a direct system call. - if (chdir(original_dir) != 0) { - DeathTestAbort(String::Format("chdir(\"%s\") failed: %s", - original_dir, - GetLastErrnoDescription().c_str())); - return EXIT_FAILURE; - } - - // We can safely call execve() as it's a direct system call. We - // cannot use execvp() as it's a libc function and thus potentially - // unsafe. Since execve() doesn't search the PATH, the user must - // invoke the test program via a valid path that contains at least - // one path separator. - execve(args->argv[0], args->argv, GetEnviron()); - DeathTestAbort(String::Format("execve(%s, ...) in %s failed: %s", - args->argv[0], - original_dir, - GetLastErrnoDescription().c_str())); - return EXIT_FAILURE; -} - -// Two utility routines that together determine the direction the stack -// grows. -// This could be accomplished more elegantly by a single recursive -// function, but we want to guard against the unlikely possibility of -// a smart compiler optimizing the recursion away. -bool StackLowerThanAddress(const void* ptr) { - int dummy; - return &dummy < ptr; -} - -bool StackGrowsDown() { - int dummy; - return StackLowerThanAddress(&dummy); -} - -// A threadsafe implementation of fork(2) for threadsafe-style death tests -// that uses clone(2). It dies with an error message if anything goes -// wrong. -static pid_t ExecDeathTestFork(char* const* argv, int close_fd) { - ExecDeathTestArgs args = { argv, close_fd }; - pid_t child_pid = -1; - -#if GTEST_HAS_CLONE - const bool use_fork = GTEST_FLAG(death_test_use_fork); - - if (!use_fork) { - static const bool stack_grows_down = StackGrowsDown(); - const size_t stack_size = getpagesize(); - // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead. - void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, - MAP_ANON | MAP_PRIVATE, -1, 0); - GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED); - void* const stack_top = - static_cast(stack) + (stack_grows_down ? stack_size : 0); - - child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); - - GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); - } -#else - const bool use_fork = true; -#endif // GTEST_HAS_CLONE - - if (use_fork && (child_pid = fork()) == 0) { - ExecDeathTestChildMain(&args); - _exit(0); - } - - GTEST_DEATH_TEST_CHECK_(child_pid != -1); - return child_pid; -} - -// The AssumeRole process for a fork-and-exec death test. It re-executes the -// main program from the beginning, setting the --gtest_filter -// and --gtest_internal_run_death_test flags to cause only the current -// death test to be re-run. -DeathTest::TestRole ExecDeathTest::AssumeRole() { - const UnitTestImpl* const impl = GetUnitTestImpl(); - const InternalRunDeathTestFlag* const flag = - impl->internal_run_death_test_flag(); - const TestInfo* const info = impl->current_test_info(); - const int death_test_index = info->result()->death_test_count(); - - if (flag != NULL) { - set_write_fd(flag->write_fd()); - return EXECUTE_TEST; - } - - int pipe_fd[2]; - GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); - // Clear the close-on-exec flag on the write end of the pipe, lest - // it be closed when the child process does an exec: - GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1); - - const String filter_flag = - String::Format("--%s%s=%s.%s", - GTEST_FLAG_PREFIX_, kFilterFlag, - info->test_case_name(), info->name()); - const String internal_flag = - String::Format("--%s%s=%s|%d|%d|%d", - GTEST_FLAG_PREFIX_, kInternalRunDeathTestFlag, - file_, line_, death_test_index, pipe_fd[1]); - Arguments args; - args.AddArguments(GetArgvs()); - args.AddArgument(filter_flag.c_str()); - args.AddArgument(internal_flag.c_str()); - - DeathTest::set_last_death_test_message(""); - - CaptureStderr(); - // See the comment in NoExecDeathTest::AssumeRole for why the next line - // is necessary. - FlushInfoLog(); - - const pid_t child_pid = ExecDeathTestFork(args.Argv(), pipe_fd[0]); - GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); - set_child_pid(child_pid); - set_read_fd(pipe_fd[0]); - set_spawned(true); - return OVERSEE_TEST; -} - -#endif // !GTEST_OS_WINDOWS - -// Creates a concrete DeathTest-derived class that depends on the -// --gtest_death_test_style flag, and sets the pointer pointed to -// by the "test" argument to its address. If the test should be -// skipped, sets that pointer to NULL. Returns true, unless the -// flag is set to an invalid value. -bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, - const char* file, int line, - DeathTest** test) { - UnitTestImpl* const impl = GetUnitTestImpl(); - const InternalRunDeathTestFlag* const flag = - impl->internal_run_death_test_flag(); - const int death_test_index = impl->current_test_info() - ->increment_death_test_count(); - - if (flag != NULL) { - if (death_test_index > flag->index()) { - DeathTest::set_last_death_test_message(String::Format( - "Death test count (%d) somehow exceeded expected maximum (%d)", - death_test_index, flag->index())); - return false; - } - - if (!(flag->file() == file && flag->line() == line && - flag->index() == death_test_index)) { - *test = NULL; - return true; - } - } - -#if GTEST_OS_WINDOWS - if (GTEST_FLAG(death_test_style) == "threadsafe" || - GTEST_FLAG(death_test_style) == "fast") { - *test = new WindowsDeathTest(statement, regex, file, line); - } -#else - if (GTEST_FLAG(death_test_style) == "threadsafe") { - *test = new ExecDeathTest(statement, regex, file, line); - } else if (GTEST_FLAG(death_test_style) == "fast") { - *test = new NoExecDeathTest(statement, regex); - } -#endif // GTEST_OS_WINDOWS - else { // NOLINT - this is more readable than unbalanced brackets inside #if. - DeathTest::set_last_death_test_message(String::Format( - "Unknown death test style \"%s\" encountered", - GTEST_FLAG(death_test_style).c_str())); - return false; - } - - return true; -} - -// Splits a given string on a given delimiter, populating a given -// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have -// ::std::string, so we can use it here. -static void SplitString(const ::std::string& str, char delimiter, - ::std::vector< ::std::string>* dest) { - ::std::vector< ::std::string> parsed; - ::std::string::size_type pos = 0; - while (::testing::internal::AlwaysTrue()) { - const ::std::string::size_type colon = str.find(delimiter, pos); - if (colon == ::std::string::npos) { - parsed.push_back(str.substr(pos)); - break; - } else { - parsed.push_back(str.substr(pos, colon - pos)); - pos = colon + 1; - } - } - dest->swap(parsed); -} - -#if GTEST_OS_WINDOWS -// Recreates the pipe and event handles from the provided parameters, -// signals the event, and returns a file descriptor wrapped around the pipe -// handle. This function is called in the child process only. -int GetStatusFileDescriptor(unsigned int parent_process_id, - size_t write_handle_as_size_t, - size_t event_handle_as_size_t) { - AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE, - FALSE, // Non-inheritable. - parent_process_id)); - if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) { - DeathTestAbort(String::Format("Unable to open parent process %u", - parent_process_id)); - } - - // TODO(vladl@google.com): Replace the following check with a - // compile-time assertion when available. - GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t)); - - const HANDLE write_handle = - reinterpret_cast(write_handle_as_size_t); - HANDLE dup_write_handle; - - // The newly initialized handle is accessible only in in the parent - // process. To obtain one accessible within the child, we need to use - // DuplicateHandle. - if (!::DuplicateHandle(parent_process_handle.Get(), write_handle, - ::GetCurrentProcess(), &dup_write_handle, - 0x0, // Requested privileges ignored since - // DUPLICATE_SAME_ACCESS is used. - FALSE, // Request non-inheritable handler. - DUPLICATE_SAME_ACCESS)) { - DeathTestAbort(String::Format( - "Unable to duplicate the pipe handle %Iu from the parent process %u", - write_handle_as_size_t, parent_process_id)); - } - - const HANDLE event_handle = reinterpret_cast(event_handle_as_size_t); - HANDLE dup_event_handle; - - if (!::DuplicateHandle(parent_process_handle.Get(), event_handle, - ::GetCurrentProcess(), &dup_event_handle, - 0x0, - FALSE, - DUPLICATE_SAME_ACCESS)) { - DeathTestAbort(String::Format( - "Unable to duplicate the event handle %Iu from the parent process %u", - event_handle_as_size_t, parent_process_id)); - } - - const int write_fd = - ::_open_osfhandle(reinterpret_cast(dup_write_handle), O_APPEND); - if (write_fd == -1) { - DeathTestAbort(String::Format( - "Unable to convert pipe handle %Iu to a file descriptor", - write_handle_as_size_t)); - } - - // Signals the parent that the write end of the pipe has been acquired - // so the parent can release its own write end. - ::SetEvent(dup_event_handle); - - return write_fd; -} -#endif // GTEST_OS_WINDOWS - -// Returns a newly created InternalRunDeathTestFlag object with fields -// initialized from the GTEST_FLAG(internal_run_death_test) flag if -// the flag is specified; otherwise returns NULL. -InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { - if (GTEST_FLAG(internal_run_death_test) == "") return NULL; - - // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we - // can use it here. - int line = -1; - int index = -1; - ::std::vector< ::std::string> fields; - SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields); - int write_fd = -1; - -#if GTEST_OS_WINDOWS - unsigned int parent_process_id = 0; - size_t write_handle_as_size_t = 0; - size_t event_handle_as_size_t = 0; - - if (fields.size() != 6 - || !ParseNaturalNumber(fields[1], &line) - || !ParseNaturalNumber(fields[2], &index) - || !ParseNaturalNumber(fields[3], &parent_process_id) - || !ParseNaturalNumber(fields[4], &write_handle_as_size_t) - || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) { - DeathTestAbort(String::Format( - "Bad --gtest_internal_run_death_test flag: %s", - GTEST_FLAG(internal_run_death_test).c_str())); - } - write_fd = GetStatusFileDescriptor(parent_process_id, - write_handle_as_size_t, - event_handle_as_size_t); -#else - if (fields.size() != 4 - || !ParseNaturalNumber(fields[1], &line) - || !ParseNaturalNumber(fields[2], &index) - || !ParseNaturalNumber(fields[3], &write_fd)) { - DeathTestAbort(String::Format( - "Bad --gtest_internal_run_death_test flag: %s", - GTEST_FLAG(internal_run_death_test).c_str())); - } -#endif // GTEST_OS_WINDOWS - return new InternalRunDeathTestFlag(fields[0], line, index, write_fd); -} - -} // namespace internal - -#endif // GTEST_HAS_DEATH_TEST - -} // namespace testing -// Copyright 2008, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Authors: keith.ray@gmail.com (Keith Ray) - - -#include - -#if GTEST_OS_WINDOWS_MOBILE -#include -#elif GTEST_OS_WINDOWS -#include -#include -#elif GTEST_OS_SYMBIAN -// Symbian OpenC has PATH_MAX in sys/syslimits.h -#include -#else -#include -#include // Some Linux distributions define PATH_MAX here. -#endif // GTEST_OS_WINDOWS_MOBILE - -#if GTEST_OS_WINDOWS -#define GTEST_PATH_MAX_ _MAX_PATH -#elif defined(PATH_MAX) -#define GTEST_PATH_MAX_ PATH_MAX -#elif defined(_XOPEN_PATH_MAX) -#define GTEST_PATH_MAX_ _XOPEN_PATH_MAX -#else -#define GTEST_PATH_MAX_ _POSIX_PATH_MAX -#endif // GTEST_OS_WINDOWS - - -namespace testing { -namespace internal { - -#if GTEST_OS_WINDOWS -// On Windows, '\\' is the standard path separator, but many tools and the -// Windows API also accept '/' as an alternate path separator. Unless otherwise -// noted, a file path can contain either kind of path separators, or a mixture -// of them. -const char kPathSeparator = '\\'; -const char kAlternatePathSeparator = '/'; -const char kPathSeparatorString[] = "\\"; -const char kAlternatePathSeparatorString[] = "/"; -#if GTEST_OS_WINDOWS_MOBILE -// Windows CE doesn't have a current directory. You should not use -// the current directory in tests on Windows CE, but this at least -// provides a reasonable fallback. -const char kCurrentDirectoryString[] = "\\"; -// Windows CE doesn't define INVALID_FILE_ATTRIBUTES -const DWORD kInvalidFileAttributes = 0xffffffff; -#else -const char kCurrentDirectoryString[] = ".\\"; -#endif // GTEST_OS_WINDOWS_MOBILE -#else -const char kPathSeparator = '/'; -const char kPathSeparatorString[] = "/"; -const char kCurrentDirectoryString[] = "./"; -#endif // GTEST_OS_WINDOWS - -// Returns whether the given character is a valid path separator. -static bool IsPathSeparator(char c) { -#if GTEST_HAS_ALT_PATH_SEP_ - return (c == kPathSeparator) || (c == kAlternatePathSeparator); -#else - return c == kPathSeparator; -#endif -} - -// Returns the current working directory, or "" if unsuccessful. -FilePath FilePath::GetCurrentDir() { -#if GTEST_OS_WINDOWS_MOBILE - // Windows CE doesn't have a current directory, so we just return - // something reasonable. - return FilePath(kCurrentDirectoryString); -#elif GTEST_OS_WINDOWS - char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; - return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); -#else - char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; - return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); -#endif // GTEST_OS_WINDOWS_MOBILE -} - -// Returns a copy of the FilePath with the case-insensitive extension removed. -// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns -// FilePath("dir/file"). If a case-insensitive extension is not -// found, returns a copy of the original FilePath. -FilePath FilePath::RemoveExtension(const char* extension) const { - String dot_extension(String::Format(".%s", extension)); - if (pathname_.EndsWithCaseInsensitive(dot_extension.c_str())) { - return FilePath(String(pathname_.c_str(), pathname_.length() - 4)); - } - return *this; -} - -// Returns a pointer to the last occurence of a valid path separator in -// the FilePath. On Windows, for example, both '/' and '\' are valid path -// separators. Returns NULL if no path separator was found. -const char* FilePath::FindLastPathSeparator() const { - const char* const last_sep = strrchr(c_str(), kPathSeparator); -#if GTEST_HAS_ALT_PATH_SEP_ - const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator); - // Comparing two pointers of which only one is NULL is undefined. - if (last_alt_sep != NULL && - (last_sep == NULL || last_alt_sep > last_sep)) { - return last_alt_sep; - } -#endif - return last_sep; -} - -// Returns a copy of the FilePath with the directory part removed. -// Example: FilePath("path/to/file").RemoveDirectoryName() returns -// FilePath("file"). If there is no directory part ("just_a_file"), it returns -// the FilePath unmodified. If there is no file part ("just_a_dir/") it -// returns an empty FilePath (""). -// On Windows platform, '\' is the path separator, otherwise it is '/'. -FilePath FilePath::RemoveDirectoryName() const { - const char* const last_sep = FindLastPathSeparator(); - return last_sep ? FilePath(String(last_sep + 1)) : *this; -} - -// RemoveFileName returns the directory path with the filename removed. -// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". -// If the FilePath is "a_file" or "/a_file", RemoveFileName returns -// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does -// not have a file, like "just/a/dir/", it returns the FilePath unmodified. -// On Windows platform, '\' is the path separator, otherwise it is '/'. -FilePath FilePath::RemoveFileName() const { - const char* const last_sep = FindLastPathSeparator(); - String dir; - if (last_sep) { - dir = String(c_str(), last_sep + 1 - c_str()); - } else { - dir = kCurrentDirectoryString; - } - return FilePath(dir); -} - -// Helper functions for naming files in a directory for xml output. - -// Given directory = "dir", base_name = "test", number = 0, -// extension = "xml", returns "dir/test.xml". If number is greater -// than zero (e.g., 12), returns "dir/test_12.xml". -// On Windows platform, uses \ as the separator rather than /. -FilePath FilePath::MakeFileName(const FilePath& directory, - const FilePath& base_name, - int number, - const char* extension) { - String file; - if (number == 0) { - file = String::Format("%s.%s", base_name.c_str(), extension); - } else { - file = String::Format("%s_%d.%s", base_name.c_str(), number, extension); - } - return ConcatPaths(directory, FilePath(file)); -} - -// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml". -// On Windows, uses \ as the separator rather than /. -FilePath FilePath::ConcatPaths(const FilePath& directory, - const FilePath& relative_path) { - if (directory.IsEmpty()) - return relative_path; - const FilePath dir(directory.RemoveTrailingPathSeparator()); - return FilePath(String::Format("%s%c%s", dir.c_str(), kPathSeparator, - relative_path.c_str())); -} - -// Returns true if pathname describes something findable in the file-system, -// either a file, directory, or whatever. -bool FilePath::FileOrDirectoryExists() const { -#if GTEST_OS_WINDOWS_MOBILE - LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str()); - const DWORD attributes = GetFileAttributes(unicode); - delete [] unicode; - return attributes != kInvalidFileAttributes; -#else - posix::StatStruct file_stat; - return posix::Stat(pathname_.c_str(), &file_stat) == 0; -#endif // GTEST_OS_WINDOWS_MOBILE -} - -// Returns true if pathname describes a directory in the file-system -// that exists. -bool FilePath::DirectoryExists() const { - bool result = false; -#if GTEST_OS_WINDOWS - // Don't strip off trailing separator if path is a root directory on - // Windows (like "C:\\"). - const FilePath& path(IsRootDirectory() ? *this : - RemoveTrailingPathSeparator()); -#else - const FilePath& path(*this); -#endif - -#if GTEST_OS_WINDOWS_MOBILE - LPCWSTR unicode = String::AnsiToUtf16(path.c_str()); - const DWORD attributes = GetFileAttributes(unicode); - delete [] unicode; - if ((attributes != kInvalidFileAttributes) && - (attributes & FILE_ATTRIBUTE_DIRECTORY)) { - result = true; - } -#else - posix::StatStruct file_stat; - result = posix::Stat(path.c_str(), &file_stat) == 0 && - posix::IsDir(file_stat); -#endif // GTEST_OS_WINDOWS_MOBILE - - return result; -} - -// Returns true if pathname describes a root directory. (Windows has one -// root directory per disk drive.) -bool FilePath::IsRootDirectory() const { -#if GTEST_OS_WINDOWS - // TODO(wan@google.com): on Windows a network share like - // \\server\share can be a root directory, although it cannot be the - // current directory. Handle this properly. - return pathname_.length() == 3 && IsAbsolutePath(); -#else - return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]); -#endif -} - -// Returns true if pathname describes an absolute path. -bool FilePath::IsAbsolutePath() const { - const char* const name = pathname_.c_str(); -#if GTEST_OS_WINDOWS - return pathname_.length() >= 3 && - ((name[0] >= 'a' && name[0] <= 'z') || - (name[0] >= 'A' && name[0] <= 'Z')) && - name[1] == ':' && - IsPathSeparator(name[2]); -#else - return IsPathSeparator(name[0]); -#endif -} - -// Returns a pathname for a file that does not currently exist. The pathname -// will be directory/base_name.extension or -// directory/base_name_.extension if directory/base_name.extension -// already exists. The number will be incremented until a pathname is found -// that does not already exist. -// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. -// There could be a race condition if two or more processes are calling this -// function at the same time -- they could both pick the same filename. -FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, - const FilePath& base_name, - const char* extension) { - FilePath full_pathname; - int number = 0; - do { - full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); - } while (full_pathname.FileOrDirectoryExists()); - return full_pathname; -} - -// Returns true if FilePath ends with a path separator, which indicates that -// it is intended to represent a directory. Returns false otherwise. -// This does NOT check that a directory (or file) actually exists. -bool FilePath::IsDirectory() const { - return !pathname_.empty() && - IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]); -} - -// Create directories so that path exists. Returns true if successful or if -// the directories already exist; returns false if unable to create directories -// for any reason. -bool FilePath::CreateDirectoriesRecursively() const { - if (!this->IsDirectory()) { - return false; - } - - if (pathname_.length() == 0 || this->DirectoryExists()) { - return true; - } - - const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName()); - return parent.CreateDirectoriesRecursively() && this->CreateFolder(); -} - -// Create the directory so that path exists. Returns true if successful or -// if the directory already exists; returns false if unable to create the -// directory for any reason, including if the parent directory does not -// exist. Not named "CreateDirectory" because that's a macro on Windows. -bool FilePath::CreateFolder() const { -#if GTEST_OS_WINDOWS_MOBILE - FilePath removed_sep(this->RemoveTrailingPathSeparator()); - LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str()); - int result = CreateDirectory(unicode, NULL) ? 0 : -1; - delete [] unicode; -#elif GTEST_OS_WINDOWS - int result = _mkdir(pathname_.c_str()); -#else - int result = mkdir(pathname_.c_str(), 0777); -#endif // GTEST_OS_WINDOWS_MOBILE - - if (result == -1) { - return this->DirectoryExists(); // An error is OK if the directory exists. - } - return true; // No error. -} - -// If input name has a trailing separator character, remove it and return the -// name, otherwise return the name string unmodified. -// On Windows platform, uses \ as the separator, other platforms use /. -FilePath FilePath::RemoveTrailingPathSeparator() const { - return IsDirectory() - ? FilePath(String(pathname_.c_str(), pathname_.length() - 1)) - : *this; -} - -// Removes any redundant separators that might be in the pathname. -// For example, "bar///foo" becomes "bar/foo". Does not eliminate other -// redundancies that might be in a pathname involving "." or "..". -// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share). -void FilePath::Normalize() { - if (pathname_.c_str() == NULL) { - pathname_ = ""; - return; - } - const char* src = pathname_.c_str(); - char* const dest = new char[pathname_.length() + 1]; - char* dest_ptr = dest; - memset(dest_ptr, 0, pathname_.length() + 1); - - while (*src != '\0') { - *dest_ptr = *src; - if (!IsPathSeparator(*src)) { - src++; - } else { -#if GTEST_HAS_ALT_PATH_SEP_ - if (*dest_ptr == kAlternatePathSeparator) { - *dest_ptr = kPathSeparator; - } -#endif - while (IsPathSeparator(*src)) - src++; - } - dest_ptr++; - } - *dest_ptr = '\0'; - pathname_ = dest; - delete[] dest; -} - -} // namespace internal -} // namespace testing -// Copyright 2008, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: wan@google.com (Zhanyong Wan) - - -#include -#include -#include - -#if GTEST_OS_WINDOWS_MOBILE -#include // For TerminateProcess() -#elif GTEST_OS_WINDOWS -#include -#include -#else -#include -#endif // GTEST_OS_WINDOWS_MOBILE - -#if GTEST_OS_MAC -#include -#include -#include -#endif // GTEST_OS_MAC - - -// Indicates that this translation unit is part of Google Test's -// implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick is to -// prevent a user from accidentally including gtest-internal-inl.h in -// his code. -#define GTEST_IMPLEMENTATION_ 1 -#undef GTEST_IMPLEMENTATION_ - -namespace testing { -namespace internal { - -#if defined(_MSC_VER) || defined(__BORLANDC__) -// MSVC and C++Builder do not provide a definition of STDERR_FILENO. -const int kStdOutFileno = 1; -const int kStdErrFileno = 2; -#else -const int kStdOutFileno = STDOUT_FILENO; -const int kStdErrFileno = STDERR_FILENO; -#endif // _MSC_VER - -#if GTEST_OS_MAC - -// Returns the number of threads running in the process, or 0 to indicate that -// we cannot detect it. -size_t GetThreadCount() { - const task_t task = mach_task_self(); - mach_msg_type_number_t thread_count; - thread_act_array_t thread_list; - const kern_return_t status = task_threads(task, &thread_list, &thread_count); - if (status == KERN_SUCCESS) { - // task_threads allocates resources in thread_list and we need to free them - // to avoid leaks. - vm_deallocate(task, - reinterpret_cast(thread_list), - sizeof(thread_t) * thread_count); - return static_cast(thread_count); - } else { - return 0; - } -} - -#else - -size_t GetThreadCount() { - // There's no portable way to detect the number of threads, so we just - // return 0 to indicate that we cannot detect it. - return 0; -} - -#endif // GTEST_OS_MAC - -#if GTEST_USES_POSIX_RE - -// Implements RE. Currently only needed for death tests. - -RE::~RE() { - if (is_valid_) { - // regfree'ing an invalid regex might crash because the content - // of the regex is undefined. Since the regex's are essentially - // the same, one cannot be valid (or invalid) without the other - // being so too. - regfree(&partial_regex_); - regfree(&full_regex_); - } - free(const_cast(pattern_)); -} - -// Returns true iff regular expression re matches the entire str. -bool RE::FullMatch(const char* str, const RE& re) { - if (!re.is_valid_) return false; - - regmatch_t match; - return regexec(&re.full_regex_, str, 1, &match, 0) == 0; -} - -// Returns true iff regular expression re matches a substring of str -// (including str itself). -bool RE::PartialMatch(const char* str, const RE& re) { - if (!re.is_valid_) return false; - - regmatch_t match; - return regexec(&re.partial_regex_, str, 1, &match, 0) == 0; -} - -// Initializes an RE from its string representation. -void RE::Init(const char* regex) { - pattern_ = posix::StrDup(regex); - - // Reserves enough bytes to hold the regular expression used for a - // full match. - const size_t full_regex_len = strlen(regex) + 10; - char* const full_pattern = new char[full_regex_len]; - - snprintf(full_pattern, full_regex_len, "^(%s)$", regex); - is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0; - // We want to call regcomp(&partial_regex_, ...) even if the - // previous expression returns false. Otherwise partial_regex_ may - // not be properly initialized can may cause trouble when it's - // freed. - // - // Some implementation of POSIX regex (e.g. on at least some - // versions of Cygwin) doesn't accept the empty string as a valid - // regex. We change it to an equivalent form "()" to be safe. - if (is_valid_) { - const char* const partial_regex = (*regex == '\0') ? "()" : regex; - is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0; - } - EXPECT_TRUE(is_valid_) - << "Regular expression \"" << regex - << "\" is not a valid POSIX Extended regular expression."; - - delete[] full_pattern; -} - -#elif GTEST_USES_SIMPLE_RE - -// Returns true iff ch appears anywhere in str (excluding the -// terminating '\0' character). -bool IsInSet(char ch, const char* str) { - return ch != '\0' && strchr(str, ch) != NULL; -} - -// Returns true iff ch belongs to the given classification. Unlike -// similar functions in , these aren't affected by the -// current locale. -bool IsDigit(char ch) { return '0' <= ch && ch <= '9'; } -bool IsPunct(char ch) { - return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"); -} -bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); } -bool IsWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); } -bool IsWordChar(char ch) { - return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || - ('0' <= ch && ch <= '9') || ch == '_'; -} - -// Returns true iff "\\c" is a supported escape sequence. -bool IsValidEscape(char c) { - return (IsPunct(c) || IsInSet(c, "dDfnrsStvwW")); -} - -// Returns true iff the given atom (specified by escaped and pattern) -// matches ch. The result is undefined if the atom is invalid. -bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { - if (escaped) { // "\\p" where p is pattern_char. - switch (pattern_char) { - case 'd': return IsDigit(ch); - case 'D': return !IsDigit(ch); - case 'f': return ch == '\f'; - case 'n': return ch == '\n'; - case 'r': return ch == '\r'; - case 's': return IsWhiteSpace(ch); - case 'S': return !IsWhiteSpace(ch); - case 't': return ch == '\t'; - case 'v': return ch == '\v'; - case 'w': return IsWordChar(ch); - case 'W': return !IsWordChar(ch); - } - return IsPunct(pattern_char) && pattern_char == ch; - } - - return (pattern_char == '.' && ch != '\n') || pattern_char == ch; -} - -// Helper function used by ValidateRegex() to format error messages. -String FormatRegexSyntaxError(const char* regex, int index) { - return (Message() << "Syntax error at index " << index - << " in simple regular expression \"" << regex << "\": ").GetString(); -} - -// Generates non-fatal failures and returns false if regex is invalid; -// otherwise returns true. -bool ValidateRegex(const char* regex) { - if (regex == NULL) { - // TODO(wan@google.com): fix the source file location in the - // assertion failures to match where the regex is used in user - // code. - ADD_FAILURE() << "NULL is not a valid simple regular expression."; - return false; - } - - bool is_valid = true; - - // True iff ?, *, or + can follow the previous atom. - bool prev_repeatable = false; - for (int i = 0; regex[i]; i++) { - if (regex[i] == '\\') { // An escape sequence - i++; - if (regex[i] == '\0') { - ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) - << "'\\' cannot appear at the end."; - return false; - } - - if (!IsValidEscape(regex[i])) { - ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) - << "invalid escape sequence \"\\" << regex[i] << "\"."; - is_valid = false; - } - prev_repeatable = true; - } else { // Not an escape sequence. - const char ch = regex[i]; - - if (ch == '^' && i > 0) { - ADD_FAILURE() << FormatRegexSyntaxError(regex, i) - << "'^' can only appear at the beginning."; - is_valid = false; - } else if (ch == '$' && regex[i + 1] != '\0') { - ADD_FAILURE() << FormatRegexSyntaxError(regex, i) - << "'$' can only appear at the end."; - is_valid = false; - } else if (IsInSet(ch, "()[]{}|")) { - ADD_FAILURE() << FormatRegexSyntaxError(regex, i) - << "'" << ch << "' is unsupported."; - is_valid = false; - } else if (IsRepeat(ch) && !prev_repeatable) { - ADD_FAILURE() << FormatRegexSyntaxError(regex, i) - << "'" << ch << "' can only follow a repeatable token."; - is_valid = false; - } - - prev_repeatable = !IsInSet(ch, "^$?*+"); - } - } - - return is_valid; -} - -// Matches a repeated regex atom followed by a valid simple regular -// expression. The regex atom is defined as c if escaped is false, -// or \c otherwise. repeat is the repetition meta character (?, *, -// or +). The behavior is undefined if str contains too many -// characters to be indexable by size_t, in which case the test will -// probably time out anyway. We are fine with this limitation as -// std::string has it too. -bool MatchRepetitionAndRegexAtHead( - bool escaped, char c, char repeat, const char* regex, - const char* str) { - const size_t min_count = (repeat == '+') ? 1 : 0; - const size_t max_count = (repeat == '?') ? 1 : - static_cast(-1) - 1; - // We cannot call numeric_limits::max() as it conflicts with the - // max() macro on Windows. - - for (size_t i = 0; i <= max_count; ++i) { - // We know that the atom matches each of the first i characters in str. - if (i >= min_count && MatchRegexAtHead(regex, str + i)) { - // We have enough matches at the head, and the tail matches too. - // Since we only care about *whether* the pattern matches str - // (as opposed to *how* it matches), there is no need to find a - // greedy match. - return true; - } - if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i])) - return false; - } - return false; -} - -// Returns true iff regex matches a prefix of str. regex must be a -// valid simple regular expression and not start with "^", or the -// result is undefined. -bool MatchRegexAtHead(const char* regex, const char* str) { - if (*regex == '\0') // An empty regex matches a prefix of anything. - return true; - - // "$" only matches the end of a string. Note that regex being - // valid guarantees that there's nothing after "$" in it. - if (*regex == '$') - return *str == '\0'; - - // Is the first thing in regex an escape sequence? - const bool escaped = *regex == '\\'; - if (escaped) - ++regex; - if (IsRepeat(regex[1])) { - // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so - // here's an indirect recursion. It terminates as the regex gets - // shorter in each recursion. - return MatchRepetitionAndRegexAtHead( - escaped, regex[0], regex[1], regex + 2, str); - } else { - // regex isn't empty, isn't "$", and doesn't start with a - // repetition. We match the first atom of regex with the first - // character of str and recurse. - return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) && - MatchRegexAtHead(regex + 1, str + 1); - } -} - -// Returns true iff regex matches any substring of str. regex must be -// a valid simple regular expression, or the result is undefined. -// -// The algorithm is recursive, but the recursion depth doesn't exceed -// the regex length, so we won't need to worry about running out of -// stack space normally. In rare cases the time complexity can be -// exponential with respect to the regex length + the string length, -// but usually it's must faster (often close to linear). -bool MatchRegexAnywhere(const char* regex, const char* str) { - if (regex == NULL || str == NULL) - return false; - - if (*regex == '^') - return MatchRegexAtHead(regex + 1, str); - - // A successful match can be anywhere in str. - do { - if (MatchRegexAtHead(regex, str)) - return true; - } while (*str++ != '\0'); - return false; -} - -// Implements the RE class. - -RE::~RE() { - free(const_cast(pattern_)); - free(const_cast(full_pattern_)); -} - -// Returns true iff regular expression re matches the entire str. -bool RE::FullMatch(const char* str, const RE& re) { - return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str); -} - -// Returns true iff regular expression re matches a substring of str -// (including str itself). -bool RE::PartialMatch(const char* str, const RE& re) { - return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str); -} - -// Initializes an RE from its string representation. -void RE::Init(const char* regex) { - pattern_ = full_pattern_ = NULL; - if (regex != NULL) { - pattern_ = posix::StrDup(regex); - } - - is_valid_ = ValidateRegex(regex); - if (!is_valid_) { - // No need to calculate the full pattern when the regex is invalid. - return; - } - - const size_t len = strlen(regex); - // Reserves enough bytes to hold the regular expression used for a - // full match: we need space to prepend a '^', append a '$', and - // terminate the string with '\0'. - char* buffer = static_cast(malloc(len + 3)); - full_pattern_ = buffer; - - if (*regex != '^') - *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'. - - // We don't use snprintf or strncpy, as they trigger a warning when - // compiled with VC++ 8.0. - memcpy(buffer, regex, len); - buffer += len; - - if (len == 0 || regex[len - 1] != '$') - *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'. - - *buffer = '\0'; -} - -#endif // GTEST_USES_POSIX_RE - - -GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line) - : severity_(severity) { - const char* const marker = - severity == GTEST_INFO ? "[ INFO ]" : - severity == GTEST_WARNING ? "[WARNING]" : - severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]"; - GetStream() << ::std::endl << marker << " " - << FormatFileLocation(file, line).c_str() << ": "; -} - -// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. -GTestLog::~GTestLog() { - GetStream() << ::std::endl; - if (severity_ == GTEST_FATAL) { - fflush(stderr); - posix::Abort(); - } -} -// Disable Microsoft deprecation warnings for POSIX functions called from -// this class (creat, dup, dup2, and close) -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4996) -#endif // _MSC_VER - -#if GTEST_HAS_STREAM_REDIRECTION_ - -// Object that captures an output stream (stdout/stderr). -class CapturedStream { - public: - // The ctor redirects the stream to a temporary file. - CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { -#if GTEST_OS_WINDOWS - char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT - char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT - - ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path); - const UINT success = ::GetTempFileNameA(temp_dir_path, - "gtest_redir", - 0, // Generate unique file name. - temp_file_path); - GTEST_CHECK_(success != 0) - << "Unable to create a temporary file in " << temp_dir_path; - const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE); - GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file " - << temp_file_path; - filename_ = temp_file_path; -#else - // There's no guarantee that a test has write access to the - // current directory, so we create the temporary file in the /tmp - // directory instead. - char name_template[] = "/tmp/captured_stream.XXXXXX"; - const int captured_fd = mkstemp(name_template); - filename_ = name_template; -#endif // GTEST_OS_WINDOWS - fflush(NULL); - dup2(captured_fd, fd_); - close(captured_fd); - } - - ~CapturedStream() { - remove(filename_.c_str()); - } - - String GetCapturedString() { - if (uncaptured_fd_ != -1) { - // Restores the original stream. - fflush(NULL); - dup2(uncaptured_fd_, fd_); - close(uncaptured_fd_); - uncaptured_fd_ = -1; - } - - FILE* const file = posix::FOpen(filename_.c_str(), "r"); - const String content = ReadEntireFile(file); - posix::FClose(file); - return content; - } - - private: - // Reads the entire content of a file as a String. - static String ReadEntireFile(FILE* file); - - // Returns the size (in bytes) of a file. - static size_t GetFileSize(FILE* file); - - const int fd_; // A stream to capture. - int uncaptured_fd_; - // Name of the temporary file holding the stderr output. - ::std::string filename_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream); -}; - -// Returns the size (in bytes) of a file. -size_t CapturedStream::GetFileSize(FILE* file) { - fseek(file, 0, SEEK_END); - return static_cast(ftell(file)); -} - -// Reads the entire content of a file as a string. -String CapturedStream::ReadEntireFile(FILE* file) { - const size_t file_size = GetFileSize(file); - char* const buffer = new char[file_size]; - - size_t bytes_last_read = 0; // # of bytes read in the last fread() - size_t bytes_read = 0; // # of bytes read so far - - fseek(file, 0, SEEK_SET); - - // Keeps reading the file until we cannot read further or the - // pre-determined file size is reached. - do { - bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); - bytes_read += bytes_last_read; - } while (bytes_last_read > 0 && bytes_read < file_size); - - const String content(buffer, bytes_read); - delete[] buffer; - - return content; -} - -#ifdef _MSC_VER -#pragma warning(pop) -#endif // _MSC_VER - -static CapturedStream* g_captured_stderr = NULL; -static CapturedStream* g_captured_stdout = NULL; - -// Starts capturing an output stream (stdout/stderr). -void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { - if (*stream != NULL) { - GTEST_LOG_(FATAL) << "Only one " << stream_name - << " capturer can exist at a time."; - } - *stream = new CapturedStream(fd); -} - -// Stops capturing the output stream and returns the captured string. -String GetCapturedStream(CapturedStream** captured_stream) { - const String content = (*captured_stream)->GetCapturedString(); - - delete *captured_stream; - *captured_stream = NULL; - - return content; -} - -// Starts capturing stdout. -void CaptureStdout() { - CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout); -} - -// Starts capturing stderr. -void CaptureStderr() { - CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr); -} - -// Stops capturing stdout and returns the captured string. -String GetCapturedStdout() { return GetCapturedStream(&g_captured_stdout); } - -// Stops capturing stderr and returns the captured string. -String GetCapturedStderr() { return GetCapturedStream(&g_captured_stderr); } - -#endif // GTEST_HAS_STREAM_REDIRECTION_ - -#if GTEST_HAS_DEATH_TEST - -// A copy of all command line arguments. Set by InitGoogleTest(). -::std::vector g_argvs; - -// Returns the command line as a vector of strings. -const ::std::vector& GetArgvs() { return g_argvs; } - -#endif // GTEST_HAS_DEATH_TEST - -#if GTEST_OS_WINDOWS_MOBILE -namespace posix { -void Abort() { - DebugBreak(); - TerminateProcess(GetCurrentProcess(), 1); -} -} // namespace posix -#endif // GTEST_OS_WINDOWS_MOBILE - -// Returns the name of the environment variable corresponding to the -// given flag. For example, FlagToEnvVar("foo") will return -// "GTEST_FOO" in the open-source version. -static String FlagToEnvVar(const char* flag) { - const String full_flag = - (Message() << GTEST_FLAG_PREFIX_ << flag).GetString(); - - Message env_var; - for (size_t i = 0; i != full_flag.length(); i++) { - env_var << static_cast(toupper(full_flag.c_str()[i])); - } - - return env_var.GetString(); -} - -// Parses 'str' for a 32-bit signed integer. If successful, writes -// the result to *value and returns true; otherwise leaves *value -// unchanged and returns false. -bool ParseInt32(const Message& src_text, const char* str, Int32* value) { - // Parses the environment variable as a decimal integer. - char* end = NULL; - const long long_value = strtol(str, &end, 10); // NOLINT - - // Has strtol() consumed all characters in the string? - if (*end != '\0') { - // No - an invalid character was encountered. - Message msg; - msg << "WARNING: " << src_text - << " is expected to be a 32-bit integer, but actually" - << " has value \"" << str << "\".\n"; - printf("%s", msg.GetString().c_str()); - fflush(stdout); - return false; - } - - // Is the parsed value in the range of an Int32? - const Int32 result = static_cast(long_value); - if (long_value == LONG_MAX || long_value == LONG_MIN || - // The parsed value overflows as a long. (strtol() returns - // LONG_MAX or LONG_MIN when the input overflows.) - result != long_value - // The parsed value overflows as an Int32. - ) { - Message msg; - msg << "WARNING: " << src_text - << " is expected to be a 32-bit integer, but actually" - << " has value " << str << ", which overflows.\n"; - printf("%s", msg.GetString().c_str()); - fflush(stdout); - return false; - } - - *value = result; - return true; -} - -// Reads and returns the Boolean environment variable corresponding to -// the given flag; if it's not set, returns default_value. -// -// The value is considered true iff it's not "0". -bool BoolFromGTestEnv(const char* flag, bool default_value) { - const String env_var = FlagToEnvVar(flag); - const char* const string_value = posix::GetEnv(env_var.c_str()); - return string_value == NULL ? - default_value : strcmp(string_value, "0") != 0; -} - -// Reads and returns a 32-bit integer stored in the environment -// variable corresponding to the given flag; if it isn't set or -// doesn't represent a valid 32-bit integer, returns default_value. -Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { - const String env_var = FlagToEnvVar(flag); - const char* const string_value = posix::GetEnv(env_var.c_str()); - if (string_value == NULL) { - // The environment variable is not set. - return default_value; - } - - Int32 result = default_value; - if (!ParseInt32(Message() << "Environment variable " << env_var, - string_value, &result)) { - printf("The default value %s is used.\n", - (Message() << default_value).GetString().c_str()); - fflush(stdout); - return default_value; - } - - return result; -} - -// Reads and returns the string environment variable corresponding to -// the given flag; if it's not set, returns default_value. -const char* StringFromGTestEnv(const char* flag, const char* default_value) { - const String env_var = FlagToEnvVar(flag); - const char* const value = posix::GetEnv(env_var.c_str()); - return value == NULL ? default_value : value; -} - -} // namespace internal -} // namespace testing -// Copyright 2008, Google Inc. -// All rights reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: mheule@google.com (Markus Heule) -// -// The Google C++ Testing Framework (Google Test) - - -// Indicates that this translation unit is part of Google Test's -// implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick is to -// prevent a user from accidentally including gtest-internal-inl.h in -// his code. -#define GTEST_IMPLEMENTATION_ 1 -#undef GTEST_IMPLEMENTATION_ - -namespace testing { - -using internal::GetUnitTestImpl; - -// Gets the summary of the failure message by omitting the stack trace -// in it. -internal::String TestPartResult::ExtractSummary(const char* message) { - const char* const stack_trace = strstr(message, internal::kStackTraceMarker); - return stack_trace == NULL ? internal::String(message) : - internal::String(message, stack_trace - message); -} - -// Prints a TestPartResult object. -std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { - return os - << result.file_name() << ":" << result.line_number() << ": " - << (result.type() == TestPartResult::kSuccess ? "Success" : - result.type() == TestPartResult::kFatalFailure ? "Fatal failure" : - "Non-fatal failure") << ":\n" - << result.message() << std::endl; -} - -// Appends a TestPartResult to the array. -void TestPartResultArray::Append(const TestPartResult& result) { - array_.push_back(result); -} - -// Returns the TestPartResult at the given index (0-based). -const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { - if (index < 0 || index >= size()) { - printf("\nInvalid index (%d) into TestPartResultArray.\n", index); - internal::posix::Abort(); - } - - return array_[index]; -} - -// Returns the number of TestPartResult objects in the array. -int TestPartResultArray::size() const { - return static_cast(array_.size()); -} - -namespace internal { - -HasNewFatalFailureHelper::HasNewFatalFailureHelper() - : has_new_fatal_failure_(false), - original_reporter_(GetUnitTestImpl()-> - GetTestPartResultReporterForCurrentThread()) { - GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this); -} - -HasNewFatalFailureHelper::~HasNewFatalFailureHelper() { - GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread( - original_reporter_); -} - -void HasNewFatalFailureHelper::ReportTestPartResult( - const TestPartResult& result) { - if (result.fatally_failed()) - has_new_fatal_failure_ = true; - original_reporter_->ReportTestPartResult(result); -} - -} // namespace internal - -} // namespace testing -// Copyright 2008 Google Inc. -// All Rights Reserved. -// -// 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 name of Google Inc. nor the names of its -// 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 THE COPYRIGHT -// OWNER 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. -// -// Author: wan@google.com (Zhanyong Wan) - - -namespace testing { -namespace internal { - -#if GTEST_HAS_TYPED_TEST_P - -// Skips to the first non-space char in str. Returns an empty string if str -// contains only whitespace characters. -static const char* SkipSpaces(const char* str) { - while (isspace(*str)) - str++; - return str; -} - -// Verifies that registered_tests match the test names in -// defined_test_names_; returns registered_tests if successful, or -// aborts the program otherwise. -const char* TypedTestCasePState::VerifyRegisteredTestNames( - const char* file, int line, const char* registered_tests) { - typedef ::std::set::const_iterator DefinedTestIter; - registered_ = true; - - // Skip initial whitespace in registered_tests since some - // preprocessors prefix stringizied literals with whitespace. - registered_tests = SkipSpaces(registered_tests); - - Message errors; - ::std::set tests; - for (const char* names = registered_tests; names != NULL; - names = SkipComma(names)) { - const String name = GetPrefixUntilComma(names); - if (tests.count(name) != 0) { - errors << "Test " << name << " is listed more than once.\n"; - continue; - } - - bool found = false; - for (DefinedTestIter it = defined_test_names_.begin(); - it != defined_test_names_.end(); - ++it) { - if (name == *it) { - found = true; - break; - } - } - - if (found) { - tests.insert(name); - } else { - errors << "No test named " << name - << " can be found in this test case.\n"; - } - } - - for (DefinedTestIter it = defined_test_names_.begin(); - it != defined_test_names_.end(); - ++it) { - if (tests.count(*it) == 0) { - errors << "You forgot to list test " << *it << ".\n"; - } - } - - const String& errors_str = errors.GetString(); - if (errors_str != "") { - fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), - errors_str.c_str()); - fflush(stderr); - posix::Abort(); - } - - return registered_tests; -} - -#endif // GTEST_HAS_TYPED_TEST_P - -} // namespace internal -} // namespace testing diff --git a/modules/gtest/src/gtestcv.cpp b/modules/gtest/src/gtestcv.cpp deleted file mode 100644 index 3b3b69523..000000000 --- a/modules/gtest/src/gtestcv.cpp +++ /dev/null @@ -1,1443 +0,0 @@ -#include "precomp.hpp" -#include -#include - -using namespace cv; - -namespace cvtest -{ - -Size randomSize(RNG& rng, double maxSizeLog) -{ - double width_log = rng.uniform(0., maxSizeLog); - double height_log = rng.uniform(0., maxSizeLog - width_log); - if( (unsigned)rng % 2 != 0 ) - std::swap(width_log, height_log); - Size sz; - sz.width = cvRound(exp(width_log)); - sz.height = cvRound(exp(height_log)); - return sz; -} - -void randomSize(RNG& rng, int minDims, int maxDims, double maxSizeLog, vector& sz) -{ - int i, dims = rng.uniform(minDims, maxDims+1); - sz.resize(dims); - for( i = 0; i < dims; i++ ) - { - double v = rng.uniform(0., maxSizeLog); - maxSizeLog -= v; - sz[i] = cvRound(exp(v)); - } - for( i = 0; i < dims; i++ ) - { - int j = rng.uniform(0, dims); - int k = rng.uniform(0, dims); - std::swap(sz[j], sz[k]); - } -} - -int randomType(RNG& rng, int typeMask, int minChannels, int maxChannels) -{ - int channels = rng.uniform(minChannels, maxChannels+1); - int depth = 0; - CV_Assert((typeMask & TYPE_MASK_ALL) != 0); - for(;;) - { - depth = rng.uniform(CV_8U, CV_64F+1); - if( ((1 << depth) & typeMask) != 0 ) - break; - } - return CV_MAKETYPE(depth, channels); -} - -double getMinVal(int depth) -{ - double val = depth == CV_8U ? 0 : depth == CV_8S ? SCHAR_MIN : depth == CV_16U ? 0 : - depth == CV_16S ? SHRT_MIN : depth == CV_32S ? INT_MIN : - depth == CV_32F ? -FLT_MAX : depth == CV_64F ? -DBL_MAX : -1; - CV_Assert(val != -1); - return val; -} - -double getMaxVal(int depth) -{ - double val = depth == CV_8U ? UCHAR_MAX : depth == CV_8S ? SCHAR_MAX : depth == CV_16U ? USHRT_MAX : - depth == CV_16S ? SHRT_MAX : depth == CV_32S ? INT_MAX : - depth == CV_32F ? FLT_MAX : depth == CV_64F ? DBL_MAX : -1; - CV_Assert(val != -1); - return val; -} - -Mat randomMat(RNG& rng, Size size, int type, double minVal, double maxVal, bool useRoi) -{ - Size size0 = size; - if( useRoi ) - { - size0.width += std::max(rng.uniform(0, 10) - 5, 0); - size0.height += std::max(rng.uniform(0, 10) - 5, 0); - } - - Mat m(size0, type); - - rng.fill(m, RNG::UNIFORM, Scalar::all(minVal), Scalar::all(maxVal)); - if( size0 == size ) - return m; - return m(Rect((size0.width-size.width)/2, (size0.height-size.height)/2, size.width, size.height)); -} - -Mat randomMat(RNG& rng, const vector& size, int type, double minVal, double maxVal, bool useRoi) -{ - int i, dims = (int)size.size(); - vector size0(dims); - vector r(dims); - bool eqsize = true; - for( i = 0; i < dims; i++ ) - { - size0[i] = size[i]; - r[i] = Range::all(); - if( useRoi ) - { - size0[i] += std::max(rng.uniform(0, 5) - 2, 0); - r[i] = Range((size0[i] - size[i])/2, (size0[i] - size[i])/2 + size[i]); - } - eqsize = eqsize && size[i] == size0[i]; - } - - Mat m(dims, &size0[0], type); - - rng.fill(m, RNG::UNIFORM, Scalar::all(minVal), Scalar::all(maxVal)); - if( eqsize ) - return m; - return m(&r[0]); -} - -void add(const Mat& _a, double alpha, const Mat& _b, double beta, - Scalar gamma, Mat& c, int ctype, bool calcAbs) -{ - Mat a = _a, b = _b; - if( a.empty() || alpha == 0 ) - { - // both alpha and beta can be 0, but at least one of a and b must be non-empty array, - // otherwise we do not know the size of the output (and may be type of the output, when ctype<0) - CV_Assert( !a.empty() || !b.empty() ); - if( !b.empty() ) - { - a = b; - alpha = beta; - b = Mat(); - beta = 0; - } - } - if( b.empty() || beta == 0 ) - { - b = Mat(); - beta = 0; - } - else - CV_Assert(a.size == b.size); - - if( ctype < 0 ) - ctype = a.depth(); - ctype = CV_MAKETYPE(CV_MAT_DEPTH(ctype), a.channels()); - c.create(a.dims, &a.size[0], ctype); - const Mat *arrays[3]; - Mat planes[3], buf[3]; - arrays[0] = &a; - arrays[1] = b.empty() ? 0 : &b; - arrays[2] = &c; - - NAryMatIterator it(arrays, planes, 3); - int i, nplanes = it.nplanes, cn=a.channels(); - size_t total = planes[0].total(), maxsize = std::min((size_t)12*12*std::max(12/cn, 1), total); - - CV_Assert(planes[0].rows == 1); - buf[0].create(1, (int)maxsize, CV_64FC(cn)); - if(!b.empty()) - buf[1].create(1, maxsize, CV_64FC(cn)); - buf[2].create(1, maxsize, CV_64FC(cn)); - scalarToRawData(gamma, buf[2].data, CV_64FC(cn), (int)(maxsize*cn)); - - for( i = 0; i < nplanes; i++, ++it) - { - for( size_t j = 0; j < total; j += maxsize ) - { - size_t j2 = min(j + maxsize, total); - Mat apart0 = planes[0].colRange((int)j, (int)j2); - Mat cpart0 = planes[2].colRange((int)j, (int)j2); - Mat apart = buf[0].colRange(0, (int)(j2 - j)); - - apart0.convertTo(apart, apart.type(), alpha); - size_t k, n = (j2 - j)*cn; - double* aptr = (double*)apart.data; - const double* gptr = (const double*)buf[2].data; - - if( b.empty() ) - { - for( k = 0; k < n; k++ ) - aptr[k] += gptr[k]; - } - else - { - Mat bpart0 = planes[1].colRange((int)j, (int)j2); - Mat bpart = buf[1].colRange(0, (int)(j2 - j)); - bpart0.convertTo(bpart, bpart.type(), beta); - const double* bptr = (const double*)bpart.data; - - for( k = 0; k < n; k++ ) - aptr[k] += bptr[k] + gptr[k]; - } - if( calcAbs ) - for( k = 0; k < n; k++ ) - aptr[k] = fabs(aptr[k]); - apart.convertTo(cpart0, cpart0.type(), 1, 0); - } - } -} - - -template inline void -convert_(const _Tp1* src, _Tp2* dst, size_t total, double alpha, double beta) -{ - size_t i; - if( alpha == 1 && beta == 0 ) - for( i = 0; i < total; i++ ) - dst[i] = saturate_cast<_Tp2>(src[i]); - else if( beta == 0 ) - for( i = 0; i < total; i++ ) - dst[i] = saturate_cast<_Tp2>(src[i]*alpha); - else - for( i = 0; i < total; i++ ) - dst[i] = saturate_cast<_Tp2>(src[i]*alpha + beta); -} - -template inline void -convertTo(const _Tp* src, void* dst, int dtype, size_t total, double alpha, double beta) -{ - switch( CV_MAT_DEPTH(dtype) ) - { - case CV_8U: - convert_(src, (uchar*)dst, total, alpha, beta); - break; - case CV_8S: - convert_(src, (schar*)dst, total, alpha, beta); - break; - case CV_16U: - convert_(src, (ushort*)dst, total, alpha, beta); - break; - case CV_16S: - convert_(src, (short*)dst, total, alpha, beta); - break; - case CV_32S: - convert_(src, (int*)dst, total, alpha, beta); - break; - case CV_32F: - convert_(src, (float*)dst, total, alpha, beta); - break; - case CV_64F: - convert_(src, (double*)dst, total, alpha, beta); - break; - default: - CV_Assert(0); - } -} - -void convert(const Mat& src, Mat& dst, int dtype, double alpha, double beta) -{ - dtype = CV_MAKETYPE(CV_MAT_DEPTH(dtype), src.channels()); - dst.create(src.dims, &src.size[0], dtype); - if( alpha == 0 ) - { - set( dst, Scalar::all(beta) ); - return; - } - if( dtype == src.type() && alpha == 1 && beta == 0 ) - { - copy( src, dst ); - return; - } - - const Mat *arrays[]={&src, &dst}; - Mat planes[2]; - - NAryMatIterator it(arrays, planes, 2); - size_t total = planes[0].total()*planes[0].channels(); - int i, nplanes = it.nplanes; - - for( i = 0; i < nplanes; i++, ++it) - { - const uchar* sptr = planes[0].data; - uchar* dptr = planes[1].data; - - switch( src.depth() ) - { - case CV_8U: - convertTo((const uchar*)sptr, dptr, dtype, total, alpha, beta); - break; - case CV_8S: - convertTo((const schar*)sptr, dptr, dtype, total, alpha, beta); - break; - case CV_16U: - convertTo((const ushort*)sptr, dptr, dtype, total, alpha, beta); - break; - case CV_16S: - convertTo((const short*)sptr, dptr, dtype, total, alpha, beta); - break; - case CV_32S: - convertTo((const int*)sptr, dptr, dtype, total, alpha, beta); - break; - case CV_32F: - convertTo((const float*)sptr, dptr, dtype, total, alpha, beta); - break; - case CV_64F: - convertTo((const double*)sptr, dptr, dtype, total, alpha, beta); - break; - } - } -} - - -void copy(const Mat& src, Mat& dst, const Mat& mask, bool invertMask) -{ - dst.create(src.dims, &src.size[0], src.type()); - - if(mask.empty()) - { - const Mat* arrays[] = {&src, &dst}; - Mat planes[2]; - NAryMatIterator it(arrays, planes, 2); - int i, nplanes = it.nplanes; - size_t planeSize = planes[0].total()*src.elemSize(); - - for( i = 0; i < nplanes; i++, ++it ) - memcpy(planes[1].data, planes[0].data, planeSize); - - return; - } - - CV_Assert( src.size == mask.size && mask.type() == CV_8U ); - - const Mat *arrays[3]={&src, &dst, &mask}; - Mat planes[3]; - - NAryMatIterator it(arrays, planes, 3); - size_t j, k, elemSize = src.elemSize(), total = planes[0].total(); - int i, nplanes = it.nplanes; - - for( i = 0; i < nplanes; i++, ++it) - { - const uchar* sptr = planes[0].data; - uchar* dptr = planes[1].data; - const uchar* mptr = planes[2].data; - - for( j = 0; j < total; j++, sptr += elemSize, dptr += elemSize ) - { - if( (mptr[j] != 0) ^ invertMask ) - for( k = 0; k < elemSize; k++ ) - dptr[k] = sptr[k]; - } - } -} - - -void set(Mat& dst, const Scalar& gamma, const Mat& mask) -{ - double buf[12]; - scalarToRawData(gamma, &buf, dst.type(), dst.channels()); - const uchar* gptr = (const uchar*)&buf[0]; - - if(mask.empty()) - { - const Mat* arrays[] = {&dst}; - Mat plane; - NAryMatIterator it(arrays, &plane, 1); - int i, nplanes = it.nplanes; - size_t j, k, elemSize = dst.elemSize(), planeSize = plane.total()*elemSize; - - for( k = 1; k < elemSize; k++ ) - if( gptr[k] != gptr[0] ) - break; - bool uniform = k >= elemSize; - - for( i = 0; i < nplanes; i++, ++it ) - { - uchar* dptr = plane.data; - if( uniform ) - memset( dptr, gptr[0], planeSize ); - else if( i == 0 ) - { - for( j = 0; j < planeSize; j += elemSize, dptr += elemSize ) - for( k = 0; k < elemSize; k++ ) - dptr[k] = gptr[k]; - } - else - memcpy(dptr, dst.data, planeSize); - } - return; - } - - CV_Assert( dst.size == mask.size && mask.type() == CV_8U ); - - const Mat *arrays[2]={&dst, &mask}; - Mat planes[2]; - - NAryMatIterator it(arrays, planes, 2); - size_t j, k, elemSize = dst.elemSize(), total = planes[0].total(); - int i, nplanes = it.nplanes; - - for( i = 0; i < nplanes; i++, ++it) - { - uchar* dptr = planes[0].data; - const uchar* mptr = planes[1].data; - - for( j = 0; j < total; j++, dptr += elemSize ) - { - if( mptr[j] ) - for( k = 0; k < elemSize; k++ ) - dptr[k] = gptr[k]; - } - } -} - - -template static void -erode_(const Mat& src, Mat& dst, const vector& ofsvec) -{ - int width = src.cols*src.channels(), n = (int)ofsvec.size(); - const int* ofs = &ofsvec[0]; - - for( int y = 0; y < dst.rows; y++ ) - { - const _Tp* sptr = src.ptr<_Tp>(y); - _Tp* dptr = dst.ptr<_Tp>(y); - - for( int x = 0; x < width; x++ ) - { - _Tp result = sptr[x + ofs[0]]; - for( int i = 1; i < n; i++ ) - result = std::min(result, sptr[x + ofs[i]]); - dptr[x] = result; - } - } -} - - -template static void -dilate_(const Mat& src, Mat& dst, const vector& ofsvec) -{ - int width = src.cols*src.channels(), n = (int)ofsvec.size(); - const int* ofs = &ofsvec[0]; - - for( int y = 0; y < dst.rows; y++ ) - { - const _Tp* sptr = src.ptr<_Tp>(y); - _Tp* dptr = dst.ptr<_Tp>(y); - - for( int x = 0; x < width; x++ ) - { - _Tp result = sptr[x + ofs[0]]; - for( int i = 1; i < n; i++ ) - result = std::max(result, sptr[x + ofs[i]]); - dptr[x] = result; - } - } -} - - -void erode(const Mat& _src, Mat& dst, const Mat& _kernel, Point anchor, - int borderType, const Scalar& _borderValue) -{ - Mat kernel = _kernel, src; - Scalar borderValue = _borderValue; - if( kernel.empty() ) - kernel = Mat::ones(3, 3, CV_8U); - else - { - CV_Assert( kernel.type() == CV_8U ); - } - if( anchor == Point(-1,-1) ) - anchor = Point(kernel.cols/2, kernel.rows/2); - if( borderType == IPL_BORDER_CONSTANT ) - borderValue = getMaxVal(src.depth()); - copyMakeBorder(_src, src, anchor.y, kernel.rows - anchor.y - 1, - anchor.x, kernel.cols - anchor.y - 1, - borderType, borderValue); - dst.create( _src.size(), src.type() ); - - vector ofs; - int step = (int)(src.step/src.elemSize1()), cn = src.channels(); - for( int i = 0; i < kernel.rows; i++ ) - for( int j = 0; j < kernel.cols; j++ ) - if( kernel.at(i, j) != 0 ) - ofs.push_back(i*step + j*cn); - if( ofs.empty() ) - ofs.push_back(anchor.y*step + anchor.x*cn); - - switch( src.depth() ) - { - case CV_8U: - erode_(src, dst, ofs); - break; - case CV_8S: - erode_(src, dst, ofs); - break; - case CV_16U: - erode_(src, dst, ofs); - break; - case CV_16S: - erode_(src, dst, ofs); - break; - case CV_32S: - erode_(src, dst, ofs); - break; - case CV_32F: - erode_(src, dst, ofs); - break; - case CV_64F: - erode_(src, dst, ofs); - break; - default: - CV_Assert(0); - } -} - -void dilate(const Mat& _src, Mat& dst, const Mat& _kernel, Point anchor, - int borderType, const Scalar& _borderValue) -{ - Mat kernel = _kernel, src; - Scalar borderValue = _borderValue; - if( kernel.empty() ) - kernel = Mat::ones(3, 3, CV_8U); - else - { - CV_Assert( kernel.type() == CV_8U ); - } - if( anchor == Point(-1,-1) ) - anchor = Point(kernel.cols/2, kernel.rows/2); - if( borderType == IPL_BORDER_CONSTANT ) - borderValue = getMinVal(src.depth()); - copyMakeBorder(_src, src, anchor.y, kernel.rows - anchor.y - 1, - anchor.x, kernel.cols - anchor.y - 1, - borderType, borderValue); - dst.create( _src.size(), src.type() ); - - vector ofs; - int step = (int)(src.step/src.elemSize1()), cn = src.channels(); - for( int i = 0; i < kernel.rows; i++ ) - for( int j = 0; j < kernel.cols; j++ ) - if( kernel.at(i, j) != 0 ) - ofs.push_back(i*step + j*cn); - if( ofs.empty() ) - ofs.push_back(anchor.y*step + anchor.x*cn); - - switch( src.depth() ) - { - case CV_8U: - dilate_(src, dst, ofs); - break; - case CV_8S: - dilate_(src, dst, ofs); - break; - case CV_16U: - dilate_(src, dst, ofs); - break; - case CV_16S: - dilate_(src, dst, ofs); - break; - case CV_32S: - dilate_(src, dst, ofs); - break; - case CV_32F: - dilate_(src, dst, ofs); - break; - case CV_64F: - dilate_(src, dst, ofs); - break; - default: - CV_Assert(0); - } -} - - -template static void -filter2D_(const Mat& src, Mat& dst, const vector& ofsvec, const vector& coeffvec) -{ - const int* ofs = &ofsvec[0]; - const double* coeff = &coeffvec[0]; - int width = dst.cols*dst.channels(), ncoeffs = (int)ofsvec.size(); - - for( int y = 0; y < dst.rows; y++ ) - { - const _Tp* sptr = src.ptr<_Tp>(y); - double* dptr = dst.ptr(y); - - for( int x = 0; x < width; x++ ) - { - double s = 0; - for( int i = 0; i < ncoeffs; i++ ) - s += sptr[ofs[i]]*coeff[i]; - dptr[x] = s; - } - } -} - - -void filter2D(const Mat& _src, Mat& dst, int ddepth, const Mat& _kernel, - Point anchor, double delta, int borderType, const Scalar& _borderValue) -{ - Mat kernel = _kernel, src, _dst; - Scalar borderValue = _borderValue; - CV_Assert( kernel.type() == CV_32F || kernel.type() == CV_64F ); - if( anchor == Point(-1,-1) ) - anchor = Point(kernel.cols/2, kernel.rows/2); - if( borderType == IPL_BORDER_CONSTANT ) - borderValue = getMinVal(src.depth()); - copyMakeBorder(_src, src, anchor.y, kernel.rows - anchor.y - 1, - anchor.x, kernel.cols - anchor.y - 1, - borderType, borderValue); - _dst.create( _src.size(), CV_MAKETYPE(CV_64F, src.channels()) ); - - vector ofs; - vector coeff(kernel.rows*kernel.cols); - Mat cmat(kernel.rows, kernel.cols, CV_64F, &coeff[0]); - convert(kernel, cmat, cmat.type()); - - int step = (int)(src.step/src.elemSize1()), cn = src.channels(); - for( int i = 0; i < kernel.rows; i++ ) - for( int j = 0; j < kernel.cols; j++ ) - ofs.push_back(i*step + j*cn); - - switch( src.depth() ) - { - case CV_8U: - filter2D_(src, _dst, ofs, coeff); - break; - case CV_8S: - filter2D_(src, _dst, ofs, coeff); - break; - case CV_16U: - filter2D_(src, _dst, ofs, coeff); - break; - case CV_16S: - filter2D_(src, _dst, ofs, coeff); - break; - case CV_32S: - filter2D_(src, _dst, ofs, coeff); - break; - case CV_32F: - filter2D_(src, _dst, ofs, coeff); - break; - case CV_64F: - filter2D_(src, _dst, ofs, coeff); - break; - default: - CV_Assert(0); - } - - convert(_dst, dst, ddepth, 1, delta); -} - - -static int borderInterpolate( int p, int len, int borderType ) -{ - if( (unsigned)p < (unsigned)len ) - ; - else if( borderType == IPL_BORDER_REPLICATE ) - p = p < 0 ? 0 : len - 1; - else if( borderType == IPL_BORDER_REFLECT || borderType == IPL_BORDER_REFLECT_101 ) - { - int delta = borderType == IPL_BORDER_REFLECT_101; - if( len == 1 ) - return 0; - do - { - if( p < 0 ) - p = -p - 1 + delta; - else - p = len - 1 - (p - len) - delta; - } - while( (unsigned)p >= (unsigned)len ); - } - else if( borderType == IPL_BORDER_WRAP ) - { - if( p < 0 ) - p -= ((p-len+1)/len)*len; - if( p >= len ) - p %= len; - } - else if( borderType == IPL_BORDER_CONSTANT ) - p = -1; - else - CV_Error( CV_StsBadArg, "Unknown/unsupported border type" ); - return p; -} - - -void copyMakeBorder(const Mat& src, Mat& dst, int top, int bottom, int left, int right, - int borderType, const Scalar& borderValue) -{ - dst.create(src.rows + top + bottom, src.cols + left + right, src.type()); - int i, j, k, esz = (int)src.elemSize(); - int width = src.cols*esz, width1 = dst.cols*esz; - - if( borderType == IPL_BORDER_CONSTANT ) - { - vector valvec((src.cols + left + right)*esz); - uchar* val = &valvec[0]; - scalarToRawData(borderValue, val, src.type(), (src.cols + left + right)*src.channels()); - - left *= esz; - right *= esz; - for( i = 0; i < src.rows; i++ ) - { - const uchar* sptr = src.ptr(i); - uchar* dptr = dst.ptr(i + top) + left; - for( j = 0; j < left; j++ ) - dptr[j - left] = val[j]; - if( dptr != sptr ) - for( j = 0; j < width; j++ ) - dptr[j] = sptr[j]; - for( j = 0; j < right; j++ ) - dptr[j + width] = val[j]; - } - - for( i = 0; i < top; i++ ) - { - uchar* dptr = dst.ptr(i); - for( j = 0; j < width1; j++ ) - dptr[j] = val[j]; - } - - for( i = 0; i < bottom; i++ ) - { - uchar* dptr = dst.ptr(i + top + src.rows); - for( j = 0; j < width1; j++ ) - dptr[j] = val[j]; - } - } - else - { - vector tabvec((left + right)*esz); - int* ltab = &tabvec[0]; - int* rtab = &tabvec[left*esz]; - for( i = 0; i < left; i++ ) - { - j = borderInterpolate(i - left, src.cols, borderType)*esz; - for( k = 0; k < esz; k++ ) - ltab[i*esz + k] = j + k; - } - for( i = 0; i < right; i++ ) - { - j = borderInterpolate(src.cols + i, src.cols, borderType)*esz; - for( k = 0; k < esz; k++ ) - rtab[i*esz + k] = j + k; - } - - left *= esz; - right *= esz; - for( i = 0; i < src.rows; i++ ) - { - const uchar* sptr = src.ptr(i); - uchar* dptr = dst.ptr(i + top); - - for( j = 0; j < left; j++ ) - dptr[j] = sptr[ltab[j]]; - if( dptr + left != sptr ) - { - for( j = 0; j < width; j++ ) - dptr[j + left] = sptr[j]; - } - for( j = 0; j < right; j++ ) - dptr[j + left + width] = sptr[rtab[j]]; - } - - for( i = 0; i < top; i++ ) - { - j = borderInterpolate(i - top, src.rows, borderType); - const uchar* sptr = dst.ptr(j + top); - uchar* dptr = dst.ptr(i); - - for( k = 0; k < width1; k++ ) - dptr[k] = sptr[k]; - } - - for( i = 0; i < bottom; i++ ) - { - j = borderInterpolate(i + src.rows, src.rows, borderType); - const uchar* sptr = dst.ptr(j + top); - uchar* dptr = dst.ptr(i + top + src.rows); - - for( k = 0; k < width1; k++ ) - dptr[k] = sptr[k]; - } - } -} - - -template static void -minMaxLoc_(const _Tp* src, size_t total, size_t startidx, - double* _maxval, double* _minval, - size_t* _minpos, size_t* _maxpos, - const uchar* mask) -{ - _Tp maxval = saturate_cast<_Tp>(*_maxval), minval = saturate_cast<_Tp>(*_minval); - size_t minpos = *_minpos, maxpos = *_maxpos; - - if( !mask ) - { - for( size_t i = 0; i < total; i++ ) - { - _Tp val = src[i]; - if( minval > val ) - { - minval = val; - minpos = startidx + i; - } - if( maxval < val ) - { - maxval = val; - maxpos = startidx + i; - } - } - } - else - { - for( size_t i = 0; i < total; i++ ) - { - _Tp val = src[i]; - if( minval > val && mask[i] ) - { - minval = val; - minpos = startidx + i; - } - if( maxval < val && mask[i] ) - { - maxval = val; - maxpos = startidx + i; - } - } - } - - *_maxval = maxval; - *_minval = minval; - *_maxpos = maxpos; - *_minpos = minpos; -} - - -static void setpos( const Mat& mtx, vector& pos, size_t idx ) -{ - pos.resize(mtx.dims); - for( int i = mtx.dims-1; i >= 0; i-- ) - { - int sz = mtx.size[i]*(i == mtx.dims-1 ? mtx.channels() : 1); - pos[i] = idx % sz; - idx /= sz; - } -} - -void minMaxLoc(const Mat& src, double* _maxval, double* _minval, - vector* _maxloc, vector* _minloc, - const Mat& mask) -{ - CV_Assert( src.channels() == 1 ); - const Mat *arrays[2]={&src, &mask}; - Mat planes[2]; - - NAryMatIterator it(arrays, planes, 2); - size_t startidx = 1, total = planes[0].total(); - int i, nplanes = it.nplanes, depth = src.depth(); - double maxval = depth < CV_32F ? INT_MIN : depth == CV_32F ? -FLT_MAX : -DBL_MAX; - double minval = depth < CV_32F ? INT_MAX : depth == CV_32F ? FLT_MAX : DBL_MAX; - size_t maxidx = 0, minidx = 0; - - for( i = 0; i < nplanes; i++, ++it, startidx += total ) - { - const uchar* sptr = planes[0].data; - const uchar* mptr = planes[1].data; - - switch( depth ) - { - case CV_8U: - minMaxLoc_((const uchar*)sptr, total, startidx, - &maxval, &minval, &maxidx, &minidx, mptr); - break; - case CV_8S: - minMaxLoc_((const schar*)sptr, total, startidx, - &maxval, &minval, &maxidx, &minidx, mptr); - break; - case CV_16U: - minMaxLoc_((const ushort*)sptr, total, startidx, - &maxval, &minval, &maxidx, &minidx, mptr); - break; - case CV_16S: - minMaxLoc_((const short*)sptr, total, startidx, - &maxval, &minval, &maxidx, &minidx, mptr); - break; - case CV_32S: - minMaxLoc_((const int*)sptr, total, startidx, - &maxval, &minval, &maxidx, &minidx, mptr); - break; - case CV_32F: - minMaxLoc_((const float*)sptr, total, startidx, - &maxval, &minval, &maxidx, &minidx, mptr); - break; - case CV_64F: - minMaxLoc_((const double*)sptr, total, startidx, - &maxval, &minval, &maxidx, &minidx, mptr); - break; - default: - CV_Assert(0); - } - } - - if( _maxval ) - *_maxval = maxval; - if( _minval ) - *_minval = minval; - if( _maxloc ) - setpos( src, *_maxloc, maxidx ); - if( _minloc ) - setpos( src, *_minloc, minidx ); -} - - -template static double -norm_(const _Tp* src, size_t total, int normType, double startval, const uchar* mask) -{ - size_t i; - double result = startval; - - if( normType == NORM_INF ) - { - if( !mask ) - for( i = 0; i < total; i++ ) - result = std::max(result, (double)std::abs(src[i])); - else - for( i = 0; i < total; i++ ) - if( mask[i] ) - result = std::max(result, (double)std::abs(src[i])); - } - else if( normType == NORM_L1 ) - { - if( !mask ) - for( i = 0; i < total; i++ ) - result += std::abs(src[i]); - else - for( i = 0; i < total; i++ ) - if( mask[i] ) - result += std::abs(src[i]); - } - else - { - if( !mask ) - for( i = 0; i < total; i++ ) - { - double v = src[i]; - result += v*v; - } - else - for( i = 0; i < total; i++ ) - if( mask[i] ) - { - double v = src[i]; - result += v*v; - } - } - return result; -} - - -template static double -norm_(const _Tp* src1, const _Tp* src2, size_t total, int normType, double startval, const uchar* mask) -{ - size_t i; - double result = startval; - - if( normType == NORM_INF ) - { - if( !mask ) - for( i = 0; i < total; i++ ) - result = std::max(result, (double)std::abs(src1[i] - src2[i])); - else - for( i = 0; i < total; i++ ) - if( mask[i] ) - result = std::max(result, (double)std::abs(src1[i] - src2[i])); - } - else if( normType == NORM_L1 ) - { - if( !mask ) - for( i = 0; i < total; i++ ) - result += std::abs(src1[i] - src2[i]); - else - for( i = 0; i < total; i++ ) - if( mask[i] ) - result += std::abs(src1[i] - src2[i]); - } - else - { - if( !mask ) - for( i = 0; i < total; i++ ) - { - double v = src1[i] - src2[i]; - result += v*v; - } - else - for( i = 0; i < total; i++ ) - if( mask[i] ) - { - double v = src1[i] - src2[i]; - result += v*v; - } - } - return result; -} - - -double norm(const Mat& src, int normType, const Mat& mask) -{ - CV_Assert( mask.empty() || (src.size == mask.size && mask.type() == CV_8U) ); - CV_Assert( normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 ); - const Mat *arrays[2]={&src, &mask}; - Mat planes[2]; - - NAryMatIterator it(arrays, planes, 2); - size_t total = planes[0].total()*planes[0].channels(); - int i, nplanes = it.nplanes, depth = src.depth(); - double result = 0; - - for( i = 0; i < nplanes; i++, ++it ) - { - const uchar* sptr = planes[0].data; - const uchar* mptr = planes[1].data; - - switch( depth ) - { - case CV_8U: - result = norm_((const uchar*)sptr, total, normType, result, mptr); - break; - case CV_8S: - result = norm_((const schar*)sptr, total, normType, result, mptr); - break; - case CV_16U: - result = norm_((const ushort*)sptr, total, normType, result, mptr); - break; - case CV_16S: - result = norm_((const short*)sptr, total, normType, result, mptr); - break; - case CV_32S: - result = norm_((const int*)sptr, total, normType, result, mptr); - break; - case CV_32F: - result = norm_((const float*)sptr, total, normType, result, mptr); - break; - case CV_64F: - result = norm_((const double*)sptr, total, normType, result, mptr); - break; - default: - CV_Error(CV_StsUnsupportedFormat, ""); - }; - } - if( normType == NORM_L2 ) - result = sqrt(result); - return result; -} - -double norm(const Mat& src1, const Mat& src2, int normType, const Mat& mask) -{ - CV_Assert( src1.type() == src2.type() && src1.size == src2.size ); - CV_Assert( mask.empty() || (src1.size == mask.size && mask.type() == CV_8U) ); - CV_Assert( normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 ); - const Mat *arrays[3]={&src1, &src2, &mask}; - Mat planes[3]; - - NAryMatIterator it(arrays, planes, 3); - size_t total = planes[0].total()*planes[0].channels(); - int i, nplanes = it.nplanes, depth = src1.depth(); - double result = 0; - - for( i = 0; i < nplanes; i++, ++it ) - { - const uchar* sptr1 = planes[0].data; - const uchar* sptr2 = planes[1].data; - const uchar* mptr = planes[2].data; - - switch( depth ) - { - case CV_8U: - result = norm_((const uchar*)sptr1, (const uchar*)sptr2, total, normType, result, mptr); - break; - case CV_8S: - result = norm_((const schar*)sptr1, (const schar*)sptr2, total, normType, result, mptr); - break; - case CV_16U: - result = norm_((const ushort*)sptr1, (const ushort*)sptr2, total, normType, result, mptr); - break; - case CV_16S: - result = norm_((const short*)sptr1, (const short*)sptr2, total, normType, result, mptr); - break; - case CV_32S: - result = norm_((const int*)sptr1, (const int*)sptr2, total, normType, result, mptr); - break; - case CV_32F: - result = norm_((const float*)sptr1, (const float*)sptr2, total, normType, result, mptr); - break; - case CV_64F: - result = norm_((const double*)sptr1, (const double*)sptr2, total, normType, result, mptr); - break; - default: - CV_Error(CV_StsUnsupportedFormat, ""); - }; - } - if( normType == NORM_L2 ) - result = sqrt(result); - return result; -} - -bool cmpEps(const Mat& src1, const Mat& src2, int int_maxdiff, int flt_maxulp, vector* loc); - -static void -logicOp_(const uchar* src1, const uchar* src2, uchar* dst, size_t total, char c) -{ - size_t i; - if( c == '&' ) - for( i = 0; i < total; i++ ) - dst[i] = src1[i] & src2[i]; - else if( c == '|' ) - for( i = 0; i < total; i++ ) - dst[i] = src1[i] | src2[i]; - else - for( i = 0; i < total; i++ ) - dst[i] = src1[i] ^ src2[i]; -} - -static void -logicOpS_(const uchar* src, const uchar* scalar, uchar* dst, size_t total, char c) -{ - const size_t blockSize = 96; - size_t i, j; - if( c == '&' ) - for( i = 0; i < total; i += blockSize, dst += blockSize, src += blockSize ) - { - size_t sz = std::min(total - i, blockSize); - for( j = 0; j < sz; j++ ) - dst[j] = src[j] & scalar[j]; - } - else if( c == '|' ) - for( i = 0; i < total; i += blockSize, dst += blockSize, src += blockSize ) - { - size_t sz = std::min(total - i, blockSize); - for( j = 0; j < sz; j++ ) - dst[j] = src[j] | scalar[j]; - } - else if( c == '^' ) - { - for( i = 0; i < total; i += blockSize, dst += blockSize, src += blockSize ) - { - size_t sz = std::min(total - i, blockSize); - for( j = 0; j < sz; j++ ) - dst[j] = src[j] ^ scalar[j]; - } - } - else - for( i = 0; i < total; i++ ) - dst[i] = ~src[i]; -} - -void logicOp( const Mat& src1, const Mat& src2, Mat& dst, char op ) -{ - CV_Assert( op == '&' || op == '|' || op == '^' ); - CV_Assert( src1.type() == src2.type() && src1.size == src2.size ); - dst.create( src1.dims, &src1.size[0], src1.type() ); - const Mat *arrays[3]={&src1, &src2, &dst}; - Mat planes[3]; - - NAryMatIterator it(arrays, planes, 3); - size_t total = planes[0].total()*planes[0].elemSize(); - int i, nplanes = it.nplanes; - - for( i = 0; i < nplanes; i++, ++it ) - { - const uchar* sptr1 = planes[0].data; - const uchar* sptr2 = planes[1].data; - uchar* dptr = planes[2].data; - - logicOp_(sptr1, sptr2, dptr, total, op); - } -} - - -void logicOp(const Mat& src, const Scalar& s, Mat& dst, char op) -{ - CV_Assert( op == '&' || op == '|' || op == '^' ); - dst.create( src.dims, &src.size[0], src.type() ); - const Mat *arrays[2]={&src, &dst}; - Mat planes[2]; - - NAryMatIterator it(arrays, planes, 2); - size_t total = planes[0].total()*planes[0].elemSize(); - int i, nplanes = it.nplanes; - double buf[12]; - scalarToRawData(s, buf, src.type(), 12); - - for( i = 0; i < nplanes; i++, ++it ) - { - const uchar* sptr = planes[0].data; - uchar* dptr = planes[1].data; - - logicOpS_(sptr, (uchar*)&buf[0], dptr, total, op); - } -} - - -template static void -compare_(const _Tp* src1, const _Tp* src2, uchar* dst, size_t total, int cmpop) -{ - size_t i; - switch( cmpop ) - { - case CMP_LT: - for( i = 0; i < total; i++ ) - dst[i] = src1[i] < src2[i] ? 255 : 0; - break; - case CMP_LE: - for( i = 0; i < total; i++ ) - dst[i] = src1[i] <= src2[i] ? 255 : 0; - break; - case CMP_EQ: - for( i = 0; i < total; i++ ) - dst[i] = src1[i] == src2[i] ? 255 : 0; - break; - case CMP_NE: - for( i = 0; i < total; i++ ) - dst[i] = src1[i] != src2[i] ? 255 : 0; - break; - case CMP_GE: - for( i = 0; i < total; i++ ) - dst[i] = src1[i] >= src2[i] ? 255 : 0; - break; - case CMP_GT: - for( i = 0; i < total; i++ ) - dst[i] = src1[i] > src2[i] ? 255 : 0; - break; - default: - CV_Error(CV_StsBadArg, "Unknown comparison operation"); - } -} - - -template static void -compareS_(const _Tp* src1, _WTp value, uchar* dst, size_t total, int cmpop) -{ - size_t i; - switch( cmpop ) - { - case CMP_LT: - for( i = 0; i < total; i++ ) - dst[i] = src1[i] < value ? 255 : 0; - break; - case CMP_LE: - for( i = 0; i < total; i++ ) - dst[i] = src1[i] <= value ? 255 : 0; - break; - case CMP_EQ: - for( i = 0; i < total; i++ ) - dst[i] = src1[i] == value ? 255 : 0; - break; - case CMP_NE: - for( i = 0; i < total; i++ ) - dst[i] = src1[i] != value ? 255 : 0; - break; - case CMP_GE: - for( i = 0; i < total; i++ ) - dst[i] = src1[i] >= value ? 255 : 0; - break; - case CMP_GT: - for( i = 0; i < total; i++ ) - dst[i] = src1[i] > value ? 255 : 0; - break; - default: - CV_Error(CV_StsBadArg, "Unknown comparison operation"); - } -} - - -void compare(const Mat& src1, const Mat& src2, Mat& dst, int cmpop) -{ - CV_Assert( src1.type() == src2.type() && src1.channels() == 1 && src1.size == src2.size ); - dst.create( src1.dims, &src1.size[0], CV_8U ); - const Mat *arrays[3]={&src1, &src2, &dst}; - Mat planes[3]; - - NAryMatIterator it(arrays, planes, 3); - size_t total = planes[0].total()*planes[0].elemSize(); - int i, nplanes = it.nplanes, depth = src1.depth(); - - for( i = 0; i < nplanes; i++, ++it ) - { - const uchar* sptr1 = planes[0].data; - const uchar* sptr2 = planes[1].data; - uchar* dptr = planes[2].data; - - switch( depth ) - { - case CV_8U: - compare_((const uchar*)sptr1, (const uchar*)sptr2, dptr, total, cmpop); - break; - case CV_8S: - compare_((const schar*)sptr1, (const schar*)sptr2, dptr, total, cmpop); - break; - case CV_16U: - compare_((const ushort*)sptr1, (const ushort*)sptr2, dptr, total, cmpop); - break; - case CV_16S: - compare_((const short*)sptr1, (const short*)sptr2, dptr, total, cmpop); - break; - case CV_32S: - compare_((const int*)sptr1, (const int*)sptr2, dptr, total, cmpop); - break; - case CV_32F: - compare_((const float*)sptr1, (const float*)sptr2, dptr, total, cmpop); - break; - case CV_64F: - compare_((const double*)sptr1, (const double*)sptr2, dptr, total, cmpop); - break; - default: - CV_Error(CV_StsUnsupportedFormat, ""); - } - } -} - -void compare(const Mat& src, double value, Mat& dst, int cmpop) -{ - CV_Assert( src.channels() == 1 ); - dst.create( src.dims, &src.size[0], CV_8U ); - const Mat *arrays[2]={&src, &dst}; - Mat planes[2]; - - NAryMatIterator it(arrays, planes, 2); - size_t total = planes[0].total()*planes[0].elemSize(); - int i, nplanes = it.nplanes, depth = src.depth(); - int ivalue = saturate_cast(value); - - for( i = 0; i < nplanes; i++, ++it ) - { - const uchar* sptr = planes[0].data; - uchar* dptr = planes[1].data; - - switch( depth ) - { - case CV_8U: - compareS_((const uchar*)sptr, ivalue, dptr, total, cmpop); - break; - case CV_8S: - compareS_((const schar*)sptr, ivalue, dptr, total, cmpop); - break; - case CV_16U: - compareS_((const ushort*)sptr, ivalue, dptr, total, cmpop); - break; - case CV_16S: - compareS_((const short*)sptr, ivalue, dptr, total, cmpop); - break; - case CV_32S: - compareS_((const int*)sptr, ivalue, dptr, total, cmpop); - break; - case CV_32F: - compareS_((const float*)sptr, value, dptr, total, cmpop); - break; - case CV_64F: - compareS_((const double*)sptr, value, dptr, total, cmpop); - break; - default: - CV_Error(CV_StsUnsupportedFormat, ""); - } - } -} - - -template static bool -cmpEpsInt_(const _Tp* src1, const _Tp* src2, int imaxdiff, size_t total, size_t startidx, size_t& idx) -{ - size_t i; - for( i = 0; i < total; i++ ) - if( std::abs(src1[i] - src2[i]) > imaxdiff ) - { - idx = i + startidx; - return false; - } - return true; -} - - -template static bool -cmpEpsFlt_(const _Tp* src1, const _Tp* src2, size_t total, int imaxdiff, size_t startidx, size_t& idx) -{ - const _Tp C = ((_Tp)1 << (sizeof(_Tp)*8-1)) - 1; - size_t i; - for( i = 0; i < total; i++ ) - { - _Tp a = src1[i], b = src2[i]; - if( a < 0 ) a ^= C; if( b < 0 ) b ^= C; - _Tp d = std::abs(double(a - b)); - if( d > imaxdiff ) - { - idx = i + startidx; - return false; - } - } - return true; -} - - -bool cmpEps(const Mat& src1, const Mat& src2, int imaxDiff, vector* loc) -{ - CV_Assert( src1.type() == src2.type() && src1.size == src2.size ); - const Mat *arrays[2]={&src1, &src2}; - Mat planes[2]; - - NAryMatIterator it(arrays, planes, 2); - size_t total = planes[0].total()*planes[0].channels(); - int i, nplanes = it.nplanes, depth = src1.depth(); - size_t startidx = 0, idx = -1; - bool ok = true; - - for( i = 0; i < nplanes; i++, ++it, startidx += total ) - { - const uchar* sptr1 = planes[0].data; - const uchar* sptr2 = planes[1].data; - - switch( depth ) - { - case CV_8U: - ok = cmpEpsInt_((const uchar*)sptr1, (const uchar*)sptr2, total, imaxDiff, startidx, idx); - break; - case CV_8S: - ok = cmpEpsInt_((const schar*)sptr1, (const schar*)sptr2, total, imaxDiff, startidx, idx); - break; - case CV_16U: - ok = cmpEpsInt_((const ushort*)sptr1, (const ushort*)sptr2, total, imaxDiff, startidx, idx); - break; - case CV_16S: - ok = cmpEpsInt_((const short*)sptr1, (const short*)sptr2, total, imaxDiff, startidx, idx); - break; - case CV_32S: - ok = cmpEpsInt_((const int*)sptr1, (const int*)sptr2, total, imaxDiff, startidx, idx); - break; - case CV_32F: - ok = cmpEpsFlt_((const int*)sptr1, (const int*)sptr2, total, imaxDiff, startidx, idx); - break; - case CV_64F: - ok = cmpEpsFlt_((const int64*)sptr1, (const int64*)sptr2, total, imaxDiff, startidx, idx); - break; - default: - CV_Error(CV_StsUnsupportedFormat, ""); - } - if(!ok) - break; - } - if(!ok && loc) - setpos(src1, *loc, idx); - return ok; -} - -} diff --git a/modules/gtest/src/precomp.cpp b/modules/gtest/src/precomp.cpp deleted file mode 100644 index c149df18f..000000000 --- a/modules/gtest/src/precomp.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "precomp.hpp" diff --git a/modules/gtest/src/precomp.hpp b/modules/gtest/src/precomp.hpp deleted file mode 100644 index 03117ece1..000000000 --- a/modules/gtest/src/precomp.hpp +++ /dev/null @@ -1 +0,0 @@ -#include "opencv2/gtest/gtestcv.hpp" diff --git a/modules/highgui/test/test_drawing.cpp b/modules/highgui/test/test_drawing.cpp new file mode 100644 index 000000000..908ea4197 --- /dev/null +++ b/modules/highgui/test/test_drawing.cpp @@ -0,0 +1,410 @@ +/*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" + +using namespace cv; + +//#define DRAW_TEST_IMAGE + +class CV_DrawingTest : public CvTest +{ +public: + CV_DrawingTest( const char* testName ) : CvTest( testName, "drawing funcs" ) {} +protected: + void run( int ); + virtual void draw( Mat& img ) = 0; + virtual int checkLineIterator( Mat& img) = 0; +}; + +void CV_DrawingTest::run( int ) +{ + int code = CvTS::OK; + Mat testImg, valImg; + const string name = "drawing/image.jpg"; + string path = ts->get_data_path(), filename; + filename = path + name; + + draw( testImg ); + +#ifdef DRAW_TEST_IMAGE + imwrite( filename, testImg ); +#else + valImg = imread( filename ); + if( valImg.empty() ) + { + ts->printf( CvTS::LOG, "test image can not be read"); + code = CvTS::FAIL_INVALID_TEST_DATA; + } + else + { + float err = (float)norm( testImg, valImg, CV_RELATIVE_L1 ); + float Eps = 0.9f; + if( err > Eps) + { + ts->printf( CvTS::LOG, "CV_RELATIVE_L1 between testImg and valImg is equal %f (larger than %f)\n", err, Eps ); + code = CvTS::FAIL_BAD_ACCURACY; + } + else + { + code = checkLineIterator( testImg ); + } + } +#endif + ts->set_failed_test_info( code ); +} + +class CV_DrawingTest_CPP : public CV_DrawingTest +{ +public: + CV_DrawingTest_CPP() : CV_DrawingTest( "drawing_cpp" ) {} +protected: + virtual void draw( Mat& img ); + virtual int checkLineIterator( Mat& img); +}; + +void CV_DrawingTest_CPP::draw( Mat& img ) +{ + Size imgSize( 600, 400 ); + img.create( imgSize, CV_8UC3 ); + + vector polyline(4); + polyline[0] = Point(0, 0); + polyline[1] = Point(imgSize.width, 0); + polyline[2] = Point(imgSize.width, imgSize.height); + polyline[3] = Point(0, imgSize.height); + const Point* pts = &polyline[0]; + int n = (int)polyline.size(); + fillPoly( img, &pts, &n, 1, Scalar::all(255) ); + + Point p1(1,1), p2(3,3); + if( clipLine(Rect(0,0,imgSize.width,imgSize.height), p1, p2) && clipLine(imgSize, p1, p2) ) + circle( img, Point(300,100), 40, Scalar(0,0,255), 3 ); // draw + + p2 = Point(3,imgSize.height+1000); + if( clipLine(Rect(0,0,imgSize.width,imgSize.height), p1, p2) && clipLine(imgSize, p1, p2) ) + circle( img, Point(500,300), 50, cvColorToScalar(255,CV_8UC3), 5, 8, 1 ); // draw + + p1 = Point(imgSize.width,1), p2 = Point(imgSize.width,3); + if( clipLine(Rect(0,0,imgSize.width,imgSize.height), p1, p2) && clipLine(imgSize, p1, p2) ) + circle( img, Point(390,100), 10, Scalar(0,0,255), 3 ); // not draw + + p1 = Point(imgSize.width-1,1), p2 = Point(imgSize.width,3); + if( clipLine(Rect(0,0,imgSize.width,imgSize.height), p1, p2) && clipLine(imgSize, p1, p2) ) + ellipse( img, Point(390,100), Size(20,30), 60, 0, 220.0, Scalar(0,200,0), 4 ); //draw + + ellipse( img, RotatedRect(Point(100,200),Size(200,100),160), Scalar(200,200,255), 5 ); + + polyline.clear(); + ellipse2Poly( Point(430,180), Size(100,150), 30, 0, 150, 20, polyline ); + pts = &polyline[0]; + n = (int)polyline.size(); + polylines( img, &pts, &n, 1, false, Scalar(0,0,150), 4, CV_AA ); + n = 0; + for( vector::const_iterator it = polyline.begin(); n < (int)polyline.size()-1; ++it, n++ ) + { + line( img, *it, *(it+1), Scalar(50,250,100)); + } + + polyline.clear(); + ellipse2Poly( Point(500,300), Size(50,80), 0, 0, 180, 10, polyline ); + pts = &polyline[0]; + n = (int)polyline.size(); + polylines( img, &pts, &n, 1, true, Scalar(100,200,100), 20 ); + fillConvexPoly( img, pts, n, Scalar(0, 80, 0) ); + + polyline.resize(8); + // external rectengular + polyline[0] = Point(0, 0); + polyline[1] = Point(80, 0); + polyline[2] = Point(80, 80); + polyline[3] = Point(0, 80); + // internal rectangular + polyline[4] = Point(20, 20); + polyline[5] = Point(60, 20); + polyline[6] = Point(60, 60); + polyline[7] = Point(20, 60); + const Point* ppts[] = {&polyline[0], &polyline[0]+4}; + int pn[] = {4, 4}; + fillPoly( img, ppts, pn, 2, Scalar(100, 100, 0), 8, 0, Point(500, 20) ); + + rectangle( img, Point(0, 300), Point(50, 398), Scalar(0,0,255) ); + + string text1 = "OpenCV"; + int baseline = 0, thickness = 3, fontFace = FONT_HERSHEY_SCRIPT_SIMPLEX; + float fontScale = 2; + Size textSize = getTextSize( text1, fontFace, fontScale, thickness, &baseline); + baseline += thickness; + Point textOrg((img.cols - textSize.width)/2, (img.rows + textSize.height)/2); + rectangle(img, textOrg + Point(0, baseline), textOrg + Point(textSize.width, -textSize.height), Scalar(0,0,255)); + line(img, textOrg + Point(0, thickness), textOrg + Point(textSize.width, thickness), Scalar(0, 0, 255)); + putText(img, text1, textOrg, fontFace, fontScale, Scalar(150,0,150), thickness, 8); + + string text2 = "abcdefghijklmnopqrstuvwxyz1234567890"; + Scalar color(200,0,0); + fontScale = 0.5, thickness = 1; + int dist = 5; + + textSize = getTextSize( text2, FONT_HERSHEY_SIMPLEX, fontScale, thickness, &baseline); + textOrg = Point(5,5)+Point(0,textSize.height+dist); + putText(img, text2, textOrg, FONT_HERSHEY_SIMPLEX, fontScale, color, thickness, CV_AA); + + fontScale = 1; + textSize = getTextSize( text2, FONT_HERSHEY_PLAIN, fontScale, thickness, &baseline); + textOrg += Point(0,textSize.height+dist); + putText(img, text2, textOrg, FONT_HERSHEY_PLAIN, fontScale, color, thickness, CV_AA); + + fontScale = 0.5; + textSize = getTextSize( text2, FONT_HERSHEY_DUPLEX, fontScale, thickness, &baseline); + textOrg += Point(0,textSize.height+dist); + putText(img, text2, textOrg, FONT_HERSHEY_DUPLEX, fontScale, color, thickness, CV_AA); + + textSize = getTextSize( text2, FONT_HERSHEY_COMPLEX, fontScale, thickness, &baseline); + textOrg += Point(0,textSize.height+dist); + putText(img, text2, textOrg, FONT_HERSHEY_COMPLEX, fontScale, color, thickness, CV_AA); + + textSize = getTextSize( text2, FONT_HERSHEY_TRIPLEX, fontScale, thickness, &baseline); + textOrg += Point(0,textSize.height+dist); + putText(img, text2, textOrg, FONT_HERSHEY_TRIPLEX, fontScale, color, thickness, CV_AA); + + fontScale = 1; + textSize = getTextSize( text2, FONT_HERSHEY_COMPLEX_SMALL, fontScale, thickness, &baseline); + textOrg += Point(0,180) + Point(0,textSize.height+dist); + putText(img, text2, textOrg, FONT_HERSHEY_COMPLEX_SMALL, fontScale, color, thickness, CV_AA); + + textSize = getTextSize( text2, FONT_HERSHEY_SCRIPT_SIMPLEX, fontScale, thickness, &baseline); + textOrg += Point(0,textSize.height+dist); + putText(img, text2, textOrg, FONT_HERSHEY_SCRIPT_SIMPLEX, fontScale, color, thickness, CV_AA); + + textSize = getTextSize( text2, FONT_HERSHEY_SCRIPT_COMPLEX, fontScale, thickness, &baseline); + textOrg += Point(0,textSize.height+dist); + putText(img, text2, textOrg, FONT_HERSHEY_SCRIPT_COMPLEX, fontScale, color, thickness, CV_AA); + + dist = 15, fontScale = 0.5; + textSize = getTextSize( text2, FONT_ITALIC, fontScale, thickness, &baseline); + textOrg += Point(0,textSize.height+dist); + putText(img, text2, textOrg, FONT_ITALIC, fontScale, color, thickness, CV_AA); +} + +int CV_DrawingTest_CPP::checkLineIterator( Mat& img ) +{ + LineIterator it( img, Point(0,300), Point(1000, 300) ); + for(int i = 0; i < it.count; ++it, i++ ) + { + Vec3b v = (Vec3b)(*(*it)) - img.at(300,i); + float err = (float)norm( v ); + if( err != 0 ) + { + ts->printf( CvTS::LOG, "LineIterator works incorrect" ); + return CvTS::FAIL_INVALID_OUTPUT; + } + } + return CvTS::OK; +} + +class CV_DrawingTest_C : public CV_DrawingTest +{ +public: + CV_DrawingTest_C() : CV_DrawingTest( "drawing_c" ) {} +protected: + virtual void draw( Mat& img ); + virtual int checkLineIterator( Mat& img); +}; + +void CV_DrawingTest_C::draw( Mat& _img ) +{ + CvSize imgSize = cvSize(600, 400); + _img.create( imgSize, CV_8UC3 ); + CvMat img = _img; + + vector polyline(4); + polyline[0] = cvPoint(0, 0); + polyline[1] = cvPoint(imgSize.width, 0); + polyline[2] = cvPoint(imgSize.width, imgSize.height); + polyline[3] = cvPoint(0, imgSize.height); + CvPoint* pts = &polyline[0]; + int n = (int)polyline.size(); + cvFillPoly( &img, &pts, &n, 1, cvScalar(255,255,255) ); + + CvPoint p1 = cvPoint(1,1), p2 = cvPoint(3,3); + if( cvClipLine(imgSize, &p1, &p2) ) + cvCircle( &img, cvPoint(300,100), 40, cvScalar(0,0,255), 3 ); // draw + + p1 = cvPoint(1,1), p2 = cvPoint(3,imgSize.height+1000); + if( cvClipLine(imgSize, &p1, &p2) ) + cvCircle( &img, cvPoint(500,300), 50, cvScalar(255,0,0), 5, 8, 1 ); // draw + + p1 = cvPoint(imgSize.width,1), p2 = cvPoint(imgSize.width,3); + if( cvClipLine(imgSize, &p1, &p2) ) + cvCircle( &img, cvPoint(390,100), 10, cvScalar(0,0,255), 3 ); // not draw + + p1 = Point(imgSize.width-1,1), p2 = Point(imgSize.width,3); + if( cvClipLine(imgSize, &p1, &p2) ) + cvEllipse( &img, cvPoint(390,100), cvSize(20,30), 60, 0, 220.0, cvScalar(0,200,0), 4 ); //draw + + CvBox2D box; + box.center.x = 100; + box.center.y = 200; + box.size.width = 200; + box.size.height = 100; + box.angle = 160; + cvEllipseBox( &img, box, Scalar(200,200,255), 5 ); + + polyline.resize(9); + pts = &polyline[0]; + n = (int)polyline.size(); + assert( cvEllipse2Poly( cvPoint(430,180), cvSize(100,150), 30, 0, 150, &polyline[0], 20 ) == n ); + cvPolyLine( &img, &pts, &n, 1, false, cvScalar(0,0,150), 4, CV_AA ); + n = 0; + for( vector::const_iterator it = polyline.begin(); n < (int)polyline.size()-1; ++it, n++ ) + { + cvLine( &img, *it, *(it+1), cvScalar(50,250,100) ); + } + + polyline.resize(19); + pts = &polyline[0]; + n = (int)polyline.size(); + assert( cvEllipse2Poly( cvPoint(500,300), cvSize(50,80), 0, 0, 180, &polyline[0], 10 ) == n ); + cvPolyLine( &img, &pts, &n, 1, true, Scalar(100,200,100), 20 ); + cvFillConvexPoly( &img, pts, n, cvScalar(0, 80, 0) ); + + polyline.resize(8); + // external rectengular + polyline[0] = cvPoint(500, 20); + polyline[1] = cvPoint(580, 20); + polyline[2] = cvPoint(580, 100); + polyline[3] = cvPoint(500, 100); + // internal rectangular + polyline[4] = cvPoint(520, 40); + polyline[5] = cvPoint(560, 40); + polyline[6] = cvPoint(560, 80); + polyline[7] = cvPoint(520, 80); + CvPoint* ppts[] = {&polyline[0], &polyline[0]+4}; + int pn[] = {4, 4}; + cvFillPoly( &img, ppts, pn, 2, cvScalar(100, 100, 0), 8, 0 ); + + cvRectangle( &img, cvPoint(0, 300), cvPoint(50, 398), cvScalar(0,0,255) ); + + string text1 = "OpenCV"; + CvFont font; + cvInitFont( &font, FONT_HERSHEY_SCRIPT_SIMPLEX, 2, 2, 0, 3 ); + int baseline = 0; + CvSize textSize; + cvGetTextSize( text1.c_str(), &font, &textSize, &baseline ); + baseline += font.thickness; + CvPoint textOrg = cvPoint((imgSize.width - textSize.width)/2, (imgSize.height + textSize.height)/2); + cvRectangle( &img, cvPoint( textOrg.x, textOrg.y + baseline), + cvPoint(textOrg.x + textSize.width, textOrg.y - textSize.height), cvScalar(0,0,255)); + cvLine( &img, cvPoint(textOrg.x, textOrg.y + font.thickness), + cvPoint(textOrg.x + textSize.width, textOrg.y + font.thickness), cvScalar(0, 0, 255)); + cvPutText( &img, text1.c_str(), textOrg, &font, cvScalar(150,0,150) ); + + int dist = 5; + string text2 = "abcdefghijklmnopqrstuvwxyz1234567890"; + CvScalar color = cvScalar(200,0,0); + cvInitFont( &font, FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1, CV_AA ); + cvGetTextSize( text2.c_str(), &font, &textSize, &baseline ); + textOrg = cvPoint(5, 5+textSize.height+dist); + cvPutText(&img, text2.c_str(), textOrg, &font, color ); + + cvInitFont( &font, FONT_HERSHEY_PLAIN, 1, 1, 0, 1, CV_AA ); + cvGetTextSize( text2.c_str(), &font, &textSize, &baseline ); + textOrg = cvPoint(textOrg.x,textOrg.y+textSize.height+dist); + cvPutText(&img, text2.c_str(), textOrg, &font, color ); + + cvInitFont( &font, FONT_HERSHEY_DUPLEX, 0.5, 0.5, 0, 1, CV_AA ); + cvGetTextSize( text2.c_str(), &font, &textSize, &baseline ); + textOrg = cvPoint(textOrg.x,textOrg.y+textSize.height+dist); + cvPutText(&img, text2.c_str(), textOrg, &font, color ); + + cvInitFont( &font, FONT_HERSHEY_COMPLEX, 0.5, 0.5, 0, 1, CV_AA ); + cvGetTextSize( text2.c_str(), &font, &textSize, &baseline ); + textOrg = cvPoint(textOrg.x,textOrg.y+textSize.height+dist); + cvPutText(&img, text2.c_str(), textOrg, &font, color ); + + cvInitFont( &font, FONT_HERSHEY_TRIPLEX, 0.5, 0.5, 0, 1, CV_AA ); + cvGetTextSize( text2.c_str(), &font, &textSize, &baseline ); + textOrg = cvPoint(textOrg.x,textOrg.y+textSize.height+dist); + cvPutText(&img, text2.c_str(), textOrg, &font, color ); + + cvInitFont( &font, FONT_HERSHEY_COMPLEX_SMALL, 1, 1, 0, 1, CV_AA ); + cvGetTextSize( text2.c_str(), &font, &textSize, &baseline ); + textOrg = cvPoint(textOrg.x,textOrg.y+textSize.height+dist + 180); + cvPutText(&img, text2.c_str(), textOrg, &font, color ); + + cvInitFont( &font, FONT_HERSHEY_SCRIPT_SIMPLEX, 1, 1, 0, 1, CV_AA ); + cvGetTextSize( text2.c_str(), &font, &textSize, &baseline ); + textOrg = cvPoint(textOrg.x,textOrg.y+textSize.height+dist); + cvPutText(&img, text2.c_str(), textOrg, &font, color ); + + cvInitFont( &font, FONT_HERSHEY_SCRIPT_COMPLEX, 1, 1, 0, 1, CV_AA ); + cvGetTextSize( text2.c_str(), &font, &textSize, &baseline ); + textOrg = cvPoint(textOrg.x,textOrg.y+textSize.height+dist); + cvPutText(&img, text2.c_str(), textOrg, &font, color ); + + dist = 15; + cvInitFont( &font, FONT_ITALIC, 0.5, 0.5, 0, 1, CV_AA ); + cvGetTextSize( text2.c_str(), &font, &textSize, &baseline ); + textOrg = cvPoint(textOrg.x,textOrg.y+textSize.height+dist); + cvPutText(&img, text2.c_str(), textOrg, &font, color ); +} + +int CV_DrawingTest_C::checkLineIterator( Mat& _img ) +{ + CvLineIterator it; + CvMat img = _img; + int count = cvInitLineIterator( &img, cvPoint(0,300), cvPoint(1000, 300), &it ); + for(int i = 0; i < count; i++ ) + { + Vec3b v = (Vec3b)(*(it.ptr)) - _img.at(300,i); + float err = (float)norm( v ); + if( err != 0 ) + { + ts->printf( CvTS::LOG, "CvLineIterator works incorrect" ); + return CvTS::FAIL_INVALID_OUTPUT; + } + CV_NEXT_LINE_POINT(it); + } + return CvTS::OK; +} + +CV_DrawingTest_CPP drawing_test_cpp; +CV_DrawingTest_C drawing_test_c; diff --git a/modules/highgui/test/test_gui.cpp b/modules/highgui/test/test_gui.cpp new file mode 100644 index 000000000..59c0f0e90 --- /dev/null +++ b/modules/highgui/test/test_gui.cpp @@ -0,0 +1,107 @@ +/*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 "cvtest.h" +#include +#include +#include +#include +#include +#include "cvaux.h" + +using namespace cv; +using namespace std; + +//#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64 +#define MARKERS + +#ifdef MARKERS + #define marker(x) cout << (x) << endl +#else + #define marker(x) +#endif + + +class CV_HighGuiOnlyGuiTest : public CvTest +{ +public: + CV_HighGuiOnlyGuiTest(); + ~CV_HighGuiOnlyGuiTest(); +protected: + void run(int); +}; + +CV_HighGuiOnlyGuiTest::CV_HighGuiOnlyGuiTest(): CvTest( "z-highgui-gui-only", "?" ) +{ + support_testing_modes = CvTS::CORRECTNESS_CHECK_MODE; +} +CV_HighGuiOnlyGuiTest::~CV_HighGuiOnlyGuiTest() {} + +void Foo(int /*k*/, void* /*z*/) {} + +void CV_HighGuiOnlyGuiTest::run( int /*start_from */) +{ + cout << "GUI 1" << endl; + namedWindow("Win"); + cout << "GUI 2" << endl; + Mat m(30, 30, CV_8U); + m = Scalar(128); + cout << "GUI 3" << endl; + imshow("Win", m); + cout << "GUI 4" << endl; + int value = 50; + cout << "GUI 5" << endl; + createTrackbar( "trackbar", "Win", &value, 100, Foo, &value); + cout << "GUI 6" << endl; + getTrackbarPos( "trackbar", "Win" ); + cout << "GUI 7" << endl; + waitKey(500); + cout << "GUI 8" << endl; + cvDestroyAllWindows(); + cout << "GUI 9" << endl; + + ts->set_failed_test_info(CvTS::OK); +} + +CV_HighGuiOnlyGuiTest highGuiOnlyGui_test; + + diff --git a/modules/highgui/test/test_main.cpp b/modules/highgui/test/test_main.cpp new file mode 100644 index 000000000..db32ab1ee --- /dev/null +++ b/modules/highgui/test/test_main.cpp @@ -0,0 +1,2 @@ +#include "test_precomp.hpp" +#include "opencv2/ts/ts_main.hpp" diff --git a/modules/highgui/test/test_precomp.cpp b/modules/highgui/test/test_precomp.cpp new file mode 100644 index 000000000..5956e13e3 --- /dev/null +++ b/modules/highgui/test/test_precomp.cpp @@ -0,0 +1 @@ +#include "test_precomp.hpp" diff --git a/modules/highgui/test/test_precomp.hpp b/modules/highgui/test/test_precomp.hpp new file mode 100644 index 000000000..7d837ce41 --- /dev/null +++ b/modules/highgui/test/test_precomp.hpp @@ -0,0 +1,9 @@ +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/core/imgproc_c.h" +#include + +#endif diff --git a/modules/highgui/test/test_video_io.cpp b/modules/highgui/test/test_video_io.cpp new file mode 100644 index 000000000..de2c895f2 --- /dev/null +++ b/modules/highgui/test/test_video_io.cpp @@ -0,0 +1,314 @@ +/*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 +#include +#include +#include + +using namespace cv; +using namespace std; + +#if defined WIN32 || defined _WIN32 +//#if 0 + +#else + +#define MARKERS1 + +#ifdef MARKERS + #define marker(x) cout << (x) << endl +#else + #define marker(x) +#endif + +struct TempDirHolder +{ + string temp_folder; + TempDirHolder() + { + char* p = tmpnam(0); + if(p[0] == '\\') p++; + temp_folder = string(p); + exec_cmd("mkdir " + temp_folder); + } + ~TempDirHolder() { exec_cmd("rm -rf " + temp_folder); } + static void exec_cmd(const string& cmd) { marker(cmd); int res = system( cmd.c_str() ); (void)res; } + + TempDirHolder& operator=(const TempDirHolder&); +}; + + +class CV_HighGuiTest : public CvTest +{ +public: + CV_HighGuiTest(); + ~CV_HighGuiTest(); +protected: + void run(int); + + bool ImagesTest(const string& dir, const string& tmp); + bool VideoTest(const string& dir, const string& tmp, int fourcc); + + bool GuiTest(const string& dir, const string& tmp); +}; + +CV_HighGuiTest::CV_HighGuiTest(): CvTest( "z-highgui", "?" ) +{ + support_testing_modes = CvTS::CORRECTNESS_CHECK_MODE; +} +CV_HighGuiTest::~CV_HighGuiTest() {} + +double PSNR(const Mat& m1, const Mat& m2) +{ + Mat tmp; + absdiff( m1.reshape(1), m2.reshape(1), tmp); + multiply(tmp, tmp, tmp); + + double MSE = 1.0/(tmp.cols * tmp.rows) * sum(tmp)[0]; + + return 20 * log10(255.0 / sqrt(MSE)); +} + +bool CV_HighGuiTest::ImagesTest(const string& dir, const string& tmp) +{ + int code = CvTS::OK; + Mat image = imread(dir + "shared/baboon.jpg"); + + if (image.empty()) + { + ts->set_failed_test_info(CvTS::FAIL_MISSING_TEST_DATA); + return false; + } + + const string exts[] = {"png", "bmp", "tiff", "jpg", "jp2", "ppm", "ras"}; + const size_t ext_num = sizeof(exts)/sizeof(exts[0]); + + for(size_t i = 0; i < ext_num; ++i) + { + ts->printf(CvTS::LOG, "ext=%s\n", exts[i].c_str()); + string ext = exts[i]; + string full_name = tmp + "/img." + ext; + marker(exts[i]); + + imwrite(full_name, image); + Mat loaded = imread(full_name); + if (loaded.empty()) + { + ts->printf(CvTS::LOG, "Reading failed at fmt=%s\n", ext.c_str()); + code = CvTS::FAIL_MISMATCH; + continue; + } + + const double thresDbell = 20; + double psnr = PSNR(loaded, image); + if (psnr < thresDbell) + { + ts->printf(CvTS::LOG, "Reading image from file: too big difference (=%g) with fmt=%s\n", psnr, ext.c_str()); + code = CvTS::FAIL_BAD_ACCURACY; + continue; + } + + FILE *f = fopen(full_name.c_str(), "rb"); + fseek(f, 0, SEEK_END); + size_t len = ftell(f); + vector from_file(len); + fseek(f, 0, SEEK_SET); + size_t read = fread(&from_file[0], len, sizeof(vector::value_type), f); (void)read; + fclose(f); + + + vector buf; + imencode("." + exts[i], image, buf); + + if (buf != from_file) + { + ts->printf(CvTS::LOG, "Encoding failed with fmt=%s\n", ext.c_str()); + code = CvTS::FAIL_MISMATCH; + continue; + } + + Mat buf_loaded = imdecode(Mat(buf), 1); + if (buf_loaded.empty()) + { + ts->printf(CvTS::LOG, "Decoding failed with fmt=%s\n", ext.c_str()); + code = CvTS::FAIL_MISMATCH; + continue; + } + + + psnr = PSNR(buf_loaded, image); + if (psnr < thresDbell) + { + ts->printf(CvTS::LOG, "Decoding image from memory: too small PSNR (=%gdb) with fmt=%s\n", psnr, ext.c_str()); + code = CvTS::FAIL_MISMATCH; + continue; + } + } + ts->set_failed_test_info(code); + return code == CvTS::OK; +} + +bool CV_HighGuiTest::VideoTest(const string& dir, const string& tmp, int fourcc) +{ + string src_file = dir + "shared/video_for_test.avi"; + string tmp_name = tmp + "/video.avi"; + + CvCapture* cap = cvCaptureFromFile(src_file.c_str()); + + if (!cap) + { + ts->set_failed_test_info(CvTS::FAIL_MISMATCH); + return false; + } + + CvVideoWriter* writer = 0; + + int counter = 0; + for(;;) + { + IplImage* img = cvQueryFrame( cap ); + + if (!img) + break; + + if (writer == 0) + { + writer = cvCreateVideoWriter(tmp_name.c_str(), fourcc, 24, cvGetSize(img)); + if (writer == 0) + { + marker("can't craete writer"); + cvReleaseCapture( &cap ); + ts->set_failed_test_info(CvTS::FAIL_MISMATCH); + return false; + } + } + + cvWriteFrame(writer, img); + } + + + cvReleaseVideoWriter( &writer ); + cvReleaseCapture( &cap ); + + marker("mid++"); + + cap = cvCaptureFromFile(src_file.c_str()); + marker("mid1"); + CvCapture *saved = cvCaptureFromFile(tmp_name.c_str()); + if (!saved) + { + ts->set_failed_test_info(CvTS::FAIL_MISMATCH); + return false; + } + + + const double thresDbell = 20; + + bool error = false; + counter = 0; + for(;;) + { + + IplImage* ipl = cvQueryFrame( cap ); + IplImage* ipl1 = cvQueryFrame( saved ); + + + if (!ipl || !ipl1) + break; + + Mat img(ipl); + Mat img1(ipl1); + + if (PSNR(img1, img) < thresDbell) + { + error = true; + break; + } + } + + cvReleaseCapture( &cap ); + cvReleaseCapture( &saved ); + + if (error) + { + ts->set_failed_test_info(CvTS::FAIL_MISMATCH); + return false; + } + + return true; +} + + +void CV_HighGuiTest::run( int /*start_from */) +{ + TempDirHolder th; + + if (!ImagesTest(ts->get_data_path(), th.temp_folder)) + return; + +#if defined WIN32 || defined __linux__ + +#if !defined HAVE_GSTREAMER || defined HAVE_GSTREAMER_APP + if (!VideoTest(ts->get_data_path(), th.temp_folder, CV_FOURCC_DEFAULT)) + return; + + + if (!VideoTest(ts->get_data_path(), th.temp_folder, CV_FOURCC('M', 'J', 'P', 'G'))) + return; + + + if (!VideoTest(ts->get_data_path(), th.temp_folder, CV_FOURCC('M', 'P', 'G', '2'))) + return; + +#endif + //if (!VideoTest(ts->get_data_path(), th.temp_folder, CV_FOURCC('D', 'X', '5', '0'))) return; +#endif + ts->set_failed_test_info(CvTS::OK); +} +CV_HighGuiTest HighGui_test; + + +#endif + diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index 8fc98576f..7f81b0d8c 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -666,6 +666,10 @@ CV_EXPORTS double compareHist( const SparseMat& H1, const SparseMat& H2, int met //! normalizes the grayscale image brightness and contrast by normalizing its histogram CV_EXPORTS_W void equalizeHist( const Mat& src, CV_OUT Mat& dst ); + +CV_EXPORTS float EMD( const Mat& signature1, const Mat& signature2, + int distType, const Mat& cost=Mat(), + float* lowerBound=0, Mat* flow=0 ); //! segments the image using watershed algorithm CV_EXPORTS_W void watershed( const Mat& image, Mat& markers ); diff --git a/modules/imgproc/src/emd.cpp b/modules/imgproc/src/emd.cpp index e6be13ed8..d6cbf804c 100644 --- a/modules/imgproc/src/emd.cpp +++ b/modules/imgproc/src/emd.cpp @@ -1137,5 +1137,23 @@ icvDistC( const float *x, const float *y, void *user_param ) return (float)s; } -/* End of file. */ +namespace cv +{ + +float EMD( const Mat& signature1, const Mat& signature2, + int distType, const Mat& cost, float* lowerBound, Mat* flow ) +{ + CvMat _signature1 = signature1; + CvMat _signature2 = signature2; + CvMat _cost = cost, _flow; + if( flow ) + _flow = *flow; + + return cvCalcEMD2( &_signature1, &_signature2, distType, 0, cost.empty() ? 0 : &_cost, + flow ? &_flow : 0, lowerBound, 0 ); +} + +} + +/* End of file. */ diff --git a/modules/imgproc/src/filter.cpp b/modules/imgproc/src/filter.cpp index 9d5fda6df..27fb2a03e 100644 --- a/modules/imgproc/src/filter.cpp +++ b/modules/imgproc/src/filter.cpp @@ -3025,8 +3025,8 @@ void sepFilter2D( const Mat& src, Mat& dst, int ddepth, dst.create( src.size(), CV_MAKETYPE(ddepth, src.channels()) ); Ptr f = createSeparableLinearFilter(src.type(), - dst.type(), kernelX, kernelY, anchor, delta, borderType ); - f->apply(src, dst); + dst.type(), kernelX, kernelY, anchor, delta, borderType & ~BORDER_ISOLATED ); + f->apply(src, dst, Rect(0,0,-1,-1), Point(), (borderType & BORDER_ISOLATED) != 0 ); } } diff --git a/modules/imgproc/src/pyramids.cpp b/modules/imgproc/src/pyramids.cpp index 5408c3a44..8e5cb8242 100644 --- a/modules/imgproc/src/pyramids.cpp +++ b/modules/imgproc/src/pyramids.cpp @@ -45,8 +45,6 @@ namespace cv { -//#undef CV_SSE2 - template struct FixPtCast { typedef int type1; diff --git a/modules/imgproc/src/undistort.cpp b/modules/imgproc/src/undistort.cpp index 6bd05abe7..de328e547 100644 --- a/modules/imgproc/src/undistort.cpp +++ b/modules/imgproc/src/undistort.cpp @@ -380,7 +380,7 @@ void undistortPoints( const Mat& src, Mat& dst, const Mat& cameraMatrix, const Mat& distCoeffs, const Mat& R, const Mat& P ) { - CV_Assert( src.isContinuous() && src.depth() == CV_32F && + CV_Assert( src.isContinuous() && (src.depth() == CV_32F || src.depth() == CV_64F) && ((src.rows == 1 && src.channels() == 2) || src.cols*src.channels() == 2)); dst.create(src.size(), src.type()); diff --git a/modules/imgproc/test/test_approxpoly.cpp b/modules/imgproc/test/test_approxpoly.cpp new file mode 100644 index 000000000..be1751a0a --- /dev/null +++ b/modules/imgproc/test/test_approxpoly.cpp @@ -0,0 +1,359 @@ +/*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 + +using namespace cv; +using namespace std; + +// +// TODO!!!: +// check_slice (and/or check) seem(s) to be broken, or this is a bug in function +// (or its inability to handle possible self-intersections in the generated contours). +// +// At least, if // return TotalErrors; +// is uncommented in check_slice, the test fails easily. +// So, now (and it looks like since 0.9.6) +// we only check that the set of vertices of the approximated polygon is +// a subset of vertices of the original contour. +// + +class CV_ApproxPolyTest : public cvtest::BaseTest +{ +public: + CV_ApproxPolyTest(); + ~CV_ApproxPolyTest(); + void clear(); + //int write_default_params(CvFileStorage* fs); + +protected: + //int read_params( CvFileStorage* fs ); + + int check_slice( CvPoint StartPt, CvPoint EndPt, + CvSeqReader* SrcReader, float Eps, + int* j, int Count ); + int check( CvSeq* SrcSeq, CvSeq* DstSeq, float Eps ); + + bool get_contour( int /*type*/, CvSeq** Seq, int* d, + CvMemStorage* storage ); + + void run(int); +}; + + +CV_ApproxPolyTest::CV_ApproxPolyTest() +{ +} + + +CV_ApproxPolyTest::~CV_ApproxPolyTest() +{ + clear(); +} + + +void CV_ApproxPolyTest::clear() +{ + cvtest::BaseTest::clear(); +} + + +/*int CV_ApproxPolyTest::write_default_params( CvFileStorage* fs ) +{ + cvtest::BaseTest::write_default_params( fs ); + if( ts->get_testing_mode() != cvtest::TS::TIMING_MODE ) + { + write_param( fs, "test_case_count", test_case_count ); + } + return 0; +} + + +int CV_ApproxPolyTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::BaseTest::read_params( fs ); + if( code < 0 ) + return code; + + test_case_count = cvReadInt( find_param( fs, "test_case_count" ), test_case_count ); + min_log_size = cvtest::clipInt( min_log_size, 1, 10 ); + return 0; +}*/ + + +bool CV_ApproxPolyTest::get_contour( int /*type*/, CvSeq** Seq, int* d, + CvMemStorage* storage ) +{ + RNG& rng = ts->get_rng(); + int max_x = INT_MIN, max_y = INT_MIN, min_x = INT_MAX, min_y = INT_MAX; + int i; + CvSeq* seq; + int total = cvtest::randInt(rng) % 1000 + 1; + CvPoint center; + int radius, angle; + double deg_to_rad = CV_PI/180.; + CvPoint pt; + + center.x = cvtest::randInt( rng ) % 1000; + center.y = cvtest::randInt( rng ) % 1000; + radius = cvtest::randInt( rng ) % 1000; + angle = cvtest::randInt( rng ) % 360; + + seq = cvCreateSeq( CV_SEQ_POLYGON, sizeof(CvContour), sizeof(CvPoint), storage ); + + for( i = 0; i < total; i++ ) + { + int d_radius = cvtest::randInt( rng ) % 10 - 5; + int d_angle = 360/total;//cvtest::randInt( rng ) % 10 - 5; + pt.x = cvRound( center.x + radius*cos(angle*deg_to_rad)); + pt.y = cvRound( center.x - radius*sin(angle*deg_to_rad)); + radius += d_radius; + angle += d_angle; + cvSeqPush( seq, &pt ); + + max_x = MAX( max_x, pt.x ); + max_y = MAX( max_y, pt.y ); + + min_x = MIN( min_x, pt.x ); + min_y = MIN( min_y, pt.y ); + } + + *d = (max_x - min_x)*(max_x - min_x) + (max_y - min_y)*(max_y - min_y); + *Seq = seq; + return true; +} + + +int CV_ApproxPolyTest::check_slice( CvPoint StartPt, CvPoint EndPt, + CvSeqReader* SrcReader, float Eps, + int* _j, int Count ) +{ + /////////// + CvPoint Pt; + /////////// + bool flag; + double dy,dx; + double A,B,C; + double Sq; + double sin_a = 0; + double cos_a = 0; + double d = 0; + double dist; + /////////// + int j, TotalErrors = 0; + + //////////////////////////////// + if( SrcReader == NULL ) + { + assert( false ); + return 0; + } + + ///////// init line //////////// + flag = true; + + dx = (double)StartPt.x - (double)EndPt.x; + dy = (double)StartPt.y - (double)EndPt.y; + + if( ( dx == 0 ) && ( dy == 0 ) ) flag = false; + else + { + A = -dy; + B = dx; + C = dy * (double)StartPt.x - dx * (double)StartPt.y; + Sq = sqrt( A*A + B*B ); + + sin_a = B/Sq; + cos_a = A/Sq; + d = C/Sq; + } + + /////// find start point and check distance //////// + for( j = *_j; j < Count; j++ ) + { + CV_READ_SEQ_ELEM( Pt, *SrcReader ); + if( StartPt.x == Pt.x && StartPt.y == Pt.y ) break; + else + { + if( flag ) dist = sin_a * Pt.y + cos_a * Pt.x - d; + else dist = sqrt( (double)(EndPt.y - Pt.y)*(EndPt.y - Pt.y) + (EndPt.x - Pt.x)*(EndPt.x - Pt.x) ); + if( dist > Eps ) TotalErrors++; + } + } + + *_j = j; + + //return TotalErrors; + return 0; +} + + +int CV_ApproxPolyTest::check( CvSeq* SrcSeq, CvSeq* DstSeq, float Eps ) +{ + ////////// + CvSeqReader DstReader; + CvSeqReader SrcReader; + CvPoint StartPt, EndPt; + /////////// + int TotalErrors = 0; + /////////// + int Count; + int i,j; + + assert( SrcSeq && DstSeq ); + + ////////// init //////////////////// + Count = SrcSeq->total; + + cvStartReadSeq( DstSeq, &DstReader, 0 ); + cvStartReadSeq( SrcSeq, &SrcReader, 0 ); + + CV_READ_SEQ_ELEM( StartPt, DstReader ); + for( i = 0 ; i < Count ; ) + { + CV_READ_SEQ_ELEM( EndPt, SrcReader ); + i++; + if( StartPt.x == EndPt.x && StartPt.y == EndPt.y ) break; + } + + ///////// start //////////////// + for( i = 1, j = 0 ; i <= DstSeq->total ; ) + { + ///////// read slice //////////// + EndPt.x = StartPt.x; + EndPt.y = StartPt.y; + CV_READ_SEQ_ELEM( StartPt, DstReader ); + i++; + + TotalErrors += check_slice( StartPt, EndPt, &SrcReader, Eps, &j, Count ); + + if( j > Count ) + { + TotalErrors++; + return TotalErrors; + } //if( !flag ) + + } // for( int i = 0 ; i < DstSeq->total ; i++ ) + + return TotalErrors; +} + + +//extern CvTestContourGenerator cvTsTestContours[]; + +void CV_ApproxPolyTest::run( int /*start_from*/ ) +{ + int code = cvtest::TS::OK; + CvMemStorage* storage = 0; + ////////////// Variables //////////////// + int IntervalsCount = 10; + /////////// + //CvTestContourGenerator Cont; + CvSeq* SrcSeq = NULL; + CvSeq* DstSeq; + int iDiam; + float dDiam, Eps, EpsStep; + + for( int i = 0; i < 30; i++ ) + { + CvMemStoragePos pos; + + ts->update_context( this, i, false ); + + ///////////////////// init contour ///////// + dDiam = 0; + while( sqrt(dDiam) / IntervalsCount == 0 ) + { + if( storage != 0 ) + cvReleaseMemStorage(&storage); + + storage = cvCreateMemStorage( 0 ); + if( get_contour( 0, &SrcSeq, &iDiam, storage ) ) + dDiam = (float)iDiam; + } + dDiam = (float)sqrt( dDiam ); + + storage = SrcSeq->storage; + + ////////////////// test ///////////// + EpsStep = dDiam / IntervalsCount ; + for( Eps = EpsStep ; Eps < dDiam ; Eps += EpsStep ) + { + cvSaveMemStoragePos( storage, &pos ); + + ////////// call function //////////// + DstSeq = cvApproxPoly( SrcSeq, SrcSeq->header_size, storage, + CV_POLY_APPROX_DP, Eps ); + + if( DstSeq == NULL ) + { + ts->printf( cvtest::TS::LOG, + "cvApproxPoly returned NULL for contour #%d, espilon = %g\n", i, Eps ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } // if( DstSeq == NULL ) + + code = check( SrcSeq, DstSeq, Eps ); + if( code != 0 ) + { + ts->printf( cvtest::TS::LOG, + "Incorrect result for the contour #%d approximated with epsilon=%g\n", i, Eps ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + + cvRestoreMemStoragePos( storage, &pos ); + } // for( Eps = EpsStep ; Eps <= Diam ; Eps += EpsStep ) + + ///////////// free memory /////////////////// + cvReleaseMemStorage(&storage); + } // for( int i = 0; NULL != ( Cont = Contours[i] ) ; i++ ) + +_exit_: + cvReleaseMemStorage(&storage); + + if( code < 0 ) + ts->set_failed_test_info( code ); +} + +TEST(Imgproc_ApproxPoly, accuracy) { CV_ApproxPolyTest test; test.safe_run(); } + diff --git a/modules/imgproc/test/test_canny.cpp b/modules/imgproc/test/test_canny.cpp new file mode 100644 index 000000000..c07c76f37 --- /dev/null +++ b/modules/imgproc/test/test_canny.cpp @@ -0,0 +1,287 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_CannyTest : public cvtest::ArrayTest +{ +public: + CV_CannyTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + int prepare_test_case( int test_case_idx ); + void run_func(); + void prepare_to_validation( int ); + int validate_test_results( int /*test_case_idx*/ ); + + int aperture_size; + bool use_true_gradient; + double threshold1, threshold2; + bool test_cpp; +}; + + +CV_CannyTest::CV_CannyTest() +{ + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + element_wise_relative_error = true; + aperture_size = 0; + use_true_gradient = false; + threshold1 = threshold2 = 0; + + test_cpp = false; +} + + +void CV_CannyTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, + vector >& types ) +{ + RNG& rng = ts->get_rng(); + double thresh_range; + + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_8U; + + aperture_size = cvtest::randInt(rng) % 2 ? 5 : 3; + thresh_range = aperture_size == 3 ? 300 : 1000; + + threshold1 = cvtest::randReal(rng)*thresh_range; + threshold2 = cvtest::randReal(rng)*thresh_range*0.3; + + if( cvtest::randInt(rng) % 2 ) + CV_SWAP( threshold1, threshold2, thresh_range ); + + use_true_gradient = cvtest::randInt(rng) % 2 != 0; + test_cpp = (cvtest::randInt(rng) & 256) == 0; +} + + +int CV_CannyTest::prepare_test_case( int test_case_idx ) +{ + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + Mat& src = test_mat[INPUT][0]; + GaussianBlur(src, src, Size(11, 11), 5, 5); + } + + return code; +} + + +double CV_CannyTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return 0; +} + + +void CV_CannyTest::run_func() +{ + if(!test_cpp) + cvCanny( test_array[INPUT][0], test_array[OUTPUT][0], threshold1, threshold2, + aperture_size + (use_true_gradient ? CV_CANNY_L2_GRADIENT : 0)); + else + { + cv::Mat _out = cv::cvarrToMat(test_array[OUTPUT][0]); + cv::Canny(cv::cvarrToMat(test_array[INPUT][0]), _out, threshold1, threshold2, + aperture_size + (use_true_gradient ? CV_CANNY_L2_GRADIENT : 0)); + } +} + + +static void +cannyFollow( int x, int y, float lowThreshold, const Mat& mag, Mat& dst ) +{ + static const int ofs[][2] = {{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1}}; + int i; + + dst.at(y, x) = (uchar)255; + + for( i = 0; i < 8; i++ ) + { + int x1 = x + ofs[i][0]; + int y1 = y + ofs[i][1]; + if( (unsigned)x1 < (unsigned)mag.cols && + (unsigned)y1 < (unsigned)mag.rows && + mag.at(y1, x1) > lowThreshold && + !dst.at(y1, x1) ) + cannyFollow( x1, y1, lowThreshold, mag, dst ); + } +} + + +static void +test_Canny( const Mat& src, Mat& dst, + double threshold1, double threshold2, + int aperture_size, bool use_true_gradient ) +{ + int m = aperture_size; + Point anchor(m/2, m/2); + const double tan_pi_8 = tan(CV_PI/8.); + const double tan_3pi_8 = tan(CV_PI*3/8); + float lowThreshold = (float)MIN(threshold1, threshold2); + float highThreshold = (float)MAX(threshold1, threshold2); + + int x, y, width = src.cols, height = src.rows; + + Mat dxkernel = cvtest::calcSobelKernel2D( 1, 0, m, 0 ); + Mat dykernel = cvtest::calcSobelKernel2D( 0, 1, m, 0 ); + Mat dx, dy, mag(height, width, CV_32F); + cvtest::filter2D(src, dx, CV_16S, dxkernel, anchor, 0, BORDER_REPLICATE); + cvtest::filter2D(src, dy, CV_16S, dykernel, anchor, 0, BORDER_REPLICATE); + + // calc gradient magnitude + for( y = 0; y < height; y++ ) + { + for( x = 0; x < width; x++ ) + { + int dxval = dx.at(y, x), dyval = dy.at(y, x); + mag.at(y, x) = use_true_gradient ? + (float)sqrt((double)(dxval*dxval + dyval*dyval)) : + (float)(fabs(dxval) + fabs(dyval)); + } + } + + // calc gradient direction, do nonmaxima suppression + for( y = 0; y < height; y++ ) + { + for( x = 0; x < width; x++ ) + { + + float a = mag.at(y, x), b = 0, c = 0; + int y1 = 0, y2 = 0, x1 = 0, x2 = 0; + + if( a <= lowThreshold ) + continue; + + int dxval = dx.at(y, x); + int dyval = dy.at(y, x); + + double tg = dxval ? (double)dyval/dxval : DBL_MAX*CV_SIGN(dyval); + + if( fabs(tg) < tan_pi_8 ) + { + y1 = y2 = y; x1 = x + 1; x2 = x - 1; + } + else if( tan_pi_8 <= tg && tg <= tan_3pi_8 ) + { + y1 = y + 1; y2 = y - 1; x1 = x + 1; x2 = x - 1; + } + else if( -tan_3pi_8 <= tg && tg <= -tan_pi_8 ) + { + y1 = y - 1; y2 = y + 1; x1 = x + 1; x2 = x - 1; + } + else + { + assert( fabs(tg) > tan_3pi_8 ); + x1 = x2 = x; y1 = y + 1; y2 = y - 1; + } + + if( (unsigned)y1 < (unsigned)height && (unsigned)x1 < (unsigned)width ) + b = (float)fabs(mag.at(y1, x1)); + + if( (unsigned)y2 < (unsigned)height && (unsigned)x2 < (unsigned)width ) + c = (float)fabs(mag.at(y2, x2)); + + if( (a > b || (a == b && ((x1 == x+1 && y1 == y) || (x1 == x && y1 == y+1)))) && a > c ) + ; + else + mag.at(y, x) = -a; + } + } + + dst = Scalar::all(0); + + // hysteresis threshold + for( y = 0; y < height; y++ ) + { + for( x = 0; x < width; x++ ) + if( mag.at(y, x) > highThreshold && !dst.at(y, x) ) + cannyFollow( x, y, lowThreshold, mag, dst ); + } +} + + +void CV_CannyTest::prepare_to_validation( int ) +{ + Mat src = test_mat[INPUT][0], dst = test_mat[REF_OUTPUT][0]; + test_Canny( src, dst, threshold1, threshold2, aperture_size, use_true_gradient ); +} + + +int CV_CannyTest::validate_test_results( int test_case_idx ) +{ + int code = cvtest::TS::OK, nz0; + prepare_to_validation(test_case_idx); + + double err = cvtest::norm(test_mat[OUTPUT][0], test_mat[REF_OUTPUT][0], CV_L1); + if( err == 0 ) + return code; + + if( err != cvRound(err) || cvRound(err)%255 != 0 ) + { + ts->printf( cvtest::TS::LOG, "Some of the pixels, produced by Canny, are not 0's or 255's; the difference is %g\n", err ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return code; + } + + nz0 = cvRound(cvtest::norm(test_mat[REF_OUTPUT][0], CV_L1)/255); + err = (err/255/MAX(nz0,100))*100; + if( err > 1 ) + { + ts->printf( cvtest::TS::LOG, "Too high percentage of non-matching edge pixels = %g%%\n", err); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + } + + return code; +} + +TEST(Imgproc_Canny, accuracy) { CV_CannyTest test; test.safe_run(); } + +/* End of file. */ diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp new file mode 100644 index 000000000..d7a4c3466 --- /dev/null +++ b/modules/imgproc/test/test_color.cpp @@ -0,0 +1,1645 @@ +/*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" + +using namespace cv; +using namespace std; + +/////////////////////////// base test class for color transformations ///////////////////////// + +class CV_ColorCvtBaseTest : public cvtest::ArrayTest +{ +public: + CV_ColorCvtBaseTest( bool custom_inv_transform, bool allow_32f, bool allow_16u ); + +protected: + int prepare_test_case( int test_case_idx ); + void prepare_to_validation( int /*test_case_idx*/ ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); + + // input --- fwd_transform -> ref_output[0] + virtual void convert_forward( const Mat& src, Mat& dst ); + // ref_output[0] --- inv_transform ---> ref_output[1] (or input -- copy --> ref_output[1]) + virtual void convert_backward( const Mat& src, const Mat& dst, Mat& dst2 ); + + // called from default implementation of convert_forward + virtual void convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ); + + // called from default implementation of convert_backward + virtual void convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ); + + const char* fwd_code_str; + const char* inv_code_str; + + void run_func(); + bool allow_16u, allow_32f; + int blue_idx; + bool inplace; + bool custom_inv_transform; + int fwd_code, inv_code; + bool test_cpp; + bool hue_channel; +}; + + +CV_ColorCvtBaseTest::CV_ColorCvtBaseTest( bool _custom_inv_transform, bool _allow_32f, bool _allow_16u ) +{ + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + allow_16u = _allow_16u; + allow_32f = _allow_32f; + custom_inv_transform = _custom_inv_transform; + fwd_code = inv_code = -1; + element_wise_relative_error = false; + + fwd_code_str = inv_code_str = 0; + + test_cpp = false; + hue_channel = false; +} + + +void CV_ColorCvtBaseTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) +{ + cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high ); + if( i == INPUT ) + { + int depth = CV_MAT_DEPTH(type); + low = Scalar::all(0.); + high = Scalar::all( depth == CV_8U ? 256 : depth == CV_16U ? 65536 : 1. ); + } +} + + +void CV_ColorCvtBaseTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int depth, cn; + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + if( allow_16u && allow_32f ) + { + depth = cvtest::randInt(rng) % 3; + depth = depth == 0 ? CV_8U : depth == 1 ? CV_16U : CV_32F; + } + else if( allow_16u || allow_32f ) + { + depth = cvtest::randInt(rng) % 2; + depth = depth == 0 ? CV_8U : allow_16u ? CV_16U : CV_32F; + } + else + depth = CV_8U; + + cn = (cvtest::randInt(rng) & 1) + 3; + blue_idx = cvtest::randInt(rng) & 1 ? 2 : 0; + + types[INPUT][0] = CV_MAKETYPE(depth, cn); + types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(depth, 3); + if( test_array[OUTPUT].size() > 1 ) + types[OUTPUT][1] = types[REF_OUTPUT][1] = CV_MAKETYPE(depth, cn); + + inplace = cn == 3 && cvtest::randInt(rng) % 2 != 0; + test_cpp = (cvtest::randInt(rng) & 256) == 0; +} + + +int CV_ColorCvtBaseTest::prepare_test_case( int test_case_idx ) +{ + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + if( code > 0 && inplace ) + cvtest::copy( test_mat[INPUT][0], test_mat[OUTPUT][0] ); + return code; +} + +void CV_ColorCvtBaseTest::run_func() +{ + CvArr* out0 = test_array[OUTPUT][0]; + cv::Mat _out0 = cv::cvarrToMat(out0), _out1 = cv::cvarrToMat(test_array[OUTPUT][1]); + + if(!test_cpp) + cvCvtColor( inplace ? out0 : test_array[INPUT][0], out0, fwd_code ); + else + cv::cvtColor( cv::cvarrToMat(inplace ? out0 : test_array[INPUT][0]), _out0, fwd_code, _out0.channels()); + + if( inplace ) + { + cvCopy( out0, test_array[OUTPUT][1] ); + out0 = test_array[OUTPUT][1]; + } + if(!test_cpp) + cvCvtColor( out0, test_array[OUTPUT][1], inv_code ); + else + cv::cvtColor(cv::cvarrToMat(out0), _out1, inv_code, _out1.channels()); +} + + +void CV_ColorCvtBaseTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + convert_forward( test_mat[INPUT][0], test_mat[REF_OUTPUT][0] ); + convert_backward( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], + test_mat[REF_OUTPUT][1] ); + int depth = test_mat[REF_OUTPUT][0].depth(); + if( depth == CV_8U && hue_channel ) + { + for( int y = 0; y < test_mat[REF_OUTPUT][0].rows; y++ ) + { + const uchar* h0 = test_mat[REF_OUTPUT][0].ptr(y); + uchar* h = test_mat[OUTPUT][0].ptr(y); + + for( int x = 0; x < test_mat[REF_OUTPUT][0].cols; x++, h0 += 3, h += 3 ) + { + if( abs(*h - *h0) == 180 && *h == 0 ) + *h = 180; + } + } + } +} + + +void CV_ColorCvtBaseTest::convert_forward( const Mat& src, Mat& dst ) +{ + const float c8u = 0.0039215686274509803f; // 1./255 + const float c16u = 1.5259021896696422e-005f; // 1./65535 + int depth = src.depth(); + int cn = src.channels(), dst_cn = dst.channels(); + int cols = src.cols, dst_cols_n = dst.cols*dst_cn; + vector _src_buf(src.cols*3); + vector _dst_buf(dst.cols*3); + float* src_buf = &_src_buf[0]; + float* dst_buf = &_dst_buf[0]; + int i, j; + + assert( (cn == 3 || cn == 4) && (dst_cn == 3 || dst_cn == 1) ); + + for( i = 0; i < src.rows; i++ ) + { + switch( depth ) + { + case CV_8U: + { + const uchar* src_row = src.ptr(i); + uchar* dst_row = dst.ptr(i); + + for( j = 0; j < cols; j++ ) + { + src_buf[j*3] = src_row[j*cn + blue_idx]*c8u; + src_buf[j*3+1] = src_row[j*cn + 1]*c8u; + src_buf[j*3+2] = src_row[j*cn + (blue_idx^2)]*c8u; + } + + convert_row_bgr2abc_32f_c3( src_buf, dst_buf, cols ); + + for( j = 0; j < dst_cols_n; j++ ) + { + int t = cvRound( dst_buf[j] ); + dst_row[j] = saturate_cast(t); + } + } + break; + case CV_16U: + { + const ushort* src_row = src.ptr(i); + ushort* dst_row = dst.ptr(i); + + for( j = 0; j < cols; j++ ) + { + src_buf[j*3] = src_row[j*cn + blue_idx]*c16u; + src_buf[j*3+1] = src_row[j*cn + 1]*c16u; + src_buf[j*3+2] = src_row[j*cn + (blue_idx^2)]*c16u; + } + + convert_row_bgr2abc_32f_c3( src_buf, dst_buf, cols ); + + for( j = 0; j < dst_cols_n; j++ ) + { + int t = cvRound( dst_buf[j] ); + dst_row[j] = saturate_cast(t); + } + } + break; + case CV_32F: + { + const float* src_row = src.ptr(i); + float* dst_row = dst.ptr(i); + + for( j = 0; j < cols; j++ ) + { + src_buf[j*3] = src_row[j*cn + blue_idx]; + src_buf[j*3+1] = src_row[j*cn + 1]; + src_buf[j*3+2] = src_row[j*cn + (blue_idx^2)]; + } + + convert_row_bgr2abc_32f_c3( src_buf, dst_row, cols ); + } + break; + default: + assert(0); + } + } +} + + +void CV_ColorCvtBaseTest::convert_row_bgr2abc_32f_c3( const float* /*src_row*/, + float* /*dst_row*/, int /*n*/ ) +{ +} + + +void CV_ColorCvtBaseTest::convert_row_abc2bgr_32f_c3( const float* /*src_row*/, + float* /*dst_row*/, int /*n*/ ) +{ +} + + +void CV_ColorCvtBaseTest::convert_backward( const Mat& src, const Mat& dst, Mat& dst2 ) +{ + if( custom_inv_transform ) + { + int depth = src.depth(); + int src_cn = dst.channels(), cn = dst2.channels(); + int cols_n = src.cols*src_cn, dst_cols = dst.cols; + vector _src_buf(src.cols*3); + vector _dst_buf(dst.cols*3); + float* src_buf = &_src_buf[0]; + float* dst_buf = &_dst_buf[0]; + int i, j; + + assert( cn == 3 || cn == 4 ); + + for( i = 0; i < src.rows; i++ ) + { + switch( depth ) + { + case CV_8U: + { + const uchar* src_row = dst.ptr(i); + uchar* dst_row = dst2.ptr(i); + + for( j = 0; j < cols_n; j++ ) + src_buf[j] = src_row[j]; + + convert_row_abc2bgr_32f_c3( src_buf, dst_buf, dst_cols ); + + for( j = 0; j < dst_cols; j++ ) + { + int b = cvRound( dst_buf[j*3]*255. ); + int g = cvRound( dst_buf[j*3+1]*255. ); + int r = cvRound( dst_buf[j*3+2]*255. ); + dst_row[j*cn + blue_idx] = saturate_cast(b); + dst_row[j*cn + 1] = saturate_cast(g); + dst_row[j*cn + (blue_idx^2)] = saturate_cast(r); + if( cn == 4 ) + dst_row[j*cn + 3] = 255; + } + } + break; + case CV_16U: + { + const ushort* src_row = dst.ptr(i); + ushort* dst_row = dst2.ptr(i); + + for( j = 0; j < cols_n; j++ ) + src_buf[j] = src_row[j]; + + convert_row_abc2bgr_32f_c3( src_buf, dst_buf, dst_cols ); + + for( j = 0; j < dst_cols; j++ ) + { + int b = cvRound( dst_buf[j*3]*65535. ); + int g = cvRound( dst_buf[j*3+1]*65535. ); + int r = cvRound( dst_buf[j*3+2]*65535. ); + dst_row[j*cn + blue_idx] = saturate_cast(b); + dst_row[j*cn + 1] = saturate_cast(g); + dst_row[j*cn + (blue_idx^2)] = saturate_cast(r); + if( cn == 4 ) + dst_row[j*cn + 3] = 65535; + } + } + break; + case CV_32F: + { + const float* src_row = dst.ptr(i); + float* dst_row = dst2.ptr(i); + + convert_row_abc2bgr_32f_c3( src_row, dst_buf, dst_cols ); + + for( j = 0; j < dst_cols; j++ ) + { + float b = dst_buf[j*3]; + float g = dst_buf[j*3+1]; + float r = dst_buf[j*3+2]; + dst_row[j*cn + blue_idx] = b; + dst_row[j*cn + 1] = g; + dst_row[j*cn + (blue_idx^2)] = r; + if( cn == 4 ) + dst_row[j*cn + 3] = 1.f; + } + } + break; + default: + assert(0); + } + } + } + else + { + int i, j, k; + int elem_size = src.elemSize(), elem_size1 = src.elemSize1(); + int width_n = src.cols*elem_size; + + for( i = 0; i < src.rows; i++ ) + { + memcpy( dst2.ptr(i), src.ptr(i), width_n ); + if( src.channels() == 4 ) + { + // clear the alpha channel + uchar* ptr = dst2.ptr(i) + elem_size1*3; + for( j = 0; j < width_n; j += elem_size ) + { + for( k = 0; k < elem_size1; k++ ) + ptr[j + k] = 0; + } + } + } + } +} + + +#undef INIT_FWD_INV_CODES +#define INIT_FWD_INV_CODES( fwd, inv ) \ + fwd_code = CV_##fwd; inv_code = CV_##inv; \ + fwd_code_str = #fwd; inv_code_str = #inv + +//// rgb <=> gray +class CV_ColorGrayTest : public CV_ColorCvtBaseTest +{ +public: + CV_ColorGrayTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ); + void convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ); + double get_success_error_level( int test_case_idx, int i, int j ); +}; + + +CV_ColorGrayTest::CV_ColorGrayTest() : CV_ColorCvtBaseTest( true, true, true ) +{ + INIT_FWD_INV_CODES( BGR2GRAY, GRAY2BGR ); +} + + +void CV_ColorGrayTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + CV_ColorCvtBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int cn = CV_MAT_CN(types[INPUT][0]); + types[OUTPUT][0] = types[REF_OUTPUT][0] = types[INPUT][0] & CV_MAT_DEPTH_MASK; + inplace = false; + + if( cn == 3 ) + { + if( blue_idx == 0 ) + fwd_code = CV_BGR2GRAY, inv_code = CV_GRAY2BGR; + else + fwd_code = CV_RGB2GRAY, inv_code = CV_GRAY2RGB; + } + else + { + if( blue_idx == 0 ) + fwd_code = CV_BGRA2GRAY, inv_code = CV_GRAY2BGRA; + else + fwd_code = CV_RGBA2GRAY, inv_code = CV_GRAY2RGBA; + } +} + + +double CV_ColorGrayTest::get_success_error_level( int /*test_case_idx*/, int i, int j ) +{ + int depth = test_mat[i][j].depth(); + return depth == CV_8U ? 2 : depth == CV_16U ? 16 : 1e-5; +} + + +void CV_ColorGrayTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int depth = test_mat[INPUT][0].depth(); + double scale = depth == CV_8U ? 255 : depth == CV_16U ? 65535 : 1; + double cr = 0.299*scale; + double cg = 0.587*scale; + double cb = 0.114*scale; + int j; + + for( j = 0; j < n; j++ ) + dst_row[j] = (float)(src_row[j*3]*cb + src_row[j*3+1]*cg + src_row[j*3+2]*cr); +} + + +void CV_ColorGrayTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int j, depth = test_mat[INPUT][0].depth(); + float scale = depth == CV_8U ? (1.f/255) : depth == CV_16U ? 1.f/65535 : 1.f; + for( j = 0; j < n; j++ ) + dst_row[j*3] = dst_row[j*3+1] = dst_row[j*3+2] = src_row[j]*scale; +} + + +//// rgb <=> ycrcb +class CV_ColorYCrCbTest : public CV_ColorCvtBaseTest +{ +public: + CV_ColorYCrCbTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ); + void convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ); +}; + + +CV_ColorYCrCbTest::CV_ColorYCrCbTest() : CV_ColorCvtBaseTest( true, true, true ) +{ + INIT_FWD_INV_CODES( BGR2YCrCb, YCrCb2BGR ); +} + + +void CV_ColorYCrCbTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + CV_ColorCvtBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + if( blue_idx == 0 ) + fwd_code = CV_BGR2YCrCb, inv_code = CV_YCrCb2BGR; + else + fwd_code = CV_RGB2YCrCb, inv_code = CV_YCrCb2RGB; +} + + +double CV_ColorYCrCbTest::get_success_error_level( int /*test_case_idx*/, int i, int j ) +{ + int depth = test_mat[i][j].depth(); + return depth == CV_8U ? 2 : depth == CV_16U ? 32 : 1e-3; +} + + +void CV_ColorYCrCbTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int depth = test_mat[INPUT][0].depth(); + double scale = depth == CV_8U ? 255 : depth == CV_16U ? 65535 : 1; + double bias = depth == CV_8U ? 128 : depth == CV_16U ? 32768 : 0.5; + + double M[] = { 0.299, 0.587, 0.114, + 0.49981, -0.41853, -0.08128, + -0.16864, -0.33107, 0.49970 }; + int j; + for( j = 0; j < 9; j++ ) + M[j] *= scale; + + for( j = 0; j < n*3; j += 3 ) + { + double r = src_row[j+2]; + double g = src_row[j+1]; + double b = src_row[j]; + double y = M[0]*r + M[1]*g + M[2]*b; + double cr = M[3]*r + M[4]*g + M[5]*b + bias; + double cb = M[6]*r + M[7]*g + M[8]*b + bias; + dst_row[j] = (float)y; + dst_row[j+1] = (float)cr; + dst_row[j+2] = (float)cb; + } +} + + +void CV_ColorYCrCbTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int depth = test_mat[INPUT][0].depth(); + double bias = depth == CV_8U ? 128 : depth == CV_16U ? 32768 : 0.5; + double scale = depth == CV_8U ? 1./255 : depth == CV_16U ? 1./65535 : 1; + double M[] = { 1, 1.40252, 0, + 1, -0.71440, -0.34434, + 1, 0, 1.77305 }; + int j; + for( j = 0; j < 9; j++ ) + M[j] *= scale; + + for( j = 0; j < n*3; j += 3 ) + { + double y = src_row[j]; + double cr = src_row[j+1] - bias; + double cb = src_row[j+2] - bias; + double r = M[0]*y + M[1]*cr + M[2]*cb; + double g = M[3]*y + M[4]*cr + M[5]*cb; + double b = M[6]*y + M[7]*cr + M[8]*cb; + dst_row[j] = (float)b; + dst_row[j+1] = (float)g; + dst_row[j+2] = (float)r; + } +} + + +//// rgb <=> hsv +class CV_ColorHSVTest : public CV_ColorCvtBaseTest +{ +public: + CV_ColorHSVTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ); + void convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ); +}; + + +CV_ColorHSVTest::CV_ColorHSVTest() : CV_ColorCvtBaseTest( true, true, false ) +{ + INIT_FWD_INV_CODES( BGR2HSV, HSV2BGR ); + hue_channel = true; +} + + +void CV_ColorHSVTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + CV_ColorCvtBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + if( blue_idx == 0 ) + fwd_code = CV_BGR2HSV, inv_code = CV_HSV2BGR; + else + fwd_code = CV_RGB2HSV, inv_code = CV_HSV2RGB; +} + + +double CV_ColorHSVTest::get_success_error_level( int /*test_case_idx*/, int i, int j ) +{ + int depth = test_mat[i][j].depth(); + return depth == CV_8U ? (j == 0 ? 4 : 16) : depth == CV_16U ? 32 : 1e-3; +} + + +void CV_ColorHSVTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int depth = test_mat[INPUT][0].depth(); + float h_scale = depth == CV_8U ? 30.f : 60.f; + float scale = depth == CV_8U ? 255.f : depth == CV_16U ? 65535.f : 1.f; + int j; + + for( j = 0; j < n*3; j += 3 ) + { + float r = src_row[j+2]; + float g = src_row[j+1]; + float b = src_row[j]; + float vmin = MIN(r,g); + float v = MAX(r,g); + float s, h, diff; + vmin = MIN(vmin,b); + v = MAX(v,b); + diff = v - vmin; + if( diff == 0 ) + s = h = 0; + else + { + s = diff/(v + FLT_EPSILON); + diff = 1.f/diff; + + h = r == v ? (g - b)*diff : + g == v ? 2 + (b - r)*diff : 4 + (r - g)*diff; + + if( h < 0 ) + h += 6; + } + + dst_row[j] = h*h_scale; + dst_row[j+1] = s*scale; + dst_row[j+2] = v*scale; + } +} + +// taken from http://www.cs.rit.edu/~ncs/color/t_convert.html +void CV_ColorHSVTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int depth = test_mat[INPUT][0].depth(); + float h_scale = depth == CV_8U ? 1.f/30 : 1.f/60; + float scale = depth == CV_8U ? 1.f/255 : depth == CV_16U ? 1.f/65535 : 1; + int j; + + for( j = 0; j < n*3; j += 3 ) + { + float h = src_row[j]*h_scale; + float s = src_row[j+1]*scale; + float v = src_row[j+2]*scale; + float r = v, g = v, b = v; + + if( h < 0 ) + h += 6; + else if( h >= 6 ) + h -= 6; + + if( s != 0 ) + { + int i = cvFloor(h); + float f = h - i; + float p = v*(1 - s); + float q = v*(1 - s*f); + float t = v*(1 - s*(1 - f)); + + if( i == 0 ) + r = v, g = t, b = p; + else if( i == 1 ) + r = q, g = v, b = p; + else if( i == 2 ) + r = p, g = v, b = t; + else if( i == 3 ) + r = p, g = q, b = v; + else if( i == 4 ) + r = t, g = p, b = v; + else + r = v, g = p, b = q; + } + + dst_row[j] = b; + dst_row[j+1] = g; + dst_row[j+2] = r; + } +} + + +//// rgb <=> hls +class CV_ColorHLSTest : public CV_ColorCvtBaseTest +{ +public: + CV_ColorHLSTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ); + void convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ); +}; + + +CV_ColorHLSTest::CV_ColorHLSTest() : CV_ColorCvtBaseTest( true, true, false ) +{ + INIT_FWD_INV_CODES( BGR2HLS, HLS2BGR ); + hue_channel = true; +} + + +void CV_ColorHLSTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + CV_ColorCvtBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + if( blue_idx == 0 ) + fwd_code = CV_BGR2HLS, inv_code = CV_HLS2BGR; + else + fwd_code = CV_RGB2HLS, inv_code = CV_HLS2RGB; +} + + +double CV_ColorHLSTest::get_success_error_level( int /*test_case_idx*/, int i, int j ) +{ + int depth = test_mat[i][j].depth(); + return depth == CV_8U ? (j == 0 ? 4 : 16) : depth == CV_16U ? 32 : 1e-4; +} + + +void CV_ColorHLSTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int depth = test_mat[INPUT][0].depth(); + float h_scale = depth == CV_8U ? 30.f : 60.f; + float scale = depth == CV_8U ? 255.f : depth == CV_16U ? 65535.f : 1.f; + int j; + + for( j = 0; j < n*3; j += 3 ) + { + float r = src_row[j+2]; + float g = src_row[j+1]; + float b = src_row[j]; + float vmin = MIN(r,g); + float v = MAX(r,g); + float s, h, l, diff; + vmin = MIN(vmin,b); + v = MAX(v,b); + diff = v - vmin; + + if( diff == 0 ) + s = h = 0, l = v; + else + { + l = (v + vmin)*0.5f; + s = l <= 0.5f ? diff / (v + vmin) : diff / (2 - v - vmin); + diff = 1.f/diff; + + h = r == v ? (g - b)*diff : + g == v ? 2 + (b - r)*diff : 4 + (r - g)*diff; + + if( h < 0 ) + h += 6; + } + + dst_row[j] = h*h_scale; + dst_row[j+1] = l*scale; + dst_row[j+2] = s*scale; + } +} + + +void CV_ColorHLSTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int depth = test_mat[INPUT][0].depth(); + float h_scale = depth == CV_8U ? 1.f/30 : 1.f/60; + float scale = depth == CV_8U ? 1.f/255 : depth == CV_16U ? 1.f/65535 : 1; + int j; + + for( j = 0; j < n*3; j += 3 ) + { + float h = src_row[j]*h_scale; + float l = src_row[j+1]*scale; + float s = src_row[j+2]*scale; + float r = l, g = l, b = l; + + if( h < 0 ) + h += 6; + else if( h >= 6 ) + h -= 6; + + if( s != 0 ) + { + float m2 = l <= 0.5f ? l*(1.f + s) : l + s - l*s; + float m1 = 2*l - m2; + float h1 = h + 2; + + if( h1 >= 6 ) + h1 -= 6; + if( h1 < 1 ) + r = m1 + (m2 - m1)*h1; + else if( h1 < 3 ) + r = m2; + else if( h1 < 4 ) + r = m1 + (m2 - m1)*(4 - h1); + else + r = m1; + + h1 = h; + + if( h1 < 1 ) + g = m1 + (m2 - m1)*h1; + else if( h1 < 3 ) + g = m2; + else if( h1 < 4 ) + g = m1 + (m2 - m1)*(4 - h1); + else + g = m1; + + h1 = h - 2; + if( h1 < 0 ) + h1 += 6; + + if( h1 < 1 ) + b = m1 + (m2 - m1)*h1; + else if( h1 < 3 ) + b = m2; + else if( h1 < 4 ) + b = m1 + (m2 - m1)*(4 - h1); + else + b = m1; + } + + dst_row[j] = b; + dst_row[j+1] = g; + dst_row[j+2] = r; + } +} + + +static const double RGB2XYZ[] = +{ + 0.412453, 0.357580, 0.180423, + 0.212671, 0.715160, 0.072169, + 0.019334, 0.119193, 0.950227 +}; + + +static const double XYZ2RGB[] = +{ + 3.240479, -1.53715, -0.498535, + -0.969256, 1.875991, 0.041556, + 0.055648, -0.204043, 1.057311 +}; + +static const float Xn = 0.950456f; +static const float Zn = 1.088754f; + + +//// rgb <=> xyz +class CV_ColorXYZTest : public CV_ColorCvtBaseTest +{ +public: + CV_ColorXYZTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ); + void convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ); +}; + + +CV_ColorXYZTest::CV_ColorXYZTest() : CV_ColorCvtBaseTest( true, true, true ) +{ + INIT_FWD_INV_CODES( BGR2XYZ, XYZ2BGR ); +} + + +void CV_ColorXYZTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + CV_ColorCvtBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + if( blue_idx == 0 ) + fwd_code = CV_BGR2XYZ, inv_code = CV_XYZ2BGR; + else + fwd_code = CV_RGB2XYZ, inv_code = CV_XYZ2RGB; +} + + +double CV_ColorXYZTest::get_success_error_level( int /*test_case_idx*/, int i, int j ) +{ + int depth = test_mat[i][j].depth(); + return depth == CV_8U ? (j == 0 ? 2 : 8) : depth == CV_16U ? (j == 0 ? 64 : 128) : 1e-1; +} + + +void CV_ColorXYZTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int depth = test_mat[INPUT][0].depth(); + double scale = depth == CV_8U ? 255 : depth == CV_16U ? 65535 : 1; + + double M[9]; + int j; + for( j = 0; j < 9; j++ ) + M[j] = RGB2XYZ[j]*scale; + + for( j = 0; j < n*3; j += 3 ) + { + double r = src_row[j+2]; + double g = src_row[j+1]; + double b = src_row[j]; + double x = M[0]*r + M[1]*g + M[2]*b; + double y = M[3]*r + M[4]*g + M[5]*b; + double z = M[6]*r + M[7]*g + M[8]*b; + dst_row[j] = (float)x; + dst_row[j+1] = (float)y; + dst_row[j+2] = (float)z; + } +} + + +void CV_ColorXYZTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int depth = test_mat[INPUT][0].depth(); + double scale = depth == CV_8U ? 1./255 : depth == CV_16U ? 1./65535 : 1; + + double M[9]; + int j; + for( j = 0; j < 9; j++ ) + M[j] = XYZ2RGB[j]*scale; + + for( j = 0; j < n*3; j += 3 ) + { + double x = src_row[j]; + double y = src_row[j+1]; + double z = src_row[j+2]; + double r = M[0]*x + M[1]*y + M[2]*z; + double g = M[3]*x + M[4]*y + M[5]*z; + double b = M[6]*x + M[7]*y + M[8]*z; + dst_row[j] = (float)b; + dst_row[j+1] = (float)g; + dst_row[j+2] = (float)r; + } +} + + +//// rgb <=> L*a*b* +class CV_ColorLabTest : public CV_ColorCvtBaseTest +{ +public: + CV_ColorLabTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ); + void convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ); +}; + + +CV_ColorLabTest::CV_ColorLabTest() : CV_ColorCvtBaseTest( true, true, false ) +{ + INIT_FWD_INV_CODES( BGR2Lab, Lab2BGR ); +} + + +void CV_ColorLabTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + CV_ColorCvtBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + if( blue_idx == 0 ) + fwd_code = CV_LBGR2Lab, inv_code = CV_Lab2LBGR; + else + fwd_code = CV_LRGB2Lab, inv_code = CV_Lab2LRGB; +} + + +double CV_ColorLabTest::get_success_error_level( int /*test_case_idx*/, int i, int j ) +{ + int depth = test_mat[i][j].depth(); + return depth == CV_8U ? 16 : depth == CV_16U ? 32 : 1e-3; +} + + +static const double _1_3 = 0.333333333333; + +void CV_ColorLabTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int depth = test_mat[INPUT][0].depth(); + float Lscale = depth == CV_8U ? 255.f/100.f : depth == CV_16U ? 65535.f/100.f : 1.f; + float ab_bias = depth == CV_8U ? 128.f : depth == CV_16U ? 32768.f : 0.f; + int j; + float M[9]; + + for( j = 0; j < 9; j++ ) + M[j] = (float)RGB2XYZ[j]; + + for( j = 0; j < n*3; j += 3 ) + { + float r = src_row[j+2]; + float g = src_row[j+1]; + float b = src_row[j]; + + float X = (r*M[0] + g*M[1] + b*M[2])*(1.f/Xn); + float Y = r*M[3] + g*M[4] + b*M[5]; + float Z = (r*M[6] + g*M[7] + b*M[8])*(1.f/Zn); + float fX, fY, fZ; + + float L, a; + + if( Y > 0.008856 ) + { + fY = (float)pow((double)Y,_1_3); + L = 116.f*fY - 16.f; + } + else + { + fY = 7.787f*Y + 16.f/116.f; + L = 903.3f*Y; + } + + if( X > 0.008856 ) + fX = (float)pow((double)X,_1_3); + else + fX = 7.787f*X + 16.f/116.f; + + if( Z > 0.008856 ) + fZ = (float)pow((double)Z,_1_3); + else + fZ = 7.787f*Z + 16.f/116.f; + + a = 500.f*(fX - fY); + b = 200.f*(fY - fZ); + + dst_row[j] = L*Lscale; + dst_row[j+1] = a + ab_bias; + dst_row[j+2] = b + ab_bias; + } +} + + +void CV_ColorLabTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int depth = test_mat[INPUT][0].depth(); + float Lscale = depth == CV_8U ? 100.f/255.f : depth == CV_16U ? 100.f/65535.f : 1.f; + float ab_bias = depth == CV_8U ? 128.f : depth == CV_16U ? 32768.f : 0.f; + int j; + float M[9]; + + for( j = 0; j < 9; j++ ) + M[j] = (float)XYZ2RGB[j]; + + for( j = 0; j < n*3; j += 3 ) + { + float L = src_row[j]*Lscale; + float a = src_row[j+1] - ab_bias; + float b = src_row[j+2] - ab_bias; + + float P = (L + 16.f)*(1.f/116.f); + float X = (P + a*0.002f); + float Z = (P - b*0.005f); + float Y = P*P*P; + X = Xn*X*X*X; + Z = Zn*Z*Z*Z; + + float r = M[0]*X + M[1]*Y + M[2]*Z; + float g = M[3]*X + M[4]*Y + M[5]*Z; + b = M[6]*X + M[7]*Y + M[8]*Z; + + dst_row[j] = b; + dst_row[j+1] = g; + dst_row[j+2] = r; + } +} + + +//// rgb <=> L*u*v* +class CV_ColorLuvTest : public CV_ColorCvtBaseTest +{ +public: + CV_ColorLuvTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ); + void convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ); +}; + + +CV_ColorLuvTest::CV_ColorLuvTest() : CV_ColorCvtBaseTest( true, true, false ) +{ + INIT_FWD_INV_CODES( BGR2Luv, Luv2BGR ); +} + + +void CV_ColorLuvTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + CV_ColorCvtBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + if( blue_idx == 0 ) + fwd_code = CV_LBGR2Luv, inv_code = CV_Luv2LBGR; + else + fwd_code = CV_LRGB2Luv, inv_code = CV_Luv2LRGB; +} + + +double CV_ColorLuvTest::get_success_error_level( int /*test_case_idx*/, int i, int j ) +{ + int depth = test_mat[i][j].depth(); + return depth == CV_8U ? 48 : depth == CV_16U ? 32 : 5e-2; +} + + +void CV_ColorLuvTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int depth = test_mat[INPUT][0].depth(); + float Lscale = depth == CV_8U ? 255.f/100.f : depth == CV_16U ? 65535.f/100.f : 1.f; + int j; + + float M[9]; + float un = 4.f*Xn/(Xn + 15.f*1.f + 3*Zn); + float vn = 9.f*1.f/(Xn + 15.f*1.f + 3*Zn); + float u_scale = 1.f, u_bias = 0.f; + float v_scale = 1.f, v_bias = 0.f; + + for( j = 0; j < 9; j++ ) + M[j] = (float)RGB2XYZ[j]; + + if( depth == CV_8U ) + { + u_scale = 0.720338983f; + u_bias = 96.5254237f; + v_scale = 0.99609375f; + v_bias = 139.453125f; + } + + for( j = 0; j < n*3; j += 3 ) + { + float r = src_row[j+2]; + float g = src_row[j+1]; + float b = src_row[j]; + + float X = r*M[0] + g*M[1] + b*M[2]; + float Y = r*M[3] + g*M[4] + b*M[5]; + float Z = r*M[6] + g*M[7] + b*M[8]; + float d = X + 15*Y + 3*Z, L, u, v; + + if( d == 0 ) + L = u = v = 0; + else + { + if( Y > 0.008856f ) + L = (float)(116.*pow((double)Y,_1_3) - 16.); + else + L = 903.3f * Y; + + d = 1.f/d; + u = 13*L*(4*X*d - un); + v = 13*L*(9*Y*d - vn); + } + dst_row[j] = L*Lscale; + dst_row[j+1] = u*u_scale + u_bias; + dst_row[j+2] = v*v_scale + v_bias; + } +} + + +void CV_ColorLuvTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ) +{ + int depth = test_mat[INPUT][0].depth(); + float Lscale = depth == CV_8U ? 100.f/255.f : depth == CV_16U ? 100.f/65535.f : 1.f; + int j; + float M[9]; + float un = 4.f*Xn/(Xn + 15.f*1.f + 3*Zn); + float vn = 9.f*1.f/(Xn + 15.f*1.f + 3*Zn); + float u_scale = 1.f, u_bias = 0.f; + float v_scale = 1.f, v_bias = 0.f; + + for( j = 0; j < 9; j++ ) + M[j] = (float)XYZ2RGB[j]; + + if( depth == CV_8U ) + { + u_scale = 1.f/0.720338983f; + u_bias = 96.5254237f; + v_scale = 1.f/0.99609375f; + v_bias = 139.453125f; + } + + for( j = 0; j < n*3; j += 3 ) + { + float L = src_row[j]*Lscale; + float u = (src_row[j+1] - u_bias)*u_scale; + float v = (src_row[j+2] - v_bias)*v_scale; + float X, Y, Z; + + if( L >= 8 ) + { + Y = (L + 16.f)*(1.f/116.f); + Y = Y*Y*Y; + } + else + { + Y = L * (1.f/903.3f); + if( L == 0 ) + L = 0.001f; + } + + u = u/(13*L) + un; + v = v/(13*L) + vn; + + X = -9*Y*u/((u - 4)*v - u*v); + Z = (9*Y - 15*v*Y - v*X)/(3*v); + + float r = M[0]*X + M[1]*Y + M[2]*Z; + float g = M[3]*X + M[4]*Y + M[5]*Z; + float b = M[6]*X + M[7]*Y + M[8]*Z; + + dst_row[j] = b; + dst_row[j+1] = g; + dst_row[j+2] = r; + } +} + + +//// rgb <=> another rgb +class CV_ColorRGBTest : public CV_ColorCvtBaseTest +{ +public: + CV_ColorRGBTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void convert_forward( const Mat& src, Mat& dst ); + void convert_backward( const Mat& src, const Mat& dst, Mat& dst2 ); + int dst_bits; +}; + + +CV_ColorRGBTest::CV_ColorRGBTest() : CV_ColorCvtBaseTest( true, true, true ) +{ + dst_bits = 0; +} + + +void CV_ColorRGBTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + CV_ColorCvtBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int cn = CV_MAT_CN(types[INPUT][0]); + + dst_bits = 24; + + if( cvtest::randInt(rng) % 3 == 0 ) + { + types[INPUT][0] = types[OUTPUT][1] = types[REF_OUTPUT][1] = CV_MAKETYPE(CV_8U,cn); + types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(CV_8U,2); + if( cvtest::randInt(rng) & 1 ) + { + if( blue_idx == 0 ) + fwd_code = CV_BGR2BGR565, inv_code = CV_BGR5652BGR; + else + fwd_code = CV_RGB2BGR565, inv_code = CV_BGR5652RGB; + dst_bits = 16; + } + else + { + if( blue_idx == 0 ) + fwd_code = CV_BGR2BGR555, inv_code = CV_BGR5552BGR; + else + fwd_code = CV_RGB2BGR555, inv_code = CV_BGR5552RGB; + dst_bits = 15; + } + } + else + { + if( cn == 3 ) + { + fwd_code = CV_RGB2BGR, inv_code = CV_BGR2RGB; + blue_idx = 2; + } + else if( blue_idx == 0 ) + fwd_code = CV_BGRA2BGR, inv_code = CV_BGR2BGRA; + else + fwd_code = CV_RGBA2BGR, inv_code = CV_BGR2RGBA; + } + + if( CV_MAT_CN(types[INPUT][0]) != CV_MAT_CN(types[OUTPUT][0]) ) + inplace = false; +} + + +double CV_ColorRGBTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return 0; +} + + +void CV_ColorRGBTest::convert_forward( const Mat& src, Mat& dst ) +{ + int depth = src.depth(), cn = src.channels(); +/*#if defined _DEBUG || defined DEBUG + int dst_cn = CV_MAT_CN(dst->type); +#endif*/ + int i, j, cols = src.cols; + int g_rshift = dst_bits == 16 ? 2 : 3; + int r_lshift = dst_bits == 16 ? 11 : 10; + + //assert( (cn == 3 || cn == 4) && (dst_cn == 3 || (dst_cn == 2 && depth == CV_8U)) ); + + for( i = 0; i < src.rows; i++ ) + { + switch( depth ) + { + case CV_8U: + { + const uchar* src_row = src.ptr(i); + uchar* dst_row = dst.ptr(i); + + if( dst_bits == 24 ) + { + for( j = 0; j < cols; j++ ) + { + uchar b = src_row[j*cn + blue_idx]; + uchar g = src_row[j*cn + 1]; + uchar r = src_row[j*cn + (blue_idx^2)]; + dst_row[j*3] = b; + dst_row[j*3+1] = g; + dst_row[j*3+2] = r; + } + } + else + { + for( j = 0; j < cols; j++ ) + { + int b = src_row[j*cn + blue_idx] >> 3; + int g = src_row[j*cn + 1] >> g_rshift; + int r = src_row[j*cn + (blue_idx^2)] >> 3; + ((ushort*)dst_row)[j] = (ushort)(b | (g << 5) | (r << r_lshift)); + if( cn == 4 && src_row[j*4+3] ) + ((ushort*)dst_row)[j] |= 1 << (r_lshift+5); + } + } + } + break; + case CV_16U: + { + const ushort* src_row = src.ptr(i); + ushort* dst_row = dst.ptr(i); + + for( j = 0; j < cols; j++ ) + { + ushort b = src_row[j*cn + blue_idx]; + ushort g = src_row[j*cn + 1]; + ushort r = src_row[j*cn + (blue_idx^2)]; + dst_row[j*3] = b; + dst_row[j*3+1] = g; + dst_row[j*3+2] = r; + } + } + break; + case CV_32F: + { + const float* src_row = src.ptr(i); + float* dst_row = dst.ptr(i); + + for( j = 0; j < cols; j++ ) + { + float b = src_row[j*cn + blue_idx]; + float g = src_row[j*cn + 1]; + float r = src_row[j*cn + (blue_idx^2)]; + dst_row[j*3] = b; + dst_row[j*3+1] = g; + dst_row[j*3+2] = r; + } + } + break; + default: + assert(0); + } + } +} + + +void CV_ColorRGBTest::convert_backward( const Mat& /*src*/, const Mat& src, Mat& dst ) +{ + int depth = src.depth(), cn = dst.channels(); +/*#if defined _DEBUG || defined DEBUG + int src_cn = CV_MAT_CN(src->type); +#endif*/ + int i, j, cols = src.cols; + int g_lshift = dst_bits == 16 ? 2 : 3; + int r_rshift = dst_bits == 16 ? 11 : 10; + + //assert( (cn == 3 || cn == 4) && (src_cn == 3 || (src_cn == 2 && depth == CV_8U)) ); + + for( i = 0; i < src.rows; i++ ) + { + switch( depth ) + { + case CV_8U: + { + const uchar* src_row = src.ptr(i); + uchar* dst_row = dst.ptr(i); + + if( dst_bits == 24 ) + { + for( j = 0; j < cols; j++ ) + { + uchar b = src_row[j*3]; + uchar g = src_row[j*3 + 1]; + uchar r = src_row[j*3 + 2]; + + dst_row[j*cn + blue_idx] = b; + dst_row[j*cn + 1] = g; + dst_row[j*cn + (blue_idx^2)] = r; + + if( cn == 4 ) + dst_row[j*cn + 3] = 255; + } + } + else + { + for( j = 0; j < cols; j++ ) + { + ushort val = ((ushort*)src_row)[j]; + uchar b = (uchar)(val << 3); + uchar g = (uchar)((val >> 5) << g_lshift); + uchar r = (uchar)((val >> r_rshift) << 3); + + dst_row[j*cn + blue_idx] = b; + dst_row[j*cn + 1] = g; + dst_row[j*cn + (blue_idx^2)] = r; + + if( cn == 4 ) + { + uchar alpha = r_rshift == 11 || (val & 0x8000) != 0 ? 255 : 0; + dst_row[j*cn + 3] = alpha; + } + } + } + } + break; + case CV_16U: + { + const ushort* src_row = src.ptr(i); + ushort* dst_row = dst.ptr(i); + + for( j = 0; j < cols; j++ ) + { + ushort b = src_row[j*3]; + ushort g = src_row[j*3 + 1]; + ushort r = src_row[j*3 + 2]; + + dst_row[j*cn + blue_idx] = b; + dst_row[j*cn + 1] = g; + dst_row[j*cn + (blue_idx^2)] = r; + + if( cn == 4 ) + dst_row[j*cn + 3] = 65535; + } + } + break; + case CV_32F: + { + const float* src_row = src.ptr(i); + float* dst_row = dst.ptr(i); + + for( j = 0; j < cols; j++ ) + { + float b = src_row[j*3]; + float g = src_row[j*3 + 1]; + float r = src_row[j*3 + 2]; + + dst_row[j*cn + blue_idx] = b; + dst_row[j*cn + 1] = g; + dst_row[j*cn + (blue_idx^2)] = r; + + if( cn == 4 ) + dst_row[j*cn + 3] = 1.f; + } + } + break; + default: + assert(0); + } + } +} + + +//// rgb <=> bayer + +class CV_ColorBayerTest : public CV_ColorCvtBaseTest +{ +public: + CV_ColorBayerTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int test_case_idx ); +}; + + +CV_ColorBayerTest::CV_ColorBayerTest() : CV_ColorCvtBaseTest( false, false, false ) +{ + test_array[OUTPUT].pop_back(); + test_array[REF_OUTPUT].pop_back(); + + fwd_code_str = "BayerBG2BGR"; + inv_code_str = ""; + fwd_code = CV_BayerBG2BGR; + inv_code = -1; +} + + +void CV_ColorBayerTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + CV_ColorCvtBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + types[INPUT][0] = CV_8UC1; + types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_8UC3; + inplace = false; + + fwd_code = cvtest::randInt(rng)%4 + CV_BayerBG2BGR; +} + + +double CV_ColorBayerTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return 1; +} + + +void CV_ColorBayerTest::run_func() +{ + if(!test_cpp) + cvCvtColor( test_array[INPUT][0], test_array[OUTPUT][0], fwd_code ); + else + { + cv::Mat _out = cv::cvarrToMat(test_array[OUTPUT][0]); + cv::cvtColor(cv::cvarrToMat(test_array[INPUT][0]), _out, fwd_code, _out.channels()); + } +} + + +void CV_ColorBayerTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + const Mat& src = test_mat[INPUT][0]; + Mat& dst = test_mat[REF_OUTPUT][0]; + int i, j, cols = src.cols - 2; + int code = fwd_code; + int bi = 0; + int step = src.step; + + if( fwd_code == CV_BayerRG2BGR || fwd_code == CV_BayerGR2BGR ) + bi ^= 2; + + for( i = 1; i < src.rows - 1; i++ ) + { + const uchar* ptr = src.ptr(i) + 1; + uchar* dst_row = dst.ptr(i) + 3; + int save_code = code; + if( cols <= 0 ) + { + dst_row[-3] = dst_row[-2] = dst_row[-1] = 0; + dst_row[cols*3] = dst_row[cols*3+1] = dst_row[cols*3+2] = 0; + continue; + } + + for( j = 0; j < cols; j++ ) + { + int b, g, r; + if( !(code & 1) ) + { + b = ptr[j]; + g = (ptr[j-1] + ptr[j+1] + ptr[j-step] + ptr[j+step])>>2; + r = (ptr[j-step-1] + ptr[j-step+1] + ptr[j+step-1] + ptr[j+step+1]) >> 2; + } + else + { + b = (ptr[j-1] + ptr[j+1]) >> 1; + g = ptr[j]; + r = (ptr[j-step] + ptr[j+step]) >> 1; + } + code ^= 1; + dst_row[j*3 + bi] = (uchar)b; + dst_row[j*3 + 1] = (uchar)g; + dst_row[j*3 + (bi^2)] = (uchar)r; + } + + dst_row[-3] = dst_row[0]; + dst_row[-2] = dst_row[1]; + dst_row[-1] = dst_row[2]; + dst_row[cols*3] = dst_row[cols*3-3]; + dst_row[cols*3+1] = dst_row[cols*3-2]; + dst_row[cols*3+2] = dst_row[cols*3-1]; + + code = save_code ^ 1; + bi ^= 2; + } + + if( src.rows <= 2 ) + { + memset( dst.ptr(), 0, (cols+2)*3 ); + memset( dst.ptr(dst.rows-1), 0, (cols+2)*3 ); + } + else + { + uchar* top_row = dst.ptr(); + uchar* bottom_row = dst.ptr(dst.rows-1); + int dstep = dst.step; + + for( j = 0; j < (cols+2)*3; j++ ) + { + top_row[j] = top_row[j + dstep]; + bottom_row[j] = bottom_row[j - dstep]; + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////// + +TEST(Imgcore_ColorGray, accuracy) { CV_ColorGrayTest test; test.safe_run(); } +TEST(Imgcore_ColorYCrCb, accuracy) { CV_ColorYCrCbTest test; test.safe_run(); } +TEST(Imgcore_ColorHSV, accuracy) { CV_ColorHSVTest test; test.safe_run(); } +TEST(Imgcore_ColorHLS, accuracy) { CV_ColorHLSTest test; test.safe_run(); } +TEST(Imgcore_ColorXYZ, accuracy) { CV_ColorXYZTest test; test.safe_run(); } +TEST(Imgcore_ColorLab, accuracy) { CV_ColorLabTest test; test.safe_run(); } +TEST(Imgcore_ColorLuv, accuracy) { CV_ColorLuvTest test; test.safe_run(); } +TEST(Imgcore_ColorRGB, accuracy) { CV_ColorRGBTest test; test.safe_run(); } +TEST(Imgcore_ColorBayer, accuracy) { CV_ColorBayerTest test; test.safe_run(); } diff --git a/modules/imgproc/test/test_contours.cpp b/modules/imgproc/test/test_contours.cpp new file mode 100644 index 000000000..3e074883a --- /dev/null +++ b/modules/imgproc/test/test_contours.cpp @@ -0,0 +1,393 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_FindContourTest : public cvtest::BaseTest +{ +public: + enum { NUM_IMG = 4 }; + + CV_FindContourTest(); + ~CV_FindContourTest(); + void clear(); + +protected: + int read_params( CvFileStorage* fs ); + int prepare_test_case( int test_case_idx ); + int validate_test_results( int test_case_idx ); + void run_func(); + + int min_blob_size, max_blob_size; + int blob_count, max_log_blob_count; + int retr_mode, approx_method; + + int min_log_img_size, max_log_img_size; + CvSize img_size; + int count, count2; + + IplImage* img[NUM_IMG]; + CvMemStorage* storage; + CvSeq *contours, *contours2, *chain; +}; + + +CV_FindContourTest::CV_FindContourTest() +{ + int i; + + test_case_count = 300; + min_blob_size = 1; + max_blob_size = 50; + max_log_blob_count = 10; + + min_log_img_size = 3; + max_log_img_size = 10; + + for( i = 0; i < NUM_IMG; i++ ) + img[i] = 0; + + storage = 0; +} + + +CV_FindContourTest::~CV_FindContourTest() +{ + clear(); +} + + +void CV_FindContourTest::clear() +{ + int i; + + cvtest::BaseTest::clear(); + + for( i = 0; i < NUM_IMG; i++ ) + cvReleaseImage( &img[i] ); + + cvReleaseMemStorage( &storage ); +} + + +int CV_FindContourTest::read_params( CvFileStorage* fs ) +{ + int t; + int code = cvtest::BaseTest::read_params( fs ); + + if( code < 0 ) + return code; + + min_blob_size = cvReadInt( find_param( fs, "min_blob_size" ), min_blob_size ); + max_blob_size = cvReadInt( find_param( fs, "max_blob_size" ), max_blob_size ); + max_log_blob_count = cvReadInt( find_param( fs, "max_log_blob_count" ), max_log_blob_count ); + min_log_img_size = cvReadInt( find_param( fs, "min_log_img_size" ), min_log_img_size ); + max_log_img_size = cvReadInt( find_param( fs, "max_log_img_size" ), max_log_img_size ); + + min_blob_size = cvtest::clipInt( min_blob_size, 1, 100 ); + max_blob_size = cvtest::clipInt( max_blob_size, 1, 100 ); + + if( min_blob_size > max_blob_size ) + CV_SWAP( min_blob_size, max_blob_size, t ); + + max_log_blob_count = cvtest::clipInt( max_log_blob_count, 1, 10 ); + + min_log_img_size = cvtest::clipInt( min_log_img_size, 1, 10 ); + max_log_img_size = cvtest::clipInt( max_log_img_size, 1, 10 ); + + if( min_log_img_size > max_log_img_size ) + CV_SWAP( min_log_img_size, max_log_img_size, t ); + + return 0; +} + + +static void +cvTsGenerateBlobImage( IplImage* img, int min_blob_size, int max_blob_size, + int blob_count, int min_brightness, int max_brightness, + RNG& rng ) +{ + int i; + CvSize size; + + assert( img->depth == IPL_DEPTH_8U && img->nChannels == 1 ); + + cvZero( img ); + + // keep the border clear + cvSetImageROI( img, cvRect(1,1,img->width-2,img->height-2) ); + size = cvGetSize( img ); + + for( i = 0; i < blob_count; i++ ) + { + CvPoint center; + CvSize axes; + int angle = cvtest::randInt(rng) % 180; + int brightness = cvtest::randInt(rng) % + (max_brightness - min_brightness) + min_brightness; + center.x = cvtest::randInt(rng) % size.width; + center.y = cvtest::randInt(rng) % size.height; + + axes.width = (cvtest::randInt(rng) % + (max_blob_size - min_blob_size) + min_blob_size + 1)/2; + axes.height = (cvtest::randInt(rng) % + (max_blob_size - min_blob_size) + min_blob_size + 1)/2; + + cvEllipse( img, center, axes, angle, 0, 360, cvScalar(brightness), CV_FILLED ); + } + + cvResetImageROI( img ); +} + + +static void +cvTsMarkContours( IplImage* img, int val ) +{ + int i, j; + int step = img->widthStep; + + assert( img->depth == IPL_DEPTH_8U && img->nChannels == 1 && (val&1) != 0); + + for( i = 1; i < img->height - 1; i++ ) + for( j = 1; j < img->width - 1; j++ ) + { + uchar* t = (uchar*)(img->imageData + img->widthStep*i + j); + if( *t == 1 && (t[-step] == 0 || t[-1] == 0 || t[1] == 0 || t[step] == 0)) + *t = (uchar)val; + } + + cvThreshold( img, img, val - 2, val, CV_THRESH_BINARY ); +} + + +int CV_FindContourTest::prepare_test_case( int test_case_idx ) +{ + RNG& rng = ts->get_rng(); + const int min_brightness = 0, max_brightness = 2; + int i, code = cvtest::BaseTest::prepare_test_case( test_case_idx ); + + if( code < 0 ) + return code; + + clear(); + + blob_count = cvRound(exp(cvtest::randReal(rng)*max_log_blob_count*CV_LOG2)); + + img_size.width = cvRound(exp((cvtest::randReal(rng)* + (max_log_img_size - min_log_img_size) + min_log_img_size)*CV_LOG2)); + img_size.height = cvRound(exp((cvtest::randReal(rng)* + (max_log_img_size - min_log_img_size) + min_log_img_size)*CV_LOG2)); + + approx_method = cvtest::randInt( rng ) % 4 + 1; + retr_mode = cvtest::randInt( rng ) % 4; + + storage = cvCreateMemStorage( 1 << 10 ); + + for( i = 0; i < NUM_IMG; i++ ) + img[i] = cvCreateImage( img_size, 8, 1 ); + + cvTsGenerateBlobImage( img[0], min_blob_size, max_blob_size, + blob_count, min_brightness, max_brightness, rng ); + + cvCopy( img[0], img[1] ); + cvCopy( img[0], img[2] ); + + cvTsMarkContours( img[1], 255 ); + + return 1; +} + + +void CV_FindContourTest::run_func() +{ + contours = contours2 = chain = 0; + count = cvFindContours( img[2], storage, &contours, sizeof(CvContour), retr_mode, approx_method ); + + cvZero( img[3] ); + + if( contours && retr_mode != CV_RETR_EXTERNAL && approx_method < CV_CHAIN_APPROX_TC89_L1 ) + cvDrawContours( img[3], contours, cvScalar(255), cvScalar(255), INT_MAX, -1 ); + + cvCopy( img[0], img[2] ); + + count2 = cvFindContours( img[2], storage, &chain, sizeof(CvChain), retr_mode, CV_CHAIN_CODE ); + + if( chain ) + contours2 = cvApproxChains( chain, storage, approx_method, 0, 0, 1 ); + + cvZero( img[2] ); + + if( contours && retr_mode != CV_RETR_EXTERNAL && approx_method < CV_CHAIN_APPROX_TC89_L1 ) + cvDrawContours( img[2], contours2, cvScalar(255), cvScalar(255), INT_MAX ); +} + + +// the whole testing is done here, run_func() is not utilized in this test +int CV_FindContourTest::validate_test_results( int /*test_case_idx*/ ) +{ + int i, code = cvtest::TS::OK; + + cvCmpS( img[0], 0, img[0], CV_CMP_GT ); + + if( count != count2 ) + { + ts->printf( cvtest::TS::LOG, "The number of contours retrieved with different " + "approximation methods is not the same\n" + "(%d contour(s) for method %d vs %d contour(s) for method %d)\n", + count, approx_method, count2, CV_CHAIN_CODE ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + } + + if( retr_mode != CV_RETR_EXTERNAL && approx_method < CV_CHAIN_APPROX_TC89_L1 ) + { + Mat _img[4]; + for( int i = 0; i < 4; i++ ) + _img[i] = cvarrToMat(img[i]); + + code = cvtest::cmpEps2(ts, _img[0], _img[3], 0, true, "Comparing original image with the map of filled contours" ); + + if( code < 0 ) + goto _exit_; + + code = cvtest::cmpEps2( ts, _img[1], _img[2], 0, true, + "Comparing contour outline vs manually produced edge map" ); + + if( code < 0 ) + goto _exit_; + } + + if( contours ) + { + CvTreeNodeIterator iterator1; + CvTreeNodeIterator iterator2; + int count3; + + for( i = 0; i < 2; i++ ) + { + CvTreeNodeIterator iterator; + cvInitTreeNodeIterator( &iterator, i == 0 ? contours : contours2, INT_MAX ); + + for( count3 = 0; cvNextTreeNode( &iterator ) != 0; count3++ ) + ; + + if( count3 != count ) + { + ts->printf( cvtest::TS::LOG, + "The returned number of retrieved contours (using the approx_method = %d) does not match\n" + "to the actual number of contours in the tree/list (returned %d, actual %d)\n", + i == 0 ? approx_method : 0, count, count3 ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + } + + cvInitTreeNodeIterator( &iterator1, contours, INT_MAX ); + cvInitTreeNodeIterator( &iterator2, contours2, INT_MAX ); + + for( count3 = 0; count3 < count; count3++ ) + { + CvSeq* seq1 = (CvSeq*)cvNextTreeNode( &iterator1 ); + CvSeq* seq2 = (CvSeq*)cvNextTreeNode( &iterator2 ); + CvSeqReader reader1; + CvSeqReader reader2; + + if( !seq1 || !seq2 ) + { + ts->printf( cvtest::TS::LOG, + "There are NULL pointers in the original contour tree or the " + "tree produced by cvApproxChains\n" ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + + cvStartReadSeq( seq1, &reader1 ); + cvStartReadSeq( seq2, &reader2 ); + + if( seq1->total != seq2->total ) + { + ts->printf( cvtest::TS::LOG, + "The original contour #%d has %d points, while the corresponding contour has %d point\n", + count3, seq1->total, seq2->total ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + + for( i = 0; i < seq1->total; i++ ) + { + CvPoint pt1; + CvPoint pt2; + + CV_READ_SEQ_ELEM( pt1, reader1 ); + CV_READ_SEQ_ELEM( pt2, reader2 ); + + if( pt1.x != pt2.x || pt1.y != pt2.y ) + { + ts->printf( cvtest::TS::LOG, + "The point #%d in the contour #%d is different from the corresponding point " + "in the approximated chain ((%d,%d) vs (%d,%d)", count3, i, pt1.x, pt1.y, pt2.x, pt2.y ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + } + } + } + +_exit_: + if( code < 0 ) + { +#if 0 + cvNamedWindow( "test", 0 ); + cvShowImage( "test", img[0] ); + cvWaitKey(); +#endif + ts->set_failed_test_info( code ); + } + + return code; +} + + +TEST(Imgproc_FindContours, accuracy) { CV_FindContourTest test; test.safe_run(); } + +/* End of file. */ diff --git a/modules/imgproc/test/test_convhull.cpp b/modules/imgproc/test/test_convhull.cpp new file mode 100644 index 000000000..a4b61f591 --- /dev/null +++ b/modules/imgproc/test/test_convhull.cpp @@ -0,0 +1,1583 @@ +/*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" + +using namespace cv; +using namespace std; + +/*static int +cvTsPointConvexPolygon( CvPoint2D32f pt, CvPoint2D32f* v, int n ) +{ + CvPoint2D32f v0 = v[n-1]; + int i, sign = 0; + + for( i = 0; i < n; i++ ) + { + CvPoint2D32f v1 = v[i]; + float dx = pt.x - v0.x, dy = pt.y - v0.y; + float dx1 = v1.x - v0.x, dy1 = v1.y - v0.y; + double t = (double)dx*dy1 - (double)dx1*dy; + if( fabs(t) > DBL_EPSILON ) + { + if( t*sign < 0 ) + break; + if( sign == 0 ) + sign = t < 0 ? -1 : 1; + } + else if( fabs(dx) + fabs(dy) < DBL_EPSILON ) + return i+1; + v0 = v1; + } + + return i < n ? -1 : 0; +}*/ + +CV_INLINE double +cvTsDist( CvPoint2D32f a, CvPoint2D32f b ) +{ + double dx = a.x - b.x; + double dy = a.y - b.y; + return sqrt(dx*dx + dy*dy); +} + +CV_INLINE double +cvTsPtLineDist( CvPoint2D32f pt, CvPoint2D32f a, CvPoint2D32f b ) +{ + double d0 = cvTsDist( pt, a ), d1; + double dd = cvTsDist( a, b ); + if( dd < FLT_EPSILON ) + return d0; + d1 = cvTsDist( pt, b ); + dd = fabs((double)(pt.x - a.x)*(b.y - a.y) - (double)(pt.y - a.y)*(b.x - a.x))/dd; + d0 = MIN( d0, d1 ); + return MIN( d0, dd ); +} + +static double +cvTsPointPolygonTest( CvPoint2D32f pt, const CvPoint2D32f* vv, int n, int* _idx=0, int* _on_edge=0 ) +{ + int i; + CvPoint2D32f v = vv[n-1], v0; + double min_dist_num = FLT_MAX, min_dist_denom = 1; + int min_dist_idx = -1, min_on_edge = 0; + int counter = 0; + double result; + + for( i = 0; i < n; i++ ) + { + double dx, dy, dx1, dy1, dx2, dy2, dist_num, dist_denom = 1; + int on_edge = 0, idx = i; + + v0 = v; v = vv[i]; + dx = v.x - v0.x; dy = v.y - v0.y; + dx1 = pt.x - v0.x; dy1 = pt.y - v0.y; + dx2 = pt.x - v.x; dy2 = pt.y - v.y; + + if( dx2*dx + dy2*dy >= 0 ) + dist_num = dx2*dx2 + dy2*dy2; + else if( dx1*dx + dy1*dy <= 0 ) + { + dist_num = dx1*dx1 + dy1*dy1; + idx = i - 1; + if( idx < 0 ) idx = n-1; + } + else + { + dist_num = (dy1*dx - dx1*dy); + dist_num *= dist_num; + dist_denom = dx*dx + dy*dy; + on_edge = 1; + } + + if( dist_num*min_dist_denom < min_dist_num*dist_denom ) + { + min_dist_num = dist_num; + min_dist_denom = dist_denom; + min_dist_idx = idx; + min_on_edge = on_edge; + if( min_dist_num == 0 ) + break; + } + + if( (v0.y <= pt.y && v.y <= pt.y) || + (v0.y > pt.y && v.y > pt.y) || + (v0.x < pt.x && v.x < pt.x) ) + continue; + + dist_num = dy1*dx - dx1*dy; + if( dy < 0 ) + dist_num = -dist_num; + counter += dist_num > 0; + } + + result = sqrt(min_dist_num/min_dist_denom); + if( counter % 2 == 0 ) + result = -result; + + if( _idx ) + *_idx = min_dist_idx; + if( _on_edge ) + *_on_edge = min_on_edge; + + return result; +} + + +/****************************************************************************************\ +* Base class for shape descriptor tests * +\****************************************************************************************/ + +class CV_BaseShapeDescrTest : public cvtest::BaseTest +{ +public: + CV_BaseShapeDescrTest(); + virtual ~CV_BaseShapeDescrTest(); + void clear(); + +protected: + int read_params( CvFileStorage* fs ); + void run_func(void); + int prepare_test_case( int test_case_idx ); + int validate_test_results( int test_case_idx ); + virtual void generate_point_set( void* points ); + virtual void extract_points(); + + int min_log_size; + int max_log_size; + int dims; + bool enable_flt_points; + + CvMemStorage* storage; + CvSeq* points1; + CvMat* points2; + void* points; + void* result; + double low_high_range; + CvScalar low, high; + + bool test_cpp; +}; + + +CV_BaseShapeDescrTest::CV_BaseShapeDescrTest() +{ + points1 = 0; + points2 = 0; + points = 0; + storage = 0; + test_case_count = 500; + min_log_size = 0; + max_log_size = 10; + low = high = cvScalarAll(0); + low_high_range = 50; + dims = 2; + enable_flt_points = true; + + test_cpp = false; +} + + +CV_BaseShapeDescrTest::~CV_BaseShapeDescrTest() +{ + clear(); +} + + +void CV_BaseShapeDescrTest::clear() +{ + cvtest::BaseTest::clear(); + cvReleaseMemStorage( &storage ); + cvReleaseMat( &points2 ); + points1 = 0; + points = 0; +} + + +int CV_BaseShapeDescrTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::BaseTest::read_params( fs ); + if( code < 0 ) + return code; + + test_case_count = cvReadInt( find_param( fs, "struct_count" ), test_case_count ); + min_log_size = cvReadInt( find_param( fs, "min_log_size" ), min_log_size ); + max_log_size = cvReadInt( find_param( fs, "max_log_size" ), max_log_size ); + + min_log_size = cvtest::clipInt( min_log_size, 0, 8 ); + max_log_size = cvtest::clipInt( max_log_size, 0, 10 ); + if( min_log_size > max_log_size ) + { + int t; + CV_SWAP( min_log_size, max_log_size, t ); + } + + return 0; +} + + +void CV_BaseShapeDescrTest::generate_point_set( void* points ) +{ + RNG& rng = ts->get_rng(); + int i, k, n, total, point_type; + CvSeqReader reader; + uchar* data = 0; + double a[4], b[4]; + + for( k = 0; k < 4; k++ ) + { + a[k] = high.val[k] - low.val[k]; + b[k] = low.val[k]; + } + memset( &reader, 0, sizeof(reader) ); + + if( CV_IS_SEQ(points) ) + { + CvSeq* ptseq = (CvSeq*)points; + total = ptseq->total; + point_type = CV_SEQ_ELTYPE(ptseq); + cvStartReadSeq( ptseq, &reader ); + } + else + { + CvMat* ptm = (CvMat*)points; + assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) ); + total = ptm->rows + ptm->cols - 1; + point_type = CV_MAT_TYPE(ptm->type); + data = ptm->data.ptr; + } + + n = CV_MAT_CN(point_type); + point_type = CV_MAT_DEPTH(point_type); + + assert( (point_type == CV_32S || point_type == CV_32F) && n <= 4 ); + + for( i = 0; i < total; i++ ) + { + int* pi; + float* pf; + if( reader.ptr ) + { + pi = (int*)reader.ptr; + pf = (float*)reader.ptr; + CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader ); + } + else + { + pi = (int*)data + i*n; + pf = (float*)data + i*n; + } + if( point_type == CV_32S ) + for( k = 0; k < n; k++ ) + pi[k] = cvRound(cvtest::randReal(rng)*a[k] + b[k]); + else + for( k = 0; k < n; k++ ) + pf[k] = (float)(cvtest::randReal(rng)*a[k] + b[k]); + } +} + + +int CV_BaseShapeDescrTest::prepare_test_case( int test_case_idx ) +{ + int size; + int use_storage = 0; + int point_type; + int i; + RNG& rng = ts->get_rng(); + + cvtest::BaseTest::prepare_test_case( test_case_idx ); + + clear(); + size = cvRound( exp((cvtest::randReal(rng) * (max_log_size - min_log_size) + min_log_size)*CV_LOG2) ); + use_storage = cvtest::randInt(rng) % 2; + point_type = CV_MAKETYPE(cvtest::randInt(rng) % + (enable_flt_points ? 2 : 1) ? CV_32F : CV_32S, dims); + + if( use_storage ) + { + storage = cvCreateMemStorage( (cvtest::randInt(rng)%10 + 1)*1024 ); + points1 = cvCreateSeq( point_type, sizeof(CvSeq), CV_ELEM_SIZE(point_type), storage ); + cvSeqPushMulti( points1, 0, size ); + points = points1; + } + else + { + int rows = 1, cols = size; + if( cvtest::randInt(rng) % 2 ) + rows = size, cols = 1; + + points2 = cvCreateMat( rows, cols, point_type ); + points = points2; + } + + for( i = 0; i < 4; i++ ) + { + low.val[i] = (cvtest::randReal(rng)-0.5)*low_high_range*2; + high.val[i] = (cvtest::randReal(rng)-0.5)*low_high_range*2; + if( low.val[i] > high.val[i] ) + { + double t; + CV_SWAP( low.val[i], high.val[i], t ); + } + if( high.val[i] < low.val[i] + 1 ) + high.val[i] += 1; + } + + generate_point_set( points ); + + test_cpp = (cvtest::randInt(rng) & 16) == 0; + return 1; +} + + +void CV_BaseShapeDescrTest::extract_points() +{ + if( points1 ) + { + points2 = cvCreateMat( 1, points1->total, CV_SEQ_ELTYPE(points1) ); + cvCvtSeqToArray( points1, points2->data.ptr ); + } + + if( CV_MAT_DEPTH(points2->type) != CV_32F && enable_flt_points ) + { + CvMat tmp = cvMat( points2->rows, points2->cols, + (points2->type & ~CV_MAT_DEPTH_MASK) | CV_32F, points2->data.ptr ); + cvConvert( points2, &tmp ); + } +} + + +void CV_BaseShapeDescrTest::run_func(void) +{ +} + + +int CV_BaseShapeDescrTest::validate_test_results( int /*test_case_idx*/ ) +{ + extract_points(); + return 0; +} + + +/****************************************************************************************\ +* Convex Hull Test * +\****************************************************************************************/ + +class CV_ConvHullTest : public CV_BaseShapeDescrTest +{ +public: + CV_ConvHullTest(); + virtual ~CV_ConvHullTest(); + void clear(); + +protected: + void run_func(void); + int prepare_test_case( int test_case_idx ); + int validate_test_results( int test_case_idx ); + + CvSeq* hull1; + CvMat* hull2; + void* hull_storage; + int orientation; + int return_points; +}; + + +CV_ConvHullTest::CV_ConvHullTest() +{ + hull1 = 0; + hull2 = 0; + hull_storage = 0; + orientation = return_points = 0; +} + + +CV_ConvHullTest::~CV_ConvHullTest() +{ + clear(); +} + + +void CV_ConvHullTest::clear() +{ + CV_BaseShapeDescrTest::clear(); + cvReleaseMat( &hull2 ); + hull1 = 0; + hull_storage = 0; +} + + +int CV_ConvHullTest::prepare_test_case( int test_case_idx ) +{ + int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); + int use_storage_for_hull = 0; + RNG& rng = ts->get_rng(); + + if( code <= 0 ) + return code; + + orientation = cvtest::randInt(rng) % 2 ? CV_CLOCKWISE : CV_COUNTER_CLOCKWISE; + return_points = cvtest::randInt(rng) % 2; + + use_storage_for_hull = (cvtest::randInt(rng) % 2) && !test_cpp; + if( use_storage_for_hull ) + { + if( !storage ) + storage = cvCreateMemStorage( (cvtest::randInt(rng)%10 + 1)*1024 ); + hull_storage = storage; + } + else + { + int rows, cols; + int sz = points1 ? points1->total : points2->cols + points2->rows - 1; + int point_type = points1 ? CV_SEQ_ELTYPE(points1) : CV_MAT_TYPE(points2->type); + + if( cvtest::randInt(rng) % 2 ) + rows = sz, cols = 1; + else + rows = 1, cols = sz; + + hull2 = cvCreateMat( rows, cols, return_points ? point_type : CV_32SC1 ); + hull_storage = hull2; + } + + return code; +} + + +void CV_ConvHullTest::run_func() +{ + if(!test_cpp) + hull1 = cvConvexHull2( points, hull_storage, orientation, return_points ); + else + { + cv::Mat _points = cv::cvarrToMat(points); + bool clockwise = orientation == CV_CLOCKWISE; + size_t n = 0; + if( !return_points ) + { + std::vector _hull; + cv::convexHull(_points, _hull, clockwise); + n = _hull.size(); + memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0])); + } + else if(_points.type() == CV_32SC2) + { + std::vector _hull; + cv::convexHull(_points, _hull, clockwise); + n = _hull.size(); + memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0])); + } + else if(_points.type() == CV_32FC2) + { + std::vector _hull; + cv::convexHull(_points, _hull, clockwise); + n = _hull.size(); + memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0])); + } + if(hull2->rows > hull2->cols) + hull2->rows = (int)n; + else + hull2->cols = (int)n; + } +} + + +int CV_ConvHullTest::validate_test_results( int test_case_idx ) +{ + int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); + CvMat* hull = 0; + CvMat* mask = 0; + int i, point_count, hull_count; + CvPoint2D32f *p, *h; + CvSeq header, hheader, *ptseq, *hseq; + CvSeqBlock block, hblock; + + if( points1 ) + ptseq = points1; + else + ptseq = cvMakeSeqHeaderForArray( CV_MAT_TYPE(points2->type), + sizeof(CvSeq), CV_ELEM_SIZE(points2->type), points2->data.ptr, + points2->rows + points2->cols - 1, &header, &block ); + point_count = ptseq->total; + p = (CvPoint2D32f*)(points2->data.ptr); + + if( hull1 ) + hseq = hull1; + else + hseq = cvMakeSeqHeaderForArray( CV_MAT_TYPE(hull2->type), + sizeof(CvSeq), CV_ELEM_SIZE(hull2->type), hull2->data.ptr, + hull2->rows + hull2->cols - 1, &hheader, &hblock ); + hull_count = hseq->total; + hull = cvCreateMat( 1, hull_count, CV_32FC2 ); + mask = cvCreateMat( 1, hull_count, CV_8UC1 ); + cvZero( mask ); + h = (CvPoint2D32f*)(hull->data.ptr); + + // extract convex hull points + if( return_points ) + { + cvCvtSeqToArray( hseq, hull->data.ptr ); + if( CV_SEQ_ELTYPE(hseq) != CV_32FC2 ) + { + CvMat tmp = cvMat( hull->rows, hull->cols, CV_32SC2, hull->data.ptr ); + cvConvert( &tmp, hull ); + } + } + else + { + CvSeqReader reader; + cvStartReadSeq( hseq, &reader ); + + for( i = 0; i < hull_count; i++ ) + { + schar* ptr = reader.ptr; + int idx; + CV_NEXT_SEQ_ELEM( hseq->elem_size, reader ); + + if( hull1 ) + idx = cvSeqElemIdx( ptseq, *(uchar**)ptr ); + else + idx = *(int*)ptr; + + if( idx < 0 || idx >= point_count ) + { + ts->printf( cvtest::TS::LOG, "Invalid convex hull point #%d\n", i ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + h[i] = p[idx]; + } + } + + // check that the convex hull is a convex polygon + if( hull_count >= 3 ) + { + CvPoint2D32f pt0 = h[hull_count-1]; + for( i = 0; i < hull_count; i++ ) + { + int j = i+1; + CvPoint2D32f pt1 = h[i], pt2 = h[j < hull_count ? j : 0]; + float dx0 = pt1.x - pt0.x, dy0 = pt1.y - pt0.y; + float dx1 = pt2.x - pt1.x, dy1 = pt2.y - pt1.y; + double t = (double)dx0*dy1 - (double)dx1*dy0; + if( (t < 0) ^ (orientation != CV_COUNTER_CLOCKWISE) ) + { + ts->printf( cvtest::TS::LOG, "The convex hull is not convex or has a wrong orientation (vtx %d)\n", i ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + pt0 = pt1; + } + } + + // check that all the points are inside the hull or on the hull edge + // and at least hull_point points are at the hull vertices + for( i = 0; i < point_count; i++ ) + { + int idx = 0, on_edge = 0; + double result = cvTsPointPolygonTest( p[i], h, hull_count, &idx, &on_edge ); + + if( result < 0 ) + { + ts->printf( cvtest::TS::LOG, "The point #%d is outside of the convex hull\n", i ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + + if( result < FLT_EPSILON && !on_edge ) + mask->data.ptr[idx] = (uchar)1; + } + + if( cvNorm( mask, 0, CV_L1 ) != hull_count ) + { + ts->printf( cvtest::TS::LOG, "Not every convex hull vertex coincides with some input point\n" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + +_exit_: + + cvReleaseMat( &hull ); + cvReleaseMat( &mask ); + if( code < 0 ) + ts->set_failed_test_info( code ); + return code; +} + + +/****************************************************************************************\ +* MinAreaRect Test * +\****************************************************************************************/ + +class CV_MinAreaRectTest : public CV_BaseShapeDescrTest +{ +public: + CV_MinAreaRectTest(); + +protected: + void run_func(void); + int validate_test_results( int test_case_idx ); + + CvBox2D box; + CvPoint2D32f box_pt[4]; +}; + + +CV_MinAreaRectTest::CV_MinAreaRectTest() +{ +} + + +void CV_MinAreaRectTest::run_func() +{ + if(!test_cpp) + { + box = cvMinAreaRect2( points, storage ); + cvBoxPoints( box, box_pt ); + } + else + { + cv::RotatedRect r = cv::minAreaRect(cv::cvarrToMat(points)); + box = (CvBox2D)r; + r.points((cv::Point2f*)box_pt); + } +} + + +int CV_MinAreaRectTest::validate_test_results( int test_case_idx ) +{ + double eps = 1e-1; + int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); + int i, j, point_count = points2->rows + points2->cols - 1; + CvPoint2D32f *p = (CvPoint2D32f*)(points2->data.ptr); + int mask[] = {0,0,0,0}; + + // check that the bounding box is a rotated rectangle: + // 1. diagonals should be equal + // 2. they must intersect in their middle points + { + double d0 = cvTsDist( box_pt[0], box_pt[2] ); + double d1 = cvTsDist( box_pt[1], box_pt[3] ); + + double x0 = (box_pt[0].x + box_pt[2].x)*0.5; + double y0 = (box_pt[0].y + box_pt[2].y)*0.5; + double x1 = (box_pt[1].x + box_pt[3].x)*0.5; + double y1 = (box_pt[1].y + box_pt[3].y)*0.5; + + if( fabs(d0 - d1) + fabs(x0 - x1) + fabs(y0 - y1) > eps*MAX(d0,d1) ) + { + ts->printf( cvtest::TS::LOG, "The bounding box is not a rectangle\n" ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + } + +#if 0 + { + int n = 4; + double a = 8, c = 8, b = 100, d = 150; + CvPoint bp[4], *bpp = bp; + cvNamedWindow( "test", 1 ); + IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 ); + cvZero(img); + for( i = 0; i < point_count; i++ ) + cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*c+d)), 3, CV_RGB(0,255,0), -1 ); + for( i = 0; i < n; i++ ) + bp[i] = cvPoint(cvRound(box_pt[i].x*a+b),cvRound(box_pt[i].y*c+d)); + cvPolyLine( img, &bpp, &n, 1, 1, CV_RGB(255,255,0), 1, CV_AA, 0 ); + cvShowImage( "test", img ); + cvWaitKey(); + cvReleaseImage(&img); + } +#endif + + // check that the box includes all the points + // and there is at least one point at (or very close to) every box side + for( i = 0; i < point_count; i++ ) + { + int idx = 0, on_edge = 0; + double result = cvTsPointPolygonTest( p[i], box_pt, 4, &idx, &on_edge ); + if( result < -eps ) + { + ts->printf( cvtest::TS::LOG, "The point #%d is outside of the box\n", i ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + + if( result < eps ) + { + for( j = 0; j < 4; j++ ) + { + double d = cvTsPtLineDist( p[i], box_pt[(j-1)&3], box_pt[j] ); + if( d < eps ) + mask[j] = (uchar)1; + } + } + } + + if( mask[0] + mask[1] + mask[2] + mask[3] != 4 ) + { + ts->printf( cvtest::TS::LOG, "Not every box side has a point nearby\n" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + +_exit_: + + if( code < 0 ) + ts->set_failed_test_info( code ); + return code; +} + + +/****************************************************************************************\ +* MinEnclosingCircle Test * +\****************************************************************************************/ + +class CV_MinCircleTest : public CV_BaseShapeDescrTest +{ +public: + CV_MinCircleTest(); + +protected: + void run_func(void); + int validate_test_results( int test_case_idx ); + + CvPoint2D32f center; + float radius; +}; + + +CV_MinCircleTest::CV_MinCircleTest() +{ +} + + +void CV_MinCircleTest::run_func() +{ + if(!test_cpp) + cvMinEnclosingCircle( points, ¢er, &radius ); + else + cv::minEnclosingCircle(cv::cvarrToMat(points), (cv::Point2f&)center, radius); +} + + +int CV_MinCircleTest::validate_test_results( int test_case_idx ) +{ + double eps = 1.03; + int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); + int i, j = 0, point_count = points2->rows + points2->cols - 1; + CvPoint2D32f *p = (CvPoint2D32f*)(points2->data.ptr); + CvPoint2D32f v[3]; + +#if 0 + { + double a = 2, b = 200, d = 400; + cvNamedWindow( "test", 1 ); + IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 ); + cvZero(img); + for( i = 0; i < point_count; i++ ) + cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*a+d)), 3, CV_RGB(0,255,0), -1 ); + cvCircle( img, cvPoint(cvRound(center.x*a+b),cvRound(center.y*a+d)), + cvRound(radius*a), CV_RGB(255,255,0), 1 ); + cvShowImage( "test", img ); + cvWaitKey(); + cvReleaseImage(&img); + } +#endif + + // check that the circle contains all the points inside and + // remember at most 3 points that are close to the boundary + for( i = 0; i < point_count; i++ ) + { + double d = cvTsDist( p[i], center ); + if( d > radius ) + { + ts->printf( cvtest::TS::LOG, "The point #%d is outside of the circle\n", i ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + + if( radius - d < eps*radius && j < 3 ) + v[j++] = p[i]; + } + + if( point_count >= 2 && (j < 2 || (j == 2 && cvTsDist(v[0],v[1]) < (radius-1)*2/eps)) ) + { + ts->printf( cvtest::TS::LOG, + "There should be at at least 3 points near the circle boundary or 2 points on the diameter\n" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + +_exit_: + + if( code < 0 ) + ts->set_failed_test_info( code ); + return code; +} + + +/****************************************************************************************\ +* Perimeter Test * +\****************************************************************************************/ + +class CV_PerimeterTest : public CV_BaseShapeDescrTest +{ +public: + CV_PerimeterTest(); + +protected: + int prepare_test_case( int test_case_idx ); + void run_func(void); + int validate_test_results( int test_case_idx ); + CvSlice slice; + int is_closed; + double result; +}; + + +CV_PerimeterTest::CV_PerimeterTest() +{ +} + + +int CV_PerimeterTest::prepare_test_case( int test_case_idx ) +{ + int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); + RNG& rng = ts->get_rng(); + int total; + + if( code < 0 ) + return code; + + is_closed = cvtest::randInt(rng) % 2; + + if( points1 ) + { + points1->flags |= CV_SEQ_KIND_CURVE; + if( is_closed ) + points1->flags |= CV_SEQ_FLAG_CLOSED; + total = points1->total; + } + else + total = points2->cols + points2->rows - 1; + + if( (cvtest::randInt(rng) % 3) && !test_cpp ) + { + slice.start_index = cvtest::randInt(rng) % total; + slice.end_index = cvtest::randInt(rng) % total; + } + else + slice = CV_WHOLE_SEQ; + + return 1; +} + + +void CV_PerimeterTest::run_func() +{ + if(!test_cpp) + result = cvArcLength( points, slice, points1 ? -1 : is_closed ); + else + result = cv::arcLength(cv::cvarrToMat(points), + !points1 ? is_closed != 0 : (points1->flags & CV_SEQ_FLAG_CLOSED) != 0); +} + + +int CV_PerimeterTest::validate_test_results( int test_case_idx ) +{ + int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); + int i, len = slice.end_index - slice.start_index, total = points2->cols + points2->rows - 1; + double result0 = 0; + CvPoint2D32f prev_pt, pt, *ptr; + + if( len < 0 ) + len += total; + + len = MIN( len, total ); + len -= !is_closed && len == total; + + ptr = (CvPoint2D32f*)points2->data.fl; + prev_pt = ptr[slice.start_index % total]; + + for( i = 1; i <= len; i++ ) + { + pt = ptr[(i + slice.start_index) % total]; + double dx = pt.x - prev_pt.x, dy = pt.y - prev_pt.y; + result0 += sqrt(dx*dx + dy*dy); + prev_pt = pt; + } + + if( cvIsNaN(result) || cvIsInf(result) ) + { + ts->printf( cvtest::TS::LOG, "cvArcLength() returned invalid value (%g)\n", result ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + } + else if( fabs(result - result0) > FLT_EPSILON*100*result0 ) + { + ts->printf( cvtest::TS::LOG, "The function returned %g, while the correct result is %g\n", result, result0 ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + + if( code < 0 ) + ts->set_failed_test_info( code ); + return code; +} + + +/****************************************************************************************\ +* FitEllipse Test * +\****************************************************************************************/ + +class CV_FitEllipseTest : public CV_BaseShapeDescrTest +{ +public: + CV_FitEllipseTest(); + +protected: + int prepare_test_case( int test_case_idx ); + void generate_point_set( void* points ); + void run_func(void); + int validate_test_results( int test_case_idx ); + CvBox2D box0, box; + double min_ellipse_size, max_noise; +}; + + +CV_FitEllipseTest::CV_FitEllipseTest() +{ + min_log_size = 5; // for robust ellipse fitting a dozen of points is needed at least + max_log_size = 10; + min_ellipse_size = 10; + max_noise = 0.05; +} + + +void CV_FitEllipseTest::generate_point_set( void* points ) +{ + RNG& rng = ts->get_rng(); + int i, total, point_type; + CvSeqReader reader; + uchar* data = 0; + double a, b; + + box0.center.x = (float)((low.val[0] + high.val[0])*0.5); + box0.center.y = (float)((low.val[1] + high.val[1])*0.5); + box0.size.width = (float)(MAX(high.val[0] - low.val[0], min_ellipse_size)*2); + box0.size.height = (float)(MAX(high.val[1] - low.val[1], min_ellipse_size)*2); + box0.angle = (float)(cvtest::randReal(rng)*180); + a = cos(box0.angle*CV_PI/180.); + b = sin(box0.angle*CV_PI/180.); + + if( box0.size.width > box0.size.height ) + { + float t; + CV_SWAP( box0.size.width, box0.size.height, t ); + } + memset( &reader, 0, sizeof(reader) ); + + if( CV_IS_SEQ(points) ) + { + CvSeq* ptseq = (CvSeq*)points; + total = ptseq->total; + point_type = CV_SEQ_ELTYPE(ptseq); + cvStartReadSeq( ptseq, &reader ); + } + else + { + CvMat* ptm = (CvMat*)points; + assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) ); + total = ptm->rows + ptm->cols - 1; + point_type = CV_MAT_TYPE(ptm->type); + data = ptm->data.ptr; + } + + assert( point_type == CV_32SC2 || point_type == CV_32FC2 ); + + for( i = 0; i < total; i++ ) + { + CvPoint* pp; + CvPoint2D32f p; + double angle = cvtest::randReal(rng)*CV_PI*2; + double x = box0.size.height*0.5*(cos(angle) + (cvtest::randReal(rng)-0.5)*2*max_noise); + double y = box0.size.width*0.5*(sin(angle) + (cvtest::randReal(rng)-0.5)*2*max_noise); + p.x = (float)(box0.center.x + a*x + b*y); + p.y = (float)(box0.center.y - b*x + a*y); + + if( reader.ptr ) + { + pp = (CvPoint*)reader.ptr; + CV_NEXT_SEQ_ELEM( sizeof(*pp), reader ); + } + else + pp = ((CvPoint*)data) + i; + if( point_type == CV_32SC2 ) + { + pp->x = cvRound(p.x); + pp->y = cvRound(p.y); + } + else + *(CvPoint2D32f*)pp = p; + } +} + + +int CV_FitEllipseTest::prepare_test_case( int test_case_idx ) +{ + min_log_size = MAX(min_log_size,4); + max_log_size = MAX(min_log_size,max_log_size); + return CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); +} + + +void CV_FitEllipseTest::run_func() +{ + if(!test_cpp) + box = cvFitEllipse2( points ); + else + box = (CvBox2D)cv::fitEllipse(cv::cvarrToMat(points)); +} + + +int CV_FitEllipseTest::validate_test_results( int test_case_idx ) +{ + int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); + double diff_angle; + + if( cvIsNaN(box.center.x) || cvIsInf(box.center.x) || + cvIsNaN(box.center.y) || cvIsInf(box.center.y) || + cvIsNaN(box.size.width) || cvIsInf(box.size.width) || + cvIsNaN(box.size.height) || cvIsInf(box.size.height) || + cvIsNaN(box.angle) || cvIsInf(box.angle) ) + { + ts->printf( cvtest::TS::LOG, "Some of the computed ellipse parameters are invalid (x=%g,y=%g,w=%g,h=%g,angle=%g)\n", + box.center.x, box.center.y, box.size.width, box.size.height, box.angle ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + + box.angle = (float)(90-box.angle); + if( box.angle < 0 ) + box.angle += 360; + if( box.angle > 360 ) + box.angle -= 360; + + if( fabs(box.center.x - box0.center.x) > 3 || + fabs(box.center.y - box0.center.y) > 3 || + fabs(box.size.width - box0.size.width) > 0.1*fabs(box0.size.width) || + fabs(box.size.height - box0.size.height) > 0.1*fabs(box0.size.height) ) + { + ts->printf( cvtest::TS::LOG, "The computed ellipse center and/or size are incorrect:\n\t" + "(x=%.1f,y=%.1f,w=%.1f,h=%.1f), while it should be (x=%.1f,y=%.1f,w=%.1f,h=%.1f)\n", + box.center.x, box.center.y, box.size.width, box.size.height, + box0.center.x, box0.center.y, box0.size.width, box0.size.height ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + + diff_angle = fabs(box0.angle - box.angle); + diff_angle = MIN( diff_angle, fabs(diff_angle - 360)); + diff_angle = MIN( diff_angle, fabs(diff_angle - 180)); + + if( box0.size.height >= 1.3*box0.size.width && diff_angle > 30 ) + { + ts->printf( cvtest::TS::LOG, "Incorrect ellipse angle (=%1.f, should be %1.f)\n", + box.angle, box0.angle ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + +_exit_: + +#if 0 + cvNamedWindow( "test", 0 ); + IplImage* img = cvCreateImage( cvSize(cvRound(low_high_range*4), + cvRound(low_high_range*4)), 8, 3 ); + cvZero( img ); + + box.center.x += (float)low_high_range*2; + box.center.y += (float)low_high_range*2; + cvEllipseBox( img, box, CV_RGB(255,0,0), 3, 8 ); + + for( int i = 0; i < points2->rows + points2->cols - 1; i++ ) + { + CvPoint pt; + pt.x = cvRound(points2->data.fl[i*2] + low_high_range*2); + pt.y = cvRound(points2->data.fl[i*2+1] + low_high_range*2); + cvCircle( img, pt, 1, CV_RGB(255,255,255), -1, 8 ); + } + + cvShowImage( "test", img ); + cvReleaseImage( &img ); + cvWaitKey(0); +#endif + + if( code < 0 ) + { + ts->set_failed_test_info( code ); + } + return code; +} + + +/****************************************************************************************\ +* FitLine Test * +\****************************************************************************************/ + +class CV_FitLineTest : public CV_BaseShapeDescrTest +{ +public: + CV_FitLineTest(); + +protected: + int prepare_test_case( int test_case_idx ); + void generate_point_set( void* points ); + void run_func(void); + int validate_test_results( int test_case_idx ); + double max_noise; + float line[6], line0[6]; + int dist_type; + double reps, aeps; +}; + + +CV_FitLineTest::CV_FitLineTest() +{ + min_log_size = 5; // for robust ellipse fitting a dozen of points is needed at least + max_log_size = 10; + max_noise = 0.05; +} + + +void CV_FitLineTest::generate_point_set( void* points ) +{ + RNG& rng = ts->get_rng(); + int i, k, n, total, point_type; + CvSeqReader reader; + uchar* data = 0; + double s = 0; + + n = dims; + for( k = 0; k < n; k++ ) + { + line0[k+n] = (float)((low.val[k] + high.val[k])*0.5); + line0[k] = (float)(high.val[k] - low.val[k]); + if( cvtest::randInt(rng) % 2 ) + line0[k] = -line0[k]; + s += (double)line0[k]*line0[k]; + } + + s = 1./sqrt(s); + for( k = 0; k < n; k++ ) + line0[k] = (float)(line0[k]*s); + + memset( &reader, 0, sizeof(reader) ); + + if( CV_IS_SEQ(points) ) + { + CvSeq* ptseq = (CvSeq*)points; + total = ptseq->total; + point_type = CV_MAT_DEPTH(CV_SEQ_ELTYPE(ptseq)); + cvStartReadSeq( ptseq, &reader ); + } + else + { + CvMat* ptm = (CvMat*)points; + assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) ); + total = ptm->rows + ptm->cols - 1; + point_type = CV_MAT_DEPTH(CV_MAT_TYPE(ptm->type)); + data = ptm->data.ptr; + } + + for( i = 0; i < total; i++ ) + { + int* pi; + float* pf; + float p[4], t; + if( reader.ptr ) + { + pi = (int*)reader.ptr; + pf = (float*)reader.ptr; + CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader ); + } + else + { + pi = (int*)data + i*n; + pf = (float*)data + i*n; + } + + t = (float)((cvtest::randReal(rng)-0.5)*low_high_range*2); + + for( k = 0; k < n; k++ ) + p[k] = (float)((cvtest::randReal(rng)-0.5)*max_noise*2 + t*line0[k] + line0[k+n]); + + if( point_type == CV_32S ) + for( k = 0; k < n; k++ ) + pi[k] = cvRound(p[k]); + else + for( k = 0; k < n; k++ ) + pf[k] = p[k]; + } +} + + +int CV_FitLineTest::prepare_test_case( int test_case_idx ) +{ + RNG& rng = ts->get_rng(); + dims = cvtest::randInt(rng) % 2 + 2; + min_log_size = MAX(min_log_size,5); + max_log_size = MAX(min_log_size,max_log_size); + int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); + dist_type = cvtest::randInt(rng) % 6 + 1; + dist_type += dist_type == CV_DIST_C; + reps = 0.1; aeps = 0.01; + return code; +} + + +void CV_FitLineTest::run_func() +{ + if(!test_cpp) + cvFitLine( points, dist_type, 0, reps, aeps, line ); + else if(dims == 2) + cv::fitLine(cv::cvarrToMat(points), (cv::Vec4f&)line[0], dist_type, 0, reps, aeps); + else + cv::fitLine(cv::cvarrToMat(points), (cv::Vec6f&)line[0], dist_type, 0, reps, aeps); +} + + +int CV_FitLineTest::validate_test_results( int test_case_idx ) +{ + int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); + int k, max_k = 0; + double vec_diff = 0, t; + + for( k = 0; k < dims*2; k++ ) + { + if( cvIsNaN(line[k]) || cvIsInf(line[k]) ) + { + ts->printf( cvtest::TS::LOG, "Some of the computed line parameters are invalid (line[%d]=%g)\n", + k, line[k] ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + } + + if( fabs(line0[1]) > fabs(line0[0]) ) + max_k = 1; + if( fabs(line0[dims-1]) > fabs(line0[max_k]) ) + max_k = dims-1; + if( line0[max_k] < 0 ) + for( k = 0; k < dims; k++ ) + line0[k] = -line0[k]; + if( line[max_k] < 0 ) + for( k = 0; k < dims; k++ ) + line[k] = -line[k]; + + for( k = 0; k < dims; k++ ) + { + double dt = line[k] - line0[k]; + vec_diff += dt*dt; + } + + if( sqrt(vec_diff) > 0.05 ) + { + if( dims == 2 ) + ts->printf( cvtest::TS::LOG, + "The computed line vector (%.2f,%.2f) is different from the actual (%.2f,%.2f)\n", + line[0], line[1], line0[0], line0[1] ); + else + ts->printf( cvtest::TS::LOG, + "The computed line vector (%.2f,%.2f,%.2f) is different from the actual (%.2f,%.2f,%.2f)\n", + line[0], line[1], line[2], line0[0], line0[1], line0[2] ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + + t = (line[max_k+dims] - line0[max_k+dims])/line0[max_k]; + for( k = 0; k < dims; k++ ) + { + double p = line0[k+dims] + t*line0[k] - line[k+dims]; + vec_diff += p*p; + } + + if( sqrt(vec_diff) > 1*MAX(fabs(t),1) ) + { + if( dims == 2 ) + ts->printf( cvtest::TS::LOG, + "The computed line point (%.2f,%.2f) is too far from the actual line\n", + line[2]+line0[2], line[3]+line0[3] ); + else + ts->printf( cvtest::TS::LOG, + "The computed line point (%.2f,%.2f,%.2f) is too far from the actual line\n", + line[3]+line0[3], line[4]+line0[4], line[5]+line0[5] ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + +_exit_: + + if( code < 0 ) + { + ts->set_failed_test_info( code ); + } + return code; +} + + +/****************************************************************************************\ +* ContourMoments Test * +\****************************************************************************************/ + + +static void +cvTsGenerateTousledBlob( CvPoint2D32f center, CvSize2D32f axes, + double max_r_scale, double angle, CvArr* points, RNG& rng ) +{ + int i, total, point_type; + uchar* data = 0; + CvSeqReader reader; + memset( &reader, 0, sizeof(reader) ); + + if( CV_IS_SEQ(points) ) + { + CvSeq* ptseq = (CvSeq*)points; + total = ptseq->total; + point_type = CV_SEQ_ELTYPE(ptseq); + cvStartReadSeq( ptseq, &reader ); + } + else + { + CvMat* ptm = (CvMat*)points; + assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) ); + total = ptm->rows + ptm->cols - 1; + point_type = CV_MAT_TYPE(ptm->type); + data = ptm->data.ptr; + } + + assert( point_type == CV_32SC2 || point_type == CV_32FC2 ); + + for( i = 0; i < total; i++ ) + { + CvPoint* pp; + CvPoint2D32f p; + + double phi0 = 2*CV_PI*i/total; + double phi = CV_PI*angle/180.; + double t = cvtest::randReal(rng)*max_r_scale + (1 - max_r_scale); + double ta = axes.height*t; + double tb = axes.width*t; + double c0 = cos(phi0)*ta, s0 = sin(phi0)*tb; + double c = cos(phi), s = sin(phi); + p.x = (float)(c0*c - s0*s + center.x); + p.y = (float)(c0*s + s0*c + center.y); + + if( reader.ptr ) + { + pp = (CvPoint*)reader.ptr; + CV_NEXT_SEQ_ELEM( sizeof(*pp), reader ); + } + else + pp = ((CvPoint*)data) + i; + + if( point_type == CV_32SC2 ) + { + pp->x = cvRound(p.x); + pp->y = cvRound(p.y); + } + else + *(CvPoint2D32f*)pp = p; + } +} + + +class CV_ContourMomentsTest : public CV_BaseShapeDescrTest +{ +public: + CV_ContourMomentsTest(); + +protected: + int prepare_test_case( int test_case_idx ); + void generate_point_set( void* points ); + void run_func(void); + int validate_test_results( int test_case_idx ); + CvMoments moments0, moments; + double area0, area; + CvSize2D32f axes; + CvPoint2D32f center; + int max_max_r_scale; + double max_r_scale, angle; + CvSize img_size; +}; + + +CV_ContourMomentsTest::CV_ContourMomentsTest() +{ + min_log_size = 3; + max_log_size = 8; + max_max_r_scale = 15; + low_high_range = 200; + enable_flt_points = false; +} + + +void CV_ContourMomentsTest::generate_point_set( void* points ) +{ + RNG& rng = ts->get_rng(); + float max_sz; + + axes.width = (float)((cvtest::randReal(rng)*0.9 + 0.1)*low_high_range); + axes.height = (float)((cvtest::randReal(rng)*0.9 + 0.1)*low_high_range); + max_sz = MAX(axes.width, axes.height); + + img_size.width = img_size.height = cvRound(low_high_range*2.2); + + center.x = (float)(img_size.width*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.width - max_sz*2)*0.8); + center.y = (float)(img_size.height*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.height - max_sz*2)*0.8); + + assert( 0 < center.x - max_sz && center.x + max_sz < img_size.width && + 0 < center.y - max_sz && center.y + max_sz < img_size.height ); + + max_r_scale = cvtest::randReal(rng)*max_max_r_scale*0.01; + angle = cvtest::randReal(rng)*360; + + cvTsGenerateTousledBlob( center, axes, max_r_scale, angle, points, rng ); + + if( points1 ) + points1->flags = CV_SEQ_MAGIC_VAL + CV_SEQ_POLYGON; +} + + +int CV_ContourMomentsTest::prepare_test_case( int test_case_idx ) +{ + min_log_size = MAX(min_log_size,3); + max_log_size = MIN(max_log_size,8); + max_log_size = MAX(min_log_size,max_log_size); + int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); + return code; +} + + +void CV_ContourMomentsTest::run_func() +{ + if(!test_cpp) + { + cvMoments( points, &moments ); + area = cvContourArea( points ); + } + else + { + moments = (CvMoments)cv::moments(cv::cvarrToMat(points)); + area = cv::contourArea(cv::cvarrToMat(points)); + } +} + + +int CV_ContourMomentsTest::validate_test_results( int test_case_idx ) +{ + int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); + int i, n = (int)(sizeof(moments)/sizeof(moments.inv_sqrt_m00)); + CvMat* img = cvCreateMat( img_size.height, img_size.width, CV_8UC1 ); + CvPoint* pt = (CvPoint*)points2->data.i; + int count = points2->cols + points2->rows - 1; + double max_v0 = 0; + + cvZero(img); + cvFillPoly( img, &pt, &count, 1, cvScalarAll(1)); + cvMoments( img, &moments0 ); + + for( i = 0; i < n; i++ ) + { + double t = fabs((&moments0.m00)[i]); + max_v0 = MAX(max_v0, t); + } + + for( i = 0; i <= n; i++ ) + { + double v = i < n ? (&moments.m00)[i] : area; + double v0 = i < n ? (&moments0.m00)[i] : moments0.m00; + + if( cvIsNaN(v) || cvIsInf(v) ) + { + ts->printf( cvtest::TS::LOG, + "The contour %s is invalid (=%g)\n", i < n ? "moment" : "area", v ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + break; + } + + if( fabs(v - v0) > 0.1*max_v0 ) + { + ts->printf( cvtest::TS::LOG, + "The computed contour %s is %g, while it should be %g\n", + i < n ? "moment" : "area", v, v0 ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + break; + } + } + + if( code < 0 ) + { +#if 0 + cvCmpS( img, 0, img, CV_CMP_GT ); + cvNamedWindow( "test", 1 ); + cvShowImage( "test", img ); + cvWaitKey(); +#endif + ts->set_failed_test_info( code ); + } + + cvReleaseMat( &img ); + return code; +} + + +TEST(Imgproc_ConvexHull, accuracy) { CV_ConvHullTest test; test.safe_run(); } +TEST(Imgproc_MinAreaRect, accuracy) { CV_MinAreaRectTest test; test.safe_run(); } +TEST(Imgproc_MinCircle, accuracy) { CV_MinCircleTest test; test.safe_run(); } +TEST(Imgproc_ContourPerimeter, accuracy) { CV_PerimeterTest test; test.safe_run(); } +TEST(Imgproc_FitEllipse, accuracy) { CV_FitEllipseTest test; test.safe_run(); } +TEST(Imgproc_FitLine, accuracy) { CV_FitLineTest test; test.safe_run(); } +TEST(Imgproc_ContourMoments, accuracy) { CV_ContourMomentsTest test; test.safe_run(); } + +/* End of file. */ + diff --git a/modules/imgproc/test/test_distancetransform.cpp b/modules/imgproc/test/test_distancetransform.cpp new file mode 100644 index 000000000..bdf205b45 --- /dev/null +++ b/modules/imgproc/test/test_distancetransform.cpp @@ -0,0 +1,297 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_DisTransTest : public cvtest::ArrayTest +{ +public: + CV_DisTransTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int ); + + void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); + int prepare_test_case( int test_case_idx ); + + int mask_size; + int dist_type; + int fill_labels; + float mask[3]; +}; + + +CV_DisTransTest::CV_DisTransTest() +{ + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + optional_mask = false; + element_wise_relative_error = true; +} + + +void CV_DisTransTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + types[INPUT][0] = CV_8UC1; + types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_32FC1; + types[OUTPUT][1] = types[REF_OUTPUT][1] = CV_32SC1; + + if( cvtest::randInt(rng) & 1 ) + { + mask_size = 3; + dist_type = cvtest::randInt(rng) % 4; + dist_type = dist_type == 0 ? CV_DIST_C : dist_type == 1 ? CV_DIST_L1 : + dist_type == 2 ? CV_DIST_L2 : CV_DIST_USER; + } + else + { + mask_size = 5; + dist_type = cvtest::randInt(rng) % 10; + dist_type = dist_type == 0 ? CV_DIST_C : dist_type == 1 ? CV_DIST_L1 : + dist_type < 6 ? CV_DIST_L2 : CV_DIST_USER; + } + + // for now, check only the "labeled" distance transform mode + fill_labels = 0; + + if( !fill_labels ) + sizes[OUTPUT][1] = sizes[REF_OUTPUT][1] = cvSize(0,0); + + if( dist_type == CV_DIST_USER ) + { + mask[0] = (float)(1.1 - cvtest::randReal(rng)*0.2); + mask[1] = (float)(1.9 - cvtest::randReal(rng)*0.8); + mask[2] = (float)(3. - cvtest::randReal(rng)); + } +} + + +double CV_DisTransTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + Size sz = test_mat[INPUT][0].size(); + return dist_type == CV_DIST_C || dist_type == CV_DIST_L1 ? 0 : 0.01*MAX(sz.width, sz.height); +} + + +void CV_DisTransTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) +{ + cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high ); + if( i == INPUT && CV_MAT_DEPTH(type) == CV_8U ) + { + low = Scalar::all(0); + high = Scalar::all(10); + } +} + +int CV_DisTransTest::prepare_test_case( int test_case_idx ) +{ + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + // the function's response to an "all-nonzeros" image is not determined, + // so put at least one zero point + Mat& mat = test_mat[INPUT][0]; + RNG& rng = ts->get_rng(); + int i = cvtest::randInt(rng) % mat.rows; + int j = cvtest::randInt(rng) % mat.cols; + mat.at(i,j) = 0; + } + + return code; +} + + +void CV_DisTransTest::run_func() +{ + cvDistTransform( test_array[INPUT][0], test_array[OUTPUT][0], dist_type, mask_size, + dist_type == CV_DIST_USER ? mask : 0, test_array[OUTPUT][1] ); +} + + +static void +cvTsDistTransform( const CvMat* _src, CvMat* _dst, int dist_type, + int mask_size, float* _mask, CvMat* /*_labels*/ ) +{ + int i, j, k; + int width = _src->cols, height = _src->rows; + const float init_val = 1e6; + float mask[3]; + CvMat* temp; + int ofs[16]; + float delta[16]; + int tstep, count; + + assert( mask_size == 3 || mask_size == 5 ); + + if( dist_type == CV_DIST_USER ) + memcpy( mask, _mask, sizeof(mask) ); + else if( dist_type == CV_DIST_C ) + { + mask_size = 3; + mask[0] = mask[1] = 1.f; + } + else if( dist_type == CV_DIST_L1 ) + { + mask_size = 3; + mask[0] = 1.f; + mask[1] = 2.f; + } + else if( mask_size == 3 ) + { + mask[0] = 0.955f; + mask[1] = 1.3693f; + } + else + { + mask[0] = 1.0f; + mask[1] = 1.4f; + mask[2] = 2.1969f; + } + + temp = cvCreateMat( height + mask_size-1, width + mask_size-1, CV_32F ); + tstep = temp->step / sizeof(float); + + if( mask_size == 3 ) + { + count = 4; + ofs[0] = -1; delta[0] = mask[0]; + ofs[1] = -tstep-1; delta[1] = mask[1]; + ofs[2] = -tstep; delta[2] = mask[0]; + ofs[3] = -tstep+1; delta[3] = mask[1]; + } + else + { + count = 8; + ofs[0] = -1; delta[0] = mask[0]; + ofs[1] = -tstep-2; delta[1] = mask[2]; + ofs[2] = -tstep-1; delta[2] = mask[1]; + ofs[3] = -tstep; delta[3] = mask[0]; + ofs[4] = -tstep+1; delta[4] = mask[1]; + ofs[5] = -tstep+2; delta[5] = mask[2]; + ofs[6] = -tstep*2-1; delta[6] = mask[2]; + ofs[7] = -tstep*2+1; delta[7] = mask[2]; + } + + for( i = 0; i < mask_size/2; i++ ) + { + float* t0 = (float*)(temp->data.ptr + i*temp->step); + float* t1 = (float*)(temp->data.ptr + (temp->rows - i - 1)*temp->step); + + for( j = 0; j < width + mask_size - 1; j++ ) + t0[j] = t1[j] = init_val; + } + + for( i = 0; i < height; i++ ) + { + uchar* s = _src->data.ptr + i*_src->step; + float* tmp = (float*)(temp->data.ptr + temp->step*(i + (mask_size/2))) + (mask_size/2); + + for( j = 0; j < mask_size/2; j++ ) + tmp[-j-1] = tmp[j + width] = init_val; + + for( j = 0; j < width; j++ ) + { + if( s[j] == 0 ) + tmp[j] = 0; + else + { + float min_dist = init_val; + for( k = 0; k < count; k++ ) + { + float t = tmp[j+ofs[k]] + delta[k]; + if( min_dist > t ) + min_dist = t; + } + tmp[j] = min_dist; + } + } + } + + for( i = height - 1; i >= 0; i-- ) + { + float* d = (float*)(_dst->data.ptr + i*_dst->step); + float* tmp = (float*)(temp->data.ptr + temp->step*(i + (mask_size/2))) + (mask_size/2); + + for( j = width - 1; j >= 0; j-- ) + { + float min_dist = tmp[j]; + if( min_dist > mask[0] ) + { + for( k = 0; k < count; k++ ) + { + float t = tmp[j-ofs[k]] + delta[k]; + if( min_dist > t ) + min_dist = t; + } + tmp[j] = min_dist; + } + d[j] = min_dist; + } + } + + cvReleaseMat( &temp ); +} + + +void CV_DisTransTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + CvMat _input = test_mat[INPUT][0], _output = test_mat[REF_OUTPUT][0]; + + cvTsDistTransform( &_input, &_output, dist_type, mask_size, mask, 0 ); +} + + +TEST(Imgproc_DistanceTransform, accuracy) { CV_DisTransTest test; test.safe_run(); } + + diff --git a/modules/imgproc/test/test_emd.cpp b/modules/imgproc/test/test_emd.cpp new file mode 100644 index 000000000..1880bb350 --- /dev/null +++ b/modules/imgproc/test/test_emd.cpp @@ -0,0 +1,95 @@ +/*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" + +using namespace cv; +using namespace std; + +/*////////////////////// emd_test /////////////////////////*/ + +class CV_EMDTest : public cvtest::BaseTest +{ +public: + CV_EMDTest(); +protected: + void run(int); +}; + + +CV_EMDTest::CV_EMDTest() +{ +} + +void CV_EMDTest::run( int ) +{ + int code = cvtest::TS::OK; + const double success_error_level = 1e-6; + #define M 10000 + double emd0 = 2460./210; + static float cost[] = + { + 16, 16, 13, 22, 17, + 14, 14, 13, 19, 15, + 19, 19, 20, 23, M, + M , 0, M, 0, 0 + }; + static float w1[] = { 50, 60, 50, 50 }, + w2[] = { 30, 20, 70, 30, 60 }; + Mat _w1(4, 1, CV_32F, w1); + Mat _w2(5, 1, CV_32F, w2); + Mat _cost(_w1.rows, _w2.rows, CV_32F, cost); + + float emd = EMD( _w1, _w2, -1, _cost ); + if( fabs( emd - emd0 ) > success_error_level*emd0 ) + { + ts->printf( cvtest::TS::LOG, + "The computed distance is %.2f, while it should be %.2f\n", emd, emd0 ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + + if( code < 0 ) + ts->set_failed_test_info( code ); +} + +TEST(Imgproc_EMD, regression) { CV_EMDTest test; test.safe_run(); } + +/* End of file. */ diff --git a/modules/imgproc/test/test_filter.cpp b/modules/imgproc/test/test_filter.cpp new file mode 100644 index 000000000..53ecff70d --- /dev/null +++ b/modules/imgproc/test/test_filter.cpp @@ -0,0 +1,1776 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_FilterBaseTest : public cvtest::ArrayTest +{ +public: + CV_FilterBaseTest( bool _fp_kernel ); + +protected: + int prepare_test_case( int test_case_idx ); + int read_params( CvFileStorage* fs ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); + CvSize aperture_size; + CvPoint anchor; + int max_aperture_size; + bool fp_kernel; + bool inplace; + int border; +}; + + +CV_FilterBaseTest::CV_FilterBaseTest( bool _fp_kernel ) : fp_kernel(_fp_kernel) +{ + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + max_aperture_size = 13; + inplace = false; + aperture_size = cvSize(0,0); + anchor = cvPoint(0,0); + element_wise_relative_error = false; +} + + +int CV_FilterBaseTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::ArrayTest::read_params( fs ); + if( code < 0 ) + return code; + + max_aperture_size = cvReadInt( find_param( fs, "max_aperture_size" ), max_aperture_size ); + max_aperture_size = cvtest::clipInt( max_aperture_size, 1, 100 ); + + return code; +} + + +void CV_FilterBaseTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) +{ + cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high ); + if( i == INPUT ) + { + if( j == 1 ) + { + if( fp_kernel ) + { + RNG& rng = ts->get_rng(); + double val = exp( cvtest::randReal(rng)*10 - 4 ); + low = Scalar::all(-val); + high = Scalar::all(val); + } + else + { + low = Scalar::all(0); + high = Scalar::all(2); + } + } + else if( CV_MAT_DEPTH(type) == CV_32F ) + { + low = Scalar::all(-10.); + high = Scalar::all(10.); + } + } +} + + +void CV_FilterBaseTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, + vector >& types ) +{ + RNG& rng = ts->get_rng(); + int depth = cvtest::randInt(rng) % CV_32F; + int cn = cvtest::randInt(rng) % 3 + 1; + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + depth += depth == CV_8S; + cn += cn == 2; + + types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(depth, cn); + + aperture_size.width = cvtest::randInt(rng) % max_aperture_size + 1; + aperture_size.height = cvtest::randInt(rng) % max_aperture_size + 1; + anchor.x = cvtest::randInt(rng) % aperture_size.width; + anchor.y = cvtest::randInt(rng) % aperture_size.height; + + types[INPUT][1] = fp_kernel ? CV_32FC1 : CV_8UC1; + sizes[INPUT][1] = aperture_size; + + inplace = cvtest::randInt(rng) % 2 != 0; + border = BORDER_REPLICATE; +} + + +int CV_FilterBaseTest::prepare_test_case( int test_case_idx ) +{ + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + if( inplace && test_mat[INPUT][0].type() == test_mat[OUTPUT][0].type()) + cvtest::copy( test_mat[INPUT][0], test_mat[OUTPUT][0] ); + else + inplace = false; + } + return code; +} + + +///////////////////////// + +class CV_MorphologyBaseTest : public CV_FilterBaseTest +{ +public: + CV_MorphologyBaseTest(); + +protected: + void prepare_to_validation( int test_case_idx ); + int prepare_test_case( int test_case_idx ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + int optype, optype_min, optype_max; + int shape; + IplConvKernel* element; +}; + + +CV_MorphologyBaseTest::CV_MorphologyBaseTest() : CV_FilterBaseTest( false ) +{ + shape = -1; + element = 0; + optype = optype_min = optype_max = -1; +} + + +void CV_MorphologyBaseTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + CV_FilterBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int depth = cvtest::randInt(rng) % 4; + depth = depth == 0 ? CV_8U : depth == 1 ? CV_16U : depth == 2 ? CV_16S : CV_32F; + int cn = CV_MAT_CN(types[INPUT][0]); + + types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(depth, cn); + shape = cvtest::randInt(rng) % 4; + if( shape >= 3 ) + shape = CV_SHAPE_CUSTOM; + else + sizes[INPUT][1] = cvSize(0,0); + optype = cvtest::randInt(rng) % (optype_max - optype_min + 1) + optype_min; +} + + +double CV_MorphologyBaseTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return test_mat[INPUT][0].depth() < CV_32F || + (optype == CV_MOP_ERODE || optype == CV_MOP_DILATE || + optype == CV_MOP_OPEN || optype == CV_MOP_CLOSE) ? 0 : 1e-5; +} + + +int CV_MorphologyBaseTest::prepare_test_case( int test_case_idx ) +{ + int code = CV_FilterBaseTest::prepare_test_case( test_case_idx ); + vector eldata; + + if( code <= 0 ) + return code; + + if( shape == CV_SHAPE_CUSTOM ) + { + eldata.resize(aperture_size.width*aperture_size.height); + uchar* src = test_mat[INPUT][1].data; + int srcstep = test_mat[INPUT][1].step; + int i, j, nonzero = 0; + + for( i = 0; i < aperture_size.height; i++ ) + { + for( j = 0; j < aperture_size.width; j++ ) + { + eldata[i*aperture_size.width + j] = src[i*srcstep + j]; + nonzero += src[i*srcstep + j] != 0; + } + } + + if( nonzero == 0 ) + eldata[anchor.y*aperture_size.width + anchor.x] = 1; + } + + cvReleaseStructuringElement( &element ); + element = cvCreateStructuringElementEx( aperture_size.width, aperture_size.height, + anchor.x, anchor.y, shape, eldata.empty() ? 0 : &eldata[0] ); + return code; +} + + +void CV_MorphologyBaseTest::prepare_to_validation( int test_case_idx ) +{ + Mat& src = test_mat[INPUT][0], &dst = test_mat[REF_OUTPUT][0]; + Mat _ielement(element->nRows, element->nCols, CV_32S, element->values); + Mat _element; + _ielement.convertTo(_element, CV_8U); + Point anchor(element->anchorX, element->anchorY); + int border = BORDER_REPLICATE; + + if( optype == CV_MOP_ERODE ) + { + cvtest::erode( src, dst, _element, anchor, border ); + } + else if( optype == CV_MOP_DILATE ) + { + cvtest::dilate( src, dst, _element, anchor, border ); + } + else + { + Mat temp; + if( optype == CV_MOP_OPEN ) + { + cvtest::erode( src, temp, _element, anchor, border ); + cvtest::dilate( temp, dst, _element, anchor, border ); + } + else if( optype == CV_MOP_CLOSE ) + { + cvtest::dilate( src, temp, _element, anchor, border ); + cvtest::erode( temp, dst, _element, anchor, border ); + } + else if( optype == CV_MOP_GRADIENT ) + { + cvtest::erode( src, temp, _element, anchor, border ); + cvtest::dilate( src, dst, _element, anchor, border ); + cvtest::add( dst, 1, temp, -1, Scalar::all(0), dst, dst.type() ); + } + else if( optype == CV_MOP_TOPHAT ) + { + cvtest::erode( src, temp, _element, anchor, border ); + cvtest::dilate( temp, dst, _element, anchor, border ); + cvtest::add( src, 1, dst, -1, Scalar::all(0), dst, dst.type() ); + } + else if( optype == CV_MOP_BLACKHAT ) + { + cvtest::dilate( src, temp, _element, anchor, border ); + cvtest::erode( temp, dst, _element, anchor, border ); + cvtest::add( dst, 1, src, -1, Scalar::all(0), dst, dst.type() ); + } + else + CV_Error( CV_StsBadArg, "Unknown operation" ); + } + + cvReleaseStructuringElement( &element ); +} + + +/////////////// erode /////////////// + +class CV_ErodeTest : public CV_MorphologyBaseTest +{ +public: + CV_ErodeTest(); +protected: + void run_func(); +}; + + +CV_ErodeTest::CV_ErodeTest() +{ + optype_min = optype_max = CV_MOP_ERODE; +} + + +void CV_ErodeTest::run_func() +{ + cvErode( inplace ? test_array[OUTPUT][0] : test_array[INPUT][0], + test_array[OUTPUT][0], element, 1 ); +} + + +/////////////// dilate /////////////// + +class CV_DilateTest : public CV_MorphologyBaseTest +{ +public: + CV_DilateTest(); +protected: + void run_func(); +}; + + +CV_DilateTest::CV_DilateTest() +{ + optype_min = optype_max = CV_MOP_DILATE; +} + + +void CV_DilateTest::run_func() +{ + cvDilate( inplace ? test_array[OUTPUT][0] : test_array[INPUT][0], + test_array[OUTPUT][0], element, 1 ); +} + +/////////////// morphEx /////////////// + +class CV_MorphExTest : public CV_MorphologyBaseTest +{ +public: + CV_MorphExTest(); +protected: + void run_func(); +}; + + +CV_MorphExTest::CV_MorphExTest() +{ + optype_min = CV_MOP_ERODE; + optype_max = CV_MOP_BLACKHAT; +} + + +void CV_MorphExTest::run_func() +{ + cvMorphologyEx( test_array[inplace ? OUTPUT : INPUT][0], + test_array[OUTPUT][0], 0, element, optype, 1 ); +} + +/////////////// generic filter /////////////// + +class CV_FilterTest : public CV_FilterBaseTest +{ +public: + CV_FilterTest(); + +protected: + void prepare_to_validation( int test_case_idx ); + void run_func(); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); +}; + + +CV_FilterTest::CV_FilterTest() : CV_FilterBaseTest( true ) +{ +} + + +void CV_FilterTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + CV_FilterBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + RNG& rng = ts->get_rng(); + int depth = cvtest::randInt(rng)%3; + int cn = CV_MAT_CN(types[INPUT][0]); + depth = depth == 0 ? CV_8U : depth == 1 ? CV_16U : CV_32F; + types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(depth, cn); +} + + +double CV_FilterTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth <= CV_8S ? 2 : depth <= CV_32S ? 32 : + depth == CV_32F ? 1e-4 : 1e-10; +} + + +void CV_FilterTest::run_func() +{ + CvMat kernel = test_mat[INPUT][1]; + cvFilter2D( test_array[inplace ? OUTPUT : INPUT][0], + test_array[OUTPUT][0], &kernel, anchor ); +} + + +void CV_FilterTest::prepare_to_validation( int test_case_idx ) +{ + cvtest::filter2D( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], test_mat[REF_OUTPUT][0].type(), + test_mat[INPUT][1], anchor, 0, BORDER_REPLICATE ); +} + + +//////////////////////// + +class CV_DerivBaseTest : public CV_FilterBaseTest +{ +public: + CV_DerivBaseTest(); +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + int _aperture_size; +}; + + +CV_DerivBaseTest::CV_DerivBaseTest() : CV_FilterBaseTest( true ) +{ + max_aperture_size = 7; +} + + +void CV_DerivBaseTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + CV_FilterBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int depth = cvtest::randInt(rng) % 2; + depth = depth == 0 ? CV_8U : CV_32F; + types[INPUT][0] = CV_MAKETYPE(depth,1); + types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(depth==CV_8U?CV_16S:CV_32F,1); + _aperture_size = (cvtest::randInt(rng)%5)*2 - 1; + sizes[INPUT][1] = aperture_size = cvSize(_aperture_size, _aperture_size); +} + + +double CV_DerivBaseTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth <= CV_8S ? 2 : 5e-4; +} + + +/////////////// sobel /////////////// + +class CV_SobelTest : public CV_DerivBaseTest +{ +public: + CV_SobelTest(); + +protected: + void prepare_to_validation( int test_case_idx ); + void run_func(); + void get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ); + int dx, dy, origin; +}; + + +CV_SobelTest::CV_SobelTest() {} + + +void CV_SobelTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, + vector >& types ) +{ + RNG& rng = ts->get_rng(); + CV_DerivBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int max_d = _aperture_size > 0 ? 2 : 1; + origin = cvtest::randInt(rng) % 2; + dx = cvtest::randInt(rng) % (max_d + 1); + dy = cvtest::randInt(rng) % (max_d + 1 - dx); + if( dx == 0 && dy == 0 ) + dx = 1; + if( cvtest::randInt(rng) % 2 ) + { + int t; + CV_SWAP( dx, dy, t ); + } + + if( _aperture_size < 0 ) + aperture_size = cvSize(3, 3); + else if( _aperture_size == 1 ) + { + if( dx == 0 ) + aperture_size = cvSize(1, 3); + else if( dy == 0 ) + aperture_size = cvSize(3, 1); + else + { + _aperture_size = 3; + aperture_size = cvSize(3, 3); + } + } + else + aperture_size = cvSize(_aperture_size, _aperture_size); + + sizes[INPUT][1] = aperture_size; + anchor.x = aperture_size.width / 2; + anchor.y = aperture_size.height / 2; +} + + +void CV_SobelTest::run_func() +{ + cvSobel( test_array[inplace ? OUTPUT : INPUT][0], + test_array[OUTPUT][0], dx, dy, _aperture_size ); + /*cv::Sobel( test_mat[inplace ? OUTPUT : INPUT][0], + test_mat[OUTPUT][0], test_mat[OUTPUT][0].depth(), + dx, dy, _aperture_size, 1, 0, border );*/ +} + + +void CV_SobelTest::prepare_to_validation( int test_case_idx ) +{ + Mat kernel = cvtest::calcSobelKernel2D( dx, dy, _aperture_size, 0 ); + cvtest::filter2D( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], test_mat[REF_OUTPUT][0].depth(), + kernel, anchor, 0, BORDER_REPLICATE); +} + + +/////////////// laplace /////////////// + +class CV_LaplaceTest : public CV_DerivBaseTest +{ +public: + CV_LaplaceTest(); + +protected: + int prepare_test_case( int test_case_idx ); + void prepare_to_validation( int test_case_idx ); + void run_func(); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); +}; + + +CV_LaplaceTest::CV_LaplaceTest() +{ +} + + +void CV_LaplaceTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + CV_DerivBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + if( _aperture_size <= 1 ) + { + if( _aperture_size < 0 ) + _aperture_size = 1; + aperture_size = cvSize(3, 3); + } + else + aperture_size = cvSize(_aperture_size, _aperture_size); + + sizes[INPUT][1] = aperture_size; + anchor.x = aperture_size.width / 2; + anchor.y = aperture_size.height / 2; +} + + +void CV_LaplaceTest::run_func() +{ + cvLaplace( test_array[inplace ? OUTPUT : INPUT][0], + test_array[OUTPUT][0], _aperture_size ); +} + + +int CV_LaplaceTest::prepare_test_case( int test_case_idx ) +{ + int code = CV_DerivBaseTest::prepare_test_case( test_case_idx ); + return _aperture_size < 0 ? 0 : code; +} + + +void CV_LaplaceTest::prepare_to_validation( int test_case_idx ) +{ + Mat kernel = cvtest::calcLaplaceKernel2D( _aperture_size ); + cvtest::filter2D( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], test_mat[REF_OUTPUT][0].depth(), + kernel, anchor, 0, BORDER_REPLICATE ); +} + + +//////////////////////////////////////////////////////////// + +class CV_SmoothBaseTest : public CV_FilterBaseTest +{ +public: + CV_SmoothBaseTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + const char* smooth_type; +}; + + +CV_SmoothBaseTest::CV_SmoothBaseTest() : CV_FilterBaseTest( true ) +{ + smooth_type = ""; +} + + +void CV_SmoothBaseTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + CV_FilterBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int depth = cvtest::randInt(rng) % 2; + int cn = CV_MAT_CN(types[INPUT][0]); + depth = depth == 0 ? CV_8U : CV_32F; + types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(depth,cn); + anchor.x = cvtest::randInt(rng)%(max_aperture_size/2+1); + anchor.y = cvtest::randInt(rng)%(max_aperture_size/2+1); + aperture_size.width = anchor.x*2 + 1; + aperture_size.height = anchor.y*2 + 1; + sizes[INPUT][1] = aperture_size; +} + + +double CV_SmoothBaseTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth <= CV_8S ? 1 : 1e-5; +} + + +/////////////// blur /////////////// + +class CV_BlurTest : public CV_SmoothBaseTest +{ +public: + CV_BlurTest(); + +protected: + int prepare_test_case( int test_case_idx ); + void prepare_to_validation( int test_case_idx ); + void run_func(); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + bool normalize; +}; + + +CV_BlurTest::CV_BlurTest() +{ +} + + +void CV_BlurTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + CV_SmoothBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + normalize = cvtest::randInt(rng) % 2 != 0; + if( !normalize ) + { + int depth = CV_MAT_DEPTH(types[INPUT][0]); + types[INPUT][0] = CV_MAKETYPE(depth, 1); + types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(depth==CV_8U?CV_16S:CV_32F,1); + } +} + + +void CV_BlurTest::run_func() +{ + cvSmooth( inplace ? test_array[OUTPUT][0] : test_array[INPUT][0], + test_array[OUTPUT][0], normalize ? CV_BLUR : CV_BLUR_NO_SCALE, + aperture_size.width, aperture_size.height ); +} + + +int CV_BlurTest::prepare_test_case( int test_case_idx ) +{ + int code = CV_SmoothBaseTest::prepare_test_case( test_case_idx ); + return code > 0 && !normalize && test_mat[INPUT][0].channels() > 1 ? 0 : code; +} + + +void CV_BlurTest::prepare_to_validation( int test_case_idx ) +{ + Mat kernel(aperture_size, CV_64F); + kernel.setTo(Scalar::all(normalize ? 1./(aperture_size.width*aperture_size.height) : 1.)); + cvtest::filter2D( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], test_mat[REF_OUTPUT][0].depth(), + kernel, anchor, 0, BORDER_REPLICATE ); +} + + +/////////////// gaussian /////////////// + +class CV_GaussianBlurTest : public CV_SmoothBaseTest +{ +public: + CV_GaussianBlurTest(); + +protected: + void prepare_to_validation( int test_case_idx ); + void run_func(); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ); + double sigma; + int param1, param2; +}; + + +CV_GaussianBlurTest::CV_GaussianBlurTest() : CV_SmoothBaseTest() +{ + sigma = 0.; + smooth_type = "Gaussian"; +} + + +double CV_GaussianBlurTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth <= CV_8S ? 8 : 1e-5; +} + + +void CV_GaussianBlurTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int kernel_case = cvtest::randInt(rng) % 2; + CV_SmoothBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + anchor = cvPoint(aperture_size.width/2,aperture_size.height/2); + + sigma = exp(cvtest::randReal(rng)*5-2); + param1 = aperture_size.width; + param2 = aperture_size.height; + + if( kernel_case == 0 ) + sigma = 0.; +} + +void CV_GaussianBlurTest::run_func() +{ + cvSmooth( test_array[inplace ? OUTPUT : INPUT][0], + test_array[OUTPUT][0], CV_GAUSSIAN, + param1, param2, sigma, sigma ); +} + + +// !!! Copied from cvSmooth, if the code is changed in cvSmooth, +// make sure to update this one too. +#define SMALL_GAUSSIAN_SIZE 7 +static void +calcGaussianKernel( int n, double sigma, vector& kernel ) +{ + static const float small_gaussian_tab[][SMALL_GAUSSIAN_SIZE] = + { + {1.f}, + {0.25f, 0.5f, 0.25f}, + {0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f}, + {0.03125, 0.109375, 0.21875, 0.28125, 0.21875, 0.109375, 0.03125} + }; + + kernel.resize(n); + if( n <= SMALL_GAUSSIAN_SIZE && sigma <= 0 ) + { + assert( n%2 == 1 ); + memcpy( &kernel[0], small_gaussian_tab[n>>1], n*sizeof(kernel[0])); + } + else + { + double sigmaX = sigma > 0 ? sigma : (n/2 - 1)*0.3 + 0.8; + double scale2X = -0.5/(sigmaX*sigmaX); + double sum = 1.; + int i; + sum = kernel[n/2] = 1.f; + + for( i = 1; i <= n/2; i++ ) + { + kernel[n/2+i] = kernel[n/2-i] = (float)exp(scale2X*i*i); + sum += kernel[n/2+i]*2; + } + + sum = 1./sum; + for( i = 0; i <= n/2; i++ ) + kernel[n/2+i] = kernel[n/2-i] = (float)(kernel[n/2+i]*sum); + } +} + + +static Mat calcGaussianKernel2D( Size ksize, double sigma ) +{ + vector kx, ky; + Mat kernel(ksize, CV_32F); + + calcGaussianKernel( kernel.cols, sigma, kx ); + calcGaussianKernel( kernel.rows, sigma, ky ); + + for( int i = 0; i < kernel.rows; i++ ) + for( int j = 0; j < kernel.cols; j++ ) + kernel.at(i, j) = kx[j]*ky[i]; + return kernel; +} + + +void CV_GaussianBlurTest::prepare_to_validation( int test_case_idx ) +{ + Mat kernel = calcGaussianKernel2D( aperture_size, sigma ); + cvtest::filter2D( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], test_mat[REF_OUTPUT][0].depth(), + kernel, anchor, 0, border & ~BORDER_ISOLATED ); +} + + +/////////////// median /////////////// + +class CV_MedianBlurTest : public CV_SmoothBaseTest +{ +public: + CV_MedianBlurTest(); + +protected: + void prepare_to_validation( int test_case_idx ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); +}; + + +CV_MedianBlurTest::CV_MedianBlurTest() +{ + smooth_type = "Median"; +} + + +void CV_MedianBlurTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + CV_SmoothBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int depth = CV_8U; + int cn = CV_MAT_CN(types[INPUT][0]); + types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(depth,cn); + types[INPUT][1] = CV_MAKETYPE(depth,1); + + aperture_size.height = aperture_size.width; + anchor.x = anchor.y = aperture_size.width / 2; + sizes[INPUT][1] = cvSize(aperture_size.width,aperture_size.height); + + sizes[OUTPUT][0] = sizes[INPUT][0]; + sizes[REF_OUTPUT][0] = sizes[INPUT][0]; + + inplace = false; + border = BORDER_REPLICATE | BORDER_ISOLATED; +} + + +double CV_MedianBlurTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return 0; +} + + +void CV_MedianBlurTest::run_func() +{ + cvSmooth( test_array[INPUT][0], test_array[OUTPUT][0], + CV_MEDIAN, aperture_size.width ); +} + + +struct median_pair +{ + int col; + int val; + median_pair() {}; + median_pair( int _col, int _val ) : col(_col), val(_val) {}; +}; + + +static void test_medianFilter( const Mat& src, Mat& dst, int m ) +{ + int i, j, k, l, m2 = m*m, n; + vector col_buf(m+1); + vector _buf0(m*m+1), _buf1(m*m+1); + median_pair *buf0 = &_buf0[0], *buf1 = &_buf1[0]; + int step = src.step/src.elemSize(); + + assert( src.rows == dst.rows + m - 1 && src.cols == dst.cols + m - 1 && + src.type() == dst.type() && src.type() == CV_8UC1 ); + + for( i = 0; i < dst.rows; i++ ) + { + uchar* dst1 = dst.ptr(i); + for( k = 0; k < m; k++ ) + { + const uchar* src1 = src.ptr(i+k); + for( j = 0; j < m-1; j++ ) + *buf0++ = median_pair(j, src1[j]); + } + + n = m2 - m; + buf0 -= n; + for( k = n-1; k > 0; k-- ) + { + int f = 0; + for( j = 0; j < k; j++ ) + { + if( buf0[j].val > buf0[j+1].val ) + { + median_pair t; + CV_SWAP( buf0[j], buf0[j+1], t ); + f = 1; + } + } + if( !f ) + break; + } + + for( j = 0; j < dst.cols; j++ ) + { + int ins_col = j + m - 1; + int del_col = j - 1; + const uchar* src1 = src.ptr(i) + ins_col; + for( k = 0; k < m; k++, src1 += step ) + { + col_buf[k] = src1[0]; + for( l = k-1; l >= 0; l-- ) + { + int t; + if( col_buf[l] < col_buf[l+1] ) + break; + CV_SWAP( col_buf[l], col_buf[l+1], t ); + } + } + + col_buf[m] = INT_MAX; + + for( k = 0, l = 0; k < n; ) + { + if( buf0[k].col == del_col ) + k++; + else if( buf0[k].val < col_buf[l] ) + *buf1++ = buf0[k++]; + else + { + assert( col_buf[l] < INT_MAX ); + *buf1++ = median_pair(ins_col,col_buf[l++]); + } + } + + for( ; l < m; l++ ) + *buf1++ = median_pair(ins_col,col_buf[l]); + + if( del_col < 0 ) + n += m; + buf1 -= n; + assert( n == m2 ); + dst1[j] = (uchar)buf1[n/2].val; + median_pair* tbuf; + CV_SWAP( buf0, buf1, tbuf ); + } + } +} + + +void CV_MedianBlurTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + // CV_SmoothBaseTest::prepare_to_validation( test_case_idx ); + const Mat& src0 = test_mat[INPUT][0]; + Mat& dst0 = test_mat[REF_OUTPUT][0]; + int i, cn = src0.channels(); + int m = aperture_size.width; + Mat src(src0.rows + m - 1, src0.cols + m - 1, src0.depth()); + Mat dst; + if( cn == 1 ) + dst = dst0; + else + dst.create(src0.size(), src0.depth()); + + for( i = 0; i < cn; i++ ) + { + Mat ptr = src0; + if( cn > 1 ) + { + cvtest::extract( src0, dst, i ); + ptr = dst; + } + cvtest::copyMakeBorder( ptr, src, m/2, m/2, m/2, m/2, border & ~BORDER_ISOLATED ); + test_medianFilter( src, dst, m ); + if( cn > 1 ) + cvtest::insert( dst, dst0, i ); + } +} + + +/////////////// pyramid tests /////////////// + +class CV_PyramidBaseTest : public CV_FilterBaseTest +{ +public: + CV_PyramidBaseTest( bool downsample ); + +protected: + double get_success_error_level( int test_case_idx, int i, int j ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + bool downsample; + Mat kernel; +}; + + +CV_PyramidBaseTest::CV_PyramidBaseTest( bool _downsample ) : CV_FilterBaseTest(true) +{ + static float kdata[] = { 1.f, 4.f, 6.f, 4.f, 1.f }; + downsample = _downsample; + Mat kernel1d(1, 5, CV_32F, kdata); + kernel = (kernel1d.t()*kernel1d)*((downsample ? 1 : 4)/256.); +} + + +double CV_PyramidBaseTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth <= CV_8S ? 1 : 1e-5; +} + + +void CV_PyramidBaseTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, + vector >& types ) +{ + RNG& rng = ts->get_rng(); + CvSize sz; + CV_FilterBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + int depth = cvtest::randInt(rng) % 2 ? CV_32F : CV_8U; + int cn = cvtest::randInt(rng) & 1 ? 3 : 1; + + aperture_size = cvSize(5,5); + anchor = cvPoint(aperture_size.width/2, aperture_size.height/2); + + types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(depth, cn); + + sz.width = MAX( sizes[INPUT][0].width/2, 1 ); + sz.height = MAX( sizes[INPUT][0].height/2, 1 ); + + if( downsample ) + { + sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = sz; + sz.width *= 2; + sz.height *= 2; + sizes[INPUT][0] = sz; + } + else + { + sizes[INPUT][0] = sz; + sz.width *= 2; + sz.height *= 2; + sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = sz; + } + + sizes[INPUT][1] = aperture_size; + inplace = false; +} + + +/////// pyrdown //////// + +class CV_PyramidDownTest : public CV_PyramidBaseTest +{ +public: + CV_PyramidDownTest(); + +protected: + void run_func(); + void prepare_to_validation( int ); +}; + + +CV_PyramidDownTest::CV_PyramidDownTest() : CV_PyramidBaseTest( true ) +{ +} + + +void CV_PyramidDownTest::run_func() +{ + cvPyrDown( test_array[INPUT][0], test_array[OUTPUT][0], CV_GAUSSIAN_5x5 ); +} + + +void CV_PyramidDownTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + Mat& src = test_mat[INPUT][0], &dst = test_mat[REF_OUTPUT][0]; + Mat temp; + cvtest::filter2D(src, temp, src.depth(), + kernel, Point(kernel.cols/2, kernel.rows/2), + 0, BORDER_REFLECT_101); + + size_t elem_size = temp.elemSize(); + size_t ncols = dst.cols*elem_size; + + for( int i = 0; i < dst.rows; i++ ) + { + const uchar* src_row = temp.ptr(i*2); + uchar* dst_row = dst.ptr(i); + + for( size_t j = 0; j < ncols; j += elem_size ) + { + for( size_t k = 0; k < elem_size; k++ ) + dst_row[j+k] = src_row[j*2+k]; + } + } +} + + +/////// pyrup //////// + +class CV_PyramidUpTest : public CV_PyramidBaseTest +{ +public: + CV_PyramidUpTest(); + +protected: + void run_func(); + void prepare_to_validation( int ); +}; + + +CV_PyramidUpTest::CV_PyramidUpTest() : CV_PyramidBaseTest( false ) +{ +} + + +void CV_PyramidUpTest::run_func() +{ + cvPyrUp( test_array[INPUT][0], test_array[OUTPUT][0], CV_GAUSSIAN_5x5 ); +} + + +void CV_PyramidUpTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + Mat& src = test_mat[INPUT][0], &dst = test_mat[REF_OUTPUT][0]; + Mat temp(dst.size(), dst.type()); + + size_t elem_size = src.elemSize(); + size_t ncols = src.cols*elem_size; + + for( int i = 0; i < src.rows; i++ ) + { + const uchar* src_row = src.ptr(i); + uchar* dst_row = temp.ptr(i*2); + + if( i*2 + 1 < temp.rows ) + memset( temp.ptr(i*2+1), 0, temp.cols*elem_size ); + for( size_t j = 0; j < ncols; j += elem_size ) + { + for( size_t k = 0; k < elem_size; k++ ) + { + dst_row[j*2+k] = src_row[j+k]; + dst_row[j*2+k+elem_size] = 0; + } + } + } + + cvtest::filter2D(temp, dst, dst.depth(), + kernel, Point(kernel.cols/2, kernel.rows/2), + 0, BORDER_REFLECT_101); + // TODO: fix the problem with 2 bottom rows. + memset(dst.ptr(dst.rows-2), 0, dst.step*2); + memset(test_mat[OUTPUT][0].ptr(dst.rows-2), 0, test_mat[OUTPUT][0].step*2); +} + + +//////////////////////// feature selection ////////////////////////// + +class CV_FeatureSelBaseTest : public cvtest::ArrayTest +{ +public: + CV_FeatureSelBaseTest( int width_factor ); + +protected: + int read_params( CvFileStorage* fs ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); + double get_success_error_level( int test_case_idx, int i, int j ); + int aperture_size, block_size; + int max_aperture_size; + int max_block_size; + int width_factor; +}; + + +CV_FeatureSelBaseTest::CV_FeatureSelBaseTest( int _width_factor ) +{ + max_aperture_size = 7; + max_block_size = 21; + // 1 input, 1 output, temp arrays are allocated in the reference functions + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + element_wise_relative_error = false; + width_factor = _width_factor; +} + + +int CV_FeatureSelBaseTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::BaseTest::read_params( fs ); + if( code < 0 ) + return code; + + max_aperture_size = cvReadInt( find_param( fs, "max_aperture_size" ), max_aperture_size ); + max_aperture_size = cvtest::clipInt( max_aperture_size, 1, 9 ); + max_block_size = cvReadInt( find_param( fs, "max_block_size" ), max_block_size ); + max_block_size = cvtest::clipInt( max_aperture_size, 1, 100 ); + + return code; +} + + +double CV_FeatureSelBaseTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth <= CV_8S ? 3e-2 : depth == CV_32F ? 1e-3 : 1e-10; +} + + +void CV_FeatureSelBaseTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) +{ + cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high ); + if( i == INPUT && CV_MAT_DEPTH(type) == CV_32F ) + { + low = Scalar::all(-10.); + high = Scalar::all(10.); + } +} + + +void CV_FeatureSelBaseTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int depth = cvtest::randInt(rng) % 2, asz; + + depth = depth == 0 ? CV_8U : CV_32F; + types[INPUT][0] = depth; + types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_32FC1; + + aperture_size = (cvtest::randInt(rng) % (max_aperture_size+2) - 1) | 1; + if( aperture_size == 1 ) + aperture_size = 3; + if( depth == CV_8U ) + aperture_size = MIN( aperture_size, 5 ); + block_size = (cvtest::randInt(rng) % max_block_size + 1) | 1; + if( block_size <= 3 ) + block_size = 3; + asz = aperture_size > 0 ? aperture_size : 3; + + sizes[INPUT][0].width = MAX( sizes[INPUT][0].width, asz + block_size ); + sizes[INPUT][0].height = MAX( sizes[INPUT][0].height, asz + block_size ); + sizes[OUTPUT][0].height = sizes[REF_OUTPUT][0].height = sizes[INPUT][0].height; + sizes[OUTPUT][0].width = sizes[REF_OUTPUT][0].width = sizes[INPUT][0].width*width_factor; +} + + +static void +test_cornerEigenValsVecs( const Mat& src, Mat& eigenv, Mat& ocv_eigenv, + int block_size, int _aperture_size, int mode ) +{ + int i, j; + int aperture_size = _aperture_size < 0 ? 3 : _aperture_size; + Point anchor( aperture_size/2, aperture_size/2 ); + + CV_Assert( src.type() == CV_8UC1 || src.type() == CV_32FC1 ); + CV_Assert( eigenv.type() == CV_32FC1 ); + CV_Assert( src.rows == eigenv.rows && + ((mode > 0 && src.cols == eigenv.cols) || + (mode == 0 && src.cols*6 == eigenv.cols)) ); + + int type = src.type(); + int ftype = CV_32FC1; + double kernel_scale = type != ftype ? 1./255 : 1; + + Mat dx2, dy2, dxdy(src.size(), CV_32F), kernel; + + kernel = cvtest::calcSobelKernel2D( 1, 0, _aperture_size ); + cvtest::filter2D( src, dx2, ftype, kernel*kernel_scale, anchor, 0, BORDER_REPLICATE ); + kernel = cvtest::calcSobelKernel2D( 0, 1, _aperture_size ); + cvtest::filter2D( src, dy2, ftype, kernel*kernel_scale, anchor, 0, BORDER_REPLICATE ); + + double denom = (1 << (aperture_size-1))*block_size; + denom = denom * denom; + if( _aperture_size < 0 ) + denom *= 4; + denom = 1./denom; + + for( i = 0; i < src.rows; i++ ) + { + float* dxdyp = dxdy.ptr(i); + float* dx2p = dx2.ptr(i); + float* dy2p = dy2.ptr(i); + + for( j = 0; j < src.cols; j++ ) + { + double xval = dx2p[j], yval = dy2p[j]; + dxdyp[j] = (float)(xval*yval*denom); + dx2p[j] = (float)(xval*xval*denom); + dy2p[j] = (float)(yval*yval*denom); + } + } + + kernel = Mat::ones(block_size, block_size, CV_32F); + anchor = Point(block_size/2, block_size/2); + + cvtest::filter2D( dx2, dx2, ftype, kernel, anchor, 0, BORDER_REPLICATE ); + cvtest::filter2D( dy2, dy2, ftype, kernel, anchor, 0, BORDER_REPLICATE ); + cvtest::filter2D( dxdy, dxdy, ftype, kernel, anchor, 0, BORDER_REPLICATE ); + + if( mode == 0 ) + { + for( i = 0; i < src.rows; i++ ) + { + float* eigenvp = eigenv.ptr(i); + float* ocv_eigenvp = ocv_eigenv.ptr(i); + const float* dxdyp = dxdy.ptr(i); + const float* dx2p = dx2.ptr(i); + const float* dy2p = dy2.ptr(i); + + for( j = 0; j < src.cols; j++ ) + { + double a = dx2p[j], b = dxdyp[j], c = dy2p[j]; + double d = sqrt((a-c)*(a-c) + 4*b*b); + double l1 = 0.5*(a + c + d); + double l2 = 0.5*(a + c - d); + double x1, y1, x2, y2, s; + + if( fabs(a - l1) + fabs(b) >= 1e-3 ) + x1 = b, y1 = l1 - a; + else + x1 = l1 - c, y1 = b; + s = 1./(sqrt(x1*x1+y1*y1)+DBL_EPSILON); + x1 *= s; y1 *= s; + + if( fabs(a - l2) + fabs(b) >= 1e-3 ) + x2 = b, y2 = l2 - a; + else + x2 = l2 - c, y2 = b; + s = 1./(sqrt(x2*x2+y2*y2)+DBL_EPSILON); + x2 *= s; y2 *= s; + + /* the orientation of eigen vectors might be inversed relative to OpenCV function, + which is normal */ + if( (fabs(x1) >= fabs(y1) && ocv_eigenvp[j*6+2]*x1 < 0) || + (fabs(x1) < fabs(y1) && ocv_eigenvp[j*6+3]*y1 < 0) ) + x1 = -x1, y1 = -y1; + + if( (fabs(x2) >= fabs(y2) && ocv_eigenvp[j*6+4]*x2 < 0) || + (fabs(x2) < fabs(y2) && ocv_eigenvp[j*6+5]*y2 < 0) ) + x2 = -x2, y2 = -y2; + + eigenvp[j*6] = (float)l1; + eigenvp[j*6+1] = (float)l2; + eigenvp[j*6+2] = (float)x1; + eigenvp[j*6+3] = (float)y1; + eigenvp[j*6+4] = (float)x2; + eigenvp[j*6+5] = (float)y2; + } + } + } + else if( mode == 1 ) + { + for( i = 0; i < src.rows; i++ ) + { + float* eigenvp = eigenv.ptr(i); + const float* dxdyp = dxdy.ptr(i); + const float* dx2p = dx2.ptr(i); + const float* dy2p = dy2.ptr(i); + + for( j = 0; j < src.cols; j++ ) + { + double a = dx2p[j], b = dxdyp[j], c = dy2p[j]; + double d = sqrt((a-c)*(a-c) + 4*b*b); + eigenvp[j] = (float)(0.5*(a + c - d)); + } + } + } +} + + +// min eigenval +class CV_MinEigenValTest : public CV_FeatureSelBaseTest +{ +public: + CV_MinEigenValTest(); + +protected: + void run_func(); + void prepare_to_validation( int ); +}; + + +CV_MinEigenValTest::CV_MinEigenValTest() : CV_FeatureSelBaseTest( 1 ) +{ +} + + +void CV_MinEigenValTest::run_func() +{ + cvCornerMinEigenVal( test_array[INPUT][0], test_array[OUTPUT][0], + block_size, aperture_size ); +} + + +void CV_MinEigenValTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + test_cornerEigenValsVecs( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], + test_mat[OUTPUT][0], block_size, aperture_size, 1 ); +} + + +// eigenval's & vec's +class CV_EigenValVecTest : public CV_FeatureSelBaseTest +{ +public: + CV_EigenValVecTest(); + +protected: + void run_func(); + void prepare_to_validation( int ); +}; + + +CV_EigenValVecTest::CV_EigenValVecTest() : CV_FeatureSelBaseTest( 6 ) +{ +} + + +void CV_EigenValVecTest::run_func() +{ + cvCornerEigenValsAndVecs( test_array[INPUT][0], test_array[OUTPUT][0], + block_size, aperture_size ); +} + + +void CV_EigenValVecTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + test_cornerEigenValsVecs( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], + test_mat[OUTPUT][0], block_size, aperture_size, 0 ); +} + + +// precornerdetect +class CV_PreCornerDetectTest : public CV_FeatureSelBaseTest +{ +public: + CV_PreCornerDetectTest(); + +protected: + void run_func(); + void prepare_to_validation( int ); + int prepare_test_case( int ); +}; + + +CV_PreCornerDetectTest::CV_PreCornerDetectTest() : CV_FeatureSelBaseTest( 1 ) +{ +} + + +void CV_PreCornerDetectTest::run_func() +{ + cvPreCornerDetect( test_array[INPUT][0], test_array[OUTPUT][0], aperture_size ); +} + + +int CV_PreCornerDetectTest::prepare_test_case( int test_case_idx ) +{ + int code = CV_FeatureSelBaseTest::prepare_test_case( test_case_idx ); + if( aperture_size < 0 ) + aperture_size = 3; + return code; +} + + +void CV_PreCornerDetectTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + /*cvTsCornerEigenValsVecs( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], + block_size, aperture_size, 0 );*/ + const Mat& src = test_mat[INPUT][0]; + Mat& dst = test_mat[REF_OUTPUT][0]; + + int type = src.type(), ftype = CV_32FC1; + Point anchor(aperture_size/2, aperture_size/2); + + double kernel_scale = type != ftype ? 1./255 : 1.; + + Mat dx, dy, d2x, d2y, dxy, kernel; + + kernel = cvtest::calcSobelKernel2D(1, 0, aperture_size); + cvtest::filter2D(src, dx, ftype, kernel*kernel_scale, anchor, 0, BORDER_REPLICATE); + kernel = cvtest::calcSobelKernel2D(2, 0, aperture_size); + cvtest::filter2D(src, d2x, ftype, kernel*kernel_scale, anchor, 0, BORDER_REPLICATE); + kernel = cvtest::calcSobelKernel2D(0, 1, aperture_size); + cvtest::filter2D(src, dy, ftype, kernel*kernel_scale, anchor, 0, BORDER_REPLICATE); + kernel = cvtest::calcSobelKernel2D(0, 2, aperture_size); + cvtest::filter2D(src, d2y, ftype, kernel*kernel_scale, anchor, 0, BORDER_REPLICATE); + kernel = cvtest::calcSobelKernel2D(1, 1, aperture_size); + cvtest::filter2D(src, dxy, ftype, kernel*kernel_scale, anchor, 0, BORDER_REPLICATE); + + double denom = 1 << (aperture_size-1); + denom = denom * denom * denom; + denom = 1./denom; + + for( int i = 0; i < src.rows; i++ ) + { + const float* _dx = dx.ptr(i); + const float* _dy = dy.ptr(i); + const float* _d2x = d2x.ptr(i); + const float* _d2y = d2y.ptr(i); + const float* _dxy = dxy.ptr(i); + float* corner = dst.ptr(i); + + for( int j = 0; j < src.cols; j++ ) + { + double x = _dx[j]; + double y = _dy[j]; + + corner[j] = (float)(denom*(x*x*_d2y[j] + y*y*_d2x[j] - 2*x*y*_dxy[j])); + } + } +} + + +///////// integral ///////// + +class CV_IntegralTest : public cvtest::ArrayTest +{ +public: + CV_IntegralTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int ); + + int prepare_test_case( int test_case_idx ); +}; + + +CV_IntegralTest::CV_IntegralTest() +{ + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + element_wise_relative_error = false; +} + + +void CV_IntegralTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) +{ + cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high ); + int depth = CV_MAT_DEPTH(type); + if( depth == CV_32F ) + { + low = Scalar::all(-10.); + high = Scalar::all(10.); + } +} + + +void CV_IntegralTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int depth = cvtest::randInt(rng) % 2, sum_depth; + int cn = cvtest::randInt(rng) % 3 + 1; + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + Size sum_size; + + depth = depth == 0 ? CV_8U : CV_32F; + cn += cn == 2; + sum_depth = depth == CV_8U && (cvtest::randInt(rng) & 1) == 1 ? CV_32S : CV_64F; + + types[INPUT][0] = CV_MAKETYPE(depth,cn); + types[OUTPUT][0] = types[REF_OUTPUT][0] = + types[OUTPUT][2] = types[REF_OUTPUT][2] = CV_MAKETYPE(sum_depth, cn); + types[OUTPUT][1] = types[REF_OUTPUT][1] = CV_MAKETYPE(CV_64F, cn); + + sum_size.width = sizes[INPUT][0].width + 1; + sum_size.height = sizes[INPUT][0].height + 1; + + sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = sum_size; + sizes[OUTPUT][1] = sizes[REF_OUTPUT][1] = + sizes[OUTPUT][2] = sizes[REF_OUTPUT][2] = Size(0,0); + + if( cvtest::randInt(rng) % 3 > 0 ) + { + sizes[OUTPUT][1] = sizes[REF_OUTPUT][1] = sum_size; + if( cvtest::randInt(rng) % 2 > 0 ) + sizes[REF_OUTPUT][2] = sizes[OUTPUT][2] = sum_size; + } +} + + +double CV_IntegralTest::get_success_error_level( int /*test_case_idx*/, int i, int j ) +{ + int depth = test_mat[i][j].depth(); + return depth == CV_32S ? 0 : FLT_EPSILON; +} + + +int CV_IntegralTest::prepare_test_case( int test_case_idx ) +{ + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + return code > 0 && ((test_array[OUTPUT][2] && test_mat[OUTPUT][2].channels() > 1) || + test_mat[OUTPUT][0].depth() < test_mat[INPUT][0].depth()) ? 0 : code; +} + + +void CV_IntegralTest::run_func() +{ + cvIntegral( test_array[INPUT][0], test_array[OUTPUT][0], + test_array[OUTPUT][1], test_array[OUTPUT][2] ); +} + + +static void test_integral( const Mat& img, Mat* sum, Mat* sqsum, Mat* tilted ) +{ + CV_Assert( img.depth() == CV_32F ); + + sum->create(img.rows+1, img.cols+1, CV_64F); + if( sqsum ) + sqsum->create(img.rows+1, img.cols+1, CV_64F); + if( tilted ) + tilted->create(img.rows+1, img.cols+1, CV_64F); + + const float* data = img.ptr(); + double* sdata = sum->ptr(); + double* sqdata = sqsum ? sqsum->ptr() : 0; + double* tdata = tilted ? tilted->ptr() : 0; + int step = img.step/sizeof(data[0]); + int sstep = sum->step/sizeof(sdata[0]); + int sqstep = sqsum ? sqsum->step/sizeof(sqdata[0]) : 0; + int tstep = tilted ? tilted->step/sizeof(tdata[0]) : 0; + Size size = img.size(); + + memset( sdata, 0, (size.width+1)*sizeof(sdata[0]) ); + if( sqsum ) + memset( sqdata, 0, (size.width+1)*sizeof(sqdata[0]) ); + if( tilted ) + memset( tdata, 0, (size.width+1)*sizeof(tdata[0]) ); + + for( ; size.height--; data += step ) + { + double s = 0, sq = 0; + int x; + sdata += sstep; + sqdata += sqstep; + tdata += tstep; + + for( x = 0; x <= size.width; x++ ) + { + double t = x > 0 ? data[x-1] : 0, ts = t; + s += t; + sq += t*t; + + sdata[x] = s + sdata[x - sstep]; + if( sqdata ) + sqdata[x] = sq + sqdata[x - sqstep]; + + if( !tdata ) + continue; + + if( x == 0 ) + ts += tdata[-tstep+1]; + else + { + ts += tdata[x-tstep-1]; + if( data > img.ptr() ) + { + ts += data[x-step-1]; + if( x < size.width ) + ts += tdata[x-tstep+1] - tdata[x-tstep*2]; + } + } + + tdata[x] = ts; + } + } +} + + +void CV_IntegralTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + Mat& src = test_mat[INPUT][0]; + int cn = src.channels(); + + Mat* sum0 = &test_mat[REF_OUTPUT][0]; + Mat* sqsum0 = test_array[REF_OUTPUT][1] ? &test_mat[REF_OUTPUT][1] : 0; + Mat* tsum0 = test_array[REF_OUTPUT][2] ? &test_mat[REF_OUTPUT][2] : 0; + + Mat plane, srcf, psum, psqsum, ptsum, psum2, psqsum2, ptsum2; + if( cn == 1 ) + { + plane = src; + psum2 = *sum0; + psqsum2 = sqsum0 ? *sqsum0 : Mat(); + ptsum2 = tsum0 ? *tsum0 : Mat(); + } + + for( int i = 0; i < cn; i++ ) + { + if( cn > 1 ) + cvtest::extract(src, plane, i); + plane.convertTo(srcf, CV_32F); + + test_integral( srcf, &psum, sqsum0 ? &psqsum : 0, tsum0 ? &ptsum : 0 ); + psum.convertTo(psum2, sum0->depth()); + if( sqsum0 ) + psqsum.convertTo(psqsum2, sqsum0->depth()); + if( tsum0 ) + ptsum.convertTo(ptsum2, tsum0->depth()); + + if( cn > 1 ) + { + cvtest::insert(psum2, *sum0, i); + if( sqsum0 ) + cvtest::insert(psqsum2, *sqsum0, i); + if( tsum0 ) + cvtest::insert(ptsum2, *tsum0, i); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////////// + +TEST(Imgproc_Erode, accuracy) { CV_ErodeTest test; test.safe_run(); } +TEST(Imgproc_Dilate, accuracy) { CV_DilateTest test; test.safe_run(); } +TEST(Imgproc_MorphologyEx, accuracy) { CV_MorphExTest test; test.safe_run(); } +TEST(Imgproc_Filter2D, accuracy) { CV_FilterTest test; test.safe_run(); } +TEST(Imgproc_Sobel, accuracy) { CV_SobelTest test; test.safe_run(); } +TEST(Imgproc_Laplace, accuracy) { CV_LaplaceTest test; test.safe_run(); } +TEST(Imgproc_Blur, accuracy) { CV_BlurTest test; test.safe_run(); } +TEST(Imgproc_GaussianBlur, accuracy) { CV_GaussianBlurTest test; test.safe_run(); } +TEST(Imgproc_MedianBlur, accuracy) { CV_MedianBlurTest test; test.safe_run(); } +TEST(Imgproc_PyramidDown, accuracy) { CV_PyramidDownTest test; test.safe_run(); } +TEST(Imgproc_PyramidUp, accuracy) { CV_PyramidUpTest test; test.safe_run(); } +TEST(Imgproc_MinEigenVal, accuracy) { CV_MinEigenValTest test; test.safe_run(); } +TEST(Imgproc_EigenValsVecs, accuracy) { CV_EigenValVecTest test; test.safe_run(); } +TEST(Imgproc_PreCornerDetect, accuracy) { CV_PreCornerDetectTest test; test.safe_run(); } +TEST(Imgproc_Integral, accuracy) { CV_IntegralTest test; test.safe_run(); } diff --git a/modules/imgproc/test/test_floodfill.cpp b/modules/imgproc/test/test_floodfill.cpp new file mode 100644 index 000000000..1357e3408 --- /dev/null +++ b/modules/imgproc/test/test_floodfill.cpp @@ -0,0 +1,533 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_FloodFillTest : public cvtest::ArrayTest +{ +public: + CV_FloodFillTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int ); + + void fill_array( int test_case_idx, int i, int j, Mat& arr ); + + /*int write_default_params(CvFileStorage* fs); + void get_timing_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types + CvSize** whole_sizes, bool *are_images ); + void print_timing_params( int test_case_idx, char* ptr, int params_left );*/ + CvPoint seed_pt; + CvScalar new_val; + CvScalar l_diff, u_diff; + int connectivity; + bool use_mask, mask_only; + int range_type; + int new_mask_val; + bool test_cpp; +}; + + +CV_FloodFillTest::CV_FloodFillTest() +{ + test_array[INPUT_OUTPUT].push_back(NULL); + test_array[INPUT_OUTPUT].push_back(NULL); + test_array[REF_INPUT_OUTPUT].push_back(NULL); + test_array[REF_INPUT_OUTPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + optional_mask = false; + element_wise_relative_error = true; + + test_cpp = false; +} + + +void CV_FloodFillTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, + vector >& types ) +{ + RNG& rng = ts->get_rng(); + int depth, cn; + int i; + double buf[8]; + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + depth = cvtest::randInt(rng) % 2; + depth = depth == 0 ? CV_8U : CV_32F; + cn = cvtest::randInt(rng) & 1 ? 3 : 1; + + use_mask = (cvtest::randInt(rng) & 1) != 0; + connectivity = (cvtest::randInt(rng) & 1) ? 4 : 8; + mask_only = use_mask && (cvtest::randInt(rng) & 1) != 0; + new_mask_val = cvtest::randInt(rng) & 255; + range_type = cvtest::randInt(rng) % 3; + + types[INPUT_OUTPUT][0] = types[REF_INPUT_OUTPUT][0] = CV_MAKETYPE(depth, cn); + types[INPUT_OUTPUT][1] = types[REF_INPUT_OUTPUT][1] = CV_8UC1; + types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_64FC1; + sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = cvSize(9,1); + + if( !use_mask ) + sizes[INPUT_OUTPUT][1] = sizes[REF_INPUT_OUTPUT][1] = cvSize(0,0); + else + { + CvSize sz = sizes[INPUT_OUTPUT][0]; + sizes[INPUT_OUTPUT][1] = sizes[REF_INPUT_OUTPUT][1] = cvSize(sz.width+2,sz.height+2); + } + + seed_pt.x = cvtest::randInt(rng) % sizes[INPUT_OUTPUT][0].width; + seed_pt.y = cvtest::randInt(rng) % sizes[INPUT_OUTPUT][0].height; + + if( range_type == 0 ) + l_diff = u_diff = Scalar::all(0.); + else + { + Mat m( 1, 8, CV_16S, buf ); + rng.fill( m, RNG::NORMAL, Scalar::all(0), Scalar::all(32) ); + for( i = 0; i < 4; i++ ) + { + l_diff.val[i] = fabs(m.at(i)/16.); + u_diff.val[i] = fabs(m.at(i+4)/16.); + } + } + + new_val = Scalar::all(0.); + for( i = 0; i < cn; i++ ) + new_val.val[i] = cvtest::randReal(rng)*255; + + test_cpp = (cvtest::randInt(rng) & 256) == 0; +} + + +double CV_FloodFillTest::get_success_error_level( int /*test_case_idx*/, int i, int j ) +{ + return i == OUTPUT ? FLT_EPSILON : j == 0 ? FLT_EPSILON : 0; +} + + +void CV_FloodFillTest::fill_array( int test_case_idx, int i, int j, Mat& arr ) +{ + RNG& rng = ts->get_rng(); + + if( i != INPUT && i != INPUT_OUTPUT ) + { + cvtest::ArrayTest::fill_array( test_case_idx, i, j, arr ); + return; + } + + if( j == 0 ) + { + Mat tmp = arr; + Scalar m = Scalar::all(128); + Scalar s = Scalar::all(10); + + if( arr.depth() == CV_32FC1 ) + tmp.create(arr.size(), CV_MAKETYPE(CV_8U, arr.channels())); + + if( range_type == 0 ) + s = Scalar::all(2); + + rng.fill(tmp, RNG::NORMAL, m, s ); + if( arr.data != tmp.data ) + cvtest::convert(tmp, arr, arr.type()); + } + else + { + Scalar l = Scalar::all(-2); + Scalar u = Scalar::all(2); + cvtest::randUni(rng, arr, l, u ); + rectangle( arr, Point(0,0), Point(arr.cols-1,arr.rows-1), Scalar::all(1), 1, 8, 0 ); + } +} + + +void CV_FloodFillTest::run_func() +{ + int flags = connectivity + (mask_only ? CV_FLOODFILL_MASK_ONLY : 0) + + (range_type == 1 ? CV_FLOODFILL_FIXED_RANGE : 0) + (new_mask_val << 8); + double* odata = test_mat[OUTPUT][0].ptr(); + + if(!test_cpp) + { + CvConnectedComp comp; + cvFloodFill( test_array[INPUT_OUTPUT][0], seed_pt, new_val, l_diff, u_diff, &comp, + flags, test_array[INPUT_OUTPUT][1] ); + odata[0] = comp.area; + odata[1] = comp.rect.x; + odata[2] = comp.rect.y; + odata[3] = comp.rect.width; + odata[4] = comp.rect.height; + odata[5] = comp.value.val[0]; + odata[6] = comp.value.val[1]; + odata[7] = comp.value.val[2]; + odata[8] = comp.value.val[3]; + } + else + { + cv::Mat img = cv::cvarrToMat(test_array[INPUT_OUTPUT][0]), + mask = test_array[INPUT_OUTPUT][1] ? cv::cvarrToMat(test_array[INPUT_OUTPUT][1]) : cv::Mat(); + cv::Rect rect; + int area; + if( !mask.data ) + area = cv::floodFill( img, seed_pt, new_val, &rect, l_diff, u_diff, flags ); + else + area = cv::floodFill( img, mask, seed_pt, new_val, &rect, l_diff, u_diff, flags ); + odata[0] = area; + odata[1] = rect.x; + odata[2] = rect.y; + odata[3] = rect.width; + odata[4] = rect.height; + odata[5] = odata[6] = odata[7] = odata[8] = 0; + } +} + + +typedef struct ff_offset_pair_t +{ + int mofs, iofs; +} +ff_offset_pair_t; + +static void +cvTsFloodFill( CvMat* _img, CvPoint seed_pt, CvScalar new_val, + CvScalar l_diff, CvScalar u_diff, CvMat* _mask, + double* comp, int connectivity, int range_type, + int new_mask_val, bool mask_only ) +{ + CvMemStorage* st = cvCreateMemStorage(); + ff_offset_pair_t p0, p; + CvSeq* seq = cvCreateSeq( 0, sizeof(CvSeq), sizeof(p0), st ); + CvMat* tmp = _img; + CvMat* mask; + CvRect r = cvRect( 0, 0, -1, -1 ); + int area = 0; + int i, j; + ushort* m; + float* img; + int mstep, step; + int cn = CV_MAT_CN(_img->type); + int mdelta[8], idelta[8], ncount; + int cols = _img->cols, rows = _img->rows; + int u0 = 0, u1 = 0, u2 = 0; + double s0 = 0, s1 = 0, s2 = 0; + + if( CV_MAT_DEPTH(_img->type) == CV_8U ) + { + tmp = cvCreateMat( rows, cols, CV_MAKETYPE(CV_32F,CV_MAT_CN(_img->type)) ); + cvTsConvert(_img, tmp); + } + + mask = cvCreateMat( rows + 2, cols + 2, CV_16UC1 ); + + if( _mask ) + cvTsConvert( _mask, mask ); + else + { + cvTsZero( mask ); + cvRectangle( mask, cvPoint(0,0), cvPoint(mask->cols-1,mask->rows-1), Scalar::all(1.), 1, 8, 0 ); + } + + new_mask_val = (new_mask_val != 0 ? new_mask_val : 1) << 8; + + m = (ushort*)(mask->data.ptr + mask->step) + 1; + mstep = mask->step / sizeof(m[0]); + img = tmp->data.fl; + step = tmp->step / sizeof(img[0]); + + p0.mofs = seed_pt.y*mstep + seed_pt.x; + p0.iofs = seed_pt.y*step + seed_pt.x*cn; + + if( m[p0.mofs] ) + goto _exit_; + + cvSeqPush( seq, &p0 ); + m[p0.mofs] = (ushort)new_mask_val; + + if( connectivity == 4 ) + { + ncount = 4; + mdelta[0] = -mstep; idelta[0] = -step; + mdelta[1] = -1; idelta[1] = -cn; + mdelta[2] = 1; idelta[2] = cn; + mdelta[3] = mstep; idelta[3] = step; + } + else + { + ncount = 8; + mdelta[0] = -mstep-1; mdelta[1] = -mstep; mdelta[2] = -mstep+1; + idelta[0] = -step-cn; idelta[1] = -step; idelta[2] = -step+cn; + + mdelta[3] = -1; mdelta[4] = 1; + idelta[3] = -cn; idelta[4] = cn; + + mdelta[5] = mstep-1; mdelta[6] = mstep; mdelta[7] = mstep+1; + idelta[5] = step-cn; idelta[6] = step; idelta[7] = step+cn; + } + + if( cn == 1 ) + { + float a0 = (float)-l_diff.val[0]; + float b0 = (float)u_diff.val[0]; + + s0 = img[p0.iofs]; + + if( range_type < 2 ) + { + a0 += (float)s0; b0 += (float)s0; + } + + while( seq->total ) + { + cvSeqPop( seq, &p0 ); + float a = a0, b = b0; + float* ptr = img + p0.iofs; + ushort* mptr = m + p0.mofs; + + if( range_type == 2 ) + a += ptr[0], b += ptr[0]; + + for( i = 0; i < ncount; i++ ) + { + int md = mdelta[i], id = idelta[i]; + float v; + if( !mptr[md] && a <= (v = ptr[id]) && v <= b ) + { + mptr[md] = (ushort)new_mask_val; + p.mofs = p0.mofs + md; + p.iofs = p0.iofs + id; + cvSeqPush( seq, &p ); + } + } + } + } + else + { + float a0 = (float)-l_diff.val[0]; + float a1 = (float)-l_diff.val[1]; + float a2 = (float)-l_diff.val[2]; + float b0 = (float)u_diff.val[0]; + float b1 = (float)u_diff.val[1]; + float b2 = (float)u_diff.val[2]; + + s0 = img[p0.iofs]; + s1 = img[p0.iofs + 1]; + s2 = img[p0.iofs + 2]; + + if( range_type < 2 ) + { + a0 += (float)s0; b0 += (float)s0; + a1 += (float)s1; b1 += (float)s1; + a2 += (float)s2; b2 += (float)s2; + } + + while( seq->total ) + { + cvSeqPop( seq, &p0 ); + float _a0 = a0, _a1 = a1, _a2 = a2; + float _b0 = b0, _b1 = b1, _b2 = b2; + float* ptr = img + p0.iofs; + ushort* mptr = m + p0.mofs; + + if( range_type == 2 ) + { + _a0 += ptr[0]; _b0 += ptr[0]; + _a1 += ptr[1]; _b1 += ptr[1]; + _a2 += ptr[2]; _b2 += ptr[2]; + } + + for( i = 0; i < ncount; i++ ) + { + int md = mdelta[i], id = idelta[i]; + float v; + if( !mptr[md] && + _a0 <= (v = ptr[id]) && v <= _b0 && + _a1 <= (v = ptr[id+1]) && v <= _b1 && + _a2 <= (v = ptr[id+2]) && v <= _b2 ) + { + mptr[md] = (ushort)new_mask_val; + p.mofs = p0.mofs + md; + p.iofs = p0.iofs + id; + cvSeqPush( seq, &p ); + } + } + } + } + + r.x = r.width = seed_pt.x; + r.y = r.height = seed_pt.y; + + if( !mask_only ) + { + s0 = new_val.val[0]; + s1 = new_val.val[1]; + s2 = new_val.val[2]; + + if( tmp != _img ) + { + u0 = saturate_cast(s0); + u1 = saturate_cast(s1); + u2 = saturate_cast(s2); + + s0 = u0; + s1 = u1; + s2 = u2; + } + } + else + s0 = s1 = s2 = 0; + + new_mask_val >>= 8; + + for( i = 0; i < rows; i++ ) + { + float* ptr = img + i*step; + ushort* mptr = m + i*mstep; + uchar* dmptr = _mask ? _mask->data.ptr + (i+1)*_mask->step + 1 : 0; + uchar* dptr = tmp != _img ? _img->data.ptr + i*_img->step : 0; + double area0 = area; + + for( j = 0; j < cols; j++ ) + { + if( mptr[j] > 255 ) + { + if( dmptr ) + dmptr[j] = (uchar)new_mask_val; + if( !mask_only ) + { + if( cn == 1 ) + { + if( dptr ) + dptr[j] = (uchar)u0; + else + ptr[j] = (float)s0; + } + else + { + if( dptr ) + { + dptr[j*3] = (uchar)u0; + dptr[j*3+1] = (uchar)u1; + dptr[j*3+2] = (uchar)u2; + } + else + { + ptr[j*3] = (float)s0; + ptr[j*3+1] = (float)s1; + ptr[j*3+2] = (float)s2; + } + } + } + else + { + if( cn == 1 ) + s0 += ptr[j]; + else + { + s0 += ptr[j*3]; + s1 += ptr[j*3+1]; + s2 += ptr[j*3+2]; + } + } + + area++; + if( r.x > j ) + r.x = j; + if( r.width < j ) + r.width = j; + } + } + + if( area != area0 ) + { + if( r.y > i ) + r.y = i; + if( r.height < i ) + r.height = i; + } + } + +_exit_: + cvReleaseMat( &mask ); + if( tmp != _img ) + cvReleaseMat( &tmp ); + + comp[0] = area; + comp[1] = r.x; + comp[2] = r.y; + comp[3] = r.width - r.x + 1; + comp[4] = r.height - r.y + 1; + if( mask_only ) + { + double t = area ? 1./area : 0; + s0 *= t; + s1 *= t; + s2 *= t; + } + comp[5] = s0; + comp[6] = s1; + comp[7] = s2; + comp[8] = 0; +} + + +void CV_FloodFillTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + double* comp = test_mat[REF_OUTPUT][0].ptr(); + CvMat _input = test_mat[REF_INPUT_OUTPUT][0]; + CvMat _mask = test_mat[REF_INPUT_OUTPUT][1]; + cvTsFloodFill( &_input, seed_pt, new_val, l_diff, u_diff, + _mask.data.ptr ? &_mask : 0, + comp, connectivity, range_type, + new_mask_val, mask_only ); + if(test_cpp) + comp[5] = comp[6] = comp[7] = comp[8] = 0; +} + +TEST(Imgproc_FloodFill, accuracy) { CV_FloodFillTest test; test.safe_run(); } + +/* End of file. */ diff --git a/modules/imgproc/test/test_grabcut.cpp b/modules/imgproc/test/test_grabcut.cpp new file mode 100644 index 000000000..61eca241a --- /dev/null +++ b/modules/imgproc/test/test_grabcut.cpp @@ -0,0 +1,141 @@ +/*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 +#include + +using namespace std; +using namespace cv; + +class CV_GrabcutTest : public cvtest::BaseTest +{ +public: + CV_GrabcutTest(); + ~CV_GrabcutTest(); +protected: + bool verify(const Mat& mask, const Mat& exp); + void run(int); +}; + +CV_GrabcutTest::CV_GrabcutTest() {} +CV_GrabcutTest::~CV_GrabcutTest() {} + +bool CV_GrabcutTest::verify(const Mat& mask, const Mat& exp) +{ + const float maxDiffRatio = 0.005f; + int expArea = countNonZero( exp ); + int nonIntersectArea = countNonZero( mask != exp ); + + float curRatio = (float)nonIntersectArea / (float)expArea; + ts->printf( cvtest::TS::LOG, "nonIntersectArea/expArea = %f\n", curRatio ); + return curRatio < maxDiffRatio; +} + +void CV_GrabcutTest::run( int /* start_from */) +{ + cvtest::DefaultRngAuto defRng; + + Mat img = imread(string(ts->get_data_path()) + "shared/airplane.jpg"); + Mat mask_prob = imread(string(ts->get_data_path()) + "grabcut/mask_prob.png", 0); + Mat exp_mask1 = imread(string(ts->get_data_path()) + "grabcut/exp_mask1.png", 0); + Mat exp_mask2 = imread(string(ts->get_data_path()) + "grabcut/exp_mask2.png", 0); + + if (img.empty() || (!mask_prob.empty() && img.size() != mask_prob.size()) || + (!exp_mask1.empty() && img.size() != exp_mask1.size()) || + (!exp_mask2.empty() && img.size() != exp_mask2.size()) ) + { + ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA); + return; + } + + Rect rect(Point(24, 126), Point(483, 294)); + Mat exp_bgdModel, exp_fgdModel; + + Mat mask; + mask = Scalar(0); + Mat bgdModel, fgdModel; + grabCut( img, mask, rect, bgdModel, fgdModel, 0, GC_INIT_WITH_RECT ); + grabCut( img, mask, rect, bgdModel, fgdModel, 2, GC_EVAL ); + + // Multiply images by 255 for more visuality of test data. + if( mask_prob.empty() ) + { + mask.copyTo( mask_prob ); + imwrite(string(ts->get_data_path()) + "grabcut/mask_prob.png", mask_prob); + } + if( exp_mask1.empty() ) + { + exp_mask1 = (mask & 1) * 255; + imwrite(string(ts->get_data_path()) + "grabcut/exp_mask1.png", exp_mask1); + } + + if (!verify((mask & 1) * 255, exp_mask1)) + { + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + + mask = mask_prob; + bgdModel.release(); + fgdModel.release(); + rect = Rect(); + grabCut( img, mask, rect, bgdModel, fgdModel, 0, GC_INIT_WITH_MASK ); + grabCut( img, mask, rect, bgdModel, fgdModel, 1, GC_EVAL ); + + if( exp_mask2.empty() ) + { + exp_mask2 = (mask & 1) * 255; + imwrite(string(ts->get_data_path()) + "grabcut/exp_mask2.png", exp_mask2); + } + + if (!verify((mask & 1) * 255, exp_mask2)) + { + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return; + } + ts->set_failed_test_info(cvtest::TS::OK); +} + +TEST(Imgproc_GrabCut, regression) { CV_GrabcutTest test; test.safe_run(); } + diff --git a/modules/imgproc/test/test_histograms.cpp b/modules/imgproc/test/test_histograms.cpp new file mode 100644 index 000000000..07633be88 --- /dev/null +++ b/modules/imgproc/test/test_histograms.cpp @@ -0,0 +1,1842 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_BaseHistTest : public cvtest::BaseTest +{ +public: + enum { MAX_HIST = 12 }; + + CV_BaseHistTest(); + ~CV_BaseHistTest(); + void clear(); + +protected: + int read_params( CvFileStorage* fs ); + void run_func(void); + int prepare_test_case( int test_case_idx ); + int validate_test_results( int test_case_idx ); + virtual void init_hist( int test_case_idx, int i ); + + virtual void get_hist_params( int test_case_idx ); + virtual float** get_hist_ranges( int test_case_idx ); + + int max_log_size; + int max_cdims; + int cdims; + int dims[CV_MAX_DIM]; + int total_size; + int hist_type; + int hist_count; + int uniform; + int gen_random_hist; + double gen_hist_max_val, gen_hist_sparse_nz_ratio; + + int init_ranges; + int img_type; + int img_max_log_size; + double low, high, range_delta; + CvSize img_size; + + vector hist; + vector _ranges; + vector ranges; + bool test_cpp; +}; + + +CV_BaseHistTest::CV_BaseHistTest() +{ + test_case_count = 100; + max_log_size = 20; + img_max_log_size = 8; + max_cdims = 6; + hist_count = 1; + init_ranges = 0; + gen_random_hist = 0; + gen_hist_max_val = 100; + + test_cpp = false; +} + + +CV_BaseHistTest::~CV_BaseHistTest() +{ + clear(); +} + + +void CV_BaseHistTest::clear() +{ + cvtest::BaseTest::clear(); + for( size_t i = 0; i < hist.size(); i++ ) + cvReleaseHist( &hist[i] ); +} + + +int CV_BaseHistTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::BaseTest::read_params( fs ); + if( code < 0 ) + return code; + + test_case_count = cvReadInt( find_param( fs, "struct_count" ), test_case_count ); + max_log_size = cvReadInt( find_param( fs, "max_log_size" ), max_log_size ); + max_log_size = cvtest::clipInt( max_log_size, 1, 20 ); + img_max_log_size = cvReadInt( find_param( fs, "max_log_array_size" ), img_max_log_size ); + img_max_log_size = cvtest::clipInt( img_max_log_size, 1, 9 ); + + max_cdims = cvReadInt( find_param( fs, "max_cdims" ), max_cdims ); + max_cdims = cvtest::clipInt( max_cdims, 1, 6 ); + + return 0; +} + + +void CV_BaseHistTest::get_hist_params( int /*test_case_idx*/ ) +{ + RNG& rng = ts->get_rng(); + int i, max_dim_size, max_ni_dim_size = 31; + double hist_size; + + cdims = cvtest::randInt(rng) % max_cdims + 1; + hist_size = exp(cvtest::randReal(rng)*max_log_size*CV_LOG2); + max_dim_size = cvRound(pow(hist_size,1./cdims)); + total_size = 1; + uniform = cvtest::randInt(rng) % 2; + hist_type = cvtest::randInt(rng) % 2 ? CV_HIST_SPARSE : CV_HIST_ARRAY; + + for( i = 0; i < cdims; i++ ) + { + dims[i] = cvtest::randInt(rng) % (max_dim_size + 2) + 2; + if( !uniform ) + dims[i] = MIN(dims[i], max_ni_dim_size); + total_size *= dims[i]; + } + + img_type = cvtest::randInt(rng) % 2 ? CV_32F : CV_8U; + img_size.width = cvRound( exp(cvtest::randReal(rng) * img_max_log_size * CV_LOG2) ); + img_size.height = cvRound( exp(cvtest::randReal(rng) * img_max_log_size * CV_LOG2) ); + + if( img_type < CV_32F ) + { + low = cvtest::getMinVal(img_type); + high = cvtest::getMaxVal(img_type); + } + else + { + high = 1000; + low = -high; + } + + range_delta = (cvtest::randInt(rng) % 2)*(high-low)*0.05; +} + + +float** CV_BaseHistTest::get_hist_ranges( int /*test_case_idx*/ ) +{ + double _low = low + range_delta, _high = high - range_delta; + + if( !init_ranges ) + return 0; + + ranges.resize(cdims); + + if( uniform ) + { + _ranges.resize(cdims*2); + for( int i = 0; i < cdims; i++ ) + { + _ranges[i*2] = (float)_low; + _ranges[i*2+1] = (float)_high; + ranges[i] = &_ranges[i*2]; + } + } + else + { + int i, dims_sum = 0, ofs = 0; + for( i = 0; i < cdims; i++ ) + dims_sum += dims[i] + 1; + _ranges.resize(dims_sum); + + for( i = 0; i < cdims; i++ ) + { + int j, n = dims[i]; + // generate logarithmic scale + double delta, q, val; + for( j = 0; j < 10; j++ ) + { + q = 1. + (j+1)*0.1; + if( (pow(q,(double)n)-1)/(q-1.) >= _high-_low ) + break; + } + + if( j == 0 ) + { + delta = (_high-_low)/n; + q = 1.; + } + else + { + q = 1 + j*0.1; + delta = cvFloor((_high-_low)*(q-1)/(pow(q,(double)n) - 1)); + delta = MAX(delta, 1.); + } + val = _low; + + for( j = 0; j <= n; j++ ) + { + _ranges[j+ofs] = (float)MIN(val,_high); + val += delta; + delta *= q; + } + ranges[i] = &_ranges[ofs]; + ofs += n + 1; + } + } + + return &ranges[0]; +} + + +void CV_BaseHistTest::init_hist( int /*test_case_idx*/, int hist_i ) +{ + if( gen_random_hist ) + { + RNG& rng = ts->get_rng(); + + if( hist_type == CV_HIST_ARRAY ) + { + Mat h = cvarrToMat(hist[hist_i]->bins); + cvtest::randUni(rng, h, Scalar::all(0), Scalar::all(gen_hist_max_val) ); + } + else + { + CvArr* arr = hist[hist_i]->bins; + int i, j, total_size = 1, nz_count; + int idx[CV_MAX_DIM]; + for( i = 0; i < cdims; i++ ) + total_size *= dims[i]; + + nz_count = cvtest::randInt(rng) % MAX( total_size/4, 100 ); + nz_count = MIN( nz_count, total_size ); + + // a zero number of non-zero elements should be allowed + for( i = 0; i < nz_count; i++ ) + { + for( j = 0; j < cdims; j++ ) + idx[j] = cvtest::randInt(rng) % dims[j]; + cvSetRealND(arr, idx, cvtest::randReal(rng)*gen_hist_max_val); + } + } + } +} + + +int CV_BaseHistTest::prepare_test_case( int test_case_idx ) +{ + int i; + float** r; + + clear(); + + cvtest::BaseTest::prepare_test_case( test_case_idx ); + get_hist_params( test_case_idx ); + r = get_hist_ranges( test_case_idx ); + hist.resize(hist_count); + + for( i = 0; i < hist_count; i++ ) + { + hist[i] = cvCreateHist( cdims, dims, hist_type, r, uniform ); + init_hist( test_case_idx, i ); + } + test_cpp = (cvtest::randInt(ts->get_rng()) % 2) != 0; + + return 1; +} + + +void CV_BaseHistTest::run_func(void) +{ +} + + +int CV_BaseHistTest::validate_test_results( int /*test_case_idx*/ ) +{ + return 0; +} + + +////////////// testing operation for reading/writing individual histogram bins ////////////// + +class CV_QueryHistTest : public CV_BaseHistTest +{ +public: + CV_QueryHistTest(); + ~CV_QueryHistTest(); + void clear(); + +protected: + void run_func(void); + int prepare_test_case( int test_case_idx ); + int validate_test_results( int test_case_idx ); + void init_hist( int test_case_idx, int i ); + + CvMat* indices; + CvMat* values; + CvMat* values0; +}; + + + +CV_QueryHistTest::CV_QueryHistTest() +{ + hist_count = 1; + indices = 0; + values = 0; + values0 = 0; +} + + +CV_QueryHistTest::~CV_QueryHistTest() +{ + clear(); +} + + +void CV_QueryHistTest::clear() +{ + cvReleaseMat( &indices ); + cvReleaseMat( &values ); + cvReleaseMat( &values0 ); + CV_BaseHistTest::clear(); +} + + +void CV_QueryHistTest::init_hist( int /*test_case_idx*/, int i ) +{ + if( hist_type == CV_HIST_ARRAY ) + cvZero( hist[i]->bins ); +} + + +int CV_QueryHistTest::prepare_test_case( int test_case_idx ) +{ + int code = CV_BaseHistTest::prepare_test_case( test_case_idx ); + + if( code > 0 ) + { + int i, j, iters; + float default_value = 0.f; + RNG& rng = ts->get_rng(); + CvMat* bit_mask = 0; + int* idx; + + iters = (cvtest::randInt(rng) % MAX(total_size/10,100)) + 1; + iters = MIN( iters, total_size*9/10 + 1 ); + + indices = cvCreateMat( 1, iters*cdims, CV_32S ); + values = cvCreateMat( 1, iters, CV_32F ); + values0 = cvCreateMat( 1, iters, CV_32F ); + idx = indices->data.i; + + //printf( "total_size = %d, cdims = %d, iters = %d\n", total_size, cdims, iters ); + + bit_mask = cvCreateMat( 1, (total_size + 7)/8, CV_8U ); + cvZero( bit_mask ); + + #define GET_BIT(n) (bit_mask->data.ptr[(n)/8] & (1 << ((n)&7))) + #define SET_BIT(n) bit_mask->data.ptr[(n)/8] |= (1 << ((n)&7)) + + // set random histogram bins' values to the linear indices of the bins + for( i = 0; i < iters; i++ ) + { + int lin_idx = 0; + for( j = 0; j < cdims; j++ ) + { + int t = cvtest::randInt(rng) % dims[j]; + idx[i*cdims + j] = t; + lin_idx = lin_idx*dims[j] + t; + } + + if( cvtest::randInt(rng) % 8 || GET_BIT(lin_idx) ) + { + values0->data.fl[i] = (float)(lin_idx+1); + SET_BIT(lin_idx); + } + else + // some histogram bins will not be initialized intentionally, + // they should be equal to the default value + values0->data.fl[i] = default_value; + } + + // do the second pass to make values0 consistent with bit_mask + for( i = 0; i < iters; i++ ) + { + int lin_idx = 0; + for( j = 0; j < cdims; j++ ) + lin_idx = lin_idx*dims[j] + idx[i*cdims + j]; + + if( GET_BIT(lin_idx) ) + values0->data.fl[i] = (float)(lin_idx+1); + } + + cvReleaseMat( &bit_mask ); + } + + return code; +} + + +void CV_QueryHistTest::run_func(void) +{ + int i, iters = values->cols; + CvArr* h = hist[0]->bins; + const int* idx = indices->data.i; + float* val = values->data.fl; + float default_value = 0.f; + + // stage 1: write bins + if( cdims == 1 ) + for( i = 0; i < iters; i++ ) + { + float v0 = values0->data.fl[i]; + if( fabs(v0 - default_value) < FLT_EPSILON ) + continue; + if( !(i % 2) ) + { + if( !(i % 4) ) + cvSetReal1D( h, idx[i], v0 ); + else + *(float*)cvPtr1D( h, idx[i] ) = v0; + } + else + cvSetRealND( h, idx+i, v0 ); + } + else if( cdims == 2 ) + for( i = 0; i < iters; i++ ) + { + float v0 = values0->data.fl[i]; + if( fabs(v0 - default_value) < FLT_EPSILON ) + continue; + if( !(i % 2) ) + { + if( !(i % 4) ) + cvSetReal2D( h, idx[i*2], idx[i*2+1], v0 ); + else + *(float*)cvPtr2D( h, idx[i*2], idx[i*2+1] ) = v0; + } + else + cvSetRealND( h, idx+i*2, v0 ); + } + else if( cdims == 3 ) + for( i = 0; i < iters; i++ ) + { + float v0 = values0->data.fl[i]; + if( fabs(v0 - default_value) < FLT_EPSILON ) + continue; + if( !(i % 2) ) + { + if( !(i % 4) ) + cvSetReal3D( h, idx[i*3], idx[i*3+1], idx[i*3+2], v0 ); + else + *(float*)cvPtr3D( h, idx[i*3], idx[i*3+1], idx[i*3+2] ) = v0; + } + else + cvSetRealND( h, idx+i*3, v0 ); + } + else + for( i = 0; i < iters; i++ ) + { + float v0 = values0->data.fl[i]; + if( fabs(v0 - default_value) < FLT_EPSILON ) + continue; + if( !(i % 2) ) + cvSetRealND( h, idx+i*cdims, v0 ); + else + *(float*)cvPtrND( h, idx+i*cdims ) = v0; + } + + // stage 2: read bins + if( cdims == 1 ) + for( i = 0; i < iters; i++ ) + { + if( !(i % 2) ) + val[i] = *(float*)cvPtr1D( h, idx[i] ); + else + val[i] = (float)cvGetReal1D( h, idx[i] ); + } + else if( cdims == 2 ) + for( i = 0; i < iters; i++ ) + { + if( !(i % 2) ) + val[i] = *(float*)cvPtr2D( h, idx[i*2], idx[i*2+1] ); + else + val[i] = (float)cvGetReal2D( h, idx[i*2], idx[i*2+1] ); + } + else if( cdims == 3 ) + for( i = 0; i < iters; i++ ) + { + if( !(i % 2) ) + val[i] = *(float*)cvPtr3D( h, idx[i*3], idx[i*3+1], idx[i*3+2] ); + else + val[i] = (float)cvGetReal3D( h, idx[i*3], idx[i*3+1], idx[i*3+2] ); + } + else + for( i = 0; i < iters; i++ ) + { + if( !(i % 2) ) + val[i] = *(float*)cvPtrND( h, idx+i*cdims ); + else + val[i] = (float)cvGetRealND( h, idx+i*cdims ); + } +} + + +int CV_QueryHistTest::validate_test_results( int /*test_case_idx*/ ) +{ + int code = cvtest::TS::OK; + int i, j, iters = values->cols; + + for( i = 0; i < iters; i++ ) + { + float v = values->data.fl[i], v0 = values0->data.fl[i]; + + if( cvIsNaN(v) || cvIsInf(v) ) + { + ts->printf( cvtest::TS::LOG, "The bin #%d has invalid value\n", i ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + } + else if( fabs(v - v0) > FLT_EPSILON ) + { + ts->printf( cvtest::TS::LOG, "The bin #%d = %g, while it should be %g\n", i, v, v0 ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + + if( code < 0 ) + { + ts->printf( cvtest::TS::LOG, "The bin index = (" ); + for( j = 0; j < cdims; j++ ) + ts->printf( cvtest::TS::LOG, "%d%s", indices->data.i[i*cdims + j], + j < cdims-1 ? ", " : ")\n" ); + break; + } + } + + if( code < 0 ) + ts->set_failed_test_info( code ); + return code; +} + + +////////////// cvGetMinMaxHistValue ////////////// + +class CV_MinMaxHistTest : public CV_BaseHistTest +{ +public: + CV_MinMaxHistTest(); + +protected: + void run_func(void); + void init_hist(int, int); + int validate_test_results( int test_case_idx ); + int min_idx[CV_MAX_DIM], max_idx[CV_MAX_DIM]; + float min_val, max_val; + int min_idx0[CV_MAX_DIM], max_idx0[CV_MAX_DIM]; + float min_val0, max_val0; +}; + + + +CV_MinMaxHistTest::CV_MinMaxHistTest() +{ + hist_count = 1; + gen_random_hist = 1; +} + + +void CV_MinMaxHistTest::init_hist(int test_case_idx, int hist_i) +{ + int i, eq = 1; + RNG& rng = ts->get_rng(); + CV_BaseHistTest::init_hist( test_case_idx, hist_i ); + + for(;;) + { + for( i = 0; i < cdims; i++ ) + { + min_idx0[i] = cvtest::randInt(rng) % dims[i]; + max_idx0[i] = cvtest::randInt(rng) % dims[i]; + eq &= min_idx0[i] == max_idx0[i]; + } + if( !eq || total_size == 1 ) + break; + } + + min_val0 = (float)(-cvtest::randReal(rng)*10 - FLT_EPSILON); + max_val0 = (float)(cvtest::randReal(rng)*10 + FLT_EPSILON + gen_hist_max_val); + + if( total_size == 1 ) + min_val0 = max_val0; + + cvSetRealND( hist[0]->bins, min_idx0, min_val0 ); + cvSetRealND( hist[0]->bins, max_idx0, max_val0 ); +} + + +void CV_MinMaxHistTest::run_func(void) +{ + if( hist_type != CV_HIST_ARRAY && test_cpp ) + { + cv::SparseMat h((CvSparseMat*)hist[0]->bins); + double _min_val = 0, _max_val = 0; + cv::minMaxLoc(h, &_min_val, &_max_val, min_idx, max_idx ); + min_val = (float)_min_val; + max_val = (float)_max_val; + } + else + cvGetMinMaxHistValue( hist[0], &min_val, &max_val, min_idx, max_idx ); +} + + +int CV_MinMaxHistTest::validate_test_results( int /*test_case_idx*/ ) +{ + int code = cvtest::TS::OK; + + if( cvIsNaN(min_val) || cvIsInf(min_val) || + cvIsNaN(max_val) || cvIsInf(max_val) ) + { + ts->printf( cvtest::TS::LOG, + "The extrema histogram bin values are invalid (min = %g, max = %g)\n", min_val, max_val ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + } + else if( fabs(min_val - min_val0) > FLT_EPSILON || + fabs(max_val - max_val0) > FLT_EPSILON ) + { + ts->printf( cvtest::TS::LOG, + "The extrema histogram bin values are incorrect: (min = %g, should be = %g), (max = %g, should be = %g)\n", + min_val, min_val0, max_val, max_val0 ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + else + { + int i; + for( i = 0; i < cdims; i++ ) + { + if( min_idx[i] != min_idx0[i] || max_idx[i] != max_idx0[i] ) + { + ts->printf( cvtest::TS::LOG, + "The %d-th coordinates of extrema histogram bin values are incorrect: " + "(min = %d, should be = %d), (max = %d, should be = %d)\n", + i, min_idx[i], min_idx0[i], max_idx[i], max_idx0[i] ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + } + } + + if( code < 0 ) + ts->set_failed_test_info( code ); + return code; +} + + +////////////// cvNormalizeHist ////////////// + +class CV_NormHistTest : public CV_BaseHistTest +{ +public: + CV_NormHistTest(); + +protected: + int prepare_test_case( int test_case_idx ); + void run_func(void); + int validate_test_results( int test_case_idx ); + double factor; +}; + + + +CV_NormHistTest::CV_NormHistTest() +{ + hist_count = 1; + gen_random_hist = 1; + factor = 0; +} + + +int CV_NormHistTest::prepare_test_case( int test_case_idx ) +{ + int code = CV_BaseHistTest::prepare_test_case( test_case_idx ); + + if( code > 0 ) + { + RNG& rng = ts->get_rng(); + factor = cvtest::randReal(rng)*10 + 0.1; + if( hist_type == CV_HIST_SPARSE && + ((CvSparseMat*)hist[0]->bins)->heap->active_count == 0 ) + factor = 0; + } + + return code; +} + + +void CV_NormHistTest::run_func(void) +{ + if( hist_type != CV_HIST_ARRAY && test_cpp ) + { + cv::SparseMat h((CvSparseMat*)hist[0]->bins); + cv::normalize(h, h, factor, CV_L1); + cvReleaseSparseMat((CvSparseMat**)&hist[0]->bins); + hist[0]->bins = (CvSparseMat*)h; + } + else + cvNormalizeHist( hist[0], factor ); +} + + +int CV_NormHistTest::validate_test_results( int /*test_case_idx*/ ) +{ + int code = cvtest::TS::OK; + double sum = 0; + + if( hist_type == CV_HIST_ARRAY ) + { + int i; + const float* ptr = (float*)cvPtr1D( hist[0]->bins, 0 ); + + for( i = 0; i < total_size; i++ ) + sum += ptr[i]; + } + else + { + CvSparseMat* sparse = (CvSparseMat*)hist[0]->bins; + CvSparseMatIterator iterator; + CvSparseNode *node; + + for( node = cvInitSparseMatIterator( sparse, &iterator ); + node != 0; node = cvGetNextSparseNode( &iterator )) + { + sum += *(float*)CV_NODE_VAL(sparse,node); + } + } + + if( cvIsNaN(sum) || cvIsInf(sum) ) + { + ts->printf( cvtest::TS::LOG, + "The normalized histogram has invalid sum =%g\n", sum ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + } + else if( fabs(sum - factor) > FLT_EPSILON*10*fabs(factor) ) + { + ts->printf( cvtest::TS::LOG, + "The normalized histogram has incorrect sum =%g, while it should be =%g\n", sum, factor ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + + if( code < 0 ) + ts->set_failed_test_info( code ); + return code; +} + + +////////////// cvThreshHist ////////////// + +class CV_ThreshHistTest : public CV_BaseHistTest +{ +public: + CV_ThreshHistTest(); + ~CV_ThreshHistTest(); + void clear(); + +protected: + int prepare_test_case( int test_case_idx ); + void run_func(void); + int validate_test_results( int test_case_idx ); + CvMat* indices; + CvMat* values; + int orig_nz_count; + + double threshold; +}; + + + +CV_ThreshHistTest::CV_ThreshHistTest() +{ + hist_count = 1; + gen_random_hist = 1; + threshold = 0; + indices = values = 0; +} + + +CV_ThreshHistTest::~CV_ThreshHistTest() +{ + clear(); +} + + +void CV_ThreshHistTest::clear() +{ + cvReleaseMat( &indices ); + cvReleaseMat( &values ); + CV_BaseHistTest::clear(); +} + + +int CV_ThreshHistTest::prepare_test_case( int test_case_idx ) +{ + int code = CV_BaseHistTest::prepare_test_case( test_case_idx ); + + if( code > 0 ) + { + RNG& rng = ts->get_rng(); + threshold = cvtest::randReal(rng)*gen_hist_max_val; + + if( hist_type == CV_HIST_ARRAY ) + { + orig_nz_count = total_size; + + values = cvCreateMat( 1, total_size, CV_32F ); + memcpy( values->data.fl, cvPtr1D( hist[0]->bins, 0 ), total_size*sizeof(float) ); + } + else + { + CvSparseMat* sparse = (CvSparseMat*)hist[0]->bins; + CvSparseMatIterator iterator; + CvSparseNode* node; + int i, k; + + orig_nz_count = sparse->heap->active_count; + + values = cvCreateMat( 1, orig_nz_count+1, CV_32F ); + indices = cvCreateMat( 1, (orig_nz_count+1)*cdims, CV_32S ); + + for( node = cvInitSparseMatIterator( sparse, &iterator ), i = 0; + node != 0; node = cvGetNextSparseNode( &iterator ), i++ ) + { + const int* idx = CV_NODE_IDX(sparse,node); + + OPENCV_ASSERT( i < orig_nz_count, "CV_ThreshHistTest::prepare_test_case", "Buffer overflow" ); + + values->data.fl[i] = *(float*)CV_NODE_VAL(sparse,node); + for( k = 0; k < cdims; k++ ) + indices->data.i[i*cdims + k] = idx[k]; + } + + OPENCV_ASSERT( i == orig_nz_count, "Unmatched buffer size", + "CV_ThreshHistTest::prepare_test_case" ); + } + } + + return code; +} + + +void CV_ThreshHistTest::run_func(void) +{ + cvThreshHist( hist[0], threshold ); +} + + +int CV_ThreshHistTest::validate_test_results( int /*test_case_idx*/ ) +{ + int code = cvtest::TS::OK; + int i; + float* ptr0 = values->data.fl; + float* ptr = 0; + CvSparseMat* sparse = 0; + + if( hist_type == CV_HIST_ARRAY ) + ptr = (float*)cvPtr1D( hist[0]->bins, 0 ); + else + sparse = (CvSparseMat*)hist[0]->bins; + + if( code > 0 ) + { + for( i = 0; i < orig_nz_count; i++ ) + { + float v0 = ptr0[i], v; + + if( hist_type == CV_HIST_ARRAY ) + v = ptr[i]; + else + { + v = (float)cvGetRealND( sparse, indices->data.i + i*cdims ); + cvClearND( sparse, indices->data.i + i*cdims ); + } + + if( v0 <= threshold ) v0 = 0.f; + if( cvIsNaN(v) || cvIsInf(v) ) + { + ts->printf( cvtest::TS::LOG, "The %d-th bin is invalid (=%g)\n", i, v ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + break; + } + else if( fabs(v0 - v) > FLT_EPSILON*10*fabs(v0) ) + { + ts->printf( cvtest::TS::LOG, "The %d-th bin is incorrect (=%g, should be =%g)\n", i, v, v0 ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + break; + } + } + } + + if( code > 0 && hist_type == CV_HIST_SPARSE ) + { + if( sparse->heap->active_count > 0 ) + { + ts->printf( cvtest::TS::LOG, + "There some extra histogram bins in the sparse histogram after the thresholding\n" ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + } + } + + if( code < 0 ) + ts->set_failed_test_info( code ); + return code; +} + + +////////////// cvCompareHist ////////////// + +class CV_CompareHistTest : public CV_BaseHistTest +{ +public: + enum { MAX_METHOD = 4 }; + + CV_CompareHistTest(); +protected: + int prepare_test_case( int test_case_idx ); + void run_func(void); + int validate_test_results( int test_case_idx ); + double result[MAX_METHOD+1]; +}; + + + +CV_CompareHistTest::CV_CompareHistTest() +{ + hist_count = 2; + gen_random_hist = 1; +} + + +int CV_CompareHistTest::prepare_test_case( int test_case_idx ) +{ + int code = CV_BaseHistTest::prepare_test_case( test_case_idx ); + + return code; +} + + +void CV_CompareHistTest::run_func(void) +{ + int k; + if( hist_type != CV_HIST_ARRAY && test_cpp ) + { + cv::SparseMat h0((CvSparseMat*)hist[0]->bins); + cv::SparseMat h1((CvSparseMat*)hist[1]->bins); + for( k = 0; k < MAX_METHOD; k++ ) + result[k] = cv::compareHist(h0, h1, k); + } + else + for( k = 0; k < MAX_METHOD; k++ ) + result[k] = cvCompareHist( hist[0], hist[1], k ); +} + + +int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ ) +{ + int code = cvtest::TS::OK; + int i; + double result0[MAX_METHOD+1]; + double s0 = 0, s1 = 0, sq0 = 0, sq1 = 0, t; + + for( i = 0; i < MAX_METHOD; i++ ) + result0[i] = 0; + + if( hist_type == CV_HIST_ARRAY ) + { + float* ptr0 = (float*)cvPtr1D( hist[0]->bins, 0 ); + float* ptr1 = (float*)cvPtr1D( hist[1]->bins, 0 ); + + for( i = 0; i < total_size; i++ ) + { + double v0 = ptr0[i], v1 = ptr1[i]; + result0[CV_COMP_CORREL] += v0*v1; + result0[CV_COMP_INTERSECT] += MIN(v0,v1); + if( fabs(v0 + v1) > DBL_EPSILON ) + result0[CV_COMP_CHISQR] += (v0 - v1)*(v0 - v1)/(v0 + v1); + s0 += v0; + s1 += v1; + sq0 += v0*v0; + sq1 += v1*v1; + result0[CV_COMP_BHATTACHARYYA] += sqrt(v0*v1); + } + } + else + { + CvSparseMat* sparse0 = (CvSparseMat*)hist[0]->bins; + CvSparseMat* sparse1 = (CvSparseMat*)hist[1]->bins; + CvSparseMatIterator iterator; + CvSparseNode* node; + + for( node = cvInitSparseMatIterator( sparse0, &iterator ); + node != 0; node = cvGetNextSparseNode( &iterator ) ) + { + const int* idx = CV_NODE_IDX(sparse0, node); + double v0 = *(float*)CV_NODE_VAL(sparse0, node); + double v1 = (float)cvGetRealND(sparse1, idx); + + result0[CV_COMP_CORREL] += v0*v1; + result0[CV_COMP_INTERSECT] += MIN(v0,v1); + if( fabs(v0 + v1) > DBL_EPSILON ) + result0[CV_COMP_CHISQR] += (v0 - v1)*(v0 - v1)/(v0 + v1); + s0 += v0; + sq0 += v0*v0; + result0[CV_COMP_BHATTACHARYYA] += sqrt(v0*v1); + } + + for( node = cvInitSparseMatIterator( sparse1, &iterator ); + node != 0; node = cvGetNextSparseNode( &iterator ) ) + { + const int* idx = CV_NODE_IDX(sparse1, node); + double v1 = *(float*)CV_NODE_VAL(sparse1, node); + double v0 = (float)cvGetRealND(sparse0, idx); + + if( fabs(v0) < DBL_EPSILON ) + result0[CV_COMP_CHISQR] += v1; + s1 += v1; + sq1 += v1*v1; + } + } + + t = (sq0 - s0*s0/total_size)*(sq1 - s1*s1/total_size); + result0[CV_COMP_CORREL] = fabs(t) > DBL_EPSILON ? + (result0[CV_COMP_CORREL] - s0*s1/total_size)/sqrt(t) : 1; + + s1 *= s0; + s0 = result0[CV_COMP_BHATTACHARYYA]; + s0 = 1. - s0*(s1 > FLT_EPSILON ? 1./sqrt(s1) : 1.); + result0[CV_COMP_BHATTACHARYYA] = sqrt(MAX(s0,0.)); + + for( i = 0; i < MAX_METHOD; i++ ) + { + double v = result[i], v0 = result0[i]; + const char* method_name = + i == CV_COMP_CHISQR ? "Chi-Square" : + i == CV_COMP_CORREL ? "Correlation" : + i == CV_COMP_INTERSECT ? "Intersection" : + i == CV_COMP_BHATTACHARYYA ? "Bhattacharyya" : "Unknown"; + + if( cvIsNaN(v) || cvIsInf(v) ) + { + ts->printf( cvtest::TS::LOG, "The comparison result using the method #%d (%s) is invalid (=%g)\n", + i, method_name, v ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + break; + } + else if( fabs(v0 - v) > FLT_EPSILON*10*MAX(fabs(v0),0.1) ) + { + ts->printf( cvtest::TS::LOG, "The comparison result using the method #%d (%s)\n\tis inaccurate (=%g, should be =%g)\n", + i, method_name, v, v0 ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + break; + } + } + + if( code < 0 ) + ts->set_failed_test_info( code ); + return code; +} + + +////////////// cvCalcHist ////////////// + +class CV_CalcHistTest : public CV_BaseHistTest +{ +public: + CV_CalcHistTest(); + ~CV_CalcHistTest(); + void clear(); + +protected: + int prepare_test_case( int test_case_idx ); + void run_func(void); + int validate_test_results( int test_case_idx ); + IplImage* images[CV_MAX_DIM+1]; + int channels[CV_MAX_DIM+1]; +}; + + + +CV_CalcHistTest::CV_CalcHistTest() +{ + int i; + + hist_count = 2; + gen_random_hist = 0; + init_ranges = 1; + + for( i = 0; i <= CV_MAX_DIM; i++ ) + { + images[i] = 0; + channels[i] = 0; + } +} + + +CV_CalcHistTest::~CV_CalcHistTest() +{ + clear(); +} + + +void CV_CalcHistTest::clear() +{ + int i; + + for( i = 0; i <= CV_MAX_DIM; i++ ) + cvReleaseImage( &images[i] ); + + CV_BaseHistTest::clear(); +} + + +int CV_CalcHistTest::prepare_test_case( int test_case_idx ) +{ + int code = CV_BaseHistTest::prepare_test_case( test_case_idx ); + + if( code > 0 ) + { + RNG& rng = ts->get_rng(); + int i; + + for( i = 0; i <= CV_MAX_DIM; i++ ) + { + if( i < cdims ) + { + int nch = 1; //cvtest::randInt(rng) % 3 + 1; + images[i] = cvCreateImage( img_size, + img_type == CV_8U ? IPL_DEPTH_8U : IPL_DEPTH_32F, nch ); + channels[i] = cvtest::randInt(rng) % nch; + Mat images_i = cvarrToMat(images[i]); + + cvtest::randUni( rng, images_i, Scalar::all(low), Scalar::all(high) ); + } + else if( i == CV_MAX_DIM && cvtest::randInt(rng) % 2 ) + { + // create mask + images[i] = cvCreateImage( img_size, IPL_DEPTH_8U, 1 ); + Mat images_i = cvarrToMat(images[i]); + + // make ~25% pixels in the mask non-zero + cvtest::randUni( rng, images_i, Scalar::all(-2), Scalar::all(2) ); + } + } + } + + return code; +} + + +void CV_CalcHistTest::run_func(void) +{ + cvCalcHist( images, hist[0], 0, images[CV_MAX_DIM] ); +} + + +static void +cvTsCalcHist( IplImage** _images, CvHistogram* hist, IplImage* _mask, int* channels ) +{ + int x, y, k, cdims; + union + { + float* fl; + uchar* ptr; + } + plane[CV_MAX_DIM]; + int nch[CV_MAX_DIM]; + int dims[CV_MAX_DIM]; + int uniform = CV_IS_UNIFORM_HIST(hist); + CvSize img_size = cvGetSize(_images[0]); + CvMat images[CV_MAX_DIM], mask = cvMat(1,1,CV_8U); + int img_depth = _images[0]->depth; + + cdims = cvGetDims( hist->bins, dims ); + + cvZero( hist->bins ); + + for( k = 0; k < cdims; k++ ) + { + cvGetMat( _images[k], &images[k] ); + nch[k] = _images[k]->nChannels; + } + + if( _mask ) + cvGetMat( _mask, &mask ); + + for( y = 0; y < img_size.height; y++ ) + { + const uchar* mptr = _mask ? &CV_MAT_ELEM(mask, uchar, y, 0 ) : 0; + + if( img_depth == IPL_DEPTH_8U ) + for( k = 0; k < cdims; k++ ) + plane[k].ptr = &CV_MAT_ELEM(images[k], uchar, y, 0 ) + channels[k]; + else + for( k = 0; k < cdims; k++ ) + plane[k].fl = &CV_MAT_ELEM(images[k], float, y, 0 ) + channels[k]; + + for( x = 0; x < img_size.width; x++ ) + { + float val[CV_MAX_DIM]; + int idx[CV_MAX_DIM]; + + if( mptr && !mptr[x] ) + continue; + if( img_depth == IPL_DEPTH_8U ) + for( k = 0; k < cdims; k++ ) + val[k] = plane[k].ptr[x*nch[k]]; + else + for( k = 0; k < cdims; k++ ) + val[k] = plane[k].fl[x*nch[k]]; + + idx[cdims-1] = -1; + + if( uniform ) + { + for( k = 0; k < cdims; k++ ) + { + double v = val[k], lo = hist->thresh[k][0], hi = hist->thresh[k][1]; + idx[k] = cvFloor((v - lo)*dims[k]/(hi - lo)); + if( idx[k] < 0 || idx[k] >= dims[k] ) + break; + } + } + else + { + for( k = 0; k < cdims; k++ ) + { + float v = val[k]; + float* t = hist->thresh2[k]; + int j, n = dims[k]; + + for( j = 0; j <= n; j++ ) + if( v < t[j] ) + break; + if( j <= 0 || j > n ) + break; + idx[k] = j-1; + } + } + + if( k < cdims ) + continue; + + (*(float*)cvPtrND( hist->bins, idx ))++; + } + } +} + + +int CV_CalcHistTest::validate_test_results( int /*test_case_idx*/ ) +{ + int code = cvtest::TS::OK; + double diff; + cvTsCalcHist( images, hist[1], images[CV_MAX_DIM], channels ); + diff = cvCompareHist( hist[0], hist[1], CV_COMP_CHISQR ); + if( diff > DBL_EPSILON ) + { + ts->printf( cvtest::TS::LOG, "The histogram does not match to the reference one\n" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + + } + + if( code < 0 ) + ts->set_failed_test_info( code ); + return code; +} + + +CV_CalcHistTest hist_calc_test; + + + +////////////// cvCalcBackProject ////////////// + +class CV_CalcBackProjectTest : public CV_BaseHistTest +{ +public: + CV_CalcBackProjectTest(); + ~CV_CalcBackProjectTest(); + void clear(); + +protected: + int prepare_test_case( int test_case_idx ); + void run_func(void); + int validate_test_results( int test_case_idx ); + IplImage* images[CV_MAX_DIM+3]; + int channels[CV_MAX_DIM+3]; +}; + + + +CV_CalcBackProjectTest::CV_CalcBackProjectTest() +{ + int i; + + hist_count = 1; + gen_random_hist = 0; + init_ranges = 1; + + for( i = 0; i < CV_MAX_DIM+3; i++ ) + { + images[i] = 0; + channels[i] = 0; + } +} + + +CV_CalcBackProjectTest::~CV_CalcBackProjectTest() +{ + clear(); +} + + +void CV_CalcBackProjectTest::clear() +{ + int i; + + for( i = 0; i < CV_MAX_DIM+3; i++ ) + cvReleaseImage( &images[i] ); + + CV_BaseHistTest::clear(); +} + + +int CV_CalcBackProjectTest::prepare_test_case( int test_case_idx ) +{ + int code = CV_BaseHistTest::prepare_test_case( test_case_idx ); + + if( code > 0 ) + { + RNG& rng = ts->get_rng(); + int i, j, n, img_len = img_size.width*img_size.height; + + for( i = 0; i < CV_MAX_DIM + 3; i++ ) + { + if( i < cdims ) + { + int nch = 1; //cvtest::randInt(rng) % 3 + 1; + images[i] = cvCreateImage( img_size, + img_type == CV_8U ? IPL_DEPTH_8U : IPL_DEPTH_32F, nch ); + channels[i] = cvtest::randInt(rng) % nch; + + Mat images_i = cvarrToMat(images[i]); + cvtest::randUni( rng, images_i, Scalar::all(low), Scalar::all(high) ); + } + else if( i == CV_MAX_DIM && cvtest::randInt(rng) % 2 ) + { + // create mask + images[i] = cvCreateImage( img_size, IPL_DEPTH_8U, 1 ); + Mat images_i = cvarrToMat(images[i]); + // make ~25% pixels in the mask non-zero + cvtest::randUni( rng, images_i, Scalar::all(-2), Scalar::all(2) ); + } + else if( i > CV_MAX_DIM ) + { + images[i] = cvCreateImage( img_size, images[0]->depth, 1 ); + } + } + + cvTsCalcHist( images, hist[0], images[CV_MAX_DIM], channels ); + + // now modify the images a bit to add some zeros go to the backprojection + n = cvtest::randInt(rng) % (img_len/20+1); + for( i = 0; i < cdims; i++ ) + { + char* data = images[i]->imageData; + for( j = 0; j < n; j++ ) + { + int idx = cvtest::randInt(rng) % img_len; + double val = cvtest::randReal(rng)*(high - low) + low; + + if( img_type == CV_8U ) + ((uchar*)data)[idx] = (uchar)cvRound(val); + else + ((float*)data)[idx] = (float)val; + } + } + } + + return code; +} + + +void CV_CalcBackProjectTest::run_func(void) +{ + cvCalcBackProject( images, images[CV_MAX_DIM+1], hist[0] ); +} + + +static void +cvTsCalcBackProject( IplImage** images, IplImage* dst, CvHistogram* hist, int* channels ) +{ + int x, y, k, cdims; + union + { + float* fl; + uchar* ptr; + } + plane[CV_MAX_DIM]; + int nch[CV_MAX_DIM]; + int dims[CV_MAX_DIM]; + int uniform = CV_IS_UNIFORM_HIST(hist); + CvSize img_size = cvGetSize(images[0]); + int img_depth = images[0]->depth; + + cdims = cvGetDims( hist->bins, dims ); + + for( k = 0; k < cdims; k++ ) + nch[k] = images[k]->nChannels; + + for( y = 0; y < img_size.height; y++ ) + { + if( img_depth == IPL_DEPTH_8U ) + for( k = 0; k < cdims; k++ ) + plane[k].ptr = &CV_IMAGE_ELEM(images[k], uchar, y, 0 ) + channels[k]; + else + for( k = 0; k < cdims; k++ ) + plane[k].fl = &CV_IMAGE_ELEM(images[k], float, y, 0 ) + channels[k]; + + for( x = 0; x < img_size.width; x++ ) + { + float val[CV_MAX_DIM]; + float bin_val = 0; + int idx[CV_MAX_DIM]; + + if( img_depth == IPL_DEPTH_8U ) + for( k = 0; k < cdims; k++ ) + val[k] = plane[k].ptr[x*nch[k]]; + else + for( k = 0; k < cdims; k++ ) + val[k] = plane[k].fl[x*nch[k]]; + idx[cdims-1] = -1; + + if( uniform ) + { + for( k = 0; k < cdims; k++ ) + { + double v = val[k], lo = hist->thresh[k][0], hi = hist->thresh[k][1]; + idx[k] = cvFloor((v - lo)*dims[k]/(hi - lo)); + if( idx[k] < 0 || idx[k] >= dims[k] ) + break; + } + } + else + { + for( k = 0; k < cdims; k++ ) + { + float v = val[k]; + float* t = hist->thresh2[k]; + int j, n = dims[k]; + + for( j = 0; j <= n; j++ ) + if( v < t[j] ) + break; + if( j <= 0 || j > n ) + break; + idx[k] = j-1; + } + } + + if( k == cdims ) + bin_val = (float)cvGetRealND( hist->bins, idx ); + + if( img_depth == IPL_DEPTH_8U ) + { + int t = cvRound(bin_val); + CV_IMAGE_ELEM( dst, uchar, y, x ) = saturate_cast(t); + } + else + CV_IMAGE_ELEM( dst, float, y, x ) = bin_val; + } + } +} + + +int CV_CalcBackProjectTest::validate_test_results( int test_case_idx ) +{ + int code = cvtest::TS::OK; + + cvTsCalcBackProject( images, images[CV_MAX_DIM+2], hist[0], channels ); + Mat a = cvarrToMat(images[CV_MAX_DIM+1]), b = cvarrToMat(images[CV_MAX_DIM+2]); + double threshold = a.depth() == CV_8U ? 2 : FLT_EPSILON; + code = cvtest::cmpEps2( ts, a, b, threshold, true, "Back project image" ); + + if( code < 0 ) + ts->set_failed_test_info( code ); + + return code; +} + + +////////////// cvCalcBackProjectPatch ////////////// + +class CV_CalcBackProjectPatchTest : public CV_BaseHistTest +{ +public: + CV_CalcBackProjectPatchTest(); + ~CV_CalcBackProjectPatchTest(); + void clear(); + +protected: + int prepare_test_case( int test_case_idx ); + void run_func(void); + int validate_test_results( int test_case_idx ); + IplImage* images[CV_MAX_DIM+2]; + int channels[CV_MAX_DIM+2]; + + CvSize patch_size; + double factor; + int method; +}; + + + +CV_CalcBackProjectPatchTest::CV_CalcBackProjectPatchTest() +{ + int i; + + hist_count = 1; + gen_random_hist = 0; + init_ranges = 1; + img_max_log_size = 6; + + for( i = 0; i < CV_MAX_DIM+2; i++ ) + { + images[i] = 0; + channels[i] = 0; + } +} + + +CV_CalcBackProjectPatchTest::~CV_CalcBackProjectPatchTest() +{ + clear(); +} + + +void CV_CalcBackProjectPatchTest::clear() +{ + int i; + + for( i = 0; i < CV_MAX_DIM+2; i++ ) + cvReleaseImage( &images[i] ); + + CV_BaseHistTest::clear(); +} + + +int CV_CalcBackProjectPatchTest::prepare_test_case( int test_case_idx ) +{ + int code = CV_BaseHistTest::prepare_test_case( test_case_idx ); + + if( code > 0 ) + { + RNG& rng = ts->get_rng(); + int i, j, n, img_len = img_size.width*img_size.height; + + patch_size.width = cvtest::randInt(rng) % img_size.width + 1; + patch_size.height = cvtest::randInt(rng) % img_size.height + 1; + patch_size.width = MIN( patch_size.width, 30 ); + patch_size.height = MIN( patch_size.height, 30 ); + + factor = 1.; + method = cvtest::randInt(rng) % CV_CompareHistTest::MAX_METHOD; + + for( i = 0; i < CV_MAX_DIM + 2; i++ ) + { + if( i < cdims ) + { + int nch = 1; //cvtest::randInt(rng) % 3 + 1; + images[i] = cvCreateImage( img_size, + img_type == CV_8U ? IPL_DEPTH_8U : IPL_DEPTH_32F, nch ); + channels[i] = cvtest::randInt(rng) % nch; + + Mat images_i = cvarrToMat(images[i]); + cvtest::randUni( rng, images_i, Scalar::all(low), Scalar::all(high) ); + } + else if( i >= CV_MAX_DIM ) + { + images[i] = cvCreateImage( + cvSize(img_size.width - patch_size.width + 1, + img_size.height - patch_size.height + 1), + IPL_DEPTH_32F, 1 ); + } + } + + cvTsCalcHist( images, hist[0], 0, channels ); + cvNormalizeHist( hist[0], factor ); + + // now modify the images a bit + n = cvtest::randInt(rng) % (img_len/10+1); + for( i = 0; i < cdims; i++ ) + { + char* data = images[i]->imageData; + for( j = 0; j < n; j++ ) + { + int idx = cvtest::randInt(rng) % img_len; + double val = cvtest::randReal(rng)*(high - low) + low; + + if( img_type == CV_8U ) + ((uchar*)data)[idx] = (uchar)cvRound(val); + else + ((float*)data)[idx] = (float)val; + } + } + } + + return code; +} + + +void CV_CalcBackProjectPatchTest::run_func(void) +{ + cvCalcBackProjectPatch( images, images[CV_MAX_DIM], patch_size, hist[0], method, factor ); +} + + +static void +cvTsCalcBackProjectPatch( IplImage** images, IplImage* dst, CvSize patch_size, + CvHistogram* hist, int method, + double factor, int* channels ) +{ + CvHistogram* model = 0; + + IplImage imgstub[CV_MAX_DIM], *img[CV_MAX_DIM]; + IplROI roi; + int i, dims; + int x, y; + CvSize size = cvGetSize(dst); + + dims = cvGetDims( hist->bins ); + cvCopyHist( hist, &model ); + cvNormalizeHist( hist, factor ); + cvZero( dst ); + + for( i = 0; i < dims; i++ ) + { + CvMat stub, *mat; + mat = cvGetMat( images[i], &stub, 0, 0 ); + img[i] = cvGetImage( mat, &imgstub[i] ); + img[i]->roi = &roi; + } + + roi.coi = 0; + + for( y = 0; y < size.height; y++ ) + { + for( x = 0; x < size.width; x++ ) + { + double result; + + roi.xOffset = x; + roi.yOffset = y; + roi.width = patch_size.width; + roi.height = patch_size.height; + + cvTsCalcHist( img, model, 0, channels ); + cvNormalizeHist( model, factor ); + result = cvCompareHist( model, hist, method ); + CV_IMAGE_ELEM( dst, float, y, x ) = (float)result; + } + } + + cvReleaseHist( &model ); +} + + +int CV_CalcBackProjectPatchTest::validate_test_results( int /*test_case_idx*/ ) +{ + int code = cvtest::TS::OK; + double err_level = 5e-3; + + cvTsCalcBackProjectPatch( images, images[CV_MAX_DIM+1], + patch_size, hist[0], method, factor, channels ); + + Mat a = cvarrToMat(images[CV_MAX_DIM]), b = cvarrToMat(images[CV_MAX_DIM+1]); + code = cvtest::cmpEps2( ts, a, b, err_level, true, "BackProjectPatch result" ); + + if( code < 0 ) + ts->set_failed_test_info( code ); + + return code; +} + + +////////////// cvCalcBayesianProb ////////////// + +class CV_BayesianProbTest : public CV_BaseHistTest +{ +public: + enum { MAX_METHOD = 4 }; + + CV_BayesianProbTest(); +protected: + int prepare_test_case( int test_case_idx ); + void run_func(void); + int validate_test_results( int test_case_idx ); + void init_hist( int test_case_idx, int i ); + void get_hist_params( int test_case_idx ); +}; + + + +CV_BayesianProbTest::CV_BayesianProbTest() +{ + hist_count = CV_MAX_DIM; + gen_random_hist = 1; +} + + +void CV_BayesianProbTest::get_hist_params( int test_case_idx ) +{ + CV_BaseHistTest::get_hist_params( test_case_idx ); + hist_type = CV_HIST_ARRAY; +} + + +void CV_BayesianProbTest::init_hist( int test_case_idx, int hist_i ) +{ + if( hist_i < hist_count/2 ) + CV_BaseHistTest::init_hist( test_case_idx, hist_i ); +} + + +int CV_BayesianProbTest::prepare_test_case( int test_case_idx ) +{ + RNG& rng = ts->get_rng(); + + hist_count = (cvtest::randInt(rng) % (MAX_HIST/2-1) + 2)*2; + hist_count = MIN( hist_count, MAX_HIST ); + int code = CV_BaseHistTest::prepare_test_case( test_case_idx ); + + return code; +} + + +void CV_BayesianProbTest::run_func(void) +{ + cvCalcBayesianProb( &hist[0], hist_count/2, &hist[hist_count/2] ); +} + + +int CV_BayesianProbTest::validate_test_results( int /*test_case_idx*/ ) +{ + int code = cvtest::TS::OK; + int i, j, n = hist_count/2; + double s[CV_MAX_DIM]; + const double err_level = 1e-5; + + for( i = 0; i < total_size; i++ ) + { + double sum = 0; + for( j = 0; j < n; j++ ) + { + double v = hist[j]->mat.data.fl[i]; + sum += v; + s[j] = v; + } + sum = sum > DBL_EPSILON ? 1./sum : 0; + + for( j = 0; j < n; j++ ) + { + double v0 = s[j]*sum; + double v = hist[j+n]->mat.data.fl[i]; + + if( cvIsNaN(v) || cvIsInf(v) ) + { + ts->printf( cvtest::TS::LOG, + "The element #%d in the destination histogram #%d is invalid (=%g)\n", + i, j, v ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + break; + } + else if( fabs(v0 - v) > err_level*fabs(v0) ) + { + ts->printf( cvtest::TS::LOG, + "The element #%d in the destination histogram #%d is inaccurate (=%g, should be =%g)\n", + i, j, v, v0 ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + break; + } + } + if( j < n ) + break; + } + + if( code < 0 ) + ts->set_failed_test_info( code ); + return code; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// + +TEST(Imgproc_Hist_Calc, accuracy) { CV_CalcHistTest test; test.safe_run(); } +TEST(Imgproc_Hist_Query, accuracy) { CV_QueryHistTest test; test.safe_run(); } + +TEST(Imgproc_Hist_Compare, accuracy) { CV_CompareHistTest test; test.safe_run(); } +TEST(Imgproc_Hist_Threshold, accuracy) { CV_ThreshHistTest test; test.safe_run(); } +TEST(Imgproc_Hist_Normalize, accuracy) { CV_NormHistTest test; test.safe_run(); } +TEST(Imgproc_Hist_MinMaxVal, accuracy) { CV_MinMaxHistTest test; test.safe_run(); } + +TEST(Imgproc_Hist_CalcBackProject, accuracy) { CV_CalcBackProjectTest test; test.safe_run(); } +TEST(Imgproc_Hist_CalcBackProjectPatch, accuracy) { CV_CalcBackProjectPatchTest test; test.safe_run(); } +TEST(Imgproc_Hist_BayesianProb, accuracy) { CV_BayesianProbTest test; test.safe_run(); } + +/* End Of File */ diff --git a/modules/imgproc/test/test_imgwarp.cpp b/modules/imgproc/test/test_imgwarp.cpp new file mode 100644 index 000000000..6bd1b8615 --- /dev/null +++ b/modules/imgproc/test/test_imgwarp.cpp @@ -0,0 +1,1387 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_ImgWarpBaseTest : public cvtest::ArrayTest +{ +public: + CV_ImgWarpBaseTest( bool warp_matrix ); + +protected: + int read_params( CvFileStorage* fs ); + int prepare_test_case( int test_case_idx ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); + void fill_array( int test_case_idx, int i, int j, Mat& arr ); + + int interpolation; + int max_interpolation; + double spatial_scale_zoom, spatial_scale_decimate; +}; + + +CV_ImgWarpBaseTest::CV_ImgWarpBaseTest( bool warp_matrix ) +{ + test_array[INPUT].push_back(NULL); + if( warp_matrix ) + test_array[INPUT].push_back(NULL); + test_array[INPUT_OUTPUT].push_back(NULL); + test_array[REF_INPUT_OUTPUT].push_back(NULL); + max_interpolation = 5; + interpolation = 0; + element_wise_relative_error = false; + spatial_scale_zoom = 0.01; + spatial_scale_decimate = 0.005; +} + + +int CV_ImgWarpBaseTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::ArrayTest::read_params( fs ); + return code; +} + + +void CV_ImgWarpBaseTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) +{ + cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high ); + if( CV_MAT_DEPTH(type) == CV_32F ) + { + low = Scalar::all(-10.); + high = Scalar::all(10); + } +} + + +void CV_ImgWarpBaseTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int depth = cvtest::randInt(rng) % 3; + int cn = cvtest::randInt(rng) % 3 + 1; + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + depth = depth == 0 ? CV_8U : depth == 1 ? CV_16U : CV_32F; + cn += cn == 2; + + types[INPUT][0] = types[INPUT_OUTPUT][0] = types[REF_INPUT_OUTPUT][0] = CV_MAKETYPE(depth, cn); + if( test_array[INPUT].size() > 1 ) + types[INPUT][1] = cvtest::randInt(rng) & 1 ? CV_32FC1 : CV_64FC1; + + interpolation = cvtest::randInt(rng) % max_interpolation; +} + + +void CV_ImgWarpBaseTest::fill_array( int test_case_idx, int i, int j, Mat& arr ) +{ + if( i != INPUT || j != 0 ) + cvtest::ArrayTest::fill_array( test_case_idx, i, j, arr ); +} + +int CV_ImgWarpBaseTest::prepare_test_case( int test_case_idx ) +{ + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + Mat& img = test_mat[INPUT][0]; + int i, j, cols = img.cols; + int type = img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); + double scale = depth == CV_16U ? 1000. : 255.*0.5; + double space_scale = spatial_scale_decimate; + vector buffer(img.cols*cn); + + if( code <= 0 ) + return code; + + if( test_mat[INPUT_OUTPUT][0].cols >= img.cols && + test_mat[INPUT_OUTPUT][0].rows >= img.rows ) + space_scale = spatial_scale_zoom; + + for( i = 0; i < img.rows; i++ ) + { + uchar* ptr = img.ptr(i); + switch( cn ) + { + case 1: + for( j = 0; j < cols; j++ ) + buffer[j] = (float)((sin((i+1)*space_scale)*sin((j+1)*space_scale)+1.)*scale); + break; + case 2: + for( j = 0; j < cols; j++ ) + { + buffer[j*2] = (float)((sin((i+1)*space_scale)+1.)*scale); + buffer[j*2+1] = (float)((sin((i+j)*space_scale)+1.)*scale); + } + break; + case 3: + for( j = 0; j < cols; j++ ) + { + buffer[j*3] = (float)((sin((i+1)*space_scale)+1.)*scale); + buffer[j*3+1] = (float)((sin(j*space_scale)+1.)*scale); + buffer[j*3+2] = (float)((sin((i+j)*space_scale)+1.)*scale); + } + break; + case 4: + for( j = 0; j < cols; j++ ) + { + buffer[j*4] = (float)((sin((i+1)*space_scale)+1.)*scale); + buffer[j*4+1] = (float)((sin(j*space_scale)+1.)*scale); + buffer[j*4+2] = (float)((sin((i+j)*space_scale)+1.)*scale); + buffer[j*4+3] = (float)((sin((i-j)*space_scale)+1.)*scale); + } + break; + default: + assert(0); + } + + /*switch( depth ) + { + case CV_8U: + for( j = 0; j < cols*cn; j++ ) + ptr[j] = (uchar)cvRound(buffer[j]); + break; + case CV_16U: + for( j = 0; j < cols*cn; j++ ) + ((ushort*)ptr)[j] = (ushort)cvRound(buffer[j]); + break; + case CV_32F: + for( j = 0; j < cols*cn; j++ ) + ((float*)ptr)[j] = (float)buffer[j]; + break; + default: + assert(0); + }*/ + cv::Mat src(1, cols*cn, CV_32F, &buffer[0]); + cv::Mat dst(1, cols*cn, depth, ptr); + src.convertTo(dst, dst.type()); + } + + return code; +} + + +///////////////////////// + +class CV_ResizeTest : public CV_ImgWarpBaseTest +{ +public: + CV_ResizeTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void run_func(); + void prepare_to_validation( int /*test_case_idx*/ ); + double get_success_error_level( int test_case_idx, int i, int j ); +}; + + +CV_ResizeTest::CV_ResizeTest() : CV_ImgWarpBaseTest( false ) +{ +} + + +void CV_ResizeTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + CV_ImgWarpBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + CvSize sz; + + sz.width = (cvtest::randInt(rng) % sizes[INPUT][0].width) + 1; + sz.height = (cvtest::randInt(rng) % sizes[INPUT][0].height) + 1; + + if( cvtest::randInt(rng) & 1 ) + { + int xfactor = cvtest::randInt(rng) % 10 + 1; + int yfactor = cvtest::randInt(rng) % 10 + 1; + + if( cvtest::randInt(rng) & 1 ) + yfactor = xfactor; + + sz.width = sizes[INPUT][0].width / xfactor; + sz.width = MAX(sz.width,1); + sz.height = sizes[INPUT][0].height / yfactor; + sz.height = MAX(sz.height,1); + sizes[INPUT][0].width = sz.width * xfactor; + sizes[INPUT][0].height = sz.height * yfactor; + } + + if( cvtest::randInt(rng) & 1 ) + sizes[INPUT_OUTPUT][0] = sizes[REF_INPUT_OUTPUT][0] = sz; + else + { + sizes[INPUT_OUTPUT][0] = sizes[REF_INPUT_OUTPUT][0] = sizes[INPUT][0]; + sizes[INPUT][0] = sz; + } + if( interpolation == 4 && + (MIN(sizes[INPUT][0].width,sizes[INPUT_OUTPUT][0].width) < 4 || + MIN(sizes[INPUT][0].height,sizes[INPUT_OUTPUT][0].height) < 4)) + interpolation = 2; +} + + +void CV_ResizeTest::run_func() +{ + cvResize( test_array[INPUT][0], test_array[INPUT_OUTPUT][0], interpolation ); +} + + +double CV_ResizeTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth == CV_8U ? 16 : depth == CV_16U ? 1024 : 1e-1; +} + + +void CV_ResizeTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + CvMat _src = test_mat[INPUT][0], _dst = test_mat[REF_INPUT_OUTPUT][0]; + CvMat *src = &_src, *dst = &_dst; + int i, j, k; + CvMat* x_idx = cvCreateMat( 1, dst->cols, CV_32SC1 ); + CvMat* y_idx = cvCreateMat( 1, dst->rows, CV_32SC1 ); + int* x_tab = x_idx->data.i; + int elem_size = CV_ELEM_SIZE(src->type); + int drows = dst->rows, dcols = dst->cols; + + if( interpolation == CV_INTER_NN ) + { + for( j = 0; j < dcols; j++ ) + { + int t = (j*src->cols*2 + MIN(src->cols,dcols) - 1)/(dcols*2); + t -= t >= src->cols; + x_idx->data.i[j] = t*elem_size; + } + + for( j = 0; j < drows; j++ ) + { + int t = (j*src->rows*2 + MIN(src->rows,drows) - 1)/(drows*2); + t -= t >= src->rows; + y_idx->data.i[j] = t; + } + } + else + { + double scale_x = (double)src->cols/dcols; + double scale_y = (double)src->rows/drows; + + for( j = 0; j < dcols; j++ ) + { + double f = ((j+0.5)*scale_x - 0.5); + i = cvRound(f); + x_idx->data.i[j] = (i < 0 ? 0 : i >= src->cols ? src->cols - 1 : i)*elem_size; + } + + for( j = 0; j < drows; j++ ) + { + double f = ((j+0.5)*scale_y - 0.5); + i = cvRound(f); + y_idx->data.i[j] = i < 0 ? 0 : i >= src->rows ? src->rows - 1 : i; + } + } + + for( i = 0; i < drows; i++ ) + { + uchar* dptr = dst->data.ptr + dst->step*i; + const uchar* sptr0 = src->data.ptr + src->step*y_idx->data.i[i]; + + for( j = 0; j < dcols; j++, dptr += elem_size ) + { + const uchar* sptr = sptr0 + x_tab[j]; + for( k = 0; k < elem_size; k++ ) + dptr[k] = sptr[k]; + } + } + + cvReleaseMat( &x_idx ); + cvReleaseMat( &y_idx ); +} + + +///////////////////////// + +static void test_remap( const Mat& src, Mat& dst, const Mat& mapx, const Mat& mapy, + Mat* mask=0, int interpolation=CV_INTER_LINEAR ) +{ + int x, y, k; + int drows = dst.rows, dcols = dst.cols; + int srows = src.rows, scols = src.cols; + uchar* sptr0 = src.data; + int depth = src.depth(), cn = src.channels(); + int elem_size = (int)src.elemSize(); + int step = src.step / CV_ELEM_SIZE(depth); + int delta; + + if( interpolation != CV_INTER_CUBIC ) + { + delta = 0; + scols -= 1; srows -= 1; + } + else + { + delta = 1; + scols = MAX(scols - 3, 0); + srows = MAX(srows - 3, 0); + } + + int scols1 = MAX(scols - 2, 0); + int srows1 = MAX(srows - 2, 0); + + if( mask ) + *mask = Scalar::all(0); + + for( y = 0; y < drows; y++ ) + { + uchar* dptr = dst.ptr(y); + const float* mx = mapx.ptr(y); + const float* my = mapy.ptr(y); + uchar* m = mask ? mask->ptr(y) : 0; + + for( x = 0; x < dcols; x++, dptr += elem_size ) + { + float xs = mx[x]; + float ys = my[x]; + int ixs = cvFloor(xs); + int iys = cvFloor(ys); + + if( (unsigned)(ixs - delta - 1) >= (unsigned)scols1 || + (unsigned)(iys - delta - 1) >= (unsigned)srows1 ) + { + if( m ) + m[x] = 1; + if( (unsigned)(ixs - delta) >= (unsigned)scols || + (unsigned)(iys - delta) >= (unsigned)srows ) + continue; + } + + xs -= ixs; + ys -= iys; + + switch( depth ) + { + case CV_8U: + { + const uchar* sptr = sptr0 + iys*step + ixs*cn; + for( k = 0; k < cn; k++ ) + { + float v00 = sptr[k]; + float v01 = sptr[cn + k]; + float v10 = sptr[step + k]; + float v11 = sptr[step + cn + k]; + + v00 = v00 + xs*(v01 - v00); + v10 = v10 + xs*(v11 - v10); + v00 = v00 + ys*(v10 - v00); + dptr[k] = (uchar)cvRound(v00); + } + } + break; + case CV_16U: + { + const ushort* sptr = (const ushort*)sptr0 + iys*step + ixs*cn; + for( k = 0; k < cn; k++ ) + { + float v00 = sptr[k]; + float v01 = sptr[cn + k]; + float v10 = sptr[step + k]; + float v11 = sptr[step + cn + k]; + + v00 = v00 + xs*(v01 - v00); + v10 = v10 + xs*(v11 - v10); + v00 = v00 + ys*(v10 - v00); + ((ushort*)dptr)[k] = (ushort)cvRound(v00); + } + } + break; + case CV_32F: + { + const float* sptr = (const float*)sptr0 + iys*step + ixs*cn; + for( k = 0; k < cn; k++ ) + { + float v00 = sptr[k]; + float v01 = sptr[cn + k]; + float v10 = sptr[step + k]; + float v11 = sptr[step + cn + k]; + + v00 = v00 + xs*(v01 - v00); + v10 = v10 + xs*(v11 - v10); + v00 = v00 + ys*(v10 - v00); + ((float*)dptr)[k] = (float)v00; + } + } + break; + default: + assert(0); + } + } + } +} + +///////////////////////// + +class CV_WarpAffineTest : public CV_ImgWarpBaseTest +{ +public: + CV_WarpAffineTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void run_func(); + int prepare_test_case( int test_case_idx ); + void prepare_to_validation( int /*test_case_idx*/ ); + double get_success_error_level( int test_case_idx, int i, int j ); +}; + + +CV_WarpAffineTest::CV_WarpAffineTest() : CV_ImgWarpBaseTest( true ) +{ + //spatial_scale_zoom = spatial_scale_decimate; + spatial_scale_decimate = spatial_scale_zoom; +} + + +void CV_WarpAffineTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + CV_ImgWarpBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + CvSize sz = sizes[INPUT][0]; + // run for the second time to get output of a different size + CV_ImgWarpBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + sizes[INPUT][0] = sz; + sizes[INPUT][1] = cvSize( 3, 2 ); +} + + +void CV_WarpAffineTest::run_func() +{ + CvMat mtx = test_mat[INPUT][1]; + cvWarpAffine( test_array[INPUT][0], test_array[INPUT_OUTPUT][0], &mtx, interpolation ); +} + + +double CV_WarpAffineTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth == CV_8U ? 16 : depth == CV_16U ? 1024 : 5e-2; +} + + +int CV_WarpAffineTest::prepare_test_case( int test_case_idx ) +{ + RNG& rng = ts->get_rng(); + int code = CV_ImgWarpBaseTest::prepare_test_case( test_case_idx ); + const Mat& src = test_mat[INPUT][0]; + const Mat& dst = test_mat[INPUT_OUTPUT][0]; + Mat& mat = test_mat[INPUT][1]; + CvPoint2D32f center; + double scale, angle; + + if( code <= 0 ) + return code; + + double buf[6]; + Mat tmp( 2, 3, mat.type(), buf ); + + center.x = (float)((cvtest::randReal(rng)*1.2 - 0.1)*src.cols); + center.y = (float)((cvtest::randReal(rng)*1.2 - 0.1)*src.rows); + angle = cvtest::randReal(rng)*360; + scale = ((double)dst.rows/src.rows + (double)dst.cols/src.cols)*0.5; + getRotationMatrix2D(center, angle, scale).convertTo(mat, mat.depth()); + rng.fill( tmp, CV_RAND_NORMAL, cvScalarAll(1.), cvScalarAll(0.01) ); + cv::max(tmp, 0.9, tmp); + cv::min(tmp, 1.1, tmp); + cv::multiply(tmp, mat, mat, 1.); + + return code; +} + + +void CV_WarpAffineTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + const Mat& src = test_mat[INPUT][0]; + Mat& dst = test_mat[REF_INPUT_OUTPUT][0]; + Mat& dst0 = test_mat[INPUT_OUTPUT][0]; + Mat mapx(dst.size(), CV_32F), mapy(dst.size(), CV_32F); + double m[6]; + Mat srcAb, dstAb( 2, 3, CV_64FC1, m ); + + //cvInvert( &tM, &M, CV_LU ); + // [R|t] -> [R^-1 | -(R^-1)*t] + test_mat[INPUT][1].convertTo( srcAb, CV_64F ); + Mat A = srcAb.colRange(0, 2); + Mat b = srcAb.col(2); + Mat invA = dstAb.colRange(0, 2); + Mat invAb = dstAb.col(2); + cv::invert(A, invA, CV_SVD); + cv::gemm(invA, b, -1, Mat(), 0, invAb); + + for( int y = 0; y < dst.rows; y++ ) + for( int x = 0; x < dst.cols; x++ ) + { + mapx.at(y, x) = (float)(x*m[0] + y*m[1] + m[2]); + mapy.at(y, x) = (float)(x*m[3] + y*m[4] + m[5]); + } + + Mat mask( dst.size(), CV_8U ); + test_remap( src, dst, mapx, mapy, &mask ); + dst.setTo(Scalar::all(0), mask); + dst0.setTo(Scalar::all(0), mask); +} + + +///////////////////////// + +class CV_WarpPerspectiveTest : public CV_ImgWarpBaseTest +{ +public: + CV_WarpPerspectiveTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void run_func(); + int prepare_test_case( int test_case_idx ); + void prepare_to_validation( int /*test_case_idx*/ ); + double get_success_error_level( int test_case_idx, int i, int j ); +}; + + +CV_WarpPerspectiveTest::CV_WarpPerspectiveTest() : CV_ImgWarpBaseTest( true ) +{ + //spatial_scale_zoom = spatial_scale_decimate; + spatial_scale_decimate = spatial_scale_zoom; +} + + +void CV_WarpPerspectiveTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + CV_ImgWarpBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + CvSize sz = sizes[INPUT][0]; + // run for the second time to get output of a different size + CV_ImgWarpBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + sizes[INPUT][0] = sz; + sizes[INPUT][1] = cvSize( 3, 3 ); +} + + +void CV_WarpPerspectiveTest::run_func() +{ + CvMat mtx = test_mat[INPUT][1]; + cvWarpPerspective( test_array[INPUT][0], test_array[INPUT_OUTPUT][0], &mtx, interpolation ); +} + + +double CV_WarpPerspectiveTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth == CV_8U ? 16 : depth == CV_16U ? 1024 : 5e-2; +} + + +int CV_WarpPerspectiveTest::prepare_test_case( int test_case_idx ) +{ + RNG& rng = ts->get_rng(); + int code = CV_ImgWarpBaseTest::prepare_test_case( test_case_idx ); + const CvMat& src = test_mat[INPUT][0]; + const CvMat& dst = test_mat[INPUT_OUTPUT][0]; + Mat& mat = test_mat[INPUT][1]; + Point2f s[4], d[4]; + int i; + + if( code <= 0 ) + return code; + + s[0] = Point2f(0,0); + d[0] = Point2f(0,0); + s[1] = Point2f(src.cols-1,0); + d[1] = Point2f(dst.cols-1,0); + s[2] = Point2f(src.cols-1,src.rows-1); + d[2] = Point2f(dst.cols-1,dst.rows-1); + s[3] = Point2f(0,src.rows-1); + d[3] = Point2f(0,dst.rows-1); + + float buf[16]; + Mat tmp( 1, 16, CV_32FC1, buf ); + + rng.fill( tmp, CV_RAND_NORMAL, cvScalarAll(0.), cvScalarAll(0.1) ); + + for( i = 0; i < 4; i++ ) + { + s[i].x += buf[i*4]*src.cols/2; + s[i].y += buf[i*4+1]*src.rows/2; + d[i].x += buf[i*4+2]*dst.cols/2; + d[i].y += buf[i*4+3]*dst.rows/2; + } + + cv::getPerspectiveTransform( s, d ).convertTo( mat, mat.depth() ); + return code; +} + + +void CV_WarpPerspectiveTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + Mat& src = test_mat[INPUT][0]; + Mat& dst = test_mat[REF_INPUT_OUTPUT][0]; + Mat& dst0 = test_mat[INPUT_OUTPUT][0]; + Mat mapx(dst.size(), CV_32F), mapy(dst.size(), CV_32F); + double m[9]; + Mat srcM, dstM(3, 3, CV_64F, m); + + //cvInvert( &tM, &M, CV_LU ); + // [R|t] -> [R^-1 | -(R^-1)*t] + test_mat[INPUT][1].convertTo( srcM, CV_64F ); + cv::invert(srcM, dstM, CV_SVD); + + for( int y = 0; y < dst.rows; y++ ) + { + for( int x = 0; x < dst.cols; x++ ) + { + double xs = x*m[0] + y*m[1] + m[2]; + double ys = x*m[3] + y*m[4] + m[5]; + double ds = x*m[6] + y*m[7] + m[8]; + + ds = ds ? 1./ds : 0; + xs *= ds; + ys *= ds; + + mapx.at(y, x) = (float)xs; + mapy.at(y, x) = (float)ys; + } + } + + Mat mask( dst.size(), CV_8U ); + test_remap( src, dst, mapx, mapy, &mask ); + dst.setTo(Scalar::all(0), mask); + dst0.setTo(Scalar::all(0), mask); +} + + +///////////////////////// + +class CV_RemapTest : public CV_ImgWarpBaseTest +{ +public: + CV_RemapTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void run_func(); + int prepare_test_case( int test_case_idx ); + void prepare_to_validation( int /*test_case_idx*/ ); + double get_success_error_level( int test_case_idx, int i, int j ); + void fill_array( int test_case_idx, int i, int j, Mat& arr ); +}; + + +CV_RemapTest::CV_RemapTest() : CV_ImgWarpBaseTest( false ) +{ + //spatial_scale_zoom = spatial_scale_decimate; + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + + spatial_scale_decimate = spatial_scale_zoom; +} + + +void CV_RemapTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + CV_ImgWarpBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + types[INPUT][1] = types[INPUT][2] = CV_32FC1; + interpolation = CV_INTER_LINEAR; +} + + +void CV_RemapTest::fill_array( int test_case_idx, int i, int j, Mat& arr ) +{ + if( i != INPUT ) + CV_ImgWarpBaseTest::fill_array( test_case_idx, i, j, arr ); +} + + +void CV_RemapTest::run_func() +{ + cvRemap( test_array[INPUT][0], test_array[INPUT_OUTPUT][0], + test_array[INPUT][1], test_array[INPUT][2], interpolation ); +} + + +double CV_RemapTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth == CV_8U ? 16 : depth == CV_16U ? 1024 : 5e-2; +} + + +int CV_RemapTest::prepare_test_case( int test_case_idx ) +{ + RNG& rng = ts->get_rng(); + int code = CV_ImgWarpBaseTest::prepare_test_case( test_case_idx ); + const Mat& src = test_mat[INPUT][0]; + double a[9] = {0,0,0,0,0,0,0,0,1}, k[4]; + Mat _a( 3, 3, CV_64F, a ); + Mat _k( 4, 1, CV_64F, k ); + double sz = MAX(src.rows, src.cols); + + if( code <= 0 ) + return code; + + double aspect_ratio = cvtest::randReal(rng)*0.6 + 0.7; + a[2] = (src.cols - 1)*0.5 + cvtest::randReal(rng)*10 - 5; + a[5] = (src.rows - 1)*0.5 + cvtest::randReal(rng)*10 - 5; + a[0] = sz/(0.9 - cvtest::randReal(rng)*0.6); + a[4] = aspect_ratio*a[0]; + k[0] = cvtest::randReal(rng)*0.06 - 0.03; + k[1] = cvtest::randReal(rng)*0.06 - 0.03; + if( k[0]*k[1] > 0 ) + k[1] = -k[1]; + k[2] = cvtest::randReal(rng)*0.004 - 0.002; + k[3] = cvtest::randReal(rng)*0.004 - 0.002; + + cvtest::initUndistortMap( _a, _k, test_mat[INPUT][1].size(), test_mat[INPUT][1], test_mat[INPUT][2] ); + return code; +} + + +void CV_RemapTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + Mat& dst = test_mat[REF_INPUT_OUTPUT][0]; + Mat& dst0 = test_mat[INPUT_OUTPUT][0]; + Mat mask( dst.size(), CV_8U ); + test_remap(test_mat[INPUT][0], dst, test_mat[INPUT][1], + test_mat[INPUT][2], &mask, interpolation ); + dst.setTo(Scalar::all(0), mask); + dst0.setTo(Scalar::all(0), mask); +} + + +////////////////////////////// undistort ///////////////////////////////// + +class CV_UndistortTest : public CV_ImgWarpBaseTest +{ +public: + CV_UndistortTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void run_func(); + int prepare_test_case( int test_case_idx ); + void prepare_to_validation( int /*test_case_idx*/ ); + double get_success_error_level( int test_case_idx, int i, int j ); + void fill_array( int test_case_idx, int i, int j, Mat& arr ); + +private: + bool useCPlus; + cv::Mat input0; + cv::Mat input1; + cv::Mat input2; + cv::Mat input_new_cam; + cv::Mat input_output; + + bool zero_new_cam; + bool zero_distortion; +}; + + +CV_UndistortTest::CV_UndistortTest() : CV_ImgWarpBaseTest( false ) +{ + //spatial_scale_zoom = spatial_scale_decimate; + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + + spatial_scale_decimate = spatial_scale_zoom; +} + + +void CV_UndistortTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + CV_ImgWarpBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int type = types[INPUT][0]; + type = CV_MAKETYPE( CV_8U, CV_MAT_CN(type) ); + types[INPUT][0] = types[INPUT_OUTPUT][0] = types[REF_INPUT_OUTPUT][0] = type; + types[INPUT][1] = cvtest::randInt(rng)%2 ? CV_64F : CV_32F; + types[INPUT][2] = cvtest::randInt(rng)%2 ? CV_64F : CV_32F; + sizes[INPUT][1] = cvSize(3,3); + sizes[INPUT][2] = cvtest::randInt(rng)%2 ? cvSize(4,1) : cvSize(1,4); + types[INPUT][3] = types[INPUT][1]; + sizes[INPUT][3] = sizes[INPUT][1]; + interpolation = CV_INTER_LINEAR; +} + + +void CV_UndistortTest::fill_array( int test_case_idx, int i, int j, Mat& arr ) +{ + if( i != INPUT ) + CV_ImgWarpBaseTest::fill_array( test_case_idx, i, j, arr ); +} + + +void CV_UndistortTest::run_func() +{ + if (!useCPlus) + { + CvMat a = test_mat[INPUT][1], k = test_mat[INPUT][2]; + cvUndistort2( test_array[INPUT][0], test_array[INPUT_OUTPUT][0], &a, &k); + } + else + { + if (zero_distortion) + { + cv::undistort(input0,input_output,input1,cv::Mat()); + } + else + { + cv::undistort(input0,input_output,input1,input2); + } + } +} + + +double CV_UndistortTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth == CV_8U ? 16 : depth == CV_16U ? 1024 : 5e-2; +} + + +int CV_UndistortTest::prepare_test_case( int test_case_idx ) +{ + RNG& rng = ts->get_rng(); + int code = CV_ImgWarpBaseTest::prepare_test_case( test_case_idx ); + + const Mat& src = test_mat[INPUT][0]; + double k[4], a[9] = {0,0,0,0,0,0,0,0,1}; + double new_cam[9] = {0,0,0,0,0,0,0,0,1}; + double sz = MAX(src.rows, src.cols); + + Mat& _new_cam0 = test_mat[INPUT][3]; + Mat _new_cam(test_mat[INPUT][3].rows,test_mat[INPUT][3].cols,CV_64F,new_cam); + Mat& _a0 = test_mat[INPUT][1]; + Mat _a(3,3,CV_64F,a); + Mat& _k0 = test_mat[INPUT][2]; + Mat _k(_k0.rows,_k0.cols, CV_MAKETYPE(CV_64F,_k0.channels()),k); + + if( code <= 0 ) + return code; + + double aspect_ratio = cvtest::randReal(rng)*0.6 + 0.7; + a[2] = (src.cols - 1)*0.5 + cvtest::randReal(rng)*10 - 5; + a[5] = (src.rows - 1)*0.5 + cvtest::randReal(rng)*10 - 5; + a[0] = sz/(0.9 - cvtest::randReal(rng)*0.6); + a[4] = aspect_ratio*a[0]; + k[0] = cvtest::randReal(rng)*0.06 - 0.03; + k[1] = cvtest::randReal(rng)*0.06 - 0.03; + if( k[0]*k[1] > 0 ) + k[1] = -k[1]; + if( cvtest::randInt(rng)%4 != 0 ) + { + k[2] = cvtest::randReal(rng)*0.004 - 0.002; + k[3] = cvtest::randReal(rng)*0.004 - 0.002; + } + else + k[2] = k[3] = 0; + + new_cam[0] = a[0] + (cvtest::randReal(rng) - (double)0.5)*0.2*a[0]; //10% + new_cam[4] = a[4] + (cvtest::randReal(rng) - (double)0.5)*0.2*a[4]; //10% + new_cam[2] = a[2] + (cvtest::randReal(rng) - (double)0.5)*0.3*test_mat[INPUT][0].rows; //15% + new_cam[5] = a[5] + (cvtest::randReal(rng) - (double)0.5)*0.3*test_mat[INPUT][0].cols; //15% + + _a.convertTo(_a0, _a0.depth()); + + zero_distortion = (cvtest::randInt(rng)%2) == 0 ? false : true; + _k.convertTo(_k0, _k0.depth()); + + zero_new_cam = (cvtest::randInt(rng)%2) == 0 ? false : true; + _new_cam.convertTo(_new_cam0, _new_cam0.depth()); + + //Testing C++ code + useCPlus = ((cvtest::randInt(rng) % 2)!=0); + if (useCPlus) + { + input0 = test_mat[INPUT][0]; + input1 = test_mat[INPUT][1]; + input2 = test_mat[INPUT][2]; + input_new_cam = test_mat[INPUT][3]; + } + + return code; +} + + +void CV_UndistortTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + if (useCPlus) + { + Mat& output = test_mat[INPUT_OUTPUT][0]; + input_output.convertTo(output, output.type()); + } + Mat& src = test_mat[INPUT][0]; + Mat& dst = test_mat[REF_INPUT_OUTPUT][0]; + Mat& dst0 = test_mat[INPUT_OUTPUT][0]; + Mat mapx, mapy; + cvtest::initUndistortMap( test_mat[INPUT][1], test_mat[INPUT][2], dst.size(), mapx, mapy ); + Mat mask( dst.size(), CV_8U ); + test_remap( src, dst, mapx, mapy, &mask, interpolation ); + dst.setTo(Scalar::all(0), mask); + dst0.setTo(Scalar::all(0), mask); +} + + +class CV_UndistortMapTest : public cvtest::ArrayTest +{ +public: + CV_UndistortMapTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void run_func(); + int prepare_test_case( int test_case_idx ); + void prepare_to_validation( int /*test_case_idx*/ ); + double get_success_error_level( int test_case_idx, int i, int j ); + void fill_array( int test_case_idx, int i, int j, Mat& arr ); + +private: + bool dualChannel; +}; + + +CV_UndistortMapTest::CV_UndistortMapTest() +{ + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + + element_wise_relative_error = false; +} + + +void CV_UndistortMapTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int depth = cvtest::randInt(rng)%2 ? CV_64F : CV_32F; + + CvSize sz = sizes[OUTPUT][0]; + types[INPUT][0] = types[INPUT][1] = depth; + dualChannel = cvtest::randInt(rng)%2 == 0; + types[OUTPUT][0] = types[OUTPUT][1] = + types[REF_OUTPUT][0] = types[REF_OUTPUT][1] = dualChannel ? CV_32FC2 : CV_32F; + sizes[INPUT][0] = cvSize(3,3); + sizes[INPUT][1] = cvtest::randInt(rng)%2 ? cvSize(4,1) : cvSize(1,4); + + sz.width = MAX(sz.width,16); + sz.height = MAX(sz.height,16); + sizes[OUTPUT][0] = sizes[OUTPUT][1] = + sizes[REF_OUTPUT][0] = sizes[REF_OUTPUT][1] = sz; +} + + +void CV_UndistortMapTest::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 ); +} + + +void CV_UndistortMapTest::run_func() +{ + CvMat a = test_mat[INPUT][0], k = test_mat[INPUT][1]; + + if (!dualChannel ) + cvInitUndistortMap( &a, &k, test_array[OUTPUT][0], test_array[OUTPUT][1] ); + else + cvInitUndistortMap( &a, &k, test_array[OUTPUT][0], 0 ); +} + + +double CV_UndistortMapTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return 1e-3; +} + + +int CV_UndistortMapTest::prepare_test_case( int test_case_idx ) +{ + RNG& rng = ts->get_rng(); + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + const Mat& mapx = test_mat[OUTPUT][0]; + double k[4], a[9] = {0,0,0,0,0,0,0,0,1}; + double sz = MAX(mapx.rows, mapx.cols); + Mat& _a0 = test_mat[INPUT][0], &_k0 = test_mat[INPUT][1]; + Mat _a(3,3,CV_64F,a); + Mat _k(_k0.rows,_k0.cols, CV_MAKETYPE(CV_64F,_k0.channels()),k); + + if( code <= 0 ) + return code; + + double aspect_ratio = cvtest::randReal(rng)*0.6 + 0.7; + a[2] = (mapx.cols - 1)*0.5 + cvtest::randReal(rng)*10 - 5; + a[5] = (mapx.rows - 1)*0.5 + cvtest::randReal(rng)*10 - 5; + a[0] = sz/(0.9 - cvtest::randReal(rng)*0.6); + a[4] = aspect_ratio*a[0]; + k[0] = cvtest::randReal(rng)*0.06 - 0.03; + k[1] = cvtest::randReal(rng)*0.06 - 0.03; + if( k[0]*k[1] > 0 ) + k[1] = -k[1]; + k[2] = cvtest::randReal(rng)*0.004 - 0.002; + k[3] = cvtest::randReal(rng)*0.004 - 0.002; + + _a.convertTo(_a0, _a0.depth()); + _k.convertTo(_k0, _k0.depth()); + + if (dualChannel) + { + test_mat[REF_OUTPUT][1] = Scalar::all(0); + test_mat[OUTPUT][1] = Scalar::all(0); + } + + return code; +} + + +void CV_UndistortMapTest::prepare_to_validation( int ) +{ + Mat mapx, mapy; + cvtest::initUndistortMap( test_mat[INPUT][0], test_mat[INPUT][1], test_mat[REF_OUTPUT][0].size(), mapx, mapy ); + if( !dualChannel ) + { + mapx.copyTo(test_mat[REF_OUTPUT][0]); + mapy.copyTo(test_mat[REF_OUTPUT][1]); + } + else + { + Mat p[2] = {mapx, mapy}; + cv::merge(p, 2, test_mat[REF_OUTPUT][0]); + } +} + +////////////////////////////// GetRectSubPix ///////////////////////////////// + +static void +test_getQuadrangeSubPix( const Mat& src, Mat& dst, double* a ) +{ + int sstep = src.step / sizeof(float); + int scols = src.cols, srows = src.rows; + + CV_Assert( src.depth() == CV_32F && src.type() == dst.type() ); + + int cn = dst.channels(); + + for( int y = 0; y < dst.rows; y++ ) + for( int x = 0; x < dst.cols; x++ ) + { + float* d = dst.ptr(y) + x*cn; + float sx = (float)(a[0]*x + a[1]*y + a[2]); + float sy = (float)(a[3]*x + a[4]*y + a[5]); + int ix = cvFloor(sx), iy = cvFloor(sy); + int dx = cn, dy = sstep; + const float* s; + sx -= ix; sy -= iy; + + if( (unsigned)ix >= (unsigned)(scols-1) ) + ix = ix < 0 ? 0 : scols - 1, sx = 0, dx = 0; + if( (unsigned)iy >= (unsigned)(srows-1) ) + iy = iy < 0 ? 0 : srows - 1, sy = 0, dy = 0; + + s = src.ptr(iy) + ix*cn; + for( int k = 0; k < cn; k++, s++ ) + { + float t0 = s[0] + sx*(s[dx] - s[0]); + float t1 = s[dy] + sx*(s[dy + dx] - s[dy]); + d[k] = t0 + sy*(t1 - t0); + } + } +} + + +class CV_GetRectSubPixTest : public CV_ImgWarpBaseTest +{ +public: + CV_GetRectSubPixTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void run_func(); + int prepare_test_case( int test_case_idx ); + void prepare_to_validation( int /*test_case_idx*/ ); + double get_success_error_level( int test_case_idx, int i, int j ); + void fill_array( int test_case_idx, int i, int j, Mat& arr ); + + CvPoint2D32f center; + bool test_cpp; +}; + + +CV_GetRectSubPixTest::CV_GetRectSubPixTest() : CV_ImgWarpBaseTest( false ) +{ + //spatial_scale_zoom = spatial_scale_decimate; + spatial_scale_decimate = spatial_scale_zoom; + test_cpp = false; +} + + +void CV_GetRectSubPixTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + CV_ImgWarpBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int src_depth = cvtest::randInt(rng) % 2, dst_depth; + int cn = cvtest::randInt(rng) % 2 ? 3 : 1; + CvSize src_size, dst_size; + + dst_depth = src_depth = src_depth == 0 ? CV_8U : CV_32F; + if( src_depth < CV_32F && cvtest::randInt(rng) % 2 ) + dst_depth = CV_32F; + + types[INPUT][0] = CV_MAKETYPE(src_depth,cn); + types[INPUT_OUTPUT][0] = types[REF_INPUT_OUTPUT][0] = CV_MAKETYPE(dst_depth,cn); + + src_size = sizes[INPUT][0]; + dst_size.width = cvRound(sqrt(cvtest::randReal(rng)*src_size.width) + 1); + dst_size.height = cvRound(sqrt(cvtest::randReal(rng)*src_size.height) + 1); + dst_size.width = MIN(dst_size.width,src_size.width); + dst_size.height = MIN(dst_size.width,src_size.height); + sizes[INPUT_OUTPUT][0] = sizes[REF_INPUT_OUTPUT][0] = dst_size; + + center.x = (float)(cvtest::randReal(rng)*src_size.width); + center.y = (float)(cvtest::randReal(rng)*src_size.height); + interpolation = CV_INTER_LINEAR; + + test_cpp = (cvtest::randInt(rng) & 256) == 0; +} + + +void CV_GetRectSubPixTest::fill_array( int test_case_idx, int i, int j, Mat& arr ) +{ + if( i != INPUT ) + CV_ImgWarpBaseTest::fill_array( test_case_idx, i, j, arr ); +} + + +void CV_GetRectSubPixTest::run_func() +{ + if(!test_cpp) + cvGetRectSubPix( test_array[INPUT][0], test_array[INPUT_OUTPUT][0], center ); + else + { + cv::Mat _out = cv::cvarrToMat(test_array[INPUT_OUTPUT][0]); + cv::getRectSubPix( cv::cvarrToMat(test_array[INPUT][0]), _out.size(), center, _out, _out.type()); + } +} + + +double CV_GetRectSubPixTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int in_depth = test_mat[INPUT][0].depth(); + int out_depth = test_mat[INPUT_OUTPUT][0].depth(); + + return in_depth >= CV_32F ? 1e-3 : out_depth >= CV_32F ? 1e-2 : 1; +} + + +int CV_GetRectSubPixTest::prepare_test_case( int test_case_idx ) +{ + return CV_ImgWarpBaseTest::prepare_test_case( test_case_idx ); +} + + +void CV_GetRectSubPixTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + Mat& src0 = test_mat[INPUT][0]; + Mat& dst0 = test_mat[REF_INPUT_OUTPUT][0]; + Mat src = src0, dst = dst0; + int ftype = CV_MAKETYPE(CV_32F,src0.channels()); + double a[] = { 1, 0, center.x - dst.cols*0.5 + 0.5, + 0, 1, center.y - dst.rows*0.5 + 0.5 }; + if( src.depth() != CV_32F ) + src0.convertTo(src, CV_32F); + + if( dst.depth() != CV_32F ) + dst.create(dst0.size(), ftype); + + test_getQuadrangeSubPix( src, dst, a ); + + if( dst.data != dst0.data ) + dst.convertTo(dst0, dst0.depth()); +} + + +class CV_GetQuadSubPixTest : public CV_ImgWarpBaseTest +{ +public: + CV_GetQuadSubPixTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void run_func(); + int prepare_test_case( int test_case_idx ); + void prepare_to_validation( int /*test_case_idx*/ ); + double get_success_error_level( int test_case_idx, int i, int j ); +}; + + +CV_GetQuadSubPixTest::CV_GetQuadSubPixTest() : CV_ImgWarpBaseTest( true ) +{ + //spatial_scale_zoom = spatial_scale_decimate; + spatial_scale_decimate = spatial_scale_zoom; +} + + +void CV_GetQuadSubPixTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + int min_size = 4; + CV_ImgWarpBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + CvSize sz = sizes[INPUT][0], dsz; + RNG& rng = ts->get_rng(); + int msz, src_depth = cvtest::randInt(rng) % 2, dst_depth; + int cn = cvtest::randInt(rng) % 2 ? 3 : 1; + + dst_depth = src_depth = src_depth == 0 ? CV_8U : CV_32F; + if( src_depth < CV_32F && cvtest::randInt(rng) % 2 ) + dst_depth = CV_32F; + + types[INPUT][0] = CV_MAKETYPE(src_depth,cn); + types[INPUT_OUTPUT][0] = types[REF_INPUT_OUTPUT][0] = CV_MAKETYPE(dst_depth,cn); + + sz.width = MAX(sz.width,min_size); + sz.height = MAX(sz.height,min_size); + sizes[INPUT][0] = sz; + msz = MIN( sz.width, sz.height ); + + dsz.width = cvRound(sqrt(cvtest::randReal(rng)*msz) + 1); + dsz.height = cvRound(sqrt(cvtest::randReal(rng)*msz) + 1); + dsz.width = MIN(dsz.width,msz); + dsz.height = MIN(dsz.width,msz); + dsz.width = MAX(dsz.width,min_size); + dsz.height = MAX(dsz.height,min_size); + sizes[INPUT_OUTPUT][0] = sizes[REF_INPUT_OUTPUT][0] = dsz; + sizes[INPUT][1] = cvSize( 3, 2 ); +} + + +void CV_GetQuadSubPixTest::run_func() +{ + CvMat mtx = test_mat[INPUT][1]; + cvGetQuadrangleSubPix( test_array[INPUT][0], test_array[INPUT_OUTPUT][0], &mtx ); +} + + +double CV_GetQuadSubPixTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int in_depth = test_mat[INPUT][0].depth(); + //int out_depth = test_mat[INPUT_OUTPUT][0].depth(); + + return in_depth >= CV_32F ? 1e-2 : 4; +} + + +int CV_GetQuadSubPixTest::prepare_test_case( int test_case_idx ) +{ + RNG& rng = ts->get_rng(); + int code = CV_ImgWarpBaseTest::prepare_test_case( test_case_idx ); + const Mat& src = test_mat[INPUT][0]; + Mat& mat = test_mat[INPUT][1]; + CvPoint2D32f center; + double scale, angle; + + if( code <= 0 ) + return code; + + double a[6]; + Mat A( 2, 3, CV_64FC1, a ); + + center.x = (float)((cvtest::randReal(rng)*1.2 - 0.1)*src.cols); + center.y = (float)((cvtest::randReal(rng)*1.2 - 0.1)*src.rows); + angle = cvtest::randReal(rng)*360; + scale = cvtest::randReal(rng)*0.2 + 0.9; + + // y = Ax + b -> x = A^-1(y - b) = A^-1*y - A^-1*b + scale = 1./scale; + angle = angle*(CV_PI/180.); + a[0] = a[4] = cos(angle)*scale; + a[1] = sin(angle)*scale; + a[3] = -a[1]; + a[2] = center.x - a[0]*center.x - a[1]*center.y; + a[5] = center.y - a[3]*center.x - a[4]*center.y; + A.convertTo( mat, mat.depth() ); + + return code; +} + + +void CV_GetQuadSubPixTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + Mat& src0 = test_mat[INPUT][0]; + Mat& dst0 = test_mat[REF_INPUT_OUTPUT][0]; + Mat src = src0, dst = dst0; + int ftype = CV_MAKETYPE(CV_32F,src0.channels()); + double a[6], dx = (dst0.cols - 1)*0.5, dy = (dst0.rows - 1)*0.5; + Mat A( 2, 3, CV_64F, a ); + + if( src.depth() != CV_32F ) + src0.convertTo(src, CV_32F); + + if( dst.depth() != CV_32F ) + dst.create(dst0.size(), ftype); + + test_mat[INPUT][1].convertTo( A, CV_64F ); + a[2] -= a[0]*dx + a[1]*dy; + a[5] -= a[3]*dx + a[4]*dy; + test_getQuadrangeSubPix( src, dst, a ); + + if( dst.data != dst0.data ) + dst.convertTo(dst0, dst0.depth()); +} + + +////////////////////////////////////////////////////////////////////////// + +TEST(Imgproc_Resize, accuracy) { CV_ResizeTest test; test.safe_run(); } +TEST(Imgproc_WarpAffine, accuracy) { CV_WarpAffineTest test; test.safe_run(); } +TEST(Imgproc_WarpPerspective, accuracy) { CV_WarpPerspectiveTest test; test.safe_run(); } +TEST(Imgproc_Remap, accuracy) { CV_RemapTest test; test.safe_run(); } +TEST(Imgproc_Undistort, accuracy) { CV_UndistortTest test; test.safe_run(); } +TEST(Imgproc_InitUndistortMap, accuracy) { CV_UndistortMapTest test; test.safe_run(); } +TEST(Imgproc_GetRectSubPix, accuracy) { CV_GetRectSubPixTest test; test.safe_run(); } +TEST(Imgproc_GetQuadSubPix, accuracy) { CV_GetQuadSubPixTest test; test.safe_run(); } + +/* End of file. */ diff --git a/modules/imgproc/test/test_inpaint.cpp b/modules/imgproc/test/test_inpaint.cpp new file mode 100644 index 000000000..b1da5cc99 --- /dev/null +++ b/modules/imgproc/test/test_inpaint.cpp @@ -0,0 +1,121 @@ +/*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 + +using namespace cv; + +class CV_InpaintTest : public cvtest::BaseTest +{ +public: + CV_InpaintTest(); + ~CV_InpaintTest(); +protected: + void run(int); +}; + +CV_InpaintTest::CV_InpaintTest() +{ +} +CV_InpaintTest::~CV_InpaintTest() {} + +void CV_InpaintTest::run( int ) +{ + string folder = string(ts->get_data_path()) + "inpaint/"; + Mat orig = imread(folder + "orig.jpg"); + Mat exp1 = imread(folder + "exp1.png"); + Mat exp2 = imread(folder + "exp2.png"); + Mat mask = imread(folder + "mask.png"); + + if (orig.empty() || exp1.empty() || exp2.empty() || mask.empty()) + { + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + Mat inv_mask; + mask.convertTo(inv_mask, CV_8UC3, -1.0, 255.0); + + Mat mask1ch; + cv::cvtColor(mask, mask1ch, CV_BGR2GRAY); + + Mat test = orig.clone(); + test.setTo(Scalar::all(255), mask1ch); + + Mat res1, res2; + inpaint( test, mask1ch, res1, 5, CV_INPAINT_NS ); + inpaint( test, mask1ch, res2, 5, CV_INPAINT_TELEA ); + + imwrite("d:/exp1.png", res1); + imwrite("d:/exp2.png", res2); + + Mat diff1, diff2; + absdiff( orig, res1, diff1 ); + absdiff( orig, res2, diff2 ); + + double n1 = norm(diff1.reshape(1), NORM_INF, inv_mask.reshape(1)); + double n2 = norm(diff2.reshape(1), NORM_INF, inv_mask.reshape(1)); + + if (n1 != 0 || n2 != 0) + { + ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH ); + return; + } + + absdiff( exp1, res1, diff1 ); + absdiff( exp2, res2, diff2 ); + + n1 = norm(diff1.reshape(1), NORM_INF, mask.reshape(1)); + n2 = norm(diff2.reshape(1), NORM_INF, mask.reshape(1)); + + const int jpeg_thres = 3; + if (n1 > jpeg_thres || n2 > jpeg_thres) + { + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + return; + } + + ts->set_failed_test_info(cvtest::TS::OK); +} + +TEST(Imgproc_Inpaint, regression) { CV_InpaintTest test; test.safe_run(); } diff --git a/modules/imgproc/test/test_main.cpp b/modules/imgproc/test/test_main.cpp new file mode 100644 index 000000000..6a686e68d --- /dev/null +++ b/modules/imgproc/test/test_main.cpp @@ -0,0 +1,4 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("cv") + diff --git a/modules/imgproc/test/test_moments.cpp b/modules/imgproc/test/test_moments.cpp new file mode 100644 index 000000000..ca53d8c7a --- /dev/null +++ b/modules/imgproc/test/test_moments.cpp @@ -0,0 +1,393 @@ +/*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" + +using namespace cv; +using namespace std; + +// image moments +class CV_MomentsTest : public cvtest::ArrayTest +{ +public: + CV_MomentsTest(); + +protected: + + enum { MOMENT_COUNT = 25 }; + int prepare_test_case( int test_case_idx ); + void prepare_to_validation( int /*test_case_idx*/ ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + int coi; + bool is_binary; +}; + + +CV_MomentsTest::CV_MomentsTest() +{ + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + coi = -1; + is_binary = false; + //element_wise_relative_error = false; +} + + +void CV_MomentsTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) +{ + cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high ); + int depth = CV_MAT_DEPTH(type); + + if( depth == CV_16U ) + { + low = Scalar::all(0); + high = Scalar::all(1000); + } + else if( depth == CV_16S ) + { + low = Scalar::all(-1000); + high = Scalar::all(1000); + } + else if( depth == CV_32F ) + { + low = Scalar::all(-1); + high = Scalar::all(1); + } +} + + +void CV_MomentsTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + int cn = cvtest::randInt(rng) % 4 + 1; + int depth = cvtest::randInt(rng) % 4; + depth = depth == 0 ? CV_8U : depth == 1 ? CV_16U : depth == 2 ? CV_16S : CV_32F; + if( cn == 2 ) + cn = 1; + + types[INPUT][0] = CV_MAKETYPE(depth, cn); + types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_64FC1; + sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = cvSize(MOMENT_COUNT,1); + + is_binary = cvtest::randInt(rng) % 2 != 0; + coi = 0; + cvmat_allowed = true; + if( cn > 1 ) + { + coi = cvtest::randInt(rng) % cn; + cvmat_allowed = false; + } +} + + +double CV_MomentsTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + int depth = test_mat[INPUT][0].depth(); + return depth != CV_32F ? FLT_EPSILON*10 : FLT_EPSILON*100; +} + +int CV_MomentsTest::prepare_test_case( int test_case_idx ) +{ + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + int cn = test_mat[INPUT][0].channels(); + if( cn > 1 ) + cvSetImageCOI( (IplImage*)test_array[INPUT][0], coi + 1 ); + } + + return code; +} + + +void CV_MomentsTest::run_func() +{ + CvMoments* m = (CvMoments*)test_mat[OUTPUT][0].ptr(); + double* others = (double*)(m + 1); + cvMoments( test_array[INPUT][0], m, is_binary ); + others[0] = cvGetNormalizedCentralMoment( m, 2, 0 ); + others[1] = cvGetNormalizedCentralMoment( m, 1, 1 ); + others[2] = cvGetNormalizedCentralMoment( m, 0, 2 ); + others[3] = cvGetNormalizedCentralMoment( m, 3, 0 ); + others[4] = cvGetNormalizedCentralMoment( m, 2, 1 ); + others[5] = cvGetNormalizedCentralMoment( m, 1, 2 ); + others[6] = cvGetNormalizedCentralMoment( m, 0, 3 ); +} + + +void CV_MomentsTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + Mat& src = test_mat[INPUT][0]; + CvMoments m; + double* mdata = test_mat[REF_OUTPUT][0].ptr(); + int depth = src.depth(); + int cn = src.channels(); + int i, y, x, cols = src.cols; + double xc = 0., yc = 0.; + + memset( &m, 0, sizeof(m)); + + for( y = 0; y < src.rows; y++ ) + { + double s0 = 0, s1 = 0, s2 = 0, s3 = 0; + uchar* ptr = src.ptr(y); + for( x = 0; x < cols; x++ ) + { + double val; + if( depth == CV_8U ) + val = ptr[x*cn + coi]; + else if( depth == CV_16U ) + val = ((ushort*)ptr)[x*cn + coi]; + else if( depth == CV_16S ) + val = ((short*)ptr)[x*cn + coi]; + else + val = ((float*)ptr)[x*cn + coi]; + + if( is_binary ) + val = val != 0; + + s0 += val; + s1 += val*x; + s2 += val*x*x; + s3 += ((val*x)*x)*x; + } + + m.m00 += s0; + m.m01 += s0*y; + m.m02 += (s0*y)*y; + m.m03 += ((s0*y)*y)*y; + + m.m10 += s1; + m.m11 += s1*y; + m.m12 += (s1*y)*y; + + m.m20 += s2; + m.m21 += s2*y; + + m.m30 += s3; + } + + if( m.m00 != 0 ) + { + xc = m.m10/m.m00, yc = m.m01/m.m00; + m.inv_sqrt_m00 = 1./sqrt(fabs(m.m00)); + } + + for( y = 0; y < src.rows; y++ ) + { + double s0 = 0, s1 = 0, s2 = 0, s3 = 0, y1 = y - yc; + uchar* ptr = src.ptr(y); + for( x = 0; x < cols; x++ ) + { + double val, x1 = x - xc; + if( depth == CV_8U ) + val = ptr[x*cn + coi]; + else if( depth == CV_16U ) + val = ((ushort*)ptr)[x*cn + coi]; + else if( depth == CV_16S ) + val = ((short*)ptr)[x*cn + coi]; + else + val = ((float*)ptr)[x*cn + coi]; + + if( is_binary ) + val = val != 0; + + s0 += val; + s1 += val*x1; + s2 += val*x1*x1; + s3 += ((val*x1)*x1)*x1; + } + + m.mu02 += s0*y1*y1; + m.mu03 += ((s0*y1)*y1)*y1; + + m.mu11 += s1*y1; + m.mu12 += (s1*y1)*y1; + + m.mu20 += s2; + m.mu21 += s2*y1; + + m.mu30 += s3; + } + + memcpy( mdata, &m, sizeof(m)); + mdata += sizeof(m)/sizeof(m.m00); + + /* calc normalized moments */ + { + double inv_m00 = m.inv_sqrt_m00*m.inv_sqrt_m00; + double s2 = inv_m00*inv_m00; /* 1./(m00 ^ (2/2 + 1)) */ + double s3 = s2*m.inv_sqrt_m00; /* 1./(m00 ^ (3/2 + 1)) */ + + mdata[0] = m.mu20 * s2; + mdata[1] = m.mu11 * s2; + mdata[2] = m.mu02 * s2; + + mdata[3] = m.mu30 * s3; + mdata[4] = m.mu21 * s3; + mdata[5] = m.mu12 * s3; + mdata[6] = m.mu03 * s3; + } + + double* a = test_mat[REF_OUTPUT][0].ptr(); + double* b = test_mat[OUTPUT][0].ptr(); + for( i = 0; i < MOMENT_COUNT; i++ ) + { + if( fabs(a[i]) < 1e-3 ) + a[i] = 0; + if( fabs(b[i]) < 1e-3 ) + b[i] = 0; + } +} + + +// Hu invariants +class CV_HuMomentsTest : public cvtest::ArrayTest +{ +public: + CV_HuMomentsTest(); + +protected: + + enum { MOMENT_COUNT = 18, HU_MOMENT_COUNT = 7 }; + + int prepare_test_case( int test_case_idx ); + void prepare_to_validation( int /*test_case_idx*/ ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); +}; + + +CV_HuMomentsTest::CV_HuMomentsTest() +{ + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); +} + + +void CV_HuMomentsTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) +{ + cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high ); + low = Scalar::all(-10000); + high = Scalar::all(10000); +} + + +void CV_HuMomentsTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_64FC1; + sizes[INPUT][0] = cvSize(MOMENT_COUNT,1); + sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = cvSize(HU_MOMENT_COUNT,1); +} + + +double CV_HuMomentsTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return FLT_EPSILON; +} + + + +int CV_HuMomentsTest::prepare_test_case( int test_case_idx ) +{ + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + // ... + } + + return code; +} + + +void CV_HuMomentsTest::run_func() +{ + cvGetHuMoments( (CvMoments*)test_mat[INPUT][0].data, + (CvHuMoments*)test_mat[OUTPUT][0].data ); +} + + +void CV_HuMomentsTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + CvMoments* m = (CvMoments*)test_mat[INPUT][0].data; + CvHuMoments* hu = (CvHuMoments*)test_mat[REF_OUTPUT][0].data; + + double inv_m00 = m->inv_sqrt_m00*m->inv_sqrt_m00; + double s2 = inv_m00*inv_m00; /* 1./(m00 ^ (2/2 + 1)) */ + double s3 = s2*m->inv_sqrt_m00; /* 1./(m00 ^ (3/2 + 1)) */ + + double nu20 = m->mu20 * s2; + double nu11 = m->mu11 * s2; + double nu02 = m->mu02 * s2; + + double nu30 = m->mu30 * s3; + double nu21 = m->mu21 * s3; + double nu12 = m->mu12 * s3; + double nu03 = m->mu03 * s3; + + #undef sqr + #define sqr(a) ((a)*(a)) + + hu->hu1 = nu20 + nu02; + hu->hu2 = sqr(nu20 - nu02) + 4*sqr(nu11); + hu->hu3 = sqr(nu30 - 3*nu12) + sqr(3*nu21 - nu03); + hu->hu4 = sqr(nu30 + nu12) + sqr(nu21 + nu03); + hu->hu5 = (nu30 - 3*nu12)*(nu30 + nu12)*(sqr(nu30 + nu12) - 3*sqr(nu21 + nu03)) + + (3*nu21 - nu03)*(nu21 + nu03)*(3*sqr(nu30 + nu12) - sqr(nu21 + nu03)); + hu->hu6 = (nu20 - nu02)*(sqr(nu30 + nu12) - sqr(nu21 + nu03)) + + 4*nu11*(nu30 + nu12)*(nu21 + nu03); + hu->hu7 = (3*nu21 - nu03)*(nu30 + nu12)*(sqr(nu30 + nu12) - 3*sqr(nu21 + nu03)) + + (3*nu12 - nu30)*(nu21 + nu03)*(3*sqr(nu30 + nu12) - sqr(nu21 + nu03)); +} + + +TEST(Imgproc_Moments, accuracy) { CV_MomentsTest test; test.safe_run(); } +TEST(Imgproc_HuMoments, accuracy) { CV_HuMomentsTest test; test.safe_run(); } diff --git a/modules/imgproc/test/test_precomp.cpp b/modules/imgproc/test/test_precomp.cpp new file mode 100644 index 000000000..5956e13e3 --- /dev/null +++ b/modules/imgproc/test/test_precomp.cpp @@ -0,0 +1 @@ +#include "test_precomp.hpp" diff --git a/modules/imgproc/test/test_precomp.hpp b/modules/imgproc/test/test_precomp.hpp new file mode 100644 index 000000000..f28d167d0 --- /dev/null +++ b/modules/imgproc/test/test_precomp.hpp @@ -0,0 +1,11 @@ +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/highgui/highgui.hpp" +#include "opencv2/highgui/highgui_c.h" +#include + +#endif diff --git a/modules/imgproc/test/test_pyrsegmentation.cpp b/modules/imgproc/test/test_pyrsegmentation.cpp new file mode 100644 index 000000000..89fe46a6c --- /dev/null +++ b/modules/imgproc/test/test_pyrsegmentation.cpp @@ -0,0 +1,204 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_PyrSegmentationTest : public cvtest::BaseTest +{ +public: + CV_PyrSegmentationTest(); +protected: + void run(int); +}; + +#define SCAN 0 + +CV_PyrSegmentationTest::CV_PyrSegmentationTest() +{ +} + +void CV_PyrSegmentationTest::run( int /*start_from*/ ) +{ + Mat _image_f, _image, _image_s; + const int level = 5; + const double range = 15; + + int code = cvtest::TS::OK; + + CvPoint _cp[] ={{33,33}, {43,33}, {43,43}, {33,43}}; + CvPoint _cp2[] ={{50,50}, {70,50}, {70,70}, {50,70}}; + CvPoint* cp = _cp; + CvPoint* cp2 = _cp2; + CvConnectedComp *dst_comp[3]; + CvRect rect[3] = {{50,50,21,21}, {0,0,128,128}, {33,33,11,11}}; + double a[3] = {441.0, 15822.0, 121.0}; + +/* ippiPoint cp3[] ={130,130, 150,130, 150,150, 130,150}; */ +/* CvPoint cp[] ={0,0, 5,5, 5,0, 10,5, 10,0, 15,5, 15,0}; */ + int nPoints = 4; + int block_size = 1000; + + CvMemStorage *storage; /* storage for connected component writing */ + CvSeq *comp; + + RNG& rng = ts->get_rng(); + int i, j, iter; + + IplImage *image, *image_f, *image_s; + CvSize size = {128, 128}; + const int threshold1 = 50, threshold2 = 50; + + rect[1].width = size.width; + rect[1].height = size.height; + a[1] = size.width*size.height - a[0] - a[2]; + + OPENCV_CALL( storage = cvCreateMemStorage( block_size ) ); + + for( iter = 0; iter < 2; iter++ ) + { + int channels = iter == 0 ? 1 : 3; + int mask[] = {0,0,0}; + + image = cvCreateImage(size, 8, channels ); + image_s = cvCloneImage( image ); + image_f = cvCloneImage( image ); + + if( channels == 1 ) + { + int color1 = 30, color2 = 110, color3 = 190; + + cvSet( image, cvScalarAll(color1)); + cvFillPoly( image, &cp, &nPoints, 1, cvScalar(color2)); + cvFillPoly( image, &cp2, &nPoints, 1, cvScalar(color3)); + } + else + { + CvScalar color1 = CV_RGB(30,30,30), color2 = CV_RGB(255,0,0), color3 = CV_RGB(0,255,0); + + assert( channels == 3 ); + cvSet( image, color1 ); + cvFillPoly( image, &cp, &nPoints, 1, color2); + cvFillPoly( image, &cp2, &nPoints, 1, color3); + } + + _image_f = cvarrToMat(image_f); + cvtest::randUni( rng, _image_f, cvScalarAll(0), cvScalarAll(range*2) ); + cvAddWeighted( image, 1, image_f, 1, -range, image_f ); + + cvPyrSegmentation( image_f, image_s, + storage, &comp, + level, threshold1, threshold2 ); + + if(comp->total != 3) + { + ts->printf( cvtest::TS::LOG, + "The segmentation function returned %d (not 3) components\n", comp->total ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + /* read the connected components */ + dst_comp[0] = (CvConnectedComp*)CV_GET_SEQ_ELEM( CvConnectedComp, comp, 0 ); + dst_comp[1] = (CvConnectedComp*)CV_GET_SEQ_ELEM( CvConnectedComp, comp, 1 ); + dst_comp[2] = (CvConnectedComp*)CV_GET_SEQ_ELEM( CvConnectedComp, comp, 2 ); + + /*{ + for( i = 0; i < 3; i++ ) + { + CvRect r = dst_comp[i]->rect; + cvRectangle( image_s, cvPoint(r.x,r.y), cvPoint(r.x+r.width,r.y+r.height), + CV_RGB(255,255,255), 3, 8, 0 ); + } + + cvNamedWindow( "test", 1 ); + cvShowImage( "test", image_s ); + cvWaitKey(0); + }*/ + + _image = cvarrToMat(image); + _image_s = cvarrToMat(image_s); + code = cvtest::cmpEps2( ts, _image, _image_s, 10, false, "the output image" ); + if( code < 0 ) + goto _exit_; + + for( i = 0; i < 3; i++) + { + for( j = 0; j < 3; j++ ) + { + if( !mask[j] && dst_comp[i]->area == a[j] && + dst_comp[i]->rect.x == rect[j].x && + dst_comp[i]->rect.y == rect[j].y && + dst_comp[i]->rect.width == rect[j].width && + dst_comp[i]->rect.height == rect[j].height ) + { + mask[j] = 1; + break; + } + } + if( j == 3 ) + { + ts->printf( cvtest::TS::LOG, "The component #%d is incorrect\n", i ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + } + + cvReleaseImage(&image_f); + cvReleaseImage(&image); + cvReleaseImage(&image_s); + } + +_exit_: + + cvReleaseMemStorage( &storage ); + cvReleaseImage(&image_f); + cvReleaseImage(&image); + cvReleaseImage(&image_s); + + if( code < 0 ) + ts->set_failed_test_info( code ); +} + +TEST(Imgproc_PyrSegmentation, regression) { CV_PyrSegmentationTest test; test.safe_run(); } + +/* End of file. */ diff --git a/modules/imgproc/test/test_subdivisions.cpp b/modules/imgproc/test/test_subdivisions.cpp new file mode 100644 index 000000000..b65b8d216 --- /dev/null +++ b/modules/imgproc/test/test_subdivisions.cpp @@ -0,0 +1,341 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_SubdivTest : public cvtest::BaseTest +{ +public: + CV_SubdivTest(); + ~CV_SubdivTest(); + void clear(); + +protected: + int read_params( CvFileStorage* fs ); + int prepare_test_case( int test_case_idx ); + int validate_test_results( int test_case_idx ); + void run_func(); + + int min_log_img_size, max_log_img_size; + CvSize img_size; + int min_log_point_count; + int max_log_point_count; + int point_count; + CvSubdiv2D* subdiv; + CvMemStorage* storage; +}; + + +CV_SubdivTest::CV_SubdivTest() +{ + test_case_count = 100; + min_log_point_count = 1; + max_log_point_count = 10; + min_log_img_size = 1; + max_log_img_size = 10; + + storage = 0; +} + + +CV_SubdivTest::~CV_SubdivTest() +{ + clear(); +} + + +void CV_SubdivTest::clear() +{ + cvtest::BaseTest::clear(); + cvReleaseMemStorage( &storage ); +} + + +int CV_SubdivTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::BaseTest::read_params( fs ); + int t; + + if( code < 0 ) + return code; + + test_case_count = cvReadInt( find_param( fs, "test_case_count" ), test_case_count ); + min_log_point_count = cvReadInt( find_param( fs, "min_log_point_count" ), min_log_point_count ); + max_log_point_count = cvReadInt( find_param( fs, "max_log_point_count" ), max_log_point_count ); + min_log_img_size = cvReadInt( find_param( fs, "min_log_img_size" ), min_log_img_size ); + max_log_img_size = cvReadInt( find_param( fs, "max_log_img_size" ), max_log_img_size ); + + min_log_point_count = cvtest::clipInt( min_log_point_count, 1, 10 ); + max_log_point_count = cvtest::clipInt( max_log_point_count, 1, 10 ); + if( min_log_point_count > max_log_point_count ) + CV_SWAP( min_log_point_count, max_log_point_count, t ); + + min_log_img_size = cvtest::clipInt( min_log_img_size, 1, 10 ); + max_log_img_size = cvtest::clipInt( max_log_img_size, 1, 10 ); + if( min_log_img_size > max_log_img_size ) + CV_SWAP( min_log_img_size, max_log_img_size, t ); + + return 0; +} + + +int CV_SubdivTest::prepare_test_case( int test_case_idx ) +{ + RNG& rng = ts->get_rng(); + int code = cvtest::BaseTest::prepare_test_case( test_case_idx ); + if( code < 0 ) + return code; + + clear(); + + point_count = cvRound(exp((cvtest::randReal(rng)* + (max_log_point_count - min_log_point_count) + min_log_point_count)*CV_LOG2)); + img_size.width = cvRound(exp((cvtest::randReal(rng)* + (max_log_img_size - min_log_img_size) + min_log_img_size)*CV_LOG2)); + img_size.height = cvRound(exp((cvtest::randReal(rng)* + (max_log_img_size - min_log_img_size) + min_log_img_size)*CV_LOG2)); + + storage = cvCreateMemStorage( 1 << 10 ); + return 1; +} + + +void CV_SubdivTest::run_func() +{ +} + + +static inline double sqdist( CvPoint2D32f pt1, CvPoint2D32f pt2 ) +{ + double dx = pt1.x - pt2.x; + double dy = pt1.y - pt2.y; + + return dx*dx + dy*dy; +} + + +static int +subdiv2DCheck( CvSubdiv2D* subdiv ) +{ + int i, j, total = subdiv->edges->total; + CV_Assert( subdiv != 0 ); + + for( i = 0; i < total; i++ ) + { + CvQuadEdge2D* edge = (CvQuadEdge2D*)cvGetSetElem(subdiv->edges,i); + + if( edge && CV_IS_SET_ELEM( edge )) + { + for( j = 0; j < 4; j++ ) + { + CvSubdiv2DEdge e = (CvSubdiv2DEdge)edge + j; + CvSubdiv2DEdge o_next = cvSubdiv2DNextEdge(e); + CvSubdiv2DEdge o_prev = cvSubdiv2DGetEdge(e, CV_PREV_AROUND_ORG ); + CvSubdiv2DEdge d_prev = cvSubdiv2DGetEdge(e, CV_PREV_AROUND_DST ); + CvSubdiv2DEdge d_next = cvSubdiv2DGetEdge(e, CV_NEXT_AROUND_DST ); + + // check points + if( cvSubdiv2DEdgeOrg(e) != cvSubdiv2DEdgeOrg(o_next)) + return 0; + if( cvSubdiv2DEdgeOrg(e) != cvSubdiv2DEdgeOrg(o_prev)) + return 0; + if( cvSubdiv2DEdgeDst(e) != cvSubdiv2DEdgeDst(d_next)) + return 0; + if( cvSubdiv2DEdgeDst(e) != cvSubdiv2DEdgeDst(d_prev)) + return 0; + if( j % 2 == 0 ) + { + if( cvSubdiv2DEdgeDst(o_next) != cvSubdiv2DEdgeOrg(d_prev)) + return 0; + if( cvSubdiv2DEdgeDst(o_prev) != cvSubdiv2DEdgeOrg(d_next)) + return 0; + if( cvSubdiv2DGetEdge(cvSubdiv2DGetEdge(cvSubdiv2DGetEdge( + e,CV_NEXT_AROUND_LEFT),CV_NEXT_AROUND_LEFT),CV_NEXT_AROUND_LEFT) != e ) + return 0; + if( cvSubdiv2DGetEdge(cvSubdiv2DGetEdge(cvSubdiv2DGetEdge( + e,CV_NEXT_AROUND_RIGHT),CV_NEXT_AROUND_RIGHT),CV_NEXT_AROUND_RIGHT) != e) + return 0; + } + } + } + } + + return 1; +} + + +// the whole testing is done here, run_func() is not utilized in this test +int CV_SubdivTest::validate_test_results( int /*test_case_idx*/ ) +{ + int code = cvtest::TS::OK; + RNG& rng = ts->get_rng(); + int j, k, real_count = point_count; + double xrange = img_size.width*(1 - FLT_EPSILON); + double yrange = img_size.height*(1 - FLT_EPSILON); + + subdiv = subdiv = cvCreateSubdivDelaunay2D( + cvRect( 0, 0, img_size.width, img_size.height ), storage ); + + CvSeq* seq = cvCreateSeq( 0, sizeof(*seq), sizeof(CvPoint2D32f), storage ); + CvSeqWriter writer; + cvStartAppendToSeq( seq, &writer ); + + // insert random points + for( j = 0; j < point_count; j++ ) + { + CvPoint2D32f pt; + CvSubdiv2DPoint* point; + + pt.x = (float)(cvtest::randReal(rng)*xrange); + pt.y = (float)(cvtest::randReal(rng)*yrange); + + CvSubdiv2DPointLocation loc = + cvSubdiv2DLocate( subdiv, pt, 0, &point ); + + if( loc == CV_PTLOC_VERTEX ) + { + int index = cvSeqElemIdx( (CvSeq*)subdiv, point ); + CvPoint2D32f* pt1; + cvFlushSeqWriter( &writer ); + pt1 = (CvPoint2D32f*)cvGetSeqElem( seq, index - 3 ); + + if( !pt1 || + fabs(pt1->x - pt.x) > FLT_EPSILON || + fabs(pt1->y - pt.y) > FLT_EPSILON ) + { + ts->printf( cvtest::TS::LOG, "The point #%d: (%.1f,%.1f) is said to coinside with a subdivision vertex, " + "however it could be found in a sequence of inserted points\n", j, pt.x, pt.y ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + real_count--; + } + + point = cvSubdivDelaunay2DInsert( subdiv, pt ); + if( point->pt.x != pt.x || point->pt.y != pt.y ) + { + ts->printf( cvtest::TS::LOG, "The point #%d: (%.1f,%.1f) has been incorrectly added\n", j, pt.x, pt.y ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + + if( (j + 1) % 10 == 0 || j == point_count - 1 ) + { + if( !subdiv2DCheck( subdiv )) + { + ts->printf( cvtest::TS::LOG, "Subdivision consistency check failed after inserting the point #%d\n", j ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + } + + if( loc != CV_PTLOC_VERTEX ) + { + CV_WRITE_SEQ_ELEM( pt, writer ); + } + } + + if( code < 0 ) + goto _exit_; + + cvCalcSubdivVoronoi2D( subdiv ); + seq = cvEndWriteSeq( &writer ); + + if( !subdiv2DCheck( subdiv )) + { + ts->printf( cvtest::TS::LOG, "The subdivision failed consistency check after building the Voronoi tesselation\n" ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + + for( j = 0; j < MAX((point_count - 5)/10 + 5, 10); j++ ) + { + CvPoint2D32f pt; + double minDistance; + + pt.x = (float)(cvtest::randReal(rng)*xrange); + pt.y = (float)(cvtest::randReal(rng)*yrange); + + CvSubdiv2DPoint* point = cvFindNearestPoint2D( subdiv, pt ); + CvSeqReader reader; + + if( !point ) + { + ts->printf( cvtest::TS::LOG, "There is no nearest point (?!) for the point (%.1f, %.1f) in the subdivision\n", + pt.x, pt.y ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + + cvStartReadSeq( seq, &reader ); + minDistance = sqdist( pt, point->pt ); + + for( k = 0; k < seq->total; k++ ) + { + CvPoint2D32f ptt; + CV_READ_SEQ_ELEM( ptt, reader ); + + double distance = sqdist( pt, ptt ); + if( minDistance > distance && sqdist(ptt, point->pt) > FLT_EPSILON*1000 ) + { + ts->printf( cvtest::TS::LOG, "The triangulation vertex (%.3f,%.3f) was said to be nearest to (%.3f,%.3f),\n" + "whereas another vertex (%.3f,%.3f) is closer\n", + point->pt.x, point->pt.y, pt.x, pt.y, ptt.x, ptt.y ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + } + } + +_exit_: + if( code < 0 ) + ts->set_failed_test_info( code ); + + return code; +} + +TEST(Imgproc_Subdiv, correctness) { CV_SubdivTest test; test.safe_run(); } + +/* End of file. */ + diff --git a/modules/imgproc/test/test_templmatch.cpp b/modules/imgproc/test/test_templmatch.cpp new file mode 100644 index 000000000..1b38e5614 --- /dev/null +++ b/modules/imgproc/test/test_templmatch.cpp @@ -0,0 +1,336 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_TemplMatchTest : public cvtest::ArrayTest +{ +public: + CV_TemplMatchTest(); + +protected: + int read_params( CvFileStorage* fs ); + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int ); + + int max_template_size; + int method; + bool test_cpp; +}; + + +CV_TemplMatchTest::CV_TemplMatchTest() +{ + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + element_wise_relative_error = false; + max_template_size = 100; + method = 0; + test_cpp = false; +} + + +int CV_TemplMatchTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::ArrayTest::read_params( fs ); + if( code < 0 ) + return code; + + max_template_size = cvReadInt( find_param( fs, "max_template_size" ), max_template_size ); + max_template_size = cvtest::clipInt( max_template_size, 1, 100 ); + + return code; +} + + +void CV_TemplMatchTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) +{ + cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high ); + int depth = CV_MAT_DEPTH(type); + if( depth == CV_32F ) + { + low = Scalar::all(-10.); + high = Scalar::all(10.); + } +} + + +void CV_TemplMatchTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int depth = cvtest::randInt(rng) % 2, cn = cvtest::randInt(rng) & 1 ? 3 : 1; + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + depth = depth == 0 ? CV_8U : CV_32F; + + types[INPUT][0] = types[INPUT][1] = CV_MAKETYPE(depth,cn); + types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_32FC1; + + sizes[INPUT][1].width = cvtest::randInt(rng)%MIN(sizes[INPUT][1].width,max_template_size) + 1; + sizes[INPUT][1].height = cvtest::randInt(rng)%MIN(sizes[INPUT][1].height,max_template_size) + 1; + sizes[OUTPUT][0].width = sizes[INPUT][0].width - sizes[INPUT][1].width + 1; + sizes[OUTPUT][0].height = sizes[INPUT][0].height - sizes[INPUT][1].height + 1; + sizes[REF_OUTPUT][0] = sizes[OUTPUT][0]; + + method = cvtest::randInt(rng)%6; + test_cpp = (cvtest::randInt(rng) & 256) == 0; +} + + +double CV_TemplMatchTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + if( test_mat[INPUT][1].depth() == CV_8U || + (method >= CV_TM_CCOEFF && test_mat[INPUT][1].cols*test_mat[INPUT][1].rows <= 2) ) + return 1e-2; + else + return 1e-3; +} + + +void CV_TemplMatchTest::run_func() +{ + if(!test_cpp) + cvMatchTemplate( test_array[INPUT][0], test_array[INPUT][1], test_array[OUTPUT][0], method ); + else + { + cv::Mat _out = cv::cvarrToMat(test_array[OUTPUT][0]); + cv::matchTemplate(cv::cvarrToMat(test_array[INPUT][0]), cv::cvarrToMat(test_array[INPUT][1]), _out, method); + } +} + + +static void cvTsMatchTemplate( const CvMat* img, const CvMat* templ, CvMat* result, int method ) +{ + int i, j, k, l; + int depth = CV_MAT_DEPTH(img->type), cn = CV_MAT_CN(img->type); + int width_n = templ->cols*cn, height = templ->rows; + int a_step = img->step / CV_ELEM_SIZE(img->type & CV_MAT_DEPTH_MASK); + int b_step = templ->step / CV_ELEM_SIZE(templ->type & CV_MAT_DEPTH_MASK); + CvScalar b_mean, b_sdv; + double b_denom = 1., b_sum2 = 0; + int area = templ->rows*templ->cols; + + cvAvgSdv(templ, &b_mean, &b_sdv); + + for( i = 0; i < cn; i++ ) + b_sum2 += (b_sdv.val[i]*b_sdv.val[i] + b_mean.val[i]*b_mean.val[i])*area; + + if( b_sdv.val[0]*b_sdv.val[0] + b_sdv.val[1]*b_sdv.val[1] + + b_sdv.val[2]*b_sdv.val[2] + b_sdv.val[3]*b_sdv.val[3] < DBL_EPSILON && + method == CV_TM_CCOEFF_NORMED ) + { + cvSet( result, cvScalarAll(1.) ); + return; + } + + if( method & 1 ) + { + b_denom = 0; + if( method != CV_TM_CCOEFF_NORMED ) + { + b_denom = b_sum2; + } + else + { + for( i = 0; i < cn; i++ ) + b_denom += b_sdv.val[i]*b_sdv.val[i]*area; + } + b_denom = sqrt(b_denom); + if( b_denom == 0 ) + b_denom = 1.; + } + + assert( CV_TM_SQDIFF <= method && method <= CV_TM_CCOEFF_NORMED ); + + for( i = 0; i < result->rows; i++ ) + { + for( j = 0; j < result->cols; j++ ) + { + CvScalar a_sum = {{ 0, 0, 0, 0 }}, a_sum2 = {{ 0, 0, 0, 0 }}; + CvScalar ccorr = {{ 0, 0, 0, 0 }}; + double value = 0.; + + if( depth == CV_8U ) + { + const uchar* a = img->data.ptr + i*img->step + j*cn; + const uchar* b = templ->data.ptr; + + if( cn == 1 || method < CV_TM_CCOEFF ) + { + for( k = 0; k < height; k++, a += a_step, b += b_step ) + for( l = 0; l < width_n; l++ ) + { + ccorr.val[0] += a[l]*b[l]; + a_sum.val[0] += a[l]; + a_sum2.val[0] += a[l]*a[l]; + } + } + else + { + for( k = 0; k < height; k++, a += a_step, b += b_step ) + for( l = 0; l < width_n; l += 3 ) + { + ccorr.val[0] += a[l]*b[l]; + ccorr.val[1] += a[l+1]*b[l+1]; + ccorr.val[2] += a[l+2]*b[l+2]; + a_sum.val[0] += a[l]; + a_sum.val[1] += a[l+1]; + a_sum.val[2] += a[l+2]; + a_sum2.val[0] += a[l]*a[l]; + a_sum2.val[1] += a[l+1]*a[l+1]; + a_sum2.val[2] += a[l+2]*a[l+2]; + } + } + } + else + { + const float* a = (const float*)(img->data.ptr + i*img->step) + j*cn; + const float* b = (const float*)templ->data.ptr; + + if( cn == 1 || method < CV_TM_CCOEFF ) + { + for( k = 0; k < height; k++, a += a_step, b += b_step ) + for( l = 0; l < width_n; l++ ) + { + ccorr.val[0] += a[l]*b[l]; + a_sum.val[0] += a[l]; + a_sum2.val[0] += a[l]*a[l]; + } + } + else + { + for( k = 0; k < height; k++, a += a_step, b += b_step ) + for( l = 0; l < width_n; l += 3 ) + { + ccorr.val[0] += a[l]*b[l]; + ccorr.val[1] += a[l+1]*b[l+1]; + ccorr.val[2] += a[l+2]*b[l+2]; + a_sum.val[0] += a[l]; + a_sum.val[1] += a[l+1]; + a_sum.val[2] += a[l+2]; + a_sum2.val[0] += a[l]*a[l]; + a_sum2.val[1] += a[l+1]*a[l+1]; + a_sum2.val[2] += a[l+2]*a[l+2]; + } + } + } + + switch( method ) + { + case CV_TM_CCORR: + case CV_TM_CCORR_NORMED: + value = ccorr.val[0]; + break; + case CV_TM_SQDIFF: + case CV_TM_SQDIFF_NORMED: + value = (a_sum2.val[0] + b_sum2 - 2*ccorr.val[0]); + break; + default: + value = (ccorr.val[0] - a_sum.val[0]*b_mean.val[0]+ + ccorr.val[1] - a_sum.val[1]*b_mean.val[1]+ + ccorr.val[2] - a_sum.val[2]*b_mean.val[2]); + } + + if( method & 1 ) + { + double denom; + + // calc denominator + if( method != CV_TM_CCOEFF_NORMED ) + { + denom = a_sum2.val[0] + a_sum2.val[1] + a_sum2.val[2]; + } + else + { + denom = a_sum2.val[0] - (a_sum.val[0]*a_sum.val[0])/area; + denom += a_sum2.val[1] - (a_sum.val[1]*a_sum.val[1])/area; + denom += a_sum2.val[2] - (a_sum.val[2]*a_sum.val[2])/area; + } + denom = sqrt(MAX(denom,0))*b_denom; + if( fabs(value) < denom ) + value /= denom; + else if( fabs(value) < denom*1.125 ) + value = value > 0 ? 1 : -1; + else + value = method != CV_TM_SQDIFF_NORMED ? 0 : 1; + } + + ((float*)(result->data.ptr + result->step*i))[j] = (float)value; + } + } +} + + +void CV_TemplMatchTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + CvMat _input = test_mat[INPUT][0], _templ = test_mat[INPUT][1]; + CvMat _output = test_mat[REF_OUTPUT][0]; + cvTsMatchTemplate( &_input, &_templ, &_output, method ); + + //if( ts->get_current_test_info()->test_case_idx == 0 ) + /*{ + CvFileStorage* fs = cvOpenFileStorage( "_match_template.yml", 0, CV_STORAGE_WRITE ); + cvWrite( fs, "image", &test_mat[INPUT][0] ); + cvWrite( fs, "template", &test_mat[INPUT][1] ); + cvWrite( fs, "ref", &test_mat[REF_OUTPUT][0] ); + cvWrite( fs, "opencv", &test_mat[OUTPUT][0] ); + cvWriteInt( fs, "method", method ); + cvReleaseFileStorage( &fs ); + }*/ + + if( method >= CV_TM_CCOEFF ) + { + // avoid numerical stability problems in singular cases (when the results are near to 0) + const double delta = 10.; + test_mat[REF_OUTPUT][0] += Scalar::all(delta); + test_mat[OUTPUT][0] += Scalar::all(delta); + } +} + +TEST(Imgproc_MatchTemplate, accuracy) { CV_TemplMatchTest test; test.safe_run(); } diff --git a/modules/imgproc/test/test_thresh.cpp b/modules/imgproc/test/test_thresh.cpp new file mode 100644 index 000000000..9ef1bf6ad --- /dev/null +++ b/modules/imgproc/test/test_thresh.cpp @@ -0,0 +1,217 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_ThreshTest : public cvtest::ArrayTest +{ +public: + CV_ThreshTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int ); + + int thresh_type; + float thresh_val; + float max_val; +}; + + +CV_ThreshTest::CV_ThreshTest() +{ + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + optional_mask = false; + element_wise_relative_error = true; +} + + +void CV_ThreshTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int depth = cvtest::randInt(rng) % 2, cn = cvtest::randInt(rng) % 4 + 1; + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + depth = depth == 0 ? CV_8U : CV_32F; + + types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(depth,cn); + thresh_type = cvtest::randInt(rng) % 5; + + if( depth == CV_8U ) + { + thresh_val = (float)(cvtest::randReal(rng)*350. - 50.); + max_val = (float)(cvtest::randReal(rng)*350. - 50.); + if( cvtest::randInt(rng)%4 == 0 ) + max_val = 255; + } + else + { + thresh_val = (float)(cvtest::randReal(rng)*1000. - 500.); + max_val = (float)(cvtest::randReal(rng)*1000. - 500.); + } +} + + +double CV_ThreshTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return FLT_EPSILON*10; +} + + +void CV_ThreshTest::run_func() +{ + cvThreshold( test_array[INPUT][0], test_array[OUTPUT][0], + thresh_val, max_val, thresh_type ); +} + + +static void test_threshold( const Mat& _src, Mat& _dst, + float thresh, float maxval, int thresh_type ) +{ + int i, j; + int depth = _src.depth(), cn = _src.channels(); + int width_n = _src.cols*cn, height = _src.rows; + int ithresh = cvFloor(thresh), ithresh2, imaxval = cvRound(maxval); + const uchar* src = _src.data; + uchar* dst = _dst.data; + size_t srcstep = _src.step, dststep = _dst.step; + + ithresh2 = saturate_cast(ithresh); + imaxval = saturate_cast(imaxval); + + assert( depth == CV_8U || depth == CV_32F ); + + switch( thresh_type ) + { + case CV_THRESH_BINARY: + for( i = 0; i < height; i++, src += srcstep, dst += dststep ) + { + if( depth == CV_8U ) + for( j = 0; j < width_n; j++ ) + dst[j] = (uchar)(src[j] > ithresh ? imaxval : 0); + else + for( j = 0; j < width_n; j++ ) + ((float*)dst)[j] = ((const float*)src)[j] > thresh ? maxval : 0.f; + } + break; + case CV_THRESH_BINARY_INV: + for( i = 0; i < height; i++, src += srcstep, dst += dststep ) + { + if( depth == CV_8U ) + for( j = 0; j < width_n; j++ ) + dst[j] = (uchar)(src[j] > ithresh ? 0 : imaxval); + else + for( j = 0; j < width_n; j++ ) + ((float*)dst)[j] = ((const float*)src)[j] > thresh ? 0.f : maxval; + } + break; + case CV_THRESH_TRUNC: + for( i = 0; i < height; i++, src += srcstep, dst += dststep ) + { + if( depth == CV_8U ) + for( j = 0; j < width_n; j++ ) + { + int s = src[j]; + dst[j] = (uchar)(s > ithresh ? ithresh2 : s); + } + else + for( j = 0; j < width_n; j++ ) + { + float s = ((const float*)src)[j]; + ((float*)dst)[j] = s > thresh ? thresh : s; + } + } + break; + case CV_THRESH_TOZERO: + for( i = 0; i < height; i++, src += srcstep, dst += dststep ) + { + if( depth == CV_8U ) + for( j = 0; j < width_n; j++ ) + { + int s = src[j]; + dst[j] = (uchar)(s > ithresh ? s : 0); + } + else + for( j = 0; j < width_n; j++ ) + { + float s = ((const float*)src)[j]; + ((float*)dst)[j] = s > thresh ? s : 0.f; + } + } + break; + case CV_THRESH_TOZERO_INV: + for( i = 0; i < height; i++, src += srcstep, dst += dststep ) + { + if( depth == CV_8U ) + for( j = 0; j < width_n; j++ ) + { + int s = src[j]; + dst[j] = (uchar)(s > ithresh ? 0 : s); + } + else + for( j = 0; j < width_n; j++ ) + { + float s = ((const float*)src)[j]; + ((float*)dst)[j] = s > thresh ? 0.f : s; + } + } + break; + default: + assert(0); + } +} + + +void CV_ThreshTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + test_threshold( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], + thresh_val, max_val, thresh_type ); +} + +TEST(Imgproc_Threshold, accuracy) { CV_ThreshTest test; test.safe_run(); } + diff --git a/modules/imgproc/test/test_watershed.cpp b/modules/imgproc/test/test_watershed.cpp new file mode 100644 index 000000000..7ab8fcb3d --- /dev/null +++ b/modules/imgproc/test/test_watershed.cpp @@ -0,0 +1,133 @@ +/*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 + +using namespace cv; +using namespace std; + +class CV_WatershedTest : public cvtest::BaseTest +{ +public: + CV_WatershedTest(); + ~CV_WatershedTest(); +protected: + void run(int); +}; + +CV_WatershedTest::CV_WatershedTest() {} +CV_WatershedTest::~CV_WatershedTest() {} + +void CV_WatershedTest::run( int /* start_from */) +{ + string exp_path = string(ts->get_data_path()) + "watershed/wshed_exp.png"; + Mat exp = imread(exp_path, 0); + Mat orig = imread(string(ts->get_data_path()) + "inpaint/orig.jpg"); + FileStorage fs(string(ts->get_data_path()) + "watershed/comp.xml", FileStorage::READ); + + if (orig.empty() || !fs.isOpened()) + { + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + CvSeq* cnts = (CvSeq*)fs["contours"].readObj(); + + Mat markers(orig.size(), CV_32SC1); + markers = Scalar(0); + IplImage iplmrks = markers; + + vector colors(1); + for(int i = 0; cnts != 0; cnts = cnts->h_next, ++i ) + { + cvDrawContours( &iplmrks, cnts, Scalar::all(i + 1), Scalar::all(i + 1), -1, CV_FILLED); + Point* p = (Point*)cvGetSeqElem(cnts, 0); + + //expected image was added with 1 in order to save to png + //so now we substract 1 to get real color + if(exp.data) + colors.push_back(exp.ptr(p->y)[p->x] - 1); + } + fs.release(); + const int compNum = (int)(colors.size() - 1); + + watershed(orig, markers); + + for(int j = 0; j < markers.rows; ++j) + { + int* line = markers.ptr(j); + for(int i = 0; i < markers.cols; ++i) + { + int& pixel = line[i]; + + if (pixel == -1) // border + continue; + + if (pixel <= 0 || pixel > compNum) + continue; // bad result, doing nothing and going to get error latter; + + // repaint in saved color to compare with expected; + if(exp.data) + pixel = colors[pixel]; + } + } + + Mat markers8U; + markers.convertTo(markers8U, CV_8U, 1, 1); + + if( exp.empty() || orig.size() != exp.size() ) + { + imwrite(exp_path, markers8U); + exp = markers8U; + } + + if (0 != norm(markers8U, exp, NORM_INF)) + { + ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH ); + return; + } + ts->set_failed_test_info(cvtest::TS::OK); +} + +TEST(Imgproc_Watershed, regression) { CV_WatershedTest test; test.safe_run(); } + diff --git a/modules/ml/test/test_emknearestkmeans.cpp b/modules/ml/test/test_emknearestkmeans.cpp new file mode 100644 index 000000000..e1d4360a4 --- /dev/null +++ b/modules/ml/test/test_emknearestkmeans.cpp @@ -0,0 +1,327 @@ +/*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" + +using namespace std; +using namespace cv; + +void defaultDistribs( vector& means, vector& covs ) +{ + float mp0[] = {0.0f, 0.0f}, cp0[] = {0.67f, 0.0f, 0.0f, 0.67f}; + float mp1[] = {5.0f, 0.0f}, cp1[] = {1.0f, 0.0f, 0.0f, 1.0f}; + float mp2[] = {1.0f, 5.0f}, cp2[] = {1.0f, 0.0f, 0.0f, 1.0f}; + Mat m0( 1, 2, CV_32FC1, mp0 ), c0( 2, 2, CV_32FC1, cp0 ); + Mat m1( 1, 2, CV_32FC1, mp1 ), c1( 2, 2, CV_32FC1, cp1 ); + Mat m2( 1, 2, CV_32FC1, mp2 ), c2( 2, 2, CV_32FC1, cp2 ); + means.resize(3), covs.resize(3); + m0.copyTo(means[0]), c0.copyTo(covs[0]); + m1.copyTo(means[1]), c1.copyTo(covs[1]); + m2.copyTo(means[2]), c2.copyTo(covs[2]); +} + +// generate points sets by normal distributions +void generateData( Mat& data, Mat& labels, const vector& sizes, const vector& means, const vector& covs, int labelType ) +{ + vector::const_iterator sit = sizes.begin(); + int total = 0; + for( ; sit != sizes.end(); ++sit ) + total += *sit; + assert( means.size() == sizes.size() && covs.size() == sizes.size() ); + assert( !data.empty() && data.rows == total ); + assert( data.type() == CV_32FC1 ); + + labels.create( data.rows, 1, labelType ); + + randn( data, Scalar::all(0.0), Scalar::all(1.0) ); + vector::const_iterator mit = means.begin(), cit = covs.begin(); + int bi, ei = 0; + sit = sizes.begin(); + for( int p = 0, l = 0; sit != sizes.end(); ++sit, ++mit, ++cit, l++ ) + { + bi = ei; + ei = bi + *sit; + assert( mit->rows == 1 && mit->cols == data.cols ); + assert( cit->rows == data.cols && cit->cols == data.cols ); + for( int i = bi; i < ei; i++, p++ ) + { + Mat r(1, data.cols, CV_32FC1, data.ptr(i)); + r = r * (*cit) + *mit; + if( labelType == CV_32FC1 ) + labels.at(p, 0) = (float)l; + else + labels.at(p, 0) = l; + } + } +} + +int maxIdx( const vector& count ) +{ + int idx = -1; + int maxVal = -1; + vector::const_iterator it = count.begin(); + for( int i = 0; it != count.end(); ++it, i++ ) + { + if( *it > maxVal) + { + maxVal = *it; + idx = i; + } + } + assert( idx >= 0); + return idx; +} + +bool getLabelsMap( const Mat& labels, const vector& sizes, vector& labelsMap ) +{ + int total = 0, setCount = (int)sizes.size(); + vector::const_iterator sit = sizes.begin(); + for( ; sit != sizes.end(); ++sit ) + total += *sit; + assert( !labels.empty() ); + assert( labels.rows == total && labels.cols == 1 ); + assert( labels.type() == CV_32SC1 || labels.type() == CV_32FC1 ); + + bool isFlt = labels.type() == CV_32FC1; + labelsMap.resize(setCount); + vector::iterator lmit = labelsMap.begin(); + vector buzy(setCount, false); + int bi, ei = 0; + for( sit = sizes.begin(); sit != sizes.end(); ++sit, ++lmit ) + { + vector count( setCount, 0 ); + bi = ei; + ei = bi + *sit; + if( isFlt ) + { + for( int i = bi; i < ei; i++ ) + count[(int)labels.at(i, 0)]++; + } + else + { + for( int i = bi; i < ei; i++ ) + count[labels.at(i, 0)]++; + } + + *lmit = maxIdx( count ); + if( buzy[*lmit] ) + return false; + buzy[*lmit] = true; + } + return true; +} + +float calcErr( const Mat& labels, const Mat& origLabels, const vector& sizes, bool labelsEquivalent = true ) +{ + int err = 0; + assert( !labels.empty() && !origLabels.empty() ); + assert( labels.cols == 1 && origLabels.cols == 1 ); + assert( labels.rows == origLabels.rows ); + assert( labels.type() == origLabels.type() ); + assert( labels.type() == CV_32SC1 || labels.type() == CV_32FC1 ); + + vector labelsMap; + bool isFlt = labels.type() == CV_32FC1; + if( !labelsEquivalent ) + { + getLabelsMap( labels, sizes, labelsMap ); + for( int i = 0; i < labels.rows; i++ ) + if( isFlt ) + err += labels.at(i, 0) != labelsMap[(int)origLabels.at(i, 0)]; + else + err += labels.at(i, 0) != labelsMap[origLabels.at(i, 0)]; + } + else + { + for( int i = 0; i < labels.rows; i++ ) + if( isFlt ) + err += labels.at(i, 0) != origLabels.at(i, 0); + else + err += labels.at(i, 0) != origLabels.at(i, 0); + } + return (float)err / (float)labels.rows; +} + +//-------------------------------------------------------------------------------------------- +class CV_KMeansTest : public cvtest::BaseTest { +public: + CV_KMeansTest() {} +protected: + virtual void run( int start_from ); +}; + +void CV_KMeansTest::run( int /*start_from*/ ) +{ + const int iters = 100; + int sizesArr[] = { 5000, 7000, 8000 }; + int pointsCount = sizesArr[0]+ sizesArr[1] + sizesArr[2]; + + Mat data( pointsCount, 2, CV_32FC1 ), labels; + vector sizes( sizesArr, sizesArr + sizeof(sizesArr) / sizeof(sizesArr[0]) ); + vector means, covs; + defaultDistribs( means, covs ); + generateData( data, labels, sizes, means, covs, CV_32SC1 ); + + int code = cvtest::TS::OK; + Mat bestLabels; + // 1. flag==KMEANS_PP_CENTERS + kmeans( data, 3, bestLabels, TermCriteria( TermCriteria::COUNT, iters, 0.0), 0, KMEANS_PP_CENTERS, 0 ); + if( calcErr( bestLabels, labels, sizes, false ) > 0.01f ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy if flag==KMEANS_PP_CENTERS" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + + // 2. flag==KMEANS_RANDOM_CENTERS + kmeans( data, 3, bestLabels, TermCriteria( TermCriteria::COUNT, iters, 0.0), 0, KMEANS_RANDOM_CENTERS, 0 ); + if( calcErr( bestLabels, labels, sizes, false ) > 0.01f ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy if flag==KMEANS_PP_CENTERS" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + + // 3. flag==KMEANS_USE_INITIAL_LABELS + labels.copyTo( bestLabels ); + RNG rng; + for( int i = 0; i < 0.5f * pointsCount; i++ ) + bestLabels.at( rng.next() % pointsCount, 0 ) = rng.next() % 3; + kmeans( data, 3, bestLabels, TermCriteria( TermCriteria::COUNT, iters, 0.0), 0, KMEANS_USE_INITIAL_LABELS, 0 ); + if( calcErr( bestLabels, labels, sizes, false ) > 0.01f ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy if flag==KMEANS_PP_CENTERS" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + + ts->set_failed_test_info( code ); +} + +//-------------------------------------------------------------------------------------------- +class CV_KNearestTest : public cvtest::BaseTest { +public: + CV_KNearestTest() {} +protected: + virtual void run( int start_from ); +}; + +void CV_KNearestTest::run( int /*start_from*/ ) +{ + int sizesArr[] = { 500, 700, 800 }; + int pointsCount = sizesArr[0]+ sizesArr[1] + sizesArr[2]; + + // train data + Mat trainData( pointsCount, 2, CV_32FC1 ), trainLabels; + vector sizes( sizesArr, sizesArr + sizeof(sizesArr) / sizeof(sizesArr[0]) ); + vector means, covs; + defaultDistribs( means, covs ); + generateData( trainData, trainLabels, sizes, means, covs, CV_32FC1 ); + + // test data + Mat testData( pointsCount, 2, CV_32FC1 ), testLabels, bestLabels; + generateData( testData, testLabels, sizes, means, covs, CV_32FC1 ); + + int code = cvtest::TS::OK; + KNearest knearest; + knearest.train( trainData, trainLabels ); + knearest.find_nearest( testData, 4, &bestLabels ); + if( calcErr( bestLabels, testLabels, sizes, true ) > 0.01f ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy on test data" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + ts->set_failed_test_info( code ); +} + +//-------------------------------------------------------------------------------------------- +class CV_EMTest : public cvtest::BaseTest { +public: + CV_EMTest() {} +protected: + virtual void run( int start_from ); +}; + +void CV_EMTest::run( int /*start_from*/ ) +{ + int sizesArr[] = { 5000, 7000, 8000 }; + int pointsCount = sizesArr[0]+ sizesArr[1] + sizesArr[2]; + + // train data + Mat trainData( pointsCount, 2, CV_32FC1 ), trainLabels; + vector sizes( sizesArr, sizesArr + sizeof(sizesArr) / sizeof(sizesArr[0]) ); + vector means, covs; + defaultDistribs( means, covs ); + generateData( trainData, trainLabels, sizes, means, covs, CV_32SC1 ); + + // test data + Mat testData( pointsCount, 2, CV_32FC1 ), testLabels, bestLabels; + generateData( testData, testLabels, sizes, means, covs, CV_32SC1 ); + + int code = cvtest::TS::OK; + ExpectationMaximization em; + CvEMParams params; + params.nclusters = 3; + em.train( trainData, Mat(), params, &bestLabels ); + + // check train error + if( calcErr( bestLabels, trainLabels, sizes, true ) > 0.002f ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy on train data" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + + // check test error + bestLabels.create( testData.rows, 1, CV_32SC1 ); + for( int i = 0; i < testData.rows; i++ ) + { + Mat sample( 1, testData.cols, CV_32FC1, testData.ptr(i)); + bestLabels.at(i,0) = (int)em.predict( sample, 0 ); + } + if( calcErr( bestLabels, testLabels, sizes, true ) > 0.005f ) + { + ts->printf( cvtest::TS::LOG, "bad accuracy on test data" ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + + ts->set_failed_test_info( code ); +} + +TEST(ML_KMeans, accuracy) { CV_KMeansTest test; test.safe_run(); } +TEST(ML_KNearest, accuracy) { CV_KNearestTest test; test.safe_run(); } +TEST(ML_EMTest, accuracy) { CV_EMTest test; test.safe_run(); } diff --git a/modules/ml/test/test_gbttest.cpp b/modules/ml/test/test_gbttest.cpp new file mode 100644 index 000000000..35951891a --- /dev/null +++ b/modules/ml/test/test_gbttest.cpp @@ -0,0 +1,275 @@ + +#include "test_precomp.hpp" + +#include +#include +#include + +using namespace std; + + +class CV_GBTreesTest : public cvtest::BaseTest +{ +public: + CV_GBTreesTest(); + ~CV_GBTreesTest(); + +protected: + void run(int); + + int TestTrainPredict(int test_num); + int TestSaveLoad(); + + int checkPredictError(int test_num); + int checkLoadSave(); + + //string model_file_name1; + //string model_file_name2; + char model_file_name1[50]; + char model_file_name2[50]; + string* datasets; + string data_path; + + CvMLData* data; + CvGBTrees* gtb; + + vector test_resps1; + vector test_resps2; +}; + + +int _get_len(const CvMat* mat) +{ + return (mat->cols > mat->rows) ? mat->cols : mat->rows; +} + + +CV_GBTreesTest::CV_GBTreesTest() +{ + datasets = 0; + data = 0; + gtb = 0; +} + +CV_GBTreesTest::~CV_GBTreesTest() +{ + if (data) + delete data; + delete[] datasets; +} + + +int CV_GBTreesTest::TestTrainPredict(int test_num) +{ + int code = cvtest::TS::OK; + + int weak_count = 200; + float shrinkage = 0.1f; + float subsample_portion = 0.5f; + int max_depth = 5; + bool use_surrogates = true; + int loss_function_type = 0; + switch (test_num) + { + case (1) : loss_function_type = CvGBTrees::SQUARED_LOSS; break; + case (2) : loss_function_type = CvGBTrees::ABSOLUTE_LOSS; break; + case (3) : loss_function_type = CvGBTrees::HUBER_LOSS; break; + case (0) : loss_function_type = CvGBTrees::DEVIANCE_LOSS; break; + default : + { + ts->printf( cvtest::TS::LOG, "Bad test_num value in CV_GBTreesTest::TestTrainPredict(..) function." ); + return cvtest::TS::FAIL_BAD_ARG_CHECK; + } + } + + int dataset_num = test_num == 0 ? 0 : 1; + if (!data) + { + data = new CvMLData(); + data->set_delimiter(','); + + if (data->read_csv(datasets[dataset_num].c_str())) + { + ts->printf( cvtest::TS::LOG, "File reading error." ); + return cvtest::TS::FAIL_INVALID_TEST_DATA; + } + + if (test_num == 0) + { + data->set_response_idx(57); + data->set_var_types("ord[0-56],cat[57]"); + } + else + { + data->set_response_idx(13); + data->set_var_types("ord[0-2,4-13],cat[3]"); + subsample_portion = 0.7f; + } + + int train_sample_count = cvFloor(_get_len(data->get_responses())*0.5f); + CvTrainTestSplit spl( train_sample_count ); + data->set_train_test_split( &spl ); + } + + data->mix_train_and_test_idx(); + + + if (gtb) delete gtb; + gtb = new CvGBTrees(); + bool tmp_code = true; + tmp_code = gtb->train(data, CvGBTreesParams(loss_function_type, weak_count, + shrinkage, subsample_portion, + max_depth, use_surrogates)); + + if (!tmp_code) + { + ts->printf( cvtest::TS::LOG, "Model training was failed."); + return cvtest::TS::FAIL_INVALID_OUTPUT; + } + + code = checkPredictError(test_num); + + return code; + +} + + +int CV_GBTreesTest::checkPredictError(int test_num) +{ + if (!gtb) + return cvtest::TS::FAIL_GENERIC; + + float mean[] = {5.430247f, 13.5654f, 12.6569f, 13.1661f}; + float sigma[] = {0.4162694f, 3.21161f, 3.43297f, 3.00624f}; + + float current_error = gtb->calc_error(data, CV_TEST_ERROR); + + if ( abs( current_error - mean[test_num]) > 6*sigma[test_num] ) + { + ts->printf( cvtest::TS::LOG, "Test error is out of range:\n" + "abs(%f/*curEr*/ - %f/*mean*/ > %f/*6*sigma*/", + current_error, mean[test_num], 6*sigma[test_num] ); + return cvtest::TS::FAIL_BAD_ACCURACY; + } + + return cvtest::TS::OK; + +} + + +int CV_GBTreesTest::TestSaveLoad() +{ + if (!gtb) + return cvtest::TS::FAIL_GENERIC; + + tmpnam(model_file_name1); + tmpnam(model_file_name2); + + if(model_file_name1[0] == '\\') + model_file_name1[0] = '_'; + if(model_file_name2[0] == '\\') + model_file_name2[0] = '_'; + + gtb->save(model_file_name1); + gtb->calc_error(data, CV_TEST_ERROR, &test_resps1); + gtb->load(model_file_name1); + gtb->calc_error(data, CV_TEST_ERROR, &test_resps2); + gtb->save(model_file_name2); + + return checkLoadSave(); + +} + + + +int CV_GBTreesTest::checkLoadSave() +{ + int code = cvtest::TS::OK; + + // 1. compare files + ifstream f1( model_file_name1 ), f2( model_file_name2 ); + string s1, s2; + int lineIdx = 0; + CV_Assert( f1.is_open() && f2.is_open() ); + for( ; !f1.eof() && !f2.eof(); lineIdx++ ) + { + getline( f1, s1 ); + getline( f2, s2 ); + if( s1.compare(s2) ) + { + ts->printf( cvtest::TS::LOG, "first and second saved files differ in %n-line; first %n line: %s; second %n-line: %s", + lineIdx, lineIdx, s1.c_str(), lineIdx, s2.c_str() ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + } + } + if( !f1.eof() || !f2.eof() ) + { + ts->printf( cvtest::TS::LOG, "First and second saved files differ in %n-line; first %n line: %s; second %n-line: %s", + lineIdx, lineIdx, s1.c_str(), lineIdx, s2.c_str() ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + } + f1.close(); + f2.close(); + // delete temporary files + remove( model_file_name1 ); + remove( model_file_name2 ); + + // 2. compare responses + CV_Assert( test_resps1.size() == test_resps2.size() ); + vector::const_iterator it1 = test_resps1.begin(), it2 = test_resps2.begin(); + for( ; it1 != test_resps1.end(); ++it1, ++it2 ) + { + if( fabs(*it1 - *it2) > FLT_EPSILON ) + { + ts->printf( cvtest::TS::LOG, "Responses predicted before saving and after loading are different" ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + } + } + return code; +} + + + +void CV_GBTreesTest::run(int) +{ + + string data_path = string(ts->get_data_path()); + datasets = new string[2]; + datasets[0] = data_path + string("spambase.data"); /*string("dataset_classification.csv");*/ + datasets[1] = data_path + string("housing_.data"); /*string("dataset_regression.csv");*/ + + int code = cvtest::TS::OK; + + for (int i = 0; i < 4; i++) + { + + int temp_code = TestTrainPredict(i); + if (temp_code != cvtest::TS::OK) + { + code = temp_code; + break; + } + + else if (i==0) + { + temp_code = TestSaveLoad(); + if (temp_code != cvtest::TS::OK) + code = temp_code; + delete data; + data = 0; + } + + delete gtb; + gtb = 0; + } + delete data; + data = 0; + + ts->set_failed_test_info( code ); +} + +///////////////////////////////////////////////////////////////////////////// +//////////////////// test registration ///////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +TEST(ML_GBTrees, regression) { CV_GBTreesTest test; test.safe_run(); } diff --git a/modules/ml/test/test_main.cpp b/modules/ml/test/test_main.cpp new file mode 100644 index 000000000..738458218 --- /dev/null +++ b/modules/ml/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("ml") diff --git a/modules/ml/test/test_mltests.cpp b/modules/ml/test/test_mltests.cpp new file mode 100644 index 000000000..fafe4f970 --- /dev/null +++ b/modules/ml/test/test_mltests.cpp @@ -0,0 +1,130 @@ +/*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" + +using namespace cv; +using namespace std; + +CV_AMLTest::CV_AMLTest( const char* _modelName ) : CV_MLBaseTest( _modelName ) +{ + validationFN = "avalidation.xml"; +} + +int CV_AMLTest::run_test_case( int testCaseIdx ) +{ + int code = cvtest::TS::OK; + code = prepare_test_case( testCaseIdx ); + + if (code == cvtest::TS::OK) + { + //#define GET_STAT +#ifdef GET_STAT + const char* data_name = ((CvFileNode*)cvGetSeqElem( dataSetNames, testCaseIdx ))->data.str.ptr; + printf("%s, %s ", name, data_name); + const int icount = 100; + float res[icount]; + for (int k = 0; k < icount; k++) + { +#endif + data.mix_train_and_test_idx(); + code = train( testCaseIdx ); +#ifdef GET_STAT + float case_result = get_error(); + + res[k] = case_result; + } + float mean = 0, sigma = 0; + for (int k = 0; k < icount; k++) + { + mean += res[k]; + } + mean = mean /icount; + for (int k = 0; k < icount; k++) + { + sigma += (res[k] - mean)*(res[k] - mean); + } + sigma = sqrt(sigma/icount); + printf("%f, %f\n", mean, sigma); +#endif + } + return code; +} + +int CV_AMLTest::validate_test_results( int testCaseIdx ) +{ + int iters; + float mean, sigma; + // read validation params + FileNode resultNode = + validationFS.getFirstTopLevelNode()["validation"][modelName][dataSetNames[testCaseIdx]]["result"]; + resultNode["iter_count"] >> iters; + if ( iters > 0) + { + resultNode["mean"] >> mean; + resultNode["sigma"] >> sigma; + float curErr = get_error( testCaseIdx, CV_TEST_ERROR ); + const int coeff = 4; + ts->printf( cvtest::TS::LOG, "Test case = %d; test error = %f; mean error = %f (diff=%f), %d*sigma = %f", + testCaseIdx, curErr, mean, abs( curErr - mean), coeff, coeff*sigma ); + if ( abs( curErr - mean) > coeff*sigma ) + { + ts->printf( cvtest::TS::LOG, "abs(%f - %f) > %f - OUT OF RANGE!\n", curErr, mean, coeff*sigma, coeff ); + return cvtest::TS::FAIL_BAD_ACCURACY; + } + else + ts->printf( cvtest::TS::LOG, ".\n" ); + + } + else + { + ts->printf( cvtest::TS::LOG, "validation info is not suitable" ); + return cvtest::TS::FAIL_INVALID_TEST_DATA; + } + return cvtest::TS::OK; +} + +TEST(ML_DTree, regression) { CV_AMLTest test( CV_DTREE ); test.safe_run(); } +TEST(ML_Boost, regression) { CV_AMLTest test( CV_BOOST ); test.safe_run(); } +TEST(ML_RTrees, regression) { CV_AMLTest test( CV_RTREES ); test.safe_run(); } +TEST(ML_ERTrees, regression) { CV_AMLTest test( CV_ERTREES ); test.safe_run(); } + +/* End of file. */ diff --git a/modules/ml/test/test_mltests2.cpp b/modules/ml/test/test_mltests2.cpp new file mode 100644 index 000000000..93c7220ff --- /dev/null +++ b/modules/ml/test/test_mltests2.cpp @@ -0,0 +1,796 @@ +/*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" + +using namespace cv; +using namespace std; + +// auxiliary functions +// 1. nbayes +void nbayes_check_data( CvMLData* _data ) +{ + if( _data->get_missing() ) + CV_Error( CV_StsBadArg, "missing values are not supported" ); + const CvMat* var_types = _data->get_var_types(); + bool is_classifier = var_types->data.ptr[var_types->cols-1] == CV_VAR_CATEGORICAL; + if( ( fabs( cvNorm( var_types, 0, CV_L1 ) - + (var_types->rows + var_types->cols - 2)*CV_VAR_ORDERED - CV_VAR_CATEGORICAL ) > FLT_EPSILON ) || + !is_classifier ) + CV_Error( CV_StsBadArg, "incorrect types of predictors or responses" ); +} +bool nbayes_train( CvNormalBayesClassifier* nbayes, CvMLData* _data ) +{ + nbayes_check_data( _data ); + const CvMat* values = _data->get_values(); + const CvMat* responses = _data->get_responses(); + const CvMat* train_sidx = _data->get_train_sample_idx(); + const CvMat* var_idx = _data->get_var_idx(); + return nbayes->train( values, responses, var_idx, train_sidx ); +} +float nbayes_calc_error( CvNormalBayesClassifier* nbayes, CvMLData* _data, int type, vector *resp ) +{ + float err = 0; + nbayes_check_data( _data ); + const CvMat* values = _data->get_values(); + const CvMat* response = _data->get_responses(); + const CvMat* sample_idx = (type == CV_TEST_ERROR) ? _data->get_test_sample_idx() : _data->get_train_sample_idx(); + int* sidx = sample_idx ? sample_idx->data.i : 0; + int r_step = CV_IS_MAT_CONT(response->type) ? + 1 : response->step / CV_ELEM_SIZE(response->type); + int sample_count = sample_idx ? sample_idx->cols : 0; + sample_count = (type == CV_TRAIN_ERROR && sample_count == 0) ? values->rows : sample_count; + float* pred_resp = 0; + if( resp && (sample_count > 0) ) + { + resp->resize( sample_count ); + pred_resp = &((*resp)[0]); + } + + for( int i = 0; i < sample_count; i++ ) + { + CvMat sample; + int si = sidx ? sidx[i] : i; + cvGetRow( values, &sample, si ); + float r = (float)nbayes->predict( &sample, 0 ); + if( pred_resp ) + pred_resp[i] = r; + int d = fabs((double)r - response->data.fl[si*r_step]) <= FLT_EPSILON ? 0 : 1; + err += d; + } + err = sample_count ? err / (float)sample_count * 100 : -FLT_MAX; + return err; +} + +// 2. knearest +void knearest_check_data_and_get_predictors( CvMLData* _data, CvMat* _predictors ) +{ + const CvMat* values = _data->get_values(); + const CvMat* var_idx = _data->get_var_idx(); + if( var_idx->cols + var_idx->rows != values->cols ) + CV_Error( CV_StsBadArg, "var_idx is not supported" ); + if( _data->get_missing() ) + CV_Error( CV_StsBadArg, "missing values are not supported" ); + int resp_idx = _data->get_response_idx(); + if( resp_idx == 0) + cvGetCols( values, _predictors, 1, values->cols ); + else if( resp_idx == values->cols - 1 ) + cvGetCols( values, _predictors, 0, values->cols - 1 ); + else + CV_Error( CV_StsBadArg, "responses must be in the first or last column; other cases are not supported" ); +} +bool knearest_train( CvKNearest* knearest, CvMLData* _data ) +{ + const CvMat* responses = _data->get_responses(); + const CvMat* train_sidx = _data->get_train_sample_idx(); + bool is_regression = _data->get_var_type( _data->get_response_idx() ) == CV_VAR_ORDERED; + CvMat predictors; + knearest_check_data_and_get_predictors( _data, &predictors ); + return knearest->train( &predictors, responses, train_sidx, is_regression ); +} +float knearest_calc_error( CvKNearest* knearest, CvMLData* _data, int k, int type, vector *resp ) +{ + float err = 0; + const CvMat* response = _data->get_responses(); + const CvMat* sample_idx = (type == CV_TEST_ERROR) ? _data->get_test_sample_idx() : _data->get_train_sample_idx(); + int* sidx = sample_idx ? sample_idx->data.i : 0; + int r_step = CV_IS_MAT_CONT(response->type) ? + 1 : response->step / CV_ELEM_SIZE(response->type); + bool is_regression = _data->get_var_type( _data->get_response_idx() ) == CV_VAR_ORDERED; + CvMat predictors; + knearest_check_data_and_get_predictors( _data, &predictors ); + int sample_count = sample_idx ? sample_idx->cols : 0; + sample_count = (type == CV_TRAIN_ERROR && sample_count == 0) ? predictors.rows : sample_count; + float* pred_resp = 0; + if( resp && (sample_count > 0) ) + { + resp->resize( sample_count ); + pred_resp = &((*resp)[0]); + } + if ( !is_regression ) + { + for( int i = 0; i < sample_count; i++ ) + { + CvMat sample; + int si = sidx ? sidx[i] : i; + cvGetRow( &predictors, &sample, si ); + float r = knearest->find_nearest( &sample, k ); + if( pred_resp ) + pred_resp[i] = r; + int d = fabs((double)r - response->data.fl[si*r_step]) <= FLT_EPSILON ? 0 : 1; + err += d; + } + err = sample_count ? err / (float)sample_count * 100 : -FLT_MAX; + } + else + { + for( int i = 0; i < sample_count; i++ ) + { + CvMat sample; + int si = sidx ? sidx[i] : i; + cvGetRow( &predictors, &sample, si ); + float r = knearest->find_nearest( &sample, k ); + if( pred_resp ) + pred_resp[i] = r; + float d = r - response->data.fl[si*r_step]; + err += d*d; + } + err = sample_count ? err / (float)sample_count : -FLT_MAX; + } + return err; +} + +// 3. svm +int str_to_svm_type(string& str) +{ + if( !str.compare("C_SVC") ) + return CvSVM::C_SVC; + if( !str.compare("NU_SVC") ) + return CvSVM::NU_SVC; + if( !str.compare("ONE_CLASS") ) + return CvSVM::ONE_CLASS; + if( !str.compare("EPS_SVR") ) + return CvSVM::EPS_SVR; + if( !str.compare("NU_SVR") ) + return CvSVM::NU_SVR; + CV_Error( CV_StsBadArg, "incorrect svm type string" ); + return -1; +} +int str_to_svm_kernel_type( string& str ) +{ + if( !str.compare("LINEAR") ) + return CvSVM::LINEAR; + if( !str.compare("POLY") ) + return CvSVM::POLY; + if( !str.compare("RBF") ) + return CvSVM::RBF; + if( !str.compare("SIGMOID") ) + return CvSVM::SIGMOID; + CV_Error( CV_StsBadArg, "incorrect svm type string" ); + return -1; +} +void svm_check_data( CvMLData* _data ) +{ + if( _data->get_missing() ) + CV_Error( CV_StsBadArg, "missing values are not supported" ); + const CvMat* var_types = _data->get_var_types(); + for( int i = 0; i < var_types->cols-1; i++ ) + if (var_types->data.ptr[i] == CV_VAR_CATEGORICAL) + { + char msg[50]; + sprintf( msg, "incorrect type of %d-predictor", i ); + CV_Error( CV_StsBadArg, msg ); + } +} +bool svm_train( CvSVM* svm, CvMLData* _data, CvSVMParams _params ) +{ + svm_check_data(_data); + const CvMat* _train_data = _data->get_values(); + const CvMat* _responses = _data->get_responses(); + const CvMat* _var_idx = _data->get_var_idx(); + const CvMat* _sample_idx = _data->get_train_sample_idx(); + return svm->train( _train_data, _responses, _var_idx, _sample_idx, _params ); +} +bool svm_train_auto( CvSVM* svm, CvMLData* _data, CvSVMParams _params, + int k_fold, CvParamGrid C_grid, CvParamGrid gamma_grid, + CvParamGrid p_grid, CvParamGrid nu_grid, CvParamGrid coef_grid, + CvParamGrid degree_grid ) +{ + svm_check_data(_data); + const CvMat* _train_data = _data->get_values(); + const CvMat* _responses = _data->get_responses(); + const CvMat* _var_idx = _data->get_var_idx(); + const CvMat* _sample_idx = _data->get_train_sample_idx(); + return svm->train_auto( _train_data, _responses, _var_idx, + _sample_idx, _params, k_fold, C_grid, gamma_grid, p_grid, nu_grid, coef_grid, degree_grid ); +} +float svm_calc_error( CvSVM* svm, CvMLData* _data, int type, vector *resp ) +{ + svm_check_data(_data); + float err = 0; + const CvMat* values = _data->get_values(); + const CvMat* response = _data->get_responses(); + const CvMat* sample_idx = (type == CV_TEST_ERROR) ? _data->get_test_sample_idx() : _data->get_train_sample_idx(); + const CvMat* var_types = _data->get_var_types(); + int* sidx = sample_idx ? sample_idx->data.i : 0; + int r_step = CV_IS_MAT_CONT(response->type) ? + 1 : response->step / CV_ELEM_SIZE(response->type); + bool is_classifier = var_types->data.ptr[var_types->cols-1] == CV_VAR_CATEGORICAL; + int sample_count = sample_idx ? sample_idx->cols : 0; + sample_count = (type == CV_TRAIN_ERROR && sample_count == 0) ? values->rows : sample_count; + float* pred_resp = 0; + if( resp && (sample_count > 0) ) + { + resp->resize( sample_count ); + pred_resp = &((*resp)[0]); + } + if ( is_classifier ) + { + for( int i = 0; i < sample_count; i++ ) + { + CvMat sample; + int si = sidx ? sidx[i] : i; + cvGetRow( values, &sample, si ); + float r = svm->predict( &sample ); + if( pred_resp ) + pred_resp[i] = r; + int d = fabs((double)r - response->data.fl[si*r_step]) <= FLT_EPSILON ? 0 : 1; + err += d; + } + err = sample_count ? err / (float)sample_count * 100 : -FLT_MAX; + } + else + { + for( int i = 0; i < sample_count; i++ ) + { + CvMat sample; + int si = sidx ? sidx[i] : i; + cvGetRow( values, &sample, si ); + float r = svm->predict( &sample ); + if( pred_resp ) + pred_resp[i] = r; + float d = r - response->data.fl[si*r_step]; + err += d*d; + } + err = sample_count ? err / (float)sample_count : -FLT_MAX; + } + return err; +} + +// 4. em +// 5. ann +int str_to_ann_train_method( string& str ) +{ + if( !str.compare("BACKPROP") ) + return CvANN_MLP_TrainParams::BACKPROP; + if( !str.compare("RPROP") ) + return CvANN_MLP_TrainParams::RPROP; + CV_Error( CV_StsBadArg, "incorrect ann train method string" ); + return -1; +} +void ann_check_data_and_get_predictors( CvMLData* _data, CvMat* _inputs ) +{ + const CvMat* values = _data->get_values(); + const CvMat* var_idx = _data->get_var_idx(); + if( var_idx->cols + var_idx->rows != values->cols ) + CV_Error( CV_StsBadArg, "var_idx is not supported" ); + if( _data->get_missing() ) + CV_Error( CV_StsBadArg, "missing values are not supported" ); + int resp_idx = _data->get_response_idx(); + if( resp_idx == 0) + cvGetCols( values, _inputs, 1, values->cols ); + else if( resp_idx == values->cols - 1 ) + cvGetCols( values, _inputs, 0, values->cols - 1 ); + else + CV_Error( CV_StsBadArg, "outputs must be in the first or last column; other cases are not supported" ); +} +void ann_get_new_responses( CvMLData* _data, Mat& new_responses, map& cls_map ) +{ + const CvMat* train_sidx = _data->get_train_sample_idx(); + int* train_sidx_ptr = train_sidx->data.i; + const CvMat* responses = _data->get_responses(); + float* responses_ptr = responses->data.fl; + int r_step = CV_IS_MAT_CONT(responses->type) ? + 1 : responses->step / CV_ELEM_SIZE(responses->type); + int cls_count = 0; + // construct cls_map + cls_map.clear(); + for( int si = 0; si < train_sidx->cols; si++ ) + { + int sidx = train_sidx_ptr[si]; + int r = cvRound(responses_ptr[sidx*r_step]); + CV_DbgAssert( fabs(responses_ptr[sidx*r_step]-r) < FLT_EPSILON ); + int cls_map_size = (int)cls_map.size(); + cls_map[r]; + if ( (int)cls_map.size() > cls_map_size ) + cls_map[r] = cls_count++; + } + new_responses.create( responses->rows, cls_count, CV_32F ); + new_responses.setTo( 0 ); + for( int si = 0; si < train_sidx->cols; si++ ) + { + int sidx = train_sidx_ptr[si]; + int r = cvRound(responses_ptr[sidx*r_step]); + int cidx = cls_map[r]; + new_responses.ptr(sidx)[cidx] = 1; + } +} +int ann_train( CvANN_MLP* ann, CvMLData* _data, Mat& new_responses, CvANN_MLP_TrainParams _params, int flags = 0 ) +{ + const CvMat* train_sidx = _data->get_train_sample_idx(); + CvMat predictors; + ann_check_data_and_get_predictors( _data, &predictors ); + CvMat _new_responses = CvMat( new_responses ); + return ann->train( &predictors, &_new_responses, 0, train_sidx, _params, flags ); +} +float ann_calc_error( CvANN_MLP* ann, CvMLData* _data, map& cls_map, int type , vector *resp_labels ) +{ + float err = 0; + const CvMat* responses = _data->get_responses(); + const CvMat* sample_idx = (type == CV_TEST_ERROR) ? _data->get_test_sample_idx() : _data->get_train_sample_idx(); + int* sidx = sample_idx ? sample_idx->data.i : 0; + int r_step = CV_IS_MAT_CONT(responses->type) ? + 1 : responses->step / CV_ELEM_SIZE(responses->type); + CvMat predictors; + ann_check_data_and_get_predictors( _data, &predictors ); + int sample_count = sample_idx ? sample_idx->cols : 0; + sample_count = (type == CV_TRAIN_ERROR && sample_count == 0) ? predictors.rows : sample_count; + float* pred_resp = 0; + vector innresp; + if( sample_count > 0 ) + { + if( resp_labels ) + { + resp_labels->resize( sample_count ); + pred_resp = &((*resp_labels)[0]); + } + else + { + innresp.resize( sample_count ); + pred_resp = &(innresp[0]); + } + } + int cls_count = (int)cls_map.size(); + Mat output( 1, cls_count, CV_32FC1 ); + CvMat _output = CvMat(output); + map::iterator b_it = cls_map.begin(); + for( int i = 0; i < sample_count; i++ ) + { + CvMat sample; + int si = sidx ? sidx[i] : i; + cvGetRow( &predictors, &sample, si ); + ann->predict( &sample, &_output ); + CvPoint best_cls = {0,0}; + cvMinMaxLoc( &_output, 0, 0, 0, &best_cls, 0 ); + int r = cvRound(responses->data.fl[si*r_step]); + CV_DbgAssert( fabs(responses->data.fl[si*r_step]-r) < FLT_EPSILON ); + r = cls_map[r]; + int d = best_cls.x == r ? 0 : 1; + err += d; + pred_resp[i] = (float)best_cls.x; + } + err = sample_count ? err / (float)sample_count * 100 : -FLT_MAX; + return err; +} + +// 6. dtree +// 7. boost +int str_to_boost_type( string& str ) +{ + if ( !str.compare("DISCRETE") ) + return CvBoost::DISCRETE; + if ( !str.compare("REAL") ) + return CvBoost::REAL; + if ( !str.compare("LOGIT") ) + return CvBoost::LOGIT; + if ( !str.compare("GENTLE") ) + return CvBoost::GENTLE; + CV_Error( CV_StsBadArg, "incorrect boost type string" ); + return -1; +} + +// 8. rtrees +// 9. ertrees + +// ---------------------------------- MLBaseTest --------------------------------------------------- + +CV_MLBaseTest::CV_MLBaseTest(const char* _modelName) +{ + int64 seeds[] = { CV_BIG_INT(0x00009fff4f9c8d52), + CV_BIG_INT(0x0000a17166072c7c), + CV_BIG_INT(0x0201b32115cd1f9a), + CV_BIG_INT(0x0513cb37abcd1234), + CV_BIG_INT(0x0001a2b3c4d5f678) + }; + + int seedCount = sizeof(seeds)/sizeof(seeds[0]); + RNG& rng = theRNG(); + + initSeed = rng.state; + + rng.state = seeds[rng(seedCount)]; + + modelName = _modelName; + nbayes = 0; + knearest = 0; + svm = 0; + em = 0; + ann = 0; + dtree = 0; + boost = 0; + rtrees = 0; + ertrees = 0; + if( !modelName.compare(CV_NBAYES) ) + nbayes = new CvNormalBayesClassifier; + else if( !modelName.compare(CV_KNEAREST) ) + knearest = new CvKNearest; + else if( !modelName.compare(CV_SVM) ) + svm = new CvSVM; + else if( !modelName.compare(CV_EM) ) + em = new CvEM; + else if( !modelName.compare(CV_ANN) ) + ann = new CvANN_MLP; + else if( !modelName.compare(CV_DTREE) ) + dtree = new CvDTree; + else if( !modelName.compare(CV_BOOST) ) + boost = new CvBoost; + else if( !modelName.compare(CV_RTREES) ) + rtrees = new CvRTrees; + else if( !modelName.compare(CV_ERTREES) ) + ertrees = new CvERTrees; +} + +CV_MLBaseTest::~CV_MLBaseTest() +{ + if( validationFS.isOpened() ) + validationFS.release(); + if( nbayes ) + delete nbayes; + if( knearest ) + delete knearest; + if( svm ) + delete svm; + if( em ) + delete em; + if( ann ) + delete ann; + if( dtree ) + delete dtree; + if( boost ) + delete boost; + if( rtrees ) + delete rtrees; + if( ertrees ) + delete ertrees; + theRNG().state = initSeed; +} + +int CV_MLBaseTest::read_params( CvFileStorage* _fs ) +{ + if( !_fs ) + test_case_count = -1; + else + { + CvFileNode* fn = cvGetRootFileNode( _fs, 0 ); + fn = (CvFileNode*)cvGetSeqElem( fn->data.seq, 0 ); + fn = cvGetFileNodeByName( _fs, fn, "run_params" ); + CvSeq* dataSetNamesSeq = cvGetFileNodeByName( _fs, fn, modelName.c_str() )->data.seq; + test_case_count = dataSetNamesSeq ? dataSetNamesSeq->total : -1; + if( test_case_count > 0 ) + { + dataSetNames.resize( test_case_count ); + vector::iterator it = dataSetNames.begin(); + for( int i = 0; i < test_case_count; i++, it++ ) + *it = ((CvFileNode*)cvGetSeqElem( dataSetNamesSeq, i ))->data.str.ptr; + } + } + return cvtest::TS::OK;; +} + +void CV_MLBaseTest::run( int start_from ) +{ + string filename = ts->get_data_path(); + filename += get_validation_filename(); + validationFS.open( filename, FileStorage::READ ); + read_params( *validationFS ); + + int code = cvtest::TS::OK; + start_from = 0; + for (int i = 0; i < test_case_count; i++) + { + int temp_code = run_test_case( i ); + if (temp_code == cvtest::TS::OK) + temp_code = validate_test_results( i ); + if (temp_code != cvtest::TS::OK) + code = temp_code; + } + if ( test_case_count <= 0) + { + ts->printf( cvtest::TS::LOG, "validation file is not determined or not correct" ); + code = cvtest::TS::FAIL_INVALID_TEST_DATA; + } + ts->set_failed_test_info( code ); +} + +int CV_MLBaseTest::prepare_test_case( int test_case_idx ) +{ + int trainSampleCount, respIdx; + string varTypes; + clear(); + + string dataPath = ts->get_data_path(); + if ( dataPath.empty() ) + { + ts->printf( cvtest::TS::LOG, "data path is empty" ); + return cvtest::TS::FAIL_INVALID_TEST_DATA; + } + + string dataName = dataSetNames[test_case_idx], + filename = dataPath + dataName + ".data"; + if ( data.read_csv( filename.c_str() ) != 0) + { + char msg[100]; + sprintf( msg, "file %s can not be read", filename.c_str() ); + ts->printf( cvtest::TS::LOG, msg ); + return cvtest::TS::FAIL_INVALID_TEST_DATA; + } + + FileNode dataParamsNode = validationFS.getFirstTopLevelNode()["validation"][modelName][dataName]["data_params"]; + CV_DbgAssert( !dataParamsNode.empty() ); + + CV_DbgAssert( !dataParamsNode["LS"].empty() ); + dataParamsNode["LS"] >> trainSampleCount; + CvTrainTestSplit spl( trainSampleCount ); + data.set_train_test_split( &spl ); + + CV_DbgAssert( !dataParamsNode["resp_idx"].empty() ); + dataParamsNode["resp_idx"] >> respIdx; + data.set_response_idx( respIdx ); + + CV_DbgAssert( !dataParamsNode["types"].empty() ); + dataParamsNode["types"] >> varTypes; + data.set_var_types( varTypes.c_str() ); + + return cvtest::TS::OK; +} + +string& CV_MLBaseTest::get_validation_filename() +{ + return validationFN; +} + +int CV_MLBaseTest::train( int testCaseIdx ) +{ + bool is_trained = false; + FileNode modelParamsNode = + validationFS.getFirstTopLevelNode()["validation"][modelName][dataSetNames[testCaseIdx]]["model_params"]; + + if( !modelName.compare(CV_NBAYES) ) + is_trained = nbayes_train( nbayes, &data ); + else if( !modelName.compare(CV_KNEAREST) ) + { + assert( 0 ); + //is_trained = knearest->train( &data ); + } + else if( !modelName.compare(CV_SVM) ) + { + string svm_type_str, kernel_type_str; + modelParamsNode["svm_type"] >> svm_type_str; + modelParamsNode["kernel_type"] >> kernel_type_str; + CvSVMParams params; + params.svm_type = str_to_svm_type( svm_type_str ); + params.kernel_type = str_to_svm_kernel_type( kernel_type_str ); + modelParamsNode["degree"] >> params.degree; + modelParamsNode["gamma"] >> params.gamma; + modelParamsNode["coef0"] >> params.coef0; + modelParamsNode["C"] >> params.C; + modelParamsNode["nu"] >> params.nu; + modelParamsNode["p"] >> params.p; + is_trained = svm_train( svm, &data, params ); + } + else if( !modelName.compare(CV_EM) ) + { + assert( 0 ); + } + else if( !modelName.compare(CV_ANN) ) + { + string train_method_str; + double param1, param2; + modelParamsNode["train_method"] >> train_method_str; + modelParamsNode["param1"] >> param1; + modelParamsNode["param2"] >> param2; + Mat new_responses; + ann_get_new_responses( &data, new_responses, cls_map ); + int layer_sz[] = { data.get_values()->cols - 1, 100, 100, (int)cls_map.size() }; + CvMat layer_sizes = + cvMat( 1, (int)(sizeof(layer_sz)/sizeof(layer_sz[0])), CV_32S, layer_sz ); + ann->create( &layer_sizes ); + is_trained = ann_train( ann, &data, new_responses, CvANN_MLP_TrainParams(cvTermCriteria(CV_TERMCRIT_ITER,300,0.01), + str_to_ann_train_method(train_method_str), param1, param2) ) >= 0; + } + else if( !modelName.compare(CV_DTREE) ) + { + int MAX_DEPTH, MIN_SAMPLE_COUNT, MAX_CATEGORIES, CV_FOLDS; + float REG_ACCURACY = 0; + bool USE_SURROGATE, IS_PRUNED; + modelParamsNode["max_depth"] >> MAX_DEPTH; + modelParamsNode["min_sample_count"] >> MIN_SAMPLE_COUNT; + modelParamsNode["use_surrogate"] >> USE_SURROGATE; + modelParamsNode["max_categories"] >> MAX_CATEGORIES; + modelParamsNode["cv_folds"] >> CV_FOLDS; + modelParamsNode["is_pruned"] >> IS_PRUNED; + is_trained = dtree->train( &data, + CvDTreeParams(MAX_DEPTH, MIN_SAMPLE_COUNT, REG_ACCURACY, USE_SURROGATE, + MAX_CATEGORIES, CV_FOLDS, false, IS_PRUNED, 0 )) != 0; + } + else if( !modelName.compare(CV_BOOST) ) + { + int BOOST_TYPE, WEAK_COUNT, MAX_DEPTH; + float WEIGHT_TRIM_RATE; + bool USE_SURROGATE; + string typeStr; + modelParamsNode["type"] >> typeStr; + BOOST_TYPE = str_to_boost_type( typeStr ); + modelParamsNode["weak_count"] >> WEAK_COUNT; + modelParamsNode["weight_trim_rate"] >> WEIGHT_TRIM_RATE; + modelParamsNode["max_depth"] >> MAX_DEPTH; + modelParamsNode["use_surrogate"] >> USE_SURROGATE; + is_trained = boost->train( &data, + CvBoostParams(BOOST_TYPE, WEAK_COUNT, WEIGHT_TRIM_RATE, MAX_DEPTH, USE_SURROGATE, 0) ) != 0; + } + else if( !modelName.compare(CV_RTREES) ) + { + int MAX_DEPTH, MIN_SAMPLE_COUNT, MAX_CATEGORIES, CV_FOLDS, NACTIVE_VARS, MAX_TREES_NUM; + float REG_ACCURACY = 0, OOB_EPS = 0.0; + bool USE_SURROGATE, IS_PRUNED; + modelParamsNode["max_depth"] >> MAX_DEPTH; + modelParamsNode["min_sample_count"] >> MIN_SAMPLE_COUNT; + modelParamsNode["use_surrogate"] >> USE_SURROGATE; + modelParamsNode["max_categories"] >> MAX_CATEGORIES; + modelParamsNode["cv_folds"] >> CV_FOLDS; + modelParamsNode["is_pruned"] >> IS_PRUNED; + modelParamsNode["nactive_vars"] >> NACTIVE_VARS; + modelParamsNode["max_trees_num"] >> MAX_TREES_NUM; + is_trained = rtrees->train( &data, CvRTParams( MAX_DEPTH, MIN_SAMPLE_COUNT, REG_ACCURACY, + USE_SURROGATE, MAX_CATEGORIES, 0, true, // (calc_var_importance == true) <=> RF processes variable importance + NACTIVE_VARS, MAX_TREES_NUM, OOB_EPS, CV_TERMCRIT_ITER)) != 0; + } + else if( !modelName.compare(CV_ERTREES) ) + { + int MAX_DEPTH, MIN_SAMPLE_COUNT, MAX_CATEGORIES, CV_FOLDS, NACTIVE_VARS, MAX_TREES_NUM; + float REG_ACCURACY = 0, OOB_EPS = 0.0; + bool USE_SURROGATE, IS_PRUNED; + modelParamsNode["max_depth"] >> MAX_DEPTH; + modelParamsNode["min_sample_count"] >> MIN_SAMPLE_COUNT; + modelParamsNode["use_surrogate"] >> USE_SURROGATE; + modelParamsNode["max_categories"] >> MAX_CATEGORIES; + modelParamsNode["cv_folds"] >> CV_FOLDS; + modelParamsNode["is_pruned"] >> IS_PRUNED; + modelParamsNode["nactive_vars"] >> NACTIVE_VARS; + modelParamsNode["max_trees_num"] >> MAX_TREES_NUM; + is_trained = ertrees->train( &data, CvRTParams( MAX_DEPTH, MIN_SAMPLE_COUNT, REG_ACCURACY, + USE_SURROGATE, MAX_CATEGORIES, 0, false, // (calc_var_importance == true) <=> RF processes variable importance + NACTIVE_VARS, MAX_TREES_NUM, OOB_EPS, CV_TERMCRIT_ITER)) != 0; + } + + if( !is_trained ) + { + ts->printf( cvtest::TS::LOG, "in test case %d model training was failed", testCaseIdx ); + return cvtest::TS::FAIL_INVALID_OUTPUT; + } + return cvtest::TS::OK; +} + +float CV_MLBaseTest::get_error( int testCaseIdx, int type, vector *resp ) +{ + float err = 0; + if( !modelName.compare(CV_NBAYES) ) + err = nbayes_calc_error( nbayes, &data, type, resp ); + else if( !modelName.compare(CV_KNEAREST) ) + { + assert( 0 ); + testCaseIdx = 0; + /*int k = 2; + validationFS.getFirstTopLevelNode()["validation"][modelName][dataSetNames[testCaseIdx]]["model_params"]["k"] >> k; + err = knearest->calc_error( &data, k, type, resp );*/ + } + else if( !modelName.compare(CV_SVM) ) + err = svm_calc_error( svm, &data, type, resp ); + else if( !modelName.compare(CV_EM) ) + assert( 0 ); + else if( !modelName.compare(CV_ANN) ) + err = ann_calc_error( ann, &data, cls_map, type, resp ); + else if( !modelName.compare(CV_DTREE) ) + err = dtree->calc_error( &data, type, resp ); + else if( !modelName.compare(CV_BOOST) ) + err = boost->calc_error( &data, type, resp ); + else if( !modelName.compare(CV_RTREES) ) + err = rtrees->calc_error( &data, type, resp ); + else if( !modelName.compare(CV_ERTREES) ) + err = ertrees->calc_error( &data, type, resp ); + return err; +} + +void CV_MLBaseTest::save( const char* filename ) +{ + if( !modelName.compare(CV_NBAYES) ) + nbayes->save( filename ); + else if( !modelName.compare(CV_KNEAREST) ) + knearest->save( filename ); + else if( !modelName.compare(CV_SVM) ) + svm->save( filename ); + else if( !modelName.compare(CV_EM) ) + em->save( filename ); + else if( !modelName.compare(CV_ANN) ) + ann->save( filename ); + else if( !modelName.compare(CV_DTREE) ) + dtree->save( filename ); + else if( !modelName.compare(CV_BOOST) ) + boost->save( filename ); + else if( !modelName.compare(CV_RTREES) ) + rtrees->save( filename ); + else if( !modelName.compare(CV_ERTREES) ) + ertrees->save( filename ); +} + +void CV_MLBaseTest::load( const char* filename ) +{ + if( !modelName.compare(CV_NBAYES) ) + nbayes->load( filename ); + else if( !modelName.compare(CV_KNEAREST) ) + knearest->load( filename ); + else if( !modelName.compare(CV_SVM) ) + svm->load( filename ); + else if( !modelName.compare(CV_EM) ) + em->load( filename ); + else if( !modelName.compare(CV_ANN) ) + ann->load( filename ); + else if( !modelName.compare(CV_DTREE) ) + dtree->load( filename ); + else if( !modelName.compare(CV_BOOST) ) + boost->load( filename ); + else if( !modelName.compare(CV_RTREES) ) + rtrees->load( filename ); + else if( !modelName.compare(CV_ERTREES) ) + ertrees->load( filename ); +} + +/* End of file. */ diff --git a/modules/ml/test/test_precomp.cpp b/modules/ml/test/test_precomp.cpp new file mode 100644 index 000000000..5956e13e3 --- /dev/null +++ b/modules/ml/test/test_precomp.cpp @@ -0,0 +1 @@ +#include "test_precomp.hpp" diff --git a/modules/ml/test/test_precomp.hpp b/modules/ml/test/test_precomp.hpp new file mode 100644 index 000000000..7a9083dfc --- /dev/null +++ b/modules/ml/test/test_precomp.hpp @@ -0,0 +1,80 @@ +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/ml/ml.hpp" +#include "opencv2/core/core_c.h" +#include +#include + +#define CV_NBAYES "nbayes" +#define CV_KNEAREST "knearest" +#define CV_SVM "svm" +#define CV_EM "em" +#define CV_ANN "ann" +#define CV_DTREE "dtree" +#define CV_BOOST "boost" +#define CV_RTREES "rtrees" +#define CV_ERTREES "ertrees" + +class CV_MLBaseTest : public cvtest::BaseTest +{ +public: + CV_MLBaseTest( const char* _modelName ); + virtual ~CV_MLBaseTest(); +protected: + virtual int read_params( CvFileStorage* fs ); + virtual void run( int startFrom ); + virtual int prepare_test_case( int testCaseIdx ); + virtual std::string& get_validation_filename(); + virtual int run_test_case( int testCaseIdx ) = 0; + virtual int validate_test_results( int testCaseIdx ) = 0; + + int train( int testCaseIdx ); + float get_error( int testCaseIdx, int type, std::vector *resp = 0 ); + void save( const char* filename ); + void load( const char* filename ); + + CvMLData data; + std::string modelName, validationFN; + std::vector dataSetNames; + cv::FileStorage validationFS; + + // MLL models + CvNormalBayesClassifier* nbayes; + CvKNearest* knearest; + CvSVM* svm; + CvEM* em; + CvANN_MLP* ann; + CvDTree* dtree; + CvBoost* boost; + CvRTrees* rtrees; + CvERTrees* ertrees; + + std::map cls_map; + + int64 initSeed; +}; + +class CV_AMLTest : public CV_MLBaseTest +{ +public: + CV_AMLTest( const char* _modelName ); +protected: + virtual int run_test_case( int testCaseIdx ); + virtual int validate_test_results( int testCaseIdx ); +}; + +class CV_SLMLTest : public CV_MLBaseTest +{ +public: + CV_SLMLTest( const char* _modelName ); +protected: + virtual int run_test_case( int testCaseIdx ); + virtual int validate_test_results( int testCaseIdx ); + + std::vector test_resps1, test_resps2; // predicted responses for test data + char fname1[50], fname2[50]; +}; + +#endif diff --git a/modules/ml/test/test_save_load.cpp b/modules/ml/test/test_save_load.cpp new file mode 100644 index 000000000..806a6aa13 --- /dev/null +++ b/modules/ml/test/test_save_load.cpp @@ -0,0 +1,138 @@ +/*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 +#include + +using namespace cv; +using namespace std; + +CV_SLMLTest::CV_SLMLTest( const char* _modelName ) : CV_MLBaseTest( _modelName ) +{ + validationFN = "slvalidation.xml"; +} + +int CV_SLMLTest::run_test_case( int testCaseIdx ) +{ + int code = cvtest::TS::OK; + code = prepare_test_case( testCaseIdx ); + + if( code == cvtest::TS::OK ) + { + data.mix_train_and_test_idx(); + code = train( testCaseIdx ); + if( code == cvtest::TS::OK ) + { + get_error( testCaseIdx, CV_TEST_ERROR, &test_resps1 ); + tmpnam(fname1); + if(fname1[0] == '\\') fname1[0] = '_'; + save( fname1 ); + load( fname1); + get_error( testCaseIdx, CV_TEST_ERROR, &test_resps2 ); + tmpnam(fname2); + if(fname2[0] == '\\') fname2[0] = '_'; + save( fname2 ); + } + else + ts->printf( cvtest::TS::LOG, "model can not be trained" ); + } + return code; +} + +int CV_SLMLTest::validate_test_results( int testCaseIdx ) +{ + int code = cvtest::TS::OK; + + // 1. compare files + ifstream f1( fname1 ), f2( fname2 ); + string s1, s2; + int lineIdx = 0; + CV_Assert( f1.is_open() && f2.is_open() ); + for( ; !f1.eof() && !f2.eof(); lineIdx++ ) + { + getline( f1, s1 ); + getline( f2, s2 ); + if( s1.compare(s2) ) + { + ts->printf( cvtest::TS::LOG, "first and second saved files differ in %n-line; first %n line: %s; second %n-line: %s", + lineIdx, lineIdx, s1.c_str(), lineIdx, s2.c_str() ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + } + } + if( !f1.eof() || !f2.eof() ) + { + ts->printf( cvtest::TS::LOG, "in test case %d first and second saved files differ in %n-line; first %n line: %s; second %n-line: %s", + testCaseIdx, lineIdx, lineIdx, s1.c_str(), lineIdx, s2.c_str() ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + } + f1.close(); + f2.close(); + // delete temporary files + remove( fname1 ); + remove( fname2 ); + + // 2. compare responses + CV_Assert( test_resps1.size() == test_resps2.size() ); + vector::const_iterator it1 = test_resps1.begin(), it2 = test_resps2.begin(); + for( ; it1 != test_resps1.end(); ++it1, ++it2 ) + { + if( fabs(*it1 - *it2) > FLT_EPSILON ) + { + ts->printf( cvtest::TS::LOG, "in test case %d responses predicted before saving and after loading is different", testCaseIdx ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + } + } + return code; +} + +TEST(ML_NaiveBayes, save_load) { CV_SLMLTest test( CV_NBAYES ); test.safe_run(); } +//CV_SLMLTest lsmlknearest( CV_KNEAREST, "slknearest" ); // does not support save! +TEST(ML_SVM, save_load) { CV_SLMLTest test( CV_SVM ); test.safe_run(); } +//CV_SLMLTest lsmlem( CV_EM, "slem" ); // does not support save! +TEST(ML_ANN, save_load) { CV_SLMLTest test( CV_ANN ); test.safe_run(); } +TEST(ML_DTree, save_load) { CV_SLMLTest test( CV_DTREE ); test.safe_run(); } +TEST(ML_Boost, save_load) { CV_SLMLTest test( CV_BOOST ); test.safe_run(); } +TEST(ML_RTrees, save_load) { CV_SLMLTest test( CV_RTREES ); test.safe_run(); } +TEST(ML_ERTrees, save_load) { CV_SLMLTest test( CV_ERTREES ); test.safe_run(); } + +/* End of file. */ diff --git a/modules/objdetect/test/test_cascadeandhog.cpp b/modules/objdetect/test/test_cascadeandhog.cpp new file mode 100644 index 000000000..dd7f0605d --- /dev/null +++ b/modules/objdetect/test/test_cascadeandhog.cpp @@ -0,0 +1,465 @@ +/*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.hpp" + +using namespace cv; +using namespace std; + +//#define GET_STAT + +#define DIST_E "distE" +#define S_E "sE" +#define NO_PAIR_E "noPairE" +//#define TOTAL_NO_PAIR_E "totalNoPairE" + +#define DETECTOR_NAMES "detector_names" +#define DETECTORS "detectors" +#define IMAGE_FILENAMES "image_filenames" +#define VALIDATION "validation" +#define FILENAME "fn" + +#define C_SCALE_CASCADE "scale_cascade" + +class CV_DetectorTest : public cvtest::BaseTest +{ +public: + CV_DetectorTest(); +protected: + virtual int prepareData( FileStorage& fs ); + virtual void run( int startFrom ); + virtual string& getValidationFilename(); + + virtual void readDetector( const FileNode& fn ) = 0; + virtual void writeDetector( FileStorage& fs, int di ) = 0; + int runTestCase( int detectorIdx, vector >& objects ); + virtual int detectMultiScale( int di, const Mat& img, vector& objects ) = 0; + int validate( int detectorIdx, vector >& objects ); + + struct + { + float dist; + float s; + float noPair; + //float totalNoPair; + } eps; + vector detectorNames; + vector detectorFilenames; + vector imageFilenames; + vector images; + string validationFilename; + FileStorage validationFS; +}; + +CV_DetectorTest::CV_DetectorTest() +{ +} + +string& CV_DetectorTest::getValidationFilename() +{ + return validationFilename; +} + +int CV_DetectorTest::prepareData( FileStorage& _fs ) +{ + if( !_fs.isOpened() ) + test_case_count = -1; + else + { + FileNode fn = _fs.getFirstTopLevelNode(); + + fn[DIST_E] >> eps.dist; + fn[S_E] >> eps.s; + fn[NO_PAIR_E] >> eps.noPair; +// fn[TOTAL_NO_PAIR_E] >> eps.totalNoPair; + + // read detectors + if( fn[DETECTOR_NAMES].node->data.seq != 0 ) + { + FileNodeIterator it = fn[DETECTOR_NAMES].begin(); + for( ; it != fn[DETECTOR_NAMES].end(); ) + { + string name; + it >> name; + detectorNames.push_back(name); + readDetector(fn[DETECTORS][name]); + } + } + test_case_count = (int)detectorNames.size(); + + // read images filenames and images + string dataPath = ts->get_data_path(); + if( fn[IMAGE_FILENAMES].node->data.seq != 0 ) + { + for( FileNodeIterator it = fn[IMAGE_FILENAMES].begin(); it != fn[IMAGE_FILENAMES].end(); ) + { + string filename; + it >> filename; + imageFilenames.push_back(filename); + Mat img = imread( dataPath+filename, 1 ); + images.push_back( img ); + } + } + } + return cvtest::TS::OK; +} + +void CV_DetectorTest::run( int ) +{ + string dataPath = ts->get_data_path(); + validationFS.open( dataPath + getValidationFilename(), FileStorage::READ ); + int code = prepareData( validationFS ); + if( code < 0 ) + { + ts->set_failed_test_info( code ); + return; + } + +#ifdef GET_STAT + validationFS.release(); + string filename = ts->get_data_path(); + filename += getValidationFilename(); + validationFS.open( filename, FileStorage::WRITE ); + validationFS << FileStorage::getDefaultObjectName(validationFilename) << "{"; + + validationFS << DIST_E << eps.dist; + validationFS << S_E << eps.s; + validationFS << NO_PAIR_E << eps.noPair; +// validationFS << TOTAL_NO_PAIR_E << eps.totalNoPair; + + // write detector names + validationFS << DETECTOR_NAMES << "["; + vector::const_iterator nit = detectorNames.begin(); + for( ; nit != detectorNames.end(); ++nit ) + { + validationFS << *nit; + } + validationFS << "]"; // DETECTOR_NAMES + + // write detectors + validationFS << DETECTORS << "{"; + assert( detectorNames.size() == detectorFilenames.size() ); + nit = detectorNames.begin(); + for( int di = 0; di < detectorNames.size(), nit != detectorNames.end(); ++nit, di++ ) + { + validationFS << *nit << "{"; + writeDetector( validationFS, di ); + validationFS << "}"; + } + validationFS << "}"; + + // write image filenames + validationFS << IMAGE_FILENAMES << "["; + vector::const_iterator it = imageFilenames.begin(); + for( int ii = 0; it != imageFilenames.end(); ++it, ii++ ) + { + char buf[10]; + sprintf( buf, "%s%d", "img_", ii ); + cvWriteComment( validationFS.fs, buf, 0 ); + validationFS << *it; + } + validationFS << "]"; // IMAGE_FILENAMES + + validationFS << VALIDATION << "{"; +#endif + + int progress = 0; + for( int di = 0; di < test_case_count; di++ ) + { + progress = update_progress( progress, di, test_case_count, 0 ); +#ifdef GET_STAT + validationFS << detectorNames[di] << "{"; +#endif + vector > objects; + int temp_code = runTestCase( di, objects ); +#ifndef GET_STAT + if (temp_code == cvtest::TS::OK) + temp_code = validate( di, objects ); +#endif + if (temp_code != cvtest::TS::OK) + code = temp_code; +#ifdef GET_STAT + validationFS << "}"; // detectorNames[di] +#endif + } + +#ifdef GET_STAT + validationFS << "}"; // VALIDATION + validationFS << "}"; // getDefaultObjectName +#endif + if ( test_case_count <= 0 || imageFilenames.size() <= 0 ) + { + ts->printf( cvtest::TS::LOG, "validation file is not determined or not correct" ); + code = cvtest::TS::FAIL_INVALID_TEST_DATA; + } + ts->set_failed_test_info( code ); +} + +int CV_DetectorTest::runTestCase( int detectorIdx, vector >& objects ) +{ + string dataPath = ts->get_data_path(), detectorFilename; + if( !detectorFilenames[detectorIdx].empty() ) + detectorFilename = dataPath + detectorFilenames[detectorIdx]; + + for( int ii = 0; ii < (int)imageFilenames.size(); ++ii ) + { + vector imgObjects; + Mat image = images[ii]; + if( image.empty() ) + { + char msg[30]; + sprintf( msg, "%s %d %s", "image ", ii, " can not be read" ); + ts->printf( cvtest::TS::LOG, msg ); + return cvtest::TS::FAIL_INVALID_TEST_DATA; + } + int code = detectMultiScale( detectorIdx, image, imgObjects ); + if( code != cvtest::TS::OK ) + return code; + + objects.push_back( imgObjects ); + +#ifdef GET_STAT + char buf[10]; + sprintf( buf, "%s%d", "img_", ii ); + string imageIdxStr = buf; + validationFS << imageIdxStr << "[:"; + for( vector::const_iterator it = imgObjects.begin(); + it != imgObjects.end(); ++it ) + { + validationFS << it->x << it->y << it->width << it->height; + } + validationFS << "]"; // imageIdxStr +#endif + } + return cvtest::TS::OK; +} + + +bool isZero( uchar i ) {return i == 0;} + +int CV_DetectorTest::validate( int detectorIdx, vector >& objects ) +{ + assert( imageFilenames.size() == objects.size() ); + int imageIdx = 0; + int totalNoPair = 0, totalValRectCount = 0; + + for( vector >::const_iterator it = objects.begin(); + it != objects.end(); ++it, imageIdx++ ) // for image + { + Size imgSize = images[imageIdx].size(); + float dist = min(imgSize.height, imgSize.width) * eps.dist; + float wDiff = imgSize.width * eps.s; + float hDiff = imgSize.height * eps.s; + + int noPair = 0; + + // read validation rectangles + char buf[10]; + sprintf( buf, "%s%d", "img_", imageIdx ); + string imageIdxStr = buf; + FileNode node = validationFS.getFirstTopLevelNode()[VALIDATION][detectorNames[detectorIdx]][imageIdxStr]; + vector valRects; + if( node.node->data.seq != 0 ) + { + for( FileNodeIterator it = node.begin(); it != node.end(); ) + { + Rect r; + it >> r.x >> r.y >> r.width >> r.height; + valRects.push_back(r); + } + } + totalValRectCount += (int)valRects.size(); + + // compare rectangles + vector map(valRects.size(), 0); + for( vector::const_iterator cr = it->begin(); + cr != it->end(); ++cr ) + { + // find nearest rectangle + Point2f cp1 = Point2f( cr->x + (float)cr->width/2.0f, cr->y + (float)cr->height/2.0f ); + int minIdx = -1, vi = 0; + float minDist = (float)norm( Point(imgSize.width, imgSize.height) ); + for( vector::const_iterator vr = valRects.begin(); + vr != valRects.end(); ++vr, vi++ ) + { + Point2f cp2 = Point2f( vr->x + (float)vr->width/2.0f, vr->y + (float)vr->height/2.0f ); + float curDist = (float)norm(cp1-cp2); + if( curDist < minDist ) + { + minIdx = vi; + minDist = curDist; + } + } + if( minIdx == -1 ) + { + noPair++; + } + else + { + Rect vr = valRects[minIdx]; + if( map[minIdx] != 0 || (minDist > dist) || (abs(cr->width - vr.width) > wDiff) || + (abs(cr->height - vr.height) > hDiff) ) + noPair++; + else + map[minIdx] = 1; + } + } + noPair += (int)count_if( map.begin(), map.end(), isZero ); + totalNoPair += noPair; + if( noPair > cvRound(valRects.size()*eps.noPair)+1 ) + break; + } + if( imageIdx < (int)imageFilenames.size() ) + { + char msg[500]; + sprintf( msg, "detector %s has overrated count of rectangles without pair on %s-image\n", + detectorNames[detectorIdx].c_str(), imageFilenames[imageIdx].c_str() ); + ts->printf( cvtest::TS::LOG, msg ); + return cvtest::TS::FAIL_BAD_ACCURACY; + } + if ( totalNoPair > cvRound(totalValRectCount*eps./*total*/noPair)+1 ) + { + ts->printf( cvtest::TS::LOG, "overrated count of rectangles without pair on all images set" ); + return cvtest::TS::FAIL_BAD_ACCURACY; + } + + return cvtest::TS::OK; +} + +//----------------------------------------------- CascadeDetectorTest ----------------------------------- +class CV_CascadeDetectorTest : public CV_DetectorTest +{ +public: + CV_CascadeDetectorTest(); +protected: + virtual void readDetector( const FileNode& fn ); + virtual void writeDetector( FileStorage& fs, int di ); + virtual int detectMultiScale( int di, const Mat& img, vector& objects ); + vector flags; +}; + +CV_CascadeDetectorTest::CV_CascadeDetectorTest() +{ + validationFilename = "cascadeandhog/cascade.xml"; +} + +void CV_CascadeDetectorTest::readDetector( const FileNode& fn ) +{ + string filename; + int flag; + fn[FILENAME] >> filename; + detectorFilenames.push_back(filename); + fn[C_SCALE_CASCADE] >> flag; + if( flag ) + flags.push_back( 0 ); + else + flags.push_back( CV_HAAR_SCALE_IMAGE ); +} + +void CV_CascadeDetectorTest::writeDetector( FileStorage& fs, int di ) +{ + int sc = flags[di] & CV_HAAR_SCALE_IMAGE ? 0 : 1; + fs << FILENAME << detectorFilenames[di]; + fs << C_SCALE_CASCADE << sc; +} + +int CV_CascadeDetectorTest::detectMultiScale( int di, const Mat& img, + vector& objects) +{ + string dataPath = ts->get_data_path(), filename; + filename = dataPath + detectorFilenames[di]; + CascadeClassifier cascade( filename ); + if( cascade.empty() ) + { + ts->printf( cvtest::TS::LOG, "cascade %s can not be opened"); + return cvtest::TS::FAIL_INVALID_TEST_DATA; + } + Mat grayImg; + cvtColor( img, grayImg, CV_BGR2GRAY ); + equalizeHist( grayImg, grayImg ); + cascade.detectMultiScale( grayImg, objects, 1.1, 3, flags[di] ); + return cvtest::TS::OK; +} + +//----------------------------------------------- HOGDetectorTest ----------------------------------- +class CV_HOGDetectorTest : public CV_DetectorTest +{ +public: + CV_HOGDetectorTest(); +protected: + virtual void readDetector( const FileNode& fn ); + virtual void writeDetector( FileStorage& fs, int di ); + virtual int detectMultiScale( int di, const Mat& img, vector& objects ); +}; + +CV_HOGDetectorTest::CV_HOGDetectorTest() +{ + validationFilename = "cascadeandhog/hog.xml"; +} + +void CV_HOGDetectorTest::readDetector( const FileNode& fn ) +{ + string filename; + if( fn[FILENAME].node->data.seq != 0 ) + fn[FILENAME] >> filename; + detectorFilenames.push_back( filename); +} + +void CV_HOGDetectorTest::writeDetector( FileStorage& fs, int di ) +{ + fs << FILENAME << detectorFilenames[di]; +} + +int CV_HOGDetectorTest::detectMultiScale( int di, const Mat& img, + vector& objects) +{ + HOGDescriptor hog; + if( detectorFilenames[di].empty() ) + hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector()); + else + assert(0); + hog.detectMultiScale(img, objects); + return cvtest::TS::OK; +} + +TEST(Objdetect_CascadeDetector, regression) { CV_CascadeDetectorTest test; test.safe_run(); } +TEST(Objdetect_HOGDetector, regression) { CV_HOGDetectorTest test; test.safe_run(); } diff --git a/modules/objdetect/test/test_latentsvmdetector.cpp b/modules/objdetect/test/test_latentsvmdetector.cpp new file mode 100644 index 000000000..0e481dead --- /dev/null +++ b/modules/objdetect/test/test_latentsvmdetector.cpp @@ -0,0 +1,125 @@ +/*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 + +using namespace cv; + +const int num_detections = 3; +const float true_scores[3] = {-0.383931f, -0.825876f, -0.959934f}; +const float score_thr = 0.05f; +const CvRect true_bounding_boxes[3] = {cvRect(0, 45, 362, 452), cvRect(304, 0, 64, 80), cvRect(236, 0, 108, 59)}; + +class CV_LatentSVMDetectorTest : public cvtest::BaseTest +{ +public: + CV_LatentSVMDetectorTest(); + ~CV_LatentSVMDetectorTest(); +protected: + void run(int); +private: + bool isEqual(CvRect r1, CvRect r2); +}; + +CV_LatentSVMDetectorTest::CV_LatentSVMDetectorTest() +{ +} + +CV_LatentSVMDetectorTest::~CV_LatentSVMDetectorTest() {} + +bool CV_LatentSVMDetectorTest::isEqual(CvRect r1, CvRect r2) +{ + return ((r1.x == r2.x) && (r1.y == r2.y) && (r1.width == r2.width) && (r1.height == r2.height)); +} + +void CV_LatentSVMDetectorTest::run( int /* start_from */) +{ + string img_path = string(ts->get_data_path()) + "latentsvmdetector/cat.jpg"; + string model_path = string(ts->get_data_path()) + "latentsvmdetector/cat.xml"; + + IplImage* image = cvLoadImage(img_path.c_str()); + if (!image) + { + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + CvLatentSvmDetector* detector = cvLoadLatentSvmDetector(model_path.c_str()); + if (!detector) + { + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + cvReleaseImage(&image); + return; + } + + CvMemStorage* storage = cvCreateMemStorage(0); + CvSeq* detections = 0; + detections = cvLatentSvmDetectObjects(image, detector, storage); + + if (detections->total != num_detections) + { + ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH ); + } + else + { + ts->set_failed_test_info(cvtest::TS::OK); + for (int i = 0; i < detections->total; i++) + { + CvObjectDetection detection = *(CvObjectDetection*)cvGetSeqElem( detections, i ); + CvRect bounding_box = detection.rect; + float score = detection.score; + if ((!isEqual(bounding_box, true_bounding_boxes[i])) || (fabs(score - true_scores[i]) > score_thr)) + { + ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH ); + break; + } + } + } + + cvReleaseMemStorage( &storage ); + cvReleaseLatentSvmDetector( &detector ); + cvReleaseImage( &image ); +} + +TEST(Objdetect_LatentSVMDetector, regression) { CV_LatentSVMDetectorTest test; test.safe_run(); } diff --git a/modules/objdetect/test/test_main.cpp b/modules/objdetect/test/test_main.cpp new file mode 100644 index 000000000..6b2499344 --- /dev/null +++ b/modules/objdetect/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("cv") diff --git a/modules/objdetect/test/test_precomp.cpp b/modules/objdetect/test/test_precomp.cpp new file mode 100644 index 000000000..5956e13e3 --- /dev/null +++ b/modules/objdetect/test/test_precomp.cpp @@ -0,0 +1 @@ +#include "test_precomp.hpp" diff --git a/modules/objdetect/test/test_precomp.hpp b/modules/objdetect/test/test_precomp.hpp new file mode 100644 index 000000000..67dd004cc --- /dev/null +++ b/modules/objdetect/test/test_precomp.hpp @@ -0,0 +1,10 @@ +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/objdetect/objdetect.hpp" +#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/highgui/highgui.hpp" +#include + +#endif diff --git a/modules/python/hdr_parser.pyc b/modules/python/hdr_parser.pyc new file mode 100755 index 000000000..8b63346d3 Binary files /dev/null and b/modules/python/hdr_parser.pyc differ diff --git a/modules/video/include/opencv2/video/tracking.hpp b/modules/video/include/opencv2/video/tracking.hpp index fc36e2148..5e5a051ea 100644 --- a/modules/video/include/opencv2/video/tracking.hpp +++ b/modules/video/include/opencv2/video/tracking.hpp @@ -257,7 +257,10 @@ CV_EXPORTS_W void calcMotionGradient( const Mat& mhi, CV_OUT Mat& mask, CV_EXPORTS_W double calcGlobalOrientation( const Mat& orientation, const Mat& mask, const Mat& mhi, double timestamp, double duration ); -// TODO: need good API for cvSegmentMotion + +CV_EXPORTS_W void segmentMotion(const Mat& mhi, Mat& segmask, + vector& boundingRects, + double timestamp, double segThresh); //! updates the object tracking window using CAMSHIFT algorithm CV_EXPORTS_W RotatedRect CamShift( const Mat& probImage, CV_IN_OUT Rect& window, diff --git a/modules/video/src/motempl.cpp b/modules/video/src/motempl.cpp index da4f43bc1..1c9435494 100644 --- a/modules/video/src/motempl.cpp +++ b/modules/video/src/motempl.cpp @@ -468,4 +468,19 @@ double cv::calcGlobalOrientation( const Mat& orientation, const Mat& mask, return cvCalcGlobalOrientation(&_orientation, &_mask, &_mhi, timestamp, duration); } +void cv::segmentMotion(const Mat& mhi, Mat& segmask, + vector& boundingRects, + double timestamp, double segThresh) +{ + segmask.create(mhi.size(), CV_32F); + CvMat c_mhi = mhi, c_segmask = segmask; + Ptr storage = cvCreateMemStorage(); + Seq comps = cvSegmentMotion(&c_mhi, &c_segmask, storage, timestamp, segThresh); + Seq::const_iterator it(comps); + size_t i, ncomps = comps.size(); + boundingRects.resize(ncomps); + for( i = 0; i < ncomps; i++, ++it) + boundingRects[i] = (*it).rect; +} + /* End of file. */ diff --git a/modules/video/test/test_accum.cpp b/modules/video/test/test_accum.cpp new file mode 100644 index 000000000..d3cab6cb6 --- /dev/null +++ b/modules/video/test/test_accum.cpp @@ -0,0 +1,247 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_AccumBaseTest : public cvtest::ArrayTest +{ +public: + CV_AccumBaseTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + double alpha; +}; + + +CV_AccumBaseTest::CV_AccumBaseTest() +{ + test_array[INPUT].push_back(NULL); + test_array[INPUT_OUTPUT].push_back(NULL); + test_array[REF_INPUT_OUTPUT].push_back(NULL); + test_array[MASK].push_back(NULL); + optional_mask = true; + element_wise_relative_error = false; +} // ctor + + +void CV_AccumBaseTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + int depth = cvtest::randInt(rng) % 3, cn = cvtest::randInt(rng) & 1 ? 3 : 1; + int accdepth = std::max((int)(cvtest::randInt(rng) % 2 + 1), depth); + int i, input_count = test_array[INPUT].size(); + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + depth = depth == 0 ? CV_8U : depth == 1 ? CV_32F : CV_64F; + accdepth = accdepth == 1 ? CV_32F : CV_64F; + accdepth = MAX(accdepth, depth); + + for( i = 0; i < input_count; i++ ) + types[INPUT][i] = CV_MAKETYPE(depth,cn); + + types[INPUT_OUTPUT][0] = types[REF_INPUT_OUTPUT][0] = CV_MAKETYPE(accdepth,cn); + + alpha = cvtest::randReal(rng); +} + + +double CV_AccumBaseTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return test_mat[INPUT_OUTPUT][0].depth() < CV_64F || + test_mat[INPUT][0].depth() == CV_32F ? FLT_EPSILON*100 : DBL_EPSILON*1000; +} + + +/// acc +class CV_AccTest : public CV_AccumBaseTest +{ +public: + CV_AccTest() {}; +protected: + void run_func(); + void prepare_to_validation( int ); +}; + + +void CV_AccTest::run_func(void) +{ + cvAcc( test_array[INPUT][0], test_array[INPUT_OUTPUT][0], test_array[MASK][0] ); +} + + +void CV_AccTest::prepare_to_validation( int ) +{ + const Mat& src = test_mat[INPUT][0]; + Mat& dst = test_mat[REF_INPUT_OUTPUT][0]; + const Mat& mask = test_array[MASK][0] ? test_mat[MASK][0] : Mat(); + Mat temp; + cvtest::add( src, 1, dst, 1, cvScalarAll(0.), temp, dst.type() ); + cvtest::copy( temp, dst, mask ); +} + + +/// square acc +class CV_SquareAccTest : public CV_AccumBaseTest +{ +public: + CV_SquareAccTest(); +protected: + void run_func(); + void prepare_to_validation( int ); +}; + + +CV_SquareAccTest::CV_SquareAccTest() +{ +} + + +void CV_SquareAccTest::run_func() +{ + cvSquareAcc( test_array[INPUT][0], test_array[INPUT_OUTPUT][0], test_array[MASK][0] ); +} + + +void CV_SquareAccTest::prepare_to_validation( int ) +{ + const Mat& src = test_mat[INPUT][0]; + Mat& dst = test_mat[REF_INPUT_OUTPUT][0]; + const Mat& mask = test_array[MASK][0] ? test_mat[MASK][0] : Mat(); + Mat temp; + + cvtest::convert( src, temp, dst.type() ); + cvtest::multiply( temp, temp, temp, 1 ); + cvtest::add( temp, 1, dst, 1, cvScalarAll(0.), temp, dst.depth() ); + cvtest::copy( temp, dst, mask ); +} + + +/// multiply acc +class CV_MultiplyAccTest : public CV_AccumBaseTest +{ +public: + CV_MultiplyAccTest(); +protected: + void run_func(); + void prepare_to_validation( int ); +}; + + +CV_MultiplyAccTest::CV_MultiplyAccTest() +{ + test_array[INPUT].push_back(NULL); +} + + +void CV_MultiplyAccTest::run_func() +{ + cvMultiplyAcc( test_array[INPUT][0], test_array[INPUT][1], + test_array[INPUT_OUTPUT][0], test_array[MASK][0] ); +} + + +void CV_MultiplyAccTest::prepare_to_validation( int ) +{ + const Mat& src1 = test_mat[INPUT][0]; + const Mat& src2 = test_mat[INPUT][1]; + Mat& dst = test_mat[REF_INPUT_OUTPUT][0]; + const Mat& mask = test_array[MASK][0] ? test_mat[MASK][0] : Mat(); + Mat temp1, temp2; + + cvtest::convert( src1, temp1, dst.type() ); + cvtest::convert( src2, temp2, dst.type() ); + + cvtest::multiply( temp1, temp2, temp1, 1 ); + cvtest::add( temp1, 1, dst, 1, cvScalarAll(0.), temp1, dst.depth() ); + cvtest::copy( temp1, dst, mask ); +} + + +/// running average +class CV_RunningAvgTest : public CV_AccumBaseTest +{ +public: + CV_RunningAvgTest(); +protected: + void run_func(); + void prepare_to_validation( int ); +}; + + +CV_RunningAvgTest::CV_RunningAvgTest() +{ +} + + +void CV_RunningAvgTest::run_func() +{ + cvRunningAvg( test_array[INPUT][0], test_array[INPUT_OUTPUT][0], + alpha, test_array[MASK][0] ); +} + + +void CV_RunningAvgTest::prepare_to_validation( int ) +{ + const Mat& src = test_mat[INPUT][0]; + Mat& dst = test_mat[REF_INPUT_OUTPUT][0]; + Mat temp; + const Mat& mask = test_array[MASK][0] ? test_mat[MASK][0] : Mat(); + double a[1], b[1]; + int accdepth = test_mat[INPUT_OUTPUT][0].depth(); + CvMat A = cvMat(1,1,accdepth,a), B = cvMat(1,1,accdepth,b); + cvSetReal1D( &A, 0, alpha); + cvSetReal1D( &B, 0, 1 - cvGetReal1D(&A, 0)); + + cvtest::convert( src, temp, dst.type() ); + cvtest::add( src, cvGetReal1D(&A, 0), dst, cvGetReal1D(&B, 0), cvScalarAll(0.), temp, temp.depth() ); + cvtest::copy( temp, dst, mask ); +} + + +TEST(Video_Acc, accuracy) { CV_AccTest test; test.safe_run(); } +TEST(Video_AccSquared, accuracy) { CV_SquareAccTest test; test.safe_run(); } +TEST(Video_AccProduct, accuracy) { CV_MultiplyAccTest test; test.safe_run(); } +TEST(Video_RunningAvg, accuracy) { CV_RunningAvgTest test; test.safe_run(); } diff --git a/modules/video/test/test_camshift.cpp b/modules/video/test/test_camshift.cpp new file mode 100644 index 000000000..b1e8483a1 --- /dev/null +++ b/modules/video/test/test_camshift.cpp @@ -0,0 +1,511 @@ +/*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" + +using namespace cv; +using namespace std; + +class CV_TrackBaseTest : public cvtest::BaseTest +{ +public: + CV_TrackBaseTest(); + virtual ~CV_TrackBaseTest(); + void clear(); + +protected: + int read_params( CvFileStorage* fs ); + void run_func(void); + int prepare_test_case( int test_case_idx ); + int validate_test_results( int test_case_idx ); + void generate_object(); + + int min_log_size, max_log_size; + CvMat* img; + CvBox2D box0; + CvSize img_size; + CvTermCriteria criteria; + int img_type; +}; + + +CV_TrackBaseTest::CV_TrackBaseTest() +{ + img = 0; + test_case_count = 100; + min_log_size = 5; + max_log_size = 8; +} + + +CV_TrackBaseTest::~CV_TrackBaseTest() +{ + clear(); +} + + +void CV_TrackBaseTest::clear() +{ + cvReleaseMat( &img ); + cvtest::BaseTest::clear(); +} + + +int CV_TrackBaseTest::read_params( CvFileStorage* fs ) +{ + int code = cvtest::BaseTest::read_params( fs ); + if( code < 0 ) + return code; + + test_case_count = cvReadInt( find_param( fs, "test_case_count" ), test_case_count ); + min_log_size = cvReadInt( find_param( fs, "min_log_size" ), min_log_size ); + max_log_size = cvReadInt( find_param( fs, "max_log_size" ), max_log_size ); + + min_log_size = cvtest::clipInt( min_log_size, 1, 10 ); + max_log_size = cvtest::clipInt( max_log_size, 1, 10 ); + if( min_log_size > max_log_size ) + { + int t; + CV_SWAP( min_log_size, max_log_size, t ); + } + + return 0; +} + + +void CV_TrackBaseTest::generate_object() +{ + int x, y; + double cx = box0.center.x; + double cy = box0.center.y; + double width = box0.size.width*0.5; + double height = box0.size.height*0.5; + double angle = box0.angle*CV_PI/180.; + double a = sin(angle), b = -cos(angle); + double inv_ww = 1./(width*width), inv_hh = 1./(height*height); + + img = cvCreateMat( img_size.height, img_size.width, img_type ); + cvZero( img ); + + // use the straightforward algorithm: for every pixel check if it is inside the ellipse + for( y = 0; y < img_size.height; y++ ) + { + uchar* ptr = img->data.ptr + img->step*y; + float* fl = (float*)ptr; + double x_ = (y - cy)*b, y_ = (y - cy)*a; + + for( x = 0; x < img_size.width; x++ ) + { + double x1 = (x - cx)*a - x_; + double y1 = (x - cx)*b + y_; + + if( x1*x1*inv_hh + y1*y1*inv_ww <= 1. ) + { + if( img_type == CV_8U ) + ptr[x] = (uchar)1; + else + fl[x] = (float)1.f; + } + } + } +} + + +int CV_TrackBaseTest::prepare_test_case( int test_case_idx ) +{ + RNG& rng = ts->get_rng(); + cvtest::BaseTest::prepare_test_case( test_case_idx ); + float m; + + clear(); + + box0.size.width = (float)exp((cvtest::randReal(rng) * (max_log_size - min_log_size) + min_log_size)*CV_LOG2); + box0.size.height = (float)exp((cvtest::randReal(rng) * (max_log_size - min_log_size) + min_log_size)*CV_LOG2); + box0.angle = (float)(cvtest::randReal(rng)*180.); + + if( box0.size.width > box0.size.height ) + { + float t; + CV_SWAP( box0.size.width, box0.size.height, t ); + } + + m = MAX( box0.size.width, box0.size.height ); + img_size.width = cvRound(cvtest::randReal(rng)*m*0.5 + m + 1); + img_size.height = cvRound(cvtest::randReal(rng)*m*0.5 + m + 1); + img_type = cvtest::randInt(rng) % 2 ? CV_32F : CV_8U; + img_type = CV_8U; + + box0.center.x = (float)(img_size.width*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.width - m)); + box0.center.y = (float)(img_size.height*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.height - m)); + + criteria = cvTermCriteria( CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 10, 0.1 ); + + generate_object(); + + return 1; +} + + +void CV_TrackBaseTest::run_func(void) +{ +} + + +int CV_TrackBaseTest::validate_test_results( int /*test_case_idx*/ ) +{ + return 0; +} + + + +///////////////////////// CamShift ////////////////////////////// + +class CV_CamShiftTest : public CV_TrackBaseTest +{ +public: + CV_CamShiftTest(); + +protected: + void run_func(void); + int prepare_test_case( int test_case_idx ); + int validate_test_results( int test_case_idx ); + void generate_object(); + + CvBox2D box; + CvRect init_rect; + CvConnectedComp comp; + int area0; +}; + + +CV_CamShiftTest::CV_CamShiftTest() +{ +} + + +int CV_CamShiftTest::prepare_test_case( int test_case_idx ) +{ + RNG& rng = ts->get_rng(); + double m; + int code = CV_TrackBaseTest::prepare_test_case( test_case_idx ); + int i, area; + + if( code <= 0 ) + return code; + + area0 = cvCountNonZero(img); + + for(i = 0; i < 100; i++) + { + CvMat temp; + + m = MAX(box0.size.width,box0.size.height)*0.8; + init_rect.x = cvFloor(box0.center.x - m*(0.45 + cvtest::randReal(rng)*0.2)); + init_rect.y = cvFloor(box0.center.y - m*(0.45 + cvtest::randReal(rng)*0.2)); + init_rect.width = cvCeil(box0.center.x + m*(0.45 + cvtest::randReal(rng)*0.2) - init_rect.x); + init_rect.height = cvCeil(box0.center.y + m*(0.45 + cvtest::randReal(rng)*0.2) - init_rect.y); + + if( init_rect.x < 0 || init_rect.y < 0 || + init_rect.x + init_rect.width >= img_size.width || + init_rect.y + init_rect.height >= img_size.height ) + continue; + + cvGetSubRect( img, &temp, init_rect ); + area = cvCountNonZero( &temp ); + + if( area >= 0.1*area0 ) + break; + } + + return i < 100 ? code : 0; +} + + +void CV_CamShiftTest::run_func(void) +{ + cvCamShift( img, init_rect, criteria, &comp, &box ); +} + + +int CV_CamShiftTest::validate_test_results( int /*test_case_idx*/ ) +{ + int code = cvtest::TS::OK; + + double m = MAX(box0.size.width, box0.size.height), delta; + double diff_angle; + + if( cvIsNaN(box.size.width) || cvIsInf(box.size.width) || box.size.width <= 0 || + cvIsNaN(box.size.height) || cvIsInf(box.size.height) || box.size.height <= 0 || + cvIsNaN(box.center.x) || cvIsInf(box.center.x) || + cvIsNaN(box.center.y) || cvIsInf(box.center.y) || + cvIsNaN(box.angle) || cvIsInf(box.angle) || box.angle < -180 || box.angle > 180 || + cvIsNaN(comp.area) || cvIsInf(comp.area) || comp.area <= 0 ) + { + ts->printf( cvtest::TS::LOG, "Invalid CvBox2D or CvConnectedComp was returned by cvCamShift\n" ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + + box.angle = (float)(180 - box.angle); + + if( fabs(box.size.width - box0.size.width) > box0.size.width*0.2 || + fabs(box.size.height - box0.size.height) > box0.size.height*0.3 ) + { + ts->printf( cvtest::TS::LOG, "Incorrect CvBox2D size (=%.1f x %.1f, should be %.1f x %.1f)\n", + box.size.width, box.size.height, box0.size.width, box0.size.height ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + + if( fabs(box.center.x - box0.center.x) > m*0.1 || + fabs(box.center.y - box0.center.y) > m*0.1 ) + { + ts->printf( cvtest::TS::LOG, "Incorrect CvBox2D position (=(%.1f, %.1f), should be (%.1f, %.1f))\n", + box.center.x, box.center.y, box0.center.x, box0.center.y ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + + if( box.angle < 0 ) + box.angle += 180; + + diff_angle = fabs(box0.angle - box.angle); + diff_angle = MIN( diff_angle, fabs(box0.angle - box.angle + 180)); + + if( fabs(diff_angle) > 30 && box0.size.height > box0.size.width*1.2 ) + { + ts->printf( cvtest::TS::LOG, "Incorrect CvBox2D angle (=%1.f, should be %1.f)\n", + box.angle, box0.angle ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + + delta = m*0.7; + + if( comp.rect.x < box0.center.x - delta || + comp.rect.y < box0.center.y - delta || + comp.rect.x + comp.rect.width > box0.center.x + delta || + comp.rect.y + comp.rect.height > box0.center.y + delta ) + { + ts->printf( cvtest::TS::LOG, + "Incorrect CvConnectedComp ((%d,%d,%d,%d) is not within (%.1f,%.1f,%.1f,%.1f))\n", + comp.rect.x, comp.rect.y, comp.rect.x + comp.rect.width, comp.rect.y + comp.rect.height, + box0.center.x - delta, box0.center.y - delta, box0.center.x + delta, box0.center.y + delta ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + + if( fabs(comp.area - area0) > area0*0.15 ) + { + ts->printf( cvtest::TS::LOG, + "Incorrect CvConnectedComp area (=%.1f, should be %d)\n", comp.area, area0 ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + +_exit_: + + if( code < 0 ) + { +#if defined _DEBUG && defined WIN32 + IplImage* dst = cvCreateImage( img_size, 8, 3 ); + cvNamedWindow( "test", 1 ); + cvCmpS( img, 0, img, CV_CMP_GT ); + cvCvtColor( img, dst, CV_GRAY2BGR ); + cvRectangle( dst, cvPoint(init_rect.x, init_rect.y), + cvPoint(init_rect.x + init_rect.width, init_rect.y + init_rect.height), + CV_RGB(255,0,0), 3, 8, 0 ); + cvEllipseBox( dst, box, CV_RGB(0,255,0), 3, 8, 0 ); + cvShowImage( "test", dst ); + cvReleaseImage( &dst ); + cvWaitKey(); +#endif + ts->set_failed_test_info( code ); + } + return code; +} + + +///////////////////////// MeanShift ////////////////////////////// + +class CV_MeanShiftTest : public CV_TrackBaseTest +{ +public: + CV_MeanShiftTest(); + +protected: + void run_func(void); + int prepare_test_case( int test_case_idx ); + int validate_test_results( int test_case_idx ); + void generate_object(); + + CvRect init_rect; + CvConnectedComp comp; + int area0, area; +}; + + +CV_MeanShiftTest::CV_MeanShiftTest() +{ +} + + +int CV_MeanShiftTest::prepare_test_case( int test_case_idx ) +{ + RNG& rng = ts->get_rng(); + double m; + int code = CV_TrackBaseTest::prepare_test_case( test_case_idx ); + int i; + + if( code <= 0 ) + return code; + + area0 = cvCountNonZero(img); + + for(i = 0; i < 100; i++) + { + CvMat temp; + + m = (box0.size.width + box0.size.height)*0.5; + init_rect.x = cvFloor(box0.center.x - m*(0.4 + cvtest::randReal(rng)*0.2)); + init_rect.y = cvFloor(box0.center.y - m*(0.4 + cvtest::randReal(rng)*0.2)); + init_rect.width = cvCeil(box0.center.x + m*(0.4 + cvtest::randReal(rng)*0.2) - init_rect.x); + init_rect.height = cvCeil(box0.center.y + m*(0.4 + cvtest::randReal(rng)*0.2) - init_rect.y); + + if( init_rect.x < 0 || init_rect.y < 0 || + init_rect.x + init_rect.width >= img_size.width || + init_rect.y + init_rect.height >= img_size.height ) + continue; + + cvGetSubRect( img, &temp, init_rect ); + area = cvCountNonZero( &temp ); + + if( area >= 0.5*area0 ) + break; + } + + return i < 100 ? code : 0; +} + + +void CV_MeanShiftTest::run_func(void) +{ + cvMeanShift( img, init_rect, criteria, &comp ); +} + + +int CV_MeanShiftTest::validate_test_results( int /*test_case_idx*/ ) +{ + int code = cvtest::TS::OK; + CvPoint2D32f c; + double m = MAX(box0.size.width, box0.size.height), delta; + + if( cvIsNaN(comp.area) || cvIsInf(comp.area) || comp.area <= 0 ) + { + ts->printf( cvtest::TS::LOG, "Invalid CvConnectedComp was returned by cvMeanShift\n" ); + code = cvtest::TS::FAIL_INVALID_OUTPUT; + goto _exit_; + } + + c.x = (float)(comp.rect.x + comp.rect.width*0.5); + c.y = (float)(comp.rect.y + comp.rect.height*0.5); + + if( fabs(c.x - box0.center.x) > m*0.1 || + fabs(c.y - box0.center.y) > m*0.1 ) + { + ts->printf( cvtest::TS::LOG, "Incorrect CvBox2D position (=(%.1f, %.1f), should be (%.1f, %.1f))\n", + c.x, c.y, box0.center.x, box0.center.y ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + + delta = m*0.7; + + if( comp.rect.x < box0.center.x - delta || + comp.rect.y < box0.center.y - delta || + comp.rect.x + comp.rect.width > box0.center.x + delta || + comp.rect.y + comp.rect.height > box0.center.y + delta ) + { + ts->printf( cvtest::TS::LOG, + "Incorrect CvConnectedComp ((%d,%d,%d,%d) is not within (%.1f,%.1f,%.1f,%.1f))\n", + comp.rect.x, comp.rect.y, comp.rect.x + comp.rect.width, comp.rect.y + comp.rect.height, + box0.center.x - delta, box0.center.y - delta, box0.center.x + delta, box0.center.y + delta ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + + if( fabs((double)(comp.area - area0)) > fabs((double)(area - area0)) + area0*0.05 ) + { + ts->printf( cvtest::TS::LOG, + "Incorrect CvConnectedComp area (=%.1f, should be %d)\n", comp.area, area0 ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + +_exit_: + + if( code < 0 ) + { +#if defined _DEBUG && defined WIN32 + IplImage* dst = cvCreateImage( img_size, 8, 3 ); + cvNamedWindow( "test", 1 ); + cvCmpS( img, 0, img, CV_CMP_GT ); + cvCvtColor( img, dst, CV_GRAY2BGR ); + cvRectangle( dst, cvPoint(init_rect.x, init_rect.y), + cvPoint(init_rect.x + init_rect.width, init_rect.y + init_rect.height), + CV_RGB(255,0,0), 3, 8, 0 ); + cvRectangle( dst, cvPoint(comp.rect.x, comp.rect.y), + cvPoint(comp.rect.x + comp.rect.width, comp.rect.y + comp.rect.height), + CV_RGB(0,255,0), 3, 8, 0 ); + cvShowImage( "test", dst ); + cvReleaseImage( &dst ); + cvWaitKey(); +#endif + ts->set_failed_test_info( code ); + } + return code; +} + + +TEST(Video_CAMShift, accuracy) { CV_CamShiftTest test; test.safe_run(); } +TEST(Video_MeanShift, accuracy) { CV_MeanShiftTest test; test.safe_run(); } + +/* End of file. */ diff --git a/modules/video/test/test_estimaterigid.cpp b/modules/video/test/test_estimaterigid.cpp new file mode 100644 index 000000000..c69fbf27a --- /dev/null +++ b/modules/video/test/test_estimaterigid.cpp @@ -0,0 +1,174 @@ +/*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 +#include +#include +#include +#include +#include + +using namespace cv; +using namespace std; + +class CV_RigidTransform_Test : public cvtest::BaseTest +{ +public: + CV_RigidTransform_Test(); + ~CV_RigidTransform_Test(); +protected: + void run(int); + + bool testNPoints(int); + bool testImage(); +}; + +CV_RigidTransform_Test::CV_RigidTransform_Test() +{ +} +CV_RigidTransform_Test::~CV_RigidTransform_Test() {} + +struct WrapAff2D +{ + const double *F; + WrapAff2D(const Mat& aff) : F(aff.ptr()) {} + Point2f operator()(const Point2f& p) + { + return Point2d( p.x * F[0] + p.y * F[1] + F[2], + p.x * F[3] + p.y * F[4] + F[5]); + } +}; + +bool CV_RigidTransform_Test::testNPoints(int from) +{ + cv::RNG rng = ts->get_rng(); + + int progress = 0; + int k, ntests = 10000; + + for( k = from; k < ntests; k++ ) + { + ts->update_context( this, k, true ); + progress = update_progress(progress, k, ntests, 0); + + Mat aff(2, 3, CV_64F); + rng.fill(aff, CV_RAND_UNI, Scalar(-2), Scalar(2)); + + int n = (unsigned)rng % 100 + 10; + + Mat fpts(1, n, CV_32FC2); + Mat tpts(1, n, CV_32FC2); + + rng.fill(fpts, CV_RAND_UNI, Scalar(0,0), Scalar(10,10)); + transform(fpts.ptr(), fpts.ptr() + n, tpts.ptr(), WrapAff2D(aff)); + + Mat noise(1, n, CV_32FC2); + rng.fill(noise, CV_RAND_NORMAL, Scalar::all(0), Scalar::all(0.001*(n<=7 ? 0 : n <= 30 ? 1 : 10))); + tpts += noise; + + Mat aff_est = estimateRigidTransform(fpts, tpts, true); + + double thres = 0.1*norm(aff); + double d = norm(aff_est, aff, NORM_L2); + if (d > thres) + { + double dB=0, nB=0; + if (n <= 4) + { + Mat A = fpts.reshape(1, 3); + Mat B = A - repeat(A.row(0), 3, 1), Bt = B.t(); + B = Bt*B; + dB = cv::determinant(B); + nB = norm(B); + if( fabs(dB) < 0.01*nB ) + continue; + } + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + ts->printf( cvtest::TS::LOG, "Threshold = %f, norm of difference = %f", thres, d ); + return false; + } + } + return true; +} + +bool CV_RigidTransform_Test::testImage() +{ + Mat img; + pyrDown(imread( string(ts->get_data_path()) + "shared/graffiti.png", 1), img); + + Mat aff = cv::getRotationMatrix2D(Point(img.cols/2, img.rows/2), 1, 0.99); + aff.ptr()[2]+=3; + aff.ptr()[5]+=3; + + Mat rotated; + warpAffine(img, rotated, aff, img.size()); + + Mat aff_est = estimateRigidTransform(img, rotated, true); + + const double thres = 0.03; + if (norm(aff_est, aff, NORM_INF) > thres) + { + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + ts->printf( cvtest::TS::LOG, "Threshold = %f, norm of difference = %f", thres, + norm(aff_est, aff, NORM_INF) ); + return false; + } + + return true; +} + +void CV_RigidTransform_Test::run( int start_from ) +{ + cvtest::DefaultRngAuto dra; + + if (!testNPoints(start_from)) + return; + + if (!testImage()) + return; + + ts->set_failed_test_info(cvtest::TS::OK); +} + +TEST(Video_RigidFlow, accuracy) { CV_RigidTransform_Test test; test.safe_run(); } diff --git a/modules/video/test/test_kalman.cpp b/modules/video/test/test_kalman.cpp new file mode 100644 index 000000000..cf08d234b --- /dev/null +++ b/modules/video/test/test_kalman.cpp @@ -0,0 +1,124 @@ +/*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" + +using namespace cv; + +class CV_KalmanTest : public cvtest::BaseTest +{ +public: + CV_KalmanTest(); +protected: + void run(int); +}; + + +CV_KalmanTest::CV_KalmanTest() +{ +} + +void CV_KalmanTest::run( int ) +{ + int code = cvtest::TS::OK; + const int Dim = 7; + const int Steps = 100; + const double max_init = 1; + const double max_noise = 0.1; + + const double EPSILON = 1.000; + RNG& rng = ts->get_rng(); + CvKalman* Kalm; + int i, j; + + CvMat* Sample = cvCreateMat(Dim,1,CV_32F); + CvMat* Temp = cvCreateMat(Dim,1,CV_32F); + + Kalm = cvCreateKalman(Dim, Dim); + CvMat Dyn = cvMat(Dim,Dim,CV_32F,Kalm->DynamMatr); + CvMat Mes = cvMat(Dim,Dim,CV_32F,Kalm->MeasurementMatr); + CvMat PNC = cvMat(Dim,Dim,CV_32F,Kalm->PNCovariance); + CvMat MNC = cvMat(Dim,Dim,CV_32F,Kalm->MNCovariance); + CvMat PriErr = cvMat(Dim,Dim,CV_32F,Kalm->PriorErrorCovariance); + CvMat PostErr = cvMat(Dim,Dim,CV_32F,Kalm->PosterErrorCovariance); + CvMat PriState = cvMat(Dim,1,CV_32F,Kalm->PriorState); + CvMat PostState = cvMat(Dim,1,CV_32F,Kalm->PosterState); + cvSetIdentity(&PNC); + cvSetIdentity(&PriErr); + cvSetIdentity(&PostErr); + cvSetZero(&MNC); + cvSetZero(&PriState); + cvSetZero(&PostState); + cvSetIdentity(&Mes); + cvSetIdentity(&Dyn); + Mat _Sample = cvarrToMat(Sample); + cvtest::randUni(rng, _Sample, cvScalarAll(-max_init), cvScalarAll(max_init)); + cvKalmanCorrect(Kalm, Sample); + for(i = 0; idata.fl[k]; + } + Temp->data.fl[j]= (float)(t+(cvtest::randReal(rng)*2-1)*max_noise); + } + cvCopy( Temp, Sample ); + cvKalmanCorrect(Kalm,Temp); + } + + Mat _state_post = cvarrToMat(Kalm->state_post); + code = cvtest::cmpEps2( ts, _Sample, _state_post, EPSILON, false, "The final estimated state" ); + + cvReleaseMat(&Sample); + cvReleaseMat(&Temp); + cvReleaseKalman(&Kalm); + + if( code < 0 ) + ts->set_failed_test_info( code ); +} + +TEST(Video_Kalman, accuracy) { CV_KalmanTest test; test.safe_run(); } + +/* End of file. */ diff --git a/modules/video/test/test_main.cpp b/modules/video/test/test_main.cpp new file mode 100644 index 000000000..6b2499344 --- /dev/null +++ b/modules/video/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("cv") diff --git a/modules/video/test/test_motiontemplates.cpp b/modules/video/test/test_motiontemplates.cpp new file mode 100644 index 000000000..452c73bc2 --- /dev/null +++ b/modules/video/test/test_motiontemplates.cpp @@ -0,0 +1,497 @@ +/*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" + +using namespace cv; +using namespace std; + +///////////////////// base MHI class /////////////////////// +class CV_MHIBaseTest : public cvtest::ArrayTest +{ +public: + CV_MHIBaseTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); + int prepare_test_case( int test_case_idx ); + double timestamp, duration, max_log_duration; + int mhi_i, mhi_ref_i; + double silh_ratio; +}; + + +CV_MHIBaseTest::CV_MHIBaseTest() +{ + timestamp = duration = 0; + max_log_duration = 9; + mhi_i = mhi_ref_i = -1; + + silh_ratio = 0.25; +} + + +void CV_MHIBaseTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) +{ + cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high ); + if( i == INPUT && CV_MAT_DEPTH(type) == CV_8U ) + { + low = Scalar::all(cvRound(-1./silh_ratio)+2.); + high = Scalar::all(2); + } + else if( i == mhi_i || i == mhi_ref_i ) + { + low = Scalar::all(-exp(max_log_duration)); + high = Scalar::all(0.); + } +} + + +void CV_MHIBaseTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + types[INPUT][0] = CV_8UC1; + types[mhi_i][0] = types[mhi_ref_i][0] = CV_32FC1; + duration = exp(cvtest::randReal(rng)*max_log_duration); + timestamp = duration + cvtest::randReal(rng)*30.-10.; +} + + +int CV_MHIBaseTest::prepare_test_case( int test_case_idx ) +{ + int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); + if( code > 0 ) + { + Mat& mat = test_mat[mhi_i][0]; + mat += Scalar::all(duration); + cv::max(mat, 0, mat); + if( mhi_i != mhi_ref_i ) + { + Mat& mat0 = test_mat[mhi_ref_i][0]; + cvtest::copy( mat, mat0 ); + } + } + + return code; +} + + +///////////////////// update motion history //////////////////////////// + +static void test_updateMHI( const Mat& silh, Mat& mhi, double timestamp, double duration ) +{ + int i, j; + float delbound = (float)(timestamp - duration); + for( i = 0; i < mhi.rows; i++ ) + { + const uchar* silh_row = silh.ptr(i); + float* mhi_row = mhi.ptr(i); + + for( j = 0; j < mhi.cols; j++ ) + { + if( silh_row[j] ) + mhi_row[j] = (float)timestamp; + else if( mhi_row[j] < delbound ) + mhi_row[j] = 0.f; + } + } +} + + +class CV_UpdateMHITest : public CV_MHIBaseTest +{ +public: + CV_UpdateMHITest(); + +protected: + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int ); +}; + + +CV_UpdateMHITest::CV_UpdateMHITest() +{ + test_array[INPUT].push_back(NULL); + test_array[INPUT_OUTPUT].push_back(NULL); + test_array[REF_INPUT_OUTPUT].push_back(NULL); + mhi_i = INPUT_OUTPUT; mhi_ref_i = REF_INPUT_OUTPUT; +} + + +double CV_UpdateMHITest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return 0; +} + + +void CV_UpdateMHITest::run_func() +{ + cvUpdateMotionHistory( test_array[INPUT][0], test_array[INPUT_OUTPUT][0], timestamp, duration ); +} + + +void CV_UpdateMHITest::prepare_to_validation( int /*test_case_idx*/ ) +{ + test_updateMHI( test_mat[INPUT][0], test_mat[REF_INPUT_OUTPUT][0], timestamp, duration ); +} + + +///////////////////// calc motion gradient //////////////////////////// + +static void test_MHIGradient( const Mat& mhi, Mat& mask, Mat& orientation, + double delta1, double delta2, int aperture_size ) +{ + Point anchor( aperture_size/2, aperture_size/2 ); + double limit = 1e-4*aperture_size*aperture_size; + + Mat dx, dy, min_mhi, max_mhi; + + Mat kernel = cvtest::calcSobelKernel2D( 1, 0, aperture_size ); + cvtest::filter2D( mhi, dx, CV_32F, kernel, anchor, 0, BORDER_REPLICATE ); + kernel = cvtest::calcSobelKernel2D( 0, 1, aperture_size ); + cvtest::filter2D( mhi, dy, CV_32F, kernel, anchor, 0, BORDER_REPLICATE ); + + kernel = Mat::ones(aperture_size, aperture_size, CV_8U); + cvtest::erode(mhi, min_mhi, kernel, anchor, 0, BORDER_REPLICATE); + cvtest::dilate(mhi, max_mhi, kernel, anchor, 0, BORDER_REPLICATE); + + if( delta1 > delta2 ) + { + double t; + CV_SWAP( delta1, delta2, t ); + } + + for( int i = 0; i < mhi.rows; i++ ) + { + uchar* mask_row = mask.ptr(i); + float* orient_row = orientation.ptr(i); + const float* dx_row = dx.ptr(i); + const float* dy_row = dy.ptr(i); + const float* min_row = min_mhi.ptr(i); + const float* max_row = max_mhi.ptr(i); + + for( int j = 0; j < mhi.cols; j++ ) + { + double delta = max_row[j] - min_row[j]; + double _dx = dx_row[j], _dy = dy_row[j]; + + if( delta1 <= delta && delta <= delta2 && + (fabs(_dx) > limit || fabs(_dy) > limit) ) + { + mask_row[j] = 1; + double angle = atan2( _dy, _dx ) * (180/CV_PI); + if( angle < 0 ) + angle += 360.; + orient_row[j] = (float)angle; + } + else + { + mask_row[j] = 0; + orient_row[j] = 0.f; + } + } + } +} + + +class CV_MHIGradientTest : public CV_MHIBaseTest +{ +public: + CV_MHIGradientTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + double get_success_error_level( int test_case_idx, int i, int j ); + void run_func(); + void prepare_to_validation( int ); + + double delta1, delta2, delta_range_log; + int aperture_size; +}; + + +CV_MHIGradientTest::CV_MHIGradientTest() +{ + mhi_i = mhi_ref_i = INPUT; + test_array[INPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + delta1 = delta2 = 0; + aperture_size = 0; + delta_range_log = 4; +} + + +void CV_MHIGradientTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + CV_MHIBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_8UC1; + types[OUTPUT][1] = types[REF_OUTPUT][1] = CV_32FC1; + delta1 = exp(cvtest::randReal(rng)*delta_range_log + 1.); + delta2 = exp(cvtest::randReal(rng)*delta_range_log + 1.); + aperture_size = (cvtest::randInt(rng)%3)*2+3; + //duration = exp(cvtest::randReal(rng)*max_log_duration); + //timestamp = duration + cvtest::randReal(rng)*30.-10.; +} + + +double CV_MHIGradientTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int j ) +{ + return j == 0 ? 0 : 2e-1; +} + + +void CV_MHIGradientTest::run_func() +{ + cvCalcMotionGradient( test_array[INPUT][0], test_array[OUTPUT][0], + test_array[OUTPUT][1], delta1, delta2, aperture_size ); +} + + +void CV_MHIGradientTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + test_MHIGradient( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], + test_mat[REF_OUTPUT][1], delta1, delta2, aperture_size ); + test_mat[REF_OUTPUT][0] += Scalar::all(1); + test_mat[OUTPUT][0] += Scalar::all(1); +} + + +////////////////////// calc global orientation ///////////////////////// + +static double test_calcGlobalOrientation( const Mat& orient, const Mat& mask, + const Mat& mhi, double timestamp, double duration ) +{ + const int HIST_SIZE = 12; + int y, x; + int histogram[HIST_SIZE]; + int max_bin = 0; + + double base_orientation = 0, delta_orientation = 0, weight = 0; + double low_time, global_orientation; + + memset( histogram, 0, sizeof( histogram )); + timestamp = 0; + + for( y = 0; y < orient.rows; y++ ) + { + const float* orient_data = orient.ptr(y); + const uchar* mask_data = mask.ptr(y); + const float* mhi_data = mhi.ptr(y); + for( x = 0; x < orient.cols; x++ ) + if( mask_data[x] ) + { + int bin = cvFloor( (orient_data[x]*HIST_SIZE)/360 ); + histogram[bin < 0 ? 0 : bin >= HIST_SIZE ? HIST_SIZE-1 : bin]++; + if( mhi_data[x] > timestamp ) + timestamp = mhi_data[x]; + } + } + + low_time = timestamp - duration; + + for( x = 1; x < HIST_SIZE; x++ ) + { + if( histogram[x] > histogram[max_bin] ) + max_bin = x; + } + + base_orientation = ((double)max_bin*360)/HIST_SIZE; + + for( y = 0; y < orient.rows; y++ ) + { + const float* orient_data = orient.ptr(y); + const float* mhi_data = mhi.ptr(y); + const uchar* mask_data = mask.ptr(y); + + for( x = 0; x < orient.cols; x++ ) + { + if( mask_data[x] && mhi_data[x] > low_time ) + { + double diff = orient_data[x] - base_orientation; + double delta_weight = (((mhi_data[x] - low_time)/duration)*254 + 1)/255; + + if( diff < -180 ) diff += 360; + if( diff > 180 ) diff -= 360; + + if( delta_weight > 0 && fabs(diff) < 45 ) + { + delta_orientation += diff*delta_weight; + weight += delta_weight; + } + } + } + } + + if( weight == 0 ) + global_orientation = base_orientation; + else + { + global_orientation = base_orientation + delta_orientation/weight; + if( global_orientation < 0 ) global_orientation += 360; + if( global_orientation > 360 ) global_orientation -= 360; + } + + return global_orientation; +} + + +class CV_MHIGlobalOrientTest : public CV_MHIBaseTest +{ +public: + CV_MHIGlobalOrientTest(); + +protected: + void get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ); + void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); + double get_success_error_level( int test_case_idx, int i, int j ); + int validate_test_results( int test_case_idx ); + void run_func(); + double angle, min_angle, max_angle; +}; + + +CV_MHIGlobalOrientTest::CV_MHIGlobalOrientTest() +{ + mhi_i = mhi_ref_i = INPUT; + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + test_array[INPUT].push_back(NULL); + min_angle = max_angle = 0; +} + + +void CV_MHIGlobalOrientTest::get_test_array_types_and_sizes( int test_case_idx, vector >& sizes, vector >& types ) +{ + RNG& rng = ts->get_rng(); + CV_MHIBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + CvSize size = sizes[INPUT][0]; + + size.width = MAX( size.width, 16 ); + size.height = MAX( size.height, 16 ); + sizes[INPUT][0] = sizes[INPUT][1] = sizes[INPUT][2] = size; + + types[INPUT][1] = CV_8UC1; // mask + types[INPUT][2] = CV_32FC1; // orientation + + min_angle = cvtest::randReal(rng)*359.9; + max_angle = cvtest::randReal(rng)*359.9; + if( min_angle >= max_angle ) + { + double t; + CV_SWAP( min_angle, max_angle, t ); + } + max_angle += 0.1; + duration = exp(cvtest::randReal(rng)*max_log_duration); + timestamp = duration + cvtest::randReal(rng)*30.-10.; +} + + +void CV_MHIGlobalOrientTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) +{ + CV_MHIBaseTest::get_minmax_bounds( i, j, type, low, high ); + if( i == INPUT && j == 2 ) + { + low = Scalar::all(min_angle); + high = Scalar::all(max_angle); + } +} + + +double CV_MHIGlobalOrientTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) +{ + return 15; +} + + +void CV_MHIGlobalOrientTest::run_func() +{ + angle = cvCalcGlobalOrientation( test_array[INPUT][2], test_array[INPUT][1], + test_array[INPUT][0], timestamp, duration ); +} + + +int CV_MHIGlobalOrientTest::validate_test_results( int test_case_idx ) +{ + //printf("%d. rows=%d, cols=%d, nzmask=%d\n", test_case_idx, test_mat[INPUT][1].rows, test_mat[INPUT][1].cols, + // cvCountNonZero(test_array[INPUT][1])); + + double ref_angle = test_calcGlobalOrientation( test_mat[INPUT][2], test_mat[INPUT][1], + test_mat[INPUT][0], timestamp, duration ); + double err_level = get_success_error_level( test_case_idx, 0, 0 ); + int code = cvtest::TS::OK; + int nz = cvCountNonZero( test_array[INPUT][1] ); + + if( nz > 32 && !(min_angle - err_level <= angle && + max_angle + err_level >= angle) && + !(min_angle - err_level <= angle+360 && + max_angle + err_level >= angle+360) ) + { + ts->printf( cvtest::TS::LOG, "The angle=%g is outside (%g,%g) range\n", + angle, min_angle - err_level, max_angle + err_level ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + else if( fabs(angle - ref_angle) > err_level && + fabs(360 - fabs(angle - ref_angle)) > err_level ) + { + ts->printf( cvtest::TS::LOG, "The angle=%g differs too much from reference value=%g\n", + angle, ref_angle ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + } + + if( code < 0 ) + ts->set_failed_test_info( code ); + return code; +} + + +TEST(Video_MHIUpdate, accuracy) { CV_UpdateMHITest test; test.safe_run(); } +TEST(Video_MHIGradient, accuracy) { CV_MHIGradientTest test; test.safe_run(); } +TEST(Video_MHIGlobalOrient, accuracy) { CV_MHIGlobalOrientTest test; test.safe_run(); } diff --git a/modules/video/test/test_optflow.cpp b/modules/video/test/test_optflow.cpp new file mode 100644 index 000000000..38a24a8c6 --- /dev/null +++ b/modules/video/test/test_optflow.cpp @@ -0,0 +1,355 @@ +/*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 +#include +#include +#include +#include + +using namespace cv; +using namespace std; + +class CV_OptFlowTest : public cvtest::BaseTest +{ +public: + CV_OptFlowTest(); + ~CV_OptFlowTest(); +protected: + void run(int); + + bool runDense(const Point& shift = Point(3, 0)); + bool runSparse(); +}; + +CV_OptFlowTest::CV_OptFlowTest() {} +CV_OptFlowTest::~CV_OptFlowTest() {} + + +Mat copnvert2flow(const Mat& velx, const Mat& vely) +{ + Mat flow(velx.size(), CV_32FC2); + for(int y = 0 ; y < flow.rows; ++y) + for(int x = 0 ; x < flow.cols; ++x) + flow.at(y, x) = Point2f(velx.at(y, x), vely.at(y, x)); + return flow; +} + +void calcOpticalFlowLK( const Mat& prev, const Mat& curr, Size winSize, Mat& flow ) +{ + Mat velx(prev.size(), CV_32F), vely(prev.size(), CV_32F); + CvMat cvvelx = velx; CvMat cvvely = vely; + CvMat cvprev = prev; CvMat cvcurr = curr; + cvCalcOpticalFlowLK( &cvprev, &cvcurr, winSize, &cvvelx, &cvvely ); + flow = copnvert2flow(velx, vely); +} + +void calcOpticalFlowBM( const Mat& prev, const Mat& curr, Size bSize, Size shiftSize, Size maxRange, int usePrevious, Mat& flow ) +{ + Size sz((curr.cols - bSize.width)/shiftSize.width, (curr.rows - bSize.height)/shiftSize.height); + Mat velx(sz, CV_32F), vely(sz, CV_32F); + + CvMat cvvelx = velx; CvMat cvvely = vely; + CvMat cvprev = prev; CvMat cvcurr = curr; + cvCalcOpticalFlowBM( &cvprev, &cvcurr, bSize, shiftSize, maxRange, usePrevious, &cvvelx, &cvvely); + flow = copnvert2flow(velx, vely); +} + +void calcOpticalFlowHS( const Mat& prev, const Mat& curr, int usePrevious, double lambda, TermCriteria criteria, Mat& flow) +{ + Mat velx(prev.size(), CV_32F), vely(prev.size(), CV_32F); + CvMat cvvelx = velx; CvMat cvvely = vely; + CvMat cvprev = prev; CvMat cvcurr = curr; + cvCalcOpticalFlowHS( &cvprev, &cvcurr, usePrevious, &cvvelx, &cvvely, lambda, criteria ); + flow = copnvert2flow(velx, vely); +} + +void calcAffineFlowPyrLK( const Mat& prev, const Mat& curr, + const vector& prev_features, vector& curr_features, + vector& status, vector& track_error, vector& matrices, + TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS,30, 0.01), + Size win_size = Size(15, 15), int level = 3, int flags = 0) +{ + CvMat cvprev = prev; + CvMat cvcurr = curr; + + size_t count = prev_features.size(); + curr_features.resize(count); + status.resize(count); + track_error.resize(count); + matrices.resize(count * 6); + + cvCalcAffineFlowPyrLK( &cvprev, &cvcurr, 0, 0, + (const CvPoint2D32f*)&prev_features[0], (CvPoint2D32f*)&curr_features[0], &matrices[0], + (int)count, win_size, level, (char*)&status[0], &track_error[0], criteria, flags ); +} + +double showFlowAndCalcError(const string& name, const Mat& gray, const Mat& flow, + const Rect& where, const Point& d, + bool showImages = false, bool writeError = false) +{ + const int mult = 16; + + if (showImages) + { + Mat tmp, cflow; + resize(gray, tmp, gray.size() * mult, 0, 0, INTER_NEAREST); + cvtColor(tmp, cflow, CV_GRAY2BGR); + + const float m2 = 0.3f; + const float minVel = 0.1f; + + for(int y = 0; y < flow.rows; ++y) + for(int x = 0; x < flow.cols; ++x) + { + Point2f f = flow.at(y, x); + + if (f.x * f.x + f.y * f.y > minVel * minVel) + { + Point p1 = Point(x, y) * mult; + Point p2 = Point(cvRound((x + f.x*m2) * mult), cvRound((y + f.y*m2) * mult)); + + line(cflow, p1, p2, CV_RGB(0, 255, 0)); + circle(cflow, Point(x, y) * mult, 2, CV_RGB(255, 0, 0)); + } + } + + rectangle(cflow, (where.tl() + d) * mult, (where.br() + d - Point(1,1)) * mult, CV_RGB(0, 0, 255)); + namedWindow(name, 1); imshow(name, cflow); + } + + double angle = atan2((float)d.y, (float)d.x); + double error = 0; + + bool all = true; + Mat inner = flow(where); + for(int y = 0; y < inner.rows; ++y) + for(int x = 0; x < inner.cols; ++x) + { + const Point2f f = inner.at(y, x); + + if (f.x == 0 && f.y == 0) + continue; + + all = false; + + double a = atan2(f.y, f.x); + error += fabs(angle - a); + } + double res = all ? numeric_limits::max() : error / (inner.cols * inner.rows); + + if (writeError) + cout << "Error " + name << " = " << res << endl; + + return res; +} + + +Mat generateImage(const Size& sz, bool doBlur = true) +{ + RNG rng; + Mat mat(sz, CV_8U); + mat = Scalar(0); + for(int y = 0; y < mat.rows; ++y) + for(int x = 0; x < mat.cols; ++x) + mat.at(y, x) = (uchar)rng; + if (doBlur) + blur(mat, mat, Size(3, 3)); + return mat; +} + +Mat generateSample(const Size& sz) +{ + Mat smpl(sz, CV_8U); + smpl = Scalar(0); + Point sc(smpl.cols/2, smpl.rows/2); + rectangle(smpl, Point(0,0), sc - Point(1,1), Scalar(255), CV_FILLED); + rectangle(smpl, sc, Point(smpl.cols, smpl.rows), Scalar(255), CV_FILLED); + return smpl; +} + +bool CV_OptFlowTest::runDense(const Point& d) +{ + Size matSize(40, 40); + Size movSize(8, 8); + + Mat smpl = generateSample(movSize); + Mat prev = generateImage(matSize); + Mat curr = prev.clone(); + + Rect rect(Point(prev.cols/2, prev.rows/2) - Point(movSize.width/2, movSize.height/2), movSize); + + Mat flowLK, flowBM, flowHS, flowFB, flowFB_G, flowBM_received, m1; + + m1 = prev(rect); smpl.copyTo(m1); + m1 = curr(Rect(rect.tl() + d, rect.br() + d)); smpl.copyTo(m1); + + calcOpticalFlowLK( prev, curr, Size(15, 15), flowLK); + calcOpticalFlowBM( prev, curr, Size(15, 15), Size(1, 1), Size(15, 15), 0, flowBM_received); + calcOpticalFlowHS( prev, curr, 0, 5, TermCriteria(TermCriteria::MAX_ITER, 400, 0), flowHS); + calcOpticalFlowFarneback( prev, curr, flowFB, 0.5, 3, std::max(d.x, d.y) + 10, 100, 6, 2, 0); + calcOpticalFlowFarneback( prev, curr, flowFB_G, 0.5, 3, std::max(d.x, d.y) + 10, 100, 6, 2, OPTFLOW_FARNEBACK_GAUSSIAN); + + flowBM.create(prev.size(), CV_32FC2); + flowBM = Scalar(0); + Point origin((flowBM.cols - flowBM_received.cols)/2, (flowBM.rows - flowBM_received.rows)/2); + Mat wcp = flowBM(Rect(origin, flowBM_received.size())); + flowBM_received.copyTo(wcp); + + double errorLK = showFlowAndCalcError("LK", prev, flowLK, rect, d); + double errorBM = showFlowAndCalcError("BM", prev, flowBM, rect, d); + double errorFB = showFlowAndCalcError("FB", prev, flowFB, rect, d); + double errorFBG = showFlowAndCalcError("FBG", prev, flowFB_G, rect, d); + double errorHS = showFlowAndCalcError("HS", prev, flowHS, rect, d); (void)errorHS; + //waitKey(); + + const double thres = 0.2; + if (errorLK > thres || errorBM > thres || errorFB > thres || errorFBG > thres /*|| errorHS > thres */) + { + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return false; + } + return true; +} + + +bool CV_OptFlowTest::runSparse() +{ + Mat prev = imread(string(ts->get_data_path()) + "optflow/rock_1.bmp", 0); + Mat next = imread(string(ts->get_data_path()) + "optflow/rock_2.bmp", 0); + + if (prev.empty() || next.empty()) + { + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return false; + } + + Mat cprev, cnext; + cvtColor(prev, cprev, CV_GRAY2BGR); + cvtColor(next, cnext, CV_GRAY2BGR); + + vector prev_pts; + vector next_ptsOpt; + vector next_ptsAff; + vector status_Opt; + vector status_Aff; + vector error; + vector matrices; + + Size netSize(10, 10); + Point2f center = Point(prev.cols/2, prev.rows/2); + + for(int i = 0 ; i < netSize.width; ++i) + for(int j = 0 ; j < netSize.width; ++j) + { + Point2f p(i * float(prev.cols)/netSize.width, j * float(prev.rows)/netSize.height); + prev_pts.push_back((p - center) * 0.5f + center); + } + + calcOpticalFlowPyrLK( prev, next, prev_pts, next_ptsOpt, status_Opt, error ); + calcAffineFlowPyrLK ( prev, next, prev_pts, next_ptsAff, status_Aff, error, matrices); + + const double expected_shift = 25; + const double thres = 1; + for(size_t i = 0; i < prev_pts.size(); ++i) + { + circle(cprev, prev_pts[i], 2, CV_RGB(255, 0, 0)); + + if (status_Opt[i]) + { + circle(cnext, next_ptsOpt[i], 2, CV_RGB(0, 0, 255)); + Point2f shift = prev_pts[i] - next_ptsOpt[i]; + + double n = sqrt(shift.ddot(shift)); + if (fabs(n - expected_shift) > thres) + { + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return false; + } + } + + if (status_Aff[i]) + { + circle(cnext, next_ptsAff[i], 4, CV_RGB(0, 255, 0)); + Point2f shift = prev_pts[i] - next_ptsAff[i]; + + double n = sqrt(shift.ddot(shift)); + if (fabs(n - expected_shift) > thres) + { + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + return false; + } + } + + } + + /*namedWindow("P"); imshow("P", cprev); + namedWindow("N"); imshow("N", cnext); + waitKey();*/ + + return true; +} + + +void CV_OptFlowTest::run( int /* start_from */) +{ + + if (!runDense(Point(3, 0))) + return; + + if (!runDense(Point(0, 3))) + return; + + //if (!runDense(Point(3, 3))) return; //probably LK works incorrectly in this case. + + if (!runSparse()) + return; + + ts->set_failed_test_info(cvtest::TS::OK); +} + + +TEST(Video_OpticalFlow, accuracy) { CV_OptFlowTest test; test.safe_run(); } + + diff --git a/modules/video/test/test_optflowpyrlk.cpp b/modules/video/test/test_optflowpyrlk.cpp new file mode 100644 index 000000000..b7ed60dfd --- /dev/null +++ b/modules/video/test/test_optflowpyrlk.cpp @@ -0,0 +1,214 @@ +/*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" + +/* ///////////////////// pyrlk_test ///////////////////////// */ + +class CV_OptFlowPyrLKTest : public cvtest::BaseTest +{ +public: + CV_OptFlowPyrLKTest(); +protected: + void run(int); +}; + + +CV_OptFlowPyrLKTest::CV_OptFlowPyrLKTest() {} + +void CV_OptFlowPyrLKTest::run( int ) +{ + int code = cvtest::TS::OK; + + const double success_error_level = 0.2; + const int bad_points_max = 2; + + /* test parameters */ + double max_err = 0., sum_err = 0; + int pt_cmpd = 0; + int pt_exceed = 0; + int merr_i = 0, merr_j = 0, merr_k = 0; + char filename[1000]; + + CvPoint2D32f *u = 0, *v = 0, *v2 = 0; + CvMat *_u = 0, *_v = 0, *_v2 = 0; + char* status = 0; + + IplImage* imgI = 0; + IplImage* imgJ = 0; + + int n = 0, i = 0; + + sprintf( filename, "%soptflow/%s", ts->get_data_path().c_str(), "lk_prev.dat" ); + _u = (CvMat*)cvLoad( filename ); + + if( !_u ) + { + ts->printf( cvtest::TS::LOG, "could not read %s\n", filename ); + code = cvtest::TS::FAIL_MISSING_TEST_DATA; + goto _exit_; + } + + sprintf( filename, "%soptflow/%s", ts->get_data_path().c_str(), "lk_next.dat" ); + _v = (CvMat*)cvLoad( filename ); + + if( !_v ) + { + ts->printf( cvtest::TS::LOG, "could not read %s\n", filename ); + code = cvtest::TS::FAIL_MISSING_TEST_DATA; + goto _exit_; + } + + if( _u->cols != 2 || CV_MAT_TYPE(_u->type) != CV_32F || + _v->cols != 2 || CV_MAT_TYPE(_v->type) != CV_32F || _v->rows != _u->rows ) + { + ts->printf( cvtest::TS::LOG, "the loaded matrices of points are not valid\n" ); + code = cvtest::TS::FAIL_MISSING_TEST_DATA; + goto _exit_; + + } + + u = (CvPoint2D32f*)_u->data.fl; + v = (CvPoint2D32f*)_v->data.fl; + + /* allocate adidtional buffers */ + _v2 = cvCloneMat( _u ); + v2 = (CvPoint2D32f*)_v2->data.fl; + + /* read first image */ + sprintf( filename, "%soptflow/%s", ts->get_data_path().c_str(), "rock_1.bmp" ); + imgI = cvLoadImage( filename, -1 ); + + if( !imgI ) + { + ts->printf( cvtest::TS::LOG, "could not read %s\n", filename ); + code = cvtest::TS::FAIL_MISSING_TEST_DATA; + goto _exit_; + } + + /* read second image */ + sprintf( filename, "%soptflow/%s", ts->get_data_path().c_str(), "rock_2.bmp" ); + imgJ = cvLoadImage( filename, -1 ); + + if( !imgJ ) + { + ts->printf( cvtest::TS::LOG, "could not read %s\n", filename ); + code = cvtest::TS::FAIL_MISSING_TEST_DATA; + goto _exit_; + } + + n = _u->rows; + status = (char*)cvAlloc(n*sizeof(status[0])); + + /* calculate flow */ + cvCalcOpticalFlowPyrLK( imgI, imgJ, 0, 0, u, v2, n, cvSize( 20, 20 ), + 4, status, 0, cvTermCriteria( CV_TERMCRIT_ITER| + CV_TERMCRIT_EPS, 30, 0.01f ), 0 ); + + /* compare results */ + for( i = 0; i < n; i++ ) + { + if( status[i] != 0 ) + { + double err; + if( cvIsNaN(v[i].x) ) + { + merr_j++; + continue; + } + + err = fabs(v2[i].x - v[i].x) + fabs(v2[i].y - v[i].y); + if( err > max_err ) + { + max_err = err; + merr_i = i; + } + + pt_exceed += err > success_error_level; + if( pt_exceed > bad_points_max ) + { + ts->printf( cvtest::TS::LOG, + "The number of poorly tracked points is too big (>=%d)\n", pt_exceed ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + + sum_err += err; + pt_cmpd++; + } + else + { + if( !cvIsNaN( v[i].x )) + { + merr_i = i; + merr_k++; + ts->printf( cvtest::TS::LOG, "The algorithm lost the point #%d\n", i ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + } + } + + if( max_err > 1 ) + { + ts->printf( cvtest::TS::LOG, "Maximum tracking error is too big (=%g)\n", max_err ); + code = cvtest::TS::FAIL_BAD_ACCURACY; + goto _exit_; + } + +_exit_: + + cvFree( &status ); + cvReleaseMat( &_u ); + cvReleaseMat( &_v ); + cvReleaseMat( &_v2 ); + + cvReleaseImage( &imgI ); + cvReleaseImage( &imgJ ); + + if( code < 0 ) + ts->set_failed_test_info( code ); +} + + +TEST(Video_OpticalFlowPyrLK, accuracy) { CV_OptFlowPyrLKTest test; test.safe_run(); } + +/* End of file. */ diff --git a/modules/video/test/test_precomp.cpp b/modules/video/test/test_precomp.cpp new file mode 100644 index 000000000..5956e13e3 --- /dev/null +++ b/modules/video/test/test_precomp.cpp @@ -0,0 +1 @@ +#include "test_precomp.hpp" diff --git a/modules/video/test/test_precomp.hpp b/modules/video/test/test_precomp.hpp new file mode 100644 index 000000000..71c5e5b7d --- /dev/null +++ b/modules/video/test/test_precomp.hpp @@ -0,0 +1,11 @@ +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/video/tracking.hpp" +#include "opencv2/highgui/highgui.hpp" +#include + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 42c840cbf..3c7bcdf73 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,9 +1,9 @@ -ENABLE_TESTING() +#ENABLE_TESTING() -add_subdirectory(cv) -add_subdirectory(cxcore) -add_subdirectory(ml) -add_subdirectory(cxts) +#add_subdirectory(cv) +#add_subdirectory(cxcore) +#add_subdirectory(ml) +#add_subdirectory(cxts) #if(WITH_CUDA) # set (BUILD_TESTS_GPU OFF CACHE BOOL "Build tests GPU")