From d0210f510e837ae3a4dd0d9a9ea505ae3f062751 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 14 Aug 2015 16:43:10 +0300 Subject: [PATCH 001/105] OpenCV version++. --- modules/core/include/opencv2/core/version.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/version.hpp b/modules/core/include/opencv2/core/version.hpp index 50562ec5ad..7457da76a9 100644 --- a/modules/core/include/opencv2/core/version.hpp +++ b/modules/core/include/opencv2/core/version.hpp @@ -50,7 +50,7 @@ #define CV_VERSION_EPOCH 2 #define CV_VERSION_MAJOR 4 #define CV_VERSION_MINOR 12 -#define CV_VERSION_REVISION 0 +#define CV_VERSION_REVISION 1 #define CVAUX_STR_EXP(__A) #__A #define CVAUX_STR(__A) CVAUX_STR_EXP(__A) From fc0e0239b8d6d3d65f59a2f062bd133d6cc845d9 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Fri, 14 Aug 2015 17:57:54 +0300 Subject: [PATCH 002/105] fixed valgrind warning in polylines (cherry picked from commit 855765986e9cd5c730730dd1f45175b182e737fa) --- modules/core/src/drawing.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/core/src/drawing.cpp b/modules/core/src/drawing.cpp index bd42ac5066..bd8f171b16 100644 --- a/modules/core/src/drawing.cpp +++ b/modules/core/src/drawing.cpp @@ -2216,6 +2216,7 @@ void cv::polylines(InputOutputArray _img, InputArrayOfArrays pts, Mat p = pts.getMat(manyContours ? i : -1); if( p.total() == 0 ) { + ptsptr[i] = NULL; npts[i] = 0; continue; } From ecc53dd7a45f2573e35a897612100ece84dbc0f2 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sat, 15 Aug 2015 00:35:38 +0300 Subject: [PATCH 003/105] fixed memory leak in core ds tests (cherry picked from commit 7719da95526b81af0195a3576774eaac605cd045) --- modules/core/test/test_ds.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/modules/core/test/test_ds.cpp b/modules/core/test/test_ds.cpp index adc8f1a42c..ded52583a2 100644 --- a/modules/core/test/test_ds.cpp +++ b/modules/core/test/test_ds.cpp @@ -493,6 +493,7 @@ class Core_SeqBaseTest : public Core_DynStructBaseTest { public: Core_SeqBaseTest(); + virtual ~Core_SeqBaseTest(); void clear(); void run( int ); @@ -503,11 +504,14 @@ protected: int test_seq_ops( int iters ); }; - Core_SeqBaseTest::Core_SeqBaseTest() { } +Core_SeqBaseTest::~Core_SeqBaseTest() +{ + clear(); +} void Core_SeqBaseTest::clear() { @@ -1208,6 +1212,7 @@ class Core_SetTest : public Core_DynStructBaseTest { public: Core_SetTest(); + virtual ~Core_SetTest(); void clear(); void run( int ); @@ -1221,6 +1226,10 @@ Core_SetTest::Core_SetTest() { } +Core_SetTest::~Core_SetTest() +{ + clear(); +} void Core_SetTest::clear() { @@ -1419,6 +1428,7 @@ class Core_GraphTest : public Core_DynStructBaseTest { public: Core_GraphTest(); + virtual ~Core_GraphTest(); void clear(); void run( int ); @@ -1432,6 +1442,10 @@ Core_GraphTest::Core_GraphTest() { } +Core_GraphTest::~Core_GraphTest() +{ + clear(); +} void Core_GraphTest::clear() { @@ -2044,6 +2058,8 @@ void Core_GraphScanTest::run( int ) CV_TS_SEQ_CHECK_CONDITION( vtx_count == 0 && edge_count == 0, "Not every vertex/edge has been visited" ); update_progressbar(); + + cvReleaseGraphScanner( &scanner ); } // for a random graph the test just checks that every graph vertex and @@ -2108,8 +2124,6 @@ void Core_GraphScanTest::run( int ) catch(int) { } - - cvReleaseGraphScanner( &scanner ); } From bf94e6a91cea3bc4742aa04308855b4cbd0bed27 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sat, 15 Aug 2015 10:06:09 +0300 Subject: [PATCH 004/105] fixed memory leaks in cvtyuv tests (cherry picked from commit b2489d31d66599ac2aaa79af2b753a24649776aa) --- modules/imgproc/test/test_cvtyuv.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/modules/imgproc/test/test_cvtyuv.cpp b/modules/imgproc/test/test_cvtyuv.cpp index 0cce64cdba..7407523fe1 100644 --- a/modules/imgproc/test/test_cvtyuv.cpp +++ b/modules/imgproc/test/test_cvtyuv.cpp @@ -548,7 +548,7 @@ void referenceRGB2YUV(const Mat& rgb, Mat& yuv, RGBreader* rgbReader, YUVwriter* struct ConversionYUV { - ConversionYUV( const int code ) + explicit ConversionYUV( const int code ) { yuvReader_ = YUVreader :: getReader(code); yuvWriter_ = YUVwriter :: getWriter(code); @@ -557,6 +557,24 @@ struct ConversionYUV grayWriter_ = GRAYwriter:: getWriter(code); } + ~ConversionYUV() + { + if (yuvReader_) + delete yuvReader_; + + if (yuvWriter_) + delete yuvWriter_; + + if (rgbReader_) + delete rgbReader_; + + if (rgbReader_) + delete rgbReader_; + + if (grayWriter_) + delete grayWriter_; + } + int getDcn() { return (rgbWriter_ != 0) ? rgbWriter_->channels() : ((grayWriter_ != 0) ? grayWriter_->channels() : yuvWriter_->channels()); From ba3b902da7801fdae78a667c47e81f71f5d212b0 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sat, 15 Aug 2015 10:09:31 +0300 Subject: [PATCH 005/105] fixed memory leaks in floodfill tests (cherry picked from commit d1b882ddcf54e52ac70bb17540c84c4a93553add) --- modules/imgproc/test/test_floodfill.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/imgproc/test/test_floodfill.cpp b/modules/imgproc/test/test_floodfill.cpp index e46e9e120c..8293a12afd 100644 --- a/modules/imgproc/test/test_floodfill.cpp +++ b/modules/imgproc/test/test_floodfill.cpp @@ -501,6 +501,8 @@ _exit_: comp[6] = s1; comp[7] = s2; comp[8] = 0; + + cvReleaseMemStorage(&st); } From 08e38e9ff93b5e88dfe17b184f4e4527aa12c020 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sat, 15 Aug 2015 10:11:52 +0300 Subject: [PATCH 006/105] fixed memory leaks in warpAffine tests (cherry picked from commit b70e27e076a7eea6a1ccc6bb5f367e539e1aa51c) --- modules/imgproc/test/test_imgwarp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/imgproc/test/test_imgwarp.cpp b/modules/imgproc/test/test_imgwarp.cpp index 705bf0a7bc..99cffbc37d 100644 --- a/modules/imgproc/test/test_imgwarp.cpp +++ b/modules/imgproc/test/test_imgwarp.cpp @@ -1426,6 +1426,9 @@ TEST(Imgproc_cvWarpAffine, regression) int h = src->height; cv2DRotationMatrix(cvPoint2D32f(w*0.5f, h*0.5f), 45.0, 1.0, &M); cvWarpAffine(src, dst, &M); + + cvReleaseImage(&src); + cvReleaseImage(&dst); } TEST(Imgproc_fitLine_vector_3d, regression) From 486c40f578e5f8f4428c86eda083ee11212ae068 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sat, 15 Aug 2015 14:30:27 +0300 Subject: [PATCH 007/105] fixed uninitialized values warning in bad arg test class (cherry picked from commit 47cee8715bb086f6b0ca0f5b301d2eec87ce9f2a) --- modules/ts/src/ts.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/ts/src/ts.cpp b/modules/ts/src/ts.cpp index ee8f01171f..bfeae74bb2 100644 --- a/modules/ts/src/ts.cpp +++ b/modules/ts/src/ts.cpp @@ -323,6 +323,7 @@ BadArgTest::BadArgTest() progress = -1; test_case_idx = -1; freq = cv::getTickFrequency(); + t = -1; // oldErrorCbk = 0; // oldErrorCbkData = 0; } @@ -338,6 +339,7 @@ int BadArgTest::run_test_case( int expected_code, const string& _descr ) { test_case_idx = 0; progress = 0; + t = 0; dt = 0; } else From 69c50e01815346475c43407d97ea17fea8973632 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sat, 15 Aug 2015 16:25:25 +0300 Subject: [PATCH 008/105] fixed typo (cherry picked from commit 370d1ff21a99046021da39b74f4081a5b69ce57e) --- modules/features2d/test/test_nearestneighbors.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/features2d/test/test_nearestneighbors.cpp b/modules/features2d/test/test_nearestneighbors.cpp index 8d1ecfd79a..2c64e648f2 100644 --- a/modules/features2d/test/test_nearestneighbors.cpp +++ b/modules/features2d/test/test_nearestneighbors.cpp @@ -65,13 +65,13 @@ protected: 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 checkGetPoints( const Mat& data ); virtual int checkFindBoxed(); virtual int checkFind( const Mat& data ); virtual void releaseModel() = 0; }; -int NearestNeighborTest::checkGetPoins( const Mat& ) +int NearestNeighborTest::checkGetPoints( const Mat& ) { return cvtest::TS::OK; } @@ -129,7 +129,7 @@ void NearestNeighborTest::run( int /*start_from*/ ) { createModel( desc ); - tempCode = checkGetPoins( desc ); + tempCode = checkGetPoints( desc ); if( tempCode != cvtest::TS::OK ) { ts->printf( cvtest::TS::LOG, "bad accuracy of GetPoints \n" ); @@ -162,7 +162,7 @@ public: CV_KDTreeTest_CPP() {} protected: virtual void createModel( const Mat& data ); - virtual int checkGetPoins( const Mat& data ); + virtual int checkGetPoints( const Mat& data ); virtual int findNeighbors( Mat& points, Mat& neighbors ); virtual int checkFindBoxed(); virtual void releaseModel(); @@ -175,7 +175,7 @@ void CV_KDTreeTest_CPP::createModel( const Mat& data ) tr = new KDTree( data, false ); } -int CV_KDTreeTest_CPP::checkGetPoins( const Mat& data ) +int CV_KDTreeTest_CPP::checkGetPoints( const Mat& data ) { Mat res1( data.size(), data.type() ), res3( data.size(), data.type() ); From 16bcc30e429321eeea995575ff9361e2cc4ef250 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sun, 16 Aug 2015 11:46:48 +0300 Subject: [PATCH 009/105] typo (cherry picked from commit 793bdaada7f170cf3fbaf3b421bb3054f37e8a63) --- modules/imgproc/test/test_cvtyuv.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/imgproc/test/test_cvtyuv.cpp b/modules/imgproc/test/test_cvtyuv.cpp index 7407523fe1..5db4972692 100644 --- a/modules/imgproc/test/test_cvtyuv.cpp +++ b/modules/imgproc/test/test_cvtyuv.cpp @@ -568,8 +568,8 @@ struct ConversionYUV if (rgbReader_) delete rgbReader_; - if (rgbReader_) - delete rgbReader_; + if (rgbWriter_) + delete rgbWriter_; if (grayWriter_) delete grayWriter_; From 3231c2f99562eb115296f1432a734fc23b333861 Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Thu, 30 Jul 2015 18:03:48 +0300 Subject: [PATCH 010/105] NearestNeighborTest: use ts->get_rng() instead of (implicit) theRNG() This ensures that test data is not dependent on the order the tests are executed in. (cherry picked from commit 1245cd175283b11800bbbdc52287957cc3d76417) --- modules/features2d/test/test_nearestneighbors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/features2d/test/test_nearestneighbors.cpp b/modules/features2d/test/test_nearestneighbors.cpp index 2c64e648f2..b63bc6433e 100644 --- a/modules/features2d/test/test_nearestneighbors.cpp +++ b/modules/features2d/test/test_nearestneighbors.cpp @@ -125,7 +125,7 @@ int NearestNeighborTest::checkFind( const Mat& data ) void NearestNeighborTest::run( int /*start_from*/ ) { int code = cvtest::TS::OK, tempCode; Mat desc( featuresCount, dims, CV_32FC1 ); - randu( desc, Scalar(minValue), Scalar(maxValue) ); + ts->get_rng().fill( desc, RNG::UNIFORM, minValue, maxValue ); createModel( desc ); From c16f465ff5b3379fb027f6256793d18cb7853d0d Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 20 Aug 2015 12:20:38 +0300 Subject: [PATCH 011/105] fixed "Conditional jump or move depends on uninitialised value" warning (cherry picked from commit f100cdb6d4373c93abc2b5613fae505ed3ce876d) --- modules/imgproc/test/test_imgwarp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/imgproc/test/test_imgwarp.cpp b/modules/imgproc/test/test_imgwarp.cpp index 99cffbc37d..a14ea532a9 100644 --- a/modules/imgproc/test/test_imgwarp.cpp +++ b/modules/imgproc/test/test_imgwarp.cpp @@ -1420,6 +1420,8 @@ TEST(Imgproc_cvWarpAffine, regression) IplImage* src = cvCreateImage(cvSize(100, 100), IPL_DEPTH_8U, 1); IplImage* dst = cvCreateImage(cvSize(100, 100), IPL_DEPTH_8U, 1); + cvZero(src); + float m[6]; CvMat M = cvMat( 2, 3, CV_32F, m ); int w = src->width; From d28e6c9b3693b02766031967dd5f0c7fd8056fc4 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 20 Aug 2015 13:28:10 +0300 Subject: [PATCH 012/105] fixed memory leak caused by illegal memory access (cherry picked from commit 4722b2d0e5b2caf053e53612ea277451c5cc53a0) --- modules/imgproc/test/test_imgwarp_strict.cpp | 34 +++++--------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/modules/imgproc/test/test_imgwarp_strict.cpp b/modules/imgproc/test/test_imgwarp_strict.cpp index c122d0b58b..365f851d5e 100644 --- a/modules/imgproc/test/test_imgwarp_strict.cpp +++ b/modules/imgproc/test/test_imgwarp_strict.cpp @@ -644,8 +644,7 @@ private: }; CV_Remap_Test::CV_Remap_Test() : - CV_ImageWarpBaseTest(), mapx(), mapy(), - borderType(-1), borderValue() + CV_ImageWarpBaseTest(), borderType(-1) { funcs[0] = &CV_Remap_Test::remap_nearest; funcs[1] = &CV_Remap_Test::remap_generic; @@ -666,7 +665,7 @@ void CV_Remap_Test::generate_test_data() // generating the mapx, mapy matrices static const int mapx_types[] = { CV_16SC2, CV_32FC1, CV_32FC2 }; mapx.create(dst.size(), mapx_types[rng.uniform(0, sizeof(mapx_types) / sizeof(int))]); - mapy = Mat(); + mapy.release(); const int n = std::min(std::min(src.cols, src.rows) / 10 + 1, 2); float _n = 0; //static_cast(-n); @@ -693,7 +692,7 @@ void CV_Remap_Test::generate_test_data() { MatIterator_ begin_y = mapy.begin(), end_y = mapy.end(); for ( ; begin_y != end_y; ++begin_y) - begin_y[0] = static_cast(rng.uniform(0, 1024)); + *begin_y = static_cast(rng.uniform(0, 1024)); } break; @@ -701,7 +700,7 @@ void CV_Remap_Test::generate_test_data() { MatIterator_ begin_y = mapy.begin(), end_y = mapy.end(); for ( ; begin_y != end_y; ++begin_y) - begin_y[0] = static_cast(rng.uniform(0, 1024)); + *begin_y = static_cast(rng.uniform(0, 1024)); } break; } @@ -718,8 +717,8 @@ void CV_Remap_Test::generate_test_data() MatIterator_ begin_y = mapy.begin(); for ( ; begin_x != end_x; ++begin_x, ++begin_y) { - begin_x[0] = rng.uniform(_n, fscols); - begin_y[0] = rng.uniform(_n, fsrows); + *begin_x = rng.uniform(_n, fscols); + *begin_y = rng.uniform(_n, fsrows); } } break; @@ -731,8 +730,8 @@ void CV_Remap_Test::generate_test_data() fsrows = static_cast(std::max(src.rows - 1 + n, 0)); for ( ; begin_x != end_x; ++begin_x) { - begin_x[0] = rng.uniform(_n, fscols); - begin_x[1] = rng.uniform(_n, fsrows); + (*begin_x)[0] = rng.uniform(_n, fscols); + (*begin_x)[1] = rng.uniform(_n, fsrows); } } break; @@ -777,23 +776,6 @@ void CV_Remap_Test::prepare_test_data_for_reference_func() { CV_ImageWarpBaseTest::prepare_test_data_for_reference_func(); convert_maps(); -/* - const int ksize = 3; - Mat kernel = getStructuringElement(CV_MOP_ERODE, Size(ksize, ksize)); - Mat mask(src.size(), CV_8UC1, Scalar::all(255)), dst_mask; - cv::erode(src, erode_src, kernel); - cv::erode(mask, dst_mask, kernel, Point(-1, -1), 1, BORDER_CONSTANT, Scalar::all(0)); - bitwise_not(dst_mask, mask); - src.copyTo(erode_src, mask); - dst_mask.release(); - - mask = Scalar::all(0); - kernel = getStructuringElement(CV_MOP_DILATE, kernel.size()); - cv::dilate(src, dilate_src, kernel); - cv::dilate(mask, dst_mask, kernel, Point(-1, -1), 1, BORDER_CONSTANT, Scalar::all(255)); - src.copyTo(dilate_src, dst_mask); - dst_mask.release(); -*/ } void CV_Remap_Test::run_reference_func() From 226ff9391724ae20250d77df2f4febd21fd3b9bf Mon Sep 17 00:00:00 2001 From: a-andre Date: Sun, 2 Aug 2015 13:13:58 +0200 Subject: [PATCH 013/105] install new headers like "opencv2/core.hpp" --- cmake/OpenCVModule.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index a70065750f..ec834d9090 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -488,7 +488,7 @@ macro(ocv_glob_module_sources) file(GLOB_RECURSE lib_srcs "src/*.cpp") file(GLOB_RECURSE lib_int_hdrs "src/*.hpp" "src/*.h") - file(GLOB lib_hdrs "include/opencv2/${name}/*.hpp" "include/opencv2/${name}/*.h") + file(GLOB lib_hdrs "include/opencv2/*.hpp" "include/opencv2/${name}/*.hpp" "include/opencv2/${name}/*.h") file(GLOB lib_hdrs_detail "include/opencv2/${name}/detail/*.hpp" "include/opencv2/${name}/detail/*.h") file(GLOB_RECURSE lib_srcs_apple "src/*.mm") if (APPLE) @@ -629,7 +629,7 @@ macro(ocv_create_module) if(OPENCV_MODULE_${the_module}_HEADERS AND ";${OPENCV_MODULES_PUBLIC};" MATCHES ";${the_module};") foreach(hdr ${OPENCV_MODULE_${the_module}_HEADERS}) string(REGEX REPLACE "^.*opencv2/" "opencv2/" hdr2 "${hdr}") - if(hdr2 MATCHES "^(opencv2/.*)/[^/]+.h(..)?$") + if(hdr2 MATCHES "^(opencv2/?.*)/[^/]+.h(..)?$") install(FILES ${hdr} DESTINATION "${OPENCV_INCLUDE_INSTALL_PATH}/${CMAKE_MATCH_1}" COMPONENT dev) endif() endforeach() From a14e524b32d9252b7c58181d9752804371c82e4f Mon Sep 17 00:00:00 2001 From: a-andre Date: Tue, 18 Aug 2015 18:48:32 +0200 Subject: [PATCH 014/105] fix documentation builder warnings --- modules/core/include/opencv2/core.hpp | 3 --- modules/core/include/opencv2/core/core.hpp | 3 --- modules/imgproc/include/opencv2/imgproc.hpp | 4 ---- modules/imgproc/include/opencv2/imgproc/imgproc.hpp | 4 ---- 4 files changed, 14 deletions(-) diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp index 98564c1a98..12773f8c1b 100644 --- a/modules/core/include/opencv2/core.hpp +++ b/modules/core/include/opencv2/core.hpp @@ -1,6 +1,3 @@ -/*! \file core.hpp - \brief The Core Functionality - */ /*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. diff --git a/modules/core/include/opencv2/core/core.hpp b/modules/core/include/opencv2/core/core.hpp index 67905ec466..3e9c92d097 100644 --- a/modules/core/include/opencv2/core/core.hpp +++ b/modules/core/include/opencv2/core/core.hpp @@ -1,6 +1,3 @@ -/*! \file core.hpp - \brief The Core Functionality - */ /*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index b07ece09b8..112f723274 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1,7 +1,3 @@ -/*! \file imgproc.hpp - \brief The Image Processing - */ - /*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index 339476a633..f46cc643c8 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -1,7 +1,3 @@ -/*! \file imgproc.hpp - \brief The Image Processing - */ - /*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. From 6613d142617ab35db3fbc36f0ff40cb2d1068fef Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Tue, 25 Aug 2015 18:50:06 +0300 Subject: [PATCH 015/105] Add missing packages to the Debian conflict list And refactor the code to make sure that the dev and runtime package lists are in sync. --- cmake/OpenCVPackaging.cmake | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/cmake/OpenCVPackaging.cmake b/cmake/OpenCVPackaging.cmake index bc59f00f8c..be52aee04b 100644 --- a/cmake/OpenCVPackaging.cmake +++ b/cmake/OpenCVPackaging.cmake @@ -115,15 +115,14 @@ if(HAVE_TBB AND NOT BUILD_TBB) endif() endif() -set(STD_OPENCV_LIBS opencv-data libopencv-calib3d2.4 libopencv-contrib2.4 libopencv-core2.4 - libopencv-features2d2.4 libopencv-flann2.4 libopencv-gpu2.4 libopencv-imgproc2.4 - libopencv-ml2.4 libopencv-ocl2.4 libopencv-stitching2.4 libopencv-ts2.4 libopencv-videostab2.4) - -set(STD_OPENCV_DEV libopencv-calib3d-dev libopencv-contrib-dev libopencv-core-dev - libopencv-dev libopencv-features2d-dev libopencv-flann-dev libopencv-gpu-dev - libopencv-highgui-dev libopencv-imgproc-dev libopencv-legacy-dev libopencv-ml-dev - libopencv-objdetect-dev libopencv-ocl-dev libopencv-photo-dev libopencv-stitching-dev - libopencv-superres-dev libopencv-ts-dev libopencv-video-dev libopencv-videostab-dev) +set(STD_OPENCV_LIBS opencv-data) +set(STD_OPENCV_DEV libopencv-dev) + +foreach(module calib3d contrib core features2d flann gpu highgui imgproc legacy + ml objdetect ocl photo stitching superres ts video videostab) + list(APPEND STD_OPENCV_LIBS "libopencv-${module}2.4") + list(APPEND STD_OPENCV_DEV "libopencv-${module}-dev") +endforeach() string(REPLACE ";" ", " CPACK_COMPONENT_LIBS_CONFLICTS "${STD_OPENCV_LIBS}") string(REPLACE ";" ", " CPACK_COMPONENT_LIBS_PROVIDES "${STD_OPENCV_LIBS}") From d122510c4f666a22e792a9b54044198f9c673f24 Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Wed, 26 Aug 2015 13:42:21 +0300 Subject: [PATCH 016/105] Only conflict with packages corresponding to modules that are built --- cmake/OpenCVPackaging.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVPackaging.cmake b/cmake/OpenCVPackaging.cmake index be52aee04b..f6d5bd04a0 100644 --- a/cmake/OpenCVPackaging.cmake +++ b/cmake/OpenCVPackaging.cmake @@ -120,8 +120,10 @@ set(STD_OPENCV_DEV libopencv-dev) foreach(module calib3d contrib core features2d flann gpu highgui imgproc legacy ml objdetect ocl photo stitching superres ts video videostab) - list(APPEND STD_OPENCV_LIBS "libopencv-${module}2.4") - list(APPEND STD_OPENCV_DEV "libopencv-${module}-dev") + if(HAVE_opencv_${module}) + list(APPEND STD_OPENCV_LIBS "libopencv-${module}2.4") + list(APPEND STD_OPENCV_DEV "libopencv-${module}-dev") + endif() endforeach() string(REPLACE ";" ", " CPACK_COMPONENT_LIBS_CONFLICTS "${STD_OPENCV_LIBS}") From 1d58e1a14a1c44a4c1b57c6a665acc7ea40d74ab Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Thu, 27 Aug 2015 16:09:37 +0300 Subject: [PATCH 017/105] fix potential out-of-border access in gpu StereoBeliefPropagation --- modules/gpu/src/stereobp.cpp | 8 ++++---- modules/gpu/test/test_calib3d.cpp | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/gpu/src/stereobp.cpp b/modules/gpu/src/stereobp.cpp index 1677f33446..3b827a3129 100644 --- a/modules/gpu/src/stereobp.cpp +++ b/modules/gpu/src/stereobp.cpp @@ -213,8 +213,8 @@ namespace if (rthis.levels > 1) { - int less_rows = (rows + 1) / 2; - int less_cols = (cols + 1) / 2; + int less_rows = rows / 2; + int less_cols = cols / 2; u2.create(less_rows * rthis.ndisp, less_cols, rthis.msg_type); d2.create(less_rows * rthis.ndisp, less_cols, rthis.msg_type); @@ -283,8 +283,8 @@ namespace for (int i = 1; i < rthis.levels; ++i) { - cols_all[i] = (cols_all[i-1] + 1) / 2; - rows_all[i] = (rows_all[i-1] + 1) / 2; + cols_all[i] = cols_all[i-1] / 2; + rows_all[i] = rows_all[i-1] / 2; datas[i].create(rows_all[i] * rthis.ndisp, cols_all[i], rthis.msg_type); diff --git a/modules/gpu/test/test_calib3d.cpp b/modules/gpu/test/test_calib3d.cpp index ea4039a0cd..80882d431d 100644 --- a/modules/gpu/test/test_calib3d.cpp +++ b/modules/gpu/test/test_calib3d.cpp @@ -114,7 +114,9 @@ GPU_TEST_P(StereoBeliefPropagation, Regression) cv::Mat h_disp(disp); h_disp.convertTo(h_disp, disp_gold.depth()); - EXPECT_MAT_NEAR(disp_gold, h_disp, 0.0); + cv::Rect roi(0, 0, disp_gold.cols - 20, disp_gold.rows - 20); + + EXPECT_MAT_NEAR(disp_gold(roi), h_disp(roi), 0.0); } INSTANTIATE_TEST_CASE_P(GPU_Calib3D, StereoBeliefPropagation, ALL_DEVICES); From 6dcd455ac4cf279b880458a24e055370b61bb1f5 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 31 Aug 2015 17:20:52 +0300 Subject: [PATCH 018/105] fixed memory leaks in modules/features2d/test/test_nearestneighbors.cpp --- .../test/test_lshindex_flannbased_matcher.cpp | 12 +++++++++++- modules/features2d/test/test_nearestneighbors.cpp | 13 ++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/modules/features2d/test/test_lshindex_flannbased_matcher.cpp b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp index ddc81b0c5d..d1c6ea7e43 100644 --- a/modules/features2d/test/test_lshindex_flannbased_matcher.cpp +++ b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp @@ -303,7 +303,8 @@ public: // // constructor // - CV_FeatureDetectorMatcherBaseTest(testparam* _tp, double _accuracy_margin, cv::Feature2D* _fe, cv::DescriptorMatcher *_flmatcher, string _flmatchername, int norm_type_for_bfmatcher) : + CV_FeatureDetectorMatcherBaseTest(testparam* _tp, double _accuracy_margin, cv::Feature2D* _fe, + cv::DescriptorMatcher *_flmatcher, string _flmatchername, int norm_type_for_bfmatcher) : tp(_tp), target_accuracy_margin_from_bfmatcher(_accuracy_margin), fe(_fe), @@ -318,6 +319,15 @@ public: bfmatcher = new cv::BFMatcher(norm_type_for_bfmatcher); } + virtual ~CV_FeatureDetectorMatcherBaseTest() + { + if (bfmatcher) + { + delete bfmatcher; + bfmatcher = NULL; + } + } + // // Main Test method // diff --git a/modules/features2d/test/test_nearestneighbors.cpp b/modules/features2d/test/test_nearestneighbors.cpp index b63bc6433e..bb1d51bf60 100644 --- a/modules/features2d/test/test_nearestneighbors.cpp +++ b/modules/features2d/test/test_nearestneighbors.cpp @@ -159,7 +159,7 @@ void NearestNeighborTest::run( int /*start_from*/ ) { class CV_KDTreeTest_CPP : public NearestNeighborTest { public: - CV_KDTreeTest_CPP() {} + CV_KDTreeTest_CPP() : NearestNeighborTest(), tr(NULL) {} protected: virtual void createModel( const Mat& data ); virtual int checkGetPoints( const Mat& data ); @@ -244,7 +244,7 @@ void CV_KDTreeTest_CPP::releaseModel() class CV_FlannTest : public NearestNeighborTest { public: - CV_FlannTest() {} + CV_FlannTest() : NearestNeighborTest(), index(NULL) { } protected: void createIndex( const Mat& data, const IndexParams& params ); int knnSearch( Mat& points, Mat& neighbors ); @@ -255,6 +255,9 @@ protected: void CV_FlannTest::createIndex( const Mat& data, const IndexParams& params ) { + // release previously allocated index + releaseModel(); + index = new Index( data, params ); } @@ -321,7 +324,11 @@ int CV_FlannTest::radiusSearch( Mat& points, Mat& neighbors ) void CV_FlannTest::releaseModel() { - delete index; + if (index) + { + delete index; + index = NULL; + } } //--------------------------------------- From 7e4e8921bc0749c030ba3913ae23af2204fd58c4 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 31 Aug 2015 17:30:42 +0300 Subject: [PATCH 019/105] fixed memory leak in descriptor regression tests --- .../features2d/test/test_descriptors_regression.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/features2d/test/test_descriptors_regression.cpp b/modules/features2d/test/test_descriptors_regression.cpp index 2185625ae9..a1c07f86bc 100644 --- a/modules/features2d/test/test_descriptors_regression.cpp +++ b/modules/features2d/test/test_descriptors_regression.cpp @@ -61,7 +61,7 @@ static void writeMatInBin( const Mat& mat, const string& filename ) 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 = (int)(mat.step * mat.rows * mat.channels()); + int dataSize = (int)(mat.step * mat.rows); fwrite( (void*)&dataSize, sizeof(int), 1, f ); fwrite( (void*)mat.data, 1, dataSize, f ); fclose(f); @@ -80,12 +80,15 @@ static Mat readMatFromBin( const string& filename ) size_t elements_read4 = fread( (void*)&dataSize, sizeof(int), 1, f ); CV_Assert(elements_read1 == 1 && elements_read2 == 1 && elements_read3 == 1 && elements_read4 == 1); - uchar* data = (uchar*)cvAlloc(dataSize); - size_t elements_read = fread( (void*)data, 1, dataSize, f ); + Mat returnMat(rows, cols, type); + CV_Assert(returnMat.step * returnMat.rows == (size_t)(dataSize)); + + size_t elements_read = fread( (void*)returnMat.data, 1, dataSize, f ); CV_Assert(elements_read == (size_t)(dataSize)); + fclose(f); - return Mat( rows, cols, type, data ); + return returnMat; } return Mat(); } From b5e42d8cc149796adb9cdc8400df6d6f1a9a324f Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 1 Sep 2015 11:26:25 +0300 Subject: [PATCH 020/105] fixed memory leak in ml module (cherry picked from commit d7bb1025f32ed268da7230cae3e8cb413dbab282) --- modules/ml/src/ertrees.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/ml/src/ertrees.cpp b/modules/ml/src/ertrees.cpp index d911834ee5..fe3e04ce90 100644 --- a/modules/ml/src/ertrees.cpp +++ b/modules/ml/src/ertrees.cpp @@ -537,6 +537,9 @@ void CvERTreeTrainData::set_data( const CvMat* _train_data, int _tflag, if( data ) delete data; + if ( pair16u32s_ptr ) + cvFree( &pair16u32s_ptr ); + if (_fdst) cvFree( &_fdst ); if (_idst) From ac33cd688cffd1c3fd541a67a758a84c90cf7bd7 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 1 Sep 2015 12:29:52 +0300 Subject: [PATCH 021/105] fixed memory leak in ANN (cherry picked from commit dfb49097e3013fa279d5882700601f5cabd6cbd8) --- modules/ml/src/ann_mlp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ml/src/ann_mlp.cpp b/modules/ml/src/ann_mlp.cpp index 7323ab57a7..69e4448169 100644 --- a/modules/ml/src/ann_mlp.cpp +++ b/modules/ml/src/ann_mlp.cpp @@ -1535,6 +1535,10 @@ void CvANN_MLP::read( CvFileStorage* fs, CvFileNode* node ) _layer_sizes = (CvMat*)cvReadByName( fs, node, "layer_sizes" ); CV_CALL( create( _layer_sizes, SIGMOID_SYM, 0, 0 )); + + cvReleaseMat( &_layer_sizes ); + _layer_sizes = NULL; + l_count = layer_sizes->cols; CV_CALL( read_params( fs, node )); From 3c3bc123fc8508e1845d0a3e1ea85f66b1a36fc9 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 1 Sep 2015 13:04:33 +0300 Subject: [PATCH 022/105] release filestorage before exception (cherry picked from commit 3a1bb933401446b442348026b9cb680d35f3f449) --- modules/ml/src/svm.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/ml/src/svm.cpp b/modules/ml/src/svm.cpp index 0a5bfffa31..594a43a895 100644 --- a/modules/ml/src/svm.cpp +++ b/modules/ml/src/svm.cpp @@ -2315,7 +2315,12 @@ void CvSVM::write( CvFileStorage* fs, const char* name ) const params.svm_type == CvSVM::ONE_CLASS ? 1 : 0; const CvSVMDecisionFunc* df = decision_func; if( !isSvmModelApplicable(sv_total, var_all, var_count, class_count) ) + { + cvReleaseFileStorage( &fs ); + fs = NULL; + CV_ERROR( CV_StsParseError, "SVM model data is invalid, check sv_count, var_* and class_count tags" ); + } cvStartWriteStruct( fs, name, CV_NODE_MAP, CV_TYPE_NAME_ML_SVM ); From 54693b3fa70c90acffbf9e13d135c5cc0a4665bd Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 1 Sep 2015 11:40:23 +0300 Subject: [PATCH 023/105] fixed memory leak in GBTrees (cherry picked from commit 1b8c2589c057f862008cd1a3be5c8802b7bf1994) --- modules/ml/src/gbt.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/ml/src/gbt.cpp b/modules/ml/src/gbt.cpp index 131eb43fee..aeaf6695af 100644 --- a/modules/ml/src/gbt.cpp +++ b/modules/ml/src/gbt.cpp @@ -1292,13 +1292,18 @@ CvGBTrees::calc_error( CvMLData* _data, int type, std::vector *resp ) return -FLT_MAX; float* pred_resp = 0; + bool needsFreeing = false; + if (resp) { resp->resize(n); pred_resp = &((*resp)[0]); } else + { pred_resp = new float[n]; + needsFreeing = true; + } Sample_predictor predictor = Sample_predictor(this, pred_resp, _data->get_values(), _data->get_missing(), _sample_idx); @@ -1331,6 +1336,9 @@ CvGBTrees::calc_error( CvMLData* _data, int type, std::vector *resp ) err = err / (float)n; } + if (needsFreeing) + delete[]pred_resp; + return err; } From d50c07e303d5bacbdfc5cfdda4767e7485106beb Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 1 Sep 2015 13:22:49 +0300 Subject: [PATCH 024/105] fixed "Conditional jump or move depends on uninitialised value(s)" in GBD (cherry picked from commit 887736bcd4365cff0531e6f763b604a1c6e92e25) --- modules/ml/src/gbt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ml/src/gbt.cpp b/modules/ml/src/gbt.cpp index aeaf6695af..9ed6b4c986 100644 --- a/modules/ml/src/gbt.cpp +++ b/modules/ml/src/gbt.cpp @@ -259,7 +259,7 @@ CvGBTrees::train( const CvMat* _train_data, int _tflag, for (int i=1; idata.fl[i]) - class_labels->data.i[k]) && (kdata.fl[i]) - class_labels->data.i[k])) k++; if (k == j) { From c36582d2df34ed1da9bb04393b65124e37eee497 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 1 Sep 2015 16:17:18 +0300 Subject: [PATCH 025/105] fixed memory leak in flann index (cherry picked from commit 32d7c1950a562d038ff6f8b810ee46298a83fc39) --- modules/flann/src/miniflann.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/flann/src/miniflann.cpp b/modules/flann/src/miniflann.cpp index 5ce3e9051a..9bf503364d 100644 --- a/modules/flann/src/miniflann.cpp +++ b/modules/flann/src/miniflann.cpp @@ -318,12 +318,14 @@ buildIndex_(void*& index, const Mat& wholedata, const Mat& data, const IndexPara ::cvflann::Matrix dataset((ElementType*)data.data, data.rows, data.cols); - IndexType* _index = NULL; - if( !index || getParam(params, "algorithm", FLANN_INDEX_LINEAR) != FLANN_INDEX_LSH) // currently, additional index support is the lsh algorithm only. + // currently, additional index support is the lsh algorithm only. + if( !index || getParam(params, "algorithm", FLANN_INDEX_LINEAR) != FLANN_INDEX_LSH) { - _index = new IndexType(dataset, get_params(params), dist); + Ptr _index = makePtr(dataset, get_params(params), dist); _index->buildIndex(); index = _index; + // HACK to prevent object destruction + _index.obj = NULL; } else // build additional lsh index { From a81f0a51237b8772cfc350a9a91fb15a42215781 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 3 Sep 2015 13:25:29 +0300 Subject: [PATCH 026/105] fixed uninitialized memory writing/reading in flann (cherry picked from commit 3934d61de7d34e9ac8040cf5ced349a51ad2e520) --- modules/flann/include/opencv2/flann/kmeans_index.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/flann/include/opencv2/flann/kmeans_index.h b/modules/flann/include/opencv2/flann/kmeans_index.h index 9c0f4e2df8..e119ceb291 100644 --- a/modules/flann/include/opencv2/flann/kmeans_index.h +++ b/modules/flann/include/opencv2/flann/kmeans_index.h @@ -384,6 +384,8 @@ public: } root_ = pool_.allocate(); + std::memset(root_, 0, sizeof(KMeansNode)); + computeNodeStatistics(root_, indices_, (int)size_); computeClustering(root_, indices_, (int)size_, branching_,0); } @@ -823,11 +825,11 @@ private: variance -= distance_(centers[c], ZeroIterator(), veclen_); node->childs[c] = pool_.allocate(); + std::memset(node->childs[c], 0, sizeof(KMeansNode)); node->childs[c]->radius = radiuses[c]; node->childs[c]->pivot = centers[c]; node->childs[c]->variance = variance; node->childs[c]->mean_radius = mean_radius; - node->childs[c]->indices = NULL; computeClustering(node->childs[c],indices+start, end-start, branching, level+1); start=end; } From 0422054aa196347b706d682e223605d76ca38c64 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 3 Sep 2015 19:13:00 +0300 Subject: [PATCH 027/105] fixed warnings in gpu module (cherry picked from commit 6a05939e1ce552008ecf95451ddf362f2be7a4c3) --- modules/gpu/src/element_operations.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/gpu/src/element_operations.cpp b/modules/gpu/src/element_operations.cpp index 780745d733..280f85a36a 100644 --- a/modules/gpu/src/element_operations.cpp +++ b/modules/gpu/src/element_operations.cpp @@ -2804,7 +2804,7 @@ void cv::gpu::bitwise_not(const GpuMat& src, GpuMat& dst, const GpuMat& mask, St } else { - const int elem_size = src.elemSize1(); + const int elem_size = static_cast(src.elemSize1()); const int num_channels = src.channels(); const int bcols = src.cols * num_channels; @@ -2895,7 +2895,7 @@ void cv::gpu::bitwise_and(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, c } else { - const int elem_size = src1.elemSize1(); + const int elem_size = static_cast(src1.elemSize1()); const int num_channels = src1.channels(); const int bcols = src1.cols * num_channels; @@ -2979,7 +2979,7 @@ void cv::gpu::bitwise_or(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, co } else { - const int elem_size = src1.elemSize1(); + const int elem_size = static_cast(src1.elemSize1()); const int num_channels = src1.channels(); const int bcols = src1.cols * num_channels; @@ -3063,7 +3063,7 @@ void cv::gpu::bitwise_xor(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, c } else { - const int elem_size = src1.elemSize1(); + const int elem_size = static_cast(src1.elemSize1()); const int num_channels = src1.channels(); const int bcols = src1.cols * num_channels; From f4ffcae8d98035cb79d64eb37de5eab6b55690e1 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 3 Sep 2015 17:21:04 +0300 Subject: [PATCH 028/105] initialize padding of CvString with zeros (cherry picked from commit 7b1eb3af7bb82dfd9258f6abdec350688cf65b51) --- modules/core/src/datastructs.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/core/src/datastructs.cpp b/modules/core/src/datastructs.cpp index 76c3b2c371..ddadd39691 100644 --- a/modules/core/src/datastructs.cpp +++ b/modules/core/src/datastructs.cpp @@ -346,6 +346,7 @@ CV_IMPL CvString cvMemStorageAllocString( CvMemStorage* storage, const char* ptr, int len ) { CvString str; + memset(&str, 0, sizeof(CvString)); str.len = len >= 0 ? len : (int)strlen(ptr); str.ptr = (char*)cvMemStorageAlloc( storage, str.len + 1 ); From c7b471f10f41bfbb63309bce7378582f220e1593 Mon Sep 17 00:00:00 2001 From: Elena Shipunova Date: Mon, 7 Sep 2015 13:50:30 +0300 Subject: [PATCH 029/105] do not proceed with removing zero-length slice (cherry picked from commit 036c3b4e6d45dcf81181bb2658601f758c976088) --- modules/core/src/datastructs.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/core/src/datastructs.cpp b/modules/core/src/datastructs.cpp index ddadd39691..3e0bf47f4c 100644 --- a/modules/core/src/datastructs.cpp +++ b/modules/core/src/datastructs.cpp @@ -1689,6 +1689,9 @@ cvSeqRemoveSlice( CvSeq* seq, CvSlice slice ) slice.end_index = slice.start_index + length; + if ( slice.start_index == slice.end_index ) + return; + if( slice.end_index < total ) { CvSeqReader reader_to, reader_from; From 558054a53d9df34960b587b0bbeaeadcde1b729d Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Tue, 8 Sep 2015 16:04:28 +0300 Subject: [PATCH 030/105] fix for gpu::StereoBeliefPropogation: use continuous memory for internal buffers (cherry picked from commit e2a9df408f712f694206974a1f24a864881371c3) --- modules/gpu/src/stereobp.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/gpu/src/stereobp.cpp b/modules/gpu/src/stereobp.cpp index 3b827a3129..dde69d6d44 100644 --- a/modules/gpu/src/stereobp.cpp +++ b/modules/gpu/src/stereobp.cpp @@ -158,7 +158,7 @@ namespace init(stream); - datas[0].create(rows * rthis.ndisp, cols, rthis.msg_type); + createContinuous(rows * rthis.ndisp, cols, rthis.msg_type, datas[0]); comp_data_callers[rthis.msg_type == CV_32F][left.channels()](left, right, datas[0], StreamAccessor::getStream(stream)); @@ -187,10 +187,10 @@ namespace private: void init(Stream& stream) { - u.create(rows * rthis.ndisp, cols, rthis.msg_type); - d.create(rows * rthis.ndisp, cols, rthis.msg_type); - l.create(rows * rthis.ndisp, cols, rthis.msg_type); - r.create(rows * rthis.ndisp, cols, rthis.msg_type); + createContinuous(rows * rthis.ndisp, cols, rthis.msg_type, u); + createContinuous(rows * rthis.ndisp, cols, rthis.msg_type, d); + createContinuous(rows * rthis.ndisp, cols, rthis.msg_type, l); + createContinuous(rows * rthis.ndisp, cols, rthis.msg_type, r); if (rthis.levels & 1) { @@ -216,10 +216,10 @@ namespace int less_rows = rows / 2; int less_cols = cols / 2; - u2.create(less_rows * rthis.ndisp, less_cols, rthis.msg_type); - d2.create(less_rows * rthis.ndisp, less_cols, rthis.msg_type); - l2.create(less_rows * rthis.ndisp, less_cols, rthis.msg_type); - r2.create(less_rows * rthis.ndisp, less_cols, rthis.msg_type); + createContinuous(less_rows * rthis.ndisp, less_cols, rthis.msg_type, u2); + createContinuous(less_rows * rthis.ndisp, less_cols, rthis.msg_type, d2); + createContinuous(less_rows * rthis.ndisp, less_cols, rthis.msg_type, l2); + createContinuous(less_rows * rthis.ndisp, less_cols, rthis.msg_type, r2); if ((rthis.levels & 1) == 0) { @@ -286,7 +286,7 @@ namespace cols_all[i] = cols_all[i-1] / 2; rows_all[i] = rows_all[i-1] / 2; - datas[i].create(rows_all[i] * rthis.ndisp, cols_all[i], rthis.msg_type); + createContinuous(rows_all[i] * rthis.ndisp, cols_all[i], rthis.msg_type, datas[i]); data_step_down_callers[funcIdx](cols_all[i], rows_all[i], rows_all[i-1], datas[i-1], datas[i], cudaStream); } From c22cc67ba8b549e4883e2f6a40495ba3db465a77 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Thu, 10 Sep 2015 10:05:04 +0300 Subject: [PATCH 031/105] revert previous change in gpu::StereoBeliefPropogation (cherry picked from commit f903192c17b1c70182c0c25bb156900ea82b731b) --- modules/gpu/src/stereobp.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/gpu/src/stereobp.cpp b/modules/gpu/src/stereobp.cpp index dde69d6d44..0864fbcadc 100644 --- a/modules/gpu/src/stereobp.cpp +++ b/modules/gpu/src/stereobp.cpp @@ -213,8 +213,8 @@ namespace if (rthis.levels > 1) { - int less_rows = rows / 2; - int less_cols = cols / 2; + int less_rows = (rows + 1) / 2; + int less_cols = (cols + 1) / 2; createContinuous(less_rows * rthis.ndisp, less_cols, rthis.msg_type, u2); createContinuous(less_rows * rthis.ndisp, less_cols, rthis.msg_type, d2); @@ -283,8 +283,8 @@ namespace for (int i = 1; i < rthis.levels; ++i) { - cols_all[i] = cols_all[i-1] / 2; - rows_all[i] = rows_all[i-1] / 2; + cols_all[i] = (cols_all[i-1] + 1) / 2; + rows_all[i] = (rows_all[i-1] + 1) / 2; createContinuous(rows_all[i] * rthis.ndisp, cols_all[i], rthis.msg_type, datas[i]); From 3494d640df0a6f3df03f29fcfc059a25c365fba5 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Thu, 10 Sep 2015 10:05:25 +0300 Subject: [PATCH 032/105] add extra checks to data_step_down to prevent out-of-border access (cherry picked from commit 3ef067cc65cd548a968588f72d3b9ecc92a0e9bb) --- modules/gpu/src/cuda/stereobp.cu | 18 +++++++++--------- modules/gpu/src/stereobp.cpp | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/gpu/src/cuda/stereobp.cu b/modules/gpu/src/cuda/stereobp.cu index 05a19b4199..d011c7f4fe 100644 --- a/modules/gpu/src/cuda/stereobp.cu +++ b/modules/gpu/src/cuda/stereobp.cu @@ -255,7 +255,7 @@ namespace cv { namespace gpu { namespace device /////////////////////////////////////////////////////////////// template - __global__ void data_step_down(int dst_cols, int dst_rows, int src_rows, const PtrStep src, PtrStep dst) + __global__ void data_step_down(int dst_cols, int dst_rows, int src_cols, int src_rows, const PtrStep src, PtrStep dst) { const int x = blockIdx.x * blockDim.x + threadIdx.x; const int y = blockIdx.y * blockDim.y + threadIdx.y; @@ -264,10 +264,10 @@ namespace cv { namespace gpu { namespace device { for (int d = 0; d < cndisp; ++d) { - float dst_reg = src.ptr(d * src_rows + (2*y+0))[(2*x+0)]; - dst_reg += src.ptr(d * src_rows + (2*y+1))[(2*x+0)]; - dst_reg += src.ptr(d * src_rows + (2*y+0))[(2*x+1)]; - dst_reg += src.ptr(d * src_rows + (2*y+1))[(2*x+1)]; + float dst_reg = src.ptr(d * src_rows + ::min(2*y+0, src_rows-1))[::min(2*x+0, src_cols-1)]; + dst_reg += src.ptr(d * src_rows + ::min(2*y+1, src_rows-1))[::min(2*x+0, src_cols-1)]; + dst_reg += src.ptr(d * src_rows + ::min(2*y+0, src_rows-1))[::min(2*x+1, src_cols-1)]; + dst_reg += src.ptr(d * src_rows + ::min(2*y+1, src_rows-1))[::min(2*x+1, src_cols-1)]; dst.ptr(d * dst_rows + y)[x] = saturate_cast(dst_reg); } @@ -275,7 +275,7 @@ namespace cv { namespace gpu { namespace device } template - void data_step_down_gpu(int dst_cols, int dst_rows, int src_rows, const PtrStepSzb& src, const PtrStepSzb& dst, cudaStream_t stream) + void data_step_down_gpu(int dst_cols, int dst_rows, int src_cols, int src_rows, const PtrStepSzb& src, const PtrStepSzb& dst, cudaStream_t stream) { dim3 threads(32, 8, 1); dim3 grid(1, 1, 1); @@ -283,15 +283,15 @@ namespace cv { namespace gpu { namespace device grid.x = divUp(dst_cols, threads.x); grid.y = divUp(dst_rows, threads.y); - data_step_down<<>>(dst_cols, dst_rows, src_rows, (PtrStepSz)src, (PtrStepSz)dst); + data_step_down<<>>(dst_cols, dst_rows, src_cols, src_rows, (PtrStepSz)src, (PtrStepSz)dst); cudaSafeCall( cudaGetLastError() ); if (stream == 0) cudaSafeCall( cudaDeviceSynchronize() ); } - template void data_step_down_gpu(int dst_cols, int dst_rows, int src_rows, const PtrStepSzb& src, const PtrStepSzb& dst, cudaStream_t stream); - template void data_step_down_gpu(int dst_cols, int dst_rows, int src_rows, const PtrStepSzb& src, const PtrStepSzb& dst, cudaStream_t stream); + template void data_step_down_gpu(int dst_cols, int dst_rows, int src_cols, int src_rows, const PtrStepSzb& src, const PtrStepSzb& dst, cudaStream_t stream); + template void data_step_down_gpu(int dst_cols, int dst_rows, int src_cols, int src_rows, const PtrStepSzb& src, const PtrStepSzb& dst, cudaStream_t stream); /////////////////////////////////////////////////////////////// /////////////////// level up messages //////////////////////// diff --git a/modules/gpu/src/stereobp.cpp b/modules/gpu/src/stereobp.cpp index 0864fbcadc..2bcefe3770 100644 --- a/modules/gpu/src/stereobp.cpp +++ b/modules/gpu/src/stereobp.cpp @@ -67,7 +67,7 @@ namespace cv { namespace gpu { namespace device template void comp_data_gpu(const PtrStepSzb& left, const PtrStepSzb& right, const PtrStepSzb& data, cudaStream_t stream); template - void data_step_down_gpu(int dst_cols, int dst_rows, int src_rows, const PtrStepSzb& src, const PtrStepSzb& dst, cudaStream_t stream); + void data_step_down_gpu(int dst_cols, int dst_rows, int src_cols, int src_rows, const PtrStepSzb& src, const PtrStepSzb& dst, cudaStream_t stream); template void level_up_messages_gpu(int dst_idx, int dst_cols, int dst_rows, int src_rows, PtrStepSzb* mus, PtrStepSzb* mds, PtrStepSzb* mls, PtrStepSzb* mrs, cudaStream_t stream); template @@ -253,7 +253,7 @@ namespace void calcBP(GpuMat& disp, Stream& stream) { - typedef void (*data_step_down_t)(int dst_cols, int dst_rows, int src_rows, const PtrStepSzb& src, const PtrStepSzb& dst, cudaStream_t stream); + typedef void (*data_step_down_t)(int dst_cols, int dst_rows, int src_cols, int src_rows, const PtrStepSzb& src, const PtrStepSzb& dst, cudaStream_t stream); static const data_step_down_t data_step_down_callers[2] = { data_step_down_gpu, data_step_down_gpu @@ -288,7 +288,7 @@ namespace createContinuous(rows_all[i] * rthis.ndisp, cols_all[i], rthis.msg_type, datas[i]); - data_step_down_callers[funcIdx](cols_all[i], rows_all[i], rows_all[i-1], datas[i-1], datas[i], cudaStream); + data_step_down_callers[funcIdx](cols_all[i], rows_all[i], cols_all[i-1], rows_all[i-1], datas[i-1], datas[i], cudaStream); } PtrStepSzb mus[] = {u, u2}; From 7746d9b7ccfad2a7ae901aadd934f81b93eb701f Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Wed, 2 Sep 2015 18:15:05 +0300 Subject: [PATCH 033/105] fix for corrent modules dependencies (cherry picked from commit 1c3d83df54d2f39c9403126bf3c1374eaf749dee) --- cmake/OpenCVModule.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index ec834d9090..cae2a39c9d 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -49,6 +49,8 @@ foreach(mod ${OPENCV_MODULES_BUILD} ${OPENCV_MODULES_DISABLED_USER} ${OPENCV_MOD if(HAVE_${mod}) unset(HAVE_${mod} CACHE) endif() + unset(OPENCV_MODULE_${mod}_DEPS CACHE) + unset(OPENCV_MODULE_${mod}_DEPS_EXT CACHE) unset(OPENCV_MODULE_${mod}_REQ_DEPS CACHE) unset(OPENCV_MODULE_${mod}_OPT_DEPS CACHE) unset(OPENCV_MODULE_${mod}_PRIVATE_REQ_DEPS CACHE) From e6f3f3c029bc7cc5e5fe1aad072f96d08a8ebba0 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 17 Sep 2015 14:02:36 +0300 Subject: [PATCH 034/105] OpenCV version++. --- modules/core/include/opencv2/core/version.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/version.hpp b/modules/core/include/opencv2/core/version.hpp index 7457da76a9..096b66e97a 100644 --- a/modules/core/include/opencv2/core/version.hpp +++ b/modules/core/include/opencv2/core/version.hpp @@ -50,7 +50,7 @@ #define CV_VERSION_EPOCH 2 #define CV_VERSION_MAJOR 4 #define CV_VERSION_MINOR 12 -#define CV_VERSION_REVISION 1 +#define CV_VERSION_REVISION 2 #define CVAUX_STR_EXP(__A) #__A #define CVAUX_STR(__A) CVAUX_STR_EXP(__A) From 7d28541bbeed68647d0294a829f3d8dc9253cf2c Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Thu, 17 Sep 2015 18:17:06 +0300 Subject: [PATCH 035/105] test2.py: fail if a downloaded image can't be decoded (cherry picked from commit 56f17e4921a379317a0602d92d7ca3677cabe9d6) --- modules/python/test/test2.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/python/test/test2.py b/modules/python/test/test2.py index 9126160948..d0912cd618 100644 --- a/modules/python/test/test2.py +++ b/modules/python/test/test2.py @@ -22,7 +22,9 @@ class NewOpenCVTests(unittest.TestCase): def get_sample(self, filename, iscolor = cv.CV_LOAD_IMAGE_COLOR): if not filename in self.image_cache: filedata = urllib.urlopen("https://raw.github.com/Itseez/opencv/2.4/" + filename).read() - self.image_cache[filename] = cv2.imdecode(np.fromstring(filedata, dtype=np.uint8), iscolor) + image = cv2.imdecode(np.fromstring(filedata, dtype=np.uint8), iscolor) + self.assertFalse(image is None) + self.image_cache[filename] = image return self.image_cache[filename] def setUp(self): From c5d009d6c4ddf03f4b45acc544b436ca004a41f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hajo=20Nils=20Krabbenho=CC=88ft?= Date: Wed, 16 Sep 2015 22:04:42 +0200 Subject: [PATCH 036/105] fix crash for large BW tif images (cherry picked from commit d38fee759928d631ea2947c67e5ee9eb347693b6) --- modules/highgui/src/grfmt_tiff.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/highgui/src/grfmt_tiff.cpp b/modules/highgui/src/grfmt_tiff.cpp index eeee318c79..067f22de2f 100644 --- a/modules/highgui/src/grfmt_tiff.cpp +++ b/modules/highgui/src/grfmt_tiff.cpp @@ -221,6 +221,11 @@ bool TiffDecoder::readData( Mat& img ) (!is_tiled && tile_height0 == std::numeric_limits::max()) ) tile_height0 = m_height; + if(dst_bpp == 8) { + // we will use TIFFReadRGBA* functions, so allocate temporary buffer for 32bit RGBA + bpp = 8; + ncn = 4; + } const size_t buffer_size = bpp * ncn * tile_height0 * tile_width0; AutoBuffer _buffer( buffer_size ); uchar* buffer = _buffer; From 2fc0ce5c246a70c83691e7f1afd689457e25e70f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hajo=20Nils=20Krabbenho=CC=88ft?= Date: Wed, 16 Sep 2015 22:19:51 +0200 Subject: [PATCH 037/105] buffer_size should be in bytes, not bits (cherry picked from commit 7825cbeb7d70c2e1e558808d7433ece414394177) --- modules/highgui/src/grfmt_tiff.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/highgui/src/grfmt_tiff.cpp b/modules/highgui/src/grfmt_tiff.cpp index 067f22de2f..c7639fe345 100644 --- a/modules/highgui/src/grfmt_tiff.cpp +++ b/modules/highgui/src/grfmt_tiff.cpp @@ -226,7 +226,7 @@ bool TiffDecoder::readData( Mat& img ) bpp = 8; ncn = 4; } - const size_t buffer_size = bpp * ncn * tile_height0 * tile_width0; + const size_t buffer_size = (bpp/bitsPerByte) * ncn * tile_height0 * tile_width0; AutoBuffer _buffer( buffer_size ); uchar* buffer = _buffer; ushort* buffer16 = (ushort*)buffer; From 33cd7f38a8f19b308cc72fc141eab5b5df798ad3 Mon Sep 17 00:00:00 2001 From: robertxwu Date: Mon, 21 Sep 2015 13:57:25 -0700 Subject: [PATCH 038/105] re-submit (cherry picked from commit 4a68cc1675990c46cf53a66f137cb8da09022e4b) --- modules/calib3d/src/calibinit.cpp | 35 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/modules/calib3d/src/calibinit.cpp b/modules/calib3d/src/calibinit.cpp index 5e6ea1d289..000ac5435d 100644 --- a/modules/calib3d/src/calibinit.cpp +++ b/modules/calib3d/src/calibinit.cpp @@ -152,7 +152,7 @@ struct CvCBQuad //static CvMat* debug_img = 0; static int icvGenerateQuads( CvCBQuad **quads, CvCBCorner **corners, - CvMemStorage *storage, CvMat *image, int flags ); + CvMemStorage *storage, CvMat *image, int flags, int *max_quad_buf_size); /*static int icvGenerateQuadsEx( CvCBQuad **out_quads, CvCBCorner **out_corners, @@ -172,7 +172,7 @@ static int icvCleanFoundConnectedQuads( int quad_count, static int icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, int *all_count, CvCBQuad **all_quads, CvCBCorner **corners, - CvSize pattern_size, CvMemStorage* storage ); + CvSize pattern_size, int max_quad_buf_size, CvMemStorage* storage ); static void icvOrderQuad(CvCBQuad *quad, CvCBCorner *corner, int common); @@ -183,7 +183,7 @@ static int icvTrimRow(CvCBQuad **quads, int count, int row, int dir); #endif static int icvAddOuterQuad(CvCBQuad *quad, CvCBQuad **quads, int quad_count, - CvCBQuad **all_quads, int all_count, CvCBCorner **corners); + CvCBQuad **all_quads, int all_count, CvCBCorner **corners, int max_quad_buf_size); static void icvRemoveQuadFromGroup(CvCBQuad **quads, int count, CvCBQuad *q0); @@ -313,6 +313,7 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, // making it difficult to detect smaller squares. for( k = 0; k < 6; k++ ) { + int max_quad_buf_size = 0; for( dilations = min_dilations; dilations <= max_dilations; dilations++ ) { if (found) @@ -368,7 +369,7 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, cvRectangle( thresh_img, cvPoint(0,0), cvPoint(thresh_img->cols-1, thresh_img->rows-1), CV_RGB(255,255,255), 3, 8); - quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img, flags ); + quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img, flags, &max_quad_buf_size); PRINTF("Quad count: %d/%d\n", quad_count, expected_corners_num); } @@ -408,8 +409,8 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, // allocate extra for adding in icvOrderFoundQuads cvFree(&quad_group); cvFree(&corner_group); - quad_group = (CvCBQuad**)cvAlloc( sizeof(quad_group[0]) * (quad_count+quad_count / 2)); - corner_group = (CvCBCorner**)cvAlloc( sizeof(corner_group[0]) * (quad_count+quad_count / 2)*4 ); + quad_group = (CvCBQuad**)cvAlloc( sizeof(quad_group[0]) * max_quad_buf_size); + corner_group = (CvCBCorner**)cvAlloc( sizeof(corner_group[0]) * max_quad_buf_size * 4 ); for( group_idx = 0; ; group_idx++ ) { @@ -424,7 +425,7 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, // maybe delete or add some PRINTF("Starting ordering of inner quads\n"); count = icvOrderFoundConnectedQuads(count, quad_group, &quad_count, &quads, &corners, - pattern_size, storage ); + pattern_size, max_quad_buf_size, storage ); PRINTF("Orig count: %d After ordering: %d\n", icount, count); @@ -623,7 +624,7 @@ icvCheckBoardMonotony( CvPoint2D32f* corners, CvSize pattern_size ) static int icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, int *all_count, CvCBQuad **all_quads, CvCBCorner **corners, - CvSize pattern_size, CvMemStorage* storage ) + CvSize pattern_size, int max_quad_buf_size, CvMemStorage* storage ) { cv::Ptr temp_storage = cvCreateChildMemStorage( storage ); CvSeq* stack = cvCreateSeq( 0, sizeof(*stack), sizeof(void*), temp_storage ); @@ -803,15 +804,18 @@ icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, if (found > 0) { PRINTF("Found %d inner quads not connected to outer quads, repairing\n", found); - for (int i=0; icount < 4 && quads[i]->ordered) { - int added = icvAddOuterQuad(quads[i],quads,quad_count,all_quads,*all_count,corners); + int added = icvAddOuterQuad(quads[i],quads,quad_count,all_quads,*all_count,corners, max_quad_buf_size); *all_count += added; quad_count += added; } } + + if (*all_count >= max_quad_buf_size) + return 0; } @@ -854,11 +858,11 @@ icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, static int icvAddOuterQuad( CvCBQuad *quad, CvCBQuad **quads, int quad_count, - CvCBQuad **all_quads, int all_count, CvCBCorner **corners ) + CvCBQuad **all_quads, int all_count, CvCBCorner **corners, int max_quad_buf_size ) { int added = 0; - for (int i=0; i<4; i++) // find no-neighbor corners + for (int i=0; i<4 && all_count < max_quad_buf_size; i++) // find no-neighbor corners { if (!quad->neighbors[i]) // ok, create and add neighbor { @@ -1649,7 +1653,7 @@ static void icvFindQuadNeighbors( CvCBQuad *quads, int quad_count ) static int icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, - CvMemStorage *storage, CvMat *image, int flags ) + CvMemStorage *storage, CvMat *image, int flags, int *max_quad_buf_size ) { int quad_count = 0; cv::Ptr temp_storage; @@ -1754,8 +1758,9 @@ icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, cvEndFindContours( &scanner ); // allocate quad & corner buffers - *out_quads = (CvCBQuad*)cvAlloc((root->total+root->total / 2) * sizeof((*out_quads)[0])); - *out_corners = (CvCBCorner*)cvAlloc((root->total+root->total / 2) * 4 * sizeof((*out_corners)[0])); + *max_quad_buf_size = MAX(1, (root->total+root->total / 2)) * 2; + *out_quads = (CvCBQuad*)cvAlloc(*max_quad_buf_size * sizeof((*out_quads)[0])); + *out_corners = (CvCBCorner*)cvAlloc(*max_quad_buf_size * 4 * sizeof((*out_corners)[0])); // Create array of quads structures for( idx = 0; idx < root->total; idx++ ) From 2243bfa181dec54bf6cecca4f99a94296f807127 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sat, 15 Aug 2015 18:30:08 +0300 Subject: [PATCH 039/105] repaired gstreamer camera capture: 1. Enabled property retrieval: height, width, FPS 2. Fixed issue when isOpened returns always true even for non-existing devices 3. Ability to work with non-0 device. Camera capture index is taken into account (cherry picked from commit dbd7912b88e36a045d1086d54f77467de5ab006f) --- modules/highgui/src/cap.cpp | 6 +- modules/highgui/src/cap_gstreamer.cpp | 130 ++++++++++++++++---------- 2 files changed, 83 insertions(+), 53 deletions(-) diff --git a/modules/highgui/src/cap.cpp b/modules/highgui/src/cap.cpp index 491e388559..5c17848e8d 100644 --- a/modules/highgui/src/cap.cpp +++ b/modules/highgui/src/cap.cpp @@ -238,10 +238,12 @@ CV_IMPL CvCapture * cvCreateCameraCapture (int index) #endif #ifdef HAVE_GSTREAMER - capture = cvCreateCapture_GStreamer(CV_CAP_GSTREAMER_V4L2, 0); + capture = cvCreateCapture_GStreamer(CV_CAP_GSTREAMER_V4L2, + reinterpret_cast(index)); if (capture) return capture; - capture = cvCreateCapture_GStreamer(CV_CAP_GSTREAMER_V4L, 0); + capture = cvCreateCapture_GStreamer(CV_CAP_GSTREAMER_V4L, + reinterpret_cast(index)); if (capture) return capture; #endif diff --git a/modules/highgui/src/cap_gstreamer.cpp b/modules/highgui/src/cap_gstreamer.cpp index cae719996e..fe4eaf25e3 100644 --- a/modules/highgui/src/cap_gstreamer.cpp +++ b/modules/highgui/src/cap_gstreamer.cpp @@ -371,9 +371,7 @@ void CvCapture_GStreamer::startPipeline() if (status == GST_STATE_CHANGE_ASYNC) { // wait for status update - GstState st1; - GstState st2; - status = gst_element_get_state(pipeline, &st1, &st2, GST_CLOCK_TIME_NONE); + status = gst_element_get_state(pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); } if (status == GST_STATE_CHANGE_FAILURE) { @@ -568,21 +566,39 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) GstElementFactory * testfac; GstStateChangeReturn status; - if (type == CV_CAP_GSTREAMER_V4L){ + int cameraID = -1; + if (type == CV_CAP_GSTREAMER_V4L || + type == CV_CAP_GSTREAMER_V4L2) + { + cameraID = static_cast(reinterpret_cast(filename)); + } + + std::stringstream stdstream; + std::string stdfilename; + + if (type == CV_CAP_GSTREAMER_V4L) + { testfac = gst_element_factory_find("v4lsrc"); if (!testfac){ return false; } g_object_unref(G_OBJECT(testfac)); - filename = "v4lsrc ! "COLOR_ELEM" ! appsink"; + + stdstream << "v4lsrc device=/dev/video" << cameraID << " ! " << COLOR_ELEM << " ! appsink"; + stdfilename = stdstream.str(); + filename = stdfilename.c_str(); } - if (type == CV_CAP_GSTREAMER_V4L2){ + else if (type == CV_CAP_GSTREAMER_V4L2) + { testfac = gst_element_factory_find("v4l2src"); if (!testfac){ return false; } g_object_unref(G_OBJECT(testfac)); - filename = "v4l2src ! "COLOR_ELEM" ! appsink"; + + stdstream << "v4l2src device=/dev/video" << cameraID << " ! " << COLOR_ELEM << " ! appsink"; + stdfilename = stdstream.str(); + filename = stdfilename.c_str(); } @@ -620,7 +636,9 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) stream = true; manualpipeline = true; } - } else { + } + else + { stream = true; uri = g_strdup(filename); } @@ -641,64 +659,77 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) uridecodebin = gst_element_make_from_uri(GST_URI_SRC, uri, "src", NULL); #endif element_from_uri = true; - }else{ + } + else + { uridecodebin = gst_element_factory_make("uridecodebin", NULL); g_object_set(G_OBJECT(uridecodebin), "uri", uri, NULL); } g_free(protocol); - if(!uridecodebin) { + if(!uridecodebin) + { //fprintf(stderr, "GStreamer: Error opening bin: %s\n", err->message); close(); return false; } } - if(manualpipeline) + if (manualpipeline) { GstIterator *it = NULL; #if GST_VERSION_MAJOR == 0 it = gst_bin_iterate_sinks(GST_BIN(uridecodebin)); - if(gst_iterator_next(it, (gpointer *)&sink) != GST_ITERATOR_OK) { + if (gst_iterator_next(it, (gpointer *)&sink) != GST_ITERATOR_OK) + { CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); return false; } #else - it = gst_bin_iterate_sinks (GST_BIN(uridecodebin)); + it = gst_bin_iterate_elements(GST_BIN(uridecodebin)); - gboolean done = FALSE; + gboolean done = false; GstElement *element = NULL; gchar* name = NULL; GValue value = G_VALUE_INIT; - while (!done) { - switch (gst_iterator_next (it, &value)) { + while (!done) + { + switch (gst_iterator_next (it, &value)) + { case GST_ITERATOR_OK: - element = GST_ELEMENT (g_value_get_object (&value)); - name = gst_element_get_name(element); - if (name){ - if(strstr(name, "opencvsink") != NULL || strstr(name, "appsink") != NULL) { - sink = GST_ELEMENT ( gst_object_ref (element) ); - done = TRUE; + element = GST_ELEMENT (g_value_get_object (&value)); + name = gst_element_get_name(element); + if (name) + { + if (strstr(name, "opencvsink") != NULL || strstr(name, "appsink") != NULL) + { + sink = GST_ELEMENT ( gst_object_ref (element) ); + done = sink && color; + } + else if (strstr(name, COLOR_ELEM) != NULL) + { + color = GST_ELEMENT ( gst_object_ref (element) ); + done = sink && color; + } + g_free(name); } - g_free(name); - } - g_value_unset (&value); + g_value_unset (&value); - break; + break; case GST_ITERATOR_RESYNC: - gst_iterator_resync (it); - break; + gst_iterator_resync (it); + break; case GST_ITERATOR_ERROR: case GST_ITERATOR_DONE: - done = TRUE; - break; - } + done = TRUE; + break; + } } gst_iterator_free (it); - - if (!sink){ + if (!sink) + { CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); return false; } @@ -715,18 +746,23 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) gst_bin_add_many(GST_BIN(pipeline), uridecodebin, color, sink, NULL); - if(element_from_uri) { - if(!gst_element_link(uridecodebin, color)) { + if(element_from_uri) + { + if(!gst_element_link(uridecodebin, color)) + { CV_ERROR(CV_StsError, "GStreamer: cannot link color -> sink\n"); gst_object_unref(pipeline); pipeline = NULL; return false; } - }else{ + } + else + { g_signal_connect(uridecodebin, "pad-added", G_CALLBACK(newPad), color); } - if(!gst_element_link(color, sink)) { + if(!gst_element_link(color, sink)) + { CV_ERROR(CV_StsError, "GStreamer: cannot link color -> sink\n"); gst_object_unref(pipeline); pipeline = NULL; @@ -754,16 +790,13 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) gst_app_sink_set_caps(GST_APP_SINK(sink), caps); gst_caps_unref(caps); - // For video files only: set pipeline to PAUSED state to get its duration - if (file) { - status = gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PAUSED); + status = gst_element_set_state(GST_ELEMENT(pipeline), + file ? GST_STATE_PAUSED : GST_STATE_PLAYING); if (status == GST_STATE_CHANGE_ASYNC) { // wait for status update - GstState st1; - GstState st2; - status = gst_element_get_state(pipeline, &st1, &st2, GST_CLOCK_TIME_NONE); + status = gst_element_get_state(pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); } if (status == GST_STATE_CHANGE_FAILURE) { @@ -814,14 +847,9 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) fps = (double)num/(double)denom; - // GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline"); - } - else - { - duration = -1; - width = -1; - height = -1; - fps = -1; + // GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline") + + stopPipeline(); } __END__; From 9a3e53e7386b7a94976d216dae41ddb48600db3d Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sun, 16 Aug 2015 21:01:22 +0300 Subject: [PATCH 040/105] repaired GStreamer 0.10 version (cherry picked from commit c19ed39a785d7ac9e39c117cfacc3827024619f7) --- cmake/OpenCVFindLibsVideo.cmake | 6 +++--- modules/highgui/src/cap_gstreamer.cpp | 30 +++++++++++++++------------ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/cmake/OpenCVFindLibsVideo.cmake b/cmake/OpenCVFindLibsVideo.cmake index 1654df73ca..514aa5cd3d 100644 --- a/cmake/OpenCVFindLibsVideo.cmake +++ b/cmake/OpenCVFindLibsVideo.cmake @@ -12,8 +12,8 @@ endif(WITH_VFW) # --- GStreamer --- ocv_clear_vars(HAVE_GSTREAMER) -# try to find gstreamer 1.x first -if(WITH_GSTREAMER) +# try to find gstreamer 1.x first if 0.10 was not requested +if(WITH_GSTREAMER AND NOT WITH_GSTREAMER_0_10) CHECK_MODULE(gstreamer-base-1.0 HAVE_GSTREAMER_BASE) CHECK_MODULE(gstreamer-video-1.0 HAVE_GSTREAMER_VIDEO) CHECK_MODULE(gstreamer-app-1.0 HAVE_GSTREAMER_APP) @@ -29,7 +29,7 @@ if(WITH_GSTREAMER) set(GSTREAMER_PBUTILS_VERSION ${ALIASOF_gstreamer-pbutils-1.0_VERSION}) endif() -endif(WITH_GSTREAMER) +endif() # gstreamer support was requested but could not find gstreamer 1.x, # so fallback/try to find gstreamer 0.10 diff --git a/modules/highgui/src/cap_gstreamer.cpp b/modules/highgui/src/cap_gstreamer.cpp index fe4eaf25e3..957587c219 100644 --- a/modules/highgui/src/cap_gstreamer.cpp +++ b/modules/highgui/src/cap_gstreamer.cpp @@ -75,10 +75,13 @@ #if GST_VERSION_MAJOR == 0 #define COLOR_ELEM "ffmpegcolorspace" +#define COLOR_ELEM_NAME "ffmpegcsp" #elif FULL_GST_VERSION < VERSION_NUM(1,5,0) #define COLOR_ELEM "videoconvert" +#define COLOR_ELEM_NAME COLOR_ELEM #else #define COLOR_ELEM "autovideoconvert" +#define COLOR_ELEM_NAME COLOR_ELEM #endif void toFraction(double decimal, double &numerator, double &denominator); @@ -677,28 +680,27 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) if (manualpipeline) { - GstIterator *it = NULL; -#if GST_VERSION_MAJOR == 0 - it = gst_bin_iterate_sinks(GST_BIN(uridecodebin)); - if (gst_iterator_next(it, (gpointer *)&sink) != GST_ITERATOR_OK) - { - CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); - return false; - } -#else - it = gst_bin_iterate_elements(GST_BIN(uridecodebin)); + GstIterator *it = gst_bin_iterate_elements(GST_BIN(uridecodebin)); - gboolean done = false; GstElement *element = NULL; + gboolean done = false; gchar* name = NULL; +#if GST_VERSION_MAJOR > 0 GValue value = G_VALUE_INIT; +#endif while (!done) { +#if GST_VERSION_MAJOR > 0 switch (gst_iterator_next (it, &value)) { case GST_ITERATOR_OK: element = GST_ELEMENT (g_value_get_object (&value)); +#else + switch (gst_iterator_next (it, (gpointer *)&element)) + { + case GST_ITERATOR_OK: +#endif name = gst_element_get_name(element); if (name) { @@ -707,14 +709,16 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) sink = GST_ELEMENT ( gst_object_ref (element) ); done = sink && color; } - else if (strstr(name, COLOR_ELEM) != NULL) + else if (strstr(name, COLOR_ELEM_NAME) != NULL) { color = GST_ELEMENT ( gst_object_ref (element) ); done = sink && color; } g_free(name); } +#if GST_VERSION_MAJOR > 0 g_value_unset (&value); +#endif break; case GST_ITERATOR_RESYNC: @@ -733,7 +737,7 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); return false; } -#endif + pipeline = uridecodebin; } else From 56654ae360b7ecdb658398fd5efd5a0c76ada71d Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sun, 16 Aug 2015 21:25:36 +0300 Subject: [PATCH 041/105] added some property setting and getting (cherry picked from commit 75fcedf0edbc83a900918de1a25195eaefc214f5) --- modules/highgui/src/cap_gstreamer.cpp | 50 +++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/modules/highgui/src/cap_gstreamer.cpp b/modules/highgui/src/cap_gstreamer.cpp index 957587c219..cab652dfd2 100644 --- a/modules/highgui/src/cap_gstreamer.cpp +++ b/modules/highgui/src/cap_gstreamer.cpp @@ -145,6 +145,7 @@ protected: gpointer data); GstElement* pipeline; GstElement* uridecodebin; + GstElement* v4l2src; GstElement* color; GstElement* sink; #if GST_VERSION_MAJOR > 0 @@ -168,6 +169,7 @@ void CvCapture_GStreamer::init() { pipeline = NULL; uridecodebin = NULL; + v4l2src = NULL; color = NULL; sink = NULL; #if GST_VERSION_MAJOR > 0 @@ -707,14 +709,18 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) if (strstr(name, "opencvsink") != NULL || strstr(name, "appsink") != NULL) { sink = GST_ELEMENT ( gst_object_ref (element) ); - done = sink && color; } else if (strstr(name, COLOR_ELEM_NAME) != NULL) { color = GST_ELEMENT ( gst_object_ref (element) ); - done = sink && color; + } + else if (strstr(name, "v4l") != NULL) + { + v4l2src = GST_ELEMENT ( gst_object_ref (element) ); } g_free(name); + + done = sink && color && v4l2src; } #if GST_VERSION_MAJOR > 0 g_value_unset (&value); @@ -884,7 +890,7 @@ double CvCapture_GStreamer::getProperty( int propId ) if(!pipeline) { CV_WARN("GStreamer: no pipeline"); - return false; + return 0; } switch(propId) { @@ -893,7 +899,7 @@ double CvCapture_GStreamer::getProperty( int propId ) status = gst_element_query_position(sink, FORMAT, &value); if(!status) { CV_WARN("GStreamer: unable to query position of stream"); - return false; + return 0; } return value * 1e-6; // nano seconds to milli seconds case CV_CAP_PROP_POS_FRAMES: @@ -901,7 +907,7 @@ double CvCapture_GStreamer::getProperty( int propId ) status = gst_element_query_position(sink, FORMAT, &value); if(!status) { CV_WARN("GStreamer: unable to query position of stream"); - return false; + return 0; } return value; case CV_CAP_PROP_POS_AVI_RATIO: @@ -909,7 +915,7 @@ double CvCapture_GStreamer::getProperty( int propId ) status = gst_element_query_position(sink, FORMAT, &value); if(!status) { CV_WARN("GStreamer: unable to query position of stream"); - return false; + return 0; } return ((double) value) / GST_FORMAT_PERCENT_MAX; case CV_CAP_PROP_FRAME_WIDTH: @@ -928,6 +934,21 @@ double CvCapture_GStreamer::getProperty( int propId ) case CV_CAP_PROP_CONTRAST: case CV_CAP_PROP_SATURATION: case CV_CAP_PROP_HUE: + if (v4l2src) + { + const gchar * propName = + propId == CV_CAP_PROP_BRIGHTNESS ? "brightness" : + propId == CV_CAP_PROP_CONTRAST ? "contrast" : + propId == CV_CAP_PROP_SATURATION ? "saturation" : + propId == CV_CAP_PROP_HUE ? "hue" : NULL; + + if (propName) + { + gint32 value32 = 0; + g_object_get(G_OBJECT(v4l2src), propName, &value32, NULL); + return value32; + } + } case CV_CAP_PROP_GAIN: case CV_CAP_PROP_CONVERT_RGB: break; @@ -944,7 +965,7 @@ double CvCapture_GStreamer::getProperty( int propId ) #undef FORMAT - return false; + return 0; } /*! @@ -1023,6 +1044,21 @@ bool CvCapture_GStreamer::setProperty( int propId, double value ) case CV_CAP_PROP_CONTRAST: case CV_CAP_PROP_SATURATION: case CV_CAP_PROP_HUE: + if (v4l2src) + { + const gchar * propName = + propId == CV_CAP_PROP_BRIGHTNESS ? "brightness" : + propId == CV_CAP_PROP_CONTRAST ? "contrast" : + propId == CV_CAP_PROP_SATURATION ? "saturation" : + propId == CV_CAP_PROP_HUE ? "hue" : NULL; + + if (propName) + { + gint32 value32 = cv::saturate_cast(value); + g_object_set(G_OBJECT(v4l2src), propName, &value32, NULL); + return true; + } + } case CV_CAP_PROP_GAIN: case CV_CAP_PROP_CONVERT_RGB: break; From 1ea65689519c1f907d6ff2a01f46485be0ee6076 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 24 Sep 2015 15:52:17 +0300 Subject: [PATCH 042/105] Fixed wrong-name-for-changelog-of-native-package warning for deb packages. (cherry picked from commit cb1dc7cb6e8886f02864467aeae6c584a7eccc76) --- cmake/OpenCVPackaging.cmake | 13 +++++++++++++ cmake/templates/changelog.Debian.in | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/cmake/OpenCVPackaging.cmake b/cmake/OpenCVPackaging.cmake index f6d5bd04a0..a1151c1cf3 100644 --- a/cmake/OpenCVPackaging.cmake +++ b/cmake/OpenCVPackaging.cmake @@ -201,6 +201,19 @@ if(CPACK_GENERATOR STREQUAL "DEB") install(FILES "${DEBIAN_CHANGELOG_OUT_FILE_GZ}" DESTINATION "share/doc/${CPACK_DEBIAN_COMPONENT_${comp_upcase}_NAME}" COMPONENT "${comp}") + + set(CHANGELOG_OUT_FILE "${CMAKE_BINARY_DIR}/deb-packages-gen/${comp}/changelog") + set(CHANGELOG_OUT_FILE_GZ "${CMAKE_BINARY_DIR}/deb-packages-gen/${comp}/changelog.gz") + file(WRITE ${CHANGELOG_OUT_FILE} "Upstream changelog stub. See https://github.com/Itseez/opencv/wiki/ChangeLog") + + execute_process(COMMAND "${GZIP_TOOL}" "-cf9" "${CHANGELOG_OUT_FILE}" + OUTPUT_FILE "${CHANGELOG_OUT_FILE_GZ}" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") + + install(FILES "${CHANGELOG_OUT_FILE_GZ}" + DESTINATION "share/doc/${CPACK_DEBIAN_COMPONENT_${comp_upcase}_NAME}" + COMPONENT "${comp}") + endforeach() endif() diff --git a/cmake/templates/changelog.Debian.in b/cmake/templates/changelog.Debian.in index 70486e8090..a47ec42bc8 100644 --- a/cmake/templates/changelog.Debian.in +++ b/cmake/templates/changelog.Debian.in @@ -1,4 +1,5 @@ @CHANGELOG_PACKAGE_NAME@ (@CPACK_PACKAGE_VERSION@) unstable; urgency=low - * Debian changelog stub. See upstream changelog or release notes in user + * Debian changelog stub. See https://github.com/Itseez/opencv/wiki/ChangeLog + or release notes in user documentation for more details. -- @CPACK_PACKAGE_CONTACT@ @CHANGELOG_PACKAGE_DATE@ From 15b313ce4bd08cb380dba6a569eb816e31ceb084 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 12 Oct 2015 00:11:45 +0300 Subject: [PATCH 043/105] fixed memory leak in findHomography tests (cherry picked from commit ec5244a73abf36014fdd2763ff1f16e5e986cbb4) --- modules/calib3d/test/test_homography.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/calib3d/test/test_homography.cpp b/modules/calib3d/test/test_homography.cpp index f68af1d7c1..2db0a3c1ab 100644 --- a/modules/calib3d/test/test_homography.cpp +++ b/modules/calib3d/test/test_homography.cpp @@ -12,6 +12,7 @@ // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2015, Itseez Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -562,6 +563,9 @@ void CV_HomographyTest::run(int) default: continue; } } + + delete[]src_data; + src_data = NULL; } } From 96a2edb3756af078d8d7752e2911687df6d1bf0f Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 12 Oct 2015 00:37:46 +0300 Subject: [PATCH 044/105] delete video readers (cherry picked from commit 0d5b739d35e7a0c2d2b88db58994397ae969f468) --- modules/highgui/test/test_ffmpeg.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/highgui/test/test_ffmpeg.cpp b/modules/highgui/test/test_ffmpeg.cpp index 09de119ac6..31108f857c 100644 --- a/modules/highgui/test/test_ffmpeg.cpp +++ b/modules/highgui/test/test_ffmpeg.cpp @@ -396,6 +396,10 @@ TEST(Highgui_Video_parallel_writers_and_readers, accuracy) if (code == 1) std::cerr << "Couldn't delete " << *i << std::endl; } + + // delete the readers + for (std::vector::iterator i = readers.begin(), end = readers.end(); i != end; ++i) + delete *i; } #endif From a5c20f859281a93689cb6f1b46006c2233e8bd44 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 12 Oct 2015 11:37:26 +0300 Subject: [PATCH 045/105] Debian formatted copyright file added to all debian packages. (cherry picked from commit 9d24b3c3b0133c29a642aa5a0d2a0634333287f3) --- cmake/OpenCVPackaging.cmake | 9 +++++++- cmake/templates/copyright | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 cmake/templates/copyright diff --git a/cmake/OpenCVPackaging.cmake b/cmake/OpenCVPackaging.cmake index a1151c1cf3..e64d447bfa 100644 --- a/cmake/OpenCVPackaging.cmake +++ b/cmake/OpenCVPackaging.cmake @@ -19,6 +19,7 @@ OpenCV makes it easy for businesses to utilize and modify the code.") set(CPACK_PACKAGE_VERSION_MINOR "${OPENCV_VERSION_MINOR}") set(CPACK_PACKAGE_VERSION_PATCH "${OPENCV_VERSION_PATCH}") set(CPACK_PACKAGE_VERSION "${OPENCV_VCSVERSION}") + set(OPENCV_DEBIAN_COPYRIGHT_FILE "") endif(NOT OPENCV_CUSTOM_PACKAGE_INFO) set(CPACK_STRIP_FILES 1) @@ -192,7 +193,7 @@ if(CPACK_GENERATOR STREQUAL "DEB") set(DEBIAN_CHANGELOG_OUT_FILE "${CMAKE_BINARY_DIR}/deb-packages-gen/${comp}/changelog.Debian") set(DEBIAN_CHANGELOG_OUT_FILE_GZ "${CMAKE_BINARY_DIR}/deb-packages-gen/${comp}/changelog.Debian.gz") set(CHANGELOG_PACKAGE_NAME "${CPACK_DEBIAN_COMPONENT_${comp_upcase}_NAME}") - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/changelog.Debian.in" "${DEBIAN_CHANGELOG_OUT_FILE}" @ONLY) + configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/changelog.Debian.in" "${DEBIAN_CHANGELOG_OUT_FILE}" @ONLY) execute_process(COMMAND "${GZIP_TOOL}" "-cf9" "${DEBIAN_CHANGELOG_OUT_FILE}" OUTPUT_FILE "${DEBIAN_CHANGELOG_OUT_FILE_GZ}" @@ -214,6 +215,12 @@ if(CPACK_GENERATOR STREQUAL "DEB") DESTINATION "share/doc/${CPACK_DEBIAN_COMPONENT_${comp_upcase}_NAME}" COMPONENT "${comp}") + if(OPENCV_DEBIAN_COPYRIGHT_FILE) + install(FILES "${OPENCV_DEBIAN_COPYRIGHT_FILE}" + DESTINATION "share/doc/${CPACK_DEBIAN_COMPONENT_${comp_upcase}_NAME}" + COMPONENT "${comp}") + endif() + endforeach() endif() diff --git a/cmake/templates/copyright b/cmake/templates/copyright new file mode 100644 index 0000000000..5e66dab7fc --- /dev/null +++ b/cmake/templates/copyright @@ -0,0 +1,43 @@ +Format: http://dep.debian.net/deps/dep5 + +Files: * +Copyright: 2000-2015, Intel Corporation + 2009-2011, Willow Garage Inc. + 2009-2015, NVIDIA Corporation + 2010-2013, Advanced Micro Devices, Inc. + 2015, OpenCV Foundation + 2015, Itseez Inc. +License: BSD-3-clause + By downloading, copying, installing or using the software you agree to this + license. If you do not agree to this license, do not download, install, copy + or use the software. +. +. + License Agreement + For Open Source Computer Vision Library + (3-clause BSD License) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: +. + \* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +. + \* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +. + \* Neither the names of the copyright holders nor the names of the + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + This software is provided by the copyright holders and contributors "as is" + and any express or implied warranties, including, but not limited to, the + implied warranties of merchantability and fitness for a particular purpose are + disclaimed. In no event shall copyright holders or contributors be liable for + any direct, indirect, incidental, special, exemplary, or consequential damages + (including, but not limited to, procurement of substitute goods or services; + loss of use, data, or profits; or business interruption) however caused + and on any theory of liability, whether in contract, strict liability, + or tort (including negligence or otherwise) arising in any way out of + the use of this software, even if advised of the possibility of such damage. From 8a37fc82238a8ade347ea1af483227db28737f0d Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 13 Oct 2015 15:02:38 +0300 Subject: [PATCH 046/105] Added missing copyright headers. (cherry picked from commit 408107ce6d16ae118167ffe22fda6bdcdb9514cb) --- include/opencv/cxmisc.h | 42 +++++++++++++++++++++ modules/flann/include/opencv2/flann/dummy.h | 29 ++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/include/opencv/cxmisc.h b/include/opencv/cxmisc.h index 6446944643..3a76656290 100644 --- a/include/opencv/cxmisc.h +++ b/include/opencv/cxmisc.h @@ -1,3 +1,45 @@ +/*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*/ + #ifndef __OPENCV_OLD_CXMISC_H__ #define __OPENCV_OLD_CXMISC_H__ diff --git a/modules/flann/include/opencv2/flann/dummy.h b/modules/flann/include/opencv2/flann/dummy.h index 26bd3fa5dd..339098180c 100644 --- a/modules/flann/include/opencv2/flann/dummy.h +++ b/modules/flann/include/opencv2/flann/dummy.h @@ -1,3 +1,32 @@ +/*********************************************************************** + * Software License Agreement (BSD License) + * + * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. + * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. + * + * THE BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + *************************************************************************/ #ifndef OPENCV_FLANN_DUMMY_H_ #define OPENCV_FLANN_DUMMY_H_ From 2d3e1703707a0734de106759ecce4bd5275eb825 Mon Sep 17 00:00:00 2001 From: a-andre Date: Tue, 13 Oct 2015 18:16:14 +0200 Subject: [PATCH 047/105] install opencv2/highgui.hpp header (cherry picked from commit d16fb3051296feebd04cdd7a372e0049629c155f) --- modules/highgui/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index 7905872b80..44cf98c613 100644 --- a/modules/highgui/CMakeLists.txt +++ b/modules/highgui/CMakeLists.txt @@ -64,7 +64,7 @@ set(highgui_srcs src/window.cpp ) -file(GLOB highgui_ext_hdrs "include/opencv2/${name}/*.hpp" "include/opencv2/${name}/*.h") +file(GLOB highgui_ext_hdrs "include/opencv2/*.hpp" "include/opencv2/${name}/*.hpp" "include/opencv2/${name}/*.h") if(HAVE_QT5) set(CMAKE_AUTOMOC ON) From ca50969c2b9bd71f2f6ce7569f8ad595b1bf5244 Mon Sep 17 00:00:00 2001 From: Aaron Simmons Date: Tue, 13 Oct 2015 17:42:40 -0600 Subject: [PATCH 048/105] brining over fix in master (#4140) for libz import on 64-bit android (cherry picked from commit 55a9fdf0512a6bd3fd82591f48db0942cbd4fc38) --- cmake/OpenCVFindLibsGrfmt.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/OpenCVFindLibsGrfmt.cmake b/cmake/OpenCVFindLibsGrfmt.cmake index f27a302ab7..43c3b16cc1 100644 --- a/cmake/OpenCVFindLibsGrfmt.cmake +++ b/cmake/OpenCVFindLibsGrfmt.cmake @@ -8,7 +8,8 @@ if(BUILD_ZLIB) else() include(FindZLIB) if(ZLIB_FOUND AND ANDROID) - if(ZLIB_LIBRARY STREQUAL "${ANDROID_SYSROOT}/usr/lib/libz.so") + if(ZLIB_LIBRARIES STREQUAL "${ANDROID_SYSROOT}/usr/lib/libz.so" OR + ZLIB_LIBRARIES STREQUAL "${ANDROID_SYSROOT}/usr/lib64/libz.so") set(ZLIB_LIBRARY z) set(ZLIB_LIBRARIES z) endif() From 42447a7610af61e07e3ee210fcfd62920319751e Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 13 Oct 2015 16:32:53 +0300 Subject: [PATCH 049/105] Set of lintain warning fixes for -samples debian package. (cherry picked from commit ff002203029220cd63bbb383b3f286a1ee7c35f3) --- samples/CMakeLists.txt | 2 +- samples/c/CMakeLists.txt | 7 ++++--- samples/cpp/CMakeLists.txt | 4 ++-- samples/gpu/CMakeLists.txt | 4 ++-- samples/gpu/performance/CMakeLists.txt | 4 ++-- samples/ocl/CMakeLists.txt | 4 ++-- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index d2089de768..1754649f10 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -22,7 +22,7 @@ endif() if(INSTALL_C_EXAMPLES) install(FILES "CMakeLists.txt" DESTINATION ${OPENCV_SAMPLES_SRC_INSTALL_PATH} - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT samples) endif() if(INSTALL_PYTHON_EXAMPLES) diff --git a/samples/c/CMakeLists.txt b/samples/c/CMakeLists.txt index 61f23666f3..cb653c752c 100644 --- a/samples/c/CMakeLists.txt +++ b/samples/c/CMakeLists.txt @@ -52,8 +52,9 @@ if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND) endif() if (OCV_DEPENDENCIES_FOUND AND INSTALL_C_EXAMPLES AND NOT WIN32) - file(GLOB C_SAMPLES *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd *.txt) + file(GLOB C_SAMPLES *.c *.cpp *.jpg *.png *.data makefile.* *.cmd *.txt) install(FILES ${C_SAMPLES} - DESTINATION ${OPENCV_SAMPLES_SRC_INSTALL_PATH}/c - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + DESTINATION "${OPENCV_SAMPLES_SRC_INSTALL_PATH}/c" + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT samples) + install(PROGRAMS build_all.sh DESTINATION "${OPENCV_SAMPLES_SRC_INSTALL_PATH}/c" COMPONENT samples) endif () diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index 65e402bc13..fa52ac4860 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -93,6 +93,6 @@ endif() if (OCV_DEPENDENCIES_FOUND AND INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB C_SAMPLES *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd *.txt) install(FILES ${C_SAMPLES} - DESTINATION ${OPENCV_SAMPLES_SRC_INSTALL_PATH}/cpp - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + DESTINATION "${OPENCV_SAMPLES_SRC_INSTALL_PATH}/cpp" + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT samples) endif() diff --git a/samples/gpu/CMakeLists.txt b/samples/gpu/CMakeLists.txt index dc7464f942..cdf9ee8562 100644 --- a/samples/gpu/CMakeLists.txt +++ b/samples/gpu/CMakeLists.txt @@ -112,6 +112,6 @@ if (OCV_DEPENDENCIES_FOUND AND INSTALL_C_EXAMPLES AND NOT WIN32) list_filterout(install_list ".*driver_api_stereo_multi.cpp") endif() install(FILES ${install_list} - DESTINATION ${OPENCV_SAMPLES_SRC_INSTALL_PATH}/gpu - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + DESTINATION "${OPENCV_SAMPLES_SRC_INSTALL_PATH}/gpu" + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT samples) endif() diff --git a/samples/gpu/performance/CMakeLists.txt b/samples/gpu/performance/CMakeLists.txt index 4a1603a916..d0810af22d 100644 --- a/samples/gpu/performance/CMakeLists.txt +++ b/samples/gpu/performance/CMakeLists.txt @@ -29,7 +29,7 @@ endif() if(INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB GPU_FILES performance/*.cpp performance/*.h performance/CMakeLists.txt) install(FILES ${GPU_FILES} - DESTINATION ${OPENCV_SAMPLES_SRC_INSTALL_PATH}/gpu/performance - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + DESTINATION "${OPENCV_SAMPLES_SRC_INSTALL_PATH}/gpu/performance" + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT samples) endif() diff --git a/samples/ocl/CMakeLists.txt b/samples/ocl/CMakeLists.txt index 10db9b10fb..706c4ae0bd 100644 --- a/samples/ocl/CMakeLists.txt +++ b/samples/ocl/CMakeLists.txt @@ -54,6 +54,6 @@ endif() if (OCV_DEPENDENCIES_FOUND AND INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB install_list *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd *.txt) install(FILES ${install_list} - DESTINATION ${OPENCV_SAMPLES_SRC_INSTALL_PATH}/ocl - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + DESTINATION "${OPENCV_SAMPLES_SRC_INSTALL_PATH}/ocl" + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT samples) endif() From 966d35a9fbd5102605c6742f493c405190b8a6cc Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 14 Oct 2015 12:52:32 +0300 Subject: [PATCH 050/105] Made samples build independent from nonfree module. (cherry picked from commit bba8c0beacf34143509b60aaf5321c56b2a21013) --- samples/CMakeLists.txt | 10 +++- samples/c/CMakeLists.txt | 2 +- samples/c/find_obj.cpp | 36 ++++++++---- samples/c/find_obj_calonder.cpp | 31 +++++++--- samples/c/one_way_sample.cpp | 30 +++++++--- samples/cpp/CMakeLists.txt | 5 +- samples/cpp/bagofwords_classification.cpp | 58 ++++++++++++------- samples/cpp/descriptor_extractor_matcher.cpp | 13 ++++- samples/cpp/fabmap_sample.cpp | 18 +++++- samples/cpp/freak_demo.cpp | 28 ++++++--- samples/cpp/generic_descriptor_match.cpp | 26 +++++++-- samples/cpp/matcher_simple.cpp | 22 +++++-- .../features2D/SURF_FlannMatcher.cpp | 27 ++++++--- .../features2D/SURF_Homography.cpp | 29 +++++++--- .../features2D/SURF_descriptor.cpp | 23 ++++++-- .../features2D/SURF_detector.cpp | 23 ++++++-- samples/ocl/CMakeLists.txt | 5 +- samples/ocl/surf_matcher.cpp | 27 ++++++--- 18 files changed, 302 insertions(+), 111 deletions(-) diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 1754649f10..df01dc76b6 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -68,8 +68,14 @@ endif() add_subdirectory(c) add_subdirectory(cpp) -add_subdirectory(ocl) -add_subdirectory(gpu) +ocv_check_dependencies(opencv_ocl) +if (OCV_DEPENDENCIES_FOUND) + add_subdirectory(ocl) +endif() +ocv_check_dependencies(opencv_gpu) +if (OCV_DEPENDENCIES_FOUND) + add_subdirectory(gpu) +endif() # # END OF BUILD CASE 2: Build samples with library binaries diff --git a/samples/c/CMakeLists.txt b/samples/c/CMakeLists.txt index cb653c752c..b26e4ab662 100644 --- a/samples/c/CMakeLists.txt +++ b/samples/c/CMakeLists.txt @@ -4,7 +4,7 @@ # ---------------------------------------------------------------------------- SET(OPENCV_C_SAMPLES_REQUIRED_DEPS opencv_core opencv_flann opencv_imgproc - opencv_highgui opencv_ml opencv_video opencv_objdetect opencv_photo opencv_nonfree + opencv_highgui opencv_ml opencv_video opencv_objdetect opencv_photo opencv_features2d opencv_calib3d opencv_legacy opencv_contrib) ocv_check_dependencies(${OPENCV_C_SAMPLES_REQUIRED_DEPS}) diff --git a/samples/c/find_obj.cpp b/samples/c/find_obj.cpp index e0431fc6d4..e0ae60cacc 100644 --- a/samples/c/find_obj.cpp +++ b/samples/c/find_obj.cpp @@ -4,19 +4,31 @@ * Author: Liu Liu * liuliu.1987+opencv@gmail.com */ -#include "opencv2/objdetect/objdetect.hpp" -#include "opencv2/features2d/features2d.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "opencv2/calib3d/calib3d.hpp" -#include "opencv2/nonfree/nonfree.hpp" -#include "opencv2/imgproc/imgproc_c.h" -#include "opencv2/legacy/legacy.hpp" -#include "opencv2/legacy/compat.hpp" - -#include -#include + +#include "opencv2/opencv_modules.hpp" #include +#ifndef HAVE_OPENCV_NONFREE + +int main(int, char**) +{ + printf("The sample requires nonfree module that is not available in your OpenCV distribution.\n"); + return -1; +} + +#else + +# include "opencv2/objdetect/objdetect.hpp" +# include "opencv2/features2d/features2d.hpp" +# include "opencv2/highgui/highgui.hpp" +# include "opencv2/calib3d/calib3d.hpp" +# include "opencv2/nonfree/nonfree.hpp" +# include "opencv2/imgproc/imgproc_c.h" +# include "opencv2/legacy/legacy.hpp" +# include "opencv2/legacy/compat.hpp" + +# include + using namespace std; static void help() { @@ -320,3 +332,5 @@ int main(int argc, char** argv) return 0; } + +#endif diff --git a/samples/c/find_obj_calonder.cpp b/samples/c/find_obj_calonder.cpp index 02cd266526..685e703c54 100644 --- a/samples/c/find_obj_calonder.cpp +++ b/samples/c/find_obj_calonder.cpp @@ -1,12 +1,25 @@ -#include "opencv2/highgui/highgui.hpp" -#include "opencv2/core/core.hpp" -#include "opencv2/imgproc/imgproc.hpp" -#include "opencv2/features2d/features2d.hpp" -#include "opencv2/nonfree/nonfree.hpp" -#include "opencv2/legacy/legacy.hpp" - +#include "opencv2/opencv_modules.hpp" #include -#include + +#ifndef HAVE_OPENCV_NONFREE + +int main(int, char**) +{ + std::cout << "The sample requires nonfree module that is not available in your OpenCV distribution." << std::endl; + return -1; +} + +#else + +# include "opencv2/highgui/highgui.hpp" +# include "opencv2/core/core.hpp" +# include "opencv2/imgproc/imgproc.hpp" +# include "opencv2/features2d/features2d.hpp" +# include "opencv2/nonfree/nonfree.hpp" +# include "opencv2/legacy/legacy.hpp" + +# include +# include using namespace std; using namespace cv; @@ -164,3 +177,5 @@ int main( int argc, char **argv ) return 0; } + +#endif diff --git a/samples/c/one_way_sample.cpp b/samples/c/one_way_sample.cpp index ad0153ba0c..6db28baf36 100644 --- a/samples/c/one_way_sample.cpp +++ b/samples/c/one_way_sample.cpp @@ -7,16 +7,28 @@ * */ -#include "opencv2/imgproc/imgproc.hpp" -#include "opencv2/features2d/features2d.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "opencv2/imgproc/imgproc_c.h" -#include "opencv2/nonfree/nonfree.hpp" -#include "opencv2/legacy/legacy.hpp" -#include "opencv2/legacy/compat.hpp" +#include "opencv2/opencv_modules.hpp" +#include + +#ifndef HAVE_OPENCV_NONFREE + +int main(int, char**) +{ + printf("The sample requires nonfree module that is not available in your OpenCV distribution.\n"); + return -1; +} + +#else + +# include "opencv2/imgproc/imgproc.hpp" +# include "opencv2/features2d/features2d.hpp" +# include "opencv2/highgui/highgui.hpp" +# include "opencv2/imgproc/imgproc_c.h" +# include "opencv2/nonfree/nonfree.hpp" +# include "opencv2/legacy/legacy.hpp" +# include "opencv2/legacy/compat.hpp" #include -#include static void help() { @@ -116,3 +128,5 @@ Mat DrawCorrespondences(const Mat& img1, const vector& features1, cons return img_corr; } + +#endif diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index fa52ac4860..5eff52a90f 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -4,8 +4,9 @@ # ---------------------------------------------------------------------------- SET(OPENCV_CPP_SAMPLES_REQUIRED_DEPS opencv_core opencv_flann opencv_imgproc - opencv_highgui opencv_ml opencv_video opencv_objdetect opencv_photo opencv_nonfree - opencv_features2d opencv_calib3d opencv_legacy opencv_contrib opencv_stitching opencv_videostab) + opencv_highgui opencv_ml opencv_video opencv_objdetect opencv_photo + opencv_features2d opencv_calib3d opencv_legacy opencv_contrib + opencv_stitching opencv_videostab) ocv_check_dependencies(${OPENCV_CPP_SAMPLES_REQUIRED_DEPS}) diff --git a/samples/cpp/bagofwords_classification.cpp b/samples/cpp/bagofwords_classification.cpp index db194b536c..d351baaa96 100644 --- a/samples/cpp/bagofwords_classification.cpp +++ b/samples/cpp/bagofwords_classification.cpp @@ -1,29 +1,41 @@ #include "opencv2/opencv_modules.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "opencv2/imgproc/imgproc.hpp" -#include "opencv2/features2d/features2d.hpp" -#include "opencv2/nonfree/nonfree.hpp" -#include "opencv2/ml/ml.hpp" -#ifdef HAVE_OPENCV_OCL -#define _OCL_SVM_ 1 //select whether using ocl::svm method or not, default is using -#include "opencv2/ocl/ocl.hpp" -#endif - -#include #include -#include -#include -#if defined WIN32 || defined _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#undef min -#undef max -#include "sys/types.h" -#endif -#include +#ifndef HAVE_OPENCV_NONFREE -#define DEBUG_DESC_PROGRESS +int main(int, char**) +{ + std::cout << "The sample requires nonfree module that is not available in your OpenCV distribution." << std::endl; + return -1; +} + +#else + +# include "opencv2/highgui/highgui.hpp" +# include "opencv2/imgproc/imgproc.hpp" +# include "opencv2/features2d/features2d.hpp" +# include "opencv2/nonfree/nonfree.hpp" +# include "opencv2/ml/ml.hpp" +# ifdef HAVE_OPENCV_OCL +# define _OCL_SVM_ 1 //select whether using ocl::svm method or not, default is using +# include "opencv2/ocl/ocl.hpp" +# endif + +# include +# include +# include + + +# if defined WIN32 || defined _WIN32 +# define WIN32_LEAN_AND_MEAN +# include +# undef min +# undef max +# include "sys/types.h" +# endif +# include + +# define DEBUG_DESC_PROGRESS using namespace cv; using namespace std; @@ -2623,3 +2635,5 @@ int main(int argc, char** argv) } return 0; } + +#endif diff --git a/samples/cpp/descriptor_extractor_matcher.cpp b/samples/cpp/descriptor_extractor_matcher.cpp index 98c6452234..4a9a63403a 100644 --- a/samples/cpp/descriptor_extractor_matcher.cpp +++ b/samples/cpp/descriptor_extractor_matcher.cpp @@ -1,8 +1,11 @@ +#include "opencv2/opencv_modules.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/calib3d/calib3d.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/features2d/features2d.hpp" -#include "opencv2/nonfree/nonfree.hpp" +#ifdef HAVE_OPENCV_NONFREE +# include "opencv2/nonfree/nonfree.hpp" +#endif #include @@ -17,14 +20,14 @@ static void help(char** argv) << "Case1: second image is obtained from the first (given) image using random generated homography matrix\n" << argv[0] << " [detectorType] [descriptorType] [matcherType] [matcherFilterType] [image] [evaluate(0 or 1)]\n" << "Example of case1:\n" - << "./descriptor_extractor_matcher SURF SURF FlannBased NoneFilter cola.jpg 0\n" + << "./descriptor_extractor_matcher ORB ORB FlannBased NoneFilter cola.jpg 0\n" << "\n" << "Case2: both images are given. If ransacReprojThreshold>=0 then homography matrix are calculated\n" << argv[0] << " [detectorType] [descriptorType] [matcherType] [matcherFilterType] [image1] [image2] [ransacReprojThreshold]\n" << "\n" << "Matches are filtered using homography matrix in case1 and case2 (if ransacReprojThreshold>=0)\n" << "Example of case2:\n" - << "./descriptor_extractor_matcher SURF SURF BruteForce CrossCheckFilter cola1.jpg cola2.jpg 3\n" + << "./descriptor_extractor_matcher ORB ORB BruteForce CrossCheckFilter cola1.jpg cola2.jpg 3\n" << "\n" << "Possible detectorType values: see in documentation on createFeatureDetector().\n" << "Possible descriptorType values: see in documentation on createDescriptorExtractor().\n" @@ -239,7 +242,11 @@ int main(int argc, char** argv) return -1; } +#ifdef HAVE_OPENCV_NONFREE cv::initModule_nonfree(); +#else + cout << "Warning: OpenCV is built without nonfree support SIFT, SURF and some other algorithms are not available." << endl; +#endif bool isWarpPerspective = argc == 7; double ransacReprojThreshold = -1; diff --git a/samples/cpp/fabmap_sample.cpp b/samples/cpp/fabmap_sample.cpp index bbe5f2b4c5..25b9dbd241 100644 --- a/samples/cpp/fabmap_sample.cpp +++ b/samples/cpp/fabmap_sample.cpp @@ -49,9 +49,21 @@ // //M*/ +#include "opencv2/opencv_modules.hpp" +#include -#include "opencv2/opencv.hpp" -#include "opencv2/nonfree/nonfree.hpp" +#ifndef HAVE_OPENCV_NONFREE + +int main(int, char**) +{ + std::cout << "The sample requires nonfree module that is not available in your OpenCV distribution." << std::endl; + return -1; +} + +#else + +# include "opencv2/opencv.hpp" +# include "opencv2/nonfree/nonfree.hpp" using namespace cv; using namespace std; @@ -212,3 +224,5 @@ int main(int argc, char * argv[]) { return 0; } + +#endif diff --git a/samples/cpp/freak_demo.cpp b/samples/cpp/freak_demo.cpp index 6dbd9215ba..6a941598c3 100644 --- a/samples/cpp/freak_demo.cpp +++ b/samples/cpp/freak_demo.cpp @@ -34,15 +34,27 @@ // 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. +#include "opencv2/opencv_modules.hpp" #include -#include -#include -#include -#include -#include -#include -#include +#ifndef HAVE_OPENCV_NONFREE + +int main(int, char**) +{ + std::cout << "The sample requires nonfree module that is not available in your OpenCV distribution." << std::endl; + return -1; +} + +#else + +# include +# include + +# include +# include +# include +# include +# include using namespace cv; @@ -126,3 +138,5 @@ int main( int argc, char** argv ) { imshow("matches", imgMatch); waitKey(0); } + +#endif diff --git a/samples/cpp/generic_descriptor_match.cpp b/samples/cpp/generic_descriptor_match.cpp index 1cb03c6a9c..198f4046a8 100644 --- a/samples/cpp/generic_descriptor_match.cpp +++ b/samples/cpp/generic_descriptor_match.cpp @@ -1,11 +1,23 @@ -#include "opencv2/calib3d/calib3d.hpp" -#include "opencv2/features2d/features2d.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "opencv2/imgproc/imgproc.hpp" -#include "opencv2/nonfree/nonfree.hpp" - +#include "opencv2/opencv_modules.hpp" #include +#ifndef HAVE_OPENCV_NONFREE + +int main(int, char**) +{ + printf("The sample requires nonfree module that is not available in your OpenCV distribution.\n"); + return -1; +} + +#else + +# include "opencv2/opencv_modules.hpp" +# include "opencv2/calib3d/calib3d.hpp" +# include "opencv2/features2d/features2d.hpp" +# include "opencv2/highgui/highgui.hpp" +# include "opencv2/imgproc/imgproc.hpp" +# include "opencv2/nonfree/nonfree.hpp" + using namespace cv; static void help() @@ -91,3 +103,5 @@ Mat DrawCorrespondences(const Mat& img1, const vector& features1, cons return img_corr; } + +#endif diff --git a/samples/cpp/matcher_simple.cpp b/samples/cpp/matcher_simple.cpp index 42e89fbaa6..2df32eb4b7 100644 --- a/samples/cpp/matcher_simple.cpp +++ b/samples/cpp/matcher_simple.cpp @@ -1,8 +1,20 @@ +#include "opencv2/opencv_modules.hpp" #include -#include "opencv2/core/core.hpp" -#include "opencv2/features2d/features2d.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "opencv2/nonfree/nonfree.hpp" + +#ifndef HAVE_OPENCV_NONFREE + +int main(int, char**) +{ + printf("The sample requires nonfree module that is not available in your OpenCV distribution.\n"); + return -1; +} + +#else + +# include "opencv2/core/core.hpp" +# include "opencv2/features2d/features2d.hpp" +# include "opencv2/highgui/highgui.hpp" +# include "opencv2/nonfree/nonfree.hpp" using namespace cv; @@ -56,3 +68,5 @@ int main(int argc, char** argv) return 0; } + +#endif diff --git a/samples/cpp/tutorial_code/features2D/SURF_FlannMatcher.cpp b/samples/cpp/tutorial_code/features2D/SURF_FlannMatcher.cpp index 09346309ec..9cc17768aa 100644 --- a/samples/cpp/tutorial_code/features2D/SURF_FlannMatcher.cpp +++ b/samples/cpp/tutorial_code/features2D/SURF_FlannMatcher.cpp @@ -4,12 +4,23 @@ * @author A. Huaman */ +#include "opencv2/opencv_modules.hpp" #include -#include -#include "opencv2/core/core.hpp" -#include "opencv2/features2d/features2d.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "opencv2/nonfree/features2d.hpp" + +#ifndef HAVE_OPENCV_NONFREE + +int main(int, char**) +{ + printf("The sample requires nonfree module that is not available in your OpenCV distribution.\n"); + return -1; +} + +#else + +# include "opencv2/core/core.hpp" +# include "opencv2/features2d/features2d.hpp" +# include "opencv2/highgui/highgui.hpp" +# include "opencv2/nonfree/features2d.hpp" using namespace cv; @@ -28,7 +39,7 @@ int main( int argc, char** argv ) Mat img_2 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE ); if( !img_1.data || !img_2.data ) - { std::cout<< " --(!) Error reading images " << std::endl; return -1; } + { printf(" --(!) Error reading images \n"); return -1; } //-- Step 1: Detect the keypoints using SURF Detector int minHessian = 400; @@ -97,4 +108,6 @@ int main( int argc, char** argv ) * @function readme */ void readme() -{ std::cout << " Usage: ./SURF_FlannMatcher " << std::endl; } +{ printf(" Usage: ./SURF_FlannMatcher \n"); } + +#endif \ No newline at end of file diff --git a/samples/cpp/tutorial_code/features2D/SURF_Homography.cpp b/samples/cpp/tutorial_code/features2D/SURF_Homography.cpp index 4b29638a5f..11dd074951 100644 --- a/samples/cpp/tutorial_code/features2D/SURF_Homography.cpp +++ b/samples/cpp/tutorial_code/features2D/SURF_Homography.cpp @@ -4,13 +4,24 @@ * @author A. Huaman */ +#include "opencv2/opencv_modules.hpp" #include -#include -#include "opencv2/core/core.hpp" -#include "opencv2/features2d/features2d.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "opencv2/calib3d/calib3d.hpp" -#include "opencv2/nonfree/features2d.hpp" + +#ifndef HAVE_OPENCV_NONFREE + +int main(int, char**) +{ + printf("The sample requires nonfree module that is not available in your OpenCV distribution.\n"); + return -1; +} + +#else + +# include "opencv2/core/core.hpp" +# include "opencv2/features2d/features2d.hpp" +# include "opencv2/highgui/highgui.hpp" +# include "opencv2/calib3d/calib3d.hpp" +# include "opencv2/nonfree/features2d.hpp" using namespace cv; @@ -29,7 +40,7 @@ int main( int argc, char** argv ) Mat img_scene = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE ); if( !img_object.data || !img_scene.data ) - { std::cout<< " --(!) Error reading images " << std::endl; return -1; } + { printf(" --(!) Error reading images \n"); return -1; } //-- Step 1: Detect the keypoints using SURF Detector int minHessian = 400; @@ -121,4 +132,6 @@ int main( int argc, char** argv ) * @function readme */ void readme() -{ std::cout << " Usage: ./SURF_Homography " << std::endl; } +{ printf(" Usage: ./SURF_Homography \n"); } + +#endif \ No newline at end of file diff --git a/samples/cpp/tutorial_code/features2D/SURF_descriptor.cpp b/samples/cpp/tutorial_code/features2D/SURF_descriptor.cpp index 527e5dd951..2fcd884ce8 100644 --- a/samples/cpp/tutorial_code/features2D/SURF_descriptor.cpp +++ b/samples/cpp/tutorial_code/features2D/SURF_descriptor.cpp @@ -4,12 +4,23 @@ * @author A. Huaman */ +#include "opencv2/opencv_modules.hpp" #include -#include -#include "opencv2/core/core.hpp" -#include "opencv2/features2d/features2d.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "opencv2/nonfree/features2d.hpp" + +#ifndef HAVE_OPENCV_NONFREE + +int main(int, char**) +{ + printf("The sample requires nonfree module that is not available in your OpenCV distribution.\n"); + return -1; +} + +#else + +# include "opencv2/core/core.hpp" +# include "opencv2/features2d/features2d.hpp" +# include "opencv2/highgui/highgui.hpp" +# include "opencv2/nonfree/features2d.hpp" using namespace cv; @@ -70,3 +81,5 @@ int main( int argc, char** argv ) */ void readme() { std::cout << " Usage: ./SURF_descriptor " << std::endl; } + +#endif \ No newline at end of file diff --git a/samples/cpp/tutorial_code/features2D/SURF_detector.cpp b/samples/cpp/tutorial_code/features2D/SURF_detector.cpp index 2625f1df3b..21dc45a949 100644 --- a/samples/cpp/tutorial_code/features2D/SURF_detector.cpp +++ b/samples/cpp/tutorial_code/features2D/SURF_detector.cpp @@ -4,12 +4,23 @@ * @author A. Huaman */ -#include +#include "opencv2/opencv_modules.hpp" #include -#include "opencv2/core/core.hpp" -#include "opencv2/features2d/features2d.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "opencv2/nonfree/features2d.hpp" + +#ifndef HAVE_OPENCV_NONFREE + +int main(int, char**) +{ + std::cout << "The sample requires nonfree module that is not available in your OpenCV distribution." << std::endl; + return -1; +} + +#else + +# include "opencv2/core/core.hpp" +# include "opencv2/features2d/features2d.hpp" +# include "opencv2/highgui/highgui.hpp" +# include "opencv2/nonfree/features2d.hpp" using namespace cv; @@ -60,3 +71,5 @@ int main( int argc, char** argv ) */ void readme() { std::cout << " Usage: ./SURF_detector " << std::endl; } + +#endif diff --git a/samples/ocl/CMakeLists.txt b/samples/ocl/CMakeLists.txt index 706c4ae0bd..90856f7ca1 100644 --- a/samples/ocl/CMakeLists.txt +++ b/samples/ocl/CMakeLists.txt @@ -1,7 +1,6 @@ SET(OPENCV_OCL_SAMPLES_REQUIRED_DEPS opencv_core opencv_flann opencv_imgproc opencv_highgui opencv_ml opencv_video opencv_objdetect opencv_features2d - opencv_calib3d opencv_legacy opencv_contrib opencv_ocl - opencv_nonfree) + opencv_calib3d opencv_legacy opencv_contrib opencv_ocl) ocv_check_dependencies(${OPENCV_OCL_SAMPLES_REQUIRED_DEPS}) @@ -52,7 +51,7 @@ if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND) endif() if (OCV_DEPENDENCIES_FOUND AND INSTALL_C_EXAMPLES AND NOT WIN32) - file(GLOB install_list *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd *.txt) + file(GLOB install_list *.c *.cpp *.jpg *.png *.data makefile.* *.txt) install(FILES ${install_list} DESTINATION "${OPENCV_SAMPLES_SRC_INSTALL_PATH}/ocl" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT samples) diff --git a/samples/ocl/surf_matcher.cpp b/samples/ocl/surf_matcher.cpp index 6023de9039..19b7f564a8 100644 --- a/samples/ocl/surf_matcher.cpp +++ b/samples/ocl/surf_matcher.cpp @@ -1,11 +1,22 @@ +#include "opencv2/opencv_modules.hpp" #include -#include -#include "opencv2/core/core.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "opencv2/ocl/ocl.hpp" -#include "opencv2/nonfree/ocl.hpp" -#include "opencv2/calib3d/calib3d.hpp" -#include "opencv2/nonfree/nonfree.hpp" + +#ifndef HAVE_OPENCV_NONFREE + +int main(int, char**) +{ + std::cout << "The sample requires nonfree module that is not available in your OpenCV distribution." << std::endl; + return -1; +} + +#else + +# include "opencv2/core/core.hpp" +# include "opencv2/highgui/highgui.hpp" +# include "opencv2/ocl/ocl.hpp" +# include "opencv2/nonfree/ocl.hpp" +# include "opencv2/calib3d/calib3d.hpp" +# include "opencv2/nonfree/nonfree.hpp" using namespace cv; using namespace cv::ocl; @@ -326,3 +337,5 @@ int main(int argc, char* argv[]) waitKey(0); return EXIT_SUCCESS; } + +#endif From 2e78a3e5e94f21f6488734bc992cf5a8a7a5a27a Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 14 Oct 2015 16:19:37 +0300 Subject: [PATCH 051/105] Fixed samples build with nonfree. (cherry picked from commit 341e7b3be24f921cdf0a63793afc2e694b4e98b6) --- samples/c/CMakeLists.txt | 2 +- samples/cpp/CMakeLists.txt | 2 +- samples/cpp/tutorial_code/features2D/SURF_descriptor.cpp | 2 +- samples/ocl/CMakeLists.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/c/CMakeLists.txt b/samples/c/CMakeLists.txt index b26e4ab662..bd82ba2637 100644 --- a/samples/c/CMakeLists.txt +++ b/samples/c/CMakeLists.txt @@ -16,7 +16,7 @@ if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function") endif() - ocv_include_modules(${OPENCV_C_SAMPLES_REQUIRED_DEPS}) + ocv_include_modules(${OPENCV_C_SAMPLES_REQUIRED_DEPS} opencv_nonfree) # --------------------------------------------- # Define executable targets diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index 5eff52a90f..6433d1fd30 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -15,7 +15,7 @@ if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND) project(cpp_samples) ocv_include_directories("${OpenCV_SOURCE_DIR}/include")#for opencv.hpp - ocv_include_modules(${OPENCV_CPP_SAMPLES_REQUIRED_DEPS}) + ocv_include_modules(${OPENCV_CPP_SAMPLES_REQUIRED_DEPS} opencv_nonfree) if(HAVE_opencv_gpu) ocv_include_directories("${OpenCV_SOURCE_DIR}/modules/gpu/include") diff --git a/samples/cpp/tutorial_code/features2D/SURF_descriptor.cpp b/samples/cpp/tutorial_code/features2D/SURF_descriptor.cpp index 2fcd884ce8..aeabd8e346 100644 --- a/samples/cpp/tutorial_code/features2D/SURF_descriptor.cpp +++ b/samples/cpp/tutorial_code/features2D/SURF_descriptor.cpp @@ -80,6 +80,6 @@ int main( int argc, char** argv ) * @function readme */ void readme() -{ std::cout << " Usage: ./SURF_descriptor " << std::endl; } +{ printf(" Usage: ./SURF_descriptor \n"); } #endif \ No newline at end of file diff --git a/samples/ocl/CMakeLists.txt b/samples/ocl/CMakeLists.txt index 90856f7ca1..ee6d4f618c 100644 --- a/samples/ocl/CMakeLists.txt +++ b/samples/ocl/CMakeLists.txt @@ -10,7 +10,7 @@ if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND) project("${project}_samples") - ocv_include_modules(${OPENCV_OCL_SAMPLES_REQUIRED_DEPS}) + ocv_include_modules(${OPENCV_OCL_SAMPLES_REQUIRED_DEPS} opencv_nonfree) if(HAVE_OPENCL) ocv_include_directories(${OPENCL_INCLUDE_DIR}) From 60eda6f25c93b37f620b9e12d7a95d753d30ef46 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 19 Oct 2015 13:36:44 +0300 Subject: [PATCH 052/105] export simple libs from OPENCV_LINKER_LIBS (fix #5541) (cherry picked from commit 937a096bf10bedd2d10803f67716393f9065abe8) --- cmake/OpenCVModule.cmake | 5 ++++- cmake/OpenCVUtils.cmake | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index cae2a39c9d..88ebe54f79 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -576,7 +576,10 @@ macro(ocv_create_module) if(NOT "${ARGN}" STREQUAL "SKIP_LINK") target_link_libraries(${the_module} ${OPENCV_MODULE_${the_module}_DEPS}) target_link_libraries(${the_module} LINK_INTERFACE_LIBRARIES ${OPENCV_MODULE_${the_module}_DEPS}) - target_link_libraries(${the_module} ${OPENCV_MODULE_${the_module}_DEPS_EXT} ${OPENCV_LINKER_LIBS} ${IPP_LIBS} ${ARGN}) + set(extra_deps ${OPENCV_MODULE_${the_module}_DEPS_EXT} ${OPENCV_LINKER_LIBS} ${IPP_LIBS} ${ARGN}) + ocv_extract_simple_libs(extra_deps _simple_deps _other_deps) + target_link_libraries(${the_module} LINK_INTERFACE_LIBRARIES ${_simple_deps}) # this list goes to "export" + target_link_libraries(${the_module} ${extra_deps}) endif() add_dependencies(opencv_modules ${the_module}) diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index c23ddccebe..0692b763d5 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -630,3 +630,21 @@ function(ocv_source_group group) file(GLOB srcs ${OCV_SOURCE_GROUP_GLOB}) source_group(${group} FILES ${srcs}) endfunction() + +# build the list of simple dependencies, that links via "-l" +# _all_libs - name of variable with input list +# _simple - name of variable with output list of simple libs +# _other - name of variable with _all_libs - _simple +macro(ocv_extract_simple_libs _all_libs _simple _other) + set(${_simple} "") + set(${_other} "") + foreach(_l ${${_all_libs}}) + if(TARGET ${_l}) + list(APPEND ${_other} ${_l}) + elseif(EXISTS "${_l}") + list(APPEND ${_other} ${_l}) + else() + list(APPEND ${_simple} ${_l}) + endif() + endforeach() +endmacro() From 8e40becab12c642e7bbafb9fe87698e8b6bf28da Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 23 Oct 2015 12:28:09 +0300 Subject: [PATCH 053/105] Debian packages with legacy C headers added to list of conflicts, relpaces, etc. (cherry picked from commit e245aed6bbebd69a63a00d45a66b358df4153888) --- cmake/OpenCVPackaging.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/OpenCVPackaging.cmake b/cmake/OpenCVPackaging.cmake index e64d447bfa..4e4b4d42c9 100644 --- a/cmake/OpenCVPackaging.cmake +++ b/cmake/OpenCVPackaging.cmake @@ -127,6 +127,8 @@ foreach(module calib3d contrib core features2d flann gpu highgui imgproc legacy endif() endforeach() +list(APPEND STD_OPENCV_DEV "libhighgui-dev" "libcv-dev" "libcvaux-dev") + string(REPLACE ";" ", " CPACK_COMPONENT_LIBS_CONFLICTS "${STD_OPENCV_LIBS}") string(REPLACE ";" ", " CPACK_COMPONENT_LIBS_PROVIDES "${STD_OPENCV_LIBS}") string(REPLACE ";" ", " CPACK_COMPONENT_LIBS_REPLACES "${STD_OPENCV_LIBS}") From 07a58c47cb75e2cd875595e01afe33d2c7881971 Mon Sep 17 00:00:00 2001 From: Teng Cao Date: Wed, 2 Dec 2015 17:31:17 +0800 Subject: [PATCH 054/105] Update cascadeclassifier.cpp minor fix since predict() returun int --- apps/traincascade/cascadeclassifier.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/traincascade/cascadeclassifier.cpp b/apps/traincascade/cascadeclassifier.cpp index aa97708338..1c09a96bc0 100644 --- a/apps/traincascade/cascadeclassifier.cpp +++ b/apps/traincascade/cascadeclassifier.cpp @@ -336,7 +336,7 @@ int CvCascadeClassifier::fillPassedSamples( int first, int count, bool isPositiv consumed++; featureEvaluator->setImage( img, isPositive ? 1 : 0, i ); - if( predict( i ) == 1.0F ) + if( predict( i ) == 1 ) { getcount++; printf("%s current samples: %d\r", isPositive ? "POS":"NEG", getcount); From 4fa14f75b850b86796fafe8f2ef678f730e613ac Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 7 Dec 2015 13:21:51 +0300 Subject: [PATCH 055/105] suppress lintian warning in opencv.pc --- cmake/OpenCVPackaging.cmake | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/cmake/OpenCVPackaging.cmake b/cmake/OpenCVPackaging.cmake index f7fbcfaaa2..775605c4f8 100644 --- a/cmake/OpenCVPackaging.cmake +++ b/cmake/OpenCVPackaging.cmake @@ -227,6 +227,32 @@ function(ocv_generate_lintian_overrides_file comp) endif() endfunction() +function(ocv_get_lintian_version version) + find_program(LINTIAN_EXECUTABLE lintian) + + if(NOT LINTIAN_EXECUTABLE) + return() + endif() + + execute_process(COMMAND ${LINTIAN_EXECUTABLE} --version + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE LINTIAN_EXITCODE + OUTPUT_VARIABLE LINTIAN_VERSION + ERROR_QUIET) + + if(NOT LINTIAN_EXITCODE EQUAL 0) + return() + endif() + + if(LINTIAN_VERSION MATCHES "([0-9]+\\.[0-9]+\\.[0-9]+)") + set(LINTIAN_VERSION "${CMAKE_MATCH_1}" CACHE INTERNAL "Lintian version") + endif() + + set("${version}" "${LINTIAN_VERSION}" PARENT_SCOPE) +endfunction() + +ocv_get_lintian_version(LINTIAN_VERSION) + set(LIBS_LINTIAN_OVERRIDES "binary-or-shlib-defines-rpath" # usr/lib/libopencv_core.so.2.4.12 "package-name-doesnt-match-sonames") # libopencv-calib3d2.4 libopencv-contrib2.4 @@ -246,6 +272,11 @@ endif() set(DEV_LINTIAN_OVERRIDES "binary-or-shlib-defines-rpath" # usr/bin/opencv_traincascade "binary-without-manpage") # usr/bin/opencv_traincascade +if(LINTIAN_VERSION VERSION_GREATER "2.5.30" OR + LINTIAN_VERSION VERSION_EQUAL "2.5.30") + list(APPEND DEV_LINTIAN_OVERRIDES "pkg-config-bad-directive") # usr/lib/pkgconfig/opencv.pc -L/usr/local/cuda-7.0/lib64 +endif() + if(NOT INSTALL_C_EXAMPLES) set(SAMPLES_LINTIAN_OVERRIDES "empty-binary-package") # samples are not installed endif() From 7cb78451d10955ff3e7030758c909c1c455a5039 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Fri, 27 Feb 2015 18:59:55 +0300 Subject: [PATCH 056/105] Backported several changes from master branch: - #3771 - inline round on ARM - #5633 - documentation for MSER - #5666 - run.py fixes --- modules/core/include/opencv2/core/types_c.h | 26 +++++++++++++ .../doc/feature_detection_and_description.rst | 39 +++++++++++++++++-- modules/ts/misc/run.py | 6 +-- modules/ts/misc/run_suite.py | 34 ++++++++-------- modules/ts/misc/run_utils.py | 13 +++---- 5 files changed, 89 insertions(+), 29 deletions(-) diff --git a/modules/core/include/opencv2/core/types_c.h b/modules/core/include/opencv2/core/types_c.h index 989b799fdf..9e9231db08 100644 --- a/modules/core/include/opencv2/core/types_c.h +++ b/modules/core/include/opencv2/core/types_c.h @@ -305,6 +305,30 @@ enum { #define CV_CMP(a,b) (((a) > (b)) - ((a) < (b))) #define CV_SIGN(a) CV_CMP((a),0) +#if defined __GNUC__ && defined __arm__ && (defined __ARM_PCS_VFP || defined __ARM_VFPV3__) +# define CV_VFP 1 +#else +# define CV_VFP 0 +#endif + + +#if CV_VFP +// 1. general scheme +#define ARM_ROUND(_value, _asm_string) \ + int res; \ + float temp; \ + asm(_asm_string : [res] "=r" (res), [temp] "=w" (temp) : [value] "w" (_value)); \ + return res; +// 2. version for double +#ifdef __clang__ +#define ARM_ROUND_DBL(value) ARM_ROUND(value, "vcvtr.s32.f64 %[temp], %[value] \n vmov %[res], %[temp]") +#else +#define ARM_ROUND_DBL(value) ARM_ROUND(value, "vcvtr.s32.f64 %[temp], %P[value] \n vmov %[res], %[temp]") +#endif +// 3. version for float +#define ARM_ROUND_FLT(value) ARM_ROUND(value, "vcvtr.s32.f32 %[temp], %[value]\n vmov %[res], %[temp]") +#endif // CV_VFP + CV_INLINE int cvRound( double value ) { #if (defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__ && defined __SSE2__ && !defined __APPLE__) @@ -323,6 +347,8 @@ CV_INLINE int cvRound( double value ) #elif defined CV_ICC || defined __GNUC__ # ifdef HAVE_TEGRA_OPTIMIZATION TEGRA_ROUND(value); +# elif CV_VFP + ARM_ROUND_DBL(value) # else return (int)lrint(value); # endif diff --git a/modules/features2d/doc/feature_detection_and_description.rst b/modules/features2d/doc/feature_detection_and_description.rst index c05a71d34a..effaed73d7 100644 --- a/modules/features2d/doc/feature_detection_and_description.rst +++ b/modules/features2d/doc/feature_detection_and_description.rst @@ -52,13 +52,46 @@ Maximally stable extremal region extractor. :: void operator()( const Mat& image, vector >& msers, const Mat& mask ) const; }; -The class encapsulates all the parameters of the MSER extraction algorithm (see -http://en.wikipedia.org/wiki/Maximally_stable_extremal_regions). Also see http://code.opencv.org/projects/opencv/wiki/MSER for useful comments and parameters description. +The class encapsulates all the parameters of the MSER extraction algorithm (see [wiki]_ article). .. note:: - * (Python) A complete example showing the use of the MSER detector can be found at opencv_source_code/samples/python2/mser.py + * there are two different implementation of MSER: one for grey image, one for color image the grey image algorithm is taken from: [nister2008linear]_ ; the paper claims to be faster than union-find method; it actually get 1.5~2m/s on my centrino L7200 1.2GHz laptop. + * the color image algorithm is taken from: [forssen2007maximally]_ ; it should be much slower than grey image method ( 3~4 times ); the chi_table.h file is taken directly from paper's source code which is distributed under GPL. + + * (Python) A complete example showing the use of the MSER detector can be found at opencv_source_code/samples/python2/mser.py + +.. [wiki] http://en.wikipedia.org/wiki/Maximally_stable_extremal_regions +.. [nister2008linear] David Nistér and Henrik Stewénius. Linear time maximally stable extremal regions. In Computer Vision–ECCV 2008, pages 183–196. Springer, 2008. +.. [forssen2007maximally] Per-Erik Forssén. Maximally stable colour regions for recognition and matching. In Computer Vision and Pattern Recognition, 2007. CVPR'07. IEEE Conference on, pages 1–8. IEEE, 2007. + +MSER::MSER +---------- +The MSER constructor + +.. ocv:function:: MSER::MSER(int _delta=5, int _min_area=60, int _max_area=14400, double _max_variation=0.25, double _min_diversity=.2, int _max_evolution=200, double _area_threshold=1.01, double _min_margin=0.003, int _edge_blur_size=5) + + :param _delta: Compares (sizei - sizei-delta)/sizei-delta + :param _min_area: Prune the area which smaller than minArea + :param _max_area: Prune the area which bigger than maxArea + :param _max_variation: Prune the area have simliar size to its children + :param _min_diversity: For color image, trace back to cut off mser with diversity less than min_diversity + :param _max_evolution: For color image, the evolution steps + :param _area_threshold: For color image, the area threshold to cause re-initialize + :param _min_margin: For color image, ignore too small margin + :param _edge_blur_size: For color image, the aperture size for edge blur + +MSER::operator() +---------------- + +Detect MSER regions + +.. ocv:function:: void MSER::operator()(const Mat& image, vector >& msers, const Mat& mask=Mat() ) const + + :param image: Input image (8UC1, 8UC3 or 8UC4) + :param msers: Resulting list of point sets + :param mask: The operation mask ORB --- diff --git a/modules/ts/misc/run.py b/modules/ts/misc/run.py index a3a13145a1..ad6a38ddb8 100755 --- a/modules/ts/misc/run.py +++ b/modules/ts/misc/run.py @@ -31,7 +31,7 @@ if __name__ == "__main__": parser.add_argument("--list", action="store_true", default=False, help="List available tests (executables)") parser.add_argument("--list_short", action="store_true", default=False, help="List available tests (aliases)") parser.add_argument("--list_short_main", action="store_true", default=False, help="List available tests (main repository, aliases)") - parser.add_argument("--configuration", metavar="CFG", default="", help="Visual Studio: force Debug or Release configuration") + parser.add_argument("--configuration", metavar="CFG", default=None, help="Force Debug or Release configuration (for Visual Studio and Java tests build)") parser.add_argument("-n", "--dry_run", action="store_true", help="Do not run the tests") parser.add_argument("-v", "--verbose", action="store_true", default=False, help="Print more debug information") @@ -95,12 +95,12 @@ if __name__ == "__main__": try: if not os.path.isdir(path): raise Err("Not a directory (should contain CMakeCache.txt ot test executables)") - cache = CMakeCache() + cache = CMakeCache(args.configuration) fname = os.path.join(path, "CMakeCache.txt") if os.path.isfile(fname): log.debug("Reading cmake cache file: %s", fname) - cache.read(path, fname, args.configuration) + cache.read(path, fname) else: log.debug("Assuming folder contains tests: %s", path) cache.setDummy(path) diff --git a/modules/ts/misc/run_suite.py b/modules/ts/misc/run_suite.py index c3d715e168..280c21caa6 100644 --- a/modules/ts/misc/run_suite.py +++ b/modules/ts/misc/run_suite.py @@ -48,22 +48,27 @@ class TestSuite(object): return sorted(self.getAliases(fname), key = len)[0] def getAliases(self, fname): + def getCuts(fname, prefix): + # filename w/o extension (opencv_test_core) + noext = re.sub(r"\.(exe|apk)$", '', fname) + # filename w/o prefix (core.exe) + nopref = fname + if fname.startswith(prefix): + nopref = fname[len(prefix):] + # filename w/o prefix and extension (core) + noprefext = noext + if noext.startswith(prefix): + noprefext = noext[len(prefix):] + return noext, nopref, noprefext # input is full path ('/home/.../bin/opencv_test_core') or 'java' res = [fname] fname = os.path.basename(fname) res.append(fname) # filename (opencv_test_core.exe) - noext = re.sub(r"\.(exe|apk)$", '', fname) - res.append(noext) # filename w/o extension (opencv_test_core) - nopref = None - if fname.startswith(self.nameprefix): - nopref = fname[len(self.nameprefix):] - res.append(nopref) # filename w/o prefix (core) - if noext.startswith(self.nameprefix): - res.append(noext[len(self.nameprefix):]) - if self.options.configuration == "Debug": - res.append(re.sub(r"d$", '', noext)) # MSVC debug config, remove 'd' suffix - if nopref: - res.append(re.sub(r"d$", '', nopref)) # MSVC debug config, remove 'd' suffix + for s in getCuts(fname, self.nameprefix): + res.append(s) + if self.cache.build_type == "Debug" and "Visual Studio" in self.cache.cmake_generator: + res.append(re.sub(r"d$", '', s)) # MSVC debug config, remove 'd' suffix + log.debug("Aliases: %s", set(res)) return set(res) def getTest(self, name): @@ -101,10 +106,7 @@ class TestSuite(object): args = args[:] exe = os.path.abspath(path) if path == "java": - cfg = self.cache.build_type - if self.options.configuration: - cfg = self.options.configuration - cmd = [self.cache.ant_executable, "-Dopencv.build.type=%s" % cfg, "buildAndTest"] + cmd = [self.cache.ant_executable, "-Dopencv.build.type=%s" % self.cache.build_type, "buildAndTest"] ret = execute(cmd, cwd = self.cache.java_test_binary_dir + "/.build") return None, ret else: diff --git a/modules/ts/misc/run_utils.py b/modules/ts/misc/run_utils.py index dca951d17a..5841631a7c 100644 --- a/modules/ts/misc/run_utils.py +++ b/modules/ts/misc/run_utils.py @@ -152,7 +152,7 @@ parse_patterns = ( {'name': "opencv_home", 'default': None, 'pattern': re.compile(r"^OpenCV_SOURCE_DIR:STATIC=(.+)$")}, {'name': "opencv_build", 'default': None, 'pattern': re.compile(r"^OpenCV_BINARY_DIR:STATIC=(.+)$")}, {'name': "tests_dir", 'default': None, 'pattern': re.compile(r"^EXECUTABLE_OUTPUT_PATH:PATH=(.+)$")}, - {'name': "build_type", 'default': "Release", 'pattern': re.compile(r"^CMAKE_BUILD_TYPE:STRING=(.*)$")}, + {'name': "build_type", 'default': "Release", 'pattern': re.compile(r"^CMAKE_BUILD_TYPE:\w+=(.*)$")}, {'name': "git_executable", 'default': None, 'pattern': re.compile(r"^GIT_EXECUTABLE:FILEPATH=(.*)$")}, {'name': "cxx_flags", 'default': "", 'pattern': re.compile(r"^CMAKE_CXX_FLAGS:STRING=(.*)$")}, {'name': "cxx_flags_debug", 'default': "", 'pattern': re.compile(r"^CMAKE_CXX_FLAGS_DEBUG:STRING=(.*)$")}, @@ -174,17 +174,19 @@ parse_patterns = ( ) class CMakeCache: - def __init__(self): + def __init__(self, cfg = None): self.setDefaultAttrs() self.cmake_home_vcver = None self.opencv_home_vcver = None self.featuresSIMD = None self.main_modules = [] + if cfg: + self.build_type = cfg def setDummy(self, path): self.tests_dir = os.path.normpath(path) - def read(self, path, fname, cfg): + def read(self, path, fname): rx = re.compile(r'^opencv_(\w+)_SOURCE_DIR:STATIC=(.*)$') module_paths = {} # name -> path with open(fname, "rt") as cachefile: @@ -213,10 +215,7 @@ class CMakeCache: # fix VS test binary path (add Debug or Release) if "Visual Studio" in self.cmake_generator: - if cfg: - self.tests_dir = os.path.join(self.tests_dir, self.options.configuration) - else: - self.tests_dir = os.path.join(self.tests_dir, self.build_type) + self.tests_dir = os.path.join(self.tests_dir, self.build_type) self.cmake_home_vcver = readGitVersion(self.git_executable, self.cmake_home) if self.opencv_home == self.cmake_home: From 46edb54ac380a15559271c70b493825a7933ae70 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Tue, 22 Dec 2015 15:58:39 +0300 Subject: [PATCH 057/105] Fixed PCH warning during ARM crosscompilation --- cmake/OpenCVPCHSupport.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVPCHSupport.cmake b/cmake/OpenCVPCHSupport.cmake index ad2bc8ba2c..13619daf27 100644 --- a/cmake/OpenCVPCHSupport.cmake +++ b/cmake/OpenCVPCHSupport.cmake @@ -169,9 +169,9 @@ MACRO(_PCH_GET_TARGET_COMPILE_FLAGS _cflags _header_name _pch_path _dowarn ) # if you have different versions of the headers for different build types # you may set _pch_dowarn IF (_dowarn) - SET(${_cflags} "${PCH_ADDITIONAL_COMPILER_FLAGS} -include \"${CMAKE_CURRENT_BINARY_DIR}/${_header_name}\" -Winvalid-pch " ) + SET(${_cflags} "${PCH_ADDITIONAL_COMPILER_FLAGS} -Winvalid-pch " ) ELSE (_dowarn) - SET(${_cflags} "${PCH_ADDITIONAL_COMPILER_FLAGS} -include \"${CMAKE_CURRENT_BINARY_DIR}/${_header_name}\" " ) + SET(${_cflags} "${PCH_ADDITIONAL_COMPILER_FLAGS} " ) ENDIF (_dowarn) ELSE(CMAKE_COMPILER_IS_GNUCXX) From c4e80952ced54978e8e6294a93d28ede251e8e9d Mon Sep 17 00:00:00 2001 From: atinfinity Date: Wed, 23 Dec 2015 23:43:01 +0900 Subject: [PATCH 058/105] changed to find TBB library(VS2015) --- cmake/OpenCVDetectTBB.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/OpenCVDetectTBB.cmake b/cmake/OpenCVDetectTBB.cmake index 8ff78bb3d4..be40b476b6 100644 --- a/cmake/OpenCVDetectTBB.cmake +++ b/cmake/OpenCVDetectTBB.cmake @@ -65,6 +65,8 @@ if(NOT HAVE_TBB) set(_TBB_LIB_PATH "${_TBB_LIB_PATH}/vc11") elseif(MSVC12) set(_TBB_LIB_PATH "${_TBB_LIB_PATH}/vc12") + elseif(MSVC14) + set(_TBB_LIB_PATH "${_TBB_LIB_PATH}/vc14") endif() set(TBB_LIB_DIR "${_TBB_LIB_PATH}" CACHE PATH "Full path of TBB library directory") link_directories("${TBB_LIB_DIR}") From 6e22020dadb3743884494e92386b90143dea90e6 Mon Sep 17 00:00:00 2001 From: Alexander Karsakov Date: Mon, 11 Jan 2016 11:39:18 +0300 Subject: [PATCH 059/105] Fixed pnpTask: don't copy inliers vector in case it's empty. --- modules/calib3d/src/solvepnp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/calib3d/src/solvepnp.cpp b/modules/calib3d/src/solvepnp.cpp index be587031ec..3c1e50e6b2 100644 --- a/modules/calib3d/src/solvepnp.cpp +++ b/modules/calib3d/src/solvepnp.cpp @@ -197,7 +197,7 @@ namespace cv } resultsMutex.lock(); - if ( (localInliers.size() > inliers.size()) || (localInliers.size() == inliers.size() && curIndex > bestIndex)) + if ( (localInliers.size() > inliers.size()) || (localInliers.size() == inliers.size() && inliers.size() > 0 && curIndex > bestIndex)) { inliers.clear(); inliers.resize(localInliers.size()); From 537d1322b469daff93432621d0c7fae3ec7f2546 Mon Sep 17 00:00:00 2001 From: takacsd Date: Mon, 11 Jan 2016 16:05:22 +0100 Subject: [PATCH 060/105] Add missing implementation to one of the Mat_<_Tp> constructor. (#5945) --- modules/core/include/opencv2/core/mat.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index e10cd9587d..631c698081 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -821,6 +821,9 @@ template inline Mat_<_Tp>::Mat_(int _dims, const int* _sz) template inline Mat_<_Tp>::Mat_(int _dims, const int* _sz, const _Tp& _s) : Mat(_dims, _sz, DataType<_Tp>::type, Scalar(_s)) {} +template inline Mat_<_Tp>::Mat_(int _dims, const int* _sz, _Tp* _data, const size_t* _steps) + : Mat(_dims, _sz, DataType<_Tp>::type, _data, _steps) {} + template inline Mat_<_Tp>::Mat_(const Mat_<_Tp>& m, const Range* ranges) : Mat(m, ranges) {} From 4f5d5851843a1a6f4bbe0bc6d761f24a04fa7b45 Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Fri, 4 Dec 2015 14:31:24 +0300 Subject: [PATCH 061/105] Update call to Tegra optimized morphology filtering --- modules/imgproc/src/morph.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/modules/imgproc/src/morph.cpp b/modules/imgproc/src/morph.cpp index 1cf4a625dd..118d4efedd 100644 --- a/modules/imgproc/src/morph.cpp +++ b/modules/imgproc/src/morph.cpp @@ -584,19 +584,11 @@ typedef MorphFVec DilateVec32f; #else -#ifdef HAVE_TEGRA_OPTIMIZATION -using tegra::ErodeRowVec8u; -using tegra::DilateRowVec8u; - -using tegra::ErodeColumnVec8u; -using tegra::DilateColumnVec8u; -#else typedef MorphRowNoVec ErodeRowVec8u; typedef MorphRowNoVec DilateRowVec8u; typedef MorphColumnNoVec ErodeColumnVec8u; typedef MorphColumnNoVec DilateColumnVec8u; -#endif typedef MorphRowNoVec ErodeRowVec16u; typedef MorphRowNoVec DilateRowVec16u; @@ -1114,6 +1106,17 @@ public: Mat srcStripe = src.rowRange(row0, row1); Mat dstStripe = dst.rowRange(row0, row1); + +#if defined HAVE_TEGRA_OPTIMIZATION + //Iterative separable filters are converted to single iteration filters + //But anyway check that we really get 1 iteration prior to processing + if( countNonZero(kernel) == kernel.rows*kernel.cols && iterations == 1 && + src.depth() == CV_8U && ( op == MORPH_ERODE || op == MORPH_DILATE ) && + tegra::morphology(srcStripe, dstStripe, op, kernel, anchor, + rowBorderType, columnBorderType, borderValue) ) + return; +#endif + Ptr f = createMorphologyFilter(op, src.type(), kernel, anchor, rowBorderType, columnBorderType, borderValue ); From 389f176a67741b403275a4e16e1dc2f26a3e64fa Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Mon, 18 Jan 2016 12:54:53 +0300 Subject: [PATCH 062/105] Removed ioctl calls to query for VIDIOC_G_INPUT and VIDIOC_ENUMINPUT since information returned by the calls is never used. Fixed icvCaptureFromCAM_V4L return value in case of V4L2 camera initialization failure. --- modules/highgui/src/cap_v4l.cpp | 47 +++------------------------------ 1 file changed, 3 insertions(+), 44 deletions(-) diff --git a/modules/highgui/src/cap_v4l.cpp b/modules/highgui/src/cap_v4l.cpp index d09c8f88cf..2599cada80 100644 --- a/modules/highgui/src/cap_v4l.cpp +++ b/modules/highgui/src/cap_v4l.cpp @@ -491,8 +491,6 @@ static int try_init_v4l2(CvCaptureCAM_V4L* capture, char *deviceName) // 0 then detected nothing // 1 then V4L2 device - int deviceIndex; - /* Open and test V4L2 device */ capture->deviceHandle = open (deviceName, O_RDWR /* required */ | O_NONBLOCK, 0); if (-1 == capture->deviceHandle) @@ -514,28 +512,6 @@ static int try_init_v4l2(CvCaptureCAM_V4L* capture, char *deviceName) return 0; } - /* Query channels number */ - if (-1 == ioctl (capture->deviceHandle, VIDIOC_G_INPUT, &deviceIndex)) - { -#ifndef NDEBUG - fprintf(stderr, "(DEBUG) try_init_v4l2 VIDIOC_G_INPUT \"%s\": %s\n", deviceName, strerror(errno)); -#endif - icvCloseCAM_V4L(capture); - return 0; - } - - /* Query information about current input */ - CLEAR (capture->inp); - capture->inp.index = deviceIndex; - if (-1 == ioctl (capture->deviceHandle, VIDIOC_ENUMINPUT, &capture->inp)) - { -#ifndef NDEBUG - fprintf(stderr, "(DEBUG) try_init_v4l2 VIDIOC_ENUMINPUT \"%s\": %s\n", deviceName, strerror(errno)); -#endif - icvCloseCAM_V4L(capture); - return 0; - } - return 1; } @@ -834,26 +810,6 @@ static int _capture_V4L2 (CvCaptureCAM_V4L *capture, char *deviceName) return -1; } - /* The following code sets the CHANNEL_NUMBER of the video input. Some video sources - have sub "Channel Numbers". For a typical V4L TV capture card, this is usually 1. - I myself am using a simple NTSC video input capture card that uses the value of 1. - If you are not in North America or have a different video standard, you WILL have to change - the following settings and recompile/reinstall. This set of settings is based on - the most commonly encountered input video source types (like my bttv card) */ - - if(capture->inp.index > 0) { - CLEAR (capture->inp); - capture->inp.index = CHANNEL_NUMBER; - /* Set only channel number to CHANNEL_NUMBER */ - /* V4L2 have a status field from selected video mode */ - if (-1 == ioctl (capture->deviceHandle, VIDIOC_ENUMINPUT, &capture->inp)) - { - fprintf (stderr, "HIGHGUI ERROR: V4L2: Aren't able to set channel number\n"); - icvCloseCAM_V4L (capture); - return -1; - } - } /* End if */ - /* Find Window info */ CLEAR (capture->form); capture->form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -1157,6 +1113,9 @@ static CvCaptureCAM_V4L * icvCaptureFromCAM_V4L (int index) } #endif /* HAVE_CAMV4L */ #ifdef HAVE_CAMV4L2 +#ifndef HAVE_CAMV4L + return NULL; +#endif /* !HAVE_CAMV4L */ } else { V4L2_SUPPORT = 1; } From bb1c2d71a8714be1b31ee31424ac7dbef1ef36d8 Mon Sep 17 00:00:00 2001 From: Elena Gvozdeva Date: Wed, 20 Jan 2016 13:07:07 +0300 Subject: [PATCH 063/105] fix bug on border at pyrUp --- modules/imgproc/src/pyramids.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/imgproc/src/pyramids.cpp b/modules/imgproc/src/pyramids.cpp index 4000167497..acf507248e 100644 --- a/modules/imgproc/src/pyramids.cpp +++ b/modules/imgproc/src/pyramids.cpp @@ -349,7 +349,7 @@ pyrUp_( const Mat& _src, Mat& _dst, int) for( ; sy <= y + 1; sy++ ) { WT* row = buf + ((sy - sy0) % PU_SZ)*bufstep; - int _sy = borderInterpolate(sy*2, dsize.height, BORDER_REFLECT_101)/2; + int _sy = borderInterpolate(sy*2, ssize.height*2, BORDER_REFLECT_101)/2; const T* src = (const T*)(_src.data + _src.step*_sy); if( ssize.width == cn ) @@ -370,6 +370,11 @@ pyrUp_( const Mat& _src, Mat& _dst, int) t0 = src[sx - cn] + src[sx]*7; t1 = src[sx]*8; row[dx] = t0; row[dx + cn] = t1; + + if (dsize.width > ssize.width*2) + { + row[(_dst.cols-1) + x] = row[dx + cn]; + } } for( x = cn; x < ssize.width - cn; x++ ) @@ -395,6 +400,17 @@ pyrUp_( const Mat& _src, Mat& _dst, int) dst1[x] = t1; dst0[x] = t0; } } + + if (dsize.height > ssize.height*2) + { + T* dst0 = _dst.ptr(ssize.height*2-2); + T* dst2 = _dst.ptr(ssize.height*2); + + for(x = 0; x < dsize.width ; x++ ) + { + dst2[x] = dst0[x]; + } + } } typedef void (*PyrFunc)(const Mat&, Mat&, int); From 4a8d1147c224214336ddd8bd8671ab7fed95fcab Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 20 Jan 2016 13:06:51 +0300 Subject: [PATCH 064/105] include available modules only --- include/opencv2/opencv.hpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/opencv2/opencv.hpp b/include/opencv2/opencv.hpp index f89547c9be..f76da92fef 100644 --- a/include/opencv2/opencv.hpp +++ b/include/opencv2/opencv.hpp @@ -43,19 +43,41 @@ #ifndef __OPENCV_ALL_HPP__ #define __OPENCV_ALL_HPP__ +#include "opencv2/opencv_modules.hpp" + #include "opencv2/core/core_c.h" #include "opencv2/core/core.hpp" +#ifdef HAVE_OPENCV_FLANN #include "opencv2/flann/miniflann.hpp" +#endif +#ifdef HAVE_OPENCV_IMGPROC #include "opencv2/imgproc/imgproc_c.h" #include "opencv2/imgproc/imgproc.hpp" +#endif +#ifdef HAVE_OPENCV_PHOTO #include "opencv2/photo/photo.hpp" +#endif +#ifdef HAVE_OPENCV_VIDEO #include "opencv2/video/video.hpp" +#endif +#ifdef HAVE_OPENCV_FEATURES2D #include "opencv2/features2d/features2d.hpp" +#endif +#ifdef HAVE_OPENCV_OBJDETECT #include "opencv2/objdetect/objdetect.hpp" +#endif +#ifdef HAVE_OPENCV_CALIB3D #include "opencv2/calib3d/calib3d.hpp" +#endif +#ifdef HAVE_OPENCV_ML #include "opencv2/ml/ml.hpp" +#endif +#ifdef HAVE_OPENCV_HIGHGUI #include "opencv2/highgui/highgui_c.h" #include "opencv2/highgui/highgui.hpp" +#endif +#ifdef HAVE_OPENCV_CONTRIB #include "opencv2/contrib/contrib.hpp" +#endif #endif From f01f1bc5e67f96a470f573888541e5c03e66cc3c Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Wed, 20 Jan 2016 19:20:36 +0300 Subject: [PATCH 065/105] Fixed "Conditional jump or move depends on uninitialised value(s)" valgrind issue due to wrong pointer recalculation for zero level of pyramid. --- modules/legacy/src/pyrsegmentation.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/legacy/src/pyrsegmentation.cpp b/modules/legacy/src/pyrsegmentation.cpp index c64b7bcde0..30fed0e569 100644 --- a/modules/legacy/src/pyrsegmentation.cpp +++ b/modules/legacy/src/pyrsegmentation.cpp @@ -130,8 +130,8 @@ static void icvMaxRoi1( _CvRect16u *max_rect, int x, int y ); (float)fabs((a).green - (b).green), \ (float)fabs((a).blue - (b).blue))*/ -#define _CV_NEXT_BASE_C1(p,n) (_CvPyramid*)((char*)(p) + (n)*sizeof(_CvPyramidBase)) -#define _CV_NEXT_BASE_C3(p,n) (_CvPyramidC3*)((char*)(p) + (n)*sizeof(_CvPyramidBaseC3)) +#define _CV_NEXT_BASE_C1(p,n) ((_CvPyramid*)((char*)(p) + (n)*(ptrdiff_t)sizeof(_CvPyramidBase))) +#define _CV_NEXT_BASE_C3(p,n) ((_CvPyramidC3*)((char*)(p) + (n)*(ptrdiff_t)sizeof(_CvPyramidBaseC3))) CV_INLINE float icvRGBDist_Max( const _CvRGBf& a, const _CvRGBf& b ) @@ -868,7 +868,7 @@ icvPyrSegmentation8uC3R( uchar * src_image, int src_step, if( p_cur[size.width].a == 0 ) { - p_cur[size.width].c = p_prev[(l != 0) - 1].c; + p_cur[size.width].c = (l != 0) ? p_prev->c : _CV_NEXT_BASE_C3( p_prev, -1 )->c; } else { From 5a5378b3bf12cbd19561ee608f343e61a22e62ec Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 20 Jan 2016 11:58:48 +0300 Subject: [PATCH 066/105] Added lintian overrides for package signature entry. --- cmake/OpenCVPackaging.cmake | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmake/OpenCVPackaging.cmake b/cmake/OpenCVPackaging.cmake index 775605c4f8..0a53e92429 100644 --- a/cmake/OpenCVPackaging.cmake +++ b/cmake/OpenCVPackaging.cmake @@ -289,6 +289,13 @@ else() set(TESTS_LINTIAN_OVERRIDES "empty-binary-package") # there is no tests endif() +set(ALL_COMPONENTS "libs" "dev" "docs" "python" "java" "samples" "tests") + +foreach (comp ${ALL_COMPONENTS}) + string(TOUPPER ${comp} comp_upcase) + list(APPEND ${comp_upcase}_LINTIAN_OVERRIDES "misplaced-extra-member-in-deb") # for signed packages +endforeach() + if(CPACK_GENERATOR STREQUAL "DEB") find_program(GZIP_TOOL NAMES "gzip" PATHS "/bin" "/usr/bin" "/usr/local/bin") if(NOT GZIP_TOOL) @@ -300,7 +307,6 @@ if(CPACK_GENERATOR STREQUAL "DEB") OUTPUT_STRIP_TRAILING_WHITESPACE) set(CHANGELOG_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}") - set(ALL_COMPONENTS "libs" "dev" "docs" "python" "java" "samples" "tests") foreach (comp ${ALL_COMPONENTS}) string(TOUPPER "${comp}" comp_upcase) From 912592de4ce0b6b1c74a56cded3f1a38b64bc6d4 Mon Sep 17 00:00:00 2001 From: Matt Leotta Date: Thu, 21 Jan 2016 16:07:56 -0500 Subject: [PATCH 067/105] Remove "INSTALL_NAME_DIR lib" target property The INSTALL_NAME_DIR property of a target specifies how a dynamic library should be found on OS X. If INSTALL_NAME_DIR is not specified the loader will search relative to the standard search paths. If specified it should either be an absolute path or relative path prefixed with either @executable_path, @load_path, or @rpath. Specifying "lib" does not make sense here and causes linking error as documented here: http://answers.opencv.org/question/4134/cmake-install_name_tool-absolute-path-for-library-on-mac-osx/ and here http://stackoverflow.com/questions/26978806/dyld-library-not-loaded-lib-libopencv-core-3-0-dylib-reason-image-not-found This patch removes INSTALL_NAME_DIR everywhere it is set to "lib". An alternate solution would be to set an absolute path like "${CMAKE_INSTALL_PREFIX}/lib" or relative path like "@executable_path/../lib". However, if there is not specific need for specifying a path, it is probably best left unset. --- apps/annotation/CMakeLists.txt | 1 - apps/haartraining/CMakeLists.txt | 1 - apps/traincascade/CMakeLists.txt | 1 - cmake/OpenCVModule.cmake | 1 - platforms/android/libinfo/CMakeLists.txt | 1 - 5 files changed, 5 deletions(-) diff --git a/apps/annotation/CMakeLists.txt b/apps/annotation/CMakeLists.txt index b34ef3904b..54d89d953f 100644 --- a/apps/annotation/CMakeLists.txt +++ b/apps/annotation/CMakeLists.txt @@ -20,7 +20,6 @@ set_target_properties(${the_target} PROPERTIES DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}" ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH} RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH} - INSTALL_NAME_DIR lib OUTPUT_NAME "opencv_annotation") if(ENABLE_SOLUTION_FOLDERS) diff --git a/apps/haartraining/CMakeLists.txt b/apps/haartraining/CMakeLists.txt index 35a26fe36b..122d01ebfe 100644 --- a/apps/haartraining/CMakeLists.txt +++ b/apps/haartraining/CMakeLists.txt @@ -38,7 +38,6 @@ set_target_properties(opencv_haartraining_engine PROPERTIES DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}" ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH} RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH} - INSTALL_NAME_DIR lib ) # ----------------------------------------------------------- diff --git a/apps/traincascade/CMakeLists.txt b/apps/traincascade/CMakeLists.txt index 941c0ec711..857604af14 100644 --- a/apps/traincascade/CMakeLists.txt +++ b/apps/traincascade/CMakeLists.txt @@ -26,7 +26,6 @@ set_target_properties(${the_target} PROPERTIES DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}" ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH} RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH} - INSTALL_NAME_DIR lib OUTPUT_NAME "opencv_traincascade") if(ENABLE_SOLUTION_FOLDERS) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 88ebe54f79..418bf46fdb 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -594,7 +594,6 @@ macro(ocv_create_module) ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH} LIBRARY_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH} RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH} - INSTALL_NAME_DIR lib ) # For dynamic link numbering convenions diff --git a/platforms/android/libinfo/CMakeLists.txt b/platforms/android/libinfo/CMakeLists.txt index 592be2d044..010ac14da0 100644 --- a/platforms/android/libinfo/CMakeLists.txt +++ b/platforms/android/libinfo/CMakeLists.txt @@ -34,7 +34,6 @@ add_library(opencv_info SHARED info.c) set_target_properties(${the_module} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH} RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH} - INSTALL_NAME_DIR lib ) get_filename_component(lib_name "libopencv_info.so" NAME) From 69eaa89e22185dba0c16bbce6e200426c7c566e9 Mon Sep 17 00:00:00 2001 From: atinfinity Date: Sun, 31 Jan 2016 01:56:45 +0900 Subject: [PATCH 068/105] fixed to use NVCUVID in 'cudacodec' module. --- cmake/OpenCVDetectCUDA.cmake | 13 ++++++++++++- modules/gpu/CMakeLists.txt | 9 ++++++--- modules/gpu/src/precomp.hpp | 4 +++- modules/gpu/src/video_writer.cpp | 2 +- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/cmake/OpenCVDetectCUDA.cmake b/cmake/OpenCVDetectCUDA.cmake index cca125034a..d2548f9af5 100644 --- a/cmake/OpenCVDetectCUDA.cmake +++ b/cmake/OpenCVDetectCUDA.cmake @@ -35,7 +35,18 @@ if(CUDA_FOUND) if(WITH_NVCUVID) find_cuda_helper_libs(nvcuvid) - set(HAVE_NVCUVID 1) + + if(WIN32) + find_cuda_helper_libs(nvcuvenc) + endif() + + if(CUDA_nvcuvid_LIBRARY) + set(HAVE_NVCUVID 1) + endif() + + if(CUDA_nvcuvenc_LIBRARY) + set(HAVE_NVCUVENC 1) + endif() endif() message(STATUS "CUDA detected: " ${CUDA_VERSION}) diff --git a/modules/gpu/CMakeLists.txt b/modules/gpu/CMakeLists.txt index 6de3e0efab..caec0efa5a 100644 --- a/modules/gpu/CMakeLists.txt +++ b/modules/gpu/CMakeLists.txt @@ -54,11 +54,14 @@ if(HAVE_CUDA) endif() if(WITH_NVCUVID) - set(cuda_link_libs ${cuda_link_libs} ${CUDA_CUDA_LIBRARY} ${CUDA_nvcuvid_LIBRARY}) + if(HAVE_NVCUVID) + set(cuda_link_libs ${cuda_link_libs} ${CUDA_CUDA_LIBRARY} ${CUDA_nvcuvid_LIBRARY}) + endif() if(WIN32) - find_cuda_helper_libs(nvcuvenc) - set(cuda_link_libs ${cuda_link_libs} ${CUDA_nvcuvenc_LIBRARY}) + if(HAVE_NVCUVENC) + set(cuda_link_libs ${cuda_link_libs} ${CUDA_nvcuvenc_LIBRARY}) + endif() endif() if(WITH_FFMPEG) diff --git a/modules/gpu/src/precomp.hpp b/modules/gpu/src/precomp.hpp index 785783b5e2..a4f75db19a 100644 --- a/modules/gpu/src/precomp.hpp +++ b/modules/gpu/src/precomp.hpp @@ -98,7 +98,9 @@ #include #ifdef WIN32 - #include + #ifdef HAVE_NVCUVENC + #include + #endif #endif #endif diff --git a/modules/gpu/src/video_writer.cpp b/modules/gpu/src/video_writer.cpp index 0937ad1f27..bf51c25408 100644 --- a/modules/gpu/src/video_writer.cpp +++ b/modules/gpu/src/video_writer.cpp @@ -42,7 +42,7 @@ #include "precomp.hpp" -#if !defined(HAVE_CUDA) || defined(CUDA_DISABLER) || !defined(HAVE_NVCUVID) || !defined(WIN32) +#if !defined(HAVE_CUDA) || defined(CUDA_DISABLER) || !defined(HAVE_NVCUVENC) || !defined(WIN32) class cv::gpu::VideoWriter_GPU::Impl { From cf43790a27f1815e2ae3ed63f16e5d8a77e100b5 Mon Sep 17 00:00:00 2001 From: atinfinity Date: Mon, 1 Feb 2016 21:15:28 +0900 Subject: [PATCH 069/105] added cmakedefine to cmake/templates/cvconfig.h.in --- cmake/templates/cvconfig.h.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/templates/cvconfig.h.in b/cmake/templates/cvconfig.h.in index d1c9e65d3d..caea7da3e7 100644 --- a/cmake/templates/cvconfig.h.in +++ b/cmake/templates/cvconfig.h.in @@ -109,6 +109,9 @@ /* NVidia Video Decoding API*/ #cmakedefine HAVE_NVCUVID +/* NVidia Video Encoding API*/ +#cmakedefine HAVE_NVCUVENC + /* OpenCL Support */ #cmakedefine HAVE_OPENCL #cmakedefine HAVE_OPENCL_STATIC From 544824ffc1c13d53742f0ddaf3f5158de0c7f748 Mon Sep 17 00:00:00 2001 From: Alexander Shishkov Date: Mon, 8 Feb 2016 11:07:58 +0300 Subject: [PATCH 070/105] Removed gittip link This project is dead from the 1st September last year --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 3a26ad8555..e74a329668 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ ### OpenCV: Open Source Computer Vision Library -[![Gittip](http://img.shields.io/gittip/OpenCV.png)](https://www.gittip.com/OpenCV/) - #### Resources * Homepage: From 068769263e166fc1de885a9f2116511cb790da98 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Fri, 12 Feb 2016 00:33:18 +0300 Subject: [PATCH 071/105] fixed compilation warning --- modules/core/include/opencv2/core/types_c.h | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/core/include/opencv2/core/types_c.h b/modules/core/include/opencv2/core/types_c.h index 9e9231db08..c21cd2c758 100644 --- a/modules/core/include/opencv2/core/types_c.h +++ b/modules/core/include/opencv2/core/types_c.h @@ -317,6 +317,7 @@ enum { #define ARM_ROUND(_value, _asm_string) \ int res; \ float temp; \ + (void)temp; \ asm(_asm_string : [res] "=r" (res), [temp] "=w" (temp) : [value] "w" (_value)); \ return res; // 2. version for double From e0f426f78bbf0fd6e161de4a778800b2fb4e7288 Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Wed, 24 Feb 2016 14:18:51 +0300 Subject: [PATCH 072/105] Backport of new python tests from master branch(PR https://github.com/Itseez/opencv/pull/6025). At the moment tests requre samples/data copied to source location from master branch. --- modules/python/test/test.py | 48 ++--- modules/python/test/test_calibration.py | 71 +++++++ modules/python/test/test_camshift.py | 92 ++++++++ modules/python/test/test_dft.py | 46 ++++ modules/python/test/test_digits.py | 197 ++++++++++++++++++ modules/python/test/test_facedetect.py | 90 ++++++++ .../python/test/test_feature_homography.py | 160 ++++++++++++++ modules/python/test/test_fitline.py | 66 ++++++ modules/python/test/test_gaussian_mix.py | 58 ++++++ modules/python/test/test_grabcut.py | 67 ++++++ modules/python/test/test_houghcircles.py | 81 +++++++ modules/python/test/test_houghlines.py | 65 ++++++ modules/python/test/test_kmeans.py | 70 +++++++ modules/python/test/test_letter_recog.py | 161 ++++++++++++++ modules/python/test/test_lk_homography.py | 96 +++++++++ modules/python/test/test_lk_track.py | 111 ++++++++++ modules/python/test/test_morphology.py | 51 +++++ modules/python/test/test_mser.py | 65 ++++++ modules/python/test/test_peopledetect.py | 62 ++++++ modules/python/test/test_squares.py | 96 +++++++++ modules/python/test/test_texture_flow.py | 51 +++++ modules/python/test/test_watershed.py | 33 +++ modules/python/test/tests_common.py | 79 +++++++ modules/python/test/tst_scene_render.py | 119 +++++++++++ 24 files changed, 2005 insertions(+), 30 deletions(-) create mode 100644 modules/python/test/test_calibration.py create mode 100644 modules/python/test/test_camshift.py create mode 100644 modules/python/test/test_dft.py create mode 100644 modules/python/test/test_digits.py create mode 100644 modules/python/test/test_facedetect.py create mode 100644 modules/python/test/test_feature_homography.py create mode 100644 modules/python/test/test_fitline.py create mode 100644 modules/python/test/test_gaussian_mix.py create mode 100644 modules/python/test/test_grabcut.py create mode 100644 modules/python/test/test_houghcircles.py create mode 100644 modules/python/test/test_houghlines.py create mode 100644 modules/python/test/test_kmeans.py create mode 100644 modules/python/test/test_letter_recog.py create mode 100644 modules/python/test/test_lk_homography.py create mode 100644 modules/python/test/test_lk_track.py create mode 100644 modules/python/test/test_morphology.py create mode 100644 modules/python/test/test_mser.py create mode 100644 modules/python/test/test_peopledetect.py create mode 100644 modules/python/test/test_squares.py create mode 100644 modules/python/test/test_texture_flow.py create mode 100644 modules/python/test/test_watershed.py create mode 100644 modules/python/test/tests_common.py create mode 100644 modules/python/test/tst_scene_render.py diff --git a/modules/python/test/test.py b/modules/python/test/test.py index a4a3d759a9..e9f1a13506 100755 --- a/modules/python/test/test.py +++ b/modules/python/test/test.py @@ -122,6 +122,15 @@ class OpenCVTests(unittest.TestCase): """ Compute a hash for an image, useful for image comparisons """ return hashlib.md5(im.tostring()).digest() +#import new OpenCV tests(do we really need old ones in this case) +from tests_common import NewOpenCVTests + +basedir = os.path.abspath(os.path.dirname(__file__)) + +def load_tests(loader, tests, pattern): + tests.addTests(loader.discover(basedir, pattern='test_*.py')) + return tests + # Tests to run first; check the handful of basic operations that the later tests rely on class PreliminaryTests(OpenCVTests): @@ -2239,36 +2248,15 @@ if __name__ == '__main__': print "Local repo path:", args.repo print "Local data path:", args.data OpenCVTests.repoPath = args.repo - OpenCVTests.dataPath = args.data + NewOpenCVTests.repoPath = args.repo + try: + OpenCVTests.dataPath = os.environ['OPENCV_TEST_DATA_PATH'] + NewOpenCVTests.extraTestDataPath = OpenCVTests.dataPath + except KeyError: + OpenCVTests.dataPath = args.data + NewOpenCVTests.extraTestDataPath = args.data + if args.data is None: + print('Missing opencv extra repository. Some of tests may fail.') random.seed(0) unit_argv = [sys.argv[0]] + other; unittest.main(argv=unit_argv) -# optlist, args = getopt.getopt(sys.argv[1:], 'l:rd') -# loops = 1 -# shuffle = 0 -# doc_frags = False -# for o,a in optlist: -# if o == '-l': -# loops = int(a) -# if o == '-r': -# shuffle = 1 -# if o == '-d': -# doc_frags = True -# -# cases = [PreliminaryTests, FunctionTests, AreaTests] -# if doc_frags: -# cases += [DocumentFragmentTests] -# everything = [(tc, t) for tc in cases for t in unittest.TestLoader().getTestCaseNames(tc) ] -# if len(args) == 0: -# # cases = [NewTests] -# args = everything -# else: -# args = [(tc, t) for (tc, t) in everything if t in args] -# -# suite = unittest.TestSuite() -# for l in range(loops): -# if shuffle: -# random.shuffle(args) -# for tc,t in args: -# suite.addTest(tc(t)) -# unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/modules/python/test/test_calibration.py b/modules/python/test/test_calibration.py new file mode 100644 index 0000000000..665521862c --- /dev/null +++ b/modules/python/test/test_calibration.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python + +''' +camera calibration for distorted images with chess board samples +reads distorted images, calculates the calibration and write undistorted images +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +from tests_common import NewOpenCVTests + +class calibration_test(NewOpenCVTests): + + def test_calibration(self): + + from glob import glob + img_names = [] + for i in range(1, 15): + if i < 10: + img_names.append('samples/data/left0{}.jpg'.format(str(i))) + elif i != 10: + img_names.append('samples/data/left{}.jpg'.format(str(i))) + + square_size = 1.0 + pattern_size = (9, 6) + pattern_points = np.zeros((np.prod(pattern_size), 3), np.float32) + pattern_points[:, :2] = np.indices(pattern_size).T.reshape(-1, 2) + pattern_points *= square_size + + obj_points = [] + img_points = [] + h, w = 0, 0 + img_names_undistort = [] + for fn in img_names: + img = self.get_sample(fn, 0) + if img is None: + continue + + h, w = img.shape[:2] + found, corners = cv2.findChessboardCorners(img, pattern_size) + if found: + term = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 30, 0.1) + cv2.cornerSubPix(img, corners, (5, 5), (-1, -1), term) + + if not found: + continue + + img_points.append(corners.reshape(-1, 2)) + obj_points.append(pattern_points) + + # calculate camera distortion + rms, camera_matrix, dist_coefs, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, (w, h), None, None, flags = 0) + + eps = 0.01 + normCamEps = 10.0 + normDistEps = 0.001 + + cameraMatrixTest = [[ 532.80992189, 0., 342.4952186 ], + [ 0., 532.93346422, 233.8879292 ], + [ 0., 0., 1. ]] + + distCoeffsTest = [ -2.81325576e-01, 2.91130406e-02, + 1.21234330e-03, -1.40825372e-04, 1.54865844e-01] + + self.assertLess(abs(rms - 0.196334638034), eps) + self.assertLess(cv2.norm(camera_matrix - cameraMatrixTest, cv2.NORM_L1), normCamEps) + self.assertLess(cv2.norm(dist_coefs - distCoeffsTest, cv2.NORM_L1), normDistEps) \ No newline at end of file diff --git a/modules/python/test/test_camshift.py b/modules/python/test/test_camshift.py new file mode 100644 index 0000000000..a824320eff --- /dev/null +++ b/modules/python/test/test_camshift.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python + +''' +Camshift tracker +================ + +This is a demo that shows mean-shift based tracking +You select a color objects such as your face and it tracks it. +This reads from video camera (0 by default, or the camera number the user enters) + +http://www.robinhewitt.com/research/track/camshift.html + +''' + +# Python 2/3 compatibility +from __future__ import print_function +import sys +PY3 = sys.version_info[0] == 3 + +if PY3: + xrange = range + +import numpy as np +import cv2 +from tst_scene_render import TestSceneRender + +from tests_common import NewOpenCVTests, intersectionRate + +class camshift_test(NewOpenCVTests): + + framesNum = 300 + frame = None + selection = None + drag_start = None + show_backproj = False + track_window = None + render = None + errors = 0 + + def prepareRender(self): + + self.render = TestSceneRender(self.get_sample('samples/data/pca_test1.jpg'), deformation = True) + + def runTracker(self): + + framesCounter = 0 + self.selection = True + + xmin, ymin, xmax, ymax = self.render.getCurrentRect() + + self.track_window = (xmin, ymin, xmax - xmin, ymax - ymin) + + while True: + framesCounter += 1 + self.frame = self.render.getNextFrame() + hsv = cv2.cvtColor(self.frame, cv2.COLOR_BGR2HSV) + mask = cv2.inRange(hsv, np.array((0., 60., 32.)), np.array((180., 255., 255.))) + + if self.selection: + x0, y0, x1, y1 = self.render.getCurrentRect() + 50 + x0 -= 100 + y0 -= 100 + + hsv_roi = hsv[y0:y1, x0:x1] + mask_roi = mask[y0:y1, x0:x1] + hist = cv2.calcHist( [hsv_roi], [0], mask_roi, [16], [0, 180] ) + cv2.normalize(hist, hist, 0, 255, cv2.NORM_MINMAX) + self.hist = hist.reshape(-1) + self.selection = False + + if self.track_window and self.track_window[2] > 0 and self.track_window[3] > 0: + self.selection = None + prob = cv2.calcBackProject([hsv], [0], self.hist, [0, 180], 1) + prob &= mask + term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 ) + track_box, self.track_window = cv2.CamShift(prob, self.track_window, term_crit) + + trackingRect = np.array(self.track_window) + trackingRect[2] += trackingRect[0] + trackingRect[3] += trackingRect[1] + + if intersectionRate(self.render.getCurrentRect(), trackingRect) < 0.4: + self.errors += 1 + + if framesCounter > self.framesNum: + break + + self.assertLess(float(self.errors) / self.framesNum, 0.4) + + def test_camshift(self): + self.prepareRender() + self.runTracker() \ No newline at end of file diff --git a/modules/python/test/test_dft.py b/modules/python/test/test_dft.py new file mode 100644 index 0000000000..f796939970 --- /dev/null +++ b/modules/python/test/test_dft.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +''' +Test for disctrete fourier transform (dft) +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import cv2 +import numpy as np +import sys + +from tests_common import NewOpenCVTests + +class dft_test(NewOpenCVTests): + def test_dft(self): + + img = self.get_sample('samples/data/rubberwhale1.png', 0) + eps = 0.001 + + #test direct transform + refDft = np.fft.fft2(img) + refDftShift = np.fft.fftshift(refDft) + refMagnitide = np.log(1.0 + np.abs(refDftShift)) + + testDft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT) + testDftShift = np.fft.fftshift(testDft) + testMagnitude = np.log(1.0 + cv2.magnitude(testDftShift[:,:,0], testDftShift[:,:,1])) + + refMagnitide = cv2.normalize(refMagnitide, 0.0, 1.0, cv2.NORM_MINMAX) + testMagnitude = cv2.normalize(testMagnitude, 0.0, 1.0, cv2.NORM_MINMAX) + + self.assertLess(cv2.norm(refMagnitide - testMagnitude), eps) + + #test inverse transform + img_back = np.fft.ifft2(refDft) + img_back = np.abs(img_back) + + img_backTest = cv2.idft(testDft) + img_backTest = cv2.magnitude(img_backTest[:,:,0], img_backTest[:,:,1]) + + img_backTest = cv2.normalize(img_backTest, 0.0, 1.0, cv2.NORM_MINMAX) + img_back = cv2.normalize(img_back, 0.0, 1.0, cv2.NORM_MINMAX) + + self.assertLess(cv2.norm(img_back - img_backTest), eps) \ No newline at end of file diff --git a/modules/python/test/test_digits.py b/modules/python/test/test_digits.py new file mode 100644 index 0000000000..587c355e37 --- /dev/null +++ b/modules/python/test/test_digits.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python + +''' +SVM and KNearest digit recognition. + +Sample loads a dataset of handwritten digits from '../data/digits.png'. +Then it trains a SVM and KNearest classifiers on it and evaluates +their accuracy. + +Following preprocessing is applied to the dataset: + - Moment-based image deskew (see deskew()) + - Digit images are split into 4 10x10 cells and 16-bin + histogram of oriented gradients is computed for each + cell + - Transform histograms to space with Hellinger metric (see [1] (RootSIFT)) + + +[1] R. Arandjelovic, A. Zisserman + "Three things everyone should know to improve object retrieval" + http://www.robots.ox.ac.uk/~vgg/publications/2012/Arandjelovic12/arandjelovic12.pdf + +''' + + +# Python 2/3 compatibility +from __future__ import print_function + +# built-in modules +from multiprocessing.pool import ThreadPool + +import cv2 + +import numpy as np +from numpy.linalg import norm + + +SZ = 20 # size of each digit is SZ x SZ +CLASS_N = 10 +DIGITS_FN = 'samples/data/digits.png' + +def split2d(img, cell_size, flatten=True): + h, w = img.shape[:2] + sx, sy = cell_size + cells = [np.hsplit(row, w//sx) for row in np.vsplit(img, h//sy)] + cells = np.array(cells) + if flatten: + cells = cells.reshape(-1, sy, sx) + return cells + +def deskew(img): + m = cv2.moments(img) + if abs(m['mu02']) < 1e-2: + return img.copy() + skew = m['mu11']/m['mu02'] + M = np.float32([[1, skew, -0.5*SZ*skew], [0, 1, 0]]) + img = cv2.warpAffine(img, M, (SZ, SZ), flags=cv2.WARP_INVERSE_MAP | cv2.INTER_LINEAR) + return img + +class StatModel(object): + def load(self, fn): + self.model.load(fn) # Known bug: https://github.com/Itseez/opencv/issues/4969 + def save(self, fn): + self.model.save(fn) + +class KNearest(StatModel): + def __init__(self, k = 3): + self.k = k + self.model = cv2.KNearest() + + def train(self, samples, responses): + self.model.train(samples, responses) + + def predict(self, samples): + retval, results, neigh_resp, dists = self.model.find_nearest(samples, self.k) + return results.ravel() + +class SVM(StatModel): + def __init__(self, C = 1, gamma = 0.5): + self.params = dict( kernel_type = cv2.SVM_RBF, + svm_type = cv2.SVM_C_SVC, + C = C, + gamma = gamma ) + self.model = cv2.SVM() + + def train(self, samples, responses): + self.model.train(samples, responses, params = self.params) + + def predict(self, samples): + return self.model.predict_all(samples).ravel() + + +def evaluate_model(model, digits, samples, labels): + resp = model.predict(samples) + err = (labels != resp).mean() + + confusion = np.zeros((10, 10), np.int32) + for i, j in zip(labels, resp): + confusion[int(i), int(j)] += 1 + + return err, confusion + +def preprocess_simple(digits): + return np.float32(digits).reshape(-1, SZ*SZ) / 255.0 + +def preprocess_hog(digits): + samples = [] + for img in digits: + gx = cv2.Sobel(img, cv2.CV_32F, 1, 0) + gy = cv2.Sobel(img, cv2.CV_32F, 0, 1) + mag, ang = cv2.cartToPolar(gx, gy) + bin_n = 16 + bin = np.int32(bin_n*ang/(2*np.pi)) + bin_cells = bin[:10,:10], bin[10:,:10], bin[:10,10:], bin[10:,10:] + mag_cells = mag[:10,:10], mag[10:,:10], mag[:10,10:], mag[10:,10:] + hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)] + hist = np.hstack(hists) + + # transform to Hellinger kernel + eps = 1e-7 + hist /= hist.sum() + eps + hist = np.sqrt(hist) + hist /= norm(hist) + eps + + samples.append(hist) + return np.float32(samples) + +from tests_common import NewOpenCVTests + +class digits_test(NewOpenCVTests): + + def load_digits(self, fn): + digits_img = self.get_sample(fn, 0) + digits = split2d(digits_img, (SZ, SZ)) + labels = np.repeat(np.arange(CLASS_N), len(digits)/CLASS_N) + return digits, labels + + def test_digits(self): + + digits, labels = self.load_digits(DIGITS_FN) + + # shuffle digits + rand = np.random.RandomState(321) + shuffle = rand.permutation(len(digits)) + digits, labels = digits[shuffle], labels[shuffle] + + digits2 = list(map(deskew, digits)) + samples = preprocess_hog(digits2) + + train_n = int(0.9*len(samples)) + digits_train, digits_test = np.split(digits2, [train_n]) + samples_train, samples_test = np.split(samples, [train_n]) + labels_train, labels_test = np.split(labels, [train_n]) + errors = list() + confusionMatrixes = list() + + model = KNearest(k=4) + model.train(samples_train, labels_train) + error, confusion = evaluate_model(model, digits_test, samples_test, labels_test) + errors.append(error) + confusionMatrixes.append(confusion) + + model = SVM(C=2.67, gamma=5.383) + model.train(samples_train, labels_train) + error, confusion = evaluate_model(model, digits_test, samples_test, labels_test) + errors.append(error) + confusionMatrixes.append(confusion) + + eps = 0.001 + normEps = len(samples_test) * 0.02 + + confusionKNN = [[45, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [ 0, 57, 0, 0, 0, 0, 0, 0, 0, 0], + [ 0, 0, 59, 1, 0, 0, 0, 0, 1, 0], + [ 0, 0, 0, 43, 0, 0, 0, 1, 0, 0], + [ 0, 0, 0, 0, 38, 0, 2, 0, 0, 0], + [ 0, 0, 0, 2, 0, 48, 0, 0, 1, 0], + [ 0, 1, 0, 0, 0, 0, 51, 0, 0, 0], + [ 0, 0, 1, 0, 0, 0, 0, 54, 0, 0], + [ 0, 0, 0, 0, 0, 1, 0, 0, 46, 0], + [ 1, 1, 0, 1, 1, 0, 0, 0, 2, 42]] + + confusionSVM = [[45, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [ 0, 57, 0, 0, 0, 0, 0, 0, 0, 0], + [ 0, 0, 59, 2, 0, 0, 0, 0, 0, 0], + [ 0, 0, 0, 43, 0, 0, 0, 1, 0, 0], + [ 0, 0, 0, 0, 40, 0, 0, 0, 0, 0], + [ 0, 0, 0, 1, 0, 50, 0, 0, 0, 0], + [ 0, 0, 0, 0, 1, 0, 51, 0, 0, 0], + [ 0, 0, 1, 0, 0, 0, 0, 54, 0, 0], + [ 0, 0, 0, 0, 0, 0, 0, 0, 47, 0], + [ 0, 1, 0, 1, 0, 0, 0, 0, 1, 45]] + + self.assertLess(cv2.norm(confusionMatrixes[0] - confusionKNN, cv2.NORM_L1), normEps) + self.assertLess(cv2.norm(confusionMatrixes[1] - confusionSVM, cv2.NORM_L1), normEps) + + self.assertLess(errors[0] - 0.034, eps) + self.assertLess(errors[1] - 0.018, eps) \ No newline at end of file diff --git a/modules/python/test/test_facedetect.py b/modules/python/test/test_facedetect.py new file mode 100644 index 0000000000..8d64fde10f --- /dev/null +++ b/modules/python/test/test_facedetect.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python + +''' +face detection using haar cascades +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +def detect(img, cascade): + rects = cascade.detectMultiScale(img, scaleFactor=1.3, minNeighbors=4, minSize=(30, 30), + flags=cv2.CASCADE_SCALE_IMAGE) + if len(rects) == 0: + return [] + rects[:,2:] += rects[:,:2] + return rects + +from tests_common import NewOpenCVTests, intersectionRate + +class facedetect_test(NewOpenCVTests): + + def test_facedetect(self): + import sys, getopt + + cascade_fn = self.repoPath + '/data/haarcascades/haarcascade_frontalface_alt.xml' + nested_fn = self.repoPath + '/data/haarcascades/haarcascade_eye.xml' + + cascade = cv2.CascadeClassifier(cascade_fn) + nested = cv2.CascadeClassifier(nested_fn) + + samples = ['samples/data/lena.jpg', 'cv/cascadeandhog/images/mona-lisa.png'] + + faces = [] + eyes = [] + + testFaces = [ + #lena + [[218, 200, 389, 371], + [ 244, 240, 294, 290], + [ 309, 246, 352, 289]], + + #lisa + [[167, 119, 307, 259], + [188, 153, 229, 194], + [236, 153, 277, 194]] + ] + + for sample in samples: + + img = self.get_sample( sample) + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + gray = cv2.GaussianBlur(gray, (5, 5), 5.1) + + rects = detect(gray, cascade) + faces.append(rects) + + if not nested.empty(): + for x1, y1, x2, y2 in rects: + roi = gray[y1:y2, x1:x2] + subrects = detect(roi.copy(), nested) + + for rect in subrects: + rect[0] += x1 + rect[2] += x1 + rect[1] += y1 + rect[3] += y1 + + eyes.append(subrects) + + faces_matches = 0 + eyes_matches = 0 + + eps = 0.8 + + for i in range(len(faces)): + for j in range(len(testFaces)): + if intersectionRate(faces[i][0], testFaces[j][0]) > eps: + faces_matches += 1 + #check eyes + if len(eyes[i]) == 2: + if intersectionRate(eyes[i][0], testFaces[j][1]) > eps and intersectionRate(eyes[i][1] , testFaces[j][2]) > eps: + eyes_matches += 1 + elif intersectionRate(eyes[i][1], testFaces[j][1]) > eps and intersectionRate(eyes[i][0], testFaces[j][2]) > eps: + eyes_matches += 1 + + self.assertEqual(faces_matches, 2) + self.assertEqual(eyes_matches, 2) \ No newline at end of file diff --git a/modules/python/test/test_feature_homography.py b/modules/python/test/test_feature_homography.py new file mode 100644 index 0000000000..280945e98b --- /dev/null +++ b/modules/python/test/test_feature_homography.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python + +''' +Feature homography +================== + +Example of using features2d framework for interactive video homography matching. +ORB features and FLANN matcher are used. The actual tracking is implemented by +PlaneTracker class in plane_tracker.py +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 +import sys +PY3 = sys.version_info[0] == 3 + +if PY3: + xrange = range + +# local modules +from tst_scene_render import TestSceneRender + +def intersectionRate(s1, s2): + + x1, y1, x2, y2 = s1 + s1 = np.array([[x1, y1], [x2,y1], [x2, y2], [x1, y2]]) + + area, intersection = cv2.intersectConvexConvex(s1, np.array(s2)) + return 2 * area / (cv2.contourArea(s1) + cv2.contourArea(np.array(s2))) + +from tests_common import NewOpenCVTests + +class feature_homography_test(NewOpenCVTests): + + render = None + tracker = None + framesCounter = 0 + frame = None + + def test_feature_homography(self): + + self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), + self.get_sample('samples/data/box.png'), noise = 0.5, speed = 0.5) + self.frame = self.render.getNextFrame() + self.tracker = PlaneTracker() + self.tracker.clear() + self.tracker.add_target(self.frame, self.render.getCurrentRect()) + + while self.framesCounter < 100: + self.framesCounter += 1 + tracked = self.tracker.track(self.frame) + if len(tracked) > 0: + tracked = tracked[0] + self.assertGreater(intersectionRate(self.render.getCurrentRect(), np.int32(tracked.quad)), 0.6) + else: + self.assertEqual(0, 1, 'Tracking error') + self.frame = self.render.getNextFrame() + + +# built-in modules +from collections import namedtuple + +FLANN_INDEX_KDTREE = 1 +FLANN_INDEX_LSH = 6 +flann_params= dict(algorithm = FLANN_INDEX_LSH, + table_number = 6, # 12 + key_size = 12, # 20 + multi_probe_level = 1) #2 + +MIN_MATCH_COUNT = 10 + +''' + image - image to track + rect - tracked rectangle (x1, y1, x2, y2) + keypoints - keypoints detected inside rect + descrs - their descriptors + data - some user-provided data +''' +PlanarTarget = namedtuple('PlaneTarget', 'image, rect, keypoints, descrs, data') + +''' + target - reference to PlanarTarget + p0 - matched points coords in target image + p1 - matched points coords in input frame + H - homography matrix from p0 to p1 + quad - target bounary quad in input frame +''' +TrackedTarget = namedtuple('TrackedTarget', 'target, p0, p1, H, quad') + +class PlaneTracker: + def __init__(self): + self.detector = cv2.ORB( nfeatures = 1000 ) + self.matcher = cv2.FlannBasedMatcher(flann_params, {}) # bug : need to pass empty dict (#1329) + self.targets = [] + self.frame_points = [] + + def add_target(self, image, rect, data=None): + '''Add a new tracking target.''' + x0, y0, x1, y1 = rect + raw_points, raw_descrs = self.detect_features(image) + points, descs = [], [] + for kp, desc in zip(raw_points, raw_descrs): + x, y = kp.pt + if x0 <= x <= x1 and y0 <= y <= y1: + points.append(kp) + descs.append(desc) + descs = np.uint8(descs) + self.matcher.add([descs]) + target = PlanarTarget(image = image, rect=rect, keypoints = points, descrs=descs, data=data) + self.targets.append(target) + + def clear(self): + '''Remove all targets''' + self.targets = [] + self.matcher.clear() + + def track(self, frame): + '''Returns a list of detected TrackedTarget objects''' + self.frame_points, frame_descrs = self.detect_features(frame) + if len(self.frame_points) < MIN_MATCH_COUNT: + return [] + matches = self.matcher.knnMatch(frame_descrs, k = 2) + matches = [m[0] for m in matches if len(m) == 2 and m[0].distance < m[1].distance * 0.75] + if len(matches) < MIN_MATCH_COUNT: + return [] + matches_by_id = [[] for _ in xrange(len(self.targets))] + for m in matches: + matches_by_id[m.imgIdx].append(m) + tracked = [] + for imgIdx, matches in enumerate(matches_by_id): + if len(matches) < MIN_MATCH_COUNT: + continue + target = self.targets[imgIdx] + p0 = [target.keypoints[m.trainIdx].pt for m in matches] + p1 = [self.frame_points[m.queryIdx].pt for m in matches] + p0, p1 = np.float32((p0, p1)) + H, status = cv2.findHomography(p0, p1, cv2.RANSAC, 3.0) + status = status.ravel() != 0 + if status.sum() < MIN_MATCH_COUNT: + continue + p0, p1 = p0[status], p1[status] + + x0, y0, x1, y1 = target.rect + quad = np.float32([[x0, y0], [x1, y0], [x1, y1], [x0, y1]]) + quad = cv2.perspectiveTransform(quad.reshape(1, -1, 2), H).reshape(-1, 2) + + track = TrackedTarget(target=target, p0=p0, p1=p1, H=H, quad=quad) + tracked.append(track) + tracked.sort(key = lambda t: len(t.p0), reverse=True) + return tracked + + def detect_features(self, frame): + '''detect_features(self, frame) -> keypoints, descrs''' + keypoints, descrs = self.detector.detectAndCompute(frame, None) + if descrs is None: # detectAndCompute returns descs=None if not keypoints found + descrs = [] + return keypoints, descrs \ No newline at end of file diff --git a/modules/python/test/test_fitline.py b/modules/python/test/test_fitline.py new file mode 100644 index 0000000000..05719b74f1 --- /dev/null +++ b/modules/python/test/test_fitline.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +''' +Robust line fitting. +================== + +Example of using cv2.fitLine function for fitting line +to points in presence of outliers. + +Switch through different M-estimator functions and see, +how well the robust functions fit the line even +in case of ~50% of outliers. + +''' + +# Python 2/3 compatibility +from __future__ import print_function +import sys +PY3 = sys.version_info[0] == 3 + +import numpy as np +import cv2 + +from tests_common import NewOpenCVTests + +w, h = 512, 256 + +def toint(p): + return tuple(map(int, p)) + +def sample_line(p1, p2, n, noise=0.0): + np.random.seed(10) + p1 = np.float32(p1) + t = np.random.rand(n,1) + return p1 + (p2-p1)*t + np.random.normal(size=(n, 2))*noise + +dist_func_names = ['CV_DIST_L2', 'CV_DIST_L1', 'CV_DIST_L12', 'CV_DIST_FAIR', 'CV_DIST_WELSCH', 'CV_DIST_HUBER'] + +class fitline_test(NewOpenCVTests): + + def test_fitline(self): + + noise = 5 + n = 200 + r = 5 / 100.0 + outn = int(n*r) + + p0, p1 = (90, 80), (w-90, h-80) + line_points = sample_line(p0, p1, n-outn, noise) + outliers = np.random.rand(outn, 2) * (w, h) + points = np.vstack([line_points, outliers]) + + lines = [] + + for name in dist_func_names: + func = getattr(cv2.cv, name) + vx, vy, cx, cy = cv2.fitLine(np.float32(points), func, 0, 0.01, 0.01) + line = [float(vx), float(vy), float(cx), float(cy)] + lines.append(line) + + eps = 0.05 + + refVec = (np.float32(p1) - p0) / cv2.norm(np.float32(p1) - p0) + + for i in range(len(lines)): + self.assertLessEqual(cv2.norm(refVec - lines[i][0:2], cv2.NORM_L2), eps) \ No newline at end of file diff --git a/modules/python/test/test_gaussian_mix.py b/modules/python/test/test_gaussian_mix.py new file mode 100644 index 0000000000..498cf3862f --- /dev/null +++ b/modules/python/test/test_gaussian_mix.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +# Python 2/3 compatibility +from __future__ import print_function +import sys +PY3 = sys.version_info[0] == 3 + +if PY3: + xrange = range + +import numpy as np +from numpy import random +import cv2 + +def make_gaussians(cluster_n, img_size): + points = [] + ref_distrs = [] + for i in xrange(cluster_n): + mean = (0.1 + 0.8*random.rand(2)) * img_size + a = (random.rand(2, 2)-0.5)*img_size*0.1 + cov = np.dot(a.T, a) + img_size*0.05*np.eye(2) + n = 100 + random.randint(900) + pts = random.multivariate_normal(mean, cov, n) + points.append( pts ) + ref_distrs.append( (mean, cov) ) + points = np.float32( np.vstack(points) ) + return points, ref_distrs + +from tests_common import NewOpenCVTests + +class gaussian_mix_test(NewOpenCVTests): + + def test_gaussian_mix(self): + + np.random.seed(10) + cluster_n = 5 + img_size = 512 + + points, ref_distrs = make_gaussians(cluster_n, img_size) + + em = cv2.EM(cluster_n,cv2.EM_COV_MAT_GENERIC) + em.train(points) + means = em.getMat("means") + covs = em.getMatVector("covs") # Known bug: https://github.com/Itseez/opencv/pull/4232 + found_distrs = zip(means, covs) + + matches_count = 0 + + meanEps = 0.05 + covEps = 0.1 + + for i in range(cluster_n): + for j in range(cluster_n): + if (cv2.norm(means[i] - ref_distrs[j][0], cv2.NORM_L2) / cv2.norm(ref_distrs[j][0], cv2.NORM_L2) < meanEps and + cv2.norm(covs[i] - ref_distrs[j][1], cv2.NORM_L2) / cv2.norm(ref_distrs[j][1], cv2.NORM_L2) < covEps): + matches_count += 1 + + self.assertEqual(matches_count, cluster_n) \ No newline at end of file diff --git a/modules/python/test/test_grabcut.py b/modules/python/test/test_grabcut.py new file mode 100644 index 0000000000..38211f7d89 --- /dev/null +++ b/modules/python/test/test_grabcut.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +''' +=============================================================================== +Interactive Image Segmentation using GrabCut algorithm. +=============================================================================== +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 +import sys + +from tests_common import NewOpenCVTests + +class grabcut_test(NewOpenCVTests): + + def verify(self, mask, exp): + + maxDiffRatio = 0.02 + expArea = np.count_nonzero(exp) + nonIntersectArea = np.count_nonzero(mask != exp) + curRatio = float(nonIntersectArea) / expArea + return curRatio < maxDiffRatio + + def scaleMask(self, mask): + + return np.where((mask==cv2.GC_FGD) + (mask==cv2.GC_PR_FGD),255,0).astype('uint8') + + def test_grabcut(self): + + img = self.get_sample('cv/shared/airplane.png') + mask_prob = self.get_sample("cv/grabcut/mask_probpy.png", 0) + exp_mask1 = self.get_sample("cv/grabcut/exp_mask1py.png", 0) + exp_mask2 = self.get_sample("cv/grabcut/exp_mask2py.png", 0) + + if img is None: + self.assertTrue(False, 'Missing test data') + + rect = (24, 126, 459, 168) + mask = np.zeros(img.shape[:2], dtype = np.uint8) + bgdModel = np.zeros((1,65),np.float64) + fgdModel = np.zeros((1,65),np.float64) + cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 0, cv2.GC_INIT_WITH_RECT) + cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 2, cv2.GC_EVAL) + + if mask_prob is None: + mask_prob = mask.copy() + cv2.imwrite(self.extraTestDataPath + '/cv/grabcut/mask_probpy.png', mask_prob) + if exp_mask1 is None: + exp_mask1 = self.scaleMask(mask) + cv2.imwrite(self.extraTestDataPath + '/cv/grabcut/exp_mask1py.png', exp_mask1) + + self.assertEqual(self.verify(self.scaleMask(mask), exp_mask1), True) + + mask = mask_prob + bgdModel = np.zeros((1,65),np.float64) + fgdModel = np.zeros((1,65),np.float64) + cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 0, cv2.GC_INIT_WITH_MASK) + cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 1, cv2.GC_EVAL) + + if exp_mask2 is None: + exp_mask2 = self.scaleMask(mask) + cv2.imwrite(self.extraTestDataPath + '/cv/grabcut/exp_mask2py.png', exp_mask2) + + self.assertEqual(self.verify(self.scaleMask(mask), exp_mask2), True) \ No newline at end of file diff --git a/modules/python/test/test_houghcircles.py b/modules/python/test/test_houghcircles.py new file mode 100644 index 0000000000..6cf7d74ebb --- /dev/null +++ b/modules/python/test/test_houghcircles.py @@ -0,0 +1,81 @@ +#!/usr/bin/python + +''' +This example illustrates how to use cv2.HoughCircles() function. +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import cv2 +import numpy as np +import sys +from numpy import pi, sin, cos + +from tests_common import NewOpenCVTests + +def circleApproximation(circle): + + nPoints = 30 + phi = 0 + dPhi = 2*pi / nPoints + contour = [] + for i in range(nPoints): + contour.append(([circle[0] + circle[2]*cos(i*dPhi), + circle[1] + circle[2]*sin(i*dPhi)])) + + return np.array(contour).astype(int) + +def convContoursIntersectiponRate(c1, c2): + + s1 = cv2.contourArea(c1) + s2 = cv2.contourArea(c2) + + s, _ = cv2.intersectConvexConvex(c1, c2) + + return 2*s/(s1+s2) + +class houghcircles_test(NewOpenCVTests): + + def test_houghcircles(self): + + fn = "samples/data/board.jpg" + + src = self.get_sample(fn, 1) + img = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY) + img = cv2.medianBlur(img, 5) + + circles = cv2.HoughCircles(img, cv2.cv.CV_HOUGH_GRADIENT, 1, 10, np.array([]), 100, 30, 1, 30)[0] + + testCircles = [[38, 181, 17.6], + [99.7, 166, 13.12], + [142.7, 160, 13.52], + [223.6, 110, 8.62], + [79.1, 206.7, 8.62], + [47.5, 351.6, 11.64], + [189.5, 354.4, 11.64], + [189.8, 298.9, 10.64], + [189.5, 252.4, 14.62], + [252.5, 393.4, 15.62], + [602.9, 467.5, 11.42], + [222, 210.4, 9.12], + [263.1, 216.7, 9.12], + [359.8, 222.6, 9.12], + [518.9, 120.9, 9.12], + [413.8, 113.4, 9.12], + [489, 127.2, 9.12], + [448.4, 121.3, 9.12], + [384.6, 128.9, 8.62]] + + matches_counter = 0 + + for i in range(len(testCircles)): + for j in range(len(circles)): + + tstCircle = circleApproximation(testCircles[i]) + circle = circleApproximation(circles[j]) + if convContoursIntersectiponRate(tstCircle, circle) > 0.6: + matches_counter += 1 + + self.assertGreater(float(matches_counter) / len(testCircles), .5) + self.assertLess(float(len(circles) - matches_counter) / len(circles), .75) \ No newline at end of file diff --git a/modules/python/test/test_houghlines.py b/modules/python/test/test_houghlines.py new file mode 100644 index 0000000000..081c84984d --- /dev/null +++ b/modules/python/test/test_houghlines.py @@ -0,0 +1,65 @@ +#!/usr/bin/python + +''' +This example illustrates how to use Hough Transform to find lines +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import cv2 +import numpy as np +import sys +import math + +from tests_common import NewOpenCVTests + +def linesDiff(line1, line2): + + norm1 = cv2.norm(line1 - line2, cv2.NORM_L2) + line3 = line1[2:4] + line1[0:2] + norm2 = cv2.norm(line3 - line2, cv2.NORM_L2) + + return min(norm1, norm2) + +class houghlines_test(NewOpenCVTests): + + def test_houghlines(self): + + fn = "/samples/data/pic1.png" + + src = self.get_sample(fn) + dst = cv2.Canny(src, 50, 200) + + lines = cv2.HoughLinesP(dst, 1, math.pi/180.0, 40, np.array([]), 50, 10)[0,:,:] + + eps = 5 + testLines = [ + #rect1 + [ 232, 25, 43, 25], + [ 43, 129, 232, 129], + [ 43, 129, 43, 25], + [232, 129, 232, 25], + #rect2 + [251, 86, 314, 183], + [252, 86, 323, 40], + [315, 183, 386, 137], + [324, 40, 386, 136], + #triangle + [245, 205, 377, 205], + [244, 206, 305, 278], + [306, 279, 377, 205], + #rect3 + [153, 177, 196, 177], + [153, 277, 153, 179], + [153, 277, 196, 277], + [196, 177, 196, 277]] + + matches_counter = 0 + + for i in range(len(testLines)): + for j in range(len(lines)): + if linesDiff(testLines[i], lines[j]) < eps: + matches_counter += 1 + + self.assertGreater(float(matches_counter) / len(testLines), .7) \ No newline at end of file diff --git a/modules/python/test/test_kmeans.py b/modules/python/test/test_kmeans.py new file mode 100644 index 0000000000..8cee11c675 --- /dev/null +++ b/modules/python/test/test_kmeans.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +''' +K-means clusterization test +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 +from numpy import random +import sys +PY3 = sys.version_info[0] == 3 +if PY3: + xrange = range + +from tests_common import NewOpenCVTests + +def make_gaussians(cluster_n, img_size): + points = [] + ref_distrs = [] + sizes = [] + for i in xrange(cluster_n): + mean = (0.1 + 0.8*random.rand(2)) * img_size + a = (random.rand(2, 2)-0.5)*img_size*0.1 + cov = np.dot(a.T, a) + img_size*0.05*np.eye(2) + n = 100 + random.randint(900) + pts = random.multivariate_normal(mean, cov, n) + points.append( pts ) + ref_distrs.append( (mean, cov) ) + sizes.append(n) + points = np.float32( np.vstack(points) ) + return points, ref_distrs, sizes + +def getMainLabelConfidence(labels, nLabels): + + n = len(labels) + labelsDict = dict.fromkeys(range(nLabels), 0) + labelsConfDict = dict.fromkeys(range(nLabels)) + + for i in range(n): + labelsDict[labels[i][0]] += 1 + + for i in range(nLabels): + labelsConfDict[i] = float(labelsDict[i]) / n + + return max(labelsConfDict.values()) + +class kmeans_test(NewOpenCVTests): + + def test_kmeans(self): + + np.random.seed(10) + + cluster_n = 5 + img_size = 512 + + points, _, clusterSizes = make_gaussians(cluster_n, img_size) + + term_crit = (cv2.TERM_CRITERIA_EPS, 30, 0.1) + ret, labels, centers = cv2.kmeans(points, cluster_n, term_crit, 10, 0) + + self.assertEqual(len(centers), cluster_n) + + offset = 0 + for i in range(cluster_n): + confidence = getMainLabelConfidence(labels[offset : (offset + clusterSizes[i])], cluster_n) + offset += clusterSizes[i] + self.assertGreater(confidence, 0.9) \ No newline at end of file diff --git a/modules/python/test/test_letter_recog.py b/modules/python/test/test_letter_recog.py new file mode 100644 index 0000000000..05f879978e --- /dev/null +++ b/modules/python/test/test_letter_recog.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python + +''' +The sample demonstrates how to train Random Trees classifier +(or Boosting classifier, or MLP, or Knearest, or Support Vector Machines) using the provided dataset. + +We use the sample database letter-recognition.data +from UCI Repository, here is the link: + +Newman, D.J. & Hettich, S. & Blake, C.L. & Merz, C.J. (1998). +UCI Repository of machine learning databases +[http://www.ics.uci.edu/~mlearn/MLRepository.html]. +Irvine, CA: University of California, Department of Information and Computer Science. + +The dataset consists of 20000 feature vectors along with the +responses - capital latin letters A..Z. +The first 10000 samples are used for training +and the remaining 10000 - to test the classifier. +====================================================== + Models: RTrees, KNearest, Boost, SVM, MLP +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +def load_base(fn): + a = np.loadtxt(fn, np.float32, delimiter=',', converters={ 0 : lambda ch : ord(ch)-ord('A') }) + samples, responses = a[:,1:], a[:,0] + return samples, responses + +class LetterStatModel(object): + class_n = 26 + train_ratio = 0.5 + + def load(self, fn): + self.model.load(fn) + def save(self, fn): + self.model.save(fn) + + def unroll_samples(self, samples): + sample_n, var_n = samples.shape + new_samples = np.zeros((sample_n * self.class_n, var_n+1), np.float32) + new_samples[:,:-1] = np.repeat(samples, self.class_n, axis=0) + new_samples[:,-1] = np.tile(np.arange(self.class_n), sample_n) + return new_samples + + def unroll_responses(self, responses): + sample_n = len(responses) + new_responses = np.zeros(sample_n*self.class_n, np.int32) + resp_idx = np.int32( responses + np.arange(sample_n)*self.class_n ) + new_responses[resp_idx] = 1 + return new_responses + +class RTrees(LetterStatModel): + def __init__(self): + self.model = cv2.RTrees() + + def train(self, samples, responses): + sample_n, var_n = samples.shape + params = dict(max_depth=20 ) + self.model.train(samples, cv2.CV_ROW_SAMPLE, responses.astype(int), params = params) + + def predict(self, samples): + return np.float32( [self.model.predict(s) for s in samples] ) + + +class KNearest(LetterStatModel): + def __init__(self): + self.model = cv2.KNearest() + + def train(self, samples, responses): + self.model.train(samples, responses) + + def predict(self, samples): + retval, results, neigh_resp, dists = self.model.find_nearest(samples, k = 10) + return results.ravel() + + +class Boost(LetterStatModel): + def __init__(self): + self.model = cv2.Boost() + + def train(self, samples, responses): + sample_n, var_n = samples.shape + new_samples = self.unroll_samples(samples) + new_responses = self.unroll_responses(responses) + var_types = np.array([cv2.CV_VAR_NUMERICAL] * var_n + [cv2.CV_VAR_CATEGORICAL, cv2.CV_VAR_CATEGORICAL], np.uint8) + params = dict(max_depth=10, weak_count=15) + self.model.train(new_samples, cv2.CV_ROW_SAMPLE, new_responses.astype(int), varType = var_types, params=params) + + def predict(self, samples): + new_samples = self.unroll_samples(samples) + pred = np.array( [self.model.predict(s) for s in new_samples] ) + pred = pred.reshape(-1, self.class_n).argmax(1) + return pred + + +class SVM(LetterStatModel): + def __init__(self): + self.model = cv2.SVM() + + def train(self, samples, responses): + params = dict( kernel_type = cv2.SVM_RBF, + svm_type = cv2.SVM_C_SVC, + C = 1, + gamma = .1 ) + self.model.train(samples, responses.astype(int), params = params) + + def predict(self, samples): + return self.model.predict_all(samples).ravel() + + +class MLP(LetterStatModel): + def __init__(self): + self.model = cv2.ANN_MLP() + + def train(self, samples, responses): + sample_n, var_n = samples.shape + new_responses = self.unroll_responses(responses).reshape(-1, self.class_n) + layer_sizes = np.int32([var_n, 100, 100, self.class_n]) + + self.model.create(layer_sizes, cv2.ANN_MLP_SIGMOID_SYM, 2, 1) + params = dict( train_method = cv2.ANN_MLP_TRAIN_PARAMS_BACKPROP, + bp_moment_scale = 0.0, + bp_dw_scale = 0.001, + term_crit = (cv2.TERM_CRITERIA_COUNT, 20, 0.01) ) + self.model.train(samples, np.float32(new_responses), None, params = params) + + def predict(self, samples): + ret, resp = self.model.predict(samples) + return resp.argmax(-1) + +from tests_common import NewOpenCVTests + +class letter_recog_test(NewOpenCVTests): + + def test_letter_recog(self): + + eps = 0.01 + + models = [RTrees, KNearest, Boost, SVM, MLP] + models = dict( [(cls.__name__.lower(), cls) for cls in models] ) + testErrors = {RTrees: (98.930000, 92.390000), KNearest: (94.960000, 92.010000), + Boost: (85.970000, 74.920000), SVM: (99.780000, 95.680000), MLP: (90.060000, 87.410000)} + + for model in models: + Model = models[model] + classifier = Model() + + samples, responses = load_base(self.repoPath + '/samples/data/letter-recognition.data') + train_n = int(len(samples)*classifier.train_ratio) + + classifier.train(samples[:train_n], responses[:train_n]) + train_rate = np.mean(classifier.predict(samples[:train_n]) == responses[:train_n].astype(int)) + test_rate = np.mean(classifier.predict(samples[train_n:]) == responses[train_n:].astype(int)) + + self.assertLess(train_rate - testErrors[Model][0], eps) + self.assertLess(test_rate - testErrors[Model][1], eps) \ No newline at end of file diff --git a/modules/python/test/test_lk_homography.py b/modules/python/test/test_lk_homography.py new file mode 100644 index 0000000000..8e526d0de7 --- /dev/null +++ b/modules/python/test/test_lk_homography.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python + +''' +Lucas-Kanade homography tracker test +=============================== +Uses goodFeaturesToTrack for track initialization and back-tracking for match verification +between frames. Finds homography between reference and current views. +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +#local modules +from tst_scene_render import TestSceneRender +from tests_common import NewOpenCVTests, isPointInRect + +lk_params = dict( winSize = (19, 19), + maxLevel = 2, + criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)) + +feature_params = dict( maxCorners = 1000, + qualityLevel = 0.01, + minDistance = 8, + blockSize = 19 ) + +def checkedTrace(img0, img1, p0, back_threshold = 1.0): + p1, st, err = cv2.calcOpticalFlowPyrLK(img0, img1, p0, None, **lk_params) + p0r, st, err = cv2.calcOpticalFlowPyrLK(img1, img0, p1, None, **lk_params) + d = abs(p0-p0r).reshape(-1, 2).max(-1) + status = d < back_threshold + return p1, status + +class lk_homography_test(NewOpenCVTests): + + render = None + framesCounter = 0 + frame = frame0 = None + p0 = None + p1 = None + gray0 = gray1 = None + numFeaturesInRectOnStart = 0 + + def test_lk_homography(self): + self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), + self.get_sample('samples/data/box.png'), noise = 0.1, speed = 1.0) + + frame = self.render.getNextFrame() + frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + self.frame0 = frame.copy() + self.p0 = cv2.goodFeaturesToTrack(frame_gray, **feature_params) + + isForegroundHomographyFound = False + + if self.p0 is not None: + self.p1 = self.p0 + self.gray0 = frame_gray + self.gray1 = frame_gray + currRect = self.render.getCurrentRect() + for (x,y) in self.p0[:,0]: + if isPointInRect((x,y), currRect): + self.numFeaturesInRectOnStart += 1 + + while self.framesCounter < 200: + self.framesCounter += 1 + frame = self.render.getNextFrame() + frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + if self.p0 is not None: + p2, trace_status = checkedTrace(self.gray1, frame_gray, self.p1) + + self.p1 = p2[trace_status].copy() + self.p0 = self.p0[trace_status].copy() + self.gray1 = frame_gray + + if len(self.p0) < 4: + self.p0 = None + continue + H, status = cv2.findHomography(self.p0, self.p1, cv2.RANSAC, 5.0) + + goodPointsInRect = 0 + goodPointsOutsideRect = 0 + for (x0, y0), (x1, y1), good in zip(self.p0[:,0], self.p1[:,0], status[:,0]): + if good: + if isPointInRect((x1,y1), self.render.getCurrentRect()): + goodPointsInRect += 1 + else: goodPointsOutsideRect += 1 + + if goodPointsOutsideRect < goodPointsInRect: + isForegroundHomographyFound = True + self.assertGreater(float(goodPointsInRect) / (self.numFeaturesInRectOnStart + 1), 0.6) + else: + p = cv2.goodFeaturesToTrack(frame_gray, **feature_params) + + self.assertEqual(isForegroundHomographyFound, True) \ No newline at end of file diff --git a/modules/python/test/test_lk_track.py b/modules/python/test/test_lk_track.py new file mode 100644 index 0000000000..ccc67a5128 --- /dev/null +++ b/modules/python/test/test_lk_track.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python + +''' +Lucas-Kanade tracker +==================== + +Lucas-Kanade sparse optical flow demo. Uses goodFeaturesToTrack +for track initialization and back-tracking for match verification +between frames. +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +#local modules +from tst_scene_render import TestSceneRender +from tests_common import NewOpenCVTests, intersectionRate, isPointInRect + +lk_params = dict( winSize = (15, 15), + maxLevel = 2, + criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)) + +feature_params = dict( maxCorners = 500, + qualityLevel = 0.3, + minDistance = 7, + blockSize = 7 ) + +def getRectFromPoints(points): + + distances = [] + for point in points: + distances.append(cv2.norm(point, cv2.NORM_L2)) + + x0, y0 = points[np.argmin(distances)] + x1, y1 = points[np.argmax(distances)] + + return np.array([x0, y0, x1, y1]) + + +class lk_track_test(NewOpenCVTests): + + track_len = 10 + detect_interval = 5 + tracks = [] + frame_idx = 0 + render = None + + def test_lk_track(self): + + self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), self.get_sample('samples/data/box.png')) + self.runTracker() + + def runTracker(self): + foregroundPointsNum = 0 + + while True: + frame = self.render.getNextFrame() + frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + + if len(self.tracks) > 0: + img0, img1 = self.prev_gray, frame_gray + p0 = np.float32([tr[-1][0] for tr in self.tracks]).reshape(-1, 1, 2) + p1, st, err = cv2.calcOpticalFlowPyrLK(img0, img1, p0, None, **lk_params) + p0r, st, err = cv2.calcOpticalFlowPyrLK(img1, img0, p1, None, **lk_params) + d = abs(p0-p0r).reshape(-1, 2).max(-1) + good = d < 1 + new_tracks = [] + for tr, (x, y), good_flag in zip(self.tracks, p1.reshape(-1, 2), good): + if not good_flag: + continue + tr.append([(x, y), self.frame_idx]) + if len(tr) > self.track_len: + del tr[0] + new_tracks.append(tr) + self.tracks = new_tracks + + if self.frame_idx % self.detect_interval == 0: + goodTracksCount = 0 + for tr in self.tracks: + oldRect = self.render.getRectInTime(self.render.timeStep * tr[0][1]) + newRect = self.render.getRectInTime(self.render.timeStep * tr[-1][1]) + if isPointInRect(tr[0][0], oldRect) and isPointInRect(tr[-1][0], newRect): + goodTracksCount += 1 + + if self.frame_idx == self.detect_interval: + foregroundPointsNum = goodTracksCount + + fgIndex = float(foregroundPointsNum) / (foregroundPointsNum + 1) + fgRate = float(goodTracksCount) / (len(self.tracks) + 1) + + if self.frame_idx > 0: + self.assertGreater(fgIndex, 0.9) + self.assertGreater(fgRate, 0.2) + + mask = np.zeros_like(frame_gray) + mask[:] = 255 + for x, y in [np.int32(tr[-1][0]) for tr in self.tracks]: + cv2.circle(mask, (x, y), 5, 0, -1) + p = cv2.goodFeaturesToTrack(frame_gray, mask = mask, **feature_params) + if p is not None: + for x, y in np.float32(p).reshape(-1, 2): + self.tracks.append([[(x, y), self.frame_idx]]) + + self.frame_idx += 1 + self.prev_gray = frame_gray + + if self.frame_idx > 300: + break \ No newline at end of file diff --git a/modules/python/test/test_morphology.py b/modules/python/test/test_morphology.py new file mode 100644 index 0000000000..309c80cfd2 --- /dev/null +++ b/modules/python/test/test_morphology.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +''' +Morphology operations. +''' + +# Python 2/3 compatibility +from __future__ import print_function +import sys +PY3 = sys.version_info[0] == 3 + +import numpy as np +import cv2 + +from tests_common import NewOpenCVTests + +class morphology_test(NewOpenCVTests): + + def test_morphology(self): + + fn = 'samples/data/rubberwhale1.png' + img = self.get_sample(fn) + + modes = ['erode/dilate', 'open/close', 'blackhat/tophat', 'gradient'] + str_modes = ['ellipse', 'rect', 'cross'] + + referenceHashes = { modes[0]: '071a526425b79e45b4d0d71ef51b0562', modes[1] : '071a526425b79e45b4d0d71ef51b0562', + modes[2] : '427e89f581b7df1b60a831b1ed4c8618', modes[3] : '0dd8ad251088a63d0dd022bcdc57361c'} + + def update(cur_mode): + cur_str_mode = str_modes[0] + sz = 10 + iters = 1 + opers = cur_mode.split('/') + if len(opers) > 1: + sz = sz - 10 + op = opers[sz > 0] + sz = abs(sz) + else: + op = opers[0] + sz = sz*2+1 + + str_name = 'MORPH_' + cur_str_mode.upper() + oper_name = 'MORPH_' + op.upper() + + st = cv2.getStructuringElement(getattr(cv2, str_name), (sz, sz)) + return cv2.morphologyEx(img, getattr(cv2, oper_name), st, iterations=iters) + + for mode in modes: + res = update(mode) + self.assertEqual(self.hashimg(res), referenceHashes[mode]) \ No newline at end of file diff --git a/modules/python/test/test_mser.py b/modules/python/test/test_mser.py new file mode 100644 index 0000000000..dfdb30a8ed --- /dev/null +++ b/modules/python/test/test_mser.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +''' +MSER detector test +''' +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +from tests_common import NewOpenCVTests + +class mser_test(NewOpenCVTests): + def test_mser(self): + + img = self.get_sample('cv/mser/puzzle.png', 0) + smallImg = [ + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255] + ] + thresharr = [ 0, 70, 120, 180, 255 ] + kDelta = 5 + np.random.seed(10) + + for i in range(100): + + use_big_image = int(np.random.rand(1,1)*7) != 0 + invert = int(np.random.rand(1,1)*2) != 0 + binarize = int(np.random.rand(1,1)*5) != 0 if use_big_image else False + blur = int(np.random.rand(1,1)*2) != 0 + thresh = thresharr[int(np.random.rand(1,1)*5)] + src0 = img if use_big_image else np.array(smallImg).astype('uint8') + src = src0.copy() + + kMinArea = 256 if use_big_image else 10 + kMaxArea = int(src.shape[0]*src.shape[1]/4) + + mserExtractor = cv2.MSER(kDelta, kMinArea, kMaxArea) + if invert: + cv2.bitwise_not(src, src) + if binarize: + _, src = cv2.threshold(src, thresh, 255, cv2.THRESH_BINARY) + if blur: + src = cv2.GaussianBlur(src, (5, 5), 1.5, 1.5) + minRegs = 7 if use_big_image else 2 + maxRegs = 1000 if use_big_image else 15 + if binarize and (thresh == 0 or thresh == 255): + minRegs = maxRegs = 0 + msers = mserExtractor.detect(src) + nmsers = len(msers) + self.assertLessEqual(minRegs, nmsers) + self.assertGreaterEqual(maxRegs, nmsers) \ No newline at end of file diff --git a/modules/python/test/test_peopledetect.py b/modules/python/test/test_peopledetect.py new file mode 100644 index 0000000000..fb0a9e9cae --- /dev/null +++ b/modules/python/test/test_peopledetect.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +''' +example to detect upright people in images using HOG features +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + + +def inside(r, q): + rx, ry, rw, rh = r + qx, qy, qw, qh = q + return rx > qx and ry > qy and rx + rw < qx + qw and ry + rh < qy + qh + +from tests_common import NewOpenCVTests, intersectionRate + +class peopledetect_test(NewOpenCVTests): + def test_peopledetect(self): + + hog = cv2.HOGDescriptor() + hog.setSVMDetector( cv2.HOGDescriptor_getDefaultPeopleDetector() ) + + dirPath = 'samples/data/' + samples = ['basketball1.png', 'basketball2.png'] + + testPeople = [ + [[23, 76, 164, 477], [440, 22, 637, 478]], + [[23, 76, 164, 477], [440, 22, 637, 478]] + ] + + eps = 0.5 + + for sample in samples: + + img = self.get_sample(dirPath + sample, 0) + + found, w = hog.detectMultiScale(img, winStride=(8,8), padding=(32,32), scale=1.05) + found_filtered = [] + for ri, r in enumerate(found): + for qi, q in enumerate(found): + if ri != qi and inside(r, q): + break + else: + found_filtered.append(r) + + matches = 0 + + for i in range(len(found_filtered)): + for j in range(len(testPeople)): + + found_rect = (found_filtered[i][0], found_filtered[i][1], + found_filtered[i][0] + found_filtered[i][2], + found_filtered[i][1] + found_filtered[i][3]) + + if intersectionRate(found_rect, testPeople[j][0]) > eps or intersectionRate(found_rect, testPeople[j][1]) > eps: + matches += 1 + + self.assertGreater(matches, 0) \ No newline at end of file diff --git a/modules/python/test/test_squares.py b/modules/python/test/test_squares.py new file mode 100644 index 0000000000..3b3fbb6d9d --- /dev/null +++ b/modules/python/test/test_squares.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python + +''' +Simple "Square Detector" program. + +Loads several images sequentially and tries to find squares in each image. +''' + +# Python 2/3 compatibility +import sys +PY3 = sys.version_info[0] == 3 + +if PY3: + xrange = range + +import numpy as np +import cv2 + + +def angle_cos(p0, p1, p2): + d1, d2 = (p0-p1).astype('float'), (p2-p1).astype('float') + return abs( np.dot(d1, d2) / np.sqrt( np.dot(d1, d1)*np.dot(d2, d2) ) ) + +def find_squares(img): + img = cv2.GaussianBlur(img, (5, 5), 0) + squares = [] + for gray in cv2.split(img): + for thrs in xrange(0, 255, 26): + if thrs == 0: + bin = cv2.Canny(gray, 0, 50, apertureSize=5) + bin = cv2.dilate(bin, None) + else: + retval, bin = cv2.threshold(gray, thrs, 255, cv2.THRESH_BINARY) + contours, hierarchy = cv2.findContours(bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) + for cnt in contours: + cnt_len = cv2.arcLength(cnt, True) + cnt = cv2.approxPolyDP(cnt, 0.02*cnt_len, True) + if len(cnt) == 4 and cv2.contourArea(cnt) > 1000 and cv2.isContourConvex(cnt): + cnt = cnt.reshape(-1, 2) + max_cos = np.max([angle_cos( cnt[i], cnt[(i+1) % 4], cnt[(i+2) % 4] ) for i in xrange(4)]) + if max_cos < 0.1 and filterSquares(squares, cnt): + squares.append(cnt) + + return squares + +def intersectionRate(s1, s2): + area, intersection = cv2.intersectConvexConvex(np.array(s1), np.array(s2)) + return 2 * area / (cv2.contourArea(np.array(s1)) + cv2.contourArea(np.array(s2))) + +def filterSquares(squares, square): + + for i in range(len(squares)): + if intersectionRate(squares[i], square) > 0.95: + return False + + return True + +from tests_common import NewOpenCVTests + +class squares_test(NewOpenCVTests): + + def test_squares(self): + + img = self.get_sample('samples/data/pic1.png') + squares = find_squares(img) + + testSquares = [ + [[43, 25], + [43, 129], + [232, 129], + [232, 25]], + + [[252, 87], + [324, 40], + [387, 137], + [315, 184]], + + [[154, 178], + [196, 180], + [198, 278], + [154, 278]], + + [[0, 0], + [400, 0], + [400, 300], + [0, 300]] + ] + + matches_counter = 0 + for i in range(len(squares)): + for j in range(len(testSquares)): + if intersectionRate(squares[i], testSquares[j]) > 0.9: + matches_counter += 1 + + self.assertGreater(matches_counter / len(testSquares), 0.9) + self.assertLess( (len(squares) - matches_counter) / len(squares), 0.2) \ No newline at end of file diff --git a/modules/python/test/test_texture_flow.py b/modules/python/test/test_texture_flow.py new file mode 100644 index 0000000000..7dc3b07040 --- /dev/null +++ b/modules/python/test/test_texture_flow.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +''' +Texture flow direction estimation. + +Sample shows how cv2.cornerEigenValsAndVecs function can be used +to estimate image texture flow direction. +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 +import sys + +from tests_common import NewOpenCVTests + + +class texture_flow_test(NewOpenCVTests): + + def test_texture_flow(self): + + img = self.get_sample('samples/data/pic6.png') + + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + h, w = img.shape[:2] + + eigen = cv2.cornerEigenValsAndVecs(gray, 15, 3) + eigen = eigen.reshape(h, w, 3, 2) # [[e1, e2], v1, v2] + flow = eigen[:,:,2] + + vis = img.copy() + vis[:] = (192 + np.uint32(vis)) / 2 + d = 80 + points = np.dstack( np.mgrid[d/2:w:d, d/2:h:d] ).reshape(-1, 2) + + textureVectors = [] + + for x, y in np.int32(points): + textureVectors.append(np.int32(flow[y, x]*d)) + + eps = 0.05 + + testTextureVectors = [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], + [-38, 70], [-79, 3], [0, 0], [0, 0], [-39, 69], [-79, -1], + [0, 0], [0, 0], [0, -79], [17, -78], [-48, -63], [65, -46], + [-69, -39], [-48, -63], [-45, 66]] + + for i in range(len(textureVectors)): + self.assertLessEqual(cv2.norm(textureVectors[i] - testTextureVectors[i], cv2.NORM_L2), eps) \ No newline at end of file diff --git a/modules/python/test/test_watershed.py b/modules/python/test/test_watershed.py new file mode 100644 index 0000000000..0a1d222f41 --- /dev/null +++ b/modules/python/test/test_watershed.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +''' +Watershed segmentation test +''' + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +import cv2 + +from tests_common import NewOpenCVTests + +class watershed_test(NewOpenCVTests): + def test_watershed(self): + + img = self.get_sample('cv/inpaint/orig.png') + markers = self.get_sample('cv/watershed/wshed_exp.png', 0) + refSegments = self.get_sample('cv/watershed/wshed_segments.png') + + if img is None or markers is None: + self.assertEqual(0, 1, 'Missing test data') + + colors = np.int32( list(np.ndindex(3, 3, 3)) ) * 122 + cv2.watershed(img, np.int32(markers)) + segments = colors[np.maximum(markers, 0)] + + if refSegments is None: + refSegments = segments.copy() + cv2.imwrite(self.extraTestDataPath + '/cv/watershed/wshed_segments.png', refSegments) + + self.assertLess(cv2.norm(segments - refSegments, cv2.NORM_L1) / 255.0, 50) \ No newline at end of file diff --git a/modules/python/test/tests_common.py b/modules/python/test/tests_common.py new file mode 100644 index 0000000000..3a636b255c --- /dev/null +++ b/modules/python/test/tests_common.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +from __future__ import print_function + +import unittest +import sys +import hashlib +import os +import numpy as np +import cv2 + +# Python 3 moved urlopen to urllib.requests +try: + from urllib.request import urlopen +except ImportError: + from urllib import urlopen + +class NewOpenCVTests(unittest.TestCase): + + # path to local repository folder containing 'samples' folder + repoPath = None + extraTestDataPath = None + # github repository url + repoUrl = 'https://raw.github.com/Itseez/opencv/master' + + def get_sample(self, filename, iscolor = cv2.IMREAD_COLOR): + if not filename in self.image_cache: + filedata = None + if NewOpenCVTests.repoPath is not None: + candidate = NewOpenCVTests.repoPath + '/' + filename + if os.path.isfile(candidate): + with open(candidate, 'rb') as f: + filedata = f.read() + if NewOpenCVTests.extraTestDataPath is not None: + candidate = NewOpenCVTests.extraTestDataPath + '/' + filename + if os.path.isfile(candidate): + with open(candidate, 'rb') as f: + filedata = f.read() + if filedata is None: + return None#filedata = urlopen(NewOpenCVTests.repoUrl + '/' + filename).read() + self.image_cache[filename] = cv2.imdecode(np.fromstring(filedata, dtype=np.uint8), iscolor) + return self.image_cache[filename] + + def setUp(self): + self.image_cache = {} + + def hashimg(self, im): + """ Compute a hash for an image, useful for image comparisons """ + return hashlib.md5(im.tostring()).hexdigest() + + if sys.version_info[:2] == (2, 6): + def assertLess(self, a, b, msg=None): + if not a < b: + self.fail('%s not less than %s' % (repr(a), repr(b))) + + def assertLessEqual(self, a, b, msg=None): + if not a <= b: + self.fail('%s not less than or equal to %s' % (repr(a), repr(b))) + + def assertGreater(self, a, b, msg=None): + if not a > b: + self.fail('%s not greater than %s' % (repr(a), repr(b))) + +def intersectionRate(s1, s2): + + x1, y1, x2, y2 = s1 + s1 = np.array([[x1, y1], [x2,y1], [x2, y2], [x1, y2]]) + + x1, y1, x2, y2 = s2 + s2 = np.array([[x1, y1], [x2,y1], [x2, y2], [x1, y2]]) + + area, intersection = cv2.intersectConvexConvex(s1, s2) + return 2 * area / (cv2.contourArea(s1) + cv2.contourArea(s2)) + +def isPointInRect(p, rect): + if rect[0] <= p[0] and rect[1] <=p[1] and p[0] <= rect[2] and p[1] <= rect[3]: + return True + else: + return False \ No newline at end of file diff --git a/modules/python/test/tst_scene_render.py b/modules/python/test/tst_scene_render.py new file mode 100644 index 0000000000..49cde80d25 --- /dev/null +++ b/modules/python/test/tst_scene_render.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python + + +# Python 2/3 compatibility +from __future__ import print_function + +import numpy as np +from numpy import pi, sin, cos + +import cv2 + +defaultSize = 512 + +class TestSceneRender(): + + def __init__(self, bgImg = None, fgImg = None, deformation = False, noise = 0.0, speed = 0.25, **params): + self.time = 0.0 + self.timeStep = 1.0 / 30.0 + self.foreground = fgImg + self.deformation = deformation + self.noise = noise + self.speed = speed + + if bgImg is not None: + self.sceneBg = bgImg.copy() + else: + self.sceneBg = np.zeros(defaultSize, defaultSize, np.uint8) + + self.w = self.sceneBg.shape[0] + self.h = self.sceneBg.shape[1] + + if fgImg is not None: + self.foreground = fgImg.copy() + self.center = self.currentCenter = (int(self.w/2 - fgImg.shape[0]/2), int(self.h/2 - fgImg.shape[1]/2)) + + self.xAmpl = self.sceneBg.shape[0] - (self.center[0] + fgImg.shape[0]) + self.yAmpl = self.sceneBg.shape[1] - (self.center[1] + fgImg.shape[1]) + + self.initialRect = np.array([ (self.h/2, self.w/2), (self.h/2, self.w/2 + self.w/10), + (self.h/2 + self.h/10, self.w/2 + self.w/10), (self.h/2 + self.h/10, self.w/2)]).astype(int) + self.currentRect = self.initialRect + np.random.seed(10) + + def getXOffset(self, time): + return int(self.xAmpl*cos(time*self.speed)) + + + def getYOffset(self, time): + return int(self.yAmpl*sin(time*self.speed)) + + def setInitialRect(self, rect): + self.initialRect = rect + + def getRectInTime(self, time): + + if self.foreground is not None: + tmp = np.array(self.center) + np.array((self.getXOffset(time), self.getYOffset(time))) + x0, y0 = tmp + x1, y1 = tmp + self.foreground.shape[0:2] + return np.array([y0, x0, y1, x1]) + else: + x0, y0 = self.initialRect[0] + np.array((self.getXOffset(time), self.getYOffset(time))) + x1, y1 = self.initialRect[2] + np.array((self.getXOffset(time), self.getYOffset(time))) + return np.array([y0, x0, y1, x1]) + + def getCurrentRect(self): + + if self.foreground is not None: + + x0 = self.currentCenter[0] + y0 = self.currentCenter[1] + x1 = self.currentCenter[0] + self.foreground.shape[0] + y1 = self.currentCenter[1] + self.foreground.shape[1] + return np.array([y0, x0, y1, x1]) + else: + x0, y0 = self.currentRect[0] + x1, y1 = self.currentRect[2] + return np.array([x0, y0, x1, y1]) + + def getNextFrame(self): + img = self.sceneBg.copy() + + if self.foreground is not None: + self.currentCenter = (self.center[0] + self.getXOffset(self.time), self.center[1] + self.getYOffset(self.time)) + img[self.currentCenter[0]:self.currentCenter[0]+self.foreground.shape[0], + self.currentCenter[1]:self.currentCenter[1]+self.foreground.shape[1]] = self.foreground + else: + self.currentRect = self.initialRect + np.int( 30*cos(self.time) + 50*sin(self.time/3)) + if self.deformation: + self.currentRect[1:3] += int(self.h/20*cos(self.time)) + cv2.fillConvexPoly(img, self.currentRect, (0, 0, 255)) + + self.time += self.timeStep + + if self.noise: + noise = np.zeros(self.sceneBg.shape, np.int8) + cv2.randn(noise, np.zeros(3), np.ones(3)*255*self.noise) + img = cv2.add(img, noise, dtype=cv2.CV_8UC3) + return img + + def resetTime(self): + self.time = 0.0 + + +if __name__ == '__main__': + + backGr = cv2.imread('../../../samples/data/lena.jpg') + + render = TestSceneRender(backGr, noise = 0.5) + + while True: + + img = render.getNextFrame() + cv2.imshow('img', img) + + ch = 0xFF & cv2.waitKey(3) + if ch == 27: + break + cv2.destroyAllWindows() \ No newline at end of file From aaa30dc5b6c65868a2c50fb7fdbdfc2836d694c4 Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Thu, 25 Feb 2016 17:06:20 +0300 Subject: [PATCH 073/105] Make some tests less strict due to improvement of related algorithms in master branch --- modules/python/test/test_feature_homography.py | 2 +- modules/python/test/test_mser.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/python/test/test_feature_homography.py b/modules/python/test/test_feature_homography.py index 280945e98b..f417033883 100644 --- a/modules/python/test/test_feature_homography.py +++ b/modules/python/test/test_feature_homography.py @@ -43,7 +43,7 @@ class feature_homography_test(NewOpenCVTests): def test_feature_homography(self): self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), - self.get_sample('samples/data/box.png'), noise = 0.5, speed = 0.5) + self.get_sample('samples/data/box.png'), noise = 0.4, speed = 0.5) self.frame = self.render.getNextFrame() self.tracker = PlaneTracker() self.tracker.clear() diff --git a/modules/python/test/test_mser.py b/modules/python/test/test_mser.py index dfdb30a8ed..47c7db196f 100644 --- a/modules/python/test/test_mser.py +++ b/modules/python/test/test_mser.py @@ -40,7 +40,7 @@ class mser_test(NewOpenCVTests): use_big_image = int(np.random.rand(1,1)*7) != 0 invert = int(np.random.rand(1,1)*2) != 0 binarize = int(np.random.rand(1,1)*5) != 0 if use_big_image else False - blur = int(np.random.rand(1,1)*2) != 0 + blur = True #int(np.random.rand(1,1)*2) != 0 #binarized images are processed incorrectly thresh = thresharr[int(np.random.rand(1,1)*5)] src0 = img if use_big_image else np.array(smallImg).astype('uint8') src = src0.copy() From 25b4d8a1b5a5a1f90f5006999766fdbd12250ce9 Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Thu, 25 Feb 2016 17:25:24 +0300 Subject: [PATCH 074/105] Added images necessary for tests --- modules/python/test/test_calibration.py | 4 ++-- modules/python/test/test_camshift.py | 2 +- modules/python/test/test_dft.py | 2 +- modules/python/test/test_digits.py | 2 +- modules/python/test/test_facedetect.py | 2 +- modules/python/test/test_feature_homography.py | 4 ++-- modules/python/test/test_houghcircles.py | 2 +- modules/python/test/test_houghlines.py | 2 +- modules/python/test/test_letter_recog.py | 2 +- modules/python/test/test_lk_homography.py | 4 ++-- modules/python/test/test_lk_track.py | 2 +- modules/python/test/test_morphology.py | 2 +- modules/python/test/test_peopledetect.py | 2 +- modules/python/test/test_squares.py | 2 +- modules/python/test/test_texture_flow.py | 2 +- modules/python/test/tst_scene_render.py | 17 ----------------- samples/python2/data/graf1.png | Bin 0 -> 951440 bytes samples/python2/data/pca_test1.jpg | Bin 0 -> 33227 bytes 18 files changed, 18 insertions(+), 35 deletions(-) create mode 100644 samples/python2/data/graf1.png create mode 100644 samples/python2/data/pca_test1.jpg diff --git a/modules/python/test/test_calibration.py b/modules/python/test/test_calibration.py index 665521862c..bb54cacdc4 100644 --- a/modules/python/test/test_calibration.py +++ b/modules/python/test/test_calibration.py @@ -21,9 +21,9 @@ class calibration_test(NewOpenCVTests): img_names = [] for i in range(1, 15): if i < 10: - img_names.append('samples/data/left0{}.jpg'.format(str(i))) + img_names.append('samples/cpp/left0{}.jpg'.format(str(i))) elif i != 10: - img_names.append('samples/data/left{}.jpg'.format(str(i))) + img_names.append('samples/cpp/left{}.jpg'.format(str(i))) square_size = 1.0 pattern_size = (9, 6) diff --git a/modules/python/test/test_camshift.py b/modules/python/test/test_camshift.py index a824320eff..6c7c189344 100644 --- a/modules/python/test/test_camshift.py +++ b/modules/python/test/test_camshift.py @@ -39,7 +39,7 @@ class camshift_test(NewOpenCVTests): def prepareRender(self): - self.render = TestSceneRender(self.get_sample('samples/data/pca_test1.jpg'), deformation = True) + self.render = TestSceneRender(self.get_sample('samples/python2/data/pca_test1.jpg'), deformation = True) def runTracker(self): diff --git a/modules/python/test/test_dft.py b/modules/python/test/test_dft.py index f796939970..9019a6a63c 100644 --- a/modules/python/test/test_dft.py +++ b/modules/python/test/test_dft.py @@ -16,7 +16,7 @@ from tests_common import NewOpenCVTests class dft_test(NewOpenCVTests): def test_dft(self): - img = self.get_sample('samples/data/rubberwhale1.png', 0) + img = self.get_sample('samples/gpu/rubberwhale1.png', 0) eps = 0.001 #test direct transform diff --git a/modules/python/test/test_digits.py b/modules/python/test/test_digits.py index 587c355e37..e30361e0eb 100644 --- a/modules/python/test/test_digits.py +++ b/modules/python/test/test_digits.py @@ -36,7 +36,7 @@ from numpy.linalg import norm SZ = 20 # size of each digit is SZ x SZ CLASS_N = 10 -DIGITS_FN = 'samples/data/digits.png' +DIGITS_FN = 'samples/python2/data/digits.png' def split2d(img, cell_size, flatten=True): h, w = img.shape[:2] diff --git a/modules/python/test/test_facedetect.py b/modules/python/test/test_facedetect.py index 8d64fde10f..094659712e 100644 --- a/modules/python/test/test_facedetect.py +++ b/modules/python/test/test_facedetect.py @@ -31,7 +31,7 @@ class facedetect_test(NewOpenCVTests): cascade = cv2.CascadeClassifier(cascade_fn) nested = cv2.CascadeClassifier(nested_fn) - samples = ['samples/data/lena.jpg', 'cv/cascadeandhog/images/mona-lisa.png'] + samples = ['samples/c/lena.jpg', 'cv/cascadeandhog/images/mona-lisa.png'] faces = [] eyes = [] diff --git a/modules/python/test/test_feature_homography.py b/modules/python/test/test_feature_homography.py index f417033883..a895be6f6a 100644 --- a/modules/python/test/test_feature_homography.py +++ b/modules/python/test/test_feature_homography.py @@ -42,8 +42,8 @@ class feature_homography_test(NewOpenCVTests): def test_feature_homography(self): - self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), - self.get_sample('samples/data/box.png'), noise = 0.4, speed = 0.5) + self.render = TestSceneRender(self.get_sample('samples/python2/data/graf1.png'), + self.get_sample('samples/c/box.png'), noise = 0.4, speed = 0.5) self.frame = self.render.getNextFrame() self.tracker = PlaneTracker() self.tracker.clear() diff --git a/modules/python/test/test_houghcircles.py b/modules/python/test/test_houghcircles.py index 6cf7d74ebb..32b474fc85 100644 --- a/modules/python/test/test_houghcircles.py +++ b/modules/python/test/test_houghcircles.py @@ -39,7 +39,7 @@ class houghcircles_test(NewOpenCVTests): def test_houghcircles(self): - fn = "samples/data/board.jpg" + fn = "samples/cpp/board.jpg" src = self.get_sample(fn, 1) img = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY) diff --git a/modules/python/test/test_houghlines.py b/modules/python/test/test_houghlines.py index 081c84984d..993c113436 100644 --- a/modules/python/test/test_houghlines.py +++ b/modules/python/test/test_houghlines.py @@ -26,7 +26,7 @@ class houghlines_test(NewOpenCVTests): def test_houghlines(self): - fn = "/samples/data/pic1.png" + fn = "/samples/cpp/pic1.png" src = self.get_sample(fn) dst = cv2.Canny(src, 50, 200) diff --git a/modules/python/test/test_letter_recog.py b/modules/python/test/test_letter_recog.py index 05f879978e..d64d8e0993 100644 --- a/modules/python/test/test_letter_recog.py +++ b/modules/python/test/test_letter_recog.py @@ -150,7 +150,7 @@ class letter_recog_test(NewOpenCVTests): Model = models[model] classifier = Model() - samples, responses = load_base(self.repoPath + '/samples/data/letter-recognition.data') + samples, responses = load_base(self.repoPath + '/samples/cpp/letter-recognition.data') train_n = int(len(samples)*classifier.train_ratio) classifier.train(samples[:train_n], responses[:train_n]) diff --git a/modules/python/test/test_lk_homography.py b/modules/python/test/test_lk_homography.py index 8e526d0de7..b1d6285a9d 100644 --- a/modules/python/test/test_lk_homography.py +++ b/modules/python/test/test_lk_homography.py @@ -44,8 +44,8 @@ class lk_homography_test(NewOpenCVTests): numFeaturesInRectOnStart = 0 def test_lk_homography(self): - self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), - self.get_sample('samples/data/box.png'), noise = 0.1, speed = 1.0) + self.render = TestSceneRender(self.get_sample('samples/python2/data/graf1.png'), + self.get_sample('samples/c/box.png'), noise = 0.1, speed = 1.0) frame = self.render.getNextFrame() frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) diff --git a/modules/python/test/test_lk_track.py b/modules/python/test/test_lk_track.py index ccc67a5128..920351c5f2 100644 --- a/modules/python/test/test_lk_track.py +++ b/modules/python/test/test_lk_track.py @@ -50,7 +50,7 @@ class lk_track_test(NewOpenCVTests): def test_lk_track(self): - self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), self.get_sample('samples/data/box.png')) + self.render = TestSceneRender(self.get_sample('samples/python2/data/graf1.png'), self.get_sample('samples/c/box.png')) self.runTracker() def runTracker(self): diff --git a/modules/python/test/test_morphology.py b/modules/python/test/test_morphology.py index 309c80cfd2..1062801eac 100644 --- a/modules/python/test/test_morphology.py +++ b/modules/python/test/test_morphology.py @@ -18,7 +18,7 @@ class morphology_test(NewOpenCVTests): def test_morphology(self): - fn = 'samples/data/rubberwhale1.png' + fn = 'samples/gpu/rubberwhale1.png' img = self.get_sample(fn) modes = ['erode/dilate', 'open/close', 'blackhat/tophat', 'gradient'] diff --git a/modules/python/test/test_peopledetect.py b/modules/python/test/test_peopledetect.py index fb0a9e9cae..efb477d426 100644 --- a/modules/python/test/test_peopledetect.py +++ b/modules/python/test/test_peopledetect.py @@ -24,7 +24,7 @@ class peopledetect_test(NewOpenCVTests): hog = cv2.HOGDescriptor() hog.setSVMDetector( cv2.HOGDescriptor_getDefaultPeopleDetector() ) - dirPath = 'samples/data/' + dirPath = 'samples/gpu/' samples = ['basketball1.png', 'basketball2.png'] testPeople = [ diff --git a/modules/python/test/test_squares.py b/modules/python/test/test_squares.py index 3b3fbb6d9d..c933b358f4 100644 --- a/modules/python/test/test_squares.py +++ b/modules/python/test/test_squares.py @@ -61,7 +61,7 @@ class squares_test(NewOpenCVTests): def test_squares(self): - img = self.get_sample('samples/data/pic1.png') + img = self.get_sample('samples/cpp/pic1.png') squares = find_squares(img) testSquares = [ diff --git a/modules/python/test/test_texture_flow.py b/modules/python/test/test_texture_flow.py index 7dc3b07040..8dae6fabd4 100644 --- a/modules/python/test/test_texture_flow.py +++ b/modules/python/test/test_texture_flow.py @@ -21,7 +21,7 @@ class texture_flow_test(NewOpenCVTests): def test_texture_flow(self): - img = self.get_sample('samples/data/pic6.png') + img = self.get_sample('samples/cpp/pic6.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) h, w = img.shape[:2] diff --git a/modules/python/test/tst_scene_render.py b/modules/python/test/tst_scene_render.py index 49cde80d25..68e03acbad 100644 --- a/modules/python/test/tst_scene_render.py +++ b/modules/python/test/tst_scene_render.py @@ -100,20 +100,3 @@ class TestSceneRender(): def resetTime(self): self.time = 0.0 - - -if __name__ == '__main__': - - backGr = cv2.imread('../../../samples/data/lena.jpg') - - render = TestSceneRender(backGr, noise = 0.5) - - while True: - - img = render.getNextFrame() - cv2.imshow('img', img) - - ch = 0xFF & cv2.waitKey(3) - if ch == 27: - break - cv2.destroyAllWindows() \ No newline at end of file diff --git a/samples/python2/data/graf1.png b/samples/python2/data/graf1.png new file mode 100644 index 0000000000000000000000000000000000000000..67e347318648420262f7d4c1f528ab0f22e48109 GIT binary patch literal 951440 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�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