From 957e80abbd3ad2fc3cf650b04d3bc20ebfdaa82e Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 15 Mar 2012 14:36:01 +0000 Subject: [PATCH] lot's of changes; nonfree & photo modules added; SIFT & SURF -> nonfree module; Inpainting -> photo; refactored features2d (ORB is still failing tests), optimized brute-force matcher and made it non-template. --- include/opencv2/opencv.hpp | 1 + modules/contrib/doc/contrib.rst | 5 + .../include/opencv2/contrib/hybridtracker.hpp | 5 +- modules/contrib/src/featuretracker.cpp | 36 +- modules/contrib/test/test_main.cpp | 3 + modules/contrib/test/test_precomp.cpp | 1 + modules/contrib/test/test_precomp.hpp | 9 + modules/core/include/opencv2/core/core.hpp | 33 +- .../core/include/opencv2/core/operations.hpp | 12 - modules/core/src/algorithm.cpp | 84 +- modules/core/src/opengl_interop.cpp | 2 + modules/core/src/stat.cpp | 334 +++ .../doc/feature_detection_and_description.rst | 171 -- .../include/opencv2/features2d/features2d.hpp | 2167 ++--------------- modules/features2d/perf/perf_orb.cpp | 6 +- modules/features2d/src/descriptors.cpp | 184 +- modules/features2d/src/detectors.cpp | 477 +--- modules/features2d/src/dynamic.cpp | 5 +- modules/features2d/src/fast.cpp | 46 +- modules/features2d/src/keypoint.cpp | 46 + modules/features2d/src/matchers.cpp | 690 ++---- modules/features2d/src/mser.cpp | 373 +-- modules/features2d/src/orb.cpp | 596 ++--- modules/features2d/src/sift.cpp | 1772 -------------- modules/features2d/src/stardetector.cpp | 249 +- modules/features2d/src/test_pairs.txt | 513 ---- modules/features2d/test/test_features2d.cpp | 44 +- modules/features2d/test/test_mser.cpp | 23 +- modules/gpu/include/opencv2/gpu/gpu.hpp | 16 +- modules/gpu/src/orb.cpp | 12 +- .../doc/miscellaneous_transformations.rst | 31 - .../include/opencv2/imgproc/imgproc.hpp | 11 - modules/imgproc/src/_list.h | 2 +- modules/legacy/doc/legacy.rst | 10 + .../legacy/include/opencv2/legacy/compat.hpp | 132 + .../legacy/include/opencv2/legacy/legacy.hpp | 1037 ++++++++ .../{features2d => legacy}/src/calonder.cpp | 0 modules/legacy/src/features2d.cpp | 126 + modules/{features2d => legacy}/src/oneway.cpp | 162 +- .../src/planardetect.cpp | 358 +++ modules/legacy/test/test_main.cpp | 3 + modules/legacy/test/test_precomp.cpp | 1 + modules/legacy/test/test_precomp.hpp | 11 + modules/nonfree/CMakeLists.txt | 2 + modules/nonfree/doc/feature_detection.rst | 172 ++ modules/nonfree/doc/nonfree.rst | 10 + .../include/opencv2/nonfree/features2d.hpp | 155 ++ .../include/opencv2/nonfree/nonfree.hpp | 57 + modules/nonfree/perf/perf_main.cpp | 3 + modules/nonfree/perf/perf_precomp.cpp | 1 + modules/nonfree/perf/perf_precomp.hpp | 12 + .../perf/perf_surf.cpp | 0 modules/nonfree/src/precomp.cpp | 44 + modules/nonfree/src/precomp.hpp | 58 + modules/nonfree/src/sift.cpp | 772 ++++++ modules/{features2d => nonfree}/src/surf.cpp | 906 ++++--- .../test/test_detectors.cpp | 0 modules/nonfree/test/test_features2d.cpp | 1029 ++++++++ modules/nonfree/test/test_main.cpp | 3 + modules/nonfree/test/test_precomp.cpp | 1 + modules/nonfree/test/test_precomp.hpp | 10 + .../include/opencv2/objdetect/objdetect.hpp | 53 +- modules/objdetect/src/planardetect.cpp | 172 -- modules/photo/CMakeLists.txt | 2 + modules/photo/doc/inpainting.rst | 33 + modules/photo/doc/photo.rst | 10 + modules/photo/include/opencv2/photo/photo.hpp | 72 + .../{imgproc => photo}/perf/perf_inpaint.cpp | 76 +- modules/photo/perf/perf_main.cpp | 3 + modules/photo/perf/perf_precomp.cpp | 1 + modules/photo/perf/perf_precomp.hpp | 12 + modules/{imgproc => photo}/src/inpaint.cpp | 19 +- modules/photo/src/precomp.cpp | 44 + modules/photo/src/precomp.hpp | 56 + .../{imgproc => photo}/test/test_inpaint.cpp | 0 modules/photo/test/test_main.cpp | 3 + modules/photo/test/test_precomp.cpp | 1 + modules/photo/test/test_precomp.hpp | 9 + modules/python/CMakeLists.txt | 8 +- modules/python/src2/cv2.cpp | 2 + modules/refman.rst | 2 + .../opencv2/stitching/detail/matchers.hpp | 4 +- modules/stitching/src/matchers.cpp | 25 +- .../include/opencv2/video/background_segm.hpp | 2 +- samples/c/CMakeLists.txt | 5 +- samples/c/find_obj.cpp | 4 + samples/c/find_obj_calonder.cpp | 4 +- samples/c/find_obj_ferns.cpp | 1 + samples/c/mser_sample.cpp | 174 +- samples/c/one_way_sample.cpp | 59 +- samples/cpp/CMakeLists.txt | 5 +- samples/cpp/brief_match_test.cpp | 2 +- .../cpp/detector_descriptor_evaluation.cpp | 3 +- samples/cpp/generic_descriptor_match.cpp | 47 +- samples/cpp/inpaint.cpp | 1 + samples/cpp/matcher_simple.cpp | 3 +- samples/cpp/video_homography.cpp | 2 +- samples/gpu/CMakeLists.txt | 3 +- samples/gpu/performance/tests.cpp | 3 +- 99 files changed, 6719 insertions(+), 7240 deletions(-) create mode 100644 modules/contrib/doc/contrib.rst create mode 100644 modules/contrib/test/test_main.cpp create mode 100644 modules/contrib/test/test_precomp.cpp create mode 100644 modules/contrib/test/test_precomp.hpp delete mode 100644 modules/features2d/src/sift.cpp delete mode 100644 modules/features2d/src/test_pairs.txt create mode 100644 modules/legacy/doc/legacy.rst rename modules/{features2d => legacy}/src/calonder.cpp (100%) create mode 100644 modules/legacy/src/features2d.cpp rename modules/{features2d => legacy}/src/oneway.cpp (92%) rename modules/{features2d => legacy}/src/planardetect.cpp (77%) create mode 100644 modules/legacy/test/test_main.cpp create mode 100644 modules/legacy/test/test_precomp.cpp create mode 100644 modules/legacy/test/test_precomp.hpp create mode 100644 modules/nonfree/CMakeLists.txt create mode 100644 modules/nonfree/doc/feature_detection.rst create mode 100644 modules/nonfree/doc/nonfree.rst create mode 100644 modules/nonfree/include/opencv2/nonfree/features2d.hpp create mode 100644 modules/nonfree/include/opencv2/nonfree/nonfree.hpp create mode 100644 modules/nonfree/perf/perf_main.cpp create mode 100644 modules/nonfree/perf/perf_precomp.cpp create mode 100644 modules/nonfree/perf/perf_precomp.hpp rename modules/{features2d => nonfree}/perf/perf_surf.cpp (100%) create mode 100644 modules/nonfree/src/precomp.cpp create mode 100644 modules/nonfree/src/precomp.hpp create mode 100644 modules/nonfree/src/sift.cpp rename modules/{features2d => nonfree}/src/surf.cpp (50%) rename modules/{features2d => nonfree}/test/test_detectors.cpp (100%) create mode 100644 modules/nonfree/test/test_features2d.cpp create mode 100644 modules/nonfree/test/test_main.cpp create mode 100644 modules/nonfree/test/test_precomp.cpp create mode 100644 modules/nonfree/test/test_precomp.hpp create mode 100644 modules/photo/CMakeLists.txt create mode 100644 modules/photo/doc/inpainting.rst create mode 100644 modules/photo/doc/photo.rst create mode 100644 modules/photo/include/opencv2/photo/photo.hpp rename modules/{imgproc => photo}/perf/perf_inpaint.cpp (96%) create mode 100644 modules/photo/perf/perf_main.cpp create mode 100644 modules/photo/perf/perf_precomp.cpp create mode 100644 modules/photo/perf/perf_precomp.hpp rename modules/{imgproc => photo}/src/inpaint.cpp (98%) create mode 100644 modules/photo/src/precomp.cpp create mode 100644 modules/photo/src/precomp.hpp rename modules/{imgproc => photo}/test/test_inpaint.cpp (100%) create mode 100644 modules/photo/test/test_main.cpp create mode 100644 modules/photo/test/test_precomp.cpp create mode 100644 modules/photo/test/test_precomp.hpp diff --git a/include/opencv2/opencv.hpp b/include/opencv2/opencv.hpp index 5935d63c45..f89547c9be 100644 --- a/include/opencv2/opencv.hpp +++ b/include/opencv2/opencv.hpp @@ -48,6 +48,7 @@ #include "opencv2/flann/miniflann.hpp" #include "opencv2/imgproc/imgproc_c.h" #include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/photo/photo.hpp" #include "opencv2/video/video.hpp" #include "opencv2/features2d/features2d.hpp" #include "opencv2/objdetect/objdetect.hpp" diff --git a/modules/contrib/doc/contrib.rst b/modules/contrib/doc/contrib.rst new file mode 100644 index 0000000000..1bb31eca2b --- /dev/null +++ b/modules/contrib/doc/contrib.rst @@ -0,0 +1,5 @@ +*************************************** +contrib. Contributed/Experimental Stuff +*************************************** + +The module contains some recently added functionality that has not been stabilized, or functionality that is considered optional. diff --git a/modules/contrib/include/opencv2/contrib/hybridtracker.hpp b/modules/contrib/include/opencv2/contrib/hybridtracker.hpp index 5bf78444f5..a3dd0a7ac9 100644 --- a/modules/contrib/include/opencv2/contrib/hybridtracker.hpp +++ b/modules/contrib/include/opencv2/contrib/hybridtracker.hpp @@ -145,9 +145,8 @@ public: class CV_EXPORTS CvFeatureTracker { private: - FeatureDetector* detector; - DescriptorExtractor* descriptor; - DescriptorMatcher* matcher; + Ptr dd; + Ptr matcher; vector matches; Mat prev_image; diff --git a/modules/contrib/src/featuretracker.cpp b/modules/contrib/src/featuretracker.cpp index 84f8460e51..8850a26e1c 100644 --- a/modules/contrib/src/featuretracker.cpp +++ b/modules/contrib/src/featuretracker.cpp @@ -53,22 +53,24 @@ CvFeatureTracker::CvFeatureTracker(CvFeatureTrackerParams _params) : switch (params.feature_type) { case CvFeatureTrackerParams::SIFT: - detector = new SiftFeatureDetector( - SIFT::DetectorParams::GET_DEFAULT_THRESHOLD(), - SIFT::DetectorParams::GET_DEFAULT_EDGE_THRESHOLD() + 0.7, - SIFT::CommonParams::DEFAULT_NOCTAVES + 4, - SIFT::CommonParams::DEFAULT_NOCTAVE_LAYERS + 2, - SIFT::CommonParams::DEFAULT_FIRST_OCTAVE, - SIFT::CommonParams::FIRST_ANGLE); + dd = Algorithm::create("Feature2D.SIFT"); + if( dd.empty() ) + CV_Error(CV_StsNotImplemented, "OpenCV has been compiled without SIFT support"); + dd->set("nOctaveLayers", 5); + dd->set("contrastThreshold", 0.04); + dd->set("edgeThreshold", 10.7); case CvFeatureTrackerParams::SURF: - detector = new SurfFeatureDetector(400, 3, 4); - default: - detector = new GoodFeaturesToTrackDetector(); + dd = Algorithm::create("Feature2D.SURF"); + if( dd.empty() ) + CV_Error(CV_StsNotImplemented, "OpenCV has been compiled without SURF support"); + dd->set("hessianThreshold", 400); + dd->set("nOctaves", 3); + dd->set("nOctaveLayers", 4); + default: + CV_Error(CV_StsBadArg, "Unknown feature type"); } - descriptor = new SurfDescriptorExtractor(3, 4, false); - - matcher = new BruteForceMatcher > (); + matcher = new BFMatcher(NORM_L2); } CvFeatureTracker::~CvFeatureTracker() @@ -105,7 +107,7 @@ Rect CvFeatureTracker::updateTrackingWindowWithSIFT(Mat image) rectangle(mask, Point(window.x, window.y), Point(window.x + window.width, window.y + window.height), Scalar(255), CV_FILLED); - detector->detect(prev_image, prev_keypoints, mask); + dd->operator()(prev_image, mask, prev_keypoints, prev_desc); window.x -= params.window_size; window.y -= params.window_size; @@ -114,12 +116,12 @@ Rect CvFeatureTracker::updateTrackingWindowWithSIFT(Mat image) rectangle(mask, Point(window.x, window.y), Point(window.x + window.width, window.y + window.height), Scalar(255), CV_FILLED); - detector->detect(image, curr_keypoints, mask); + dd->operator()(image, mask, curr_keypoints, curr_desc); if (prev_keypoints.size() > 4 && curr_keypoints.size() > 4) { - descriptor->compute(prev_image, prev_keypoints, prev_desc); - descriptor->compute(image, curr_keypoints, curr_desc); + //descriptor->compute(prev_image, prev_keypoints, prev_desc); + //descriptor->compute(image, curr_keypoints, curr_desc); matcher->match(prev_desc, curr_desc, matches); diff --git a/modules/contrib/test/test_main.cpp b/modules/contrib/test/test_main.cpp new file mode 100644 index 0000000000..6b24993447 --- /dev/null +++ b/modules/contrib/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("cv") diff --git a/modules/contrib/test/test_precomp.cpp b/modules/contrib/test/test_precomp.cpp new file mode 100644 index 0000000000..5956e13e3e --- /dev/null +++ b/modules/contrib/test/test_precomp.cpp @@ -0,0 +1 @@ +#include "test_precomp.hpp" diff --git a/modules/contrib/test/test_precomp.hpp b/modules/contrib/test/test_precomp.hpp new file mode 100644 index 0000000000..2536eadb2c --- /dev/null +++ b/modules/contrib/test/test_precomp.hpp @@ -0,0 +1,9 @@ +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/contrib/contrib.hpp" +#include + +#endif + diff --git a/modules/core/include/opencv2/core/core.hpp b/modules/core/include/opencv2/core/core.hpp index 2c49962825..6892a8a74c 100644 --- a/modules/core/include/opencv2/core/core.hpp +++ b/modules/core/include/opencv2/core/core.hpp @@ -118,7 +118,7 @@ CV_EXPORTS string tempfile( const char* suffix CV_DEFAULT(0)); // matrix decomposition types enum { DECOMP_LU=0, DECOMP_SVD=1, DECOMP_EIG=2, DECOMP_CHOLESKY=3, DECOMP_QR=4, DECOMP_NORMAL=16 }; -enum { NORM_INF=1, NORM_L1=2, NORM_L2=4, NORM_TYPE_MASK=7, NORM_RELATIVE=8, NORM_MINMAX=32}; +enum { NORM_INF=1, NORM_L1=2, NORM_L2=4, NORM_L2SQR=5, NORM_HAMMING=6, NORM_HAMMING2=7, NORM_TYPE_MASK=7, NORM_RELATIVE=8, NORM_MINMAX=32 }; enum { CMP_EQ=0, CMP_GT=1, CMP_GE=2, CMP_LT=3, CMP_LE=4, CMP_NE=5 }; enum { GEMM_1_T=1, GEMM_2_T=2, GEMM_3_T=4 }; enum { DFT_INVERSE=1, DFT_SCALE=2, DFT_ROWS=4, DFT_COMPLEX_OUTPUT=16, DFT_REAL_OUTPUT=32, @@ -2057,6 +2057,14 @@ CV_EXPORTS_W double norm(InputArray src1, int normType=NORM_L2, InputArray mask= //! computes norm of selected part of the difference between two arrays CV_EXPORTS_W double norm(InputArray src1, InputArray src2, int normType=NORM_L2, InputArray mask=noArray()); + +//! naive nearest neighbor finder +CV_EXPORTS_W void batchDistance(InputArray src1, InputArray src2, + OutputArray dist, int dtype, OutputArray nidx, + int normType=NORM_L2, int K=0, + InputArray mask=noArray(), int update=0, + bool crosscheck=false); + //! scales and shifts array elements so that either the specified norm (alpha) or the minimum (alpha) and maximum (beta) array values get the specified values CV_EXPORTS_W void normalize( InputArray src, OutputArray dst, double alpha=1, double beta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray()); @@ -4244,10 +4252,20 @@ public: template typename ParamType<_Tp>::member_type get(const string& name) const; template typename ParamType<_Tp>::member_type get(const char* name) const; - template void set(const string& name, - typename ParamType<_Tp>::const_param_type value); - template void set(const char* name, - typename ParamType<_Tp>::const_param_type value); + void set(const string& name, int value); + void set(const string& name, double value); + void set(const string& name, bool value); + void set(const string& name, const string& value); + void set(const string& name, const Mat& value); + void set(const string& name, const Ptr& value); + + void set(const char* name, int value); + void set(const char* name, double value); + void set(const char* name, bool value); + void set(const char* name, const string& value); + void set(const char* name, const Mat& value); + void set(const char* name, const Ptr& value); + string paramHelp(const string& name) const; int paramType(const char* name) const; int paramType(const string& name) const; @@ -4293,6 +4311,11 @@ public: int (Algorithm::*getter)()=0, void (Algorithm::*setter)(int)=0, const string& help=string()); + void addParam(const Algorithm* algo, const char* name, + const bool& value, bool readOnly=false, + int (Algorithm::*getter)()=0, + void (Algorithm::*setter)(int)=0, + const string& help=string()); void addParam(const Algorithm* algo, const char* name, const double& value, bool readOnly=false, double (Algorithm::*getter)()=0, diff --git a/modules/core/include/opencv2/core/operations.hpp b/modules/core/include/opencv2/core/operations.hpp index 56a5272db0..5df670ca61 100644 --- a/modules/core/include/opencv2/core/operations.hpp +++ b/modules/core/include/opencv2/core/operations.hpp @@ -3823,18 +3823,6 @@ template inline typename ParamType<_Tp>::member_type Algorithm::ge return value; } -template inline void Algorithm::set(const string& name, - typename ParamType<_Tp>::const_param_type value) -{ - info()->set(this, name.c_str(), ParamType<_Tp>::type, &value); -} - -template inline void Algorithm::set(const char* name, - typename ParamType<_Tp>::const_param_type value) -{ - info()->set(this, name, ParamType<_Tp>::type, &value); -} - } #endif // __cplusplus diff --git a/modules/core/src/algorithm.cpp b/modules/core/src/algorithm.cpp index d7d85f5cc8..b7b084e7b4 100644 --- a/modules/core/src/algorithm.cpp +++ b/modules/core/src/algorithm.cpp @@ -77,7 +77,7 @@ template struct sorted_vector b = c; } - if( vec[a].first == key ) + if( a < vec.size() && vec[a].first == key ) { value = vec[a].second; return true; @@ -176,6 +176,66 @@ string Algorithm::name() const { return info()->name(); } + +void Algorithm::set(const string& name, int value) +{ + info()->set(this, name.c_str(), ParamType::type, &value); +} + +void Algorithm::set(const string& name, double value) +{ + info()->set(this, name.c_str(), ParamType::type, &value); +} + +void Algorithm::set(const string& name, bool value) +{ + info()->set(this, name.c_str(), ParamType::type, &value); +} + +void Algorithm::set(const string& name, const string& value) +{ + info()->set(this, name.c_str(), ParamType::type, &value); +} + +void Algorithm::set(const string& name, const Mat& value) +{ + info()->set(this, name.c_str(), ParamType::type, &value); +} + +void Algorithm::set(const string& name, const Ptr& value) +{ + info()->set(this, name.c_str(), ParamType::type, &value); +} + +void Algorithm::set(const char* name, int value) +{ + info()->set(this, name, ParamType::type, &value); +} + +void Algorithm::set(const char* name, double value) +{ + info()->set(this, name, ParamType::type, &value); +} + +void Algorithm::set(const char* name, bool value) +{ + info()->set(this, name, ParamType::type, &value); +} + +void Algorithm::set(const char* name, const string& value) +{ + info()->set(this, name, ParamType::type, &value); +} + +void Algorithm::set(const char* name, const Mat& value) +{ + info()->set(this, name, ParamType::type, &value); +} + +void Algorithm::set(const char* name, const Ptr& value) +{ + info()->set(this, name, ParamType::type, &value); +} string Algorithm::paramHelp(const string& name) const { @@ -261,25 +321,25 @@ void AlgorithmInfo::read(Algorithm* algo, const FileNode& fn) const if( n.empty() ) continue; if( p.type == Param::INT ) - algo->set(pname, (int)n); + algo->set(pname, (int)n); else if( p.type == Param::BOOLEAN ) - algo->set(pname, (int)n != 0); + algo->set(pname, (int)n != 0); else if( p.type == Param::REAL ) - algo->set(pname, (double)n); + algo->set(pname, (double)n); else if( p.type == Param::STRING ) - algo->set(pname, (string)n); + algo->set(pname, (string)n); else if( p.type == Param::MAT ) { Mat m; cv::read(fn, m); - algo->set(pname, m); + algo->set(pname, m); } else if( p.type == Param::ALGORITHM ) { Ptr nestedAlgo = Algorithm::_create((string)n["name"]); CV_Assert( !nestedAlgo.empty() ); nestedAlgo->read(n); - algo->set(pname, nestedAlgo); + algo->set(pname, nestedAlgo); } else CV_Error( CV_StsUnsupportedFormat, "unknown/unsupported parameter type"); @@ -505,6 +565,16 @@ void AlgorithmInfo::addParam(const Algorithm* algo, const char* name, (Algorithm::Getter)getter, (Algorithm::Setter)setter, help); } +void AlgorithmInfo::addParam(const Algorithm* algo, const char* name, + const bool& value, bool readOnly, + int (Algorithm::*getter)(), + void (Algorithm::*setter)(int), + const string& help) +{ + addParam_(algo, name, ParamType::type, &value, readOnly, + (Algorithm::Getter)getter, (Algorithm::Setter)setter, help); +} + void AlgorithmInfo::addParam(const Algorithm* algo, const char* name, const double& value, bool readOnly, double (Algorithm::*getter)(), diff --git a/modules/core/src/opengl_interop.cpp b/modules/core/src/opengl_interop.cpp index e30096ff03..10375d06d5 100644 --- a/modules/core/src/opengl_interop.cpp +++ b/modules/core/src/opengl_interop.cpp @@ -113,11 +113,13 @@ namespace const CvOpenGlFuncTab* g_glFuncTab = 0; +#ifdef HAVE_CUDA const CvOpenGlFuncTab* glFuncTab() { static EmptyGlFuncTab empty; return g_glFuncTab ? g_glFuncTab : ∅ } +#endif } CvOpenGlFuncTab::~CvOpenGlFuncTab() diff --git a/modules/core/src/stat.cpp b/modules/core/src/stat.cpp index 3d6348ba8e..23adf90ac9 100644 --- a/modules/core/src/stat.cpp +++ b/modules/core/src/stat.cpp @@ -41,6 +41,7 @@ //M*/ #include "precomp.hpp" +#include namespace cv { @@ -1425,6 +1426,339 @@ double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _m } +///////////////////////////////////// batch distance /////////////////////////////////////// + +namespace cv +{ + +template +void batchDistL1_(const _Tp* src1, const _Tp* src2, size_t step2, + int nvecs, int len, _Rt* dist, const uchar* mask) +{ + step2 /= sizeof(src2[0]); + if( !mask ) + { + for( int i = 0; i < nvecs; i++ ) + dist[i] = normL1<_Tp, _Rt>(src1, src2 + step2*i, len); + } + else + { + _Rt val0 = std::numeric_limits<_Rt>::max(); + for( int i = 0; i < nvecs; i++ ) + dist[i] = mask[i] ? normL1<_Tp, _Rt>(src1, src2 + step2*i, len) : val0; + } +} + +template +void batchDistL2Sqr_(const _Tp* src1, const _Tp* src2, size_t step2, + int nvecs, int len, _Rt* dist, const uchar* mask) +{ + step2 /= sizeof(src2[0]); + if( !mask ) + { + for( int i = 0; i < nvecs; i++ ) + dist[i] = normL2Sqr<_Tp, _Rt>(src1, src2 + step2*i, len); + } + else + { + _Rt val0 = std::numeric_limits<_Rt>::max(); + for( int i = 0; i < nvecs; i++ ) + dist[i] = mask[i] ? normL2Sqr<_Tp, _Rt>(src1, src2 + step2*i, len) : val0; + } +} + +template +void batchDistL2_(const _Tp* src1, const _Tp* src2, size_t step2, + int nvecs, int len, _Rt* dist, const uchar* mask) +{ + step2 /= sizeof(src2[0]); + if( !mask ) + { + for( int i = 0; i < nvecs; i++ ) + dist[i] = std::sqrt(normL2Sqr<_Tp, _Rt>(src1, src2 + step2*i, len)); + } + else + { + _Rt val0 = std::numeric_limits<_Rt>::max(); + for( int i = 0; i < nvecs; i++ ) + dist[i] = mask[i] ? std::sqrt(normL2Sqr<_Tp, _Rt>(src1, src2 + step2*i, len)) : val0; + } +} + +static void batchDistHamming(const uchar* src1, const uchar* src2, size_t step2, + int nvecs, int len, int* dist, const uchar* mask) +{ + step2 /= sizeof(src2[0]); + if( !mask ) + { + for( int i = 0; i < nvecs; i++ ) + dist[i] = normHamming(src1, src2 + step2*i, len); + } + else + { + int val0 = INT_MAX; + for( int i = 0; i < nvecs; i++ ) + dist[i] = mask[i] ? normHamming(src1, src2 + step2*i, len) : val0; + } +} + +static void batchDistHamming2(const uchar* src1, const uchar* src2, size_t step2, + int nvecs, int len, int* dist, const uchar* mask) +{ + step2 /= sizeof(src2[0]); + if( !mask ) + { + for( int i = 0; i < nvecs; i++ ) + dist[i] = normHamming(src1, src2 + step2*i, len, 2); + } + else + { + int val0 = INT_MAX; + for( int i = 0; i < nvecs; i++ ) + dist[i] = mask[i] ? normHamming(src1, src2 + step2*i, len, 2) : val0; + } +} + +static void batchDistL1_8u32s(const uchar* src1, const uchar* src2, size_t step2, + int nvecs, int len, int* dist, const uchar* mask) +{ + batchDistL1_(src1, src2, step2, nvecs, len, dist, mask); +} + +static void batchDistL1_8u32f(const uchar* src1, const uchar* src2, size_t step2, + int nvecs, int len, float* dist, const uchar* mask) +{ + batchDistL1_(src1, src2, step2, nvecs, len, dist, mask); +} + +static void batchDistL2Sqr_8u32s(const uchar* src1, const uchar* src2, size_t step2, + int nvecs, int len, int* dist, const uchar* mask) +{ + batchDistL2Sqr_(src1, src2, step2, nvecs, len, dist, mask); +} + +static void batchDistL2Sqr_8u32f(const uchar* src1, const uchar* src2, size_t step2, + int nvecs, int len, float* dist, const uchar* mask) +{ + batchDistL2Sqr_(src1, src2, step2, nvecs, len, dist, mask); +} + +static void batchDistL2_8u32f(const uchar* src1, const uchar* src2, size_t step2, + int nvecs, int len, float* dist, const uchar* mask) +{ + batchDistL2_(src1, src2, step2, nvecs, len, dist, mask); +} + +static void batchDistL1_32f(const float* src1, const float* src2, size_t step2, + int nvecs, int len, float* dist, const uchar* mask) +{ + batchDistL1_(src1, src2, step2, nvecs, len, dist, mask); +} + +static void batchDistL2Sqr_32f(const float* src1, const float* src2, size_t step2, + int nvecs, int len, float* dist, const uchar* mask) +{ + batchDistL2Sqr_(src1, src2, step2, nvecs, len, dist, mask); +} + +static void batchDistL2_32f(const float* src1, const float* src2, size_t step2, + int nvecs, int len, float* dist, const uchar* mask) +{ + batchDistL2_(src1, src2, step2, nvecs, len, dist, mask); +} + +typedef void (*BatchDistFunc)(const uchar* src1, const uchar* src2, size_t step2, + int nvecs, int len, uchar* dist, const uchar* mask); + + +struct BatchDistInvoker +{ + BatchDistInvoker( const Mat& _src1, const Mat& _src2, + Mat& _dist, Mat& _nidx, int _K, + const Mat& _mask, int _update, + BatchDistFunc _func) + { + src1 = &_src1; + src2 = &_src2; + dist = &_dist; + nidx = &_nidx; + K = _K; + mask = &_mask; + update = _update; + func = _func; + } + + void operator()(const BlockedRange& range) const + { + AutoBuffer buf(src2->rows); + int* bufptr = buf; + Cv32suf val0; + if( dist->type() == CV_32S ) + val0.i = INT_MAX; + else + val0.f = FLT_MAX; + + for( int i = range.begin(); i < range.end(); i++ ) + { + func(src1->ptr(i), src2->ptr(), src2->step, src2->rows, src2->cols, + K > 0 ? (uchar*)bufptr : dist->ptr(i), mask->data ? mask->ptr(i) : 0); + + if( K > 0 ) + { + int* nidxptr = nidx->ptr(i); + // since positive float's can be compared just like int's, + // we handle both CV_32S and CV_32F cases with a single branch + int* distptr = (int*)dist->ptr(i); + + int k, k0, k0_, j; + for( k0 = 0; k0 < K; k0++ ) + if( nidxptr[k0] < 0 ) + break; + k0_ = std::max(k0, 1); + + for( j = 0; j < src2->rows; j++ ) + { + int d = bufptr[j]; + if( d < distptr[k0_-1] ) + { + for( k = std::min(k0-1, K-2); k >= 0 && distptr[k] > d; k-- ) + { + nidxptr[k+1] = nidxptr[k]; + distptr[k+1] = distptr[k]; + } + nidxptr[k+1] = j + update; + distptr[k+1] = d; + k0_ = k0 = std::min(k0 + 1, K); + } + } + } + } + } + + const Mat *src1; + const Mat *src2; + Mat *dist; + Mat *nidx; + const Mat *mask; + int K; + int update; + BatchDistFunc func; +}; + +} + +void cv::batchDistance( InputArray _src1, InputArray _src2, + OutputArray _dist, int dtype, OutputArray _nidx, + int normType, int K, InputArray _mask, + int update, bool crosscheck ) +{ + Mat src1 = _src1.getMat(), src2 = _src2.getMat(), mask = _mask.getMat(); + int type = src1.type(); + CV_Assert( type == src2.type() && src1.cols == src2.cols && + (type == CV_32F || type == CV_8U)); + CV_Assert( _nidx.needed() == (K > 0) ); + + if( dtype == -1 ) + { + dtype = normType == NORM_HAMMING || normType == NORM_HAMMING2 ? CV_32S : CV_32F; + } + CV_Assert( (type == CV_8U && dtype == CV_32S) || dtype == CV_32F); + + K = std::min(K, src2.rows); + + _dist.create(src1.rows, (K > 0 ? K : src2.rows), dtype); + Mat dist = _dist.getMat(), nidx; + if( _nidx.needed() ) + { + _nidx.create(dist.size(), CV_32S); + nidx = _nidx.getMat(); + } + + if( update == 0 && K > 0 ) + { + dist = Scalar::all(dtype == CV_32S ? (double)INT_MAX : (double)FLT_MAX); + nidx = Scalar::all(-1); + } + + if( crosscheck ) + { + CV_Assert( K == 1 && update == 0 && mask.empty() ); + Mat tdist, tidx; + batchDistance(src2, src1, tdist, dtype, tidx, normType, K, mask, 0, false); + + // if an idx-th element from src1 appeared to be the nearest to i-th element of src2, + // we update the minimum mutual distance between idx-th element of src1 and the whole src2 set. + // As a result, if nidx[idx] = i*, it means that idx-th element of src1 is the nearest + // to i*-th element of src2 and i*-th element of src2 is the closest to idx-th element of src1. + // If nidx[idx] = -1, it means that there is no such ideal couple for it in src2. + // This O(N) procedure is called cross-check and it helps to eliminate some false matches. + if( dtype == CV_32S ) + { + for( int i = 0; i < tdist.rows; i++ ) + { + int idx = tidx.at(i); + int d = tdist.at(i), d0 = dist.at(idx); + if( d < d0 ) + { + dist.at(idx) = d0; + nidx.at(idx) = i + update; + } + } + } + else + { + for( int i = 0; i < tdist.rows; i++ ) + { + int idx = tidx.at(i); + float d = tdist.at(i), d0 = dist.at(idx); + if( d < d0 ) + { + dist.at(idx) = d0; + nidx.at(idx) = i + update; + } + } + } + return; + } + + BatchDistFunc func = 0; + if( type == CV_8U ) + { + if( normType == NORM_L1 && dtype == CV_32S ) + func = (BatchDistFunc)batchDistL1_8u32s; + else if( normType == NORM_L1 && dtype == CV_32F ) + func = (BatchDistFunc)batchDistL1_8u32f; + else if( normType == NORM_L2SQR && dtype == CV_32S ) + func = (BatchDistFunc)batchDistL2Sqr_8u32s; + else if( normType == NORM_L2SQR && dtype == CV_32F ) + func = (BatchDistFunc)batchDistL2Sqr_8u32f; + else if( normType == NORM_L2 && dtype == CV_32F ) + func = (BatchDistFunc)batchDistL2_8u32f; + else if( normType == NORM_HAMMING && dtype == CV_32S ) + func = (BatchDistFunc)batchDistHamming; + else if( normType == NORM_HAMMING2 && dtype == CV_32S ) + func = (BatchDistFunc)batchDistHamming2; + } + else if( type == CV_32F && dtype == CV_32F ) + { + if( normType == NORM_L1 ) + func = (BatchDistFunc)batchDistL1_32f; + else if( normType == NORM_L2SQR ) + func = (BatchDistFunc)batchDistL2Sqr_32f; + else if( normType == NORM_L2 ) + func = (BatchDistFunc)batchDistL2_32f; + } + + if( func == 0 ) + CV_Error_(CV_StsUnsupportedFormat, + ("The combination of type=%d, dtype=%d and normType=%d is not supported", + type, dtype, normType)); + + parallel_for(BlockedRange(0, src1.rows), + BatchDistInvoker(src1, src2, dist, nidx, K, mask, update, func)); +} + + CV_IMPL CvScalar cvSum( const CvArr* srcarr ) { cv::Scalar sum = cv::sum(cv::cvarrToMat(srcarr, false, true, 1)); diff --git a/modules/features2d/doc/feature_detection_and_description.rst b/modules/features2d/doc/feature_detection_and_description.rst index ebe904b4a1..56db0b8f6e 100644 --- a/modules/features2d/doc/feature_detection_and_description.rst +++ b/modules/features2d/doc/feature_detection_and_description.rst @@ -92,177 +92,6 @@ Finds keypoints in an image :param params: The algorithm parameters stored in ``CvStarDetectorParams`` (OpenCV 1.x API only) - -SIFT ----- -.. ocv:class:: SIFT - -Class for extracting keypoints and computing descriptors using the Scale Invariant Feature Transform (SIFT) approach. :: - - class CV_EXPORTS SIFT - { - public: - struct CommonParams - { - static const int DEFAULT_NOCTAVES = 4; - static const int DEFAULT_NOCTAVE_LAYERS = 3; - static const int DEFAULT_FIRST_OCTAVE = -1; - enum{ FIRST_ANGLE = 0, AVERAGE_ANGLE = 1 }; - - CommonParams(); - CommonParams( int _nOctaves, int _nOctaveLayers, int _firstOctave, - int _angleMode ); - int nOctaves, nOctaveLayers, firstOctave; - int angleMode; - }; - - struct DetectorParams - { - static double GET_DEFAULT_THRESHOLD() - { return 0.04 / SIFT::CommonParams::DEFAULT_NOCTAVE_LAYERS / 2.0; } - static double GET_DEFAULT_EDGE_THRESHOLD() { return 10.0; } - - DetectorParams(); - DetectorParams( double _threshold, double _edgeThreshold ); - double threshold, edgeThreshold; - }; - - struct DescriptorParams - { - static double GET_DEFAULT_MAGNIFICATION() { return 3.0; } - static const bool DEFAULT_IS_NORMALIZE = true; - static const int DESCRIPTOR_SIZE = 128; - - DescriptorParams(); - DescriptorParams( double _magnification, bool _isNormalize, - bool _recalculateAngles ); - double magnification; - bool isNormalize; - bool recalculateAngles; - }; - - SIFT(); - //! sift-detector constructor - SIFT( double _threshold, double _edgeThreshold, - int _nOctaves=CommonParams::DEFAULT_NOCTAVES, - int _nOctaveLayers=CommonParams::DEFAULT_NOCTAVE_LAYERS, - int _firstOctave=CommonParams::DEFAULT_FIRST_OCTAVE, - int _angleMode=CommonParams::FIRST_ANGLE ); - //! sift-descriptor constructor - SIFT( double _magnification, bool _isNormalize=true, - bool _recalculateAngles = true, - int _nOctaves=CommonParams::DEFAULT_NOCTAVES, - int _nOctaveLayers=CommonParams::DEFAULT_NOCTAVE_LAYERS, - int _firstOctave=CommonParams::DEFAULT_FIRST_OCTAVE, - int _angleMode=CommonParams::FIRST_ANGLE ); - SIFT( const CommonParams& _commParams, - const DetectorParams& _detectorParams = DetectorParams(), - const DescriptorParams& _descriptorParams = DescriptorParams() ); - - //! returns the descriptor size in floats (128) - int descriptorSize() const { return DescriptorParams::DESCRIPTOR_SIZE; } - //! finds the keypoints using the SIFT algorithm - void operator()(const Mat& img, const Mat& mask, - vector& keypoints) const; - //! finds the keypoints and computes descriptors for them using SIFT algorithm. - //! Optionally it can compute descriptors for the user-provided keypoints - void operator()(const Mat& img, const Mat& mask, - vector& keypoints, - Mat& descriptors, - bool useProvidedKeypoints=false) const; - - CommonParams getCommonParams () const { return commParams; } - DetectorParams getDetectorParams () const { return detectorParams; } - DescriptorParams getDescriptorParams () const { return descriptorParams; } - protected: - ... - }; - - - - -SURF ----- -.. ocv:class:: SURF - -Class for extracting Speeded Up Robust Features from an image [Bay06]_. The class is derived from ``CvSURFParams`` structure, which specifies the algorithm parameters: - - .. ocv:member:: int extended - - * 0 means that the basic descriptors (64 elements each) shall be computed - * 1 means that the extended descriptors (128 elements each) shall be computed - - .. ocv:member:: int upright - - * 0 means that detector computes orientation of each feature. - * 1 means that the orientation is not computed (which is much, much faster). For example, if you match images from a stereo pair, or do image stitching, the matched features likely have very similar angles, and you can speed up feature extraction by setting ``upright=1``. - - .. ocv:member:: double hessianThreshold - - Threshold for the keypoint detector. Only features, whose hessian is larger than ``hessianThreshold`` are retained by the detector. Therefore, the larger the value, the less keypoints you will get. A good default value could be from 300 to 500, depending from the image contrast. - - .. ocv:member:: int nOctaves - - The number of a gaussian pyramid octaves that the detector uses. It is set to 4 by default. If you want to get very large features, use the larger value. If you want just small features, decrease it. - - .. ocv:member:: int nOctaveLayers - - The number of images within each octave of a gaussian pyramid. It is set to 2 by default. - - -.. [Bay06] Bay, H. and Tuytelaars, T. and Van Gool, L. "SURF: Speeded Up Robust Features", 9th European Conference on Computer Vision, 2006 - - -SURF::SURF ----------- -The SURF extractor constructors. - -.. ocv:function:: SURF::SURF() - -.. ocv:function:: SURF::SURF(double hessianThreshold, int nOctaves=4, int nOctaveLayers=2, bool extended=false, bool upright=false) - -.. ocv:pyfunction:: cv2.SURF(_hessianThreshold[, _nOctaves[, _nOctaveLayers[, _extended[, _upright]]]]) -> - - :param hessianThreshold: Threshold for hessian keypoint detector used in SURF. - - :param nOctaves: Number of pyramid octaves the keypoint detector will use. - - :param nOctaveLayers: Number of octave layers within each octave. - - :param extended: Extended descriptor flag (true - use extended 128-element descriptors; false - use 64-element descriptors). - - :param upright: Up-right or rotated features flag (true - do not compute orientation of features; false - compute orientation). - - -SURF::operator() ----------------- -Detects keypoints and computes SURF descriptors for them. - -.. ocv:function:: void SURF::operator()(const Mat& image, const Mat& mask, vector& keypoints) -.. ocv:function:: void SURF::operator()(const Mat& image, const Mat& mask, vector& keypoints, vector& descriptors, bool useProvidedKeypoints=false) - -.. ocv:pyfunction:: cv2.SURF.detect(img, mask) -> keypoints -.. ocv:pyfunction:: cv2.SURF.detect(img, mask[, useProvidedKeypoints]) -> keypoints, descriptors - -.. ocv:cfunction:: void cvExtractSURF( const CvArr* image, const CvArr* mask, CvSeq** keypoints, CvSeq** descriptors, CvMemStorage* storage, CvSURFParams params ) - -.. ocv:pyoldfunction:: cv.ExtractSURF(image, mask, storage, params)-> (keypoints, descriptors) - - :param image: Input 8-bit grayscale image - - :param mask: Optional input mask that marks the regions where we should detect features. - - :param keypoints: The input/output vector of keypoints - - :param descriptors: The output concatenated vectors of descriptors. Each descriptor is 64- or 128-element vector, as returned by ``SURF::descriptorSize()``. So the total size of ``descriptors`` will be ``keypoints.size()*descriptorSize()``. - - :param useProvidedKeypoints: Boolean flag. If it is true, the keypoint detector is not run. Instead, the provided vector of keypoints is used and the algorithm just computes their descriptors. - - :param storage: Memory storage for the output keypoints and descriptors in OpenCV 1.x API. - - :param params: SURF algorithm parameters in OpenCV 1.x API. - - ORB ---- .. ocv:class:: ORB diff --git a/modules/features2d/include/opencv2/features2d/features2d.hpp b/modules/features2d/include/opencv2/features2d/features2d.hpp index 20cc81217b..39bc2c85de 100644 --- a/modules/features2d/include/opencv2/features2d/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d/features2d.hpp @@ -49,166 +49,8 @@ #ifdef __cplusplus #include -extern "C" { -#endif - -typedef struct CvSURFPoint -{ - CvPoint2D32f pt; - - int laplacian; - int size; - float dir; - float hessian; - -} CvSURFPoint; - -CV_INLINE CvSURFPoint cvSURFPoint( CvPoint2D32f pt, int laplacian, - int size, float dir CV_DEFAULT(0), - float hessian CV_DEFAULT(0)) -{ - CvSURFPoint kp; - - kp.pt = pt; - kp.laplacian = laplacian; - kp.size = size; - kp.dir = dir; - kp.hessian = hessian; - - return kp; -} - -typedef struct CvSURFParams -{ - int extended; - int upright; - double hessianThreshold; - - int nOctaves; - int nOctaveLayers; - -} CvSURFParams; - -CVAPI(CvSURFParams) cvSURFParams( double hessianThreshold, int extended CV_DEFAULT(0) ); - -// If useProvidedKeyPts!=0, keypoints are not detected, but descriptors are computed -// at the locations provided in keypoints (a CvSeq of CvSURFPoint). -CVAPI(void) cvExtractSURF( const CvArr* img, const CvArr* mask, - CvSeq** keypoints, CvSeq** descriptors, - CvMemStorage* storage, CvSURFParams params, int useProvidedKeyPts CV_DEFAULT(0) ); - -/*! - Maximal Stable Regions Parameters -*/ -typedef struct CvMSERParams -{ - //! delta, in the code, it compares (size_{i}-size_{i-delta})/size_{i-delta} - int delta; - //! prune the area which bigger than maxArea - int maxArea; - //! prune the area which smaller than minArea - int minArea; - //! prune the area have simliar size to its children - float maxVariation; - //! trace back to cut off mser with diversity < min_diversity - float minDiversity; - - /////// the next few params for MSER of color image - - //! for color image, the evolution steps - int maxEvolution; - //! the area threshold to cause re-initialize - double areaThreshold; - //! ignore too small margin - double minMargin; - //! the aperture size for edge blur - int edgeBlurSize; -} CvMSERParams; - -CVAPI(CvMSERParams) cvMSERParams( int delta CV_DEFAULT(5), int min_area CV_DEFAULT(60), - int max_area CV_DEFAULT(14400), float max_variation CV_DEFAULT(.25f), - float min_diversity CV_DEFAULT(.2f), int max_evolution CV_DEFAULT(200), - double area_threshold CV_DEFAULT(1.01), - double min_margin CV_DEFAULT(.003), - int edge_blur_size CV_DEFAULT(5) ); - -// Extracts the contours of Maximally Stable Extremal Regions -CVAPI(void) cvExtractMSER( CvArr* _img, CvArr* _mask, CvSeq** contours, CvMemStorage* storage, CvMSERParams params ); - - -typedef struct CvStarKeypoint -{ - CvPoint pt; - int size; - float response; -} CvStarKeypoint; - -CV_INLINE CvStarKeypoint cvStarKeypoint(CvPoint pt, int size, float response) -{ - CvStarKeypoint kpt; - kpt.pt = pt; - kpt.size = size; - kpt.response = response; - return kpt; -} - -typedef struct CvStarDetectorParams -{ - int maxSize; - int responseThreshold; - int lineThresholdProjected; - int lineThresholdBinarized; - int suppressNonmaxSize; -} CvStarDetectorParams; - -CV_INLINE CvStarDetectorParams cvStarDetectorParams( - int maxSize CV_DEFAULT(45), - int responseThreshold CV_DEFAULT(30), - int lineThresholdProjected CV_DEFAULT(10), - int lineThresholdBinarized CV_DEFAULT(8), - int suppressNonmaxSize CV_DEFAULT(5)) -{ - CvStarDetectorParams params; - params.maxSize = maxSize; - params.responseThreshold = responseThreshold; - params.lineThresholdProjected = lineThresholdProjected; - params.lineThresholdBinarized = lineThresholdBinarized; - params.suppressNonmaxSize = suppressNonmaxSize; - - return params; -} - -CVAPI(CvSeq*) cvGetStarKeypoints( const CvArr* img, CvMemStorage* storage, - CvStarDetectorParams params CV_DEFAULT(cvStarDetectorParams())); - -#ifdef __cplusplus -} - namespace cv { - struct CV_EXPORTS DefaultRngAuto - { - const uint64 old_state; - - DefaultRngAuto() : old_state(theRNG().state) { theRNG().state = (uint64)-1; } - ~DefaultRngAuto() { theRNG().state = old_state; } - - DefaultRngAuto& operator=(const DefaultRngAuto&); - }; - - -// CvAffinePose: defines a parameterized affine transformation of an image patch. -// An image patch is rotated on angle phi (in degrees), then scaled lambda1 times -// along horizontal and lambda2 times along vertical direction, and then rotated again -// on angle (theta - phi). -class CV_EXPORTS CvAffinePose -{ -public: - float phi; - float theta; - float lambda1; - float lambda2; -}; /*! The Keypoint Class @@ -241,12 +83,12 @@ public: size_t hash() const; //! converts vector of keypoints to vector of points - static void convert(const std::vector& keypoints, - CV_OUT std::vector& points2f, - const std::vector& keypointIndexes=std::vector()); + static void convert(const vector& keypoints, + CV_OUT vector& points2f, + const vector& keypointIndexes=vector()); //! converts vector of points to the vector of keypoints, where each keypoint is assigned the same size and the same orientation - static void convert(const std::vector& points2f, - CV_OUT std::vector& keypoints, + static void convert(const vector& points2f, + CV_OUT vector& keypoints, float size=1, float response=1, int octave=0, int class_id=-1); //! computes overlap for pair of keypoints; @@ -284,7 +126,8 @@ public: /* * Remove keypoints of sizes out of range. */ - static void runByKeypointSize( vector& keypoints, float minSize, float maxSize=std::numeric_limits::max() ); + static void runByKeypointSize( vector& keypoints, float minSize, + float maxSize=FLT_MAX ); /* * Remove keypoints from some image by mask for pixels of this image. */ @@ -293,1091 +136,20 @@ public: * Remove duplicated keypoints. */ static void removeDuplicated( vector& keypoints ); -}; - -/*! - SIFT implementation. - - The class implements SIFT algorithm by D. Lowe. -*/ - -class CV_EXPORTS SIFT -{ -public: - struct CV_EXPORTS CommonParams - { - static const int DEFAULT_NOCTAVES = 4; - static const int DEFAULT_NOCTAVE_LAYERS = 3; - static const int DEFAULT_FIRST_OCTAVE = -1; - enum { FIRST_ANGLE = 0, AVERAGE_ANGLE = 1 }; - - CommonParams(); - CommonParams( int _nOctaves, int _nOctaveLayers, int /*_firstOctave*/, int /*_angleMode*/ ); - CommonParams( int _nOctaves, int _nOctaveLayers ); - int nOctaves, nOctaveLayers; - int firstOctave; // it is not used now (firstOctave == 0 always) - int angleMode; // it is not used now - }; - - struct CV_EXPORTS DetectorParams - { - static double GET_DEFAULT_THRESHOLD() { return 0.04; } - static double GET_DEFAULT_EDGE_THRESHOLD() { return 10.0; } - - DetectorParams(); - DetectorParams( double _threshold, double _edgeThreshold ); - double threshold, edgeThreshold; - }; - - struct CV_EXPORTS DescriptorParams - { - static double GET_DEFAULT_MAGNIFICATION() { return 3.0; } - static const bool DEFAULT_IS_NORMALIZE = true; - static const int DESCRIPTOR_SIZE = 128; - - DescriptorParams(); - DescriptorParams( double _magnification, bool /*_isNormalize*/, bool _recalculateAngles ); - DescriptorParams( bool _recalculateAngles ); - double magnification; - bool isNormalize; // it is not used now (true always) - bool recalculateAngles; - }; - - SIFT(); - //! sift-detector constructor - SIFT( double _threshold, double _edgeThreshold, - int _nOctaves=CommonParams::DEFAULT_NOCTAVES, - int _nOctaveLayers=CommonParams::DEFAULT_NOCTAVE_LAYERS, - int _firstOctave=CommonParams::DEFAULT_FIRST_OCTAVE, - int _angleMode=CommonParams::FIRST_ANGLE ); - //! sift-descriptor constructor - SIFT( double _magnification, bool _isNormalize=true, - bool _recalculateAngles = true, - int _nOctaves=CommonParams::DEFAULT_NOCTAVES, - int _nOctaveLayers=CommonParams::DEFAULT_NOCTAVE_LAYERS, - int _firstOctave=CommonParams::DEFAULT_FIRST_OCTAVE, - int _angleMode=CommonParams::FIRST_ANGLE ); - SIFT( const CommonParams& _commParams, - const DetectorParams& _detectorParams = DetectorParams(), - const DescriptorParams& _descriptorParams = DescriptorParams() ); - - //! returns the descriptor size in floats (128) - int descriptorSize() const; - //! finds the keypoints using SIFT algorithm - void operator()(const Mat& img, const Mat& mask, - vector& keypoints) const; - //! finds the keypoints and computes descriptors for them using SIFT algorithm. - //! Optionally it can compute descriptors for the user-provided keypoints - void operator()(const Mat& img, const Mat& mask, - vector& keypoints, - Mat& descriptors, - bool useProvidedKeypoints=false) const; - - CommonParams getCommonParams () const; - DetectorParams getDetectorParams () const; - DescriptorParams getDescriptorParams () const; - -protected: - CommonParams commParams; - DetectorParams detectorParams; - DescriptorParams descriptorParams; -}; - -/*! - SURF implementation. - - The class implements SURF algorithm by H. Bay et al. - */ -class CV_EXPORTS_W SURF : public CvSURFParams -{ -public: - //! the default constructor - CV_WRAP SURF(); - //! the full constructor taking all the necessary parameters - CV_WRAP SURF(double _hessianThreshold, int _nOctaves=4, - int _nOctaveLayers=2, bool _extended=false, bool _upright=false); - - //! returns the descriptor size in float's (64 or 128) - CV_WRAP int descriptorSize() const; - //! finds the keypoints using fast hessian detector used in SURF - CV_WRAP_AS(detect) void operator()(const Mat& img, const Mat& mask, - CV_OUT vector& keypoints) const; - //! finds the keypoints and computes their descriptors. Optionally it can compute descriptors for the user-provided keypoints - CV_WRAP_AS(detect) void operator()(const Mat& img, const Mat& mask, - CV_OUT vector& keypoints, - CV_OUT vector& descriptors, - bool useProvidedKeypoints=false) const; -}; - -/*! - ORB implementation. -*/ -class CV_EXPORTS ORB -{ -public: - - /** the size of the signature in bytes */ - enum { kBytes = 32 }; - - struct CV_EXPORTS CommonParams - { - enum { DEFAULT_N_LEVELS = 3, DEFAULT_FIRST_LEVEL = 0, HARRIS_SCORE=0, FAST_SCORE=1 }; - - /** default constructor */ - CommonParams(float scale_factor = 1.2f, unsigned n_levels = DEFAULT_N_LEVELS, int edge_threshold = 31, - unsigned first_level = DEFAULT_FIRST_LEVEL, int WTA_K=2, int score_type=HARRIS_SCORE) : - scale_factor_(scale_factor), n_levels_(n_levels), first_level_(first_level >= n_levels ? 0 : first_level), - edge_threshold_(edge_threshold), WTA_K_(WTA_K), score_type_(score_type) - { - patch_size_ = 31; - } - void read(const FileNode& fn); - void write(FileStorage& fs) const; - - /** Coefficient by which we divide the dimensions from one scale pyramid level to the next */ - float scale_factor_; - /** The number of levels in the scale pyramid */ - unsigned n_levels_; - /** The level at which the image is given - * if 1, that means we will also look at the image scale_factor_ times bigger + /* + * Retain the specified number of the best keypoints (according to the response) */ - unsigned first_level_; - /** How far from the boundary the points should be. */ - int edge_threshold_; - - /** How many random points are used to produce each cell of the descriptor (2, 3, 4 ...) */ - int WTA_K_; - - /** Type of the score to use (FAST, HARRIS, ...) */ - int score_type_; - - /** The size of the patch that will be used for orientation and comparisons (only 31 is supported)*/ - int patch_size_; - }; - - /** Constructor - * @param n_features the number of desired features - * @param detector_params parameters to use - */ - ORB(size_t n_features = 500, const CommonParams & detector_params = CommonParams()); - - /** destructor to empty the patterns */ - ~ORB(); - - /** returns the descriptor size in bytes */ - int descriptorSize() const; - - /** Compute the ORB features and descriptors on an image - * @param img the image to compute the features and descriptors on - * @param mask the mask to apply - * @param keypoints the resulting keypoints - */ - void - operator()(const cv::Mat &image, const cv::Mat &mask, std::vector & keypoints); - - /** Compute the ORB features and descriptors on an image - * @param img the image to compute the features and descriptors on - * @param mask the mask to apply - * @param keypoints the resulting keypoints - * @param descriptors the resulting descriptors - * @param useProvidedKeypoints if true, the keypoints are used as an input - */ - void - operator()(const cv::Mat &image, const cv::Mat &mask, std::vector & keypoints, cv::Mat & descriptors, - bool useProvidedKeypoints = false); - - void read(const FileNode& fn); - void write(FileStorage& fs) const; - -private: - - /** The size of the patch used when comparing regions in the patterns */ - static const int kKernelWidth = 5; - - /** Compute the ORB features and descriptors on an image - * @param image the image to compute the features and descriptors on - * @param mask the mask to apply - * @param keypoints the resulting keypoints - * @param descriptors the resulting descriptors - * @param do_keypoints if true, the keypoints are computed, otherwise used as an input - * @param do_descriptors if true, also computes the descriptors - */ - void - operator()(const cv::Mat &image, const cv::Mat &mask, std::vector & keypoints, cv::Mat & descriptors, - bool do_keypoints, bool do_descriptors); - - /** Compute the ORB keypoints on an image - * @param image_pyramid the image pyramid to compute the features and descriptors on - * @param mask_pyramid the masks to apply at every level - * @param keypoints the resulting keypoints, clustered per level - */ - void computeKeyPoints(const std::vector& image_pyramid, const std::vector& mask_pyramid, - std::vector >& keypoints) const; - - /** Compute the ORB keypoint orientations - * @param image the image to compute the features and descriptors on - * @param integral_image the integral image of the image (can be empty, but the computation will be slower) - * @param level the scale at which we compute the orientation - * @param keypoints the resulting keypoints - */ - void - computeOrientation(const cv::Mat& image, const cv::Mat& integral_image, unsigned level, - std::vector& keypoints) const; - - /** Compute the ORB descriptors - * @param image the image to compute the features and descriptors on - * @param integral_image the integral image of the image (can be empty, but the computation will be slower) - * @param level the scale at which we compute the orientation - * @param keypoints the keypoints to use - * @param descriptors the resulting descriptors - */ - void - computeDescriptors(const cv::Mat& image, const cv::Mat& integral_image, unsigned level, - std::vector& keypoints, cv::Mat & descriptors) const; - - /** Compute the integral image and upadte the cached values - * @param image the image to compute the features and descriptors on - * @param level the scale at which we compute the orientation - * @param descriptors the resulting descriptors - */ - void computeIntegralImage(const cv::Mat & image, unsigned level, cv::Mat &integral_image); - - /** Parameters tuning ORB */ - CommonParams params_; - - /** The steps of the integral images for each scale */ - vector integral_image_steps_; - - /** The number of desired features per scale */ - vector n_features_per_level_; - - /** The overall number of desired features */ - size_t n_features_; - - /** The circular region to compute a feature orientation */ - vector u_max_; - - /** Points to compute BRIEF descriptors from */ - vector pattern; + static void retainBest(vector& keypoints, int npoints); }; -/*! - Maximal Stable Extremal Regions class. - - The class implements MSER algorithm introduced by J. Matas. - Unlike SIFT, SURF and many other detectors in OpenCV, this is salient region detector, - not the salient point detector. - - It returns the regions, each of those is encoded as a contour. -*/ -class CV_EXPORTS_W MSER : public CvMSERParams -{ -public: - //! the default constructor - CV_WRAP MSER(); - //! the full constructor - CV_WRAP MSER( int _delta, int _min_area, int _max_area, - double _max_variation, double _min_diversity, - int _max_evolution, double _area_threshold, - double _min_margin, int _edge_blur_size ); - //! the operator that extracts the MSERs from the image or the specific part of it - CV_WRAP_AS(detect) void operator()( const Mat& image, - CV_OUT vector >& msers, const Mat& mask ) const; -}; - -/*! - The "Star" Detector. - - The class implements the keypoint detector introduced by K. Konolige. -*/ -class CV_EXPORTS_W StarDetector : public CvStarDetectorParams -{ -public: - //! the default constructor - CV_WRAP StarDetector(); - //! the full constructor - CV_WRAP StarDetector(int _maxSize, int _responseThreshold, - int _lineThresholdProjected, - int _lineThresholdBinarized, - int _suppressNonmaxSize); - //! finds the keypoints in the image - CV_WRAP_AS(detect) void operator()(const Mat& image, - CV_OUT vector& keypoints) const; -}; - -//! detects corners using FAST algorithm by E. Rosten -CV_EXPORTS void FAST( const Mat& image, CV_OUT vector& keypoints, - int threshold, bool nonmaxSupression=true ); - -/*! - The Patch Generator class -*/ -class CV_EXPORTS PatchGenerator -{ -public: - PatchGenerator(); - PatchGenerator(double _backgroundMin, double _backgroundMax, - double _noiseRange, bool _randomBlur=true, - double _lambdaMin=0.6, double _lambdaMax=1.5, - double _thetaMin=-CV_PI, double _thetaMax=CV_PI, - double _phiMin=-CV_PI, double _phiMax=CV_PI ); - void operator()(const Mat& image, Point2f pt, Mat& patch, Size patchSize, RNG& rng) const; - void operator()(const Mat& image, const Mat& transform, Mat& patch, - Size patchSize, RNG& rng) const; - void warpWholeImage(const Mat& image, Mat& matT, Mat& buf, - CV_OUT Mat& warped, int border, RNG& rng) const; - void generateRandomTransform(Point2f srcCenter, Point2f dstCenter, - CV_OUT Mat& transform, RNG& rng, - bool inverse=false) const; - void setAffineParam(double lambda, double theta, double phi); - double backgroundMin, backgroundMax; - double noiseRange; - bool randomBlur; - double lambdaMin, lambdaMax; - double thetaMin, thetaMax; - double phiMin, phiMax; -}; - - -class CV_EXPORTS LDetector -{ -public: - LDetector(); - LDetector(int _radius, int _threshold, int _nOctaves, - int _nViews, double _baseFeatureSize, double _clusteringDistance); - void operator()(const Mat& image, - CV_OUT vector& keypoints, - int maxCount=0, bool scaleCoords=true) const; - void operator()(const vector& pyr, - CV_OUT vector& keypoints, - int maxCount=0, bool scaleCoords=true) const; - void getMostStable2D(const Mat& image, CV_OUT vector& keypoints, - int maxCount, const PatchGenerator& patchGenerator) const; - void setVerbose(bool verbose); - - void read(const FileNode& node); - void write(FileStorage& fs, const String& name=String()) const; - - int radius; - int threshold; - int nOctaves; - int nViews; - bool verbose; - - double baseFeatureSize; - double clusteringDistance; -}; - -typedef LDetector YAPE; - -class CV_EXPORTS FernClassifier -{ -public: - FernClassifier(); - FernClassifier(const FileNode& node); - FernClassifier(const vector >& points, - const vector& refimgs, - const vector >& labels=vector >(), - int _nclasses=0, int _patchSize=PATCH_SIZE, - int _signatureSize=DEFAULT_SIGNATURE_SIZE, - int _nstructs=DEFAULT_STRUCTS, - int _structSize=DEFAULT_STRUCT_SIZE, - int _nviews=DEFAULT_VIEWS, - int _compressionMethod=COMPRESSION_NONE, - const PatchGenerator& patchGenerator=PatchGenerator()); - virtual ~FernClassifier(); - virtual void read(const FileNode& n); - virtual void write(FileStorage& fs, const String& name=String()) const; - virtual void trainFromSingleView(const Mat& image, - const vector& keypoints, - int _patchSize=PATCH_SIZE, - int _signatureSize=DEFAULT_SIGNATURE_SIZE, - int _nstructs=DEFAULT_STRUCTS, - int _structSize=DEFAULT_STRUCT_SIZE, - int _nviews=DEFAULT_VIEWS, - int _compressionMethod=COMPRESSION_NONE, - const PatchGenerator& patchGenerator=PatchGenerator()); - virtual void train(const vector >& points, - const vector& refimgs, - const vector >& labels=vector >(), - int _nclasses=0, int _patchSize=PATCH_SIZE, - int _signatureSize=DEFAULT_SIGNATURE_SIZE, - int _nstructs=DEFAULT_STRUCTS, - int _structSize=DEFAULT_STRUCT_SIZE, - int _nviews=DEFAULT_VIEWS, - int _compressionMethod=COMPRESSION_NONE, - const PatchGenerator& patchGenerator=PatchGenerator()); - virtual int operator()(const Mat& img, Point2f kpt, vector& signature) const; - virtual int operator()(const Mat& patch, vector& signature) const; - virtual void clear(); - virtual bool empty() const; - void setVerbose(bool verbose); - - int getClassCount() const; - int getStructCount() const; - int getStructSize() const; - int getSignatureSize() const; - int getCompressionMethod() const; - Size getPatchSize() const; - - struct Feature - { - uchar x1, y1, x2, y2; - Feature() : x1(0), y1(0), x2(0), y2(0) {} - Feature(int _x1, int _y1, int _x2, int _y2) - : x1((uchar)_x1), y1((uchar)_y1), x2((uchar)_x2), y2((uchar)_y2) - {} - template bool operator ()(const Mat_<_Tp>& patch) const - { return patch(y1,x1) > patch(y2, x2); } - }; - - enum - { - PATCH_SIZE = 31, - DEFAULT_STRUCTS = 50, - DEFAULT_STRUCT_SIZE = 9, - DEFAULT_VIEWS = 5000, - DEFAULT_SIGNATURE_SIZE = 176, - COMPRESSION_NONE = 0, - COMPRESSION_RANDOM_PROJ = 1, - COMPRESSION_PCA = 2, - DEFAULT_COMPRESSION_METHOD = COMPRESSION_NONE - }; - -protected: - virtual void prepare(int _nclasses, int _patchSize, int _signatureSize, - int _nstructs, int _structSize, - int _nviews, int _compressionMethod); - virtual void finalize(RNG& rng); - virtual int getLeaf(int fidx, const Mat& patch) const; - - bool verbose; - int nstructs; - int structSize; - int nclasses; - int signatureSize; - int compressionMethod; - int leavesPerStruct; - Size patchSize; - vector features; - vector classCounters; - vector posteriors; -}; - - -/****************************************************************************************\ -* Calonder Classifier * -\****************************************************************************************/ - -struct RTreeNode; - -struct CV_EXPORTS BaseKeypoint -{ - int x; - int y; - IplImage* image; - - BaseKeypoint() - : x(0), y(0), image(NULL) - {} - - BaseKeypoint(int x, int y, IplImage* image) - : x(x), y(y), image(image) - {} -}; - -class CV_EXPORTS RandomizedTree -{ -public: - friend class RTreeClassifier; - - static const uchar PATCH_SIZE = 32; - static const int DEFAULT_DEPTH = 9; - static const int DEFAULT_VIEWS = 5000; - static const size_t DEFAULT_REDUCED_NUM_DIM = 176; - static float GET_LOWER_QUANT_PERC() { return .03f; } - static float GET_UPPER_QUANT_PERC() { return .92f; } - - RandomizedTree(); - ~RandomizedTree(); - - void train(std::vector const& base_set, RNG &rng, - int depth, int views, size_t reduced_num_dim, int num_quant_bits); - void train(std::vector const& base_set, RNG &rng, - PatchGenerator &make_patch, int depth, int views, size_t reduced_num_dim, - int num_quant_bits); - - // following two funcs are EXPERIMENTAL (do not use unless you know exactly what you do) - static void quantizeVector(float *vec, int dim, int N, float bnds[2], int clamp_mode=0); - static void quantizeVector(float *src, int dim, int N, float bnds[2], uchar *dst); - - // patch_data must be a 32x32 array (no row padding) - float* getPosterior(uchar* patch_data); - const float* getPosterior(uchar* patch_data) const; - uchar* getPosterior2(uchar* patch_data); - const uchar* getPosterior2(uchar* patch_data) const; - - void read(const char* file_name, int num_quant_bits); - void read(std::istream &is, int num_quant_bits); - void write(const char* file_name) const; - void write(std::ostream &os) const; - - int classes() { return classes_; } - int depth() { return depth_; } - - //void setKeepFloatPosteriors(bool b) { keep_float_posteriors_ = b; } - void discardFloatPosteriors() { freePosteriors(1); } - - inline void applyQuantization(int num_quant_bits) { makePosteriors2(num_quant_bits); } - - // debug - void savePosteriors(std::string url, bool append=false); - void savePosteriors2(std::string url, bool append=false); - -private: - int classes_; - int depth_; - int num_leaves_; - std::vector nodes_; - float **posteriors_; // 16-bytes aligned posteriors - uchar **posteriors2_; // 16-bytes aligned posteriors - std::vector leaf_counts_; - - void createNodes(int num_nodes, RNG &rng); - void allocPosteriorsAligned(int num_leaves, int num_classes); - void freePosteriors(int which); // which: 1=posteriors_, 2=posteriors2_, 3=both - void init(int classes, int depth, RNG &rng); - void addExample(int class_id, uchar* patch_data); - void finalize(size_t reduced_num_dim, int num_quant_bits); - int getIndex(uchar* patch_data) const; - inline float* getPosteriorByIndex(int index); - inline const float* getPosteriorByIndex(int index) const; - inline uchar* getPosteriorByIndex2(int index); - inline const uchar* getPosteriorByIndex2(int index) const; - //void makeRandomMeasMatrix(float *cs_phi, PHI_DISTR_TYPE dt, size_t reduced_num_dim); - void convertPosteriorsToChar(); - void makePosteriors2(int num_quant_bits); - void compressLeaves(size_t reduced_num_dim); - void estimateQuantPercForPosteriors(float perc[2]); -}; - - -inline uchar* getData(IplImage* image) -{ - return reinterpret_cast(image->imageData); -} - -inline float* RandomizedTree::getPosteriorByIndex(int index) -{ - return const_cast(const_cast(this)->getPosteriorByIndex(index)); -} - -inline const float* RandomizedTree::getPosteriorByIndex(int index) const -{ - return posteriors_[index]; -} - -inline uchar* RandomizedTree::getPosteriorByIndex2(int index) -{ - return const_cast(const_cast(this)->getPosteriorByIndex2(index)); -} - -inline const uchar* RandomizedTree::getPosteriorByIndex2(int index) const -{ - return posteriors2_[index]; -} - -struct CV_EXPORTS RTreeNode -{ - short offset1, offset2; - - RTreeNode() {} - RTreeNode(uchar x1, uchar y1, uchar x2, uchar y2) - : offset1(y1*RandomizedTree::PATCH_SIZE + x1), - offset2(y2*RandomizedTree::PATCH_SIZE + x2) - {} - - //! Left child on 0, right child on 1 - inline bool operator() (uchar* patch_data) const - { - return patch_data[offset1] > patch_data[offset2]; - } -}; - -class CV_EXPORTS RTreeClassifier -{ -public: - static const int DEFAULT_TREES = 48; - static const size_t DEFAULT_NUM_QUANT_BITS = 4; - - RTreeClassifier(); - void train(std::vector const& base_set, - RNG &rng, - int num_trees = RTreeClassifier::DEFAULT_TREES, - int depth = RandomizedTree::DEFAULT_DEPTH, - int views = RandomizedTree::DEFAULT_VIEWS, - size_t reduced_num_dim = RandomizedTree::DEFAULT_REDUCED_NUM_DIM, - int num_quant_bits = DEFAULT_NUM_QUANT_BITS); - void train(std::vector const& base_set, - RNG &rng, - PatchGenerator &make_patch, - int num_trees = RTreeClassifier::DEFAULT_TREES, - int depth = RandomizedTree::DEFAULT_DEPTH, - int views = RandomizedTree::DEFAULT_VIEWS, - size_t reduced_num_dim = RandomizedTree::DEFAULT_REDUCED_NUM_DIM, - int num_quant_bits = DEFAULT_NUM_QUANT_BITS); - - // sig must point to a memory block of at least classes()*sizeof(float|uchar) bytes - void getSignature(IplImage *patch, uchar *sig) const; - void getSignature(IplImage *patch, float *sig) const; - void getSparseSignature(IplImage *patch, float *sig, float thresh) const; - // TODO: deprecated in favor of getSignature overload, remove - void getFloatSignature(IplImage *patch, float *sig) const { getSignature(patch, sig); } - - static int countNonZeroElements(float *vec, int n, double tol=1e-10); - static inline void safeSignatureAlloc(uchar **sig, int num_sig=1, int sig_len=176); - static inline uchar* safeSignatureAlloc(int num_sig=1, int sig_len=176); - - inline int classes() const { return classes_; } - inline int original_num_classes() const { return original_num_classes_; } - - void setQuantization(int num_quant_bits); - void discardFloatPosteriors(); - - void read(const char* file_name); - void read(std::istream &is); - void write(const char* file_name) const; - void write(std::ostream &os) const; - - // experimental and debug - void saveAllFloatPosteriors(std::string file_url); - void saveAllBytePosteriors(std::string file_url); - void setFloatPosteriorsFromTextfile_176(std::string url); - float countZeroElements(); - - std::vector trees_; - -private: - int classes_; - int num_quant_bits_; - mutable uchar **posteriors_; - mutable unsigned short *ptemp_; - int original_num_classes_; - bool keep_floats_; -}; - -/****************************************************************************************\ -* One-Way Descriptor * -\****************************************************************************************/ - -class CV_EXPORTS OneWayDescriptor -{ -public: - OneWayDescriptor(); - ~OneWayDescriptor(); - - // allocates memory for given descriptor parameters - void Allocate(int pose_count, CvSize size, int nChannels); - - // GenerateSamples: generates affine transformed patches with averaging them over small transformation variations. - // If external poses and transforms were specified, uses them instead of generating random ones - // - pose_count: the number of poses to be generated - // - frontal: the input patch (can be a roi in a larger image) - // - norm: if nonzero, normalizes the output patch so that the sum of pixel intensities is 1 - void GenerateSamples(int pose_count, IplImage* frontal, int norm = 0); - - // GenerateSamplesFast: generates affine transformed patches with averaging them over small transformation variations. - // Uses precalculated transformed pca components. - // - frontal: the input patch (can be a roi in a larger image) - // - pca_hr_avg: pca average vector - // - pca_hr_eigenvectors: pca eigenvectors - // - pca_descriptors: an array of precomputed descriptors of pca components containing their affine transformations - // pca_descriptors[0] corresponds to the average, pca_descriptors[1]-pca_descriptors[pca_dim] correspond to eigenvectors - void GenerateSamplesFast(IplImage* frontal, CvMat* pca_hr_avg, - CvMat* pca_hr_eigenvectors, OneWayDescriptor* pca_descriptors); - - // sets the poses and corresponding transforms - void SetTransforms(CvAffinePose* poses, CvMat** transforms); - - // Initialize: builds a descriptor. - // - pose_count: the number of poses to build. If poses were set externally, uses them rather than generating random ones - // - frontal: input patch. Can be a roi in a larger image - // - feature_name: the feature name to be associated with the descriptor - // - norm: if 1, the affine transformed patches are normalized so that their sum is 1 - void Initialize(int pose_count, IplImage* frontal, const char* feature_name = 0, int norm = 0); - - // InitializeFast: builds a descriptor using precomputed descriptors of pca components - // - pose_count: the number of poses to build - // - frontal: input patch. Can be a roi in a larger image - // - feature_name: the feature name to be associated with the descriptor - // - pca_hr_avg: average vector for PCA - // - pca_hr_eigenvectors: PCA eigenvectors (one vector per row) - // - pca_descriptors: precomputed descriptors of PCA components, the first descriptor for the average vector - // followed by the descriptors for eigenvectors - void InitializeFast(int pose_count, IplImage* frontal, const char* feature_name, - CvMat* pca_hr_avg, CvMat* pca_hr_eigenvectors, OneWayDescriptor* pca_descriptors); - - // ProjectPCASample: unwarps an image patch into a vector and projects it into PCA space - // - patch: input image patch - // - avg: PCA average vector - // - eigenvectors: PCA eigenvectors, one per row - // - pca_coeffs: output PCA coefficients - void ProjectPCASample(IplImage* patch, CvMat* avg, CvMat* eigenvectors, CvMat* pca_coeffs) const; - - // InitializePCACoeffs: projects all warped patches into PCA space - // - avg: PCA average vector - // - eigenvectors: PCA eigenvectors, one per row - void InitializePCACoeffs(CvMat* avg, CvMat* eigenvectors); - - // EstimatePose: finds the closest match between an input patch and a set of patches with different poses - // - patch: input image patch - // - pose_idx: the output index of the closest pose - // - distance: the distance to the closest pose (L2 distance) - void EstimatePose(IplImage* patch, int& pose_idx, float& distance) const; - - // EstimatePosePCA: finds the closest match between an input patch and a set of patches with different poses. - // The distance between patches is computed in PCA space - // - patch: input image patch - // - pose_idx: the output index of the closest pose - // - distance: distance to the closest pose (L2 distance in PCA space) - // - avg: PCA average vector. If 0, matching without PCA is used - // - eigenvectors: PCA eigenvectors, one per row - void EstimatePosePCA(CvArr* patch, int& pose_idx, float& distance, CvMat* avg, CvMat* eigenvalues) const; - - // GetPatchSize: returns the size of each image patch after warping (2 times smaller than the input patch) - CvSize GetPatchSize() const - { - return m_patch_size; - } - - // GetInputPatchSize: returns the required size of the patch that the descriptor is built from - // (2 time larger than the patch after warping) - CvSize GetInputPatchSize() const - { - return cvSize(m_patch_size.width*2, m_patch_size.height*2); - } - - // GetPatch: returns a patch corresponding to specified pose index - // - index: pose index - // - return value: the patch corresponding to specified pose index - IplImage* GetPatch(int index); - - // GetPose: returns a pose corresponding to specified pose index - // - index: pose index - // - return value: the pose corresponding to specified pose index - CvAffinePose GetPose(int index) const; - - // Save: saves all patches with different poses to a specified path - void Save(const char* path); - - // ReadByName: reads a descriptor from a file storage - // - fs: file storage - // - parent: parent node - // - name: node name - // - return value: 1 if succeeded, 0 otherwise - int ReadByName(CvFileStorage* fs, CvFileNode* parent, const char* name); - - // ReadByName: reads a descriptor from a file node - // - parent: parent node - // - name: node name - // - return value: 1 if succeeded, 0 otherwise - int ReadByName(const FileNode &parent, const char* name); - - // Write: writes a descriptor into a file storage - // - fs: file storage - // - name: node name - void Write(CvFileStorage* fs, const char* name); - - // GetFeatureName: returns a name corresponding to a feature - const char* GetFeatureName() const; - - // GetCenter: returns the center of the feature - CvPoint GetCenter() const; - - void SetPCADimHigh(int pca_dim_high) {m_pca_dim_high = pca_dim_high;}; - void SetPCADimLow(int pca_dim_low) {m_pca_dim_low = pca_dim_low;}; - - int GetPCADimLow() const; - int GetPCADimHigh() const; - - CvMat** GetPCACoeffs() const {return m_pca_coeffs;} - -protected: - int m_pose_count; // the number of poses - CvSize m_patch_size; // size of each image - IplImage** m_samples; // an array of length m_pose_count containing the patch in different poses - IplImage* m_input_patch; - IplImage* m_train_patch; - CvMat** m_pca_coeffs; // an array of length m_pose_count containing pca decomposition of the patch in different poses - CvAffinePose* m_affine_poses; // an array of poses - CvMat** m_transforms; // an array of affine transforms corresponding to poses - - string m_feature_name; // the name of the feature associated with the descriptor - CvPoint m_center; // the coordinates of the feature (the center of the input image ROI) - - int m_pca_dim_high; // the number of descriptor pca components to use for generating affine poses - int m_pca_dim_low; // the number of pca components to use for comparison -}; - - -// OneWayDescriptorBase: encapsulates functionality for training/loading a set of one way descriptors -// and finding the nearest closest descriptor to an input feature -class CV_EXPORTS OneWayDescriptorBase -{ -public: - - // creates an instance of OneWayDescriptor from a set of training files - // - patch_size: size of the input (large) patch - // - pose_count: the number of poses to generate for each descriptor - // - train_path: path to training files - // - pca_config: the name of the file that contains PCA for small patches (2 times smaller - // than patch_size each dimension - // - pca_hr_config: the name of the file that contains PCA for large patches (of patch_size size) - // - pca_desc_config: the name of the file that contains descriptors of PCA components - OneWayDescriptorBase(CvSize patch_size, int pose_count, const char* train_path = 0, const char* pca_config = 0, - const char* pca_hr_config = 0, const char* pca_desc_config = 0, int pyr_levels = 1, - int pca_dim_high = 100, int pca_dim_low = 100); - - OneWayDescriptorBase(CvSize patch_size, int pose_count, const string &pca_filename, const string &train_path = string(), const string &images_list = string(), - float _scale_min = 0.7f, float _scale_max=1.5f, float _scale_step=1.2f, int pyr_levels = 1, - int pca_dim_high = 100, int pca_dim_low = 100); - - - virtual ~OneWayDescriptorBase(); - void clear (); - - - // Allocate: allocates memory for a given number of descriptors - void Allocate(int train_feature_count); - - // AllocatePCADescriptors: allocates memory for pca descriptors - void AllocatePCADescriptors(); - - // returns patch size - CvSize GetPatchSize() const {return m_patch_size;}; - // returns the number of poses for each descriptor - int GetPoseCount() const {return m_pose_count;}; - - // returns the number of pyramid levels - int GetPyrLevels() const {return m_pyr_levels;}; - - // returns the number of descriptors - int GetDescriptorCount() const {return m_train_feature_count;}; - - // CreateDescriptorsFromImage: creates descriptors for each of the input features - // - src: input image - // - features: input features - // - pyr_levels: the number of pyramid levels - void CreateDescriptorsFromImage(IplImage* src, const std::vector& features); - - // CreatePCADescriptors: generates descriptors for PCA components, needed for fast generation of feature descriptors - void CreatePCADescriptors(); - - // returns a feature descriptor by feature index - const OneWayDescriptor* GetDescriptor(int desc_idx) const {return &m_descriptors[desc_idx];}; - - // FindDescriptor: finds the closest descriptor - // - patch: input image patch - // - desc_idx: output index of the closest descriptor to the input patch - // - pose_idx: output index of the closest pose of the closest descriptor to the input patch - // - distance: distance from the input patch to the closest feature pose - // - _scales: scales of the input patch for each descriptor - // - scale_ranges: input scales variation (float[2]) - void FindDescriptor(IplImage* patch, int& desc_idx, int& pose_idx, float& distance, float* _scale = 0, float* scale_ranges = 0) const; - - // - patch: input image patch - // - n: number of the closest indexes - // - desc_idxs: output indexes of the closest descriptor to the input patch (n) - // - pose_idx: output indexes of the closest pose of the closest descriptor to the input patch (n) - // - distances: distance from the input patch to the closest feature pose (n) - // - _scales: scales of the input patch - // - scale_ranges: input scales variation (float[2]) - void FindDescriptor(IplImage* patch, int n, std::vector& desc_idxs, std::vector& pose_idxs, - std::vector& distances, std::vector& _scales, float* scale_ranges = 0) const; - - // FindDescriptor: finds the closest descriptor - // - src: input image - // - pt: center of the feature - // - desc_idx: output index of the closest descriptor to the input patch - // - pose_idx: output index of the closest pose of the closest descriptor to the input patch - // - distance: distance from the input patch to the closest feature pose - void FindDescriptor(IplImage* src, cv::Point2f pt, int& desc_idx, int& pose_idx, float& distance) const; - - // InitializePoses: generates random poses - void InitializePoses(); - - // InitializeTransformsFromPoses: generates 2x3 affine matrices from poses (initializes m_transforms) - void InitializeTransformsFromPoses(); - - // InitializePoseTransforms: subsequently calls InitializePoses and InitializeTransformsFromPoses - void InitializePoseTransforms(); - - // InitializeDescriptor: initializes a descriptor - // - desc_idx: descriptor index - // - train_image: image patch (ROI is supported) - // - feature_label: feature textual label - void InitializeDescriptor(int desc_idx, IplImage* train_image, const char* feature_label); - - void InitializeDescriptor(int desc_idx, IplImage* train_image, const cv::KeyPoint& keypoint, const char* feature_label); - - // InitializeDescriptors: load features from an image and create descriptors for each of them - void InitializeDescriptors(IplImage* train_image, const vector& features, - const char* feature_label = "", int desc_start_idx = 0); - - // Write: writes this object to a file storage - // - fs: output filestorage - void Write (FileStorage &fs) const; - - // Read: reads OneWayDescriptorBase object from a file node - // - fn: input file node - void Read (const FileNode &fn); - - // LoadPCADescriptors: loads PCA descriptors from a file - // - filename: input filename - int LoadPCADescriptors(const char* filename); - - // LoadPCADescriptors: loads PCA descriptors from a file node - // - fn: input file node - int LoadPCADescriptors(const FileNode &fn); - - // SavePCADescriptors: saves PCA descriptors to a file - // - filename: output filename - void SavePCADescriptors(const char* filename); - - // SavePCADescriptors: saves PCA descriptors to a file storage - // - fs: output file storage - void SavePCADescriptors(CvFileStorage* fs) const; - - // GeneratePCA: calculate and save PCA components and descriptors - // - img_path: path to training PCA images directory - // - images_list: filename with filenames of training PCA images - void GeneratePCA(const char* img_path, const char* images_list, int pose_count=500); - - // SetPCAHigh: sets the high resolution pca matrices (copied to internal structures) - void SetPCAHigh(CvMat* avg, CvMat* eigenvectors); - - // SetPCALow: sets the low resolution pca matrices (copied to internal structures) - void SetPCALow(CvMat* avg, CvMat* eigenvectors); - - int GetLowPCA(CvMat** avg, CvMat** eigenvectors) - { - *avg = m_pca_avg; - *eigenvectors = m_pca_eigenvectors; - return m_pca_dim_low; - }; - - int GetPCADimLow() const {return m_pca_dim_low;}; - int GetPCADimHigh() const {return m_pca_dim_high;}; - - void ConvertDescriptorsArrayToTree(); // Converting pca_descriptors array to KD tree - - // GetPCAFilename: get default PCA filename - static string GetPCAFilename () { return "pca.yml"; } - - virtual bool empty() const { return m_train_feature_count <= 0 ? true : false; } - -protected: - CvSize m_patch_size; // patch size - int m_pose_count; // the number of poses for each descriptor - int m_train_feature_count; // the number of the training features - OneWayDescriptor* m_descriptors; // array of train feature descriptors - CvMat* m_pca_avg; // PCA average Vector for small patches - CvMat* m_pca_eigenvectors; // PCA eigenvectors for small patches - CvMat* m_pca_hr_avg; // PCA average Vector for large patches - CvMat* m_pca_hr_eigenvectors; // PCA eigenvectors for large patches - OneWayDescriptor* m_pca_descriptors; // an array of PCA descriptors - - cv::flann::Index* m_pca_descriptors_tree; - CvMat* m_pca_descriptors_matrix; - - CvAffinePose* m_poses; // array of poses - CvMat** m_transforms; // array of affine transformations corresponding to poses - - int m_pca_dim_high; - int m_pca_dim_low; - - int m_pyr_levels; - float scale_min; - float scale_max; - float scale_step; - - // SavePCAall: saves PCA components and descriptors to a file storage - // - fs: output file storage - void SavePCAall (FileStorage &fs) const; - - // LoadPCAall: loads PCA components and descriptors from a file node - // - fn: input file node - void LoadPCAall (const FileNode &fn); -}; - -class CV_EXPORTS OneWayDescriptorObject : public OneWayDescriptorBase -{ -public: - // creates an instance of OneWayDescriptorObject from a set of training files - // - patch_size: size of the input (large) patch - // - pose_count: the number of poses to generate for each descriptor - // - train_path: path to training files - // - pca_config: the name of the file that contains PCA for small patches (2 times smaller - // than patch_size each dimension - // - pca_hr_config: the name of the file that contains PCA for large patches (of patch_size size) - // - pca_desc_config: the name of the file that contains descriptors of PCA components - OneWayDescriptorObject(CvSize patch_size, int pose_count, const char* train_path, const char* pca_config, - const char* pca_hr_config = 0, const char* pca_desc_config = 0, int pyr_levels = 1); - - OneWayDescriptorObject(CvSize patch_size, int pose_count, const string &pca_filename, - const string &train_path = string (), const string &images_list = string (), - float _scale_min = 0.7f, float _scale_max=1.5f, float _scale_step=1.2f, int pyr_levels = 1); - - - virtual ~OneWayDescriptorObject(); - - // Allocate: allocates memory for a given number of features - // - train_feature_count: the total number of features - // - object_feature_count: the number of features extracted from the object - void Allocate(int train_feature_count, int object_feature_count); - - - void SetLabeledFeatures(const vector& features) {m_train_features = features;}; - vector& GetLabeledFeatures() {return m_train_features;}; - const vector& GetLabeledFeatures() const {return m_train_features;}; - vector _GetLabeledFeatures() const; - - // IsDescriptorObject: returns 1 if descriptor with specified index is positive, otherwise 0 - int IsDescriptorObject(int desc_idx) const; - - // MatchPointToPart: returns the part number of a feature if it matches one of the object parts, otherwise -1 - int MatchPointToPart(CvPoint pt) const; - - // GetDescriptorPart: returns the part number of the feature corresponding to a specified descriptor - // - desc_idx: descriptor index - int GetDescriptorPart(int desc_idx) const; - - - void InitializeObjectDescriptors(IplImage* train_image, const vector& features, - const char* feature_label, int desc_start_idx = 0, float scale = 1.0f, - int is_background = 0); - - // GetObjectFeatureCount: returns the number of object features - int GetObjectFeatureCount() const {return m_object_feature_count;}; - -protected: - int* m_part_id; // contains part id for each of object descriptors - vector m_train_features; // train features - int m_object_feature_count; // the number of the positive features - -}; - - -/****************************************************************************************\ -* FeatureDetector * -\****************************************************************************************/ +/************************************ Base Classes ************************************/ /* * Abstract base class for 2D image feature detectors. */ -class CV_EXPORTS_W FeatureDetector +class CV_EXPORTS_W FeatureDetector : public virtual Algorithm { public: virtual ~FeatureDetector(); @@ -1398,21 +170,16 @@ public: * masks Masks for image set. masks[i] is a mask for images[i]. */ void detect( const vector& images, vector >& keypoints, const vector& masks=vector() ) const; - - // Read detector object from a file node. - virtual void read( const FileNode& ); - // Read detector object from a file node. - virtual void write( FileStorage& ) const; - + // Return true if detector object is empty CV_WRAP virtual bool empty() const; - + // Create feature detector by detector name. CV_WRAP static Ptr create( const string& detectorType ); - + protected: virtual void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const = 0; - + /* * Remove keypoints that are not in the mask. * Helper function, useful when wrapping a library call for keypoint detection that @@ -1420,14 +187,207 @@ protected: */ static void removeInvalidPoints( const Mat& mask, vector& keypoints ); }; + + +/* + * Abstract base class for computing descriptors for image keypoints. + * + * In this interface we assume a keypoint descriptor can be represented as a + * dense, fixed-dimensional vector of some basic type. Most descriptors used + * in practice follow this pattern, as it makes it very easy to compute + * distances between descriptors. Therefore we represent a collection of + * descriptors as a Mat, where each row is one keypoint descriptor. + */ +class CV_EXPORTS_W DescriptorExtractor : public virtual Algorithm +{ +public: + virtual ~DescriptorExtractor(); + + /* + * Compute the descriptors for a set of keypoints in an image. + * image The image. + * keypoints The input keypoints. Keypoints for which a descriptor cannot be computed are removed. + * descriptors Copmputed descriptors. Row i is the descriptor for keypoint i. + */ + CV_WRAP void compute( const Mat& image, CV_OUT CV_IN_OUT vector& keypoints, CV_OUT Mat& descriptors ) const; + + /* + * Compute the descriptors for a keypoints collection detected in image collection. + * images Image collection. + * keypoints Input keypoints collection. keypoints[i] is keypoints detected in images[i]. + * Keypoints for which a descriptor cannot be computed are removed. + * descriptors Descriptor collection. descriptors[i] are descriptors computed for set keypoints[i]. + */ + void compute( const vector& images, vector >& keypoints, vector& descriptors ) const; + + CV_WRAP virtual int descriptorSize() const = 0; + CV_WRAP virtual int descriptorType() const = 0; + + CV_WRAP virtual bool empty() const; + + CV_WRAP static Ptr create( const string& descriptorExtractorType ); + +protected: + virtual void computeImpl( const Mat& image, vector& keypoints, Mat& descriptors ) const = 0; + + /* + * Remove keypoints within borderPixels of an image edge. + */ + static void removeBorderKeypoints( vector& keypoints, + Size imageSize, int borderSize ); +}; + + + +/* + * Abstract base class for simultaneous 2D feature detection descriptor extraction. + */ +class CV_EXPORTS_W Feature2D : public FeatureDetector, public DescriptorExtractor +{ +public: + /* + * Detect keypoints in an image. + * image The image. + * keypoints The detected keypoints. + * mask Mask specifying where to look for keypoints (optional). Must be a char + * matrix with non-zero values in the region of interest. + * useProvidedKeypoints If true, the method will skip the detection phase and will compute + * descriptors for the provided keypoints + */ + CV_WRAP_AS(detectAndCompute) virtual void operator()( InputArray image, InputArray mask, + CV_OUT vector& keypoints, + OutputArray descriptors, + bool useProvidedKeypoints=false ) const = 0; + + // Create feature detector and descriptor extractor by name. + static Ptr create( const string& name ); +}; + + +/*! + ORB implementation. +*/ +class CV_EXPORTS ORB : public Feature2D +{ +public: + // the size of the signature in bytes + enum { kBytes = 32, HARRIS_SCORE=0, FAST_SCORE=1 }; + + explicit ORB(int nfeatures = 500, float scaleFactor = 1.2f, int nlevels = 3, int edgeThreshold = 31, + int firstLevel = 0, int WTA_K=2, int scoreType=0, int patchSize=31 ); + + // returns the descriptor size in bytes + int descriptorSize() const; + // returns the descriptor type + int descriptorType() const; + + // Compute the ORB features and descriptors on an image + void operator()(InputArray image, InputArray mask, vector& keypoints) const; + + // Compute the ORB features and descriptors on an image + void operator()( InputArray image, InputArray mask, vector& keypoints, + OutputArray descriptors, bool useProvidedKeypoints=false ) const; + + AlgorithmInfo* info() const; + +protected: + + void computeImpl( const Mat& image, vector& keypoints, Mat& descriptors ) const; + void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; + + CV_PROP_RW int nfeatures; + CV_PROP_RW double scaleFactor; + CV_PROP_RW int nlevels; + CV_PROP_RW int edgeThreshold; + CV_PROP_RW int firstLevel; + CV_PROP_RW int WTA_K; + CV_PROP_RW int scoreType; + CV_PROP_RW int patchSize; +}; + +typedef ORB OrbFeatureDetector; +typedef ORB OrbDescriptorExtractor; + + +/*! + Maximal Stable Extremal Regions class. + + The class implements MSER algorithm introduced by J. Matas. + Unlike SIFT, SURF and many other detectors in OpenCV, this is salient region detector, + not the salient point detector. + + It returns the regions, each of those is encoded as a contour. +*/ +class CV_EXPORTS_W MSER : public FeatureDetector +{ +public: + //! the full constructor + explicit 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 ); + + //! the operator that extracts the MSERs from the image or the specific part of it + CV_WRAP_AS(detect) void operator()( const Mat& image, vector >& msers, + const Mat& mask=Mat() ) const; + AlgorithmInfo* info() const; + +protected: + void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; + + int delta; + int minArea; + int maxArea; + double maxVariation; + double minDiversity; + int maxEvolution; + double areaThreshold; + double minMargin; + int edgeBlurSize; +}; + +typedef MSER MserFeatureDetector; + +/*! + The "Star" Detector. + + The class implements the keypoint detector introduced by K. Konolige. +*/ +class CV_EXPORTS_W StarDetector : public FeatureDetector +{ +public: + //! the full constructor + CV_WRAP StarDetector(int _maxSize=45, int _responseThreshold=30, + int _lineThresholdProjected=10, + int _lineThresholdBinarized=8, + int _suppressNonmaxSize=5); + + //! finds the keypoints in the image + CV_WRAP_AS(detect) void operator()(const Mat& image, + CV_OUT vector& keypoints) const; + + AlgorithmInfo* info() const; + +protected: + void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; + + int maxSize; + int responseThreshold; + int lineThresholdProjected; + int lineThresholdBinarized; + int suppressNonmaxSize; +}; + +//! detects corners using FAST algorithm by E. Rosten +CV_EXPORTS void FAST( InputArray image, CV_OUT vector& keypoints, + int threshold, bool nonmaxSupression=true ); class CV_EXPORTS_W FastFeatureDetector : public FeatureDetector { public: CV_WRAP FastFeatureDetector( int threshold=10, bool nonmaxSuppression=true ); - virtual void read( const FileNode& fn ); - virtual void write( FileStorage& fs ) const; - + AlgorithmInfo* info() const; + protected: virtual void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; @@ -1436,121 +396,26 @@ protected: }; -class CV_EXPORTS GoodFeaturesToTrackDetector : public FeatureDetector +class CV_EXPORTS GFTTDetector : public FeatureDetector { public: - class CV_EXPORTS Params - { - public: - Params( int maxCorners=1000, double qualityLevel=0.01, double minDistance=1., - int blockSize=3, bool useHarrisDetector=false, double k=0.04 ); - void read( const FileNode& fn ); - void write( FileStorage& fs ) const; - - int maxCorners; - double qualityLevel; - double minDistance; - int blockSize; - bool useHarrisDetector; - double k; - }; - - GoodFeaturesToTrackDetector( const GoodFeaturesToTrackDetector::Params& params=GoodFeaturesToTrackDetector::Params() ); - GoodFeaturesToTrackDetector( int maxCorners, double qualityLevel, double minDistance, - int blockSize=3, bool useHarrisDetector=false, double k=0.04 ); - virtual void read( const FileNode& fn ); - virtual void write( FileStorage& fs ) const; + GFTTDetector( int maxCorners=1000, double qualityLevel=0.01, double minDistance=1, + int blockSize=3, bool useHarrisDetector=false, double k=0.04 ); + AlgorithmInfo* info() const; protected: virtual void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; - Params params; + int nfeatures; + double qualityLevel; + double minDistance; + int blockSize; + bool useHarrisDetector; + double k; }; -class CV_EXPORTS MserFeatureDetector : public FeatureDetector -{ -public: - MserFeatureDetector( CvMSERParams params=cvMSERParams() ); - MserFeatureDetector( int delta, int minArea, int maxArea, double maxVariation, double minDiversity, - int maxEvolution, double areaThreshold, double minMargin, int edgeBlurSize ); - virtual void read( const FileNode& fn ); - virtual void write( FileStorage& fs ) const; - -protected: - virtual void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; - - MSER mser; -}; - -class CV_EXPORTS_W StarFeatureDetector : public FeatureDetector -{ -public: - StarFeatureDetector( const CvStarDetectorParams& params=cvStarDetectorParams() ); - CV_WRAP StarFeatureDetector( int maxSize, int responseThreshold=30, int lineThresholdProjected = 10, - int lineThresholdBinarized=8, int suppressNonmaxSize=5 ); - virtual void read( const FileNode& fn ); - virtual void write( FileStorage& fs ) const; - -protected: - virtual void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; - - StarDetector star; -}; - -class CV_EXPORTS SiftFeatureDetector : public FeatureDetector -{ -public: - SiftFeatureDetector( const SIFT::DetectorParams& detectorParams=SIFT::DetectorParams(), - const SIFT::CommonParams& commonParams=SIFT::CommonParams() ); - SiftFeatureDetector( double threshold, double edgeThreshold, - int nOctaves=SIFT::CommonParams::DEFAULT_NOCTAVES, - int nOctaveLayers=SIFT::CommonParams::DEFAULT_NOCTAVE_LAYERS, - int firstOctave=SIFT::CommonParams::DEFAULT_FIRST_OCTAVE, - int angleMode=SIFT::CommonParams::FIRST_ANGLE ); - virtual void read( const FileNode& fn ); - virtual void write( FileStorage& fs ) const; - -protected: - virtual void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; - - SIFT sift; -}; - -class CV_EXPORTS_W SurfFeatureDetector : public FeatureDetector -{ -public: - CV_WRAP SurfFeatureDetector( double hessianThreshold=400., int octaves=3, int octaveLayers=4, bool upright=false ); - virtual void read( const FileNode& fn ); - virtual void write( FileStorage& fs ) const; - -protected: - virtual void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; - - SURF surf; -}; - -/** Feature detector for the ORB feature - * Basically fast followed by a Harris check - */ -class CV_EXPORTS OrbFeatureDetector : public cv::FeatureDetector -{ -public: - /** Default constructor - * @param n_features the number of desired features - * @param params parameters to use - */ - OrbFeatureDetector(size_t n_features = 700, ORB::CommonParams params = ORB::CommonParams()); - - virtual void read(const cv::FileNode&); - virtual void write(cv::FileStorage&) const; - -protected: - virtual void - detectImpl(const cv::Mat& image, std::vector& keypoints, const cv::Mat& mask = cv::Mat()) const; -private: - /** the ORB object we use for the computations */ - mutable ORB orb_; -}; +typedef GFTTDetector GoodFeaturesToTrackDetector; +typedef StarDetector StarFeatureDetector; class CV_EXPORTS_W SimpleBlobDetector : public FeatureDetector { @@ -1602,37 +467,29 @@ protected: Params params; }; + class CV_EXPORTS DenseFeatureDetector : public FeatureDetector { public: - class CV_EXPORTS Params - { - public: - Params( float initFeatureScale=1.f, int featureScaleLevels=1, float featureScaleMul=0.1f, - int initXyStep=6, int initImgBound=0, bool varyXyStepWithScale=true, bool varyImgBoundWithScale=false ); - float initFeatureScale; - int featureScaleLevels; - float featureScaleMul; - - int initXyStep; - int initImgBound; - - bool varyXyStepWithScale; - bool varyImgBoundWithScale; - - void read( const FileNode& fn ); - void write( FileStorage& fs ) const; - }; - - DenseFeatureDetector( const DenseFeatureDetector::Params& params=DenseFeatureDetector::Params() ); - - virtual void read( const FileNode& fn ); - virtual void write( FileStorage& fs ) const; + explicit DenseFeatureDetector( float initFeatureScale=1.f, int featureScaleLevels=1, + float featureScaleMul=0.1f, + int initXyStep=6, int initImgBound=0, + bool varyXyStepWithScale=true, + bool varyImgBoundWithScale=false ); + AlgorithmInfo* info() const; protected: virtual void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; - Params params; + float initFeatureScale; + int featureScaleLevels; + float featureScaleMul; + + int initXyStep; + int initImgBound; + + bool varyXyStepWithScale; + bool varyImgBoundWithScale; }; /* @@ -1649,8 +506,9 @@ public: * gridRows Grid rows count. * gridCols Grid column count. */ - CV_WRAP GridAdaptedFeatureDetector( const Ptr& detector, int maxTotalKeypoints=1000, - int gridRows=4, int gridCols=4 ); + CV_WRAP GridAdaptedFeatureDetector( const Ptr& detector, + int maxTotalKeypoints=1000, + int gridRows=4, int gridCols=4 ); // TODO implement read/write virtual bool empty() const; @@ -1761,8 +619,8 @@ public: */ FastAdjuster(int init_thresh=20, bool nonmax=true, int min_thresh=1, int max_thresh=200); - virtual void tooFew(int min, int n_detected); - virtual void tooMany(int max, int n_detected); + virtual void tooFew(int minv, int n_detected); + virtual void tooMany(int maxv, int n_detected); virtual bool good() const; virtual Ptr clone() const; @@ -1784,8 +642,8 @@ class CV_EXPORTS StarAdjuster: public AdjusterAdapter public: StarAdjuster(double initial_thresh=30.0, double min_thresh=2., double max_thresh=200.); - virtual void tooFew(int min, int n_detected); - virtual void tooMany(int max, int n_detected); + virtual void tooFew(int minv, int n_detected); + virtual void tooMany(int maxv, int n_detected); virtual bool good() const; virtual Ptr clone() const; @@ -1794,7 +652,6 @@ protected: virtual void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; double thresh_, init_thresh_, min_thresh_, max_thresh_; - CvStarDetectorParams params_; //todo use these instead of thresh_ }; class CV_EXPORTS SurfAdjuster: public AdjusterAdapter @@ -1802,8 +659,8 @@ class CV_EXPORTS SurfAdjuster: public AdjusterAdapter public: SurfAdjuster( double initial_thresh=400.f, double min_thresh=2, double max_thresh=1000 ); - virtual void tooFew(int min, int n_detected); - virtual void tooMany(int max, int n_detected); + virtual void tooFew(int minv, int n_detected); + virtual void tooMany(int maxv, int n_detected); virtual bool good() const; virtual Ptr clone() const; @@ -1817,203 +674,7 @@ protected: CV_EXPORTS Mat windowedMatchingMask( const vector& keypoints1, const vector& keypoints2, float maxDeltaX, float maxDeltaY ); -/****************************************************************************************\ -* DescriptorExtractor * -\****************************************************************************************/ -/* - * Abstract base class for computing descriptors for image keypoints. - * - * In this interface we assume a keypoint descriptor can be represented as a - * dense, fixed-dimensional vector of some basic type. Most descriptors used - * in practice follow this pattern, as it makes it very easy to compute - * distances between descriptors. Therefore we represent a collection of - * descriptors as a cv::Mat, where each row is one keypoint descriptor. - */ -class CV_EXPORTS_W DescriptorExtractor -{ -public: - virtual ~DescriptorExtractor(); - - /* - * Compute the descriptors for a set of keypoints in an image. - * image The image. - * keypoints The input keypoints. Keypoints for which a descriptor cannot be computed are removed. - * descriptors Copmputed descriptors. Row i is the descriptor for keypoint i. - */ - CV_WRAP void compute( const Mat& image, CV_OUT CV_IN_OUT vector& keypoints, CV_OUT Mat& descriptors ) const; - - /* - * Compute the descriptors for a keypoints collection detected in image collection. - * images Image collection. - * keypoints Input keypoints collection. keypoints[i] is keypoints detected in images[i]. - * Keypoints for which a descriptor cannot be computed are removed. - * descriptors Descriptor collection. descriptors[i] are descriptors computed for set keypoints[i]. - */ - void compute( const vector& images, vector >& keypoints, vector& descriptors ) const; - - virtual void read( const FileNode& ); - virtual void write( FileStorage& ) const; - - CV_WRAP virtual int descriptorSize() const = 0; - CV_WRAP virtual int descriptorType() const = 0; - - CV_WRAP virtual bool empty() const; - - CV_WRAP static Ptr create( const string& descriptorExtractorType ); - -protected: - virtual void computeImpl( const Mat& image, vector& keypoints, Mat& descriptors ) const = 0; - - /* - * Remove keypoints within borderPixels of an image edge. - */ - static void removeBorderKeypoints( vector& keypoints, - Size imageSize, int borderSize ); -}; - -/* - * SiftDescriptorExtractor - */ -class CV_EXPORTS SiftDescriptorExtractor : public DescriptorExtractor -{ -public: - SiftDescriptorExtractor( const SIFT::DescriptorParams& descriptorParams=SIFT::DescriptorParams(), - const SIFT::CommonParams& commonParams=SIFT::CommonParams() ); - SiftDescriptorExtractor( double magnification, bool isNormalize=true, bool recalculateAngles=true, - int nOctaves=SIFT::CommonParams::DEFAULT_NOCTAVES, - int nOctaveLayers=SIFT::CommonParams::DEFAULT_NOCTAVE_LAYERS, - int firstOctave=SIFT::CommonParams::DEFAULT_FIRST_OCTAVE, - int angleMode=SIFT::CommonParams::FIRST_ANGLE ); - - virtual void read( const FileNode &fn ); - virtual void write( FileStorage &fs ) const; - - virtual int descriptorSize() const; - virtual int descriptorType() const; - -protected: - virtual void computeImpl( const Mat& image, vector& keypoints, Mat& descriptors ) const; - - SIFT sift; -}; - -/* - * SurfDescriptorExtractor - */ -class CV_EXPORTS_W SurfDescriptorExtractor : public DescriptorExtractor -{ -public: - CV_WRAP SurfDescriptorExtractor( int nOctaves=4, int nOctaveLayers=2, bool extended=false, bool upright=false ); - - virtual void read( const FileNode &fn ); - virtual void write( FileStorage &fs ) const; - - virtual int descriptorSize() const; - virtual int descriptorType() const; - -protected: - virtual void computeImpl( const Mat& image, vector& keypoints, Mat& descriptors ) const; - - SURF surf; -}; - -/** The descriptor extractor for the ORB descriptor - * There are two ways to speed up its computation: - * - if you know the step size of the integral image, use setStepSize so that offsets are precomputed and cached - * - if you know the integral image, use setIntegralImage so that it is not recomputed. This calls - * setStepSize automatically - */ -class CV_EXPORTS OrbDescriptorExtractor : public cv::DescriptorExtractor -{ -public: - /** default constructor - * @param params parameters to use - */ - OrbDescriptorExtractor(ORB::CommonParams params = ORB::CommonParams()); - - /** destructor */ - ~OrbDescriptorExtractor() - { - } - - virtual int descriptorSize() const; - virtual int descriptorType() const; - - virtual void read(const cv::FileNode&); - virtual void write(cv::FileStorage&) const; - -protected: - void computeImpl(const cv::Mat& image, std::vector& keypoints, cv::Mat& descriptors) const; -private: - /** the ORB object we use for the computations */ - mutable ORB orb_; -}; - -/* - * CalonderDescriptorExtractor - */ -template -class CV_EXPORTS CalonderDescriptorExtractor : public DescriptorExtractor -{ -public: - CalonderDescriptorExtractor( const string& classifierFile ); - - virtual void read( const FileNode &fn ); - virtual void write( FileStorage &fs ) const; - - virtual int descriptorSize() const { return classifier_.classes(); } - virtual int descriptorType() const { return DataType::type; } - - virtual bool empty() const; - -protected: - virtual void computeImpl( const Mat& image, vector& keypoints, Mat& descriptors ) const; - - RTreeClassifier classifier_; - static const int BORDER_SIZE = 16; -}; - -template -CalonderDescriptorExtractor::CalonderDescriptorExtractor(const std::string& classifier_file) -{ - classifier_.read( classifier_file.c_str() ); -} - -template -void CalonderDescriptorExtractor::computeImpl( const cv::Mat& image, - std::vector& keypoints, - cv::Mat& descriptors) const -{ - // Cannot compute descriptors for keypoints on the image border. - KeyPointsFilter::runByImageBorder(keypoints, image.size(), BORDER_SIZE); - - /// @todo Check 16-byte aligned - descriptors.create(keypoints.size(), classifier_.classes(), cv::DataType::type); - - int patchSize = RandomizedTree::PATCH_SIZE; - int offset = patchSize / 2; - for (size_t i = 0; i < keypoints.size(); ++i) - { - cv::Point2f pt = keypoints[i].pt; - IplImage ipl = image( Rect((int)(pt.x - offset), (int)(pt.y - offset), patchSize, patchSize) ); - classifier_.getSignature( &ipl, descriptors.ptr(i)); - } -} - -template -void CalonderDescriptorExtractor::read( const FileNode& ) -{} - -template -void CalonderDescriptorExtractor::write( FileStorage& ) const -{} - -template -bool CalonderDescriptorExtractor::empty() const -{ - return classifier_.trees_.empty(); -} /* * OpponentColorDescriptorExtractor @@ -2064,9 +725,9 @@ public: /// @todo read and write for brief protected: - virtual void computeImpl(const Mat& image, std::vector& keypoints, Mat& descriptors) const; + virtual void computeImpl(const Mat& image, vector& keypoints, Mat& descriptors) const; - typedef void(*PixelTestFn)(const Mat&, const std::vector&, Mat&); + typedef void(*PixelTestFn)(const Mat&, const vector&, Mat&); int bytes_; PixelTestFn test_fn_; @@ -2169,7 +830,7 @@ template struct CV_EXPORTS HammingMultilevel */ struct CV_EXPORTS_W_SIMPLE DMatch { - CV_WRAP DMatch() : queryIdx(-1), trainIdx(-1), imgIdx(-1), distance(std::numeric_limits::max()) {} + CV_WRAP DMatch() : queryIdx(-1), trainIdx(-1), imgIdx(-1), distance(FLT_MAX) {} CV_WRAP DMatch( int _queryIdx, int _trainIdx, float _distance ) : queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(-1), distance(_distance) {} CV_WRAP DMatch( int _queryIdx, int _trainIdx, int _imgIdx, float _distance ) : @@ -2194,7 +855,7 @@ struct CV_EXPORTS_W_SIMPLE DMatch /* * Abstract base class for matching two sets of descriptors. */ -class CV_EXPORTS_W DescriptorMatcher +class CV_EXPORTS_W DescriptorMatcher : public Algorithm { public: virtual ~DescriptorMatcher(); @@ -2330,12 +991,11 @@ protected: * For efficiency, BruteForceMatcher is templated on the distance metric. * For float descriptors, a common choice would be cv::L2. */ -template -class CV_EXPORTS BruteForceMatcher : public DescriptorMatcher +class CV_EXPORTS BFMatcher : public DescriptorMatcher { public: - BruteForceMatcher( Distance d = Distance() ) : distance(d) {} - virtual ~BruteForceMatcher() {} + BFMatcher( int normType, bool crossCheck=false ); + virtual ~BFMatcher() {} virtual bool isMaskSupported() const { return true; } @@ -2347,183 +1007,10 @@ protected: virtual void radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, float maxDistance, const vector& masks=vector(), bool compactResult=false ); - Distance distance; - -private: - /* - * Next two methods are used to implement specialization. - */ - static void commonKnnMatchImpl( BruteForceMatcher& matcher, - const Mat& queryDescriptors, vector >& matches, int k, - const vector& masks, bool compactResult ); - static void commonRadiusMatchImpl( BruteForceMatcher& matcher, - const Mat& queryDescriptors, vector >& matches, float maxDistance, - const vector& masks, bool compactResult ); + int normType; + bool crossCheck; }; -template -Ptr BruteForceMatcher::clone( bool emptyTrainData ) const -{ - BruteForceMatcher* matcher = new BruteForceMatcher(distance); - if( !emptyTrainData ) - { - matcher->trainDescCollection.resize(trainDescCollection.size()); - std::transform( trainDescCollection.begin(), trainDescCollection.end(), - matcher->trainDescCollection.begin(), clone_op ); - } - return matcher; -} - -template -void BruteForceMatcher::knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int k, - const vector& masks, bool compactResult ) -{ - commonKnnMatchImpl( *this, queryDescriptors, matches, k, masks, compactResult ); -} - -template -void BruteForceMatcher::radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, - float maxDistance, const vector& masks, bool compactResult ) -{ - commonRadiusMatchImpl( *this, queryDescriptors, matches, maxDistance, masks, compactResult ); -} - -template -inline void BruteForceMatcher::commonKnnMatchImpl( BruteForceMatcher& matcher, - const Mat& queryDescriptors, vector >& matches, int knn, - const vector& masks, bool compactResult ) -{ - typedef typename Distance::ValueType ValueType; - typedef typename Distance::ResultType DistanceType; - CV_DbgAssert( !queryDescriptors.empty() ); - CV_Assert( DataType::type == queryDescriptors.type() ); - - int dimension = queryDescriptors.cols; - matches.reserve(queryDescriptors.rows); - - size_t imgCount = matcher.trainDescCollection.size(); - vector allDists( imgCount ); // distances between one query descriptor and all train descriptors - for( size_t i = 0; i < imgCount; i++ ) - allDists[i] = Mat( 1, matcher.trainDescCollection[i].rows, DataType::type ); - - for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ ) - { - if( matcher.isMaskedOut( masks, qIdx ) ) - { - if( !compactResult ) // push empty vector - matches.push_back( vector() ); - } - else - { - // 1. compute distances between i-th query descriptor and all train descriptors - for( size_t iIdx = 0; iIdx < imgCount; iIdx++ ) - { - CV_Assert( DataType::type == matcher.trainDescCollection[iIdx].type() || matcher.trainDescCollection[iIdx].empty() ); - CV_Assert( queryDescriptors.cols == matcher.trainDescCollection[iIdx].cols || - matcher.trainDescCollection[iIdx].empty() ); - - const ValueType* d1 = (const ValueType*)(queryDescriptors.data + queryDescriptors.step*qIdx); - allDists[iIdx].setTo( Scalar::all(std::numeric_limits::max()) ); - for( int tIdx = 0; tIdx < matcher.trainDescCollection[iIdx].rows; tIdx++ ) - { - if( masks.empty() || matcher.isPossibleMatch(masks[iIdx], qIdx, tIdx) ) - { - const ValueType* d2 = (const ValueType*)(matcher.trainDescCollection[iIdx].data + - matcher.trainDescCollection[iIdx].step*tIdx); - allDists[iIdx].at(0, tIdx) = matcher.distance(d1, d2, dimension); - } - } - } - - // 2. choose k nearest matches for query[i] - matches.push_back( vector() ); - vector >::reverse_iterator curMatches = matches.rbegin(); - for( int k = 0; k < knn; k++ ) - { - DMatch bestMatch; - bestMatch.distance = std::numeric_limits::max(); - for( size_t iIdx = 0; iIdx < imgCount; iIdx++ ) - { - if( !allDists[iIdx].empty() ) - { - double minVal; - Point minLoc; - minMaxLoc( allDists[iIdx], &minVal, 0, &minLoc, 0 ); - if( minVal < bestMatch.distance ) - bestMatch = DMatch( qIdx, minLoc.x, (int)iIdx, (float)minVal ); - } - } - if( bestMatch.trainIdx == -1 ) - break; - - allDists[bestMatch.imgIdx].at(0, bestMatch.trainIdx) = std::numeric_limits::max(); - curMatches->push_back( bestMatch ); - } - //TODO should already be sorted at this point? - std::sort( curMatches->begin(), curMatches->end() ); - } - } -} - -template -inline void BruteForceMatcher::commonRadiusMatchImpl( BruteForceMatcher& matcher, - const Mat& queryDescriptors, vector >& matches, float maxDistance, - const vector& masks, bool compactResult ) -{ - typedef typename Distance::ValueType ValueType; - typedef typename Distance::ResultType DistanceType; - CV_DbgAssert( !queryDescriptors.empty() ); - CV_Assert( DataType::type == queryDescriptors.type() ); - - int dimension = queryDescriptors.cols; - matches.reserve(queryDescriptors.rows); - - size_t imgCount = matcher.trainDescCollection.size(); - for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ ) - { - if( matcher.isMaskedOut( masks, qIdx ) ) - { - if( !compactResult ) // push empty vector - matches.push_back( vector() ); - } - else - { - matches.push_back( vector() ); - vector >::reverse_iterator curMatches = matches.rbegin(); - for( size_t iIdx = 0; iIdx < imgCount; iIdx++ ) - { - CV_Assert( DataType::type == matcher.trainDescCollection[iIdx].type() || - matcher.trainDescCollection[iIdx].empty() ); - CV_Assert( queryDescriptors.cols == matcher.trainDescCollection[iIdx].cols || - matcher.trainDescCollection[iIdx].empty() ); - - const ValueType* d1 = (const ValueType*)(queryDescriptors.data + queryDescriptors.step*qIdx); - for( int tIdx = 0; tIdx < matcher.trainDescCollection[iIdx].rows; tIdx++ ) - { - if( masks.empty() || matcher.isPossibleMatch(masks[iIdx], qIdx, tIdx) ) - { - const ValueType* d2 = (const ValueType*)(matcher.trainDescCollection[iIdx].data + - matcher.trainDescCollection[iIdx].step*tIdx); - DistanceType d = matcher.distance(d1, d2, dimension); - if( d < maxDistance ) - curMatches->push_back( DMatch( qIdx, tIdx, (int)iIdx, (float)d ) ); - } - } - } - std::sort( curMatches->begin(), curMatches->end() ); - } - } -} - -/* - * BruteForceMatcher L2 specialization - */ -template<> -void BruteForceMatcher >::knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int k, - const vector& masks, bool compactResult ); -template<> -void BruteForceMatcher >::radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, - float maxDistance, const vector& masks, bool compactResult ); /* * Flann based matcher @@ -2724,144 +1211,6 @@ protected: KeyPointCollection trainPointCollection; }; -/* - * OneWayDescriptorMatcher - */ -class OneWayDescriptorMatcher; -typedef OneWayDescriptorMatcher OneWayDescriptorMatch; - -class CV_EXPORTS OneWayDescriptorMatcher : public GenericDescriptorMatcher -{ -public: - class CV_EXPORTS Params - { - public: - static const int POSE_COUNT = 500; - static const int PATCH_WIDTH = 24; - static const int PATCH_HEIGHT = 24; - static float GET_MIN_SCALE() { return 0.7f; } - static float GET_MAX_SCALE() { return 1.5f; } - static float GET_STEP_SCALE() { return 1.2f; } - - Params( int poseCount = POSE_COUNT, - Size patchSize = Size(PATCH_WIDTH, PATCH_HEIGHT), - string pcaFilename = string(), - string trainPath = string(), string trainImagesList = string(), - float minScale = GET_MIN_SCALE(), float maxScale = GET_MAX_SCALE(), - float stepScale = GET_STEP_SCALE() ); - - int poseCount; - Size patchSize; - string pcaFilename; - string trainPath; - string trainImagesList; - - float minScale, maxScale, stepScale; - }; - - OneWayDescriptorMatcher( const Params& params=Params() ); - virtual ~OneWayDescriptorMatcher(); - - void initialize( const Params& params, const Ptr& base=Ptr() ); - - // Clears keypoints storing in collection and OneWayDescriptorBase - virtual void clear(); - - virtual void train(); - - virtual bool isMaskSupported(); - - virtual void read( const FileNode &fn ); - virtual void write( FileStorage& fs ) const; - - virtual bool empty() const; - - virtual Ptr clone( bool emptyTrainData=false ) const; - -protected: - // Matches a set of keypoints from a single image of the training set. A rectangle with a center in a keypoint - // and size (patch_width/2*scale, patch_height/2*scale) is cropped from the source image for each - // keypoint. scale is iterated from DescriptorOneWayParams::min_scale to DescriptorOneWayParams::max_scale. - // The minimum distance to each training patch with all its affine poses is found over all scales. - // The class ID of a match is returned for each keypoint. The distance is calculated over PCA components - // loaded with DescriptorOneWay::Initialize, kd tree is used for finding minimum distances. - virtual void knnMatchImpl( const Mat& queryImage, vector& queryKeypoints, - vector >& matches, int k, - const vector& masks, bool compactResult ); - virtual void radiusMatchImpl( const Mat& queryImage, vector& queryKeypoints, - vector >& matches, float maxDistance, - const vector& masks, bool compactResult ); - - Ptr base; - Params params; - int prevTrainCount; -}; - -/* - * FernDescriptorMatcher - */ -class FernDescriptorMatcher; -typedef FernDescriptorMatcher FernDescriptorMatch; - -class CV_EXPORTS FernDescriptorMatcher : public GenericDescriptorMatcher -{ -public: - class CV_EXPORTS Params - { - public: - Params( int nclasses=0, - int patchSize=FernClassifier::PATCH_SIZE, - int signatureSize=FernClassifier::DEFAULT_SIGNATURE_SIZE, - int nstructs=FernClassifier::DEFAULT_STRUCTS, - int structSize=FernClassifier::DEFAULT_STRUCT_SIZE, - int nviews=FernClassifier::DEFAULT_VIEWS, - int compressionMethod=FernClassifier::COMPRESSION_NONE, - const PatchGenerator& patchGenerator=PatchGenerator() ); - - Params( const string& filename ); - - int nclasses; - int patchSize; - int signatureSize; - int nstructs; - int structSize; - int nviews; - int compressionMethod; - PatchGenerator patchGenerator; - - string filename; - }; - - FernDescriptorMatcher( const Params& params=Params() ); - virtual ~FernDescriptorMatcher(); - - virtual void clear(); - - virtual void train(); - - virtual bool isMaskSupported(); - - virtual void read( const FileNode &fn ); - virtual void write( FileStorage& fs ) const; - virtual bool empty() const; - - virtual Ptr clone( bool emptyTrainData=false ) const; - -protected: - virtual void knnMatchImpl( const Mat& queryImage, vector& queryKeypoints, - vector >& matches, int k, - const vector& masks, bool compactResult ); - virtual void radiusMatchImpl( const Mat& queryImage, vector& queryKeypoints, - vector >& matches, float maxDistance, - const vector& masks, bool compactResult ); - - void trainFernClassifier(); - void calcBestProbAndMatchIdx( const Mat& image, const Point2f& pt, - float& bestProb, int& bestMatchIdx, vector& signature ); - Ptr classifier; - Params params; - int prevTrainCount; -}; /****************************************************************************************\ * VectorDescriptorMatcher * diff --git a/modules/features2d/perf/perf_orb.cpp b/modules/features2d/perf/perf_orb.cpp index cb8b6c09f0..086ee3a910 100644 --- a/modules/features2d/perf/perf_orb.cpp +++ b/modules/features2d/perf/perf_orb.cpp @@ -22,7 +22,7 @@ PERF_TEST_P(orb, detect, testing::Values(ORB_IMAGES)) Mat mask; declare.in(frame); - ORB detector(1500, ORB::CommonParams(1.3f, 5)); + ORB detector(1500, 1.3f, 5); vector points; TEST_CYCLE() detector(frame, mask, points); @@ -39,7 +39,7 @@ PERF_TEST_P(orb, extract, testing::Values(ORB_IMAGES)) Mat mask; declare.in(frame); - ORB detector(1500, ORB::CommonParams(1.3f, 5)); + ORB detector(1500, 1.3f, 5); vector points; detector(frame, mask, points); @@ -58,7 +58,7 @@ PERF_TEST_P(orb, full, testing::Values(ORB_IMAGES)) Mat mask; declare.in(frame); - ORB detector(1500, ORB::CommonParams(1.3f, 5)); + ORB detector(1500, 1.3f, 5); vector points; Mat descriptors; diff --git a/modules/features2d/src/descriptors.cpp b/modules/features2d/src/descriptors.cpp index 30ddfb2965..6faddd500d 100644 --- a/modules/features2d/src/descriptors.cpp +++ b/modules/features2d/src/descriptors.cpp @@ -77,11 +77,11 @@ void DescriptorExtractor::compute( const vector& imageCollection, vector& keypoints, Ptr DescriptorExtractor::create(const string& descriptorExtractorType) { - DescriptorExtractor* de = 0; - - size_t pos = 0; - if (!descriptorExtractorType.compare("SIFT")) + if( descriptorExtractorType.find("Opponent") == 0) { - de = new SiftDescriptorExtractor(); + size_t pos = string("Opponent").size(); + return DescriptorExtractor::create(descriptorExtractorType.substr(pos)); } - else if (!descriptorExtractorType.compare("SURF")) - { - de = new SurfDescriptorExtractor(); - } - else if (!descriptorExtractorType.compare("ORB")) - { - de = new OrbDescriptorExtractor(); - } - else if (!descriptorExtractorType.compare("BRIEF")) - { - de = new BriefDescriptorExtractor(); - } - else if ( (pos=descriptorExtractorType.find("Opponent")) == 0) - { - pos += string("Opponent").size(); - de = new OpponentColorDescriptorExtractor( DescriptorExtractor::create(descriptorExtractorType.substr(pos)) ); - } - return de; + + return Algorithm::create("Feature2D." + descriptorExtractorType); } -/****************************************************************************************\ -* SiftDescriptorExtractor * -\****************************************************************************************/ -SiftDescriptorExtractor::SiftDescriptorExtractor(const SIFT::DescriptorParams& descriptorParams, - const SIFT::CommonParams& commonParams) - : sift( descriptorParams.magnification, descriptorParams.isNormalize, descriptorParams.recalculateAngles, - commonParams.nOctaves, commonParams.nOctaveLayers, commonParams.firstOctave, commonParams.angleMode ) -{} - -SiftDescriptorExtractor::SiftDescriptorExtractor( double magnification, bool isNormalize, bool recalculateAngles, - int nOctaves, int nOctaveLayers, int firstOctave, int angleMode ) - : sift( magnification, isNormalize, recalculateAngles, nOctaves, nOctaveLayers, firstOctave, angleMode ) -{} - -void SiftDescriptorExtractor::computeImpl( const Mat& image, - vector& keypoints, - Mat& descriptors) const -{ - bool useProvidedKeypoints = true; - Mat grayImage = image; - if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); - - sift(grayImage, Mat(), keypoints, descriptors, useProvidedKeypoints); -} - -void SiftDescriptorExtractor::read (const FileNode &fn) -{ - double magnification = fn["magnification"]; - bool isNormalize = (int)fn["isNormalize"] != 0; - bool recalculateAngles = (int)fn["recalculateAngles"] != 0; - int nOctaves = fn["nOctaves"]; - int nOctaveLayers = fn["nOctaveLayers"]; - int firstOctave = fn["firstOctave"]; - int angleMode = fn["angleMode"]; - - sift = SIFT( magnification, isNormalize, recalculateAngles, nOctaves, nOctaveLayers, firstOctave, angleMode ); -} - -void SiftDescriptorExtractor::write (FileStorage &fs) const -{ -// fs << "algorithm" << getAlgorithmName (); - - SIFT::CommonParams commParams = sift.getCommonParams (); - SIFT::DescriptorParams descriptorParams = sift.getDescriptorParams (); - fs << "magnification" << descriptorParams.magnification; - fs << "isNormalize" << descriptorParams.isNormalize; - fs << "recalculateAngles" << descriptorParams.recalculateAngles; - fs << "nOctaves" << commParams.nOctaves; - fs << "nOctaveLayers" << commParams.nOctaveLayers; - fs << "firstOctave" << commParams.firstOctave; - fs << "angleMode" << commParams.angleMode; -} - -int SiftDescriptorExtractor::descriptorSize() const -{ - return sift.descriptorSize(); -} - -int SiftDescriptorExtractor::descriptorType() const -{ - return CV_32FC1; -} - -/****************************************************************************************\ -* SurfDescriptorExtractor * -\****************************************************************************************/ -SurfDescriptorExtractor::SurfDescriptorExtractor( int nOctaves, - int nOctaveLayers, bool extended, bool upright ) - : surf( 0.0, nOctaves, nOctaveLayers, extended, upright ) -{} - -void SurfDescriptorExtractor::computeImpl( const Mat& image, - vector& keypoints, - Mat& descriptors) const -{ - // Compute descriptors for given keypoints - vector _descriptors; - Mat mask; - bool useProvidedKeypoints = true; - Mat grayImage = image; - if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); - - surf(grayImage, mask, keypoints, _descriptors, useProvidedKeypoints); - - descriptors.create((int)keypoints.size(), (int)surf.descriptorSize(), CV_32FC1); - assert( (int)_descriptors.size() == descriptors.rows * descriptors.cols ); - std::copy(_descriptors.begin(), _descriptors.end(), descriptors.begin()); -} - -void SurfDescriptorExtractor::read( const FileNode &fn ) -{ - int nOctaves = fn["nOctaves"]; - int nOctaveLayers = fn["nOctaveLayers"]; - bool extended = (int)fn["extended"] != 0; - bool upright = (int)fn["upright"] != 0; - - surf = SURF( 0.0, nOctaves, nOctaveLayers, extended, upright ); -} - -void SurfDescriptorExtractor::write( FileStorage &fs ) const -{ -// fs << "algorithm" << getAlgorithmName (); - - fs << "nOctaves" << surf.nOctaves; - fs << "nOctaveLayers" << surf.nOctaveLayers; - fs << "extended" << surf.extended; - fs << "upright" << surf.upright; -} - -int SurfDescriptorExtractor::descriptorSize() const -{ - return surf.descriptorSize(); -} - -int SurfDescriptorExtractor::descriptorType() const -{ - return CV_32FC1; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - -/** Default constructor */ -OrbDescriptorExtractor::OrbDescriptorExtractor(ORB::CommonParams params) -{ - orb_ = ORB(0, params); -} -void OrbDescriptorExtractor::computeImpl(const cv::Mat& image, std::vector& keypoints, - cv::Mat& descriptors) const -{ - cv::Mat empty_mask; - orb_(image, empty_mask, keypoints, descriptors, true); -} -void OrbDescriptorExtractor::read(const cv::FileNode& fn) -{ - orb_.read(fn); -} -void OrbDescriptorExtractor::write(cv::FileStorage& fs) const -{ - orb_.write(fs); -} -int OrbDescriptorExtractor::descriptorSize() const -{ - return orb_.descriptorSize(); -} -int OrbDescriptorExtractor::descriptorType() const -{ - return CV_8UC1; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// /****************************************************************************************\ * OpponentColorDescriptorExtractor * diff --git a/modules/features2d/src/detectors.cpp b/modules/features2d/src/detectors.cpp index 487b135a5c..f68c5e58d1 100644 --- a/modules/features2d/src/detectors.cpp +++ b/modules/features2d/src/detectors.cpp @@ -45,6 +45,7 @@ using namespace std; namespace cv { + /* * FeatureDetector */ @@ -71,11 +72,11 @@ void FeatureDetector::detect(const vector& imageCollection, vector& ke Ptr FeatureDetector::create( const string& detectorType ) { - FeatureDetector* fd = 0; - size_t pos = 0; - - if( !detectorType.compare( "FAST" ) ) + if( detectorType.find("Grid") == 0 ) { - fd = new FastFeatureDetector(); + return new GridAdaptedFeatureDetector(FeatureDetector::create( + detectorType.substr(strlen("Grid")))); } - else if( !detectorType.compare( "STAR" ) ) + + if( detectorType.find("Pyramid") == 0 ) { - fd = new StarFeatureDetector(); + return new PyramidAdaptedFeatureDetector(FeatureDetector::create( + detectorType.substr(strlen("Pyramid")))); } - else if( !detectorType.compare( "SIFT" ) ) + + if( detectorType.find("Dynamic") == 0 ) { - fd = new SiftFeatureDetector(); + return new DynamicAdaptedFeatureDetector(AdjusterAdapter::create( + detectorType.substr(strlen("Dynamic")))); } - else if( !detectorType.compare( "SURF" ) ) + + if( detectorType.compare( "HARRIS" ) == 0 ) { - fd = new SurfFeatureDetector(); - } - else if( !detectorType.compare( "ORB" ) ) - { - fd = new OrbFeatureDetector(); - } - else if( !detectorType.compare( "MSER" ) ) - { - fd = new MserFeatureDetector(); - } - else if( !detectorType.compare( "GFTT" ) ) - { - fd = new GoodFeaturesToTrackDetector(); - } - else if( !detectorType.compare( "HARRIS" ) ) - { - GoodFeaturesToTrackDetector::Params params; - params.useHarrisDetector = true; - fd = new GoodFeaturesToTrackDetector(params); - } - else if( !detectorType.compare( "Dense" ) ) - { - fd = new DenseFeatureDetector(); - } - else if( !detectorType.compare( "SimpleBlob" ) ) - { - fd = new SimpleBlobDetector(); - } - else if( (pos=detectorType.find("Grid")) == 0 ) - { - pos += string("Grid").size(); - fd = new GridAdaptedFeatureDetector( FeatureDetector::create(detectorType.substr(pos)) ); - } - else if( (pos=detectorType.find("Pyramid")) == 0 ) - { - pos += string("Pyramid").size(); - fd = new PyramidAdaptedFeatureDetector( FeatureDetector::create(detectorType.substr(pos)) ); - } - else if( (pos=detectorType.find("Dynamic")) == 0 ) - { - pos += string("Dynamic").size(); - fd = new DynamicAdaptedFeatureDetector( AdjusterAdapter::create(detectorType.substr(pos)) ); + Ptr fd = FeatureDetector::create("GFTT"); + fd->set("useHarrisDetector", true); + return fd; } - return fd; + return Algorithm::create("Feature2D." + detectorType); } -/* - * FastFeatureDetector - */ -FastFeatureDetector::FastFeatureDetector( int _threshold, bool _nonmaxSuppression ) - : threshold(_threshold), nonmaxSuppression(_nonmaxSuppression) -{} -void FastFeatureDetector::read (const FileNode& fn) -{ - threshold = fn["threshold"]; - nonmaxSuppression = (int)fn["nonmaxSuppression"] ? true : false; -} - -void FastFeatureDetector::write (FileStorage& fs) const -{ - fs << "threshold" << threshold; - fs << "nonmaxSuppression" << nonmaxSuppression; -} - -void FastFeatureDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask ) const -{ - Mat grayImage = image; - if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); - FAST( grayImage, keypoints, threshold, nonmaxSuppression ); - KeyPointsFilter::runByPixelsMask( keypoints, mask ); -} - -/* - * GoodFeaturesToTrackDetector - */ -GoodFeaturesToTrackDetector::Params::Params( int _maxCorners, double _qualityLevel, double _minDistance, - int _blockSize, bool _useHarrisDetector, double _k ) : - maxCorners(_maxCorners), qualityLevel(_qualityLevel), minDistance(_minDistance), +GFTTDetector::GFTTDetector( int _nfeatures, double _qualityLevel, + double _minDistance, int _blockSize, + bool _useHarrisDetector, double _k ) + : nfeatures(_nfeatures), qualityLevel(_qualityLevel), minDistance(_minDistance), blockSize(_blockSize), useHarrisDetector(_useHarrisDetector), k(_k) -{} - -void GoodFeaturesToTrackDetector::Params::read (const FileNode& fn) { - maxCorners = fn["maxCorners"]; - qualityLevel = fn["qualityLevel"]; - minDistance = fn["minDistance"]; - blockSize = fn["blockSize"]; - useHarrisDetector = (int)fn["useHarrisDetector"] != 0; - k = fn["k"]; } -void GoodFeaturesToTrackDetector::Params::write (FileStorage& fs) const -{ - fs << "maxCorners" << maxCorners; - fs << "qualityLevel" << qualityLevel; - fs << "minDistance" << minDistance; - fs << "blockSize" << blockSize; - fs << "useHarrisDetector" << useHarrisDetector; - fs << "k" << k; -} - -GoodFeaturesToTrackDetector::GoodFeaturesToTrackDetector( const Params& _params ) : params(_params) -{} - -GoodFeaturesToTrackDetector::GoodFeaturesToTrackDetector( int maxCorners, double qualityLevel, - double minDistance, int blockSize, - bool useHarrisDetector, double k ) -{ - params = Params( maxCorners, qualityLevel, minDistance, blockSize, useHarrisDetector, k ); -} - -void GoodFeaturesToTrackDetector::read (const FileNode& fn) -{ - params.read(fn); -} - -void GoodFeaturesToTrackDetector::write (FileStorage& fs) const -{ - params.write(fs); -} - -void GoodFeaturesToTrackDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask) const +void GFTTDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask) const { Mat grayImage = image; if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); vector corners; - goodFeaturesToTrack( grayImage, corners, params.maxCorners, params.qualityLevel, params.minDistance, mask, - params.blockSize, params.useHarrisDetector, params.k ); + goodFeaturesToTrack( grayImage, corners, nfeatures, qualityLevel, minDistance, mask, + blockSize, useHarrisDetector, k ); keypoints.resize(corners.size()); vector::const_iterator corner_it = corners.begin(); vector::iterator keypoint_it = keypoints.begin(); for( ; corner_it != corners.end(); ++corner_it, ++keypoint_it ) { - *keypoint_it = KeyPoint( *corner_it, (float)params.blockSize ); + *keypoint_it = KeyPoint( *corner_it, (float)blockSize ); } } -/* - * MserFeatureDetector - */ -MserFeatureDetector::MserFeatureDetector( int delta, int minArea, int maxArea, - double maxVariation, double minDiversity, - int maxEvolution, double areaThreshold, - double minMargin, int edgeBlurSize ) - : mser( delta, minArea, maxArea, maxVariation, minDiversity, - maxEvolution, areaThreshold, minMargin, edgeBlurSize ) -{} - -MserFeatureDetector::MserFeatureDetector( CvMSERParams params ) - : mser( params.delta, params.minArea, params.maxArea, params.maxVariation, params.minDiversity, - params.maxEvolution, params.areaThreshold, params.minMargin, params.edgeBlurSize ) -{} - -void MserFeatureDetector::read (const FileNode& fn) +static Algorithm* createGFTT() { return new GFTTDetector; } +static Algorithm* createHarris() { - int delta = fn["delta"]; - int minArea = fn["minArea"]; - int maxArea = fn["maxArea"]; - float maxVariation = fn["maxVariation"]; - float minDiversity = fn["minDiversity"]; - int maxEvolution = fn["maxEvolution"]; - double areaThreshold = fn["areaThreshold"]; - double minMargin = fn["minMargin"]; - int edgeBlurSize = fn["edgeBlurSize"]; - - mser = MSER( delta, minArea, maxArea, maxVariation, minDiversity, - maxEvolution, areaThreshold, minMargin, edgeBlurSize ); + GFTTDetector* d = new GFTTDetector; + d->set("useHarris", true); + return d; } -void MserFeatureDetector::write (FileStorage& fs) const +static AlgorithmInfo gftt_info("Feature2D.GFTT", createGFTT); +static AlgorithmInfo harris_info("Feature2D.HARRIS", createHarris); + +AlgorithmInfo* GFTTDetector::info() const { - //fs << "algorithm" << getAlgorithmName (); - - fs << "delta" << mser.delta; - fs << "minArea" << mser.minArea; - fs << "maxArea" << mser.maxArea; - fs << "maxVariation" << mser.maxVariation; - fs << "minDiversity" << mser.minDiversity; - fs << "maxEvolution" << mser.maxEvolution; - fs << "areaThreshold" << mser.areaThreshold; - fs << "minMargin" << mser.minMargin; - fs << "edgeBlurSize" << mser.edgeBlurSize; -} - - -void MserFeatureDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask ) const -{ - vector > msers; - - mser(image, msers, mask); - - vector >::const_iterator contour_it = msers.begin(); - for( ; contour_it != msers.end(); ++contour_it ) + static volatile bool initialized = false; + if( !initialized ) { - // TODO check transformation from MSER region to KeyPoint - RotatedRect rect = fitEllipse(Mat(*contour_it)); - float diam = sqrt(rect.size.height*rect.size.width); - - if( diam > std::numeric_limits::epsilon() ) - keypoints.push_back( KeyPoint( rect.center, diam, rect.angle) ); + gftt_info.addParam(this, "nfeatures", nfeatures); + gftt_info.addParam(this, "qualityLevel", qualityLevel); + gftt_info.addParam(this, "minDistance", minDistance); + gftt_info.addParam(this, "useHarrisDetector", useHarrisDetector); + gftt_info.addParam(this, "k", k); + + harris_info.addParam(this, "nfeatures", nfeatures); + harris_info.addParam(this, "qualityLevel", qualityLevel); + harris_info.addParam(this, "minDistance", minDistance); + harris_info.addParam(this, "useHarrisDetector", useHarrisDetector); + harris_info.addParam(this, "k", k); + + initialized = true; } -} - -/* - * StarFeatureDetector - */ - -StarFeatureDetector::StarFeatureDetector( const CvStarDetectorParams& params ) - : star( params.maxSize, params.responseThreshold, params.lineThresholdProjected, - params.lineThresholdBinarized, params.suppressNonmaxSize) -{} - -StarFeatureDetector::StarFeatureDetector(int maxSize, int responseThreshold, - int lineThresholdProjected, - int lineThresholdBinarized, - int suppressNonmaxSize) - : star( maxSize, responseThreshold, lineThresholdProjected, - lineThresholdBinarized, suppressNonmaxSize) -{} - -void StarFeatureDetector::read (const FileNode& fn) -{ - int maxSize = fn["maxSize"]; - int responseThreshold = fn["responseThreshold"]; - int lineThresholdProjected = fn["lineThresholdProjected"]; - int lineThresholdBinarized = fn["lineThresholdBinarized"]; - int suppressNonmaxSize = fn["suppressNonmaxSize"]; - - star = StarDetector( maxSize, responseThreshold, lineThresholdProjected, - lineThresholdBinarized, suppressNonmaxSize); -} - -void StarFeatureDetector::write (FileStorage& fs) const -{ - //fs << "algorithm" << getAlgorithmName (); - - fs << "maxSize" << star.maxSize; - fs << "responseThreshold" << star.responseThreshold; - fs << "lineThresholdProjected" << star.lineThresholdProjected; - fs << "lineThresholdBinarized" << star.lineThresholdBinarized; - fs << "suppressNonmaxSize" << star.suppressNonmaxSize; -} - -void StarFeatureDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask ) const -{ - Mat grayImage = image; - if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); - - star(grayImage, keypoints); - KeyPointsFilter::runByPixelsMask( keypoints, mask ); -} - -/* - * SiftFeatureDetector - */ -SiftFeatureDetector::SiftFeatureDetector( const SIFT::DetectorParams &detectorParams, - const SIFT::CommonParams &commonParams ) - : sift(detectorParams.threshold, detectorParams.edgeThreshold, - commonParams.nOctaves, commonParams.nOctaveLayers, commonParams.firstOctave, commonParams.angleMode) -{ -} - -SiftFeatureDetector::SiftFeatureDetector( double threshold, double edgeThreshold, - int nOctaves, int nOctaveLayers, int firstOctave, int angleMode ) : - sift(threshold, edgeThreshold, nOctaves, nOctaveLayers, firstOctave, angleMode) -{ -} - -void SiftFeatureDetector::read( const FileNode& fn ) -{ - double threshold = fn["threshold"]; - double edgeThreshold = fn["edgeThreshold"]; - int nOctaves = fn["nOctaves"]; - int nOctaveLayers = fn["nOctaveLayers"]; - int firstOctave = fn["firstOctave"]; - int angleMode = fn["angleMode"]; - - sift = SIFT(threshold, edgeThreshold, nOctaves, nOctaveLayers, firstOctave, angleMode); -} - -void SiftFeatureDetector::write (FileStorage& fs) const -{ - //fs << "algorithm" << getAlgorithmName (); - - SIFT::CommonParams commParams = sift.getCommonParams (); - SIFT::DetectorParams detectorParams = sift.getDetectorParams (); - fs << "threshold" << detectorParams.threshold; - fs << "edgeThreshold" << detectorParams.edgeThreshold; - fs << "nOctaves" << commParams.nOctaves; - fs << "nOctaveLayers" << commParams.nOctaveLayers; - fs << "firstOctave" << commParams.firstOctave; - fs << "angleMode" << commParams.angleMode; -} - - -void SiftFeatureDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask ) const -{ - Mat grayImage = image; - if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); - - sift(grayImage, mask, keypoints); -} - -/* - * SurfFeatureDetector - */ -SurfFeatureDetector::SurfFeatureDetector( double hessianThreshold, int octaves, int octaveLayers, bool upright ) - : surf(hessianThreshold, octaves, octaveLayers, false, upright) -{} - -void SurfFeatureDetector::read (const FileNode& fn) -{ - double hessianThreshold = fn["hessianThreshold"]; - int octaves = fn["octaves"]; - int octaveLayers = fn["octaveLayers"]; - bool upright = (int)fn["upright"] != 0; - - surf = SURF( hessianThreshold, octaves, octaveLayers, false, upright ); -} - -void SurfFeatureDetector::write (FileStorage& fs) const -{ - //fs << "algorithm" << getAlgorithmName (); - - fs << "hessianThreshold" << surf.hessianThreshold; - fs << "octaves" << surf.nOctaves; - fs << "octaveLayers" << surf.nOctaveLayers; - fs << "upright" << surf.upright; -} - -void SurfFeatureDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask ) const -{ - Mat grayImage = image; - if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); - - surf(grayImage, mask, keypoints); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** Default constructor - * @param n_features the number of desired features - */ -OrbFeatureDetector::OrbFeatureDetector(size_t n_features, ORB::CommonParams params) -{ - orb_ = ORB(n_features, params); -} - -void OrbFeatureDetector::read(const FileNode& fn) -{ - orb_.read(fn); -} - -void OrbFeatureDetector::write(FileStorage& fs) const -{ - orb_.write(fs); -} - -void OrbFeatureDetector::detectImpl(const cv::Mat& image, std::vector& keypoints, const cv::Mat& mask) const -{ - orb_(image, mask, keypoints); + return &gftt_info; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -477,7 +182,7 @@ void OrbFeatureDetector::detectImpl(const cv::Mat& image, std::vector& keypoints, const Mat& mask ) const { - float curScale = params.initFeatureScale; - int curStep = params.initXyStep; - int curBound = params.initImgBound; - for( int curLevel = 0; curLevel < params.featureScaleLevels; curLevel++ ) + float curScale = initFeatureScale; + int curStep = initXyStep; + int curBound = initImgBound; + for( int curLevel = 0; curLevel < featureScaleLevels; curLevel++ ) { for( int x = curBound; x < image.cols - curBound; x += curStep ) { @@ -540,13 +207,35 @@ void DenseFeatureDetector::detectImpl( const Mat& image, vector& keypo } } - curScale = curScale * params.featureScaleMul; - if( params.varyXyStepWithScale ) curStep = static_cast( curStep * params.featureScaleMul + 0.5f ); - if( params.varyImgBoundWithScale ) curBound = static_cast( curBound * params.featureScaleMul + 0.5f ); + curScale = curScale * featureScaleMul; + if( varyXyStepWithScale ) curStep = static_cast( curStep * featureScaleMul + 0.5f ); + if( varyImgBoundWithScale ) curBound = static_cast( curBound * featureScaleMul + 0.5f ); } KeyPointsFilter::runByPixelsMask( keypoints, mask ); } + + +static Algorithm* createDense() { return new DenseFeatureDetector; } + +AlgorithmInfo* DenseFeatureDetector::info() const +{ + static AlgorithmInfo info_("Feature2D.Dense", createDense); + static volatile bool initialized = false; + if( !initialized ) + { + info_.addParam(this, "initFeatureScale", initFeatureScale); + info_.addParam(this, "featureScaleLevels", featureScaleLevels); + info_.addParam(this, "featureScaleMul", featureScaleMul); + info_.addParam(this, "initXyStep", initXyStep); + info_.addParam(this, "initImgBound", initImgBound); + info_.addParam(this, "varyXyStepWithScale", varyXyStepWithScale); + info_.addParam(this, "varyImgBoundWithScale", varyImgBoundWithScale); + + initialized = true; + } + return &info_; +} /* * GridAdaptedFeatureDetector diff --git a/modules/features2d/src/dynamic.cpp b/modules/features2d/src/dynamic.cpp index 6b2511cd39..3503cad5be 100644 --- a/modules/features2d/src/dynamic.cpp +++ b/modules/features2d/src/dynamic.cpp @@ -169,8 +169,9 @@ SurfAdjuster::SurfAdjuster( double initial_thresh, double min_thresh, double max void SurfAdjuster::detectImpl(const Mat& image, vector& keypoints, const cv::Mat& mask) const { - SurfFeatureDetector detector_tmp(thresh_); - detector_tmp.detect(image, keypoints, mask); + Ptr surf = FeatureDetector::create("SURF"); + surf->set("hessianThreshold", thresh_); + surf->detect(image, keypoints, mask); } void SurfAdjuster::tooFew(int, int) diff --git a/modules/features2d/src/fast.cpp b/modules/features2d/src/fast.cpp index 4fe842119e..fb8f0b1c8e 100644 --- a/modules/features2d/src/fast.cpp +++ b/modules/features2d/src/fast.cpp @@ -185,11 +185,10 @@ static int cornerScore(const uchar* ptr, const int pixel[], int threshold) return threshold; } -} - -void cv::FAST(const Mat& img, std::vector& keypoints, int threshold, bool nonmax_suppression) +void FAST(InputArray _img, std::vector& keypoints, int threshold, bool nonmax_suppression) { + Mat img = _img.getMat(); const int K = 8, N = 16 + K + 1; int i, j, k, pixel[N]; makeOffsets(pixel, img.step); @@ -280,7 +279,7 @@ void cv::FAST(const Mat& img, std::vector& keypoints, int threshold, b { cornerpos[ncorners++] = j+k; if(nonmax_suppression) - curr[j+k] = (uchar)cornerScore(ptr+k, pixel, threshold); + curr[j+k] = cornerScore(ptr+k, pixel, threshold); } } #endif @@ -318,7 +317,7 @@ void cv::FAST(const Mat& img, std::vector& keypoints, int threshold, b { cornerpos[ncorners++] = j; if(nonmax_suppression) - curr[j] = (uchar)cornerScore(ptr, pixel, threshold); + curr[j] = cornerScore(ptr, pixel, threshold); break; } } @@ -340,7 +339,7 @@ void cv::FAST(const Mat& img, std::vector& keypoints, int threshold, b { cornerpos[ncorners++] = j; if(nonmax_suppression) - curr[j] = (uchar)cornerScore(ptr, pixel, threshold); + curr[j] = cornerScore(ptr, pixel, threshold); break; } } @@ -375,3 +374,38 @@ void cv::FAST(const Mat& img, std::vector& keypoints, int threshold, b } } } + + +/* + * FastFeatureDetector + */ +FastFeatureDetector::FastFeatureDetector( int _threshold, bool _nonmaxSuppression ) +: threshold(_threshold), nonmaxSuppression(_nonmaxSuppression) +{} + +void FastFeatureDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask ) const +{ + Mat grayImage = image; + if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); + FAST( grayImage, keypoints, threshold, nonmaxSuppression ); + KeyPointsFilter::runByPixelsMask( keypoints, mask ); +} + + +static Algorithm* createFAST() { return new FastFeatureDetector; } +static AlgorithmInfo fast_info("Feature2D.FAST", createFAST); + +AlgorithmInfo* FastFeatureDetector::info() const +{ + static volatile bool initialized = false; + if( !initialized ) + { + fast_info.addParam(this, "threshold", threshold); + fast_info.addParam(this, "nonmaxSuppression", nonmaxSuppression); + + initialized = true; + } + return &fast_info; +} + +} diff --git a/modules/features2d/src/keypoint.cpp b/modules/features2d/src/keypoint.cpp index ffeb534638..c89799ddbb 100644 --- a/modules/features2d/src/keypoint.cpp +++ b/modules/features2d/src/keypoint.cpp @@ -165,6 +165,52 @@ float KeyPoint::overlap( const KeyPoint& kp1, const KeyPoint& kp2 ) return ovrl; } + + +struct KeypointResponseGreaterThanThreshold +{ + KeypointResponseGreaterThanThreshold(float _value) : + value(_value) + { + } + inline bool operator()(const KeyPoint& kpt) const + { + return kpt.response >= value; + } + float value; +}; + +struct KeypointResponseGreater +{ + inline bool operator()(const KeyPoint& kp1, const KeyPoint& kp2) const + { + return kp1.response > kp2.response; + } +}; + +// takes keypoints and culls them by the response +void KeyPointsFilter::retainBest(vector& keypoints, int n_points) +{ + //this is only necessary if the keypoints size is greater than the number of desired points. + if( n_points > 0 && keypoints.size() > (size_t)n_points ) + { + if (n_points==0) + { + keypoints.clear(); + return; + } + //first use nth element to partition the keypoints into the best and worst. + std::nth_element(keypoints.begin(), keypoints.begin() + n_points, keypoints.end(), KeypointResponseGreater()); + //this is the boundary response, and in the case of FAST may be ambigous + float ambiguous_response = keypoints[n_points - 1].response; + //use std::partition to grab all of the keypoints with the boundary response. + vector::const_iterator new_end = + std::partition(keypoints.begin() + n_points, keypoints.end(), + KeypointResponseGreaterThanThreshold(ambiguous_response)); + //resize the keypoints, given this new end point. nth_element and partition reordered the points inplace + keypoints.resize(new_end - keypoints.begin()); + } +} struct RoiPredicate { diff --git a/modules/features2d/src/matchers.cpp b/modules/features2d/src/matchers.cpp index b678e31abf..3fd02399cb 100755 --- a/modules/features2d/src/matchers.cpp +++ b/modules/features2d/src/matchers.cpp @@ -314,6 +314,144 @@ bool DescriptorMatcher::isMaskedOut( const vector& masks, int queryIdx ) return !masks.empty() && outCount == masks.size() ; } + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +BFMatcher::BFMatcher( int _normType, bool _crossCheck ) +{ + normType = _normType; + crossCheck = _crossCheck; +} + +Ptr BFMatcher::clone( bool emptyTrainData ) const +{ + BFMatcher* matcher = new BFMatcher(normType, crossCheck); + if( !emptyTrainData ) + { + matcher->trainDescCollection.resize(trainDescCollection.size()); + std::transform( trainDescCollection.begin(), trainDescCollection.end(), + matcher->trainDescCollection.begin(), clone_op ); + } + return matcher; +} + + +void BFMatcher::knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int knn, + const vector& masks, bool compactResult ) +{ + const int IMGIDX_SHIFT = 18; + const int IMGIDX_ONE = (1 << IMGIDX_SHIFT); + + if( queryDescriptors.empty() || trainDescCollection.empty() ) + { + matches.clear(); + return; + } + CV_Assert( queryDescriptors.type() == trainDescCollection[0].type() ); + + matches.reserve(queryDescriptors.rows); + + Mat dist, nidx; + + int iIdx, imgCount = (int)trainDescCollection.size(), update = 0; + int dtype = normType == NORM_HAMMING || normType == NORM_HAMMING2 || + (normType == NORM_L1 && queryDescriptors.type() == CV_8U) ? CV_32S : CV_32F; + + CV_Assert( (int64)imgCount*IMGIDX_ONE < INT_MAX ); + + for( iIdx = 0; iIdx < imgCount; iIdx++ ) + { + CV_Assert( trainDescCollection[iIdx].rows < IMGIDX_ONE ); + batchDistance(queryDescriptors, trainDescCollection[iIdx], dist, dtype, nidx, + normType, knn, masks.empty() ? Mat() : masks[iIdx], update, crossCheck); + update += IMGIDX_ONE; + } + + if( dtype == CV_32S ) + { + Mat temp; + dist.convertTo(temp, CV_32F); + dist = temp; + } + + for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ ) + { + const float* distptr = dist.ptr(qIdx); + const int* nidxptr = nidx.ptr(qIdx); + + matches.push_back( vector() ); + vector& mq = matches.back(); + mq.reserve(knn); + + for( int k = 0; k < knn; k++ ) + { + if( nidxptr[k] < 0 ) + break; + mq.push_back( DMatch(qIdx, nidxptr[k] & (IMGIDX_ONE - 1), + nidxptr[k] >> IMGIDX_SHIFT, distptr[k]) ); + } + + if( mq.empty() && compactResult ) + matches.pop_back(); + } +} + + +void BFMatcher::radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, + float maxDistance, const vector& masks, bool compactResult ) +{ + if( queryDescriptors.empty() || trainDescCollection.empty() ) + { + matches.clear(); + return; + } + CV_Assert( queryDescriptors.type() == trainDescCollection[0].type() ); + + matches.resize(queryDescriptors.rows); + Mat dist, distf; + + int iIdx, imgCount = (int)trainDescCollection.size(); + int dtype = normType == NORM_HAMMING || + (normType == NORM_L1 && queryDescriptors.type() == CV_8U) ? CV_32S : CV_32F; + + for( iIdx = 0; iIdx < imgCount; iIdx++ ) + { + batchDistance(queryDescriptors, trainDescCollection[iIdx], dist, dtype, noArray(), + normType, 0, masks.empty() ? Mat() : masks[iIdx], 0, false); + if( dtype == CV_32S ) + dist.convertTo(distf, CV_32F); + else + distf = dist; + + for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ ) + { + const float* distptr = dist.ptr(qIdx); + + vector& mq = matches[qIdx]; + for( int k = 0; k < dist.cols; k++ ) + { + if( distptr[k] <= maxDistance ) + mq.push_back( DMatch(qIdx, k, iIdx, distptr[k]) ); + } + } + } + + int qIdx0 = 0; + for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ ) + { + if( matches[qIdx].empty() && compactResult ) + continue; + + if( qIdx0 < qIdx ) + std::swap(matches[qIdx], matches[qIdx0]); + + std::sort( matches[qIdx0].begin(), matches[qIdx0].end() ); + qIdx0++; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + /* * Factory function for DescriptorMatcher creating */ @@ -326,31 +464,24 @@ Ptr DescriptorMatcher::create( const string& descriptorMatche } else if( !descriptorMatcherType.compare( "BruteForce" ) ) // L2 { - dm = new BruteForceMatcher >(); + dm = new BFMatcher(NORM_L2); } else if( !descriptorMatcherType.compare( "BruteForce-SL2" ) ) // Squared L2 { - dm = new BruteForceMatcher >(); + dm = new BFMatcher(NORM_L2SQR); } else if( !descriptorMatcherType.compare( "BruteForce-L1" ) ) { - dm = new BruteForceMatcher >(); + dm = new BFMatcher(NORM_L1); } - else if( !descriptorMatcherType.compare("BruteForce-Hamming") ) + else if( !descriptorMatcherType.compare("BruteForce-Hamming") || + !descriptorMatcherType.compare("BruteForce-HammingLUT") ) { - dm = new BruteForceMatcher(); - } - else if( !descriptorMatcherType.compare("BruteForce-HammingLUT") ) - { - dm = new BruteForceMatcher(); + dm = new BFMatcher(NORM_HAMMING); } else if( !descriptorMatcherType.compare("BruteForce-Hamming(2)") ) { - dm = new BruteForceMatcher >(); - } - else if( !descriptorMatcherType.compare("BruteForce-Hamming(4)") ) - { - dm = new BruteForceMatcher >(); + dm = new BFMatcher(NORM_HAMMING2); } else CV_Error( CV_StsBadArg, "Unknown matcher name" ); @@ -358,199 +489,6 @@ Ptr DescriptorMatcher::create( const string& descriptorMatche return dm; } -/* - * BruteForce SL2 and L2 specialization - */ -template<> -void BruteForceMatcher >::knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int knn, - const vector& masks, bool compactResult ) -{ -#ifndef HAVE_EIGEN - commonKnnMatchImpl( *this, queryDescriptors, matches, knn, masks, compactResult ); -#else - CV_Assert( queryDescriptors.type() == CV_32FC1 || queryDescriptors.empty() ); - CV_Assert( masks.empty() || masks.size() == trainDescCollection.size() ); - - matches.reserve(queryDescriptors.rows); - size_t imgCount = trainDescCollection.size(); - - Eigen::Matrix e_query_t; - vector > e_trainCollection(trainDescCollection.size()); - vector > e_trainNorms2(trainDescCollection.size()); - cv2eigen( queryDescriptors.t(), e_query_t); - for( size_t i = 0; i < trainDescCollection.size(); i++ ) - { - cv2eigen( trainDescCollection[i], e_trainCollection[i] ); - e_trainNorms2[i] = e_trainCollection[i].rowwise().squaredNorm() / 2; - } - - vector > e_allDists( imgCount ); // distances between one query descriptor and all train descriptors - - for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ ) - { - if( isMaskedOut( masks, qIdx ) ) - { - if( !compactResult ) // push empty vector - matches.push_back( vector() ); - } - else - { - float queryNorm2 = e_query_t.col(qIdx).squaredNorm(); - // 1. compute distances between i-th query descriptor and all train descriptors - for( size_t iIdx = 0; iIdx < imgCount; iIdx++ ) - { - CV_Assert( masks.empty() || masks[iIdx].empty() || - ( masks[iIdx].rows == queryDescriptors.rows && masks[iIdx].cols == trainDescCollection[iIdx].rows && - masks[iIdx].type() == CV_8UC1 ) ); - CV_Assert( trainDescCollection[iIdx].type() == CV_32FC1 || trainDescCollection[iIdx].empty() ); - CV_Assert( queryDescriptors.cols == trainDescCollection[iIdx].cols ); - - e_allDists[iIdx] = e_trainCollection[iIdx] *e_query_t.col(qIdx); - e_allDists[iIdx] -= e_trainNorms2[iIdx]; - - if( !masks.empty() && !masks[iIdx].empty() ) - { - const uchar* maskPtr = (uchar*)masks[iIdx].ptr(qIdx); - for( int c = 0; c < masks[iIdx].cols; c++ ) - { - if( maskPtr[c] == 0 ) - e_allDists[iIdx](c) = -std::numeric_limits::max(); - } - } - } - - // 2. choose knn nearest matches for query[i] - matches.push_back( vector() ); - vector >::reverse_iterator curMatches = matches.rbegin(); - for( int k = 0; k < knn; k++ ) - { - float totalMaxCoeff = -std::numeric_limits::max(); - int bestTrainIdx = -1, bestImgIdx = -1; - for( size_t iIdx = 0; iIdx < imgCount; iIdx++ ) - { - int loc; - float curMaxCoeff = e_allDists[iIdx].maxCoeff( &loc ); - if( curMaxCoeff > totalMaxCoeff ) - { - totalMaxCoeff = curMaxCoeff; - bestTrainIdx = loc; - bestImgIdx = iIdx; - } - } - if( bestTrainIdx == -1 ) - break; - - e_allDists[bestImgIdx](bestTrainIdx) = -std::numeric_limits::max(); - curMatches->push_back( DMatch(qIdx, bestTrainIdx, bestImgIdx, (-2)*totalMaxCoeff + queryNorm2) ); - } - std::sort( curMatches->begin(), curMatches->end() ); - } - } -#endif -} - -template<> -void BruteForceMatcher >::radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, float maxDistance, - const vector& masks, bool compactResult ) -{ -#ifndef HAVE_EIGEN - commonRadiusMatchImpl( *this, queryDescriptors, matches, maxDistance, masks, compactResult ); -#else - CV_Assert( queryDescriptors.type() == CV_32FC1 || queryDescriptors.empty() ); - CV_Assert( masks.empty() || masks.size() == trainDescCollection.size() ); - - matches.reserve(queryDescriptors.rows); - size_t imgCount = trainDescCollection.size(); - - Eigen::Matrix e_query_t; - vector > e_trainCollection(trainDescCollection.size()); - vector > e_trainNorms2(trainDescCollection.size()); - cv2eigen( queryDescriptors.t(), e_query_t); - for( size_t i = 0; i < trainDescCollection.size(); i++ ) - { - cv2eigen( trainDescCollection[i], e_trainCollection[i] ); - e_trainNorms2[i] = e_trainCollection[i].rowwise().squaredNorm() / 2; - } - - vector > e_allDists( imgCount ); // distances between one query descriptor and all train descriptors - - for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ ) - { - if( isMaskedOut( masks, qIdx ) ) - { - if( !compactResult ) // push empty vector - matches.push_back( vector() ); - } - else - { - float queryNorm2 = e_query_t.col(qIdx).squaredNorm(); - // 1. compute distances between i-th query descriptor and all train descriptors - for( size_t iIdx = 0; iIdx < imgCount; iIdx++ ) - { - CV_Assert( masks.empty() || masks[iIdx].empty() || - ( masks[iIdx].rows == queryDescriptors.rows && masks[iIdx].cols == trainDescCollection[iIdx].rows && - masks[iIdx].type() == CV_8UC1 ) ); - CV_Assert( trainDescCollection[iIdx].type() == CV_32FC1 || trainDescCollection[iIdx].empty() ); - CV_Assert( queryDescriptors.cols == trainDescCollection[iIdx].cols ); - - e_allDists[iIdx] = e_trainCollection[iIdx] *e_query_t.col(qIdx); - e_allDists[iIdx] -= e_trainNorms2[iIdx]; - } - - matches.push_back( vector() ); - vector >::reverse_iterator curMatches = matches.rbegin(); - for( size_t iIdx = 0; iIdx < imgCount; iIdx++ ) - { - assert( e_allDists[iIdx].rows() == trainDescCollection[iIdx].rows ); - for( int tIdx = 0; tIdx < e_allDists[iIdx].rows(); tIdx++ ) - { - if( masks.empty() || isPossibleMatch(masks[iIdx], qIdx, tIdx) ) - { - float d = (-2)*e_allDists[iIdx](tIdx) + queryNorm2; - if( d < maxDistance ) - curMatches->push_back( DMatch( qIdx, tIdx, iIdx, d ) ); - } - } - } - std::sort( curMatches->begin(), curMatches->end() ); - } - } -#endif -} - -inline void sqrtDistance( vector >& matches ) -{ - for( size_t imgIdx = 0; imgIdx < matches.size(); imgIdx++ ) - { - for( size_t matchIdx = 0; matchIdx < matches[imgIdx].size(); matchIdx++ ) - { - matches[imgIdx][matchIdx].distance = std::sqrt( matches[imgIdx][matchIdx].distance ); - } - } -} - -template<> -void BruteForceMatcher >::knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int knn, - const vector& masks, bool compactResult ) -{ - BruteForceMatcher > matcherSL2; - matcherSL2.add( getTrainDescriptors() ); - matcherSL2.knnMatch( queryDescriptors, matches, knn, masks, compactResult ); - - sqrtDistance( matches ); -} - -template<> -void BruteForceMatcher >::radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, float maxDistance, - const vector& masks, bool compactResult ) -{ - const float maxDistance2 = maxDistance * maxDistance; - BruteForceMatcher > matcherSL2; - matcherSL2.add( getTrainDescriptors() ); - matcherSL2.radiusMatch( queryDescriptors, matches, maxDistance2, masks, compactResult ); - - sqrtDistance( matches ); -} /* * Flann based matcher @@ -1124,16 +1062,9 @@ bool GenericDescriptorMatcher::empty() const Ptr GenericDescriptorMatcher::create( const string& genericDescritptorMatcherType, const string ¶msFilename ) { - Ptr descriptorMatcher; - if( ! genericDescritptorMatcherType.compare("ONEWAY") ) - { - descriptorMatcher = new OneWayDescriptorMatcher(); - } - else if( ! genericDescritptorMatcherType.compare("FERN") ) - { - descriptorMatcher = new FernDescriptorMatcher(); - } - + Ptr descriptorMatcher = + Algorithm::create("DescriptorMatcher." + genericDescritptorMatcherType); + if( !paramsFilename.empty() && !descriptorMatcher.empty() ) { FileStorage fs = FileStorage( paramsFilename, FileStorage::READ ); @@ -1146,331 +1077,6 @@ Ptr GenericDescriptorMatcher::create( const string& ge return descriptorMatcher; } -/****************************************************************************************\ -* OneWayDescriptorMatcher * -\****************************************************************************************/ - -OneWayDescriptorMatcher::Params::Params( int _poseCount, Size _patchSize, string _pcaFilename, - string _trainPath, string _trainImagesList, - float _minScale, float _maxScale, float _stepScale ) : - poseCount(_poseCount), patchSize(_patchSize), pcaFilename(_pcaFilename), - trainPath(_trainPath), trainImagesList(_trainImagesList), - minScale(_minScale), maxScale(_maxScale), stepScale(_stepScale) -{} - - -OneWayDescriptorMatcher::OneWayDescriptorMatcher( const Params& _params) -{ - initialize(_params); -} - -OneWayDescriptorMatcher::~OneWayDescriptorMatcher() -{} - -void OneWayDescriptorMatcher::initialize( const Params& _params, const Ptr& _base ) -{ - clear(); - - if( _base.empty() ) - base = _base; - - params = _params; -} - -void OneWayDescriptorMatcher::clear() -{ - GenericDescriptorMatcher::clear(); - - prevTrainCount = 0; - if( !base.empty() ) - base->clear(); -} - -void OneWayDescriptorMatcher::train() -{ - if( base.empty() || prevTrainCount < (int)trainPointCollection.keypointCount() ) - { - base = new OneWayDescriptorObject( params.patchSize, params.poseCount, params.pcaFilename, - params.trainPath, params.trainImagesList, params.minScale, params.maxScale, params.stepScale ); - - base->Allocate( (int)trainPointCollection.keypointCount() ); - prevTrainCount = (int)trainPointCollection.keypointCount(); - - const vector >& points = trainPointCollection.getKeypoints(); - int count = 0; - for( size_t i = 0; i < points.size(); i++ ) - { - IplImage _image = trainPointCollection.getImage((int)i); - for( size_t j = 0; j < points[i].size(); j++ ) - base->InitializeDescriptor( count++, &_image, points[i][j], "" ); - } - -#if defined(_KDTREE) - base->ConvertDescriptorsArrayToTree(); -#endif - } -} - -bool OneWayDescriptorMatcher::isMaskSupported() -{ - return false; -} - -void OneWayDescriptorMatcher::knnMatchImpl( const Mat& queryImage, vector& queryKeypoints, - vector >& matches, int knn, - const vector& /*masks*/, bool /*compactResult*/ ) -{ - train(); - - CV_Assert( knn == 1 ); // knn > 1 unsupported because of bug in OneWayDescriptorBase for this case - - matches.resize( queryKeypoints.size() ); - IplImage _qimage = queryImage; - for( size_t i = 0; i < queryKeypoints.size(); i++ ) - { - int descIdx = -1, poseIdx = -1; - float distance; - base->FindDescriptor( &_qimage, queryKeypoints[i].pt, descIdx, poseIdx, distance ); - matches[i].push_back( DMatch((int)i, descIdx, distance) ); - } -} - -void OneWayDescriptorMatcher::radiusMatchImpl( const Mat& queryImage, vector& queryKeypoints, - vector >& matches, float maxDistance, - const vector& /*masks*/, bool /*compactResult*/ ) -{ - train(); - - matches.resize( queryKeypoints.size() ); - IplImage _qimage = queryImage; - for( size_t i = 0; i < queryKeypoints.size(); i++ ) - { - int descIdx = -1, poseIdx = -1; - float distance; - base->FindDescriptor( &_qimage, queryKeypoints[i].pt, descIdx, poseIdx, distance ); - if( distance < maxDistance ) - matches[i].push_back( DMatch((int)i, descIdx, distance) ); - } -} - -void OneWayDescriptorMatcher::read( const FileNode &fn ) -{ - base = new OneWayDescriptorObject( params.patchSize, params.poseCount, string (), string (), string (), - params.minScale, params.maxScale, params.stepScale ); - base->Read (fn); -} - -void OneWayDescriptorMatcher::write( FileStorage& fs ) const -{ - base->Write (fs); -} - -bool OneWayDescriptorMatcher::empty() const -{ - return base.empty() || base->empty(); -} - -Ptr OneWayDescriptorMatcher::clone( bool emptyTrainData ) const -{ - OneWayDescriptorMatcher* matcher = new OneWayDescriptorMatcher( params ); - - if( !emptyTrainData ) - { - CV_Error( CV_StsNotImplemented, "deep clone functionality is not implemented, because " - "OneWayDescriptorBase has not copy constructor or clone method "); - - //matcher->base; - matcher->params = params; - matcher->prevTrainCount = prevTrainCount; - matcher->trainPointCollection = trainPointCollection; - } - return matcher; -} - -/****************************************************************************************\ -* FernDescriptorMatcher * -\****************************************************************************************/ -FernDescriptorMatcher::Params::Params( int _nclasses, int _patchSize, int _signatureSize, - int _nstructs, int _structSize, int _nviews, int _compressionMethod, - const PatchGenerator& _patchGenerator ) : - nclasses(_nclasses), patchSize(_patchSize), signatureSize(_signatureSize), - nstructs(_nstructs), structSize(_structSize), nviews(_nviews), - compressionMethod(_compressionMethod), patchGenerator(_patchGenerator) -{} - -FernDescriptorMatcher::Params::Params( const string& _filename ) -{ - filename = _filename; -} - -FernDescriptorMatcher::FernDescriptorMatcher( const Params& _params ) -{ - prevTrainCount = 0; - params = _params; - if( !params.filename.empty() ) - { - classifier = new FernClassifier; - FileStorage fs(params.filename, FileStorage::READ); - if( fs.isOpened() ) - classifier->read( fs.getFirstTopLevelNode() ); - } -} - -FernDescriptorMatcher::~FernDescriptorMatcher() -{} - -void FernDescriptorMatcher::clear() -{ - GenericDescriptorMatcher::clear(); - - classifier.release(); - prevTrainCount = 0; -} - -void FernDescriptorMatcher::train() -{ - if( classifier.empty() || prevTrainCount < (int)trainPointCollection.keypointCount() ) - { - assert( params.filename.empty() ); - - vector > points( trainPointCollection.imageCount() ); - for( size_t imgIdx = 0; imgIdx < trainPointCollection.imageCount(); imgIdx++ ) - KeyPoint::convert( trainPointCollection.getKeypoints((int)imgIdx), points[imgIdx] ); - - classifier = new FernClassifier( points, trainPointCollection.getImages(), vector >(), 0, // each points is a class - params.patchSize, params.signatureSize, params.nstructs, params.structSize, - params.nviews, params.compressionMethod, params.patchGenerator ); - } -} - -bool FernDescriptorMatcher::isMaskSupported() -{ - return false; -} - -void FernDescriptorMatcher::calcBestProbAndMatchIdx( const Mat& image, const Point2f& pt, - float& bestProb, int& bestMatchIdx, vector& signature ) -{ - (*classifier)( image, pt, signature); - - bestProb = -FLT_MAX; - bestMatchIdx = -1; - for( int ci = 0; ci < classifier->getClassCount(); ci++ ) - { - if( signature[ci] > bestProb ) - { - bestProb = signature[ci]; - bestMatchIdx = ci; - } - } -} - -void FernDescriptorMatcher::knnMatchImpl( const Mat& queryImage, vector& queryKeypoints, - vector >& matches, int knn, - const vector& /*masks*/, bool /*compactResult*/ ) -{ - train(); - - matches.resize( queryKeypoints.size() ); - vector signature( (size_t)classifier->getClassCount() ); - - for( size_t queryIdx = 0; queryIdx < queryKeypoints.size(); queryIdx++ ) - { - (*classifier)( queryImage, queryKeypoints[queryIdx].pt, signature); - - for( int k = 0; k < knn; k++ ) - { - DMatch bestMatch; - size_t best_ci = 0; - for( size_t ci = 0; ci < signature.size(); ci++ ) - { - if( -signature[ci] < bestMatch.distance ) - { - int imgIdx = -1, trainIdx = -1; - trainPointCollection.getLocalIdx( (int)ci , imgIdx, trainIdx ); - bestMatch = DMatch( (int)queryIdx, trainIdx, imgIdx, -signature[ci] ); - best_ci = ci; - } - } - - if( bestMatch.trainIdx == -1 ) - break; - signature[best_ci] = -std::numeric_limits::max(); - matches[queryIdx].push_back( bestMatch ); - } - } -} - -void FernDescriptorMatcher::radiusMatchImpl( const Mat& queryImage, vector& queryKeypoints, - vector >& matches, float maxDistance, - const vector& /*masks*/, bool /*compactResult*/ ) -{ - train(); - matches.resize( queryKeypoints.size() ); - vector signature( (size_t)classifier->getClassCount() ); - - for( size_t i = 0; i < queryKeypoints.size(); i++ ) - { - (*classifier)( queryImage, queryKeypoints[i].pt, signature); - - for( int ci = 0; ci < classifier->getClassCount(); ci++ ) - { - if( -signature[ci] < maxDistance ) - { - int imgIdx = -1, trainIdx = -1; - trainPointCollection.getLocalIdx( ci , imgIdx, trainIdx ); - matches[i].push_back( DMatch( (int)i, trainIdx, imgIdx, -signature[ci] ) ); - } - } - } -} - -void FernDescriptorMatcher::read( const FileNode &fn ) -{ - params.nclasses = fn["nclasses"]; - params.patchSize = fn["patchSize"]; - params.signatureSize = fn["signatureSize"]; - params.nstructs = fn["nstructs"]; - params.structSize = fn["structSize"]; - params.nviews = fn["nviews"]; - params.compressionMethod = fn["compressionMethod"]; - - //classifier->read(fn); -} - -void FernDescriptorMatcher::write( FileStorage& fs ) const -{ - fs << "nclasses" << params.nclasses; - fs << "patchSize" << params.patchSize; - fs << "signatureSize" << params.signatureSize; - fs << "nstructs" << params.nstructs; - fs << "structSize" << params.structSize; - fs << "nviews" << params.nviews; - fs << "compressionMethod" << params.compressionMethod; - -// classifier->write(fs); -} - -bool FernDescriptorMatcher::empty() const -{ - return classifier.empty() || classifier->empty(); -} - -Ptr FernDescriptorMatcher::clone( bool emptyTrainData ) const -{ - FernDescriptorMatcher* matcher = new FernDescriptorMatcher( params ); - if( !emptyTrainData ) - { - CV_Error( CV_StsNotImplemented, "deep clone dunctionality is not implemented, because " - "FernClassifier has not copy constructor or clone method "); - - //matcher->classifier; - matcher->params = params; - matcher->prevTrainCount = prevTrainCount; - matcher->trainPointCollection = trainPointCollection; - } - return matcher; -} /****************************************************************************************\ * VectorDescriptorMatcher * diff --git a/modules/features2d/src/mser.cpp b/modules/features2d/src/mser.cpp index 58ab7985bf..e96ccc57d4 100644 --- a/modules/features2d/src/mser.cpp +++ b/modules/features2d/src/mser.cpp @@ -41,7 +41,10 @@ #include "precomp.hpp" -#define TABLE_SIZE 400 +namespace cv +{ + +const int TABLE_SIZE = 400; static double chitab3[]={0, 0.0150057, 0.0239478, 0.0315227, 0.0383427, 0.0446605, 0.0506115, 0.0562786, @@ -144,36 +147,36 @@ static double chitab3[]={0, 0.0150057, 0.0239478, 0.0315227, 3.37455, 3.48653, 3.61862, 3.77982, 3.98692, 4.2776, 4.77167, 133.333 }; -typedef struct CvLinkedPoint +typedef struct LinkedPoint { - struct CvLinkedPoint* prev; - struct CvLinkedPoint* next; - CvPoint pt; + struct LinkedPoint* prev; + struct LinkedPoint* next; + Point pt; } -CvLinkedPoint; +LinkedPoint; // the history of region grown -typedef struct CvMSERGrowHistory +typedef struct MSERGrowHistory { - struct CvMSERGrowHistory* shortcut; - struct CvMSERGrowHistory* child; + struct MSERGrowHistory* shortcut; + struct MSERGrowHistory* child; int stable; // when it ever stabled before, record the size int val; int size; } -CvMSERGrowHistory; +MSERGrowHistory; -typedef struct CvMSERConnectedComp +typedef struct MSERConnectedComp { - CvLinkedPoint* head; - CvLinkedPoint* tail; - CvMSERGrowHistory* history; + LinkedPoint* head; + LinkedPoint* tail; + MSERGrowHistory* history; unsigned long grey_level; int size; int dvar; // the derivative of last var float var; // the current variation (most time is the variation of one-step back) } -CvMSERConnectedComp; +MSERConnectedComp; // Linear Time MSER claims by using bsf can get performance gain, here is the implementation // however it seems that will not do any good in real world test @@ -186,24 +189,29 @@ inline void _bitreset(unsigned long * a, unsigned long b) *a &= ~(1<size = 0; comp->var = 0; @@ -212,9 +220,8 @@ icvInitMSERComp( CvMSERConnectedComp* comp ) } // add history of size to a connected component -CV_INLINE static void -icvMSERNewHistory( CvMSERConnectedComp* comp, - CvMSERGrowHistory* history ) +static void +MSERNewHistory( MSERConnectedComp* comp, MSERGrowHistory* history ) { history->child = history; if ( NULL == comp->history ) @@ -232,14 +239,14 @@ icvMSERNewHistory( CvMSERConnectedComp* comp, } // merging two connected component -CV_INLINE static void -icvMSERMergeComp( CvMSERConnectedComp* comp1, - CvMSERConnectedComp* comp2, - CvMSERConnectedComp* comp, - CvMSERGrowHistory* history ) +static void +MSERMergeComp( MSERConnectedComp* comp1, + MSERConnectedComp* comp2, + MSERConnectedComp* comp, + MSERGrowHistory* history ) { - CvLinkedPoint* head; - CvLinkedPoint* tail; + LinkedPoint* head; + LinkedPoint* tail; comp->grey_level = comp2->grey_level; history->child = history; // select the winner by size @@ -301,18 +308,17 @@ icvMSERMergeComp( CvMSERConnectedComp* comp1, comp->size = comp1->size + comp2->size; } -CV_INLINE static float -icvMSERVariationCalc( CvMSERConnectedComp* comp, - int delta ) +static float +MSERVariationCalc( MSERConnectedComp* comp, int delta ) { - CvMSERGrowHistory* history = comp->history; + MSERGrowHistory* history = comp->history; int val = comp->grey_level; if ( NULL != history ) { - CvMSERGrowHistory* shortcut = history->shortcut; + MSERGrowHistory* shortcut = history->shortcut; while ( shortcut != shortcut->shortcut && shortcut->val + delta > val ) shortcut = shortcut->shortcut; - CvMSERGrowHistory* child = shortcut->child; + MSERGrowHistory* child = shortcut->child; while ( child != child->child && child->val + delta <= val ) { shortcut = child; @@ -328,15 +334,13 @@ icvMSERVariationCalc( CvMSERConnectedComp* comp, return 1.; } -CV_INLINE static bool -icvMSERStableCheck( CvMSERConnectedComp* comp, - CvMSERParams params ) +static bool MSERStableCheck( MSERConnectedComp* comp, MSERParams params ) { // tricky part: it actually check the stablity of one-step back if ( comp->history == NULL || comp->history->size <= params.minArea || comp->history->size >= params.maxArea ) return 0; float div = (float)(comp->history->size-comp->history->stable)/(float)comp->history->size; - float var = icvMSERVariationCalc( comp, params.delta ); + float var = MSERVariationCalc( comp, params.delta ); int dvar = ( comp->var < var || (unsigned long)(comp->history->val + 1) < comp->grey_level ); int stable = ( dvar && !comp->dvar && comp->var < params.maxVariation && div > params.minDiversity ); comp->var = var; @@ -347,9 +351,7 @@ icvMSERStableCheck( CvMSERConnectedComp* comp, } // add a pixel to the pixel list -CV_INLINE static void -icvAccumulateMSERComp( CvMSERConnectedComp* comp, - CvLinkedPoint* point ) +static void accumulateMSERComp( MSERConnectedComp* comp, LinkedPoint* point ) { if ( comp->size > 0 ) { @@ -366,14 +368,12 @@ icvAccumulateMSERComp( CvMSERConnectedComp* comp, } // convert the point set to CvSeq -CV_INLINE static CvContour* -icvMSERToContour( CvMSERConnectedComp* comp, - CvMemStorage* storage ) +static CvContour* MSERToContour( MSERConnectedComp* comp, CvMemStorage* storage ) { CvSeq* _contour = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage ); CvContour* contour = (CvContour*)_contour; cvSeqPushMulti( _contour, 0, comp->history->size ); - CvLinkedPoint* lpt = comp->head; + LinkedPoint* lpt = comp->head; for ( int i = 0; i < comp->history->size; i++ ) { CvPoint* pt = CV_GET_SEQ_ELEM( CvPoint, _contour, i ); @@ -391,8 +391,7 @@ icvMSERToContour( CvMSERConnectedComp* comp, // 17~19 bits is the direction // 8~11 bits is the bucket it falls to (for BitScanForward) // 0~8 bits is the color -static int* -icvPreprocessMSER_8UC1( CvMat* img, +static int* preprocessMSER_8UC1( CvMat* img, int*** heap_cur, CvMat* src, CvMat* mask ) @@ -476,17 +475,16 @@ icvPreprocessMSER_8UC1( CvMat* img, return startptr; } -static void -icvExtractMSER_8UC1_Pass( int* ioptr, +static void extractMSER_8UC1_Pass( int* ioptr, int* imgptr, int*** heap_cur, - CvLinkedPoint* ptsptr, - CvMSERGrowHistory* histptr, - CvMSERConnectedComp* comptr, + LinkedPoint* ptsptr, + MSERGrowHistory* histptr, + MSERConnectedComp* comptr, int step, int stepmask, int stepgap, - CvMSERParams params, + MSERParams params, int color, CvSeq* contours, CvMemStorage* storage ) @@ -494,7 +492,7 @@ icvExtractMSER_8UC1_Pass( int* ioptr, comptr->grey_level = 256; comptr++; comptr->grey_level = (*imgptr)&0xff; - icvInitMSERComp( comptr ); + initMSERComp( comptr ); *imgptr |= 0x80000000; heap_cur += (*imgptr)&0xff; int dir[] = { 1, step, -1, -step }; @@ -527,7 +525,7 @@ icvExtractMSER_8UC1_Pass( int* ioptr, #endif imgptr = imgptr_nbr; comptr++; - icvInitMSERComp( comptr ); + initMSERComp( comptr ); comptr->grey_level = (*imgptr)&0xff; continue; } else { @@ -544,7 +542,7 @@ icvExtractMSER_8UC1_Pass( int* ioptr, int i = (int)(imgptr-ioptr); ptsptr->pt = cvPoint( i&stepmask, i>>stepgap ); // get the current location - icvAccumulateMSERComp( comptr, ptsptr ); + accumulateMSERComp( comptr, ptsptr ); ptsptr++; // get the next pixel from boundary heap if ( **heap_cur ) @@ -595,13 +593,13 @@ icvExtractMSER_8UC1_Pass( int* ioptr, if ( pixel_val < comptr[-1].grey_level ) { // check the stablity and push a new history, increase the grey level - if ( icvMSERStableCheck( comptr, params ) ) + if ( MSERStableCheck( comptr, params ) ) { - CvContour* contour = icvMSERToContour( comptr, storage ); + CvContour* contour = MSERToContour( comptr, storage ); contour->color = color; cvSeqPush( contours, &contour ); } - icvMSERNewHistory( comptr, histptr ); + MSERNewHistory( comptr, histptr ); comptr[0].grey_level = pixel_val; histptr++; } else { @@ -609,20 +607,20 @@ icvExtractMSER_8UC1_Pass( int* ioptr, for ( ; ; ) { comptr--; - icvMSERMergeComp( comptr+1, comptr, comptr, histptr ); + MSERMergeComp( comptr+1, comptr, comptr, histptr ); histptr++; if ( pixel_val <= comptr[0].grey_level ) break; if ( pixel_val < comptr[-1].grey_level ) { // check the stablity here otherwise it wouldn't be an ER - if ( icvMSERStableCheck( comptr, params ) ) + if ( MSERStableCheck( comptr, params ) ) { - CvContour* contour = icvMSERToContour( comptr, storage ); + CvContour* contour = MSERToContour( comptr, storage ); contour->color = color; cvSeqPush( contours, &contour ); } - icvMSERNewHistory( comptr, histptr ); + MSERNewHistory( comptr, histptr ); comptr[0].grey_level = pixel_val; histptr++; break; @@ -635,12 +633,11 @@ icvExtractMSER_8UC1_Pass( int* ioptr, } } -static void -icvExtractMSER_8UC1( CvMat* src, +static void extractMSER_8UC1( CvMat* src, CvMat* mask, CvSeq* contours, CvMemStorage* storage, - CvMSERParams params ) + MSERParams params ) { int step = 8; int stepgap = 3; @@ -662,16 +659,16 @@ icvExtractMSER_8UC1( CvMat* src, heap_start[0] = heap; // pre-allocate linked point and grow history - CvLinkedPoint* pts = (CvLinkedPoint*)cvAlloc( src->rows*src->cols*sizeof(pts[0]) ); - CvMSERGrowHistory* history = (CvMSERGrowHistory*)cvAlloc( src->rows*src->cols*sizeof(history[0]) ); - CvMSERConnectedComp comp[257]; + LinkedPoint* pts = (LinkedPoint*)cvAlloc( src->rows*src->cols*sizeof(pts[0]) ); + MSERGrowHistory* history = (MSERGrowHistory*)cvAlloc( src->rows*src->cols*sizeof(history[0]) ); + MSERConnectedComp comp[257]; // darker to brighter (MSER-) - imgptr = icvPreprocessMSER_8UC1( img, heap_start, src, mask ); - icvExtractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, -1, contours, storage ); + imgptr = preprocessMSER_8UC1( img, heap_start, src, mask ); + extractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, -1, contours, storage ); // brighter to darker (MSER+) - imgptr = icvPreprocessMSER_8UC1( img, heap_start, src, mask ); - icvExtractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, 1, contours, storage ); + imgptr = preprocessMSER_8UC1( img, heap_start, src, mask ); + extractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, 1, contours, storage ); // clean up cvFree( &history ); @@ -680,26 +677,26 @@ icvExtractMSER_8UC1( CvMat* src, cvReleaseMat( &img ); } -struct CvMSCRNode; +struct MSCRNode; -typedef struct CvTempMSCR +struct TempMSCR { - CvMSCRNode* head; - CvMSCRNode* tail; + MSCRNode* head; + MSCRNode* tail; double m; // the margin used to prune area later int size; -} CvTempMSCR; +}; -typedef struct CvMSCRNode +struct MSCRNode { - CvMSCRNode* shortcut; + MSCRNode* shortcut; // to make the finding of root less painful - CvMSCRNode* prev; - CvMSCRNode* next; + MSCRNode* prev; + MSCRNode* next; // a point double-linked list - CvTempMSCR* tmsr; + TempMSCR* tmsr; // the temporary msr (set to NULL at every re-initialise) - CvTempMSCR* gmsr; + TempMSCR* gmsr; // the global msr (once set, never to NULL) int index; // the index of the node, at this point, it should be x at the first 16-bits, and y at the last 16-bits. @@ -708,25 +705,23 @@ typedef struct CvMSCRNode int size, sizei; double dt, di; double s; -} CvMSCRNode; +}; -typedef struct CvMSCREdge +struct MSCREdge { double chi; - CvMSCRNode* left; - CvMSCRNode* right; -} CvMSCREdge; + MSCRNode* left; + MSCRNode* right; +}; -CV_INLINE static double -icvChisquaredDistance( uchar* x, uchar* y ) +static double ChiSquaredDistance( uchar* x, uchar* y ) { return (double)((x[0]-y[0])*(x[0]-y[0]))/(double)(x[0]+y[0]+1e-10)+ (double)((x[1]-y[1])*(x[1]-y[1]))/(double)(x[1]+y[1]+1e-10)+ (double)((x[2]-y[2])*(x[2]-y[2]))/(double)(x[2]+y[2]+1e-10); } -CV_INLINE static void -icvInitMSCRNode( CvMSCRNode* node ) +static void initMSCRNode( MSCRNode* node ) { node->gmsr = node->tmsr = NULL; node->reinit = 0xffff; @@ -736,9 +731,8 @@ icvInitMSCRNode( CvMSCRNode* node ) } // the preprocess to get the edge list with proper gaussian blur -static int -icvPreprocessMSER_8UC3( CvMSCRNode* node, - CvMSCREdge* edge, +static int preprocessMSER_8UC3( MSCRNode* node, + MSCREdge* edge, double* total, CvMat* src, CvMat* mask, @@ -755,7 +749,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, { for ( int j = 0; j < src->cols-1; j++ ) { - *dxptr = icvChisquaredDistance( srcptr, lastptr ); + *dxptr = ChiSquaredDistance( srcptr, lastptr ); dxptr++; srcptr += 3; lastptr += 3; @@ -770,7 +764,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, { for ( int j = 0; j < src->cols; j++ ) { - *dyptr = icvChisquaredDistance( srcptr, lastptr ); + *dyptr = ChiSquaredDistance( srcptr, lastptr ); dyptr++; srcptr += 3; lastptr += 3; @@ -793,8 +787,8 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, Ne = 0; int maskcpt = mask->step-mask->cols+1; uchar* maskptr = mask->data.ptr; - CvMSCRNode* nodeptr = node; - icvInitMSCRNode( nodeptr ); + MSCRNode* nodeptr = node; + initMSCRNode( nodeptr ); nodeptr->index = 0; *total += edge->chi = *dxptr; if ( maskptr[0] && maskptr[1] ) @@ -809,7 +803,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, maskptr++; for ( int i = 1; i < src->cols-1; i++ ) { - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = i; if ( maskptr[0] && maskptr[1] ) { @@ -823,13 +817,13 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, nodeptr++; maskptr++; } - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = src->cols-1; nodeptr++; maskptr += maskcpt; for ( int i = 1; i < src->rows-1; i++ ) { - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = i<<16; if ( maskptr[0] ) { @@ -856,7 +850,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, maskptr++; for ( int j = 1; j < src->cols-1; j++ ) { - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = (i<<16)|j; if ( maskptr[0] ) { @@ -882,7 +876,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, nodeptr++; maskptr++; } - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = (i<<16)|(src->cols-1); if ( maskptr[0] && maskptr[-mask->step] ) { @@ -896,7 +890,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, nodeptr++; maskptr += maskcpt; } - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = (src->rows-1)<<16; if ( maskptr[0] ) { @@ -923,7 +917,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, maskptr++; for ( int i = 1; i < src->cols-1; i++ ) { - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = ((src->rows-1)<<16)|i; if ( maskptr[0] ) { @@ -949,7 +943,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, nodeptr++; maskptr++; } - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = ((src->rows-1)<<16)|(src->cols-1); if ( maskptr[0] && maskptr[-mask->step] ) { @@ -959,8 +953,8 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, Ne++; } } else { - CvMSCRNode* nodeptr = node; - icvInitMSCRNode( nodeptr ); + MSCRNode* nodeptr = node; + initMSCRNode( nodeptr ); nodeptr->index = 0; *total += edge->chi = *dxptr; dxptr++; @@ -970,7 +964,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, nodeptr++; for ( int i = 1; i < src->cols-1; i++ ) { - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = i; *total += edge->chi = *dxptr; dxptr++; @@ -979,12 +973,12 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, edge++; nodeptr++; } - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = src->cols-1; nodeptr++; for ( int i = 1; i < src->rows-1; i++ ) { - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = i<<16; *total += edge->chi = *dyptr; dyptr++; @@ -999,7 +993,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, nodeptr++; for ( int j = 1; j < src->cols-1; j++ ) { - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = (i<<16)|j; *total += edge->chi = *dyptr; dyptr++; @@ -1013,7 +1007,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, edge++; nodeptr++; } - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = (i<<16)|(src->cols-1); *total += edge->chi = *dyptr; dyptr++; @@ -1022,7 +1016,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, edge++; nodeptr++; } - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = (src->rows-1)<<16; *total += edge->chi = *dxptr; dxptr++; @@ -1037,7 +1031,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, nodeptr++; for ( int i = 1; i < src->cols-1; i++ ) { - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = ((src->rows-1)<<16)|i; *total += edge->chi = *dxptr; dxptr++; @@ -1051,7 +1045,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, edge++; nodeptr++; } - icvInitMSCRNode( nodeptr ); + initMSCRNode( nodeptr ); nodeptr->index = ((src->rows-1)<<16)|(src->cols-1); *total += edge->chi = *dyptr; edge->left = nodeptr-src->cols; @@ -1063,14 +1057,13 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node, #define cmp_mscr_edge(edge1, edge2) \ ((edge1).chi < (edge2).chi) -static CV_IMPLEMENT_QSORT( icvQuickSortMSCREdge, CvMSCREdge, cmp_mscr_edge ) +static CV_IMPLEMENT_QSORT( QuickSortMSCREdge, MSCREdge, cmp_mscr_edge ) // to find the root of one region -CV_INLINE static CvMSCRNode* -icvFindMSCR( CvMSCRNode* x ) +static MSCRNode* findMSCR( MSCRNode* x ) { - CvMSCRNode* prev = x; - CvMSCRNode* next; + MSCRNode* prev = x; + MSCRNode* next; for ( ; ; ) { next = x->shortcut; @@ -1079,7 +1072,7 @@ icvFindMSCR( CvMSCRNode* x ) prev= x; x = next; } - CvMSCRNode* root = x; + MSCRNode* root = x; for ( ; ; ) { prev = x->shortcut; @@ -1093,9 +1086,7 @@ icvFindMSCR( CvMSCRNode* x ) // the stable mscr should be: // bigger than minArea and smaller than maxArea // differ from its ancestor more than minDiversity -CV_INLINE static bool -icvMSCRStableCheck( CvMSCRNode* x, - CvMSERParams params ) +static bool MSCRStableCheck( MSCRNode* x, MSERParams params ) { if ( x->size <= params.minArea || x->size >= params.maxArea ) return 0; @@ -1106,25 +1097,25 @@ icvMSCRStableCheck( CvMSCRNode* x, } static void -icvExtractMSER_8UC3( CvMat* src, +extractMSER_8UC3( CvMat* src, CvMat* mask, CvSeq* contours, CvMemStorage* storage, - CvMSERParams params ) + MSERParams params ) { - CvMSCRNode* map = (CvMSCRNode*)cvAlloc( src->cols*src->rows*sizeof(map[0]) ); + MSCRNode* map = (MSCRNode*)cvAlloc( src->cols*src->rows*sizeof(map[0]) ); int Ne = src->cols*src->rows*2-src->cols-src->rows; - CvMSCREdge* edge = (CvMSCREdge*)cvAlloc( Ne*sizeof(edge[0]) ); - CvTempMSCR* mscr = (CvTempMSCR*)cvAlloc( src->cols*src->rows*sizeof(mscr[0]) ); + MSCREdge* edge = (MSCREdge*)cvAlloc( Ne*sizeof(edge[0]) ); + TempMSCR* mscr = (TempMSCR*)cvAlloc( src->cols*src->rows*sizeof(mscr[0]) ); double emean = 0; CvMat* dx = cvCreateMat( src->rows, src->cols-1, CV_64FC1 ); CvMat* dy = cvCreateMat( src->rows-1, src->cols, CV_64FC1 ); - Ne = icvPreprocessMSER_8UC3( map, edge, &emean, src, mask, dx, dy, Ne, params.edgeBlurSize ); + Ne = preprocessMSER_8UC3( map, edge, &emean, src, mask, dx, dy, Ne, params.edgeBlurSize ); emean = emean / (double)Ne; - icvQuickSortMSCREdge( edge, Ne, 0 ); - CvMSCREdge* edge_ub = edge+Ne; - CvMSCREdge* edgeptr = edge; - CvTempMSCR* mscrptr = mscr; + QuickSortMSCREdge( edge, Ne, 0 ); + MSCREdge* edge_ub = edge+Ne; + MSCREdge* edgeptr = edge; + TempMSCR* mscrptr = mscr; // the evolution process for ( int i = 0; i < params.maxEvolution; i++ ) { @@ -1135,21 +1126,21 @@ icvExtractMSER_8UC3( CvMat* src, // to process all the edges in the list that chi < thres while ( edgeptr < edge_ub && edgeptr->chi < thres ) { - CvMSCRNode* lr = icvFindMSCR( edgeptr->left ); - CvMSCRNode* rr = icvFindMSCR( edgeptr->right ); + MSCRNode* lr = findMSCR( edgeptr->left ); + MSCRNode* rr = findMSCR( edgeptr->right ); // get the region root (who is responsible) if ( lr != rr ) { // rank idea take from: N-tree Disjoint-Set Forests for Maximally Stable Extremal Regions if ( rr->rank > lr->rank ) { - CvMSCRNode* tmp; + MSCRNode* tmp; CV_SWAP( lr, rr, tmp ); } else if ( lr->rank == rr->rank ) { // at the same rank, we will compare the size if ( lr->size > rr->size ) { - CvMSCRNode* tmp; + MSCRNode* tmp; CV_SWAP( lr, rr, tmp ); } lr->rank++; @@ -1181,7 +1172,7 @@ icvExtractMSER_8UC3( CvMat* src, if ( s < lr->s ) { // skip the first one and check stablity - if ( i > lr->reinit+1 && icvMSCRStableCheck( lr, params ) ) + if ( i > lr->reinit+1 && MSCRStableCheck( lr, params ) ) { if ( lr->tmsr == NULL ) { @@ -1202,13 +1193,13 @@ icvExtractMSER_8UC3( CvMat* src, if ( edgeptr >= edge_ub ) break; } - for ( CvTempMSCR* ptr = mscr; ptr < mscrptr; ptr++ ) + for ( TempMSCR* ptr = mscr; ptr < mscrptr; ptr++ ) // to prune area with margin less than minMargin if ( ptr->m > params.minMargin ) { CvSeq* _contour = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage ); cvSeqPushMulti( _contour, 0, ptr->size ); - CvMSCRNode* lpt = ptr->head; + MSCRNode* lpt = ptr->head; for ( int i = 0; i < ptr->size; i++ ) { CvPoint* pt = CV_GET_SEQ_ELEM( CvPoint, _contour, i ); @@ -1228,12 +1219,12 @@ icvExtractMSER_8UC3( CvMat* src, cvFree( &map ); } -void -cvExtractMSER( CvArr* _img, +static void +extractMSER( CvArr* _img, CvArr* _mask, CvSeq** _contours, CvMemStorage* storage, - CvMSERParams params ) + MSERParams params ) { CvMat srchdr, *src = cvGetMat( _img, &srchdr ); CvMat maskhdr, *mask = _mask ? cvGetMat( _mask, &maskhdr ) : 0; @@ -1252,30 +1243,24 @@ cvExtractMSER( CvArr* _img, switch ( CV_MAT_TYPE(src->type) ) { case CV_8UC1: - icvExtractMSER_8UC1( src, mask, contours, storage, params ); + extractMSER_8UC1( src, mask, contours, storage, params ); break; case CV_8UC3: - icvExtractMSER_8UC3( src, mask, contours, storage, params ); + extractMSER_8UC3( src, mask, contours, storage, params ); break; } } -namespace cv -{ - -MSER::MSER() -{ - *(CvMSERParams*)this = cvMSERParams(); -} - MSER::MSER( int _delta, int _min_area, int _max_area, double _max_variation, double _min_diversity, int _max_evolution, double _area_threshold, double _min_margin, int _edge_blur_size ) + : delta(_delta), minArea(_min_area), maxArea(_max_area), + maxVariation(_max_variation), minDiversity(_min_diversity), + maxEvolution(_max_evolution), areaThreshold(_area_threshold), + minMargin(_min_margin), edgeBlurSize(_edge_blur_size) { - *(CvMSERParams*)this = cvMSERParams(_delta, _min_area, _max_area, (float)_max_variation, - (float)_min_diversity, _max_evolution, _area_threshold, _min_margin, _edge_blur_size); } void MSER::operator()( const Mat& image, vector >& dstcontours, const Mat& mask ) const @@ -1285,12 +1270,56 @@ void MSER::operator()( const Mat& image, vector >& dstcontours, co pmask = &(_mask = mask); MemStorage storage(cvCreateMemStorage(0)); Seq contours; - cvExtractMSER( &_image, pmask, &contours.seq, storage, *(const CvMSERParams*)this ); + extractMSER( &_image, pmask, &contours.seq, storage, + MSERParams(delta, minArea, maxArea, maxVariation, minDiversity, + maxEvolution, areaThreshold, minMargin, edgeBlurSize)); SeqIterator it = contours.begin(); size_t i, ncontours = contours.size(); dstcontours.resize(ncontours); for( i = 0; i < ncontours; i++, ++it ) Seq(*it).copyTo(dstcontours[i]); } + + +void MserFeatureDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask ) const +{ + vector > msers; + + (*this)(image, msers, mask); + + vector >::const_iterator contour_it = msers.begin(); + for( ; contour_it != msers.end(); ++contour_it ) + { + // TODO check transformation from MSER region to KeyPoint + RotatedRect rect = fitEllipse(Mat(*contour_it)); + float diam = sqrt(rect.size.height*rect.size.width); + + if( diam > std::numeric_limits::epsilon() ) + keypoints.push_back( KeyPoint( rect.center, diam, rect.angle) ); + } +} + +static Algorithm* createMSER() { return new MSER; } +static AlgorithmInfo mser_info("Feature2D.MSER", createMSER); + +AlgorithmInfo* MSER::info() const +{ + static volatile bool initialized = false; + if( !initialized ) + { + mser_info.addParam(this, "delta", delta); + mser_info.addParam(this, "minArea", minArea); + mser_info.addParam(this, "maxArea", maxArea); + mser_info.addParam(this, "maxVariation", maxVariation); + mser_info.addParam(this, "minDiversity", minDiversity); + mser_info.addParam(this, "maxEvolution", maxEvolution); + mser_info.addParam(this, "areaThreshold", areaThreshold); + mser_info.addParam(this, "minMargin", minMargin); + mser_info.addParam(this, "edgeBlurSize", edgeBlurSize); + + initialized = true; + } + return &mser_info; +} } diff --git a/modules/features2d/src/orb.cpp b/modules/features2d/src/orb.cpp index e878cff0cb..377801e1d6 100644 --- a/modules/features2d/src/orb.cpp +++ b/modules/features2d/src/orb.cpp @@ -94,27 +94,6 @@ HarrisResponses(const Mat& img, vector& pts, int blockSize, float harr //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -struct KeypointResponseGreaterThanThreshold -{ - KeypointResponseGreaterThanThreshold(float _value) : - value(_value) - { - } - inline bool operator()(const KeyPoint& kpt) const - { - return kpt.response >= value; - } - float value; -}; - -struct KeypointResponseGreater -{ - inline bool operator()(const KeyPoint& kp1, const KeyPoint& kp2) const - { - return kp1.response > kp2.response; - } -}; - static float IC_Angle(const Mat& image, const int half_k, Point2f pt, const vector & u_max) { @@ -224,37 +203,37 @@ static void computeOrbDescriptor(const KeyPoint& kpt, { for (int i = 0; i < dsize; ++i, pattern += 16) { - int t0, t1, t2, t3, a, b, k, val; + int t0, t1, t2, t3, u, v, k, val; t0 = GET_VALUE(0); t1 = GET_VALUE(1); t2 = GET_VALUE(2); t3 = GET_VALUE(3); - a = 0, b = 2; - if( t1 > t0 ) t0 = t1, a = 1; - if( t3 > t2 ) t2 = t3, b = 3; - k = t0 > t2 ? a : b; + u = 0, v = 2; + if( t1 > t0 ) t0 = t1, u = 1; + if( t3 > t2 ) t2 = t3, v = 3; + k = t0 > t2 ? u : v; val = k; t0 = GET_VALUE(4); t1 = GET_VALUE(5); t2 = GET_VALUE(6); t3 = GET_VALUE(7); - a = 0, b = 2; - if( t1 > t0 ) t0 = t1, a = 1; - if( t3 > t2 ) t2 = t3, b = 3; - k = t0 > t2 ? a : b; + u = 0, v = 2; + if( t1 > t0 ) t0 = t1, u = 1; + if( t3 > t2 ) t2 = t3, v = 3; + k = t0 > t2 ? u : v; val |= k << 2; t0 = GET_VALUE(8); t1 = GET_VALUE(9); t2 = GET_VALUE(10); t3 = GET_VALUE(11); - a = 0, b = 2; - if( t1 > t0 ) t0 = t1, a = 1; - if( t3 > t2 ) t2 = t3, b = 3; - k = t0 > t2 ? a : b; + u = 0, v = 2; + if( t1 > t0 ) t0 = t1, u = 1; + if( t3 > t2 ) t2 = t3, v = 3; + k = t0 > t2 ? u : v; val |= k << 4; t0 = GET_VALUE(12); t1 = GET_VALUE(13); t2 = GET_VALUE(14); t3 = GET_VALUE(15); - a = 0, b = 2; - if( t1 > t0 ) t0 = t1, a = 1; - if( t3 > t2 ) t2 = t3, b = 3; - k = t0 > t2 ? a : b; + u = 0, v = 2; + if( t1 > t0 ) t0 = t1, u = 1; + if( t3 > t2 ) t2 = t3, v = 3; + k = t0 > t2 ? u : v; val |= k << 6; desc[i] = (uchar)val; @@ -567,168 +546,195 @@ static void makeRandomPattern(int patchSize, Point* pattern, int npoints) } -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// -void ORB::CommonParams::read(const FileNode& fn) +static Algorithm* createORB() { return new ORB; } +static AlgorithmInfo orb_info("Feature2D.ORB", createORB); + +AlgorithmInfo* ORB::info() const { - scale_factor_ = fn["scaleFactor"]; - n_levels_ = int(fn["nLevels"]); - first_level_ = int(fn["firstLevel"]); - edge_threshold_ = fn["edgeThreshold"]; - patch_size_ = fn["patchSize"]; - WTA_K_ = fn["WTA_K"]; - if( WTA_K_ == 0 ) WTA_K_ = 2; - score_type_ = fn["scoreType"]; + static volatile bool initialized = false; + if( !initialized ) + { + orb_info.addParam(this, "nFeatures", nfeatures); + orb_info.addParam(this, "scaleFactor", scaleFactor); + orb_info.addParam(this, "nLevels", nlevels); + orb_info.addParam(this, "firstLevel", firstLevel); + orb_info.addParam(this, "edgeThreshold", edgeThreshold); + orb_info.addParam(this, "patchSize", patchSize); + orb_info.addParam(this, "WTA_K", WTA_K); + orb_info.addParam(this, "scoreType", scoreType); + + initialized = true; + } + return &orb_info; } - -void ORB::CommonParams::write(FileStorage& fs) const + +static inline float getScale(int level, int firstLevel, double scaleFactor) { - fs << "scaleFactor" << scale_factor_; - fs << "nLevels" << int(n_levels_); - fs << "firstLevel" << int(first_level_); - fs << "edgeThreshold" << int(edge_threshold_); - fs << "patchSize" << int(patch_size_); - fs << "WTA_K" << WTA_K_; - fs << "scoreType" << score_type_; -} - -void ORB::read(const FileNode& fn) -{ - CommonParams params; - params.read(fn); - int n_features = int(fn["nFeatures"]); - *this = ORB(n_features, params); -} - -void ORB::write(FileStorage& fs) const -{ - params_.write(fs); - fs << "nFeatures" << int(n_features_); -} - -static inline float get_scale(const ORB::CommonParams& params, int level) -{ - return std::pow(params.scale_factor_, float(level) - float(params.first_level_)); + return (float)std::pow(scaleFactor, (double)(level - firstLevel)); } /** Constructor * @param detector_params parameters to use */ -ORB::ORB(size_t n_features, const CommonParams & detector_params) : - params_(detector_params), n_features_(n_features) -{ - // fill the extractors and descriptors for the corresponding scales - int n_levels = (int)params_.n_levels_; - float factor = (float)(1.0 / params_.scale_factor_); - float n_desired_features_per_scale = n_features_*(1 - factor)/(1 - (float)pow((double)factor, (double)n_levels)); - - n_features_per_level_.resize(n_levels); - int sum_n_features = 0; - for( int level = 0; level < n_levels-1; level++ ) - { - n_features_per_level_[level] = cvRound(n_desired_features_per_scale); - sum_n_features += n_features_per_level_[level]; - n_desired_features_per_scale *= factor; - } - n_features_per_level_[n_levels-1] = n_features - sum_n_features; - - // Make sure we forget about what is too close to the boundary - //params_.edge_threshold_ = std::max(params_.edge_threshold_, params_.patch_size_/2 + kKernelWidth / 2 + 2); - - // pre-compute the end of a row in a circular patch - int half_patch_size = params_.patch_size_ / 2; - u_max_.resize(half_patch_size + 1); - for (int v = 0; v <= half_patch_size * sqrt(2.f) / 2 + 1; ++v) - u_max_[v] = cvRound(sqrt(float(half_patch_size * half_patch_size - v * v))); - - // Make sure we are symmetric - for (int v = half_patch_size, v_0 = 0; v >= half_patch_size * sqrt(2.f) / 2; --v) - { - while (u_max_[v_0] == u_max_[v_0 + 1]) - ++v_0; - u_max_[v] = v_0; - ++v_0; - } - - const int npoints = 512; - Point pattern_buf[npoints]; - const Point* pattern0 = (const Point*)bit_pattern_31_; - if( params_.patch_size_ != 31 ) - { - pattern0 = pattern_buf; - makeRandomPattern(params_.patch_size_, pattern_buf, npoints); - } - - CV_Assert( params_.WTA_K_ == 2 || params_.WTA_K_ == 3 || params_.WTA_K_ == 4 ); - - if( params_.WTA_K_ == 2 ) - std::copy(pattern0, pattern0 + npoints, back_inserter(pattern)); - else - { - int ntuples = descriptorSize()*4; - initializeOrbPattern(pattern0, pattern, ntuples, params_.WTA_K_, npoints); - } -} +ORB::ORB(int _nfeatures, float _scaleFactor, int _nlevels, int _edgeThreshold, + int _firstLevel, int WTA_K, int _scoreType, int _patchSize) : + nfeatures(_nfeatures), scaleFactor(_scaleFactor), nlevels(_nlevels), + edgeThreshold(_edgeThreshold), firstLevel(_firstLevel), WTA_K(WTA_K), + scoreType(_scoreType), patchSize(_patchSize) +{} -/** destructor to empty the patterns */ -ORB::~ORB() -{ -} - -/** returns the descriptor size in bytes */ int ORB::descriptorSize() const { return kBytes; } +int ORB::descriptorType() const +{ + return CV_8U; +} + /** Compute the ORB features and descriptors on an image * @param img the image to compute the features and descriptors on * @param mask the mask to apply * @param keypoints the resulting keypoints */ -void ORB::operator()(const Mat &image, const Mat &mask, vector & keypoints) +void ORB::operator()(InputArray image, InputArray mask, vector& keypoints) const { - Mat empty_descriptors; - this->operator ()(image, mask, keypoints, empty_descriptors, true, false); + (*this)(image, mask, keypoints, noArray(), false); } -/** Compute the ORB features and descriptors on an image - * @param img the image to compute the features and descriptors on - * @param mask the mask to apply + +/** Compute the ORB keypoint orientations + * @param image the image to compute the features and descriptors on + * @param integral_image the integral image of the iamge (can be empty, but the computation will be slower) + * @param scale the scale at which we compute the orientation * @param keypoints the resulting keypoints - * @param descriptors the resulting descriptors - * @param useProvidedKeypoints if true, the keypoints are used as an input */ -void ORB::operator()(const Mat &image, const Mat &mask, vector & keypoints, - Mat & descriptors, bool useProvidedKeypoints) +static void computeOrientation(const Mat& image, vector& keypoints, + int halfPatchSize, const vector& umax) { - this->operator ()(image, mask, keypoints, descriptors, !useProvidedKeypoints, true); -} - -//takes keypoints and culls them by the response -static void cull(vector& keypoints, size_t n_points) -{ - //this is only necessary if the keypoints size is greater than the number of desired points. - if (keypoints.size() > n_points) + // Process each keypoint + for (vector::iterator keypoint = keypoints.begin(), + keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint) { - if (n_points==0) { - keypoints.clear(); - return; - } - //first use nth element to partition the keypoints into the best and worst. - std::nth_element(keypoints.begin(), keypoints.begin() + n_points, keypoints.end(), KeypointResponseGreater()); - //this is the boundary response, and in the case of FAST may be ambigous - float ambiguous_response = keypoints[n_points - 1].response; - //use std::partition to grab all of the keypoints with the boundary response. - vector::const_iterator new_end = - std::partition(keypoints.begin() + n_points, keypoints.end(), - KeypointResponseGreaterThanThreshold(ambiguous_response)); - //resize the keypoints, given this new end point. nth_element and partition reordered the points inplace - keypoints.resize(new_end - keypoints.begin()); + keypoint->angle = IC_Angle(image, halfPatchSize, keypoint->pt, umax); } } + +/** Compute the ORB keypoints on an image + * @param image_pyramid the image pyramid to compute the features and descriptors on + * @param mask_pyramid the masks to apply at every level + * @param keypoints the resulting keypoints, clustered per level + */ +static void computeKeyPoints(const vector& imagePyramid, + const vector& maskPyramid, + vector >& allKeypoints, + int nfeatures, int firstLevel, double scaleFactor, + int edgeThreshold, int patchSize, int scoreType ) +{ + int nlevels = (int)imagePyramid.size(); + vector nfeaturesPerLevel(nlevels); + + // fill the extractors and descriptors for the corresponding scales + float factor = (float)(1.0 / scaleFactor); + float ndesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - (float)pow((double)factor, (double)nlevels)); + + int sumFeatures = 0; + for( int level = 0; level < nlevels-1; level++ ) + { + nfeaturesPerLevel[level] = cvRound(ndesiredFeaturesPerScale); + sumFeatures += nfeaturesPerLevel[level]; + ndesiredFeaturesPerScale *= factor; + } + nfeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0); + + // Make sure we forget about what is too close to the boundary + //edge_threshold_ = std::max(edge_threshold_, patch_size_/2 + kKernelWidth / 2 + 2); + + // pre-compute the end of a row in a circular patch + int halfPatchSize = patchSize / 2; + vector umax(halfPatchSize + 1); + + int v, v0, vmax = cvFloor(halfPatchSize * sqrt(2.f) / 2 + 1); + int vmin = cvCeil(halfPatchSize * sqrt(2.f) / 2); + for (v = 0; v <= vmax; ++v) + umax[v] = cvRound(sqrt(halfPatchSize * halfPatchSize - v * v)); + + // Make sure we are symmetric + for (v = halfPatchSize, v0 = 0; v >= vmin; --v) + { + while (umax[v0] == umax[v0 + 1]) + ++v0; + umax[v] = v0; + ++v0; + } + + allKeypoints.resize(nlevels); + + for (int level = 0; level < nlevels; ++level) + { + int nfeatures = nfeaturesPerLevel[level]; + allKeypoints[level].reserve(nfeatures*2); + + vector & keypoints = allKeypoints[level]; + + // Detect FAST features, 20 is a good threshold + FastFeatureDetector fd(20, true); + fd.detect(imagePyramid[level], keypoints, maskPyramid[level]); + + // Remove keypoints very close to the border + KeyPointsFilter::runByImageBorder(keypoints, imagePyramid[level].size(), edgeThreshold); + + if( scoreType == ORB::HARRIS_SCORE ) + { + // Keep more points than necessary as FAST does not give amazing corners + KeyPointsFilter::retainBest(keypoints, 2 * nfeatures); + + // Compute the Harris cornerness (better scoring than FAST) + HarrisResponses(imagePyramid[level], keypoints, 7, HARRIS_K); + } + + //cull to the final desired level, using the new Harris scores or the original FAST scores. + KeyPointsFilter::retainBest(keypoints, nfeatures); + + float sf = getScale(level, firstLevel, scaleFactor); + + // Set the level of the coordinates + for (vector::iterator keypoint = keypoints.begin(), + keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint) + { + keypoint->octave = level; + keypoint->size = patchSize*sf; + } + + computeOrientation(imagePyramid[level], keypoints, halfPatchSize, umax); + } +} + + +/** Compute the ORB decriptors + * @param image the image to compute the features and descriptors on + * @param integral_image the integral image of the image (can be empty, but the computation will be slower) + * @param level the scale at which we compute the orientation + * @param keypoints the keypoints to use + * @param descriptors the resulting descriptors + */ +static void computeDescriptors(const Mat& image, vector& keypoints, Mat& descriptors, + const vector& pattern, int dsize, int WTA_K) +{ + //convert to grayscale if more than one color + CV_Assert(image.type() == CV_8UC1); + //create the descriptor mat, keypoints.size() rows, BYTES cols + descriptors = Mat::zeros((int)keypoints.size(), dsize, CV_8UC1); + + for (size_t i = 0; i < keypoints.size(); i++) + computeOrbDescriptor(keypoints[i], image, &pattern[0], descriptors.ptr((int)i), dsize, WTA_K); +} + /** Compute the ORB features and descriptors on an image * @param img the image to compute the features and descriptors on @@ -738,24 +744,25 @@ static void cull(vector& keypoints, size_t n_points) * @param do_keypoints if true, the keypoints are computed, otherwise used as an input * @param do_descriptors if true, also computes the descriptors */ -void ORB::operator()(const Mat &image_in, const Mat &mask, vector & keypoints_in_out, - Mat& descriptors, bool do_keypoints, bool do_descriptors) +void ORB::operator()( InputArray _image, InputArray _mask, vector& _keypoints, + OutputArray _descriptors, bool useProvidedKeypoints) const { - if (((!do_keypoints) && (!do_descriptors)) || (image_in.empty())) + bool do_keypoints = !useProvidedKeypoints; + bool do_descriptors = _descriptors.needed(); + + if( (!do_keypoints && !do_descriptors) || _image.empty() ) return; //ROI handling const int HARRIS_BLOCK_SIZE = 9; - int half_patch_size = params_.patch_size_ / 2; - int border = std::max(params_.edge_threshold_, std::max(half_patch_size, HARRIS_BLOCK_SIZE/2))+1; + int halfPatchSize = patchSize / 2; + int border = std::max(edgeThreshold, std::max(halfPatchSize, HARRIS_BLOCK_SIZE/2))+1; - Mat image; - if (image_in.type() != CV_8UC1) - cvtColor(image_in, image, CV_BGR2GRAY); - else - image = image_in; + Mat image = _image.getMat(), mask = _mask.getMat(); + if( image.type() != CV_8UC1 ) + cvtColor(_image, image, CV_BGR2GRAY); - int n_levels = (int)params_.n_levels_; + int nlevels = this->nlevels; if( !do_keypoints ) { @@ -768,46 +775,46 @@ void ORB::operator()(const Mat &image_in, const Mat &mask, vector & ke // // In short, ultimately the descriptor should // ignore octave parameter and deal only with the keypoint size. - n_levels = 0; - for( size_t i = 0; i < keypoints_in_out.size(); i++ ) - n_levels = std::max(n_levels, std::max(keypoints_in_out[i].octave, 0)); - n_levels++; + nlevels = 0; + for( size_t i = 0; i < _keypoints.size(); i++ ) + nlevels = std::max(nlevels, std::max(_keypoints[i].octave, 0)); + nlevels++; } // Pre-compute the scale pyramids - vector image_pyramid(n_levels), mask_pyramid(n_levels); - for (int level = 0; level < n_levels; ++level) + vector imagePyramid(nlevels), maskPyramid(nlevels); + for (int level = 0; level < nlevels; ++level) { - float scale = 1/get_scale(params_, level); + float scale = 1/getScale(level, firstLevel, scale); Size sz(cvRound(image.cols*scale), cvRound(image.rows*scale)); Size wholeSize(sz.width + border*2, sz.height + border*2); Mat temp(wholeSize, image.type()), masktemp; - image_pyramid[level] = temp(Rect(border, border, sz.width, sz.height)); + imagePyramid[level] = temp(Rect(border, border, sz.width, sz.height)); if( !mask.empty() ) { masktemp = Mat(wholeSize, mask.type()); - mask_pyramid[level] = masktemp(Rect(border, border, sz.width, sz.height)); + maskPyramid[level] = masktemp(Rect(border, border, sz.width, sz.height)); } // Compute the resized image - if (level != (int)params_.first_level_) + if( level != firstLevel ) { - if( level < (int)params_.first_level_ ) + if( level < firstLevel ) { - resize(image, image_pyramid[level], sz, scale, scale, INTER_LINEAR); + resize(image, imagePyramid[level], sz, scale, scale, INTER_LINEAR); if (!mask.empty()) - resize(mask, mask_pyramid[level], sz, scale, scale, INTER_LINEAR); - copyMakeBorder(image_pyramid[level], temp, border, border, border, border, + resize(mask, maskPyramid[level], sz, scale, scale, INTER_LINEAR); + copyMakeBorder(imagePyramid[level], temp, border, border, border, border, BORDER_REFLECT_101+BORDER_ISOLATED); } else { - float sf = params_.scale_factor_; - resize(image_pyramid[level-1], image_pyramid[level], sz, 1./sf, 1./sf, INTER_LINEAR); + float sf = scaleFactor; + resize(imagePyramid[level-1], imagePyramid[level], sz, 1./sf, 1./sf, INTER_LINEAR); if (!mask.empty()) - resize(mask_pyramid[level-1], mask_pyramid[level], sz, 1./sf, 1./sf, INTER_LINEAR); - copyMakeBorder(image_pyramid[level], temp, border, border, border, border, + resize(maskPyramid[level-1], maskPyramid[level], sz, 1./sf, 1./sf, INTER_LINEAR); + copyMakeBorder(imagePyramid[level], temp, border, border, border, border, BORDER_REFLECT_101+BORDER_ISOLATED); } } @@ -815,22 +822,24 @@ void ORB::operator()(const Mat &image_in, const Mat &mask, vector & ke { copyMakeBorder(image, temp, border, border, border, border, BORDER_REFLECT_101); - image.copyTo(image_pyramid[level]); + image.copyTo(imagePyramid[level]); if( !mask.empty() ) - mask.copyTo(mask_pyramid[level]); + mask.copyTo(maskPyramid[level]); } if( !mask.empty() ) - copyMakeBorder(mask_pyramid[level], masktemp, border, border, border, border, + copyMakeBorder(maskPyramid[level], masktemp, border, border, border, border, BORDER_CONSTANT+BORDER_ISOLATED); } // Pre-compute the keypoints (we keep the best over all scales, so this has to be done beforehand - vector < vector > all_keypoints; - if (do_keypoints) + vector < vector > allKeypoints; + if( do_keypoints ) { // Get keypoints, those will be far enough from the border that no check will be required for the descriptor - computeKeyPoints(image_pyramid, mask_pyramid, all_keypoints); + computeKeyPoints(imagePyramid, maskPyramid, allKeypoints, + nfeatures, firstLevel, scaleFactor, + edgeThreshold, patchSize, scoreType); // make sure we have the right number of keypoints keypoints /*vector temp; @@ -842,7 +851,7 @@ void ORB::operator()(const Mat &image_in, const Mat &mask, vector & ke keypoints.clear(); } - cull(temp, n_features_); + KeyPoint::retainBest(temp, n_features_); for (vector::iterator keypoint = temp.begin(), keypoint_end = temp.end(); keypoint != keypoint_end; ++keypoint) @@ -851,48 +860,72 @@ void ORB::operator()(const Mat &image_in, const Mat &mask, vector & ke else { // Remove keypoints very close to the border - KeyPointsFilter::runByImageBorder(keypoints_in_out, image.size(), params_.edge_threshold_); + KeyPointsFilter::runByImageBorder(_keypoints, image.size(), edgeThreshold); // Cluster the input keypoints depending on the level they were computed at - all_keypoints.resize(n_levels); - for (vector::iterator keypoint = keypoints_in_out.begin(), - keypoint_end = keypoints_in_out.end(); keypoint != keypoint_end; ++keypoint) - all_keypoints[keypoint->octave].push_back(*keypoint); + allKeypoints.resize(nlevels); + for (vector::iterator keypoint = _keypoints.begin(), + keypointEnd = _keypoints.end(); keypoint != keypointEnd; ++keypoint) + allKeypoints[keypoint->octave].push_back(*keypoint); // Make sure we rescale the coordinates - for (int level = 0; level < n_levels; ++level) + for (int level = 0; level < nlevels; ++level) { - if (level == (int)params_.first_level_) + if (level == firstLevel) continue; - vector & keypoints = all_keypoints[level]; - float scale = 1/get_scale(params_, level); + vector & keypoints = allKeypoints[level]; + float scale = 1/getScale(level, firstLevel, scaleFactor); for (vector::iterator keypoint = keypoints.begin(), - keypoint_end = keypoints.end(); keypoint != keypoint_end; ++keypoint) + keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint) keypoint->pt *= scale; } } - if (do_descriptors) + Mat descriptors; + vector pattern; + + if( do_descriptors ) { int nkeypoints = 0; - for (int level = 0; level < n_levels; ++level) - nkeypoints += (int)all_keypoints[level].size(); + for (int level = 0; level < nlevels; ++level) + nkeypoints += (int)allKeypoints[level].size(); if( nkeypoints == 0 ) - descriptors.release(); + _descriptors.release(); else - descriptors.create(nkeypoints, descriptorSize(), CV_8U); + { + _descriptors.create(nkeypoints, descriptorSize(), CV_8U); + descriptors = _descriptors.getMat(); + } + + const int npoints = 512; + Point patternbuf[npoints]; + const Point* pattern0 = (const Point*)bit_pattern_31_; + + if( patchSize != 31 ) + { + pattern0 = patternbuf; + makeRandomPattern(patchSize, patternbuf, npoints); + } + + CV_Assert( WTA_K == 2 || WTA_K == 3 || WTA_K == 4 ); + + if( WTA_K == 2 ) + std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern)); + else + { + int ntuples = descriptorSize()*4; + initializeOrbPattern(pattern0, pattern, ntuples, WTA_K, npoints); + } } - keypoints_in_out.clear(); + _keypoints.clear(); int offset = 0; - for (int level = 0; level < n_levels; ++level) + for (int level = 0; level < nlevels; ++level) { // Get the features and compute their orientation - vector& keypoints = all_keypoints[level]; + vector& keypoints = allKeypoints[level]; int nkeypoints = (int)keypoints.size(); - if (nkeypoints==0) - continue; // Compute the descriptors if (do_descriptors) @@ -900,122 +933,33 @@ void ORB::operator()(const Mat &image_in, const Mat &mask, vector & ke Mat desc = descriptors.rowRange(offset, offset + nkeypoints); offset += nkeypoints; // preprocess the resized image - Mat& working_mat = image_pyramid[level]; + Mat& workingMat = imagePyramid[level]; //boxFilter(working_mat, working_mat, working_mat.depth(), Size(5,5), Point(-1,-1), true, BORDER_REFLECT_101); - GaussianBlur(working_mat, working_mat, Size(7, 7), 2, 2, BORDER_REFLECT_101); - computeDescriptors(working_mat, Mat(), level, keypoints, desc); + GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101); + computeDescriptors(workingMat, keypoints, desc, pattern, descriptorSize(), WTA_K); } // Copy to the output data - if (level != (int)params_.first_level_) + if (level != firstLevel) { - float scale = get_scale(params_, level); + float scale = getScale(level, firstLevel, scaleFactor); for (vector::iterator keypoint = keypoints.begin(), - keypoint_end = keypoints.end(); keypoint != keypoint_end; ++keypoint) + keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint) keypoint->pt *= scale; } // And add the keypoints to the output - keypoints_in_out.insert(keypoints_in_out.end(), keypoints.begin(), keypoints.end()); + _keypoints.insert(_keypoints.end(), keypoints.begin(), keypoints.end()); } } - -/** Compute the ORB keypoints on an image - * @param image_pyramid the image pyramid to compute the features and descriptors on - * @param mask_pyramid the masks to apply at every level - * @param keypoints the resulting keypoints, clustered per level - */ -void ORB::computeKeyPoints(const vector& image_pyramid, - const vector& mask_pyramid, - vector >& all_keypoints_out) const -{ - all_keypoints_out.resize(params_.n_levels_); - for (int level = 0; level < (int)params_.n_levels_; ++level) - { - int n_features = n_features_per_level_[level]; - all_keypoints_out[level].reserve(n_features*2); - - vector & keypoints = all_keypoints_out[level]; - - // Detect FAST features, 20 is a good threshold - FastFeatureDetector fd(20, true); - fd.detect(image_pyramid[level], keypoints, mask_pyramid[level]); - - // Remove keypoints very close to the border - KeyPointsFilter::runByImageBorder(keypoints, image_pyramid[level].size(), params_.edge_threshold_); - - if( params_.score_type_ == CommonParams::HARRIS_SCORE ) - { - // Keep more points than necessary as FAST does not give amazing corners - cull(keypoints, 2 * n_features); - - // Compute the Harris cornerness (better scoring than FAST) - HarrisResponses(image_pyramid[level], keypoints, 7, HARRIS_K); - } - - //cull to the final desired level, using the new Harris scores or the original FAST scores. - cull(keypoints, n_features); - - float sf = get_scale(params_, level); - - // Set the level of the coordinates - for (vector::iterator keypoint = keypoints.begin(), - keypoint_end = keypoints.end(); keypoint != keypoint_end; ++keypoint) - { - keypoint->octave = level; - keypoint->size = params_.patch_size_*sf; - } - - computeOrientation(image_pyramid[level], Mat(), level, keypoints); - } -} - -/** Compute the ORB keypoint orientations - * @param image the image to compute the features and descriptors on - * @param integral_image the integral image of the iamge (can be empty, but the computation will be slower) - * @param scale the scale at which we compute the orientation - * @param keypoints the resulting keypoints - */ -void ORB::computeOrientation(const Mat& image, const Mat&, unsigned int /*scale*/, - vector& keypoints) const +void ORB::detectImpl( const Mat& image, vector& keypoints, const Mat& mask) const { - int half_patch_size = params_.patch_size_/2; - - // Process each keypoint - for (vector::iterator keypoint = keypoints.begin(), - keypoint_end = keypoints.end(); keypoint != keypoint_end; ++keypoint) - { - keypoint->angle = IC_Angle(image, half_patch_size, keypoint->pt, u_max_); - } -} + (*this)(image, mask, keypoints, noArray(), false); +} -/** Compute the integral image and upadte the cached values - * @param image the image to compute the features and descriptors on - * @param level the scale at which we compute the orientation - * @param descriptors the resulting descriptors - */ -void ORB::computeIntegralImage(const Mat&, unsigned int, Mat&) +void ORB::computeImpl( const Mat& image, vector& keypoints, Mat& descriptors) const { -} - -/** Compute the ORB decriptors - * @param image the image to compute the features and descriptors on - * @param integral_image the integral image of the image (can be empty, but the computation will be slower) - * @param level the scale at which we compute the orientation - * @param keypoints the keypoints to use - * @param descriptors the resulting descriptors - */ -void ORB::computeDescriptors(const Mat& image, const Mat& /*integral_image*/, unsigned int, - vector& keypoints, Mat& descriptors) const -{ - //convert to grayscale if more than one color - CV_Assert(image.type() == CV_8UC1); - //create the descriptor mat, keypoints.size() rows, BYTES cols - int dsize = descriptorSize(); - descriptors = Mat::zeros((int)keypoints.size(), dsize, CV_8UC1); - - for (size_t i = 0; i < keypoints.size(); i++) - computeOrbDescriptor(keypoints[i], image, &pattern[0], descriptors.ptr((int)i), dsize, params_.WTA_K_); + (*this)(image, Mat(), keypoints, descriptors, true); } } diff --git a/modules/features2d/src/sift.cpp b/modules/features2d/src/sift.cpp deleted file mode 100644 index 179097f636..0000000000 --- a/modules/features2d/src/sift.cpp +++ /dev/null @@ -1,1772 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// Intel License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2000, Intel Corporation, all rights reserved. -// Third party copyrights are property of their respective owners. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * The name of Intel Corporation may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors "as is" and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -// -//M*/ - -/****************************************************************************************\ - Implementation of SIFT taken from http://blogs.oregonstate.edu/hess/code/sift/ -\****************************************************************************************/ - -// Copyright (c) 2006-2010, Rob Hess -// All rights reserved. - -// The following patent has been issued for methods embodied in this -// software: "Method and apparatus for identifying scale invariant features -// in an image and use of same for locating an object in an image," David -// G. Lowe, US Patent 6,711,293 (March 23, 2004). Provisional application -// filed March 8, 1999. Asignee: The University of British Columbia. For -// further details, contact David Lowe (lowe@cs.ubc.ca) or the -// University-Industry Liaison Office of the University of British -// Columbia. - -// Note that restrictions imposed by this patent (and possibly others) -// exist independently of and may be in conflict with the freedoms granted -// in this license, which refers to copyright of the program, not patents -// for any methods that it implements. Both copyright and patent law must -// be obeyed to legally use and redistribute this program and it is not the -// purpose of this license to induce you to infringe any patents or other -// property right claims or to contest validity of any such claims. If you -// redistribute or use the program, then this license merely protects you -// from committing copyright infringement. It does not protect you from -// committing patent infringement. So, before you do anything with this -// program, make sure that you have permission to do so not merely in terms -// of copyright, but also in terms of patent law. - -// Please note that this license is not to be understood as a guarantee -// either. If you use the program according to this license, but in -// conflict with patent law, it does not mean that the licensor will refund -// you for any losses that you incur if you are sued for your patent -// infringement. - -// 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 and -// patent notices, this list of conditions and the following -// disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in -// the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Oregon State University nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. - -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// HOLDER 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. - -#include "precomp.hpp" - -const double a_180divPI = 180./CV_PI; -const double a_PIdiv180 = CV_PI/180.; - -static inline float getOctaveSamplingPeriod( int o ) -{ - return (o >= 0) ? (1 << o) : 1.0f / (1 << -o) ; -} - -/* - Interpolates a histogram peak from left, center, and right values -*/ -#define interp_hist_peak( l, c, r ) ( 0.5 * ((l)-(r)) / ((l) - 2.0*(c) + (r)) ) - - -// utils.h -/** - A function to get a pixel value from a 32-bit floating-point image. - - @param img an image - @param r row - @param c column - @return Returns the value of the pixel at (\a r, \a c) in \a img -*/ -static inline float pixval32f( IplImage* img, int r, int c ) -{ - return ( (float*)(img->imageData + img->widthStep*r) )[c]; -} - - -// imgfeatures.h - -/** holds feature data relevant to detection */ -struct detection_data -{ - int r; // row - int c; // col - int octv; - int intvl; - double subintvl; - double scl_octv; -}; - -/** max feature descriptor length */ -#define FEATURE_MAX_D 128 - -/** - Structure to represent an affine invariant image feature. The fields - x, y, a, b, c represent the affine region around the feature: - - a(x-u)(x-u) + 2b(x-u)(y-v) + c(y-v)(y-v) = 1 -*/ -struct feature -{ - double x; /**< x coord */ - double y; /**< y coord */ - - double scl; /**< scale of a Lowe-style feature */ - double ori; /**< orientation of a Lowe-style feature */ - - int d; /**< descriptor length */ - double descr[FEATURE_MAX_D]; /**< descriptor */ - - detection_data* feature_data; /**< user-definable data */ - - int class_id; - float response; -}; - -/******************************* Defs and macros *****************************/ - -/** default number of sampled intervals per octave */ -#define SIFT_INTVLS 3 - -/** default sigma for initial gaussian smoothing */ -#define SIFT_SIGMA 1.6 - -/** default threshold on keypoint contrast |D(x)| */ -#define SIFT_CONTR_THR 0.04 - -/** default threshold on keypoint ratio of principle curvatures */ -#define SIFT_CURV_THR 10 - -/** double image size before pyramid construction? */ -#define SIFT_IMG_DBL 1 - -/** default width of descriptor histogram array */ -#define SIFT_DESCR_WIDTH 4 - -/** default number of bins per histogram in descriptor array */ -#define SIFT_DESCR_HIST_BINS 8 - -/* assumed gaussian blur for input image */ -#define SIFT_INIT_SIGMA 0.5 - -/* width of border in which to ignore keypoints */ -#define SIFT_IMG_BORDER 5 - -/* maximum steps of keypoint interpolation before failure */ -#define SIFT_MAX_INTERP_STEPS 5 - -/* default number of bins in histogram for orientation assignment */ -#define SIFT_ORI_HIST_BINS 36 - -/* determines gaussian sigma for orientation assignment */ -#define SIFT_ORI_SIG_FCTR 1.5 - -/* determines the radius of the region used in orientation assignment */ -#define SIFT_ORI_RADIUS 3.0 * SIFT_ORI_SIG_FCTR - -/* number of passes of orientation histogram smoothing */ -#define SIFT_ORI_SMOOTH_PASSES 2 - -/* orientation magnitude relative to max that results in new feature */ -#define SIFT_ORI_PEAK_RATIO 0.8 - -/* determines the size of a single descriptor orientation histogram */ -#define SIFT_DESCR_SCL_FCTR 3.0 - -/* threshold on magnitude of elements of descriptor vector */ -#define SIFT_DESCR_MAG_THR 0.2 - -/* factor used to convert floating-point descriptor to unsigned char */ -#define SIFT_INT_DESCR_FCTR 512.0 - -/**************************************************************************/ - -/* - Converts an image to 32-bit grayscale - - @param img a 3-channel 8-bit color (BGR) or 8-bit gray image - - @return Returns a 32-bit grayscale image -*/ -static IplImage* convert_to_gray32( IplImage* img ) -{ - IplImage* gray8, * gray32; - - gray32 = cvCreateImage( cvGetSize(img), IPL_DEPTH_32F, 1 ); - if( img->nChannels == 1 ) - gray8 = (IplImage*)cvClone( img ); - else - { - gray8 = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 1 ); - cvCvtColor( img, gray8, CV_BGR2GRAY ); - } - cvConvertScale( gray8, gray32, 1.0 / 255.0, 0 ); - - cvReleaseImage( &gray8 ); - return gray32; -} - -/* - Converts an image to 8-bit grayscale and Gaussian-smooths it. The image is - optionally doubled in size prior to smoothing. - - @param img input image - @param img_dbl if true, image is doubled in size prior to smoothing - @param sigma total std of Gaussian smoothing -*/ -static IplImage* create_init_img( IplImage* img, int img_dbl, double sigma ) -{ - IplImage* gray, * dbl; - double sig_diff; - - gray = convert_to_gray32( img ); - if( img_dbl ) - { - sig_diff = sqrt( sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA * 4 ); - dbl = cvCreateImage( cvSize( img->width*2, img->height*2 ), - IPL_DEPTH_32F, 1 ); - cvResize( gray, dbl, CV_INTER_CUBIC ); - cvSmooth( dbl, dbl, CV_GAUSSIAN, 0, 0, sig_diff, sig_diff ); - cvReleaseImage( &gray ); - return dbl; - } - else - { - sig_diff = sqrt( sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA ); - cvSmooth( gray, gray, CV_GAUSSIAN, 0, 0, sig_diff, sig_diff ); - return gray; - } -} - -/* - Downsamples an image to a quarter of its size (half in each dimension) - using nearest-neighbor interpolation - - @param img an image - - @return Returns an image whose dimensions are half those of img -*/ -static IplImage* downsample( IplImage* img ) -{ - IplImage* smaller = cvCreateImage( cvSize(img->width / 2, img->height / 2), - img->depth, img->nChannels ); - cvResize( img, smaller, CV_INTER_NN ); - - return smaller; -} - -/* - Builds Gaussian scale space pyramid from an image - - @param base base image of the pyramid - @param octvs number of octaves of scale space - @param intvls number of intervals per octave - @param sigma amount of Gaussian smoothing per octave - - @return Returns a Gaussian scale space pyramid as an octvs x (intvls + 3) - array -*/ -static IplImage*** build_gauss_pyr( IplImage* base, int octvs, - int intvls, double sigma ) -{ - IplImage*** gauss_pyr; - const int _intvls = intvls; -#if defined WIN32 || defined _WIN32 || defined WINCE - double *sig = new double[_intvls+3]; -#else - double sig[_intvls+3]; -#endif - double sig_total, sig_prev, k; - int i, o; - - gauss_pyr = (IplImage***)calloc( octvs, sizeof( IplImage** ) ); - for( i = 0; i < octvs; i++ ) - gauss_pyr[i] = (IplImage**)calloc( intvls + 3, sizeof( IplImage *) ); - - /* - precompute Gaussian sigmas using the following formula: - - \sigma_{total}^2 = \sigma_{i}^2 + \sigma_{i-1}^2 - */ - sig[0] = sigma; - k = pow( 2.0, 1.0 / intvls ); - for( i = 1; i < intvls + 3; i++ ) - { - sig_prev = pow( k, i - 1 ) * sigma; - sig_total = sig_prev * k; - sig[i] = sqrt( sig_total * sig_total - sig_prev * sig_prev ); - } - - for( o = 0; o < octvs; o++ ) - for( i = 0; i < intvls + 3; i++ ) - { - if( o == 0 && i == 0 ) - gauss_pyr[o][i] = cvCloneImage(base); - - /* base of new octave is halved image from end of previous octave */ - else if( i == 0 ) - gauss_pyr[o][i] = downsample( gauss_pyr[o-1][intvls] ); - - /* blur the current octave's last image to create the next one */ - else - { - gauss_pyr[o][i] = cvCreateImage( cvGetSize(gauss_pyr[o][i-1]), - IPL_DEPTH_32F, 1 ); - cvSmooth( gauss_pyr[o][i-1], gauss_pyr[o][i], - CV_GAUSSIAN, 0, 0, sig[i], sig[i] ); - } - } - -#if defined WIN32 || defined _WIN32 || defined WINCE - delete[] sig; -#endif - - return gauss_pyr; -} - -/* - Builds a difference of Gaussians scale space pyramid by subtracting adjacent - intervals of a Gaussian pyramid - - @param gauss_pyr Gaussian scale-space pyramid - @param octvs number of octaves of scale space - @param intvls number of intervals per octave - - @return Returns a difference of Gaussians scale space pyramid as an - octvs x (intvls + 2) array -*/ -static IplImage*** build_dog_pyr( IplImage*** gauss_pyr, int octvs, int intvls ) -{ - IplImage*** dog_pyr; - int i, o; - - dog_pyr = (IplImage***)calloc( octvs, sizeof( IplImage** ) ); - for( i = 0; i < octvs; i++ ) - dog_pyr[i] = (IplImage**)calloc( intvls + 2, sizeof(IplImage*) ); - - for( o = 0; o < octvs; o++ ) - for( i = 0; i < intvls + 2; i++ ) - { - dog_pyr[o][i] = cvCreateImage( cvGetSize(gauss_pyr[o][i]), - IPL_DEPTH_32F, 1 ); - cvSub( gauss_pyr[o][i+1], gauss_pyr[o][i], dog_pyr[o][i], NULL ); - } - - return dog_pyr; -} - -/* - Determines whether a pixel is a scale-space extremum by comparing it to it's - 3x3x3 pixel neighborhood. - - @param dog_pyr DoG scale space pyramid - @param octv pixel's scale space octave - @param intvl pixel's within-octave interval - @param r pixel's image row - @param c pixel's image col - - @return Returns 1 if the specified pixel is an extremum (max or min) among - it's 3x3x3 pixel neighborhood. -*/ -static int is_extremum( IplImage*** dog_pyr, int octv, int intvl, int r, int c ) -{ - double val = pixval32f( dog_pyr[octv][intvl], r, c ); - int i, j, k; - - /* check for maximum */ - if( val > 0 ) - { - for( i = -1; i <= 1; i++ ) - for( j = -1; j <= 1; j++ ) - for( k = -1; k <= 1; k++ ) - if( val < pixval32f( dog_pyr[octv][intvl+i], r + j, c + k ) ) - return 0; - } - - /* check for minimum */ - else - { - for( i = -1; i <= 1; i++ ) - for( j = -1; j <= 1; j++ ) - for( k = -1; k <= 1; k++ ) - if( val > pixval32f( dog_pyr[octv][intvl+i], r + j, c + k ) ) - return 0; - } - - return 1; -} - -/* - Computes the partial derivatives in x, y, and scale of a pixel in the DoG - scale space pyramid. - - @param dog_pyr DoG scale space pyramid - @param octv pixel's octave in dog_pyr - @param intvl pixel's interval in octv - @param r pixel's image row - @param c pixel's image col - - @return Returns the vector of partial derivatives for pixel I - { dI/dx, dI/dy, dI/ds }^T as a CvMat* -*/ -static CvMat* deriv_3D( IplImage*** dog_pyr, int octv, int intvl, int r, int c ) -{ - CvMat* dI; - double dx, dy, ds; - - dx = ( pixval32f( dog_pyr[octv][intvl], r, c+1 ) - - pixval32f( dog_pyr[octv][intvl], r, c-1 ) ) / 2.0; - dy = ( pixval32f( dog_pyr[octv][intvl], r+1, c ) - - pixval32f( dog_pyr[octv][intvl], r-1, c ) ) / 2.0; - ds = ( pixval32f( dog_pyr[octv][intvl+1], r, c ) - - pixval32f( dog_pyr[octv][intvl-1], r, c ) ) / 2.0; - - dI = cvCreateMat( 3, 1, CV_64FC1 ); - cvmSet( dI, 0, 0, dx ); - cvmSet( dI, 1, 0, dy ); - cvmSet( dI, 2, 0, ds ); - - return dI; -} - -/* - Computes the 3D Hessian matrix for a pixel in the DoG scale space pyramid. - - @param dog_pyr DoG scale space pyramid - @param octv pixel's octave in dog_pyr - @param intvl pixel's interval in octv - @param r pixel's image row - @param c pixel's image col - - @return Returns the Hessian matrix (below) for pixel I as a CvMat* - - / Ixx Ixy Ixs \
- | Ixy Iyy Iys |
- \ Ixs Iys Iss / -*/ -static CvMat* hessian_3D( IplImage*** dog_pyr, int octv, int intvl, int r, - int c ) -{ - CvMat* H; - double v, dxx, dyy, dss, dxy, dxs, dys; - - v = pixval32f( dog_pyr[octv][intvl], r, c ); - dxx = ( pixval32f( dog_pyr[octv][intvl], r, c+1 ) + - pixval32f( dog_pyr[octv][intvl], r, c-1 ) - 2 * v ); - dyy = ( pixval32f( dog_pyr[octv][intvl], r+1, c ) + - pixval32f( dog_pyr[octv][intvl], r-1, c ) - 2 * v ); - dss = ( pixval32f( dog_pyr[octv][intvl+1], r, c ) + - pixval32f( dog_pyr[octv][intvl-1], r, c ) - 2 * v ); - dxy = ( pixval32f( dog_pyr[octv][intvl], r+1, c+1 ) - - pixval32f( dog_pyr[octv][intvl], r+1, c-1 ) - - pixval32f( dog_pyr[octv][intvl], r-1, c+1 ) + - pixval32f( dog_pyr[octv][intvl], r-1, c-1 ) ) / 4.0; - dxs = ( pixval32f( dog_pyr[octv][intvl+1], r, c+1 ) - - pixval32f( dog_pyr[octv][intvl+1], r, c-1 ) - - pixval32f( dog_pyr[octv][intvl-1], r, c+1 ) + - pixval32f( dog_pyr[octv][intvl-1], r, c-1 ) ) / 4.0; - dys = ( pixval32f( dog_pyr[octv][intvl+1], r+1, c ) - - pixval32f( dog_pyr[octv][intvl+1], r-1, c ) - - pixval32f( dog_pyr[octv][intvl-1], r+1, c ) + - pixval32f( dog_pyr[octv][intvl-1], r-1, c ) ) / 4.0; - - H = cvCreateMat( 3, 3, CV_64FC1 ); - cvmSet( H, 0, 0, dxx ); - cvmSet( H, 0, 1, dxy ); - cvmSet( H, 0, 2, dxs ); - cvmSet( H, 1, 0, dxy ); - cvmSet( H, 1, 1, dyy ); - cvmSet( H, 1, 2, dys ); - cvmSet( H, 2, 0, dxs ); - cvmSet( H, 2, 1, dys ); - cvmSet( H, 2, 2, dss ); - - return H; -} - -/* - Performs one step of extremum interpolation. Based on Eqn. (3) in Lowe's - paper. - - @param dog_pyr difference of Gaussians scale space pyramid - @param octv octave of scale space - @param intvl interval being interpolated - @param r row being interpolated - @param c column being interpolated - @param xi output as interpolated subpixel increment to interval - @param xr output as interpolated subpixel increment to row - @param xc output as interpolated subpixel increment to col -*/ - -static void interp_step( IplImage*** dog_pyr, int octv, int intvl, int r, int c, - double* xi, double* xr, double* xc ) -{ - CvMat* dD, * H, * H_inv, X; - double x[3] = { 0 }; - - dD = deriv_3D( dog_pyr, octv, intvl, r, c ); - H = hessian_3D( dog_pyr, octv, intvl, r, c ); - H_inv = cvCreateMat( 3, 3, CV_64FC1 ); - cvInvert( H, H_inv, CV_SVD ); - cvInitMatHeader( &X, 3, 1, CV_64FC1, x, CV_AUTOSTEP ); - cvGEMM( H_inv, dD, -1, NULL, 0, &X, 0 ); - - cvReleaseMat( &dD ); - cvReleaseMat( &H ); - cvReleaseMat( &H_inv ); - - *xi = x[2]; - *xr = x[1]; - *xc = x[0]; -} - -/* - Calculates interpolated pixel contrast. Based on Eqn. (3) in Lowe's - paper. - - @param dog_pyr difference of Gaussians scale space pyramid - @param octv octave of scale space - @param intvl within-octave interval - @param r pixel row - @param c pixel column - @param xi interpolated subpixel increment to interval - @param xr interpolated subpixel increment to row - @param xc interpolated subpixel increment to col - - @param Returns interpolated contrast. -*/ -static double interp_contr( IplImage*** dog_pyr, int octv, int intvl, int r, - int c, double xi, double xr, double xc ) -{ - CvMat* dD, X, T; - double t[1], x[3] = { xc, xr, xi }; - - cvInitMatHeader( &X, 3, 1, CV_64FC1, x, CV_AUTOSTEP ); - cvInitMatHeader( &T, 1, 1, CV_64FC1, t, CV_AUTOSTEP ); - dD = deriv_3D( dog_pyr, octv, intvl, r, c ); - cvGEMM( dD, &X, 1, NULL, 0, &T, CV_GEMM_A_T ); - cvReleaseMat( &dD ); - - return pixval32f( dog_pyr[octv][intvl], r, c ) + t[0] * 0.5; -} - -/* - Allocates and initializes a new feature - - @return Returns a pointer to the new feature -*/ -static struct feature* new_feature( void ) -{ - struct feature* feat; - struct detection_data* ddata; - - feat = (feature*) malloc( sizeof( struct feature ) ); - memset( feat, 0, sizeof( struct feature ) ); - ddata = (detection_data*) malloc( sizeof( struct detection_data ) ); - memset( ddata, 0, sizeof( struct detection_data ) ); - feat->feature_data = ddata; - - return feat; -} - -/* - Interpolates a scale-space extremum's location and scale to subpixel - accuracy to form an image feature. Rejects features with low contrast. - Based on Section 4 of Lowe's paper. - - @param dog_pyr DoG scale space pyramid - @param octv feature's octave of scale space - @param intvl feature's within-octave interval - @param r feature's image row - @param c feature's image column - @param intvls total intervals per octave - @param contr_thr threshold on feature contrast - - @return Returns the feature resulting from interpolation of the given - parameters or NULL if the given location could not be interpolated or - if contrast at the interpolated loation was too low. If a feature is - returned, its scale, orientation, and descriptor are yet to be determined. -*/ -static struct feature* interp_extremum( IplImage*** dog_pyr, int octv, - int intvl, int r, int c, int intvls, - double contr_thr ) -{ - struct feature* feat; - struct detection_data* ddata; - double xi=0, xr=0, xc=0, contr; - int i = 0; - - while( i < SIFT_MAX_INTERP_STEPS ) - { - interp_step( dog_pyr, octv, intvl, r, c, &xi, &xr, &xc ); - if( std::abs( xi ) < 0.5 && std::abs( xr ) < 0.5 && std::abs( xc ) < 0.5 ) - break; - - c += cvRound( xc ); - r += cvRound( xr ); - intvl += cvRound( xi ); - - if( intvl < 1 || - intvl > intvls || - c < SIFT_IMG_BORDER || - r < SIFT_IMG_BORDER || - c >= dog_pyr[octv][0]->width - SIFT_IMG_BORDER || - r >= dog_pyr[octv][0]->height - SIFT_IMG_BORDER ) - { - return NULL; - } - - i++; - } - - /* ensure convergence of interpolation */ - if( i >= SIFT_MAX_INTERP_STEPS ) - return NULL; - - contr = interp_contr( dog_pyr, octv, intvl, r, c, xi, xr, xc ); - if( std::abs( contr ) < contr_thr / intvls ) - return NULL; - - feat = new_feature(); - ddata = feat->feature_data; - feat->x = ( c + xc ) * pow( 2.0, octv ); - feat->y = ( r + xr ) * pow( 2.0, octv ); - ddata->r = r; - ddata->c = c; - ddata->octv = octv; - ddata->intvl = intvl; - ddata->subintvl = xi; - - return feat; -} - -/* - Determines whether a feature is too edge like to be stable by computing the - ratio of principal curvatures at that feature. Based on Section 4.1 of - Lowe's paper. - - @param dog_img image from the DoG pyramid in which feature was detected - @param r feature row - @param c feature col - @param curv_thr high threshold on ratio of principal curvatures - - @return Returns 0 if the feature at (r,c) in dog_img is sufficiently - corner-like or 1 otherwise. -*/ -static int is_too_edge_like( IplImage* dog_img, int r, int c, int curv_thr ) -{ - double d, dxx, dyy, dxy, tr, det; - - /* principal curvatures are computed using the trace and det of Hessian */ - d = pixval32f(dog_img, r, c); - dxx = pixval32f( dog_img, r, c+1 ) + pixval32f( dog_img, r, c-1 ) - 2 * d; - dyy = pixval32f( dog_img, r+1, c ) + pixval32f( dog_img, r-1, c ) - 2 * d; - dxy = ( pixval32f(dog_img, r+1, c+1) - pixval32f(dog_img, r+1, c-1) - - pixval32f(dog_img, r-1, c+1) + pixval32f(dog_img, r-1, c-1) ) / 4.0; - tr = dxx + dyy; - det = dxx * dyy - dxy * dxy; - - /* negative determinant -> curvatures have different signs; reject feature */ - if( det <= 0 ) - return 1; - - if( tr * tr / det < ( curv_thr + 1.0 )*( curv_thr + 1.0 ) / curv_thr ) - return 0; - return 1; -} - -/* - Detects features at extrema in DoG scale space. Bad features are discarded - based on contrast and ratio of principal curvatures. - - @param dog_pyr DoG scale space pyramid - @param octvs octaves of scale space represented by dog_pyr - @param intvls intervals per octave - @param contr_thr low threshold on feature contrast - @param curv_thr high threshold on feature ratio of principal curvatures - @param storage memory storage in which to store detected features - - @return Returns an array of detected features whose scales, orientations, - and descriptors are yet to be determined. -*/ -static CvSeq* scale_space_extrema( IplImage*** dog_pyr, int octvs, int intvls, - double contr_thr, int curv_thr, - CvMemStorage* storage ) -{ - CvSeq* features; - double prelim_contr_thr = 0.5 * contr_thr / intvls; - struct feature* feat; - struct detection_data* ddata; - int o, i, r, c; - - features = cvCreateSeq( 0, sizeof(CvSeq), sizeof(struct feature), storage ); - for( o = 0; o < octvs; o++ ) - for( i = 1; i <= intvls; i++ ) - for(r = SIFT_IMG_BORDER; r < dog_pyr[o][0]->height-SIFT_IMG_BORDER; r++) - for(c = SIFT_IMG_BORDER; c < dog_pyr[o][0]->width-SIFT_IMG_BORDER; c++) - /* perform preliminary check on contrast */ - if( std::abs( pixval32f( dog_pyr[o][i], r, c ) ) > prelim_contr_thr ) - if( is_extremum( dog_pyr, o, i, r, c ) ) - { - feat = interp_extremum(dog_pyr, o, i, r, c, intvls, contr_thr); - if( feat ) - { - ddata = feat->feature_data; - if( ! is_too_edge_like( dog_pyr[ddata->octv][ddata->intvl], - ddata->r, ddata->c, curv_thr ) ) - { - cvSeqPush( features, feat ); - } - else - free( ddata ); - free( feat ); - } - } - - return features; -} - -/* - Calculates characteristic scale for each feature in an array. - - @param features array of features - @param sigma amount of Gaussian smoothing per octave of scale space - @param intvls intervals per octave of scale space -*/ -static void calc_feature_scales( CvSeq* features, double sigma, int intvls ) -{ - struct feature* feat; - struct detection_data* ddata; - double intvl; - int i, n; - - n = features->total; - for( i = 0; i < n; i++ ) - { - feat = CV_GET_SEQ_ELEM( struct feature, features, i ); - ddata = feat->feature_data; - intvl = ddata->intvl + ddata->subintvl; - feat->scl = sigma * pow( 2.0, ddata->octv + intvl / intvls ); - ddata->scl_octv = sigma * pow( 2.0, intvl / intvls ); - } -} - - - -/* - Halves feature coordinates and scale in case the input image was doubled - prior to scale space construction. - - @param features array of features -*/ -static void adjust_for_img_dbl( CvSeq* features ) -{ - struct feature* feat; - int i, n; - - n = features->total; - for( i = 0; i < n; i++ ) - { - feat = CV_GET_SEQ_ELEM( struct feature, features, i ); - feat->x /= 2.0; - feat->y /= 2.0; - feat->scl /= 2.0; - } -} - -/* - Calculates the gradient magnitude and orientation at a given pixel. - - @param img image - @param r pixel row - @param c pixel col - @param mag output as gradient magnitude at pixel (r,c) - @param ori output as gradient orientation at pixel (r,c) - - @return Returns 1 if the specified pixel is a valid one and sets mag and - ori accordingly; otherwise returns 0 -*/ -static int calc_grad_mag_ori( IplImage* img, int r, int c, double* mag, - double* ori ) -{ - double dx, dy; - - if( r > 0 && r < img->height - 1 && c > 0 && c < img->width - 1 ) - { - dx = pixval32f( img, r, c+1 ) - pixval32f( img, r, c-1 ); - dy = pixval32f( img, r-1, c ) - pixval32f( img, r+1, c ); - *mag = sqrt( dx*dx + dy*dy ); - *ori = atan2( dy, dx ); - return 1; - } - - else - return 0; -} - -/* - Computes a gradient orientation histogram at a specified pixel. - - @param img image - @param r pixel row - @param c pixel col - @param n number of histogram bins - @param rad radius of region over which histogram is computed - @param sigma std for Gaussian weighting of histogram entries - - @return Returns an n-element array containing an orientation histogram - representing orientations between 0 and 2 PI. -*/ -static double* ori_hist( IplImage* img, int r, int c, int n, int rad, - double sigma ) -{ - double* hist; - double mag, ori, w, exp_denom, PI2 = CV_PI * 2.0; - int bin, i, j; - - hist = (double*) calloc( n, sizeof( double ) ); - exp_denom = 2.0 * sigma * sigma; - for( i = -rad; i <= rad; i++ ) - for( j = -rad; j <= rad; j++ ) - if( calc_grad_mag_ori( img, r + i, c + j, &mag, &ori ) ) - { - w = exp( -( i*i + j*j ) / exp_denom ); - bin = cvRound( n * ( ori + CV_PI ) / PI2 ); - bin = ( bin < n )? bin : 0; - hist[bin] += w * mag; - } - - return hist; -} - -/* - Gaussian smooths an orientation histogram. - - @param hist an orientation histogram - @param n number of bins -*/ -static void smooth_ori_hist( double* hist, int n ) -{ - double prev, tmp, h0 = hist[0]; - int i; - - prev = hist[n-1]; - for( i = 0; i < n; i++ ) - { - tmp = hist[i]; - hist[i] = 0.25 * prev + 0.5 * hist[i] + - 0.25 * ( ( i+1 == n )? h0 : hist[i+1] ); - prev = tmp; - } -} - -/* - Finds the magnitude of the dominant orientation in a histogram - - @param hist an orientation histogram - @param n number of bins - - @return Returns the value of the largest bin in hist -*/ -static double dominant_ori( double* hist, int n ) -{ - double omax; - int maxbin, i; - - omax = hist[0]; - maxbin = 0; - for( i = 1; i < n; i++ ) - if( hist[i] > omax ) - { - omax = hist[i]; - maxbin = i; - } - return omax; -} - -/* - Makes a deep copy of a feature - - @param feat feature to be cloned - - @return Returns a deep copy of feat -*/ -static struct feature* clone_feature( struct feature* feat ) -{ - struct feature* new_feat; - struct detection_data* ddata; - - new_feat = new_feature(); - ddata = new_feat->feature_data; - memcpy( new_feat, feat, sizeof( struct feature ) ); - memcpy( ddata, feat->feature_data, sizeof( struct detection_data ) ); - new_feat->feature_data = ddata; - - return new_feat; -} - -/* - Adds features to an array for every orientation in a histogram greater than - a specified threshold. - - @param features new features are added to the end of this array - @param hist orientation histogram - @param n number of bins in hist - @param mag_thr new features are added for entries in hist greater than this - @param feat new features are clones of this with different orientations -*/ -static void add_good_ori_features( CvSeq* features, double* hist, int n, - double mag_thr, struct feature* feat ) -{ - struct feature* new_feat; - double bin, PI2 = CV_PI * 2.0; - int l, r, i; - - for( i = 0; i < n; i++ ) - { - l = ( i == 0 )? n - 1 : i-1; - r = ( i + 1 ) % n; - - if( hist[i] > hist[l] && hist[i] > hist[r] && hist[i] >= mag_thr ) - { - bin = i + interp_hist_peak( hist[l], hist[i], hist[r] ); - bin = ( bin < 0 )? n + bin : ( bin >= n )? bin - n : bin; - new_feat = clone_feature( feat ); - new_feat->ori = ( ( PI2 * bin ) / n ) - CV_PI; - cvSeqPush( features, new_feat ); - free( new_feat ); - } - } -} - -/* - Computes a canonical orientation for each image feature in an array. Based - on Section 5 of Lowe's paper. This function adds features to the array when - there is more than one dominant orientation at a given feature location. - - @param features an array of image features - @param gauss_pyr Gaussian scale space pyramid -*/ -static void calc_feature_oris( CvSeq* features, IplImage*** gauss_pyr ) -{ - struct feature* feat; - struct detection_data* ddata; - double* hist; - double omax; - int i, j, n = features->total; - - for( i = 0; i < n; i++ ) - { - feat = (feature*) malloc( sizeof( struct feature ) ); - cvSeqPopFront( features, feat ); - ddata = feat->feature_data; - hist = ori_hist( gauss_pyr[ddata->octv][ddata->intvl], - ddata->r, ddata->c, SIFT_ORI_HIST_BINS, - cvRound( SIFT_ORI_RADIUS * ddata->scl_octv ), - SIFT_ORI_SIG_FCTR * ddata->scl_octv ); - for( j = 0; j < SIFT_ORI_SMOOTH_PASSES; j++ ) - smooth_ori_hist( hist, SIFT_ORI_HIST_BINS ); - omax = dominant_ori( hist, SIFT_ORI_HIST_BINS ); - add_good_ori_features( features, hist, SIFT_ORI_HIST_BINS, - omax * SIFT_ORI_PEAK_RATIO, feat ); - free( ddata ); - free( feat ); - free( hist ); - } -} - -/* - Interpolates an entry into the array of orientation histograms that form - the feature descriptor. - - @param hist 2D array of orientation histograms - @param rbin sub-bin row coordinate of entry - @param cbin sub-bin column coordinate of entry - @param obin sub-bin orientation coordinate of entry - @param mag size of entry - @param d width of 2D array of orientation histograms - @param n number of bins per orientation histogram -*/ -static void interp_hist_entry( double*** hist, double rbin, double cbin, - double obin, double mag, int d, int n ) -{ - double d_r, d_c, d_o, v_r, v_c, v_o; - double** row, * h; - int r0, c0, o0, rb, cb, ob, r, c, o; - - r0 = cvFloor( rbin ); - c0 = cvFloor( cbin ); - o0 = cvFloor( obin ); - d_r = rbin - r0; - d_c = cbin - c0; - d_o = obin - o0; - - /* - The entry is distributed into up to 8 bins. Each entry into a bin - is multiplied by a weight of 1 - d for each dimension, where d is the - distance from the center value of the bin measured in bin units. - */ - for( r = 0; r <= 1; r++ ) - { - rb = r0 + r; - if( rb >= 0 && rb < d ) - { - v_r = mag * ( ( r == 0 )? 1.0 - d_r : d_r ); - row = hist[rb]; - for( c = 0; c <= 1; c++ ) - { - cb = c0 + c; - if( cb >= 0 && cb < d ) - { - v_c = v_r * ( ( c == 0 )? 1.0 - d_c : d_c ); - h = row[cb]; - for( o = 0; o <= 1; o++ ) - { - ob = ( o0 + o ) % n; - v_o = v_c * ( ( o == 0 )? 1.0 - d_o : d_o ); - h[ob] += v_o; - } - } - } - } - } -} - -/* - Computes the 2D array of orientation histograms that form the feature - descriptor. Based on Section 6.1 of Lowe's paper. - - @param img image used in descriptor computation - @param r row coord of center of orientation histogram array - @param c column coord of center of orientation histogram array - @param ori canonical orientation of feature whose descr is being computed - @param scl scale relative to img of feature whose descr is being computed - @param d width of 2d array of orientation histograms - @param n bins per orientation histogram - - @return Returns a d x d array of n-bin orientation histograms. -*/ -static double*** descr_hist( IplImage* img, int r, int c, double ori, - double scl, int d, int n ) -{ - double*** hist; - double cos_t, sin_t, hist_width, exp_denom, r_rot, c_rot, grad_mag, - grad_ori, w, rbin, cbin, obin, bins_per_rad, PI2 = 2.0 * CV_PI; - int radius, i, j; - - hist = (double***) calloc( d, sizeof( double** ) ); - for( i = 0; i < d; i++ ) - { - hist[i] = (double**) calloc( d, sizeof( double* ) ); - for( j = 0; j < d; j++ ) - hist[i][j] = (double*) calloc( n, sizeof( double ) ); - } - - cos_t = cos( ori ); - sin_t = sin( ori ); - bins_per_rad = n / PI2; - exp_denom = d * d * 0.5; - hist_width = SIFT_DESCR_SCL_FCTR * scl; - radius = (int)(hist_width * sqrt(2.0) * ( d + 1.0 ) * 0.5 + 0.5); - for( i = -radius; i <= radius; i++ ) - for( j = -radius; j <= radius; j++ ) - { - /* - Calculate sample's histogram array coords rotated relative to ori. - Subtract 0.5 so samples that fall e.g. in the center of row 1 (i.e. - r_rot = 1.5) have full weight placed in row 1 after interpolation. - */ - c_rot = ( j * cos_t - i * sin_t ) / hist_width; - r_rot = ( j * sin_t + i * cos_t ) / hist_width; - rbin = r_rot + d / 2 - 0.5; - cbin = c_rot + d / 2 - 0.5; - - if( rbin > -1.0 && rbin < d && cbin > -1.0 && cbin < d ) - if( calc_grad_mag_ori( img, r + i, c + j, &grad_mag, &grad_ori )) - { - grad_ori -= ori; - while( grad_ori < 0.0 ) - grad_ori += PI2; - while( grad_ori >= PI2 ) - grad_ori -= PI2; - - obin = grad_ori * bins_per_rad; - w = exp( -(c_rot * c_rot + r_rot * r_rot) / exp_denom ); - interp_hist_entry( hist, rbin, cbin, obin, grad_mag * w, d, n ); - } - } - - return hist; -} - -/* - Normalizes a feature's descriptor vector to unitl length - - @param feat feature -*/ -static void normalize_descr( struct feature* feat ) -{ - double cur, len_inv, len_sq = 0.0; - int i, d = feat->d; - - for( i = 0; i < d; i++ ) - { - cur = feat->descr[i]; - len_sq += cur*cur; - } - len_inv = 1.0 / sqrt( len_sq ); - for( i = 0; i < d; i++ ) - feat->descr[i] *= len_inv; -} - -/* - Converts the 2D array of orientation histograms into a feature's descriptor - vector. - - @param hist 2D array of orientation histograms - @param d width of hist - @param n bins per histogram - @param feat feature into which to store descriptor -*/ -static void hist_to_descr( double*** hist, int d, int n, struct feature* feat ) -{ - int int_val, i, r, c, o, k = 0; - - for( r = 0; r < d; r++ ) - for( c = 0; c < d; c++ ) - for( o = 0; o < n; o++ ) - feat->descr[k++] = hist[r][c][o]; - - feat->d = k; - normalize_descr( feat ); - for( i = 0; i < k; i++ ) - if( feat->descr[i] > SIFT_DESCR_MAG_THR ) - feat->descr[i] = SIFT_DESCR_MAG_THR; - normalize_descr( feat ); - - /* convert floating-point descriptor to integer valued descriptor */ - for( i = 0; i < k; i++ ) - { - int_val = (int)(SIFT_INT_DESCR_FCTR * feat->descr[i]); - feat->descr[i] = MIN( 255, int_val ); - } -} - -/* - Compares features for a decreasing-scale ordering. Intended for use with - CvSeqSort - - @param feat1 first feature - @param feat2 second feature - @param param unused - - @return Returns 1 if feat1's scale is greater than feat2's, -1 if vice versa, - and 0 if their scales are equal -*/ -static int feature_cmp( void* feat1, void* feat2, void* /*param*/ ) -{ - struct feature* f1 = (struct feature*) feat1; - struct feature* f2 = (struct feature*) feat2; - - if( f1->scl < f2->scl ) - return 1; - if( f1->scl > f2->scl ) - return -1; - return 0; -} - -/* - De-allocates memory held by a descriptor histogram - - @param hist pointer to a 2D array of orientation histograms - @param d width of hist -*/ -static void release_descr_hist( double**** hist, int d ) -{ - int i, j; - - for( i = 0; i < d; i++) - { - for( j = 0; j < d; j++ ) - free( (*hist)[i][j] ); - free( (*hist)[i] ); - } - free( *hist ); - *hist = NULL; -} - - -/* - De-allocates memory held by a scale space pyramid - - @param pyr scale space pyramid - @param octvs number of octaves of scale space - @param n number of images per octave -*/ -static void release_pyr( IplImage**** pyr, int octvs, int n ) -{ - int i, j; - for( i = 0; i < octvs; i++ ) - { - for( j = 0; j < n; j++ ) - cvReleaseImage( &(*pyr)[i][j] ); - free( (*pyr)[i] ); - } - free( *pyr ); - *pyr = NULL; -} - -/* - Computes feature descriptors for features in an array. Based on Section 6 - of Lowe's paper. - - @param features array of features - @param gauss_pyr Gaussian scale space pyramid - @param d width of 2D array of orientation histograms - @param n number of bins per orientation histogram -*/ -static void compute_descriptors( CvSeq* features, IplImage*** gauss_pyr, int d, - int n ) -{ - struct feature* feat; - struct detection_data* ddata; - double*** hist; - int i, k = features->total; - - for( i = 0; i < k; i++ ) - { - feat = CV_GET_SEQ_ELEM( struct feature, features, i ); - ddata = feat->feature_data; - hist = descr_hist( gauss_pyr[ddata->octv][ddata->intvl], ddata->r, - ddata->c, feat->ori, ddata->scl_octv, d, n ); - hist_to_descr( hist, d, n, feat ); - release_descr_hist( &hist, d ); - } -} - -/***** some auxilary stucture (there is not it in original implementation) *******/ - -struct ImagePyrData -{ - ImagePyrData( IplImage* img, int octvs, int intvls, double _sigma, int img_dbl ) - { - if( ! img ) - CV_Error( CV_StsBadArg, "NULL image pointer" ); - - /* build scale space pyramid; smallest dimension of top level is ~4 pixels */ - init_img = create_init_img( img, img_dbl, _sigma ); - - int max_octvs = static_cast( log( static_cast(MIN( init_img->width, init_img->height ))) / log(2.0) - 2.0); - octvs = std::max( std::min( octvs, max_octvs ), 1 ); - - gauss_pyr = build_gauss_pyr( init_img, octvs, intvls, _sigma ); - dog_pyr = build_dog_pyr( gauss_pyr, octvs, intvls ); - - octaves = octvs; - intervals = intvls; - sigma = _sigma; - is_img_dbl = img_dbl != 0 ? true : false; - } - - virtual ~ImagePyrData() - { - cvReleaseImage( &init_img ); - release_pyr( &gauss_pyr, octaves, intervals + 3 ); - release_pyr( &dog_pyr, octaves, intervals + 2 ); - } - - IplImage* init_img; - IplImage*** gauss_pyr, *** dog_pyr; - - int octaves, intervals; - double sigma; - - bool is_img_dbl; -}; - -void release_features( struct feature** feat, int count ) -{ - for( int i = 0; i < count; i++ ) - { - free( (*feat)[i].feature_data ); - (*feat)[i].feature_data = NULL; - } - free( *feat ); -} - -void compute_features( const ImagePyrData* imgPyrData, struct feature** feat, int& count, - double contr_thr, int curv_thr ) -{ - CvMemStorage* storage; - CvSeq* features; - - storage = cvCreateMemStorage( 0 ); - features = scale_space_extrema( imgPyrData->dog_pyr, imgPyrData->octaves, imgPyrData->intervals, - contr_thr, curv_thr, storage ); - - calc_feature_scales( features, imgPyrData->sigma, imgPyrData->intervals ); - if( imgPyrData->is_img_dbl ) - adjust_for_img_dbl( features ); - calc_feature_oris( features, imgPyrData->gauss_pyr ); - - /* sort features by decreasing scale and move from CvSeq to array */ - cvSeqSort( features, (CvCmpFunc)feature_cmp, NULL ); - int n = features->total; - *feat = (feature*)calloc( n, sizeof(struct feature) ); - *feat = (feature*)cvCvtSeqToArray( features, *feat, CV_WHOLE_SEQ ); - - cvReleaseMemStorage( &storage ); - - count = n; -} - -/****************************************************************************************\ - 2.) wrapper of Rob Hess`s SIFT -\****************************************************************************************/ - -using namespace cv; - -SIFT::CommonParams::CommonParams() : - nOctaves(DEFAULT_NOCTAVES), nOctaveLayers(DEFAULT_NOCTAVE_LAYERS), - firstOctave(DEFAULT_FIRST_OCTAVE), angleMode(FIRST_ANGLE) -{} - -SIFT::CommonParams::CommonParams( int _nOctaves, int _nOctaveLayers, int /*_firstOctave*/, int /*_angleMode*/ ) : - nOctaves(_nOctaves), nOctaveLayers(_nOctaveLayers), - firstOctave(-1/*_firstOctave*/), angleMode(FIRST_ANGLE/*_angleMode*/) -{} - -SIFT::CommonParams::CommonParams( int _nOctaves, int _nOctaveLayers ) : - nOctaves(_nOctaves), nOctaveLayers(_nOctaveLayers), firstOctave(-1), angleMode(FIRST_ANGLE) -{} - -SIFT::DetectorParams::DetectorParams() : - threshold(GET_DEFAULT_THRESHOLD()), edgeThreshold(GET_DEFAULT_EDGE_THRESHOLD()) -{} - -SIFT::DetectorParams::DetectorParams( double _threshold, double _edgeThreshold ) : - threshold(_threshold), edgeThreshold(_edgeThreshold) -{} - -SIFT::DescriptorParams::DescriptorParams() : - magnification(GET_DEFAULT_MAGNIFICATION()), isNormalize(DEFAULT_IS_NORMALIZE), - recalculateAngles(true) -{} - -SIFT::DescriptorParams::DescriptorParams( double _magnification, bool /*_isNormalize*/, bool _recalculateAngles ) : - magnification(_magnification), isNormalize(true/*_isNormalize*/), - recalculateAngles(_recalculateAngles) -{} - -SIFT::DescriptorParams::DescriptorParams( bool _recalculateAngles ) - : magnification(GET_DEFAULT_MAGNIFICATION()), isNormalize(true),recalculateAngles(_recalculateAngles) -{} - -SIFT::SIFT() -{} - -SIFT::SIFT( double _threshold, double _edgeThreshold, int _nOctaves, - int _nOctaveLayers, int _firstOctave, int _angleMode ) -{ - detectorParams = DetectorParams(_threshold, _edgeThreshold); - commParams = CommonParams(_nOctaves, _nOctaveLayers, _firstOctave, _angleMode); -} - -SIFT::SIFT( double _magnification, bool _isNormalize, bool _recalculateAngles, int _nOctaves, - int _nOctaveLayers, int _firstOctave, int _angleMode ) -{ - descriptorParams = DescriptorParams(_magnification, _isNormalize, _recalculateAngles); - commParams = CommonParams(_nOctaves, _nOctaveLayers, _firstOctave, _angleMode); -} - -SIFT::SIFT( const CommonParams& _commParams, - const DetectorParams& _detectorParams, - const DescriptorParams& _descriptorParams ) -{ - commParams = _commParams; - detectorParams = _detectorParams; - descriptorParams = _descriptorParams; -} - -int SIFT::descriptorSize() const -{ - return DescriptorParams::DESCRIPTOR_SIZE; -} - -SIFT::CommonParams SIFT::getCommonParams () const -{ - return commParams; -} - -SIFT::DetectorParams SIFT::getDetectorParams () const -{ - return detectorParams; -} - -SIFT::DescriptorParams SIFT::getDescriptorParams () const -{ - return descriptorParams; -} - -struct SiftParams -{ - SiftParams( int argO, int argS ) - { - O = argO; - S = argS; - - sigma0 = 1.6 * powf(2.0f, 1.0f / S ) ; - - omin = -1; - smin = -1; - smax = S + 1; - } - - int O; - int S; - - double sigma0; - - int omin; - int smin; - int smax; -}; - -inline KeyPoint featureToKeyPoint( const feature& feat ) -{ - float size = (float)(feat.scl * SIFT::DescriptorParams::GET_DEFAULT_MAGNIFICATION() * 4); // 4==NBP - float angle = (float)(feat.ori * a_180divPI); - return KeyPoint( (float)feat.x, (float)feat.y, size, angle, feat.response, feat.feature_data->octv, feat.class_id ); -} - -static void fillFeatureData( feature& feat, const SiftParams& params ) -{ - - /* - The formula linking the keypoint scale sigma to the octave and - scale index is - - (1) sigma(o,s) = sigma0 2^(o+s/S) - - for which - - (2) o + s/S = log2 sigma/sigma0 == phi. - - In addition to the scale index s (which can be fractional due to - scale interpolation) a keypoint has an integer scale index is too - (which is the index of the scale level where it was detected in - the DoG scale space). We have the constraints: - - - o and is are integer - - - is is in the range [smin+1, smax-2 ] - - - o is in the range [omin, omin+O-1] - - - is = rand(s) most of the times (but not always, due to the way s - is obtained by quadratic interpolation of the DoG scale space). - - Depending on the values of smin and smax, often (2) has multiple - solutions is,o that satisfy all constraints. In this case we - choose the one with biggest index o (this saves a bit of - computation). - - DETERMINING THE OCTAVE INDEX O - - From (2) we have o = phi - s/S and we want to pick the biggest - possible index o in the feasible range. This corresponds to - selecting the smallest possible index s. We write s = is + ds - where in most cases |ds|<.5 (but in general |ds|<1). So we have - - o = phi - s/S, s = is + ds , |ds| < .5 (or |ds| < 1). - - Since is is in the range [smin+1,smax-2], s is in the range - [smin+.5,smax-1.5] (or [smin,smax-1]), the number o is an integer - in the range phi+[-smax+1.5,-smin-.5] (or - phi+[-smax+1,-smin]). Thus the maximum value of o is obtained for - o = floor(phi-smin-.5) (or o = floor(phi-smin)). - - Finally o is clamped to make sure it is contained in the feasible - range. - - DETERMINING THE SCALE INDEXES S AND IS - - Given o we can derive is by writing (2) as - - s = is + ds = S(phi - o). - - We then take is = round(s) and clamp its value to be in the - feasible range. - */ - - double sigma = feat.scl; - double x = feat.x; - double y = feat.y; - - int o, ix, iy, is; - float s, phi; - - phi = static_cast(log( sigma / params.sigma0 ) / log(2.0)); - o = (int)std::floor( phi - (float(params.smin)+.5)/params.S ); - o = std::min(o, params.omin+params.O-1); - o = std::max(o, params.omin); - s = params.S * (phi - o); - - is = int(s + 0.5); - is = std::min(is, params.smax - 2); - is = std::max(is, params.smin + 1); - - float per = getOctaveSamplingPeriod(o) ; - ix = int(x / per + 0.5) ; - iy = int(y / per + 0.5) ; - - - detection_data* ddata = feat.feature_data; - - ddata->r = iy; - ddata->c = ix; - - ddata->octv = o + 1; - ddata->intvl = is + 1; - - ddata->subintvl = s - is; - ddata->scl_octv = params.sigma0 * pow(2.0, static_cast(s / params.S)); -} - -inline void keyPointToFeature( const KeyPoint& keypoint, feature& feat, const SiftParams& params ) -{ - feat.x = keypoint.pt.x; - feat.y = keypoint.pt.y; - - feat.scl = keypoint.size / (SIFT::DescriptorParams::GET_DEFAULT_MAGNIFICATION()*4); // 4==NBP - feat.ori = keypoint.angle * a_PIdiv180; - - feat.response = keypoint.response; - feat.class_id = keypoint.class_id; - - feat.feature_data = (detection_data*) calloc( 1, sizeof( detection_data ) ); - fillFeatureData( feat, params ); -} - -// detectors -void SIFT::operator()(const Mat& image, const Mat& mask, - vector& keypoints) const -{ - if( image.empty() || image.type() != CV_8UC1 ) - CV_Error( CV_StsBadArg, "image is empty or has incorrect type (!=CV_8UC1)" ); - - if( !mask.empty() && mask.type() != CV_8UC1 ) - CV_Error( CV_StsBadArg, "mask has incorrect type (!=CV_8UC1)" ); - - Mat subImage, subMask; - Rect brect( 0, 0, image.cols, image.rows ); - if( mask.empty() ) - { - subImage = image; - } - else - { - vector points; - points.reserve( image.rows * image.cols ); - for( int y = 0; y < mask.rows; y++ ) - { - for( int x = 0; x < mask.cols; x++ ) - { - if( mask.at(y,x) ) - points.push_back( cv::Point(x,y) ); - } - } - brect = cv::boundingRect( points ); - - if( brect.x == 0 && brect.y == 0 && brect.width == mask.cols && brect.height == mask.rows ) - { - subImage = image; - } - else - { - subImage = image( brect ); - subMask = mask( brect ); - } - } - - Mat fimg; - subImage.convertTo( fimg, CV_32FC1 ); - - // compute features - IplImage img = fimg; - struct feature* features; - - ImagePyrData pyrImages( &img, commParams.nOctaves, commParams.nOctaveLayers, SIFT_SIGMA, SIFT_IMG_DBL ); - - int feature_count = 0; - compute_features( &pyrImages, &features, feature_count, detectorParams.threshold, (int)detectorParams.edgeThreshold ); - - // convert to KeyPoint structure - keypoints.resize( feature_count ); - for( int i = 0; i < feature_count; i++ ) - { - keypoints[i] = featureToKeyPoint( features[i] ); - } - release_features( &features, feature_count ); - - KeyPointsFilter::removeDuplicated( keypoints ); - - if( !subMask.empty() ) - { - // filter points by subMask and convert the points coordinates from subImage size to image size - KeyPointsFilter::runByPixelsMask( keypoints, subMask ); - int dx = brect.x, dy = brect.y; - vector::iterator it = keypoints.begin(), - end = keypoints.end(); - for( ; it != end; ++it ) - { - it->pt.x += dx; - it->pt.y += dy; - } - } -} - -void release_features_data( CvSeq* featuresSeq ) -{ - for( int i = 0; i < featuresSeq->total; i++ ) - { - feature * ft = CV_GET_SEQ_ELEM( feature, featuresSeq, i ); - free( ft->feature_data ); - } -} - -// Calculate orientation of features. -// Note: calc_feature_oris() duplicates the points with several dominant orientations. -// So if keypoints was detected by Sift feature detector then some points will be -// duplicated twice. -void recalculateAngles( vector& keypoints, IplImage*** gauss_pyr, - int nOctaves, int nOctaveLayers ) -{ - CvMemStorage* storage = cvCreateMemStorage( 0 ); - CvSeq* featuresSeq = cvCreateSeq( 0, sizeof(CvSeq), sizeof(struct feature), storage ); - - for( size_t i = 0; i < keypoints.size(); i++ ) - { - feature ft; - keyPointToFeature( keypoints[i], ft, SiftParams( nOctaves, nOctaveLayers ) ); - cvSeqPush( featuresSeq, &ft ); - } - - calc_feature_oris( featuresSeq, gauss_pyr ); - - keypoints.resize( featuresSeq->total ); - for( int i = 0; i < featuresSeq->total; i++ ) - { - feature * ft = CV_GET_SEQ_ELEM( feature, featuresSeq, i ); - keypoints[i] = featureToKeyPoint( *ft ); - } - - // Remove duplicated keypoints. - KeyPointsFilter::removeDuplicated( keypoints ); - - release_features_data( featuresSeq ); - cvReleaseMemStorage( &storage ); -} - -// descriptors -void SIFT::operator()(const Mat& image, const Mat& mask, - vector& keypoints, - Mat& descriptors, - bool useProvidedKeypoints) const -{ - if( image.empty() || image.type() != CV_8UC1 ) - CV_Error( CV_StsBadArg, "img is empty or has incorrect type" ); - - Mat fimg; - image.convertTo(fimg, CV_32FC1/*, 1.0/255.0*/); - - if( !useProvidedKeypoints ) - (*this)(image, mask, keypoints); - else - { - // filter keypoints by mask - KeyPointsFilter::runByPixelsMask( keypoints, mask ); - } - - IplImage img = fimg; - ImagePyrData pyrImages( &img, commParams.nOctaves, commParams.nOctaveLayers, SIFT_SIGMA, SIFT_IMG_DBL ); - - if( descriptorParams.recalculateAngles ) - recalculateAngles( keypoints, pyrImages.gauss_pyr, commParams.nOctaves, commParams.nOctaveLayers ); - - CvMemStorage* storage = cvCreateMemStorage( 0 ); - CvSeq* featuresSeq = cvCreateSeq( 0, sizeof(CvSeq), sizeof(struct feature), storage ); - - for( size_t i = 0; i < keypoints.size(); i++ ) - { - feature ft; - keyPointToFeature( keypoints[i], ft, SiftParams( commParams.nOctaves, commParams.nOctaveLayers ) ); - cvSeqPush( featuresSeq, &ft ); - } - compute_descriptors( featuresSeq, pyrImages.gauss_pyr, SIFT_DESCR_WIDTH, SIFT_DESCR_HIST_BINS ); - CV_DbgAssert( (int)keypoints.size() == featuresSeq->total ); - // TODO check that keypoint fiels is the same as before compute_descriptors() - - descriptors.create( featuresSeq->total, SIFT::DescriptorParams::DESCRIPTOR_SIZE, CV_32FC1 ); - for( int i = 0; i < featuresSeq->total; i++ ) - { - float* rowPtr = descriptors.ptr(i); - feature * featurePtr = CV_GET_SEQ_ELEM( feature, featuresSeq, i ); - CV_Assert( featurePtr ); - double* desc = featurePtr->descr; - for( int j = 0; j < descriptors.cols; j++ ) - { - rowPtr[j] = (float)desc[j]; - } - } - - release_features_data( featuresSeq ); - cvReleaseMemStorage( &storage ); -} diff --git a/modules/features2d/src/stardetector.cpp b/modules/features2d/src/stardetector.cpp index e7097235c9..e81741b0e2 100644 --- a/modules/features2d/src/stardetector.cpp +++ b/modules/features2d/src/stardetector.cpp @@ -10,7 +10,7 @@ // License Agreement // For Open Source Computer Vision Library // -// Copyright (C) 2008, Willow Garage Inc., all rights reserved. +// Copyright (C) 2008-2012, 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, @@ -41,20 +41,23 @@ #include "precomp.hpp" -static void -icvComputeIntegralImages( const CvMat* matI, CvMat* matS, CvMat* matT, CvMat* _FT ) +namespace cv { - int x, y, rows = matI->rows, cols = matI->cols; - const uchar* I = matI->data.ptr; - int *S = matS->data.i, *T = matT->data.i, *FT = _FT->data.i; - int istep = matI->step, step = matS->step/sizeof(S[0]); + +static void +computeIntegralImages( const Mat& matI, Mat& matS, Mat& matT, Mat& _FT ) +{ + CV_Assert( matI.type() == CV_8U ); - assert( CV_MAT_TYPE(matI->type) == CV_8UC1 && - CV_MAT_TYPE(matS->type) == CV_32SC1 && - CV_ARE_TYPES_EQ(matS, matT) && CV_ARE_TYPES_EQ(matS, _FT) && - CV_ARE_SIZES_EQ(matS, matT) && CV_ARE_SIZES_EQ(matS, _FT) && - matS->step == matT->step && matS->step == _FT->step && - matI->rows+1 == matS->rows && matI->cols+1 == matS->cols ); + int x, y, rows = matI.rows, cols = matI.cols; + + matS.create(rows + 1, cols + 1, CV_32S); + matT.create(rows + 1, cols + 1, CV_32S); + _FT.create(rows + 1, cols + 1, CV_32S); + + const uchar* I = matI.ptr(); + int *S = matS.ptr(), *T = matT.ptr(), *FT = _FT.ptr(); + int istep = matI.step, step = matS.step/sizeof(S[0]); for( x = 0; x <= cols; x++ ) S[x] = T[x] = FT[x] = 0; @@ -92,16 +95,14 @@ icvComputeIntegralImages( const CvMat* matI, CvMat* matS, CvMat* matT, CvMat* _F } } -typedef struct CvStarFeature +struct StarFeature { int area; int* p[8]; -} -CvStarFeature; +}; static int -icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* sizes, - const CvStarDetectorParams* params ) +StarDetectorComputeResponses( const Mat& img, Mat& responses, Mat& sizes, int maxSize ) { const int MAX_PATTERN = 17; static const int sizes0[] = {1, 2, 3, 4, 6, 8, 11, 12, 16, 22, 23, 32, 45, 46, 64, 90, 128, -1}; @@ -117,22 +118,19 @@ icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* size absmask.i = 0x7fffffff; volatile bool useSIMD = cv::checkHardwareSupport(CV_CPU_SSE2); #endif - CvStarFeature f[MAX_PATTERN]; + StarFeature f[MAX_PATTERN]; - CvMat *sum = 0, *tilted = 0, *flatTilted = 0; - int y, i=0, rows = img->rows, cols = img->cols, step; + Mat sum, tilted, flatTilted; + int y, i=0, rows = img.rows, cols = img.cols; int border, npatterns=0, maxIdx=0; -#ifdef _OPENMP - int nthreads = cvGetNumThreads(); -#endif - assert( CV_MAT_TYPE(img->type) == CV_8UC1 && - CV_MAT_TYPE(responses->type) == CV_32FC1 && - CV_MAT_TYPE(sizes->type) == CV_16SC1 && - CV_ARE_SIZES_EQ(responses, sizes) ); + CV_Assert( img.type() == CV_8UC1 ); + + responses.create( img.size(), CV_32F ); + sizes.create( img.size(), CV_16S ); while( pairs[i][0] >= 0 && ! - ( sizes0[pairs[i][0]] >= params->maxSize + ( sizes0[pairs[i][0]] >= maxSize || sizes0[pairs[i+1][0]] + sizes0[pairs[i+1][0]]/2 >= std::min(rows, cols) ) ) { ++i; @@ -141,13 +139,9 @@ icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* size npatterns = i; npatterns += (pairs[npatterns-1][0] >= 0); maxIdx = pairs[npatterns-1][0]; - - sum = cvCreateMat( rows + 1, cols + 1, CV_32SC1 ); - tilted = cvCreateMat( rows + 1, cols + 1, CV_32SC1 ); - flatTilted = cvCreateMat( rows + 1, cols + 1, CV_32SC1 ); - step = sum->step/CV_ELEM_SIZE(sum->type); - - icvComputeIntegralImages( img, sum, tilted, flatTilted ); + + computeIntegralImages( img, sum, tilted, flatTilted ); + int step = (int)(sum.step/sum.elemSize()); for( i = 0; i <= maxIdx; i++ ) { @@ -155,15 +149,15 @@ icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* size int ur_area = (2*ur_size + 1)*(2*ur_size + 1); int t_area = t_size*t_size + (t_size + 1)*(t_size + 1); - f[i].p[0] = sum->data.i + (ur_size + 1)*step + ur_size + 1; - f[i].p[1] = sum->data.i - ur_size*step + ur_size + 1; - f[i].p[2] = sum->data.i + (ur_size + 1)*step - ur_size; - f[i].p[3] = sum->data.i - ur_size*step - ur_size; + f[i].p[0] = sum.ptr() + (ur_size + 1)*step + ur_size + 1; + f[i].p[1] = sum.ptr() - ur_size*step + ur_size + 1; + f[i].p[2] = sum.ptr() + (ur_size + 1)*step - ur_size; + f[i].p[3] = sum.ptr() - ur_size*step - ur_size; - f[i].p[4] = tilted->data.i + (t_size + 1)*step + 1; - f[i].p[5] = flatTilted->data.i - t_size; - f[i].p[6] = flatTilted->data.i + t_size + 1; - f[i].p[7] = tilted->data.i - t_size*step + 1; + f[i].p[4] = tilted.ptr() + (t_size + 1)*step + 1; + f[i].p[5] = flatTilted.ptr() - t_size; + f[i].p[6] = flatTilted.ptr() + t_size + 1; + f[i].p[7] = tilted.ptr() - t_size*step + 1; f[i].area = ur_area + t_area; sizes1[i] = sizes0[i]; @@ -199,10 +193,10 @@ icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* size for( y = 0; y < border; y++ ) { - float* r_ptr = (float*)(responses->data.ptr + responses->step*y); - float* r_ptr2 = (float*)(responses->data.ptr + responses->step*(rows - 1 - y)); - short* s_ptr = (short*)(sizes->data.ptr + sizes->step*y); - short* s_ptr2 = (short*)(sizes->data.ptr + sizes->step*(rows - 1 - y)); + float* r_ptr = responses.ptr(y); + float* r_ptr2 = responses.ptr(rows - 1 - y); + short* s_ptr = sizes.ptr(y); + short* s_ptr2 = sizes.ptr(rows - 1 - y); memset( r_ptr, 0, cols*sizeof(r_ptr[0])); memset( r_ptr2, 0, cols*sizeof(r_ptr2[0])); @@ -210,14 +204,11 @@ icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* size memset( s_ptr2, 0, cols*sizeof(s_ptr2[0])); } -#ifdef _OPENMP - #pragma omp parallel for num_threads(nthreads) schedule(static) -#endif for( y = border; y < rows - border; y++ ) { int x = border, i; - float* r_ptr = (float*)(responses->data.ptr + responses->step*y); - short* s_ptr = (short*)(sizes->data.ptr + sizes->step*y); + float* r_ptr = responses.ptr(y); + short* s_ptr = sizes.ptr(y); memset( r_ptr, 0, border*sizeof(r_ptr[0])); memset( s_ptr, 0, border*sizeof(s_ptr[0])); @@ -300,22 +291,17 @@ icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* size } } - cvReleaseMat(&sum); - cvReleaseMat(&tilted); - cvReleaseMat(&flatTilted); - return border; } -static bool -icvStarDetectorSuppressLines( const CvMat* responses, const CvMat* sizes, CvPoint pt, - const CvStarDetectorParams* params ) +static bool StarDetectorSuppressLines( const Mat& responses, const Mat& sizes, Point pt, + int lineThresholdProjected, int lineThresholdBinarized ) { - const float* r_ptr = responses->data.fl; - int rstep = responses->step/sizeof(r_ptr[0]); - const short* s_ptr = sizes->data.s; - int sstep = sizes->step/sizeof(s_ptr[0]); + const float* r_ptr = responses.ptr(); + int rstep = (int)(responses.step/sizeof(r_ptr[0])); + const short* s_ptr = sizes.ptr(); + int sstep = (int)(sizes.step/sizeof(s_ptr[0])); int sz = s_ptr[pt.y*sstep + pt.x]; int x, y, delta = sz/4, radius = delta*4; float Lxx = 0, Lyy = 0, Lxy = 0; @@ -329,7 +315,7 @@ icvStarDetectorSuppressLines( const CvMat* responses, const CvMat* sizes, CvPoin Lxx += Lx*Lx; Lyy += Ly*Ly; Lxy += Lx*Ly; } - if( (Lxx + Lyy)*(Lxx + Lyy) >= params->lineThresholdProjected*(Lxx*Lyy - Lxy*Lxy) ) + if( (Lxx + Lyy)*(Lxx + Lyy) >= lineThresholdProjected*(Lxx*Lyy - Lxy*Lxy) ) return true; for( y = pt.y - radius; y <= pt.y + radius; y += delta ) @@ -340,7 +326,7 @@ icvStarDetectorSuppressLines( const CvMat* responses, const CvMat* sizes, CvPoin Lxxb += Lxb * Lxb; Lyyb += Lyb * Lyb; Lxyb += Lxb * Lyb; } - if( (Lxxb + Lyyb)*(Lxxb + Lyyb) >= params->lineThresholdBinarized*(Lxxb*Lyyb - Lxyb*Lxyb) ) + if( (Lxxb + Lyyb)*(Lxxb + Lyyb) >= lineThresholdBinarized*(Lxxb*Lyyb - Lxyb*Lxyb) ) return true; return false; @@ -348,24 +334,27 @@ icvStarDetectorSuppressLines( const CvMat* responses, const CvMat* sizes, CvPoin static void -icvStarDetectorSuppressNonmax( const CvMat* responses, const CvMat* sizes, - CvSeq* keypoints, int border, - const CvStarDetectorParams* params ) +StarDetectorSuppressNonmax( const Mat& responses, const Mat& sizes, + vector& keypoints, int border, + int responseThreshold, + int lineThresholdProjected, + int lineThresholdBinarized, + int suppressNonmaxSize ) { - int x, y, x1, y1, delta = params->suppressNonmaxSize/2; - int rows = responses->rows, cols = responses->cols; - const float* r_ptr = responses->data.fl; - int rstep = responses->step/sizeof(r_ptr[0]); - const short* s_ptr = sizes->data.s; - int sstep = sizes->step/sizeof(s_ptr[0]); + int x, y, x1, y1, delta = suppressNonmaxSize/2; + int rows = responses.rows, cols = responses.cols; + const float* r_ptr = responses.ptr(); + int rstep = (int)(responses.step/sizeof(r_ptr[0])); + const short* s_ptr = sizes.ptr(); + int sstep = (int)(sizes.step/sizeof(s_ptr[0])); short featureSize = 0; for( y = border; y < rows - border; y += delta+1 ) for( x = border; x < cols - border; x += delta+1 ) { - float maxResponse = (float)params->responseThreshold; - float minResponse = (float)-params->responseThreshold; - CvPoint maxPt = {-1,-1}, minPt = {-1,-1}; + float maxResponse = (float)responseThreshold; + float minResponse = (float)-responseThreshold; + Point maxPt(-1, -1), minPt(-1, -1); int tileEndY = MIN(y + delta, rows - border - 1); int tileEndX = MIN(x + delta, cols - border - 1); @@ -376,12 +365,12 @@ icvStarDetectorSuppressNonmax( const CvMat* responses, const CvMat* sizes, if( maxResponse < val ) { maxResponse = val; - maxPt = cvPoint(x1, y1); + maxPt = Point(x1, y1); } else if( minResponse > val ) { minResponse = val; - minPt = cvPoint(x1, y1); + minPt = Point(x1, y1); } } @@ -396,10 +385,11 @@ icvStarDetectorSuppressNonmax( const CvMat* responses, const CvMat* sizes, } if( (featureSize = s_ptr[maxPt.y*sstep + maxPt.x]) >= 4 && - !icvStarDetectorSuppressLines( responses, sizes, maxPt, params )) + !StarDetectorSuppressLines( responses, sizes, maxPt, lineThresholdProjected, + lineThresholdBinarized )) { - CvStarKeypoint kpt = cvStarKeypoint( maxPt, featureSize, maxResponse ); - cvSeqPush( keypoints, &kpt ); + KeyPoint kpt((float)maxPt.x, (float)maxPt.y, featureSize, -1, maxResponse); + keypoints.push_back(kpt); } } skip_max: @@ -414,66 +404,67 @@ icvStarDetectorSuppressNonmax( const CvMat* responses, const CvMat* sizes, } if( (featureSize = s_ptr[minPt.y*sstep + minPt.x]) >= 4 && - !icvStarDetectorSuppressLines( responses, sizes, minPt, params )) + !StarDetectorSuppressLines( responses, sizes, minPt, + lineThresholdProjected, lineThresholdBinarized)) { - CvStarKeypoint kpt = cvStarKeypoint( minPt, featureSize, minResponse ); - cvSeqPush( keypoints, &kpt ); + KeyPoint kpt((float)minPt.x, (float)minPt.y, featureSize, -1, maxResponse); + keypoints.push_back(kpt); } } skip_min: ; } } - -CV_IMPL CvSeq* -cvGetStarKeypoints( const CvArr* _img, CvMemStorage* storage, - CvStarDetectorParams params ) -{ - CvMat stub, *img = cvGetMat(_img, &stub); - CvSeq* keypoints = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvStarKeypoint), storage ); - CvMat* responses = cvCreateMat( img->rows, img->cols, CV_32FC1 ); - CvMat* sizes = cvCreateMat( img->rows, img->cols, CV_16SC1 ); - - int border = icvStarDetectorComputeResponses( img, responses, sizes, ¶ms ); - if( border >= 0 ) - icvStarDetectorSuppressNonmax( responses, sizes, keypoints, border, ¶ms ); - - cvReleaseMat( &responses ); - cvReleaseMat( &sizes ); - - return border >= 0 ? keypoints : 0; -} - -namespace cv -{ - -StarDetector::StarDetector() -{ - *(CvStarDetectorParams*)this = cvStarDetectorParams(); -} - + StarDetector::StarDetector(int _maxSize, int _responseThreshold, int _lineThresholdProjected, int _lineThresholdBinarized, int _suppressNonmaxSize) -{ - *(CvStarDetectorParams*)this = cvStarDetectorParams(_maxSize, _responseThreshold, - _lineThresholdProjected, _lineThresholdBinarized, _suppressNonmaxSize); -} +: maxSize(_maxSize), responseThreshold(_responseThreshold), + lineThresholdProjected(_lineThresholdProjected), + lineThresholdBinarized(_lineThresholdBinarized), + suppressNonmaxSize(_suppressNonmaxSize) +{} -void StarDetector::operator()(const Mat& image, vector& keypoints) const + +void StarDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask ) const { - CvMat _image = image; - MemStorage storage(cvCreateMemStorage(0)); - Seq kp = cvGetStarKeypoints( &_image, storage, *(const CvStarDetectorParams*)this); - Seq::iterator it = kp.begin(); - keypoints.resize(kp.size()); - size_t i, n = kp.size(); - for( i = 0; i < n; i++, ++it ) + Mat grayImage = image; + if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); + + (*this)(grayImage, keypoints); + KeyPointsFilter::runByPixelsMask( keypoints, mask ); +} + +void StarDetector::operator()(const Mat& img, vector& keypoints) const +{ + Mat responses, sizes; + int border = StarDetectorComputeResponses( img, responses, sizes, maxSize ); + keypoints.clear(); + if( border >= 0 ) + StarDetectorSuppressNonmax( responses, sizes, keypoints, border, + responseThreshold, lineThresholdProjected, + lineThresholdBinarized, suppressNonmaxSize ); +} + + +static Algorithm* createStarDetector() { return new StarDetector; } +static AlgorithmInfo star_info("Feature2D.STAR", createStarDetector); + +AlgorithmInfo* StarDetector::info() const +{ + static volatile bool initialized = false; + if( !initialized ) { - const CvStarKeypoint& kpt = *it; - keypoints[i] = KeyPoint(kpt.pt, (float)kpt.size, -1.f, kpt.response, 0); + star_info.addParam(this, "maxSize", maxSize); + star_info.addParam(this, "responseThreshold", responseThreshold); + star_info.addParam(this, "lineThresholdProjected", lineThresholdProjected); + star_info.addParam(this, "lineThresholdBinarized", lineThresholdBinarized); + star_info.addParam(this, "suppressNonmaxSize", suppressNonmaxSize); + + initialized = true; } -} + return &star_info; +} } diff --git a/modules/features2d/src/test_pairs.txt b/modules/features2d/src/test_pairs.txt deleted file mode 100644 index 310b529ff6..0000000000 --- a/modules/features2d/src/test_pairs.txt +++ /dev/null @@ -1,513 +0,0 @@ -# x1 y1 x2 y2 --1 -2 -1 7 --1 -14 3 -3 --2 1 2 11 -6 1 -7 -10 -2 13 0 -1 -5 -14 -3 5 -8 -2 4 2 -8 -11 5 -15 --23 -6 -9 8 -6 -12 8 -10 --1 -3 1 8 -6 3 6 5 --6 -7 -5 5 --2 22 -8 -11 -7 14 5 8 -14 -1 -14 -5 -9 -14 0 2 --3 7 6 22 -6 -6 -5 -8 -9 -5 -1 7 --7 -3 -18 -10 --5 4 11 0 -3 2 10 9 -3 -10 9 4 -12 0 19 -3 -15 1 -5 -11 --1 14 8 7 --23 7 5 -5 --6 0 17 -10 --4 13 -4 -3 -1 -12 2 -12 -8 0 22 3 -13 -13 -1 3 -17 -16 10 6 -15 7 0 -5 --12 2 -2 19 --6 3 -15 -4 -3 8 14 0 --11 4 5 5 --7 11 1 7 -12 6 3 21 -2 -3 1 14 -1 5 11 -5 --17 3 2 -6 -8 6 -10 5 --2 -14 4 0 --7 5 5 -6 -4 10 -7 4 -0 22 -18 7 --3 -1 18 0 -22 -4 3 -5 --7 1 -3 2 --20 19 -2 17 --10 3 24 -8 --14 -5 5 7 -12 -2 -15 -4 -12 4 -19 0 -13 20 5 3 --12 -8 0 5 -6 -5 -11 -7 --11 6 -22 -3 -4 15 1 10 --4 -7 -6 15 -10 5 24 0 -6 3 -2 22 -14 -13 -4 4 -8 -13 -22 -18 --1 -1 3 -7 --12 -19 3 4 -10 8 -2 13 --1 -6 -5 -6 --21 2 2 -3 --7 4 16 0 --5 -6 -1 -12 --1 1 18 9 -10 -7 6 -11 -3 4 -7 19 -5 -18 5 -4 -0 4 4 -20 --11 7 12 18 -17 -20 7 -18 -15 2 -11 19 -6 -18 3 -7 -1 -4 13 -14 -3 17 -8 2 -2 -7 6 1 --9 17 8 -2 --6 -8 12 -1 -4 -2 6 -1 -7 -2 8 6 --1 -8 -9 -7 --9 8 0 15 -22 0 -15 -4 --1 -14 -2 3 --4 -7 -7 17 --2 -8 -4 9 --7 5 7 7 -13 -5 11 -8 --4 11 8 0 --11 5 -6 -9 --6 2 -20 3 -2 -6 10 6 --6 -6 7 -15 --3 -6 1 2 -0 11 2 -3 --12 7 5 14 --7 0 -1 -1 -0 -16 8 6 -11 22 -3 0 -0 19 -17 5 --14 -23 -19 -13 -10 -8 -2 -11 -6 -11 13 -10 --7 1 0 14 -1 -12 -5 -5 -7 4 -1 8 --5 -1 2 15 --1 -3 -10 7 --6 3 -18 10 --13 -7 10 -13 --1 1 -10 13 -14 -19 -14 8 --13 -4 1 7 --2 1 -7 12 --5 3 -5 1 --2 -2 -10 8 -14 2 7 8 -9 3 2 8 -1 -9 0 -18 -0 4 12 1 -9 0 -10 -14 --9 -13 6 -2 -5 1 10 10 --6 -3 -5 -16 -6 11 0 -5 -10 -23 2 1 --5 13 9 -3 --1 -4 -5 -13 -13 10 8 -11 -20 19 2 -9 --8 4 -9 0 -10 -14 19 15 --12 -14 -3 -10 --3 -23 -2 17 --11 -3 -14 6 --2 19 2 -4 -5 -5 -13 3 --2 2 4 -5 -4 17 -11 17 --2 -7 23 1 -13 8 -16 1 --5 -13 -17 1 -6 4 -3 -8 --9 -5 -10 -2 -0 -9 -2 -7 -0 5 2 5 --16 -4 3 6 --15 2 12 -2 --1 4 2 6 -1 1 -8 -2 -12 -2 -2 -5 -8 -8 9 -9 --10 2 1 3 -10 -4 4 -9 -12 6 5 2 --8 -3 5 0 -1 -13 2 -7 --10 -1 -18 7 -8 -1 -10 -9 --1 -23 2 6 --3 -5 2 3 -11 0 -7 -4 -2 15 -3 -10 --8 -20 3 -13 --12 -19 -11 5 --13 -17 2 -3 -4 7 0 -12 --1 5 -6 -14 -11 -4 -4 0 -10 3 -3 7 -21 13 6 -11 -24 -12 -4 -7 -16 4 -14 3 -5 -3 -12 -7 --4 0 -5 7 --9 -17 -7 13 --6 22 5 -11 --8 2 -11 23 --10 7 14 -1 --10 -3 3 8 -1 -13 0 -6 --21 -7 -14 6 -19 18 -6 -4 -7 10 -4 -1 -21 -1 -5 1 -6 -10 -2 -11 --3 18 7 -1 --9 -3 10 -5 -14 -13 -3 17 --19 11 -18 -1 --2 8 -23 -18 --5 0 -9 -2 --11 -4 -8 2 -6 14 -6 -3 -0 -3 0 -15 -4 -9 -9 -15 -11 -1 11 3 --16 -10 7 -7 --10 -2 -2 -10 --3 -5 -23 5 --8 13 -11 -15 -11 -15 -6 6 --3 -16 2 -2 -12 6 24 -16 -0 -10 11 8 -7 -7 -7 -19 -16 5 -3 9 -7 9 -16 -7 -2 3 9 -10 -1 21 7 8 -0 7 17 1 -12 -8 6 9 --7 11 -6 -8 -0 19 3 9 --7 1 -11 -5 -8 0 14 -2 --2 12 -6 -15 -12 4 -21 0 --4 17 -7 -6 --9 -10 -7 -14 --10 -15 -14 -15 --5 -7 -12 5 -0 -4 -4 15 -2 5 -23 -6 --21 -4 4 -6 -5 -10 6 -15 --3 4 5 -1 -19 -4 -4 -23 -17 -4 -11 13 -12 1 -14 4 --6 -11 10 -20 -5 4 20 3 --20 -8 1 3 -9 -19 -3 9 -15 18 -4 11 -16 12 7 8 --8 -14 9 -3 -0 -6 -4 2 --10 1 2 -1 --7 8 18 -6 -12 9 -23 -7 --6 8 2 5 -6 -9 -7 -12 --2 -1 2 -7 -9 9 15 7 -2 6 6 -6 -12 16 19 0 -3 4 0 6 --1 -2 17 2 -1 8 1 3 --1 -12 0 -11 -2 -11 9 7 -3 -1 4 -19 --11 -1 3 -1 --10 1 -4 -10 -3 -2 11 6 -7 3 -8 -9 --14 24 -10 -2 --3 -3 -6 -18 --10 -13 -1 -7 --7 2 -6 9 --4 2 -13 6 --4 4 3 -2 -2 -4 13 9 -5 -11 -11 -6 --2 4 -9 11 -0 -19 -5 -23 --7 -5 -6 -3 --4 -6 14 12 --11 12 -16 -8 -15 -21 6 -12 --1 -2 16 -8 --1 6 -2 -8 --1 1 8 -9 --4 3 -2 -2 -0 -7 -8 4 --11 11 2 -12 -3 2 7 11 --4 -7 -6 -9 --7 3 0 -5 --7 3 -5 -10 --1 -3 -10 8 -8 0 1 5 -0 9 16 1 -4 8 -3 -11 -9 -15 17 8 -2 0 17 -9 --11 -6 -3 -10 -1 1 -8 15 --13 -12 4 -2 -4 -6 -10 -6 --7 5 -5 7 -6 10 9 8 -7 -5 -3 -18 -3 -6 4 5 --13 -10 -3 -5 -2 -11 0 -16 --21 7 -13 -5 --14 -14 -4 -4 -9 4 -3 7 -11 4 -4 10 -17 6 17 9 -8 -10 -11 0 --16 -6 8 -6 -5 -13 -5 10 -2 3 16 12 --8 13 -6 0 -0 10 -11 4 -5 8 -2 10 --7 11 3 -13 -4 2 -3 -7 --2 -14 16 -11 --6 11 6 7 -15 -3 -10 8 -8 -3 -12 12 -6 -13 7 -14 --5 -11 -6 -8 --6 7 3 6 -10 -4 1 5 -16 9 13 10 -10 -17 8 2 -1 -5 -4 4 -8 -14 2 -5 --9 4 -3 -6 --7 3 0 -10 --8 -2 4 -10 -5 -8 24 -9 --8 2 -9 8 -17 -4 2 -5 -0 14 9 -9 -15 11 5 -6 -1 -8 4 -3 --21 9 2 10 --1 2 11 4 -3 24 -2 2 -17 -8 -10 -14 -5 6 7 -13 -10 11 -1 0 -6 4 6 -10 --2 -12 6 5 --1 3 -15 8 --4 1 11 -7 -11 1 0 5 --12 6 1 10 --2 -3 4 -1 --11 -2 12 -1 --8 7 -18 -20 -0 2 2 -9 --1 -13 2 -16 --1 3 -17 -5 -8 15 -14 3 --12 -13 15 6 --8 2 6 2 -22 6 -23 -3 --7 -2 0 -6 --10 13 6 -6 -7 6 12 -10 -7 -6 11 -2 --22 0 -17 -2 --1 -4 -14 -11 --8 -2 12 7 --5 12 -13 7 --2 2 6 -7 -8 0 23 -3 -12 6 -11 13 --10 -21 8 10 -0 -3 15 7 --6 7 -12 -5 --10 -21 -11 12 --11 -5 -11 8 -0 5 -1 -11 --9 8 -1 7 --23 11 -5 21 --5 0 6 -8 -8 -6 12 8 -5 -7 -2 3 --20 -5 9 -12 -12 -6 3 -11 -5 4 11 13 -12 2 -12 13 --13 -4 7 4 -15 0 -16 -3 -2 -3 14 -2 --14 4 -11 16 -3 -13 10 23 --19 9 5 2 -3 5 -7 14 --13 19 15 -11 -0 14 -5 -2 --4 11 -6 0 -5 -2 -8 -13 --15 -11 -17 -7 -3 1 -8 -10 --10 -13 -12 7 --13 0 -6 23 --17 2 -3 -7 -3 1 -10 4 -4 13 -6 14 --2 -19 5 -1 --8 9 -5 10 --1 7 7 5 --10 9 0 19 -5 7 -7 -4 -1 -11 -11 -1 --1 2 11 -4 -7 -1 -2 2 --20 1 -6 -9 --18 -4 -18 8 --2 -16 -6 7 --6 -3 -4 -1 --16 0 -5 24 --2 -4 9 -1 -2 -8 15 -6 -4 11 -3 0 -6 7 -10 2 --9 -7 -6 12 -15 24 -1 -8 --9 15 -15 -3 --5 17 -10 11 -13 -2 4 -15 --1 -2 -23 4 -3 -16 -14 -7 --5 -3 -9 -10 -3 -5 -1 -2 -4 -1 8 1 -9 12 -14 9 -17 -9 0 -3 -4 5 -6 13 --8 -1 10 19 --5 8 2 -15 --9 -12 -5 -4 -0 12 4 24 --2 8 4 14 --4 8 16 -7 --1 5 -4 -8 -18 -2 17 -5 --2 8 -2 -9 --7 3 -6 1 --22 -5 -2 -5 --10 -8 1 14 --13 -3 9 3 --1 -4 0 -1 --21 -7 -19 12 -8 -8 8 24 --6 12 3 -2 --11 -5 -4 -22 -5 -3 4 -4 -24 -16 -9 7 -23 -10 18 -9 -12 1 21 17 --6 24 -11 -3 -17 -7 -6 1 -4 4 -7 2 -6 14 3 -12 -0 -6 13 -16 -5 -10 12 7 -2 5 -3 6 -0 7 1 -23 --5 15 14 1 --1 -3 6 6 --9 6 12 -9 --2 4 7 -4 --5 -4 4 4 -0 -13 -10 6 --12 2 -3 -6 -0 16 3 -3 --14 5 11 6 -11 5 -13 0 -5 7 -5 -1 -4 12 10 6 -4 -10 -11 -1 -10 4 5 -14 --14 11 0 -13 -8 2 24 12 -3 -1 2 -1 --14 9 3 -23 --6 -8 9 0 -14 -15 -10 10 --6 -10 -5 -7 -5 11 -15 -3 -0 1 8 1 --6 -11 -18 -4 -0 9 -4 22 --1 -5 4 -9 -2 -20 6 1 -2 1 -12 -9 -15 5 -6 4 -4 19 11 4 --4 17 -1 -8 --12 -8 -3 7 -9 11 1 8 -22 9 15 -15 --7 -7 -23 1 -13 -5 2 -8 --5 3 -11 11 --18 3 -5 14 -7 -20 -23 -10 --5 -2 0 6 --13 -17 2 -3 --1 -6 -2 14 --16 -12 6 15 --2 -12 -19 3 diff --git a/modules/features2d/test/test_features2d.cpp b/modules/features2d/test/test_features2d.cpp index a9cd60164e..d6be1cebce 100644 --- a/modules/features2d/test/test_features2d.cpp +++ b/modules/features2d/test/test_features2d.cpp @@ -440,7 +440,7 @@ protected: fs.open( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::WRITE ); if( fs.isOpened() ) { - SurfFeatureDetector fd; + ORB fd; fd.detect(img, keypoints); write( fs, "keypoints", keypoints ); } @@ -491,7 +491,7 @@ private: CV_DescriptorExtractorTest& operator=(const CV_DescriptorExtractorTest&) { return *this; } }; -template +/*template class CV_CalonderDescriptorExtractorTest : public CV_DescriptorExtractorTest { public: @@ -506,7 +506,7 @@ protected: new CalonderDescriptorExtractor( string(CV_DescriptorExtractorTest::ts->get_data_path()) + FEATURES2D_DIR + "/calonder_classifier.rtc"); } -}; +};*/ /****************************************************************************************\ * Algorithmic tests for descriptor matchers * @@ -991,24 +991,12 @@ TEST( Features2d_Detector_MSER, regression ) test.safe_run(); } -TEST( Features2d_Detector_SIFT, regression ) -{ - CV_FeatureDetectorTest test( "detector-sift", FeatureDetector::create("SIFT") ); - test.safe_run(); -} - TEST( Features2d_Detector_STAR, regression ) { CV_FeatureDetectorTest test( "detector-star", FeatureDetector::create("STAR") ); test.safe_run(); } -TEST( Features2d_Detector_SURF, regression ) -{ - CV_FeatureDetectorTest test( "detector-surf", FeatureDetector::create("SURF") ); - test.safe_run(); -} - TEST( Features2d_Detector_ORB, regression ) { CV_FeatureDetectorTest test( "detector-orb", FeatureDetector::create("ORB") ); @@ -1027,23 +1015,6 @@ TEST( Features2d_Detector_PyramidFAST, regression ) test.safe_run(); } -/* - * Descriptors - */ -TEST( Features2d_DescriptorExtractor_SIFT, regression ) -{ - CV_DescriptorExtractorTest > test( "descriptor-sift", 0.03f, - DescriptorExtractor::create("SIFT"), 8.06652f ); - test.safe_run(); -} - -TEST( Features2d_DescriptorExtractor_SURF, regression ) -{ - CV_DescriptorExtractorTest > test( "descriptor-surf", 0.035f, - DescriptorExtractor::create("SURF"), 0.147372f ); - test.safe_run(); -} - TEST( Features2d_DescriptorExtractor_ORB, regression ) { // TODO adjust the parameters below @@ -1066,13 +1037,6 @@ TEST( Features2d_DescriptorExtractor_BRIEF, regression ) test.safe_run(); }*/ -TEST( Features2d_DescriptorExtractor_OpponentSURF, regression ) -{ - CV_DescriptorExtractorTest > test( "descriptor-opponent-surf", 0.18f, - DescriptorExtractor::create("OpponentSURF"), 0.147372f ); - test.safe_run(); -} - #if CV_SSE2 TEST( Features2d_DescriptorExtractor_Calonder_uchar, regression ) { @@ -1096,7 +1060,7 @@ TEST( Features2d_DescriptorExtractor_Calonder_float, regression ) */ TEST( Features2d_DescriptorMatcher_BruteForce, regression ) { - CV_DescriptorMatcherTest test( "descriptor-matcher-brute-force", new BruteForceMatcher >, 0.01f ); + CV_DescriptorMatcherTest test( "descriptor-matcher-brute-force", new BFMatcher(NORM_L2), 0.01f ); test.safe_run(); } diff --git a/modules/features2d/test/test_mser.cpp b/modules/features2d/test/test_mser.cpp index 20ed528669..32a4be976e 100644 --- a/modules/features2d/test/test_mser.cpp +++ b/modules/features2d/test/test_mser.cpp @@ -155,27 +155,24 @@ void CV_MserTest::run(int) { string image_path = string(ts->get_data_path()) + "mser/puzzle.png"; - IplImage* img = cvLoadImage( image_path.c_str()); - if (!img) + Mat img = imread( image_path ); + if (img.empty()) { ts->printf( cvtest::TS::LOG, "Unable to open image mser/puzzle.png\n"); ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA); return; } - CvSeq* contours; - CvMemStorage* storage= cvCreateMemStorage(); - IplImage* hsv = cvCreateImage( cvGetSize( img ), IPL_DEPTH_8U, 3 ); - cvCvtColor( img, hsv, CV_BGR2YCrCb ); - CvMSERParams params = cvMSERParams();//cvMSERParams( 5, 60, cvRound(.2*img->width*img->height), .25, .2 ); - cvExtractMSER( hsv, NULL, &contours, storage, params ); + Mat yuv; + cvtColor(img, yuv, COLOR_BGR2YCrCb); + vector > msers; + MSER()(yuv, msers); vector boxes; vector boxes_orig; - for ( int i = 0; i < contours->total; i++ ) + for ( size_t i = 0; i < msers.size(); i++ ) { - CvContour* r = *(CvContour**)cvGetSeqElem( contours, i ); - CvBox2D box = cvFitEllipse2( r ); + RotatedRect box = fitEllipse(msers[i]); box.angle=(float)CV_PI/2-box.angle; boxes.push_back(box); } @@ -203,10 +200,6 @@ void CV_MserTest::run(int) ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); ts->printf( cvtest::TS::LOG, "Incorrect correspondence in box %d\n",n_box); } - - cvReleaseMemStorage(&storage); - cvReleaseImage(&hsv); - cvReleaseImage(&img); } TEST(Features2d_MSER, regression) { CV_MserTest test; test.safe_run(); } diff --git a/modules/gpu/include/opencv2/gpu/gpu.hpp b/modules/gpu/include/opencv2/gpu/gpu.hpp index 244f336d1d..6ecea83298 100644 --- a/modules/gpu/include/opencv2/gpu/gpu.hpp +++ b/modules/gpu/include/opencv2/gpu/gpu.hpp @@ -1511,7 +1511,7 @@ private: ////////////////////////////////// SURF ////////////////////////////////////////// -class CV_EXPORTS SURF_GPU : public CvSURFParams +class CV_EXPORTS SURF_GPU { public: enum KeypointLayout @@ -1566,6 +1566,14 @@ public: void releaseMemory(); + // SURF parameters; + int extended; + int upright; + double hessianThreshold; + + int nOctaves; + int nOctaveLayers; + //! max keypoints = min(keypointsRatio * img.size().area(), 65535) float keypointsRatio; @@ -1656,7 +1664,7 @@ public: //! Constructor //! n_features - the number of desired features //! detector_params - parameters to use - explicit ORB_GPU(size_t n_features = 500, const ORB::CommonParams& detector_params = ORB::CommonParams()); + explicit ORB_GPU(size_t n_features = 500, float scaleFactor = 1.2f, int nlevels = 3); //! Compute the ORB features on an image //! image - the image to compute the features (supports only CV_8UC1 images) @@ -1682,7 +1690,7 @@ public: //! returns the descriptor size in bytes inline int descriptorSize() const { return kBytes; } - void setParams(size_t n_features, const ORB::CommonParams& detector_params); + void setParams(size_t n_features, float scaleFactor, int nlevels); inline void setFastParams(int threshold, bool nonmaxSupression = true) { fastDetector_.threshold = threshold; @@ -1706,7 +1714,7 @@ private: void mergeKeyPoints(GpuMat& keypoints); - ORB::CommonParams params_; + ORB params_; // The number of desired features per scale std::vector n_features_per_level_; diff --git a/modules/gpu/src/orb.cpp b/modules/gpu/src/orb.cpp index 1166999d93..9972fced3e 100644 --- a/modules/gpu/src/orb.cpp +++ b/modules/gpu/src/orb.cpp @@ -48,14 +48,14 @@ using namespace cv::gpu; #if !defined (HAVE_CUDA) -cv::gpu::ORB_GPU::ORB_GPU(size_t, const ORB::CommonParams&) : fastDetector_(0) { throw_nogpu(); } +cv::gpu::ORB_GPU::ORB_GPU(size_t, float, int) : fastDetector_(0) { throw_nogpu(); } void cv::gpu::ORB_GPU::operator()(const GpuMat&, const GpuMat&, std::vector&) { throw_nogpu(); } void cv::gpu::ORB_GPU::operator()(const GpuMat&, const GpuMat&, GpuMat&) { throw_nogpu(); } void cv::gpu::ORB_GPU::operator()(const GpuMat&, const GpuMat&, std::vector&, GpuMat&) { throw_nogpu(); } void cv::gpu::ORB_GPU::operator()(const GpuMat&, const GpuMat&, GpuMat&, GpuMat&) { throw_nogpu(); } void cv::gpu::ORB_GPU::downloadKeyPoints(GpuMat&, std::vector&) { throw_nogpu(); } void cv::gpu::ORB_GPU::convertKeyPoints(Mat&, std::vector&) { throw_nogpu(); } -void cv::gpu::ORB_GPU::setParams(size_t, const ORB::CommonParams&) { throw_nogpu(); } +void cv::gpu::ORB_GPU::setParams(size_t, float, int) { throw_nogpu(); } void cv::gpu::ORB_GPU::release() { throw_nogpu(); } void cv::gpu::ORB_GPU::buildScalePyramids(const GpuMat&, const GpuMat&) { throw_nogpu(); } void cv::gpu::ORB_GPU::computeKeyPointsPyramid() { throw_nogpu(); } @@ -83,10 +83,10 @@ namespace cv { namespace gpu { namespace device } }}} -cv::gpu::ORB_GPU::ORB_GPU(size_t n_features, const ORB::CommonParams& detector_params) : +cv::gpu::ORB_GPU::ORB_GPU(size_t n_features, float scaleFactor, int nlevels) : fastDetector_(DEFAULT_FAST_THRESHOLD) { - setParams(n_features, detector_params); + setParams(n_features, scaleFactor, nlevels); blurFilter = createGaussianFilter_GPU(CV_8UC1, Size(7, 7), 2, 2, BORDER_REFLECT_101); @@ -407,9 +407,9 @@ namespace } } -void cv::gpu::ORB_GPU::setParams(size_t n_features, const ORB::CommonParams& detector_params) +void cv::gpu::ORB_GPU::setParams(size_t n_features, float scaleFactor, int nlevels) { - params_ = detector_params; + params_ = ORB((int)n_features, scaleFactor, nlevels); // fill the extractors and descriptors for the corresponding scales int n_levels = static_cast(params_.n_levels_); diff --git a/modules/imgproc/doc/miscellaneous_transformations.rst b/modules/imgproc/doc/miscellaneous_transformations.rst index 6bc9767e6c..2d689fd532 100644 --- a/modules/imgproc/doc/miscellaneous_transformations.rst +++ b/modules/imgproc/doc/miscellaneous_transformations.rst @@ -586,37 +586,6 @@ Use these functions to either mark a connected component with the specified colo -inpaint ------------ -Restores the selected region in an image using the region neighborhood. - -.. ocv:function:: void inpaint( InputArray src, InputArray inpaintMask, OutputArray dst, double inpaintRadius, int flags ) - -.. ocv:pyfunction:: cv2.inpaint(src, inpaintMask, inpaintRange, flags[, dst]) -> dst - -.. ocv:cfunction:: void cvInpaint( const CvArr* src, const CvArr* mask, CvArr* dst, double inpaintRadius, int flags) -.. ocv:pyoldfunction:: cv.Inpaint(src, mask, dst, inpaintRadius, flags) -> None - - :param src: Input 8-bit 1-channel or 3-channel image. - - :param inpaintMask: Inpainting mask, 8-bit 1-channel image. Non-zero pixels indicate the area that needs to be inpainted. - - :param dst: Output image with the same size and type as ``src`` . - - :param inpaintRadius: Radius of a circlular neighborhood of each point inpainted that is considered by the algorithm. - - :param flags: Inpainting method that could be one of the following: - - * **INPAINT_NS** Navier-Stokes based method. - - * **INPAINT_TELEA** Method by Alexandru Telea [Telea04]_. - -The function reconstructs the selected image area from the pixel near the area boundary. The function may be used to remove dust and scratches from a scanned photo, or to remove undesirable objects from still images or video. See -http://en.wikipedia.org/wiki/Inpainting -for more details. - - - integral ------------ Calculates the integral of an image. diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index 87779e867c..df6e527e6c 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -751,17 +751,6 @@ CV_EXPORTS_W void grabCut( InputArray img, InputOutputArray mask, Rect rect, InputOutputArray bgdModel, InputOutputArray fgdModel, int iterCount, int mode = GC_EVAL ); -//! the inpainting algorithm -enum -{ - INPAINT_NS=CV_INPAINT_NS, // Navier-Stokes algorithm - INPAINT_TELEA=CV_INPAINT_TELEA // A. Telea algorithm -}; - -//! restores the damaged image areas using one of the available intpainting algorithms -CV_EXPORTS_W void inpaint( InputArray src, InputArray inpaintMask, - OutputArray dst, double inpaintRange, int flags ); - //! builds the discrete Voronoi diagram CV_EXPORTS_AS(distanceTransformWithLabels) void distanceTransform( InputArray src, OutputArray dst, OutputArray labels, int distanceType, int maskSize ); diff --git a/modules/imgproc/src/_list.h b/modules/imgproc/src/_list.h index b2b63e9cb6..a19f7e2af9 100644 --- a/modules/imgproc/src/_list.h +++ b/modules/imgproc/src/_list.h @@ -345,7 +345,7 @@ void prefix##remove_at_##type(_CVLIST* l, CVPOS pos)\ void prefix##set_##type(CVPOS pos, type* data)\ {\ ELEMENT_##type* element = ((ELEMENT_##type*)(pos.m_pos));\ - memcpy(&(element->m_data), data, sizeof(data));\ + memcpy(&(element->m_data), data, sizeof(*data));\ }\ type* prefix##get_##type(CVPOS pos)\ {\ diff --git a/modules/legacy/doc/legacy.rst b/modules/legacy/doc/legacy.rst new file mode 100644 index 0000000000..9d8636ee73 --- /dev/null +++ b/modules/legacy/doc/legacy.rst @@ -0,0 +1,10 @@ +******************************** +photo. Computational Photography +******************************** + +.. highlight:: cpp + +.. toctree:: + :maxdepth: 2 + + inpainting diff --git a/modules/legacy/include/opencv2/legacy/compat.hpp b/modules/legacy/include/opencv2/legacy/compat.hpp index 7644cbbd23..210da48ee9 100644 --- a/modules/legacy/include/opencv2/legacy/compat.hpp +++ b/modules/legacy/include/opencv2/legacy/compat.hpp @@ -600,6 +600,138 @@ CV_EXPORTS void cvProjectPointsSimple( int point_count, CvPoint3D64f* _object_p #define cvConvertPointsHomogenious cvConvertPointsHomogeneous + +//////////////////////////////////// feature extractors: obsolete API ////////////////////////////////// + +typedef struct CvSURFPoint +{ + CvPoint2D32f pt; + + int laplacian; + int size; + float dir; + float hessian; + +} CvSURFPoint; + +CV_INLINE CvSURFPoint cvSURFPoint( CvPoint2D32f pt, int laplacian, + int size, float dir CV_DEFAULT(0), + float hessian CV_DEFAULT(0)) +{ + CvSURFPoint kp; + + kp.pt = pt; + kp.laplacian = laplacian; + kp.size = size; + kp.dir = dir; + kp.hessian = hessian; + + return kp; +} + +typedef struct CvSURFParams +{ + int extended; + int upright; + double hessianThreshold; + + int nOctaves; + int nOctaveLayers; + +} CvSURFParams; + +CVAPI(CvSURFParams) cvSURFParams( double hessianThreshold, int extended CV_DEFAULT(0) ); + +// If useProvidedKeyPts!=0, keypoints are not detected, but descriptors are computed +// at the locations provided in keypoints (a CvSeq of CvSURFPoint). +CVAPI(void) cvExtractSURF( const CvArr* img, const CvArr* mask, + CvSeq** keypoints, CvSeq** descriptors, + CvMemStorage* storage, CvSURFParams params, + int useProvidedKeyPts CV_DEFAULT(0) ); + +/*! + Maximal Stable Regions Parameters + */ +typedef struct CvMSERParams +{ + //! delta, in the code, it compares (size_{i}-size_{i-delta})/size_{i-delta} + int delta; + //! prune the area which bigger than maxArea + int maxArea; + //! prune the area which smaller than minArea + int minArea; + //! prune the area have simliar size to its children + float maxVariation; + //! trace back to cut off mser with diversity < min_diversity + float minDiversity; + + /////// the next few params for MSER of color image + + //! for color image, the evolution steps + int maxEvolution; + //! the area threshold to cause re-initialize + double areaThreshold; + //! ignore too small margin + double minMargin; + //! the aperture size for edge blur + int edgeBlurSize; +} CvMSERParams; + +CVAPI(CvMSERParams) cvMSERParams( int delta CV_DEFAULT(5), int min_area CV_DEFAULT(60), + int max_area CV_DEFAULT(14400), float max_variation CV_DEFAULT(.25f), + float min_diversity CV_DEFAULT(.2f), int max_evolution CV_DEFAULT(200), + double area_threshold CV_DEFAULT(1.01), + double min_margin CV_DEFAULT(.003), + int edge_blur_size CV_DEFAULT(5) ); + +// Extracts the contours of Maximally Stable Extremal Regions +CVAPI(void) cvExtractMSER( CvArr* _img, CvArr* _mask, CvSeq** contours, CvMemStorage* storage, CvMSERParams params ); + + +typedef struct CvStarKeypoint +{ + CvPoint pt; + int size; + float response; +} CvStarKeypoint; + +CV_INLINE CvStarKeypoint cvStarKeypoint(CvPoint pt, int size, float response) +{ + CvStarKeypoint kpt; + kpt.pt = pt; + kpt.size = size; + kpt.response = response; + return kpt; +} + +typedef struct CvStarDetectorParams +{ + int maxSize; + int responseThreshold; + int lineThresholdProjected; + int lineThresholdBinarized; + int suppressNonmaxSize; +} CvStarDetectorParams; + +CV_INLINE CvStarDetectorParams cvStarDetectorParams( + int maxSize CV_DEFAULT(45), + int responseThreshold CV_DEFAULT(30), + int lineThresholdProjected CV_DEFAULT(10), + int lineThresholdBinarized CV_DEFAULT(8), + int suppressNonmaxSize CV_DEFAULT(5)) +{ + CvStarDetectorParams params; + params.maxSize = maxSize; + params.responseThreshold = responseThreshold; + params.lineThresholdProjected = lineThresholdProjected; + params.lineThresholdBinarized = lineThresholdBinarized; + params.suppressNonmaxSize = suppressNonmaxSize; + + return params; +} + +CVAPI(CvSeq*) cvGetStarKeypoints( const CvArr* img, CvMemStorage* storage, + CvStarDetectorParams params CV_DEFAULT(cvStarDetectorParams())); #ifdef __cplusplus } diff --git a/modules/legacy/include/opencv2/legacy/legacy.hpp b/modules/legacy/include/opencv2/legacy/legacy.hpp index d3aaaf634d..7b1184d1c6 100644 --- a/modules/legacy/include/opencv2/legacy/legacy.hpp +++ b/modules/legacy/include/opencv2/legacy/legacy.hpp @@ -1761,6 +1761,1043 @@ protected: IplImage* m_mask; }; + +namespace cv +{ + +/*! + The Patch Generator class + */ +class CV_EXPORTS PatchGenerator +{ +public: + PatchGenerator(); + PatchGenerator(double _backgroundMin, double _backgroundMax, + double _noiseRange, bool _randomBlur=true, + double _lambdaMin=0.6, double _lambdaMax=1.5, + double _thetaMin=-CV_PI, double _thetaMax=CV_PI, + double _phiMin=-CV_PI, double _phiMax=CV_PI ); + void operator()(const Mat& image, Point2f pt, Mat& patch, Size patchSize, RNG& rng) const; + void operator()(const Mat& image, const Mat& transform, Mat& patch, + Size patchSize, RNG& rng) const; + void warpWholeImage(const Mat& image, Mat& matT, Mat& buf, + CV_OUT Mat& warped, int border, RNG& rng) const; + void generateRandomTransform(Point2f srcCenter, Point2f dstCenter, + CV_OUT Mat& transform, RNG& rng, + bool inverse=false) const; + void setAffineParam(double lambda, double theta, double phi); + + double backgroundMin, backgroundMax; + double noiseRange; + bool randomBlur; + double lambdaMin, lambdaMax; + double thetaMin, thetaMax; + double phiMin, phiMax; +}; + + +class CV_EXPORTS LDetector +{ +public: + LDetector(); + LDetector(int _radius, int _threshold, int _nOctaves, + int _nViews, double _baseFeatureSize, double _clusteringDistance); + void operator()(const Mat& image, + CV_OUT vector& keypoints, + int maxCount=0, bool scaleCoords=true) const; + void operator()(const vector& pyr, + CV_OUT vector& keypoints, + int maxCount=0, bool scaleCoords=true) const; + void getMostStable2D(const Mat& image, CV_OUT vector& keypoints, + int maxCount, const PatchGenerator& patchGenerator) const; + void setVerbose(bool verbose); + + void read(const FileNode& node); + void write(FileStorage& fs, const String& name=String()) const; + + int radius; + int threshold; + int nOctaves; + int nViews; + bool verbose; + + double baseFeatureSize; + double clusteringDistance; +}; + +typedef LDetector YAPE; + +class CV_EXPORTS FernClassifier +{ +public: + FernClassifier(); + FernClassifier(const FileNode& node); + FernClassifier(const vector >& points, + const vector& refimgs, + const vector >& labels=vector >(), + int _nclasses=0, int _patchSize=PATCH_SIZE, + int _signatureSize=DEFAULT_SIGNATURE_SIZE, + int _nstructs=DEFAULT_STRUCTS, + int _structSize=DEFAULT_STRUCT_SIZE, + int _nviews=DEFAULT_VIEWS, + int _compressionMethod=COMPRESSION_NONE, + const PatchGenerator& patchGenerator=PatchGenerator()); + virtual ~FernClassifier(); + virtual void read(const FileNode& n); + virtual void write(FileStorage& fs, const String& name=String()) const; + virtual void trainFromSingleView(const Mat& image, + const vector& keypoints, + int _patchSize=PATCH_SIZE, + int _signatureSize=DEFAULT_SIGNATURE_SIZE, + int _nstructs=DEFAULT_STRUCTS, + int _structSize=DEFAULT_STRUCT_SIZE, + int _nviews=DEFAULT_VIEWS, + int _compressionMethod=COMPRESSION_NONE, + const PatchGenerator& patchGenerator=PatchGenerator()); + virtual void train(const vector >& points, + const vector& refimgs, + const vector >& labels=vector >(), + int _nclasses=0, int _patchSize=PATCH_SIZE, + int _signatureSize=DEFAULT_SIGNATURE_SIZE, + int _nstructs=DEFAULT_STRUCTS, + int _structSize=DEFAULT_STRUCT_SIZE, + int _nviews=DEFAULT_VIEWS, + int _compressionMethod=COMPRESSION_NONE, + const PatchGenerator& patchGenerator=PatchGenerator()); + virtual int operator()(const Mat& img, Point2f kpt, vector& signature) const; + virtual int operator()(const Mat& patch, vector& signature) const; + virtual void clear(); + virtual bool empty() const; + void setVerbose(bool verbose); + + int getClassCount() const; + int getStructCount() const; + int getStructSize() const; + int getSignatureSize() const; + int getCompressionMethod() const; + Size getPatchSize() const; + + struct Feature + { + uchar x1, y1, x2, y2; + Feature() : x1(0), y1(0), x2(0), y2(0) {} + Feature(int _x1, int _y1, int _x2, int _y2) + : x1((uchar)_x1), y1((uchar)_y1), x2((uchar)_x2), y2((uchar)_y2) + {} + template bool operator ()(const Mat_<_Tp>& patch) const + { return patch(y1,x1) > patch(y2, x2); } + }; + + enum + { + PATCH_SIZE = 31, + DEFAULT_STRUCTS = 50, + DEFAULT_STRUCT_SIZE = 9, + DEFAULT_VIEWS = 5000, + DEFAULT_SIGNATURE_SIZE = 176, + COMPRESSION_NONE = 0, + COMPRESSION_RANDOM_PROJ = 1, + COMPRESSION_PCA = 2, + DEFAULT_COMPRESSION_METHOD = COMPRESSION_NONE + }; + +protected: + virtual void prepare(int _nclasses, int _patchSize, int _signatureSize, + int _nstructs, int _structSize, + int _nviews, int _compressionMethod); + virtual void finalize(RNG& rng); + virtual int getLeaf(int fidx, const Mat& patch) const; + + bool verbose; + int nstructs; + int structSize; + int nclasses; + int signatureSize; + int compressionMethod; + int leavesPerStruct; + Size patchSize; + vector features; + vector classCounters; + vector posteriors; +}; + + +/****************************************************************************************\ + * Calonder Classifier * + \****************************************************************************************/ + +struct RTreeNode; + +struct CV_EXPORTS BaseKeypoint +{ + int x; + int y; + IplImage* image; + + BaseKeypoint() + : x(0), y(0), image(NULL) + {} + + BaseKeypoint(int x, int y, IplImage* image) + : x(x), y(y), image(image) + {} +}; + +class CV_EXPORTS RandomizedTree +{ +public: + friend class RTreeClassifier; + + static const uchar PATCH_SIZE = 32; + static const int DEFAULT_DEPTH = 9; + static const int DEFAULT_VIEWS = 5000; + static const size_t DEFAULT_REDUCED_NUM_DIM = 176; + static float GET_LOWER_QUANT_PERC() { return .03f; } + static float GET_UPPER_QUANT_PERC() { return .92f; } + + RandomizedTree(); + ~RandomizedTree(); + + void train(vector const& base_set, RNG &rng, + int depth, int views, size_t reduced_num_dim, int num_quant_bits); + void train(vector const& base_set, RNG &rng, + PatchGenerator &make_patch, int depth, int views, size_t reduced_num_dim, + int num_quant_bits); + + // following two funcs are EXPERIMENTAL (do not use unless you know exactly what you do) + static void quantizeVector(float *vec, int dim, int N, float bnds[2], int clamp_mode=0); + static void quantizeVector(float *src, int dim, int N, float bnds[2], uchar *dst); + + // patch_data must be a 32x32 array (no row padding) + float* getPosterior(uchar* patch_data); + const float* getPosterior(uchar* patch_data) const; + uchar* getPosterior2(uchar* patch_data); + const uchar* getPosterior2(uchar* patch_data) const; + + void read(const char* file_name, int num_quant_bits); + void read(std::istream &is, int num_quant_bits); + void write(const char* file_name) const; + void write(std::ostream &os) const; + + int classes() { return classes_; } + int depth() { return depth_; } + + //void setKeepFloatPosteriors(bool b) { keep_float_posteriors_ = b; } + void discardFloatPosteriors() { freePosteriors(1); } + + inline void applyQuantization(int num_quant_bits) { makePosteriors2(num_quant_bits); } + + // debug + void savePosteriors(std::string url, bool append=false); + void savePosteriors2(std::string url, bool append=false); + +private: + int classes_; + int depth_; + int num_leaves_; + vector nodes_; + float **posteriors_; // 16-bytes aligned posteriors + uchar **posteriors2_; // 16-bytes aligned posteriors + vector leaf_counts_; + + void createNodes(int num_nodes, RNG &rng); + void allocPosteriorsAligned(int num_leaves, int num_classes); + void freePosteriors(int which); // which: 1=posteriors_, 2=posteriors2_, 3=both + void init(int classes, int depth, RNG &rng); + void addExample(int class_id, uchar* patch_data); + void finalize(size_t reduced_num_dim, int num_quant_bits); + int getIndex(uchar* patch_data) const; + inline float* getPosteriorByIndex(int index); + inline const float* getPosteriorByIndex(int index) const; + inline uchar* getPosteriorByIndex2(int index); + inline const uchar* getPosteriorByIndex2(int index) const; + //void makeRandomMeasMatrix(float *cs_phi, PHI_DISTR_TYPE dt, size_t reduced_num_dim); + void convertPosteriorsToChar(); + void makePosteriors2(int num_quant_bits); + void compressLeaves(size_t reduced_num_dim); + void estimateQuantPercForPosteriors(float perc[2]); +}; + + +inline uchar* getData(IplImage* image) +{ + return reinterpret_cast(image->imageData); +} + +inline float* RandomizedTree::getPosteriorByIndex(int index) +{ + return const_cast(const_cast(this)->getPosteriorByIndex(index)); +} + +inline const float* RandomizedTree::getPosteriorByIndex(int index) const +{ + return posteriors_[index]; +} + +inline uchar* RandomizedTree::getPosteriorByIndex2(int index) +{ + return const_cast(const_cast(this)->getPosteriorByIndex2(index)); +} + +inline const uchar* RandomizedTree::getPosteriorByIndex2(int index) const +{ + return posteriors2_[index]; +} + +struct CV_EXPORTS RTreeNode +{ + short offset1, offset2; + + RTreeNode() {} + RTreeNode(uchar x1, uchar y1, uchar x2, uchar y2) + : offset1(y1*RandomizedTree::PATCH_SIZE + x1), + offset2(y2*RandomizedTree::PATCH_SIZE + x2) + {} + + //! Left child on 0, right child on 1 + inline bool operator() (uchar* patch_data) const + { + return patch_data[offset1] > patch_data[offset2]; + } +}; + +class CV_EXPORTS RTreeClassifier +{ +public: + static const int DEFAULT_TREES = 48; + static const size_t DEFAULT_NUM_QUANT_BITS = 4; + + RTreeClassifier(); + void train(vector const& base_set, + RNG &rng, + int num_trees = RTreeClassifier::DEFAULT_TREES, + int depth = RandomizedTree::DEFAULT_DEPTH, + int views = RandomizedTree::DEFAULT_VIEWS, + size_t reduced_num_dim = RandomizedTree::DEFAULT_REDUCED_NUM_DIM, + int num_quant_bits = DEFAULT_NUM_QUANT_BITS); + void train(vector const& base_set, + RNG &rng, + PatchGenerator &make_patch, + int num_trees = RTreeClassifier::DEFAULT_TREES, + int depth = RandomizedTree::DEFAULT_DEPTH, + int views = RandomizedTree::DEFAULT_VIEWS, + size_t reduced_num_dim = RandomizedTree::DEFAULT_REDUCED_NUM_DIM, + int num_quant_bits = DEFAULT_NUM_QUANT_BITS); + + // sig must point to a memory block of at least classes()*sizeof(float|uchar) bytes + void getSignature(IplImage *patch, uchar *sig) const; + void getSignature(IplImage *patch, float *sig) const; + void getSparseSignature(IplImage *patch, float *sig, float thresh) const; + // TODO: deprecated in favor of getSignature overload, remove + void getFloatSignature(IplImage *patch, float *sig) const { getSignature(patch, sig); } + + static int countNonZeroElements(float *vec, int n, double tol=1e-10); + static inline void safeSignatureAlloc(uchar **sig, int num_sig=1, int sig_len=176); + static inline uchar* safeSignatureAlloc(int num_sig=1, int sig_len=176); + + inline int classes() const { return classes_; } + inline int original_num_classes() const { return original_num_classes_; } + + void setQuantization(int num_quant_bits); + void discardFloatPosteriors(); + + void read(const char* file_name); + void read(std::istream &is); + void write(const char* file_name) const; + void write(std::ostream &os) const; + + // experimental and debug + void saveAllFloatPosteriors(std::string file_url); + void saveAllBytePosteriors(std::string file_url); + void setFloatPosteriorsFromTextfile_176(std::string url); + float countZeroElements(); + + vector trees_; + +private: + int classes_; + int num_quant_bits_; + mutable uchar **posteriors_; + mutable unsigned short *ptemp_; + int original_num_classes_; + bool keep_floats_; +}; + +/****************************************************************************************\ +* One-Way Descriptor * +\****************************************************************************************/ + +// CvAffinePose: defines a parameterized affine transformation of an image patch. +// An image patch is rotated on angle phi (in degrees), then scaled lambda1 times +// along horizontal and lambda2 times along vertical direction, and then rotated again +// on angle (theta - phi). +class CV_EXPORTS CvAffinePose +{ +public: + float phi; + float theta; + float lambda1; + float lambda2; +}; + +class CV_EXPORTS OneWayDescriptor +{ +public: + OneWayDescriptor(); + ~OneWayDescriptor(); + + // allocates memory for given descriptor parameters + void Allocate(int pose_count, CvSize size, int nChannels); + + // GenerateSamples: generates affine transformed patches with averaging them over small transformation variations. + // If external poses and transforms were specified, uses them instead of generating random ones + // - pose_count: the number of poses to be generated + // - frontal: the input patch (can be a roi in a larger image) + // - norm: if nonzero, normalizes the output patch so that the sum of pixel intensities is 1 + void GenerateSamples(int pose_count, IplImage* frontal, int norm = 0); + + // GenerateSamplesFast: generates affine transformed patches with averaging them over small transformation variations. + // Uses precalculated transformed pca components. + // - frontal: the input patch (can be a roi in a larger image) + // - pca_hr_avg: pca average vector + // - pca_hr_eigenvectors: pca eigenvectors + // - pca_descriptors: an array of precomputed descriptors of pca components containing their affine transformations + // pca_descriptors[0] corresponds to the average, pca_descriptors[1]-pca_descriptors[pca_dim] correspond to eigenvectors + void GenerateSamplesFast(IplImage* frontal, CvMat* pca_hr_avg, + CvMat* pca_hr_eigenvectors, OneWayDescriptor* pca_descriptors); + + // sets the poses and corresponding transforms + void SetTransforms(CvAffinePose* poses, CvMat** transforms); + + // Initialize: builds a descriptor. + // - pose_count: the number of poses to build. If poses were set externally, uses them rather than generating random ones + // - frontal: input patch. Can be a roi in a larger image + // - feature_name: the feature name to be associated with the descriptor + // - norm: if 1, the affine transformed patches are normalized so that their sum is 1 + void Initialize(int pose_count, IplImage* frontal, const char* feature_name = 0, int norm = 0); + + // InitializeFast: builds a descriptor using precomputed descriptors of pca components + // - pose_count: the number of poses to build + // - frontal: input patch. Can be a roi in a larger image + // - feature_name: the feature name to be associated with the descriptor + // - pca_hr_avg: average vector for PCA + // - pca_hr_eigenvectors: PCA eigenvectors (one vector per row) + // - pca_descriptors: precomputed descriptors of PCA components, the first descriptor for the average vector + // followed by the descriptors for eigenvectors + void InitializeFast(int pose_count, IplImage* frontal, const char* feature_name, + CvMat* pca_hr_avg, CvMat* pca_hr_eigenvectors, OneWayDescriptor* pca_descriptors); + + // ProjectPCASample: unwarps an image patch into a vector and projects it into PCA space + // - patch: input image patch + // - avg: PCA average vector + // - eigenvectors: PCA eigenvectors, one per row + // - pca_coeffs: output PCA coefficients + void ProjectPCASample(IplImage* patch, CvMat* avg, CvMat* eigenvectors, CvMat* pca_coeffs) const; + + // InitializePCACoeffs: projects all warped patches into PCA space + // - avg: PCA average vector + // - eigenvectors: PCA eigenvectors, one per row + void InitializePCACoeffs(CvMat* avg, CvMat* eigenvectors); + + // EstimatePose: finds the closest match between an input patch and a set of patches with different poses + // - patch: input image patch + // - pose_idx: the output index of the closest pose + // - distance: the distance to the closest pose (L2 distance) + void EstimatePose(IplImage* patch, int& pose_idx, float& distance) const; + + // EstimatePosePCA: finds the closest match between an input patch and a set of patches with different poses. + // The distance between patches is computed in PCA space + // - patch: input image patch + // - pose_idx: the output index of the closest pose + // - distance: distance to the closest pose (L2 distance in PCA space) + // - avg: PCA average vector. If 0, matching without PCA is used + // - eigenvectors: PCA eigenvectors, one per row + void EstimatePosePCA(CvArr* patch, int& pose_idx, float& distance, CvMat* avg, CvMat* eigenvalues) const; + + // GetPatchSize: returns the size of each image patch after warping (2 times smaller than the input patch) + CvSize GetPatchSize() const + { + return m_patch_size; + } + + // GetInputPatchSize: returns the required size of the patch that the descriptor is built from + // (2 time larger than the patch after warping) + CvSize GetInputPatchSize() const + { + return cvSize(m_patch_size.width*2, m_patch_size.height*2); + } + + // GetPatch: returns a patch corresponding to specified pose index + // - index: pose index + // - return value: the patch corresponding to specified pose index + IplImage* GetPatch(int index); + + // GetPose: returns a pose corresponding to specified pose index + // - index: pose index + // - return value: the pose corresponding to specified pose index + CvAffinePose GetPose(int index) const; + + // Save: saves all patches with different poses to a specified path + void Save(const char* path); + + // ReadByName: reads a descriptor from a file storage + // - fs: file storage + // - parent: parent node + // - name: node name + // - return value: 1 if succeeded, 0 otherwise + int ReadByName(CvFileStorage* fs, CvFileNode* parent, const char* name); + + // ReadByName: reads a descriptor from a file node + // - parent: parent node + // - name: node name + // - return value: 1 if succeeded, 0 otherwise + int ReadByName(const FileNode &parent, const char* name); + + // Write: writes a descriptor into a file storage + // - fs: file storage + // - name: node name + void Write(CvFileStorage* fs, const char* name); + + // GetFeatureName: returns a name corresponding to a feature + const char* GetFeatureName() const; + + // GetCenter: returns the center of the feature + CvPoint GetCenter() const; + + void SetPCADimHigh(int pca_dim_high) {m_pca_dim_high = pca_dim_high;}; + void SetPCADimLow(int pca_dim_low) {m_pca_dim_low = pca_dim_low;}; + + int GetPCADimLow() const; + int GetPCADimHigh() const; + + CvMat** GetPCACoeffs() const {return m_pca_coeffs;} + +protected: + int m_pose_count; // the number of poses + CvSize m_patch_size; // size of each image + IplImage** m_samples; // an array of length m_pose_count containing the patch in different poses + IplImage* m_input_patch; + IplImage* m_train_patch; + CvMat** m_pca_coeffs; // an array of length m_pose_count containing pca decomposition of the patch in different poses + CvAffinePose* m_affine_poses; // an array of poses + CvMat** m_transforms; // an array of affine transforms corresponding to poses + + string m_feature_name; // the name of the feature associated with the descriptor + CvPoint m_center; // the coordinates of the feature (the center of the input image ROI) + + int m_pca_dim_high; // the number of descriptor pca components to use for generating affine poses + int m_pca_dim_low; // the number of pca components to use for comparison +}; + + +// OneWayDescriptorBase: encapsulates functionality for training/loading a set of one way descriptors +// and finding the nearest closest descriptor to an input feature +class CV_EXPORTS OneWayDescriptorBase +{ +public: + + // creates an instance of OneWayDescriptor from a set of training files + // - patch_size: size of the input (large) patch + // - pose_count: the number of poses to generate for each descriptor + // - train_path: path to training files + // - pca_config: the name of the file that contains PCA for small patches (2 times smaller + // than patch_size each dimension + // - pca_hr_config: the name of the file that contains PCA for large patches (of patch_size size) + // - pca_desc_config: the name of the file that contains descriptors of PCA components + OneWayDescriptorBase(CvSize patch_size, int pose_count, const char* train_path = 0, const char* pca_config = 0, + const char* pca_hr_config = 0, const char* pca_desc_config = 0, int pyr_levels = 1, + int pca_dim_high = 100, int pca_dim_low = 100); + + OneWayDescriptorBase(CvSize patch_size, int pose_count, const string &pca_filename, const string &train_path = string(), const string &images_list = string(), + float _scale_min = 0.7f, float _scale_max=1.5f, float _scale_step=1.2f, int pyr_levels = 1, + int pca_dim_high = 100, int pca_dim_low = 100); + + + virtual ~OneWayDescriptorBase(); + void clear (); + + + // Allocate: allocates memory for a given number of descriptors + void Allocate(int train_feature_count); + + // AllocatePCADescriptors: allocates memory for pca descriptors + void AllocatePCADescriptors(); + + // returns patch size + CvSize GetPatchSize() const {return m_patch_size;}; + // returns the number of poses for each descriptor + int GetPoseCount() const {return m_pose_count;}; + + // returns the number of pyramid levels + int GetPyrLevels() const {return m_pyr_levels;}; + + // returns the number of descriptors + int GetDescriptorCount() const {return m_train_feature_count;}; + + // CreateDescriptorsFromImage: creates descriptors for each of the input features + // - src: input image + // - features: input features + // - pyr_levels: the number of pyramid levels + void CreateDescriptorsFromImage(IplImage* src, const vector& features); + + // CreatePCADescriptors: generates descriptors for PCA components, needed for fast generation of feature descriptors + void CreatePCADescriptors(); + + // returns a feature descriptor by feature index + const OneWayDescriptor* GetDescriptor(int desc_idx) const {return &m_descriptors[desc_idx];}; + + // FindDescriptor: finds the closest descriptor + // - patch: input image patch + // - desc_idx: output index of the closest descriptor to the input patch + // - pose_idx: output index of the closest pose of the closest descriptor to the input patch + // - distance: distance from the input patch to the closest feature pose + // - _scales: scales of the input patch for each descriptor + // - scale_ranges: input scales variation (float[2]) + void FindDescriptor(IplImage* patch, int& desc_idx, int& pose_idx, float& distance, float* _scale = 0, float* scale_ranges = 0) const; + + // - patch: input image patch + // - n: number of the closest indexes + // - desc_idxs: output indexes of the closest descriptor to the input patch (n) + // - pose_idx: output indexes of the closest pose of the closest descriptor to the input patch (n) + // - distances: distance from the input patch to the closest feature pose (n) + // - _scales: scales of the input patch + // - scale_ranges: input scales variation (float[2]) + void FindDescriptor(IplImage* patch, int n, vector& desc_idxs, vector& pose_idxs, + vector& distances, vector& _scales, float* scale_ranges = 0) const; + + // FindDescriptor: finds the closest descriptor + // - src: input image + // - pt: center of the feature + // - desc_idx: output index of the closest descriptor to the input patch + // - pose_idx: output index of the closest pose of the closest descriptor to the input patch + // - distance: distance from the input patch to the closest feature pose + void FindDescriptor(IplImage* src, cv::Point2f pt, int& desc_idx, int& pose_idx, float& distance) const; + + // InitializePoses: generates random poses + void InitializePoses(); + + // InitializeTransformsFromPoses: generates 2x3 affine matrices from poses (initializes m_transforms) + void InitializeTransformsFromPoses(); + + // InitializePoseTransforms: subsequently calls InitializePoses and InitializeTransformsFromPoses + void InitializePoseTransforms(); + + // InitializeDescriptor: initializes a descriptor + // - desc_idx: descriptor index + // - train_image: image patch (ROI is supported) + // - feature_label: feature textual label + void InitializeDescriptor(int desc_idx, IplImage* train_image, const char* feature_label); + + void InitializeDescriptor(int desc_idx, IplImage* train_image, const KeyPoint& keypoint, const char* feature_label); + + // InitializeDescriptors: load features from an image and create descriptors for each of them + void InitializeDescriptors(IplImage* train_image, const vector& features, + const char* feature_label = "", int desc_start_idx = 0); + + // Write: writes this object to a file storage + // - fs: output filestorage + void Write (FileStorage &fs) const; + + // Read: reads OneWayDescriptorBase object from a file node + // - fn: input file node + void Read (const FileNode &fn); + + // LoadPCADescriptors: loads PCA descriptors from a file + // - filename: input filename + int LoadPCADescriptors(const char* filename); + + // LoadPCADescriptors: loads PCA descriptors from a file node + // - fn: input file node + int LoadPCADescriptors(const FileNode &fn); + + // SavePCADescriptors: saves PCA descriptors to a file + // - filename: output filename + void SavePCADescriptors(const char* filename); + + // SavePCADescriptors: saves PCA descriptors to a file storage + // - fs: output file storage + void SavePCADescriptors(CvFileStorage* fs) const; + + // GeneratePCA: calculate and save PCA components and descriptors + // - img_path: path to training PCA images directory + // - images_list: filename with filenames of training PCA images + void GeneratePCA(const char* img_path, const char* images_list, int pose_count=500); + + // SetPCAHigh: sets the high resolution pca matrices (copied to internal structures) + void SetPCAHigh(CvMat* avg, CvMat* eigenvectors); + + // SetPCALow: sets the low resolution pca matrices (copied to internal structures) + void SetPCALow(CvMat* avg, CvMat* eigenvectors); + + int GetLowPCA(CvMat** avg, CvMat** eigenvectors) + { + *avg = m_pca_avg; + *eigenvectors = m_pca_eigenvectors; + return m_pca_dim_low; + }; + + int GetPCADimLow() const {return m_pca_dim_low;}; + int GetPCADimHigh() const {return m_pca_dim_high;}; + + void ConvertDescriptorsArrayToTree(); // Converting pca_descriptors array to KD tree + + // GetPCAFilename: get default PCA filename + static string GetPCAFilename () { return "pca.yml"; } + + virtual bool empty() const { return m_train_feature_count <= 0 ? true : false; } + +protected: + CvSize m_patch_size; // patch size + int m_pose_count; // the number of poses for each descriptor + int m_train_feature_count; // the number of the training features + OneWayDescriptor* m_descriptors; // array of train feature descriptors + CvMat* m_pca_avg; // PCA average Vector for small patches + CvMat* m_pca_eigenvectors; // PCA eigenvectors for small patches + CvMat* m_pca_hr_avg; // PCA average Vector for large patches + CvMat* m_pca_hr_eigenvectors; // PCA eigenvectors for large patches + OneWayDescriptor* m_pca_descriptors; // an array of PCA descriptors + + cv::flann::Index* m_pca_descriptors_tree; + CvMat* m_pca_descriptors_matrix; + + CvAffinePose* m_poses; // array of poses + CvMat** m_transforms; // array of affine transformations corresponding to poses + + int m_pca_dim_high; + int m_pca_dim_low; + + int m_pyr_levels; + float scale_min; + float scale_max; + float scale_step; + + // SavePCAall: saves PCA components and descriptors to a file storage + // - fs: output file storage + void SavePCAall (FileStorage &fs) const; + + // LoadPCAall: loads PCA components and descriptors from a file node + // - fn: input file node + void LoadPCAall (const FileNode &fn); +}; + +class CV_EXPORTS OneWayDescriptorObject : public OneWayDescriptorBase +{ +public: + // creates an instance of OneWayDescriptorObject from a set of training files + // - patch_size: size of the input (large) patch + // - pose_count: the number of poses to generate for each descriptor + // - train_path: path to training files + // - pca_config: the name of the file that contains PCA for small patches (2 times smaller + // than patch_size each dimension + // - pca_hr_config: the name of the file that contains PCA for large patches (of patch_size size) + // - pca_desc_config: the name of the file that contains descriptors of PCA components + OneWayDescriptorObject(CvSize patch_size, int pose_count, const char* train_path, const char* pca_config, + const char* pca_hr_config = 0, const char* pca_desc_config = 0, int pyr_levels = 1); + + OneWayDescriptorObject(CvSize patch_size, int pose_count, const string &pca_filename, + const string &train_path = string (), const string &images_list = string (), + float _scale_min = 0.7f, float _scale_max=1.5f, float _scale_step=1.2f, int pyr_levels = 1); + + + virtual ~OneWayDescriptorObject(); + + // Allocate: allocates memory for a given number of features + // - train_feature_count: the total number of features + // - object_feature_count: the number of features extracted from the object + void Allocate(int train_feature_count, int object_feature_count); + + + void SetLabeledFeatures(const vector& features) {m_train_features = features;}; + vector& GetLabeledFeatures() {return m_train_features;}; + const vector& GetLabeledFeatures() const {return m_train_features;}; + vector _GetLabeledFeatures() const; + + // IsDescriptorObject: returns 1 if descriptor with specified index is positive, otherwise 0 + int IsDescriptorObject(int desc_idx) const; + + // MatchPointToPart: returns the part number of a feature if it matches one of the object parts, otherwise -1 + int MatchPointToPart(CvPoint pt) const; + + // GetDescriptorPart: returns the part number of the feature corresponding to a specified descriptor + // - desc_idx: descriptor index + int GetDescriptorPart(int desc_idx) const; + + + void InitializeObjectDescriptors(IplImage* train_image, const vector& features, + const char* feature_label, int desc_start_idx = 0, float scale = 1.0f, + int is_background = 0); + + // GetObjectFeatureCount: returns the number of object features + int GetObjectFeatureCount() const {return m_object_feature_count;}; + +protected: + int* m_part_id; // contains part id for each of object descriptors + vector m_train_features; // train features + int m_object_feature_count; // the number of the positive features + +}; + + +/* + * OneWayDescriptorMatcher + */ +class OneWayDescriptorMatcher; +typedef OneWayDescriptorMatcher OneWayDescriptorMatch; + +class CV_EXPORTS OneWayDescriptorMatcher : public GenericDescriptorMatcher +{ +public: + class CV_EXPORTS Params + { + public: + static const int POSE_COUNT = 500; + static const int PATCH_WIDTH = 24; + static const int PATCH_HEIGHT = 24; + static float GET_MIN_SCALE() { return 0.7f; } + static float GET_MAX_SCALE() { return 1.5f; } + static float GET_STEP_SCALE() { return 1.2f; } + + Params( int poseCount = POSE_COUNT, + Size patchSize = Size(PATCH_WIDTH, PATCH_HEIGHT), + string pcaFilename = string(), + string trainPath = string(), string trainImagesList = string(), + float minScale = GET_MIN_SCALE(), float maxScale = GET_MAX_SCALE(), + float stepScale = GET_STEP_SCALE() ); + + int poseCount; + Size patchSize; + string pcaFilename; + string trainPath; + string trainImagesList; + + float minScale, maxScale, stepScale; + }; + + OneWayDescriptorMatcher( const Params& params=Params() ); + virtual ~OneWayDescriptorMatcher(); + + void initialize( const Params& params, const Ptr& base=Ptr() ); + + // Clears keypoints storing in collection and OneWayDescriptorBase + virtual void clear(); + + virtual void train(); + + virtual bool isMaskSupported(); + + virtual void read( const FileNode &fn ); + virtual void write( FileStorage& fs ) const; + + virtual bool empty() const; + + virtual Ptr clone( bool emptyTrainData=false ) const; + +protected: + // Matches a set of keypoints from a single image of the training set. A rectangle with a center in a keypoint + // and size (patch_width/2*scale, patch_height/2*scale) is cropped from the source image for each + // keypoint. scale is iterated from DescriptorOneWayParams::min_scale to DescriptorOneWayParams::max_scale. + // The minimum distance to each training patch with all its affine poses is found over all scales. + // The class ID of a match is returned for each keypoint. The distance is calculated over PCA components + // loaded with DescriptorOneWay::Initialize, kd tree is used for finding minimum distances. + virtual void knnMatchImpl( const Mat& queryImage, vector& queryKeypoints, + vector >& matches, int k, + const vector& masks, bool compactResult ); + virtual void radiusMatchImpl( const Mat& queryImage, vector& queryKeypoints, + vector >& matches, float maxDistance, + const vector& masks, bool compactResult ); + + Ptr base; + Params params; + int prevTrainCount; +}; + +/* + * FernDescriptorMatcher + */ +class FernDescriptorMatcher; +typedef FernDescriptorMatcher FernDescriptorMatch; + +class CV_EXPORTS FernDescriptorMatcher : public GenericDescriptorMatcher +{ +public: + class CV_EXPORTS Params + { + public: + Params( int nclasses=0, + int patchSize=FernClassifier::PATCH_SIZE, + int signatureSize=FernClassifier::DEFAULT_SIGNATURE_SIZE, + int nstructs=FernClassifier::DEFAULT_STRUCTS, + int structSize=FernClassifier::DEFAULT_STRUCT_SIZE, + int nviews=FernClassifier::DEFAULT_VIEWS, + int compressionMethod=FernClassifier::COMPRESSION_NONE, + const PatchGenerator& patchGenerator=PatchGenerator() ); + + Params( const string& filename ); + + int nclasses; + int patchSize; + int signatureSize; + int nstructs; + int structSize; + int nviews; + int compressionMethod; + PatchGenerator patchGenerator; + + string filename; + }; + + FernDescriptorMatcher( const Params& params=Params() ); + virtual ~FernDescriptorMatcher(); + + virtual void clear(); + + virtual void train(); + + virtual bool isMaskSupported(); + + virtual void read( const FileNode &fn ); + virtual void write( FileStorage& fs ) const; + virtual bool empty() const; + + virtual Ptr clone( bool emptyTrainData=false ) const; + +protected: + virtual void knnMatchImpl( const Mat& queryImage, vector& queryKeypoints, + vector >& matches, int k, + const vector& masks, bool compactResult ); + virtual void radiusMatchImpl( const Mat& queryImage, vector& queryKeypoints, + vector >& matches, float maxDistance, + const vector& masks, bool compactResult ); + + void trainFernClassifier(); + void calcBestProbAndMatchIdx( const Mat& image, const Point2f& pt, + float& bestProb, int& bestMatchIdx, vector& signature ); + Ptr classifier; + Params params; + int prevTrainCount; +}; + + +/* + * CalonderDescriptorExtractor + */ +template +class CV_EXPORTS CalonderDescriptorExtractor : public DescriptorExtractor +{ +public: + CalonderDescriptorExtractor( const string& classifierFile ); + + virtual void read( const FileNode &fn ); + virtual void write( FileStorage &fs ) const; + + virtual int descriptorSize() const { return classifier_.classes(); } + virtual int descriptorType() const { return DataType::type; } + + virtual bool empty() const; + +protected: + virtual void computeImpl( const Mat& image, vector& keypoints, Mat& descriptors ) const; + + RTreeClassifier classifier_; + static const int BORDER_SIZE = 16; +}; + +template +CalonderDescriptorExtractor::CalonderDescriptorExtractor(const std::string& classifier_file) +{ + classifier_.read( classifier_file.c_str() ); +} + +template +void CalonderDescriptorExtractor::computeImpl( const Mat& image, + vector& keypoints, + Mat& descriptors) const +{ + // Cannot compute descriptors for keypoints on the image border. + KeyPointsFilter::runByImageBorder(keypoints, image.size(), BORDER_SIZE); + + /// @todo Check 16-byte aligned + descriptors.create(keypoints.size(), classifier_.classes(), cv::DataType::type); + + int patchSize = RandomizedTree::PATCH_SIZE; + int offset = patchSize / 2; + for (size_t i = 0; i < keypoints.size(); ++i) + { + cv::Point2f pt = keypoints[i].pt; + IplImage ipl = image( Rect((int)(pt.x - offset), (int)(pt.y - offset), patchSize, patchSize) ); + classifier_.getSignature( &ipl, descriptors.ptr(i)); + } +} + +template +void CalonderDescriptorExtractor::read( const FileNode& ) +{} + +template +void CalonderDescriptorExtractor::write( FileStorage& ) const +{} + +template +bool CalonderDescriptorExtractor::empty() const +{ + return classifier_.trees_.empty(); +} + +/****************************************************************************************\ +* Planar Object Detection * +\****************************************************************************************/ + +class CV_EXPORTS PlanarObjectDetector +{ +public: + PlanarObjectDetector(); + PlanarObjectDetector(const FileNode& node); + PlanarObjectDetector(const vector& pyr, int _npoints=300, + int _patchSize=FernClassifier::PATCH_SIZE, + int _nstructs=FernClassifier::DEFAULT_STRUCTS, + int _structSize=FernClassifier::DEFAULT_STRUCT_SIZE, + int _nviews=FernClassifier::DEFAULT_VIEWS, + const LDetector& detector=LDetector(), + const PatchGenerator& patchGenerator=PatchGenerator()); + virtual ~PlanarObjectDetector(); + virtual void train(const vector& pyr, int _npoints=300, + int _patchSize=FernClassifier::PATCH_SIZE, + int _nstructs=FernClassifier::DEFAULT_STRUCTS, + int _structSize=FernClassifier::DEFAULT_STRUCT_SIZE, + int _nviews=FernClassifier::DEFAULT_VIEWS, + const LDetector& detector=LDetector(), + const PatchGenerator& patchGenerator=PatchGenerator()); + virtual void train(const vector& pyr, const vector& keypoints, + int _patchSize=FernClassifier::PATCH_SIZE, + int _nstructs=FernClassifier::DEFAULT_STRUCTS, + int _structSize=FernClassifier::DEFAULT_STRUCT_SIZE, + int _nviews=FernClassifier::DEFAULT_VIEWS, + const LDetector& detector=LDetector(), + const PatchGenerator& patchGenerator=PatchGenerator()); + Rect getModelROI() const; + vector getModelPoints() const; + const LDetector& getDetector() const; + const FernClassifier& getClassifier() const; + void setVerbose(bool verbose); + + void read(const FileNode& node); + void write(FileStorage& fs, const String& name=String()) const; + bool operator()(const Mat& image, CV_OUT Mat& H, CV_OUT vector& corners) const; + bool operator()(const vector& pyr, const vector& keypoints, + CV_OUT Mat& H, CV_OUT vector& corners, + CV_OUT vector* pairs=0) const; + +protected: + bool verbose; + Rect modelROI; + vector modelPoints; + LDetector ldetector; + FernClassifier fernClassifier; +}; + +} + + //#include "cvvidsurv.hpp" #endif diff --git a/modules/features2d/src/calonder.cpp b/modules/legacy/src/calonder.cpp similarity index 100% rename from modules/features2d/src/calonder.cpp rename to modules/legacy/src/calonder.cpp diff --git a/modules/legacy/src/features2d.cpp b/modules/legacy/src/features2d.cpp new file mode 100644 index 0000000000..94097bd253 --- /dev/null +++ b/modules/legacy/src/features2d.cpp @@ -0,0 +1,126 @@ +/* Original code has been submitted by Liu Liu. Here is the copyright. +---------------------------------------------------------------------------------- + * An OpenCV Implementation of SURF + * Further Information Refer to "SURF: Speed-Up Robust Feature" + * Author: Liu Liu + * liuliu.1987+opencv@gmail.com + * + * There are still serveral lacks for this experimental implementation: + * 1.The interpolation of sub-pixel mentioned in article was not implemented yet; + * 2.A comparision with original libSurf.so shows that the hessian detector is not a 100% match to their implementation; + * 3.Due to above reasons, I recommanded the original one for study and reuse; + * + * However, the speed of this implementation is something comparable to original one. + * + * Copyright© 2008, Liu Liu All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * The name of Contributor 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 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. + */ + +#include "precomp.hpp" + +using namespace cv; + + + +CV_IMPL CvSURFParams cvSURFParams(double threshold, int extended) +{ + CvSURFParams params; + params.hessianThreshold = threshold; + params.extended = extended; + params.upright = 0; + params.nOctaves = 4; + params.nOctaveLayers = 2; + return params; +} + +CV_IMPL void +cvExtractSURF( const CvArr* _img, const CvArr* _mask, + CvSeq** _keypoints, CvSeq** _descriptors, + CvMemStorage* storage, CvSURFParams params, + int useProvidedKeyPts) +{ + Mat img = cvarrToMat(_img), mask; + if(_mask) + mask = cvarrToMat(_mask); + vector kpt; + Mat descr; + + Ptr surf = Algorithm::create("Feature2D.SURF"); + if( surf.empty() ) + CV_Error(CV_StsNotImplemented, "OpenCV was built without SURF support"); + + surf->set("hessianThreshold", params.hessianThreshold); + surf->set("nOctaves", params.nOctaves); + surf->set("nOctaveLayers", params.nOctaveLayers); + surf->set("upright", params.upright != 0); + surf->set("extended", params.extended != 0); + + surf->operator()(img, mask, kpt, _descriptors ? _OutputArray(descr) : noArray(), + useProvidedKeyPts != 0); + + if( _keypoints ) + *_keypoints = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvSURFPoint), storage); + + if( _descriptors ) + *_descriptors = cvCreateSeq( 0, sizeof(CvSeq), descr.cols*descr.elemSize(), storage ); + + for( size_t i = 0; i < kpt.size(); i++ ) + { + if( _keypoints ) + { + CvSURFPoint pt = cvSURFPoint(kpt[i].pt, kpt[i].class_id, cvRound(kpt[i].size)); + cvSeqPush(*_keypoints, &pt); + } + if( _descriptors ) + cvSeqPush(*_descriptors, descr.ptr(i)); + } +} + +CV_IMPL CvSeq* +cvGetStarKeypoints( const CvArr* _img, CvMemStorage* storage, + CvStarDetectorParams params ) +{ + Ptr star = new StarDetector(params.maxSize, params.responseThreshold, + params.lineThresholdProjected, + params.lineThresholdBinarized, + params.suppressNonmaxSize); + vector kpts; + star->detect(cvarrToMat(_img), kpts, Mat()); + + CvSeq* seq = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvStarKeypoint), storage); + for( size_t i = 0; i < kpts.size(); i++ ) + { + CvStarKeypoint kpt = cvStarKeypoint(kpts[i].pt, cvRound(kpts[i].size), kpts[i].response); + cvSeqPush(seq, &kpt); + } + return seq; +} + + diff --git a/modules/features2d/src/oneway.cpp b/modules/legacy/src/oneway.cpp similarity index 92% rename from modules/features2d/src/oneway.cpp rename to modules/legacy/src/oneway.cpp index 5af607c6cc..737cecfdde 100644 --- a/modules/features2d/src/oneway.cpp +++ b/modules/legacy/src/oneway.cpp @@ -8,13 +8,9 @@ */ #include "precomp.hpp" -#include "opencv2/opencv_modules.hpp" +#include "opencv2/highgui/highgui.hpp" #include -#ifdef HAVE_OPENCV_HIGHGUI -# include "opencv2/highgui/highgui.hpp" -#endif - namespace cv{ inline int round(float value) @@ -659,7 +655,6 @@ namespace cv{ void OneWayDescriptor::Save(const char* path) { -#ifdef HAVE_OPENCV_HIGHGUI for(int i = 0; i < m_pose_count; i++) { char buf[1024]; @@ -674,9 +669,6 @@ namespace cv{ cvReleaseImage(&patch); } -#else - CV_Error( CV_StsNotImplemented, "This method required opencv_highgui disabled in current build" ); -#endif } void OneWayDescriptor::Write(CvFileStorage* fs, const char* name) @@ -1737,9 +1729,12 @@ namespace cv{ void extractPatches (IplImage *img, vector& patches, CvSize patch_size) { vector features; - SURF surf_extractor(1.0f); + Ptr surf_extractor = FeatureDetector::create("SURF"); + if( surf_extractor.empty() ) + CV_Error(CV_StsNotImplemented, "OpenCV was built without SURF support"); + surf_extractor->set("hessianThreshold", 1.0); //printf("Extracting SURF features..."); - surf_extractor(img, Mat(), features); + surf_extractor->detect(Mat(img), features); //printf("done\n"); for (int j = 0; j < (int)features.size(); j++) @@ -1780,7 +1775,6 @@ namespace cv{ void loadPCAFeatures(const char* path, const char* images_list, vector& patches, CvSize patch_size) { -#ifdef HAVE_OPENCV_HIGHGUI char images_filename[1024]; sprintf(images_filename, "%s/%s", path, images_list); FILE *pFile = fopen(images_filename, "r"); @@ -1809,9 +1803,6 @@ namespace cv{ cvReleaseImage(&img); } fclose(pFile); -#else - CV_Error( CV_StsNotImplemented, "This method required opencv_highgui disabled in current build" ); -#endif } void generatePCAFeatures(const char* path, const char* img_filename, FileStorage& fs, const char* postfix, @@ -2159,4 +2150,145 @@ namespace cv{ cvReleaseMat(&_eigenvectors); } } + + /****************************************************************************************\ + * OneWayDescriptorMatcher * + \****************************************************************************************/ + + OneWayDescriptorMatcher::Params::Params( int _poseCount, Size _patchSize, string _pcaFilename, + string _trainPath, string _trainImagesList, + float _minScale, float _maxScale, float _stepScale ) : + poseCount(_poseCount), patchSize(_patchSize), pcaFilename(_pcaFilename), + trainPath(_trainPath), trainImagesList(_trainImagesList), + minScale(_minScale), maxScale(_maxScale), stepScale(_stepScale) + {} + + + OneWayDescriptorMatcher::OneWayDescriptorMatcher( const Params& _params) + { + initialize(_params); + } + + OneWayDescriptorMatcher::~OneWayDescriptorMatcher() + {} + + void OneWayDescriptorMatcher::initialize( const Params& _params, const Ptr& _base ) + { + clear(); + + if( _base.empty() ) + base = _base; + + params = _params; + } + + void OneWayDescriptorMatcher::clear() + { + GenericDescriptorMatcher::clear(); + + prevTrainCount = 0; + if( !base.empty() ) + base->clear(); + } + + void OneWayDescriptorMatcher::train() + { + if( base.empty() || prevTrainCount < (int)trainPointCollection.keypointCount() ) + { + base = new OneWayDescriptorObject( params.patchSize, params.poseCount, params.pcaFilename, + params.trainPath, params.trainImagesList, params.minScale, params.maxScale, params.stepScale ); + + base->Allocate( (int)trainPointCollection.keypointCount() ); + prevTrainCount = (int)trainPointCollection.keypointCount(); + + const vector >& points = trainPointCollection.getKeypoints(); + int count = 0; + for( size_t i = 0; i < points.size(); i++ ) + { + IplImage _image = trainPointCollection.getImage((int)i); + for( size_t j = 0; j < points[i].size(); j++ ) + base->InitializeDescriptor( count++, &_image, points[i][j], "" ); + } + +#if defined(_KDTREE) + base->ConvertDescriptorsArrayToTree(); +#endif + } + } + + bool OneWayDescriptorMatcher::isMaskSupported() + { + return false; + } + + void OneWayDescriptorMatcher::knnMatchImpl( const Mat& queryImage, vector& queryKeypoints, + vector >& matches, int knn, + const vector& /*masks*/, bool /*compactResult*/ ) + { + train(); + + CV_Assert( knn == 1 ); // knn > 1 unsupported because of bug in OneWayDescriptorBase for this case + + matches.resize( queryKeypoints.size() ); + IplImage _qimage = queryImage; + for( size_t i = 0; i < queryKeypoints.size(); i++ ) + { + int descIdx = -1, poseIdx = -1; + float distance; + base->FindDescriptor( &_qimage, queryKeypoints[i].pt, descIdx, poseIdx, distance ); + matches[i].push_back( DMatch((int)i, descIdx, distance) ); + } + } + + void OneWayDescriptorMatcher::radiusMatchImpl( const Mat& queryImage, vector& queryKeypoints, + vector >& matches, float maxDistance, + const vector& /*masks*/, bool /*compactResult*/ ) + { + train(); + + matches.resize( queryKeypoints.size() ); + IplImage _qimage = queryImage; + for( size_t i = 0; i < queryKeypoints.size(); i++ ) + { + int descIdx = -1, poseIdx = -1; + float distance; + base->FindDescriptor( &_qimage, queryKeypoints[i].pt, descIdx, poseIdx, distance ); + if( distance < maxDistance ) + matches[i].push_back( DMatch((int)i, descIdx, distance) ); + } + } + + void OneWayDescriptorMatcher::read( const FileNode &fn ) + { + base = new OneWayDescriptorObject( params.patchSize, params.poseCount, string (), string (), string (), + params.minScale, params.maxScale, params.stepScale ); + base->Read (fn); + } + + void OneWayDescriptorMatcher::write( FileStorage& fs ) const + { + base->Write (fs); + } + + bool OneWayDescriptorMatcher::empty() const + { + return base.empty() || base->empty(); + } + + Ptr OneWayDescriptorMatcher::clone( bool emptyTrainData ) const + { + OneWayDescriptorMatcher* matcher = new OneWayDescriptorMatcher( params ); + + if( !emptyTrainData ) + { + CV_Error( CV_StsNotImplemented, "deep clone functionality is not implemented, because " + "OneWayDescriptorBase has not copy constructor or clone method "); + + //matcher->base; + matcher->params = params; + matcher->prevTrainCount = prevTrainCount; + matcher->trainPointCollection = trainPointCollection; + } + return matcher; + } } diff --git a/modules/features2d/src/planardetect.cpp b/modules/legacy/src/planardetect.cpp similarity index 77% rename from modules/features2d/src/planardetect.cpp rename to modules/legacy/src/planardetect.cpp index 9359d8f58f..22a0285446 100644 --- a/modules/features2d/src/planardetect.cpp +++ b/modules/legacy/src/planardetect.cpp @@ -1215,5 +1215,363 @@ void FernClassifier::setVerbose(bool _verbose) { verbose = _verbose; } + + +/****************************************************************************************\ +* FernDescriptorMatcher * +\****************************************************************************************/ +FernDescriptorMatcher::Params::Params( int _nclasses, int _patchSize, int _signatureSize, + int _nstructs, int _structSize, int _nviews, int _compressionMethod, + const PatchGenerator& _patchGenerator ) : +nclasses(_nclasses), patchSize(_patchSize), signatureSize(_signatureSize), +nstructs(_nstructs), structSize(_structSize), nviews(_nviews), +compressionMethod(_compressionMethod), patchGenerator(_patchGenerator) +{} + +FernDescriptorMatcher::Params::Params( const string& _filename ) +{ + filename = _filename; +} + +FernDescriptorMatcher::FernDescriptorMatcher( const Params& _params ) +{ + prevTrainCount = 0; + params = _params; + if( !params.filename.empty() ) + { + classifier = new FernClassifier; + FileStorage fs(params.filename, FileStorage::READ); + if( fs.isOpened() ) + classifier->read( fs.getFirstTopLevelNode() ); + } +} + +FernDescriptorMatcher::~FernDescriptorMatcher() +{} + +void FernDescriptorMatcher::clear() +{ + GenericDescriptorMatcher::clear(); + + classifier.release(); + prevTrainCount = 0; +} + +void FernDescriptorMatcher::train() +{ + if( classifier.empty() || prevTrainCount < (int)trainPointCollection.keypointCount() ) + { + assert( params.filename.empty() ); + + vector > points( trainPointCollection.imageCount() ); + for( size_t imgIdx = 0; imgIdx < trainPointCollection.imageCount(); imgIdx++ ) + KeyPoint::convert( trainPointCollection.getKeypoints((int)imgIdx), points[imgIdx] ); + + classifier = new FernClassifier( points, trainPointCollection.getImages(), vector >(), 0, // each points is a class + params.patchSize, params.signatureSize, params.nstructs, params.structSize, + params.nviews, params.compressionMethod, params.patchGenerator ); + } +} + +bool FernDescriptorMatcher::isMaskSupported() +{ + return false; +} + +void FernDescriptorMatcher::calcBestProbAndMatchIdx( const Mat& image, const Point2f& pt, + float& bestProb, int& bestMatchIdx, vector& signature ) +{ + (*classifier)( image, pt, signature); + + bestProb = -FLT_MAX; + bestMatchIdx = -1; + for( int ci = 0; ci < classifier->getClassCount(); ci++ ) + { + if( signature[ci] > bestProb ) + { + bestProb = signature[ci]; + bestMatchIdx = ci; + } + } +} + +void FernDescriptorMatcher::knnMatchImpl( const Mat& queryImage, vector& queryKeypoints, + vector >& matches, int knn, + const vector& /*masks*/, bool /*compactResult*/ ) +{ + train(); + + matches.resize( queryKeypoints.size() ); + vector signature( (size_t)classifier->getClassCount() ); + + for( size_t queryIdx = 0; queryIdx < queryKeypoints.size(); queryIdx++ ) + { + (*classifier)( queryImage, queryKeypoints[queryIdx].pt, signature); + + for( int k = 0; k < knn; k++ ) + { + DMatch bestMatch; + size_t best_ci = 0; + for( size_t ci = 0; ci < signature.size(); ci++ ) + { + if( -signature[ci] < bestMatch.distance ) + { + int imgIdx = -1, trainIdx = -1; + trainPointCollection.getLocalIdx( (int)ci , imgIdx, trainIdx ); + bestMatch = DMatch( (int)queryIdx, trainIdx, imgIdx, -signature[ci] ); + best_ci = ci; + } + } + + if( bestMatch.trainIdx == -1 ) + break; + signature[best_ci] = -std::numeric_limits::max(); + matches[queryIdx].push_back( bestMatch ); + } + } +} + +void FernDescriptorMatcher::radiusMatchImpl( const Mat& queryImage, vector& queryKeypoints, + vector >& matches, float maxDistance, + const vector& /*masks*/, bool /*compactResult*/ ) +{ + train(); + matches.resize( queryKeypoints.size() ); + vector signature( (size_t)classifier->getClassCount() ); + + for( size_t i = 0; i < queryKeypoints.size(); i++ ) + { + (*classifier)( queryImage, queryKeypoints[i].pt, signature); + + for( int ci = 0; ci < classifier->getClassCount(); ci++ ) + { + if( -signature[ci] < maxDistance ) + { + int imgIdx = -1, trainIdx = -1; + trainPointCollection.getLocalIdx( ci , imgIdx, trainIdx ); + matches[i].push_back( DMatch( (int)i, trainIdx, imgIdx, -signature[ci] ) ); + } + } + } +} + +void FernDescriptorMatcher::read( const FileNode &fn ) +{ + params.nclasses = fn["nclasses"]; + params.patchSize = fn["patchSize"]; + params.signatureSize = fn["signatureSize"]; + params.nstructs = fn["nstructs"]; + params.structSize = fn["structSize"]; + params.nviews = fn["nviews"]; + params.compressionMethod = fn["compressionMethod"]; + + //classifier->read(fn); +} + +void FernDescriptorMatcher::write( FileStorage& fs ) const +{ + fs << "nclasses" << params.nclasses; + fs << "patchSize" << params.patchSize; + fs << "signatureSize" << params.signatureSize; + fs << "nstructs" << params.nstructs; + fs << "structSize" << params.structSize; + fs << "nviews" << params.nviews; + fs << "compressionMethod" << params.compressionMethod; + + // classifier->write(fs); +} + +bool FernDescriptorMatcher::empty() const +{ + return classifier.empty() || classifier->empty(); +} + +Ptr FernDescriptorMatcher::clone( bool emptyTrainData ) const +{ + FernDescriptorMatcher* matcher = new FernDescriptorMatcher( params ); + if( !emptyTrainData ) + { + CV_Error( CV_StsNotImplemented, "deep clone dunctionality is not implemented, because " + "FernClassifier has not copy constructor or clone method "); + + //matcher->classifier; + matcher->params = params; + matcher->prevTrainCount = prevTrainCount; + matcher->trainPointCollection = trainPointCollection; + } + return matcher; +} + +////////////////////////////////////// Planar Object Detector //////////////////////////////////// + +PlanarObjectDetector::PlanarObjectDetector() +{ +} + +PlanarObjectDetector::PlanarObjectDetector(const FileNode& node) +{ + read(node); +} + +PlanarObjectDetector::PlanarObjectDetector(const vector& pyr, int npoints, + int patchSize, int nstructs, int structSize, + int nviews, const LDetector& detector, + const PatchGenerator& patchGenerator) +{ + train(pyr, npoints, patchSize, nstructs, + structSize, nviews, detector, patchGenerator); +} + +PlanarObjectDetector::~PlanarObjectDetector() +{ +} + +vector PlanarObjectDetector::getModelPoints() const +{ + return modelPoints; +} + +void PlanarObjectDetector::train(const vector& pyr, int npoints, + int patchSize, int nstructs, int structSize, + int nviews, const LDetector& detector, + const PatchGenerator& patchGenerator) +{ + modelROI = Rect(0, 0, pyr[0].cols, pyr[0].rows); + ldetector = detector; + ldetector.setVerbose(verbose); + ldetector.getMostStable2D(pyr[0], modelPoints, npoints, patchGenerator); + + npoints = (int)modelPoints.size(); + fernClassifier.setVerbose(verbose); + fernClassifier.trainFromSingleView(pyr[0], modelPoints, + patchSize, (int)modelPoints.size(), nstructs, structSize, nviews, + FernClassifier::COMPRESSION_NONE, patchGenerator); +} + +void PlanarObjectDetector::train(const vector& pyr, const vector& keypoints, + int patchSize, int nstructs, int structSize, + int nviews, const LDetector& detector, + const PatchGenerator& patchGenerator) +{ + modelROI = Rect(0, 0, pyr[0].cols, pyr[0].rows); + ldetector = detector; + ldetector.setVerbose(verbose); + modelPoints.resize(keypoints.size()); + std::copy(keypoints.begin(), keypoints.end(), modelPoints.begin()); + + fernClassifier.setVerbose(verbose); + fernClassifier.trainFromSingleView(pyr[0], modelPoints, + patchSize, (int)modelPoints.size(), nstructs, structSize, nviews, + FernClassifier::COMPRESSION_NONE, patchGenerator); +} + +void PlanarObjectDetector::read(const FileNode& node) +{ + FileNodeIterator it = node["model-roi"].begin(), it_end; + it >> modelROI.x >> modelROI.y >> modelROI.width >> modelROI.height; + ldetector.read(node["detector"]); + fernClassifier.read(node["fern-classifier"]); + cv::read(node["model-points"], modelPoints); + CV_Assert(modelPoints.size() == (size_t)fernClassifier.getClassCount()); +} + + +void PlanarObjectDetector::write(FileStorage& fs, const String& objname) const +{ + WriteStructContext ws(fs, objname, CV_NODE_MAP); + + { + WriteStructContext wsroi(fs, "model-roi", CV_NODE_SEQ + CV_NODE_FLOW); + cv::write(fs, modelROI.x); + cv::write(fs, modelROI.y); + cv::write(fs, modelROI.width); + cv::write(fs, modelROI.height); + } + ldetector.write(fs, "detector"); + cv::write(fs, "model-points", modelPoints); + fernClassifier.write(fs, "fern-classifier"); +} + + +bool PlanarObjectDetector::operator()(const Mat& image, Mat& H, vector& corners) const +{ + vector pyr; + buildPyramid(image, pyr, ldetector.nOctaves - 1); + vector keypoints; + ldetector(pyr, keypoints); + + return (*this)(pyr, keypoints, H, corners); +} + +bool PlanarObjectDetector::operator()(const vector& pyr, const vector& keypoints, + Mat& matH, vector& corners, vector* pairs) const +{ + int i, j, m = (int)modelPoints.size(), n = (int)keypoints.size(); + vector bestMatches(m, -1); + vector maxLogProb(m, -FLT_MAX); + vector signature; + vector fromPt, toPt; + + for( i = 0; i < n; i++ ) + { + KeyPoint kpt = keypoints[i]; + CV_Assert(0 <= kpt.octave && kpt.octave < (int)pyr.size()); + kpt.pt.x /= (float)(1 << kpt.octave); + kpt.pt.y /= (float)(1 << kpt.octave); + int k = fernClassifier(pyr[kpt.octave], kpt.pt, signature); + if( k >= 0 && (bestMatches[k] < 0 || signature[k] > maxLogProb[k]) ) + { + maxLogProb[k] = signature[k]; + bestMatches[k] = i; + } + } + + if(pairs) + pairs->resize(0); + + for( i = 0; i < m; i++ ) + if( bestMatches[i] >= 0 ) + { + fromPt.push_back(modelPoints[i].pt); + toPt.push_back(keypoints[bestMatches[i]].pt); + } + + if( fromPt.size() < 4 ) + return false; + + vector mask; + matH = findHomography(fromPt, toPt, RANSAC, 10, mask); + if( matH.data ) + { + const Mat_& H = matH; + corners.resize(4); + for( i = 0; i < 4; i++ ) + { + Point2f pt((float)(modelROI.x + (i == 0 || i == 3 ? 0 : modelROI.width)), + (float)(modelROI.y + (i <= 1 ? 0 : modelROI.height))); + double w = 1./(H(2,0)*pt.x + H(2,1)*pt.y + H(2,2)); + corners[i] = Point2f((float)((H(0,0)*pt.x + H(0,1)*pt.y + H(0,2))*w), + (float)((H(1,0)*pt.x + H(1,1)*pt.y + H(1,2))*w)); + } + } + + if( pairs ) + { + for( i = j = 0; i < m; i++ ) + if( bestMatches[i] >= 0 && mask[j++] ) + { + pairs->push_back(i); + pairs->push_back(bestMatches[i]); + } + } + + return matH.data != 0; +} + + +void PlanarObjectDetector::setVerbose(bool _verbose) +{ + verbose = _verbose; +} } diff --git a/modules/legacy/test/test_main.cpp b/modules/legacy/test/test_main.cpp new file mode 100644 index 0000000000..6b24993447 --- /dev/null +++ b/modules/legacy/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("cv") diff --git a/modules/legacy/test/test_precomp.cpp b/modules/legacy/test/test_precomp.cpp new file mode 100644 index 0000000000..5956e13e3e --- /dev/null +++ b/modules/legacy/test/test_precomp.cpp @@ -0,0 +1 @@ +#include "test_precomp.hpp" diff --git a/modules/legacy/test/test_precomp.hpp b/modules/legacy/test/test_precomp.hpp new file mode 100644 index 0000000000..f28d167d0b --- /dev/null +++ b/modules/legacy/test/test_precomp.hpp @@ -0,0 +1,11 @@ +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/highgui/highgui.hpp" +#include "opencv2/highgui/highgui_c.h" +#include + +#endif diff --git a/modules/nonfree/CMakeLists.txt b/modules/nonfree/CMakeLists.txt new file mode 100644 index 0000000000..873532c3fb --- /dev/null +++ b/modules/nonfree/CMakeLists.txt @@ -0,0 +1,2 @@ +set(the_description "Functionality with possible limitations on the use") +ocv_define_module(nonfree opencv_imgproc opencv_features2d) diff --git a/modules/nonfree/doc/feature_detection.rst b/modules/nonfree/doc/feature_detection.rst new file mode 100644 index 0000000000..c3ab805e1c --- /dev/null +++ b/modules/nonfree/doc/feature_detection.rst @@ -0,0 +1,172 @@ +Feature Detection and Description +================================= + +SIFT +---- +.. ocv:class:: SIFT + +Class for extracting keypoints and computing descriptors using the Scale Invariant Feature Transform (SIFT) approach. :: + + class CV_EXPORTS SIFT + { + public: + struct CommonParams + { + static const int DEFAULT_NOCTAVES = 4; + static const int DEFAULT_NOCTAVE_LAYERS = 3; + static const int DEFAULT_FIRST_OCTAVE = -1; + enum{ FIRST_ANGLE = 0, AVERAGE_ANGLE = 1 }; + + CommonParams(); + CommonParams( int _nOctaves, int _nOctaveLayers, int _firstOctave, + int _angleMode ); + int nOctaves, nOctaveLayers, firstOctave; + int angleMode; + }; + + struct DetectorParams + { + static double GET_DEFAULT_THRESHOLD() + { return 0.04 / SIFT::CommonParams::DEFAULT_NOCTAVE_LAYERS / 2.0; } + static double GET_DEFAULT_EDGE_THRESHOLD() { return 10.0; } + + DetectorParams(); + DetectorParams( double _threshold, double _edgeThreshold ); + double threshold, edgeThreshold; + }; + + struct DescriptorParams + { + static double GET_DEFAULT_MAGNIFICATION() { return 3.0; } + static const bool DEFAULT_IS_NORMALIZE = true; + static const int DESCRIPTOR_SIZE = 128; + + DescriptorParams(); + DescriptorParams( double _magnification, bool _isNormalize, + bool _recalculateAngles ); + double magnification; + bool isNormalize; + bool recalculateAngles; + }; + + SIFT(); + //! sift-detector constructor + SIFT( double _threshold, double _edgeThreshold, + int _nOctaves=CommonParams::DEFAULT_NOCTAVES, + int _nOctaveLayers=CommonParams::DEFAULT_NOCTAVE_LAYERS, + int _firstOctave=CommonParams::DEFAULT_FIRST_OCTAVE, + int _angleMode=CommonParams::FIRST_ANGLE ); + //! sift-descriptor constructor + SIFT( double _magnification, bool _isNormalize=true, + bool _recalculateAngles = true, + int _nOctaves=CommonParams::DEFAULT_NOCTAVES, + int _nOctaveLayers=CommonParams::DEFAULT_NOCTAVE_LAYERS, + int _firstOctave=CommonParams::DEFAULT_FIRST_OCTAVE, + int _angleMode=CommonParams::FIRST_ANGLE ); + SIFT( const CommonParams& _commParams, + const DetectorParams& _detectorParams = DetectorParams(), + const DescriptorParams& _descriptorParams = DescriptorParams() ); + + //! returns the descriptor size in floats (128) + int descriptorSize() const { return DescriptorParams::DESCRIPTOR_SIZE; } + //! finds the keypoints using the SIFT algorithm + void operator()(const Mat& img, const Mat& mask, + vector& keypoints) const; + //! finds the keypoints and computes descriptors for them using SIFT algorithm. + //! Optionally it can compute descriptors for the user-provided keypoints + void operator()(const Mat& img, const Mat& mask, + vector& keypoints, + Mat& descriptors, + bool useProvidedKeypoints=false) const; + + CommonParams getCommonParams () const { return commParams; } + DetectorParams getDetectorParams () const { return detectorParams; } + DescriptorParams getDescriptorParams () const { return descriptorParams; } + protected: + ... + }; + + + + +SURF +---- +.. ocv:class:: SURF + +Class for extracting Speeded Up Robust Features from an image [Bay06]_. The class is derived from ``CvSURFParams`` structure, which specifies the algorithm parameters: + + .. ocv:member:: int extended + + * 0 means that the basic descriptors (64 elements each) shall be computed + * 1 means that the extended descriptors (128 elements each) shall be computed + + .. ocv:member:: int upright + + * 0 means that detector computes orientation of each feature. + * 1 means that the orientation is not computed (which is much, much faster). For example, if you match images from a stereo pair, or do image stitching, the matched features likely have very similar angles, and you can speed up feature extraction by setting ``upright=1``. + + .. ocv:member:: double hessianThreshold + + Threshold for the keypoint detector. Only features, whose hessian is larger than ``hessianThreshold`` are retained by the detector. Therefore, the larger the value, the less keypoints you will get. A good default value could be from 300 to 500, depending from the image contrast. + + .. ocv:member:: int nOctaves + + The number of a gaussian pyramid octaves that the detector uses. It is set to 4 by default. If you want to get very large features, use the larger value. If you want just small features, decrease it. + + .. ocv:member:: int nOctaveLayers + + The number of images within each octave of a gaussian pyramid. It is set to 2 by default. + + +.. [Bay06] Bay, H. and Tuytelaars, T. and Van Gool, L. "SURF: Speeded Up Robust Features", 9th European Conference on Computer Vision, 2006 + + +SURF::SURF +---------- +The SURF extractor constructors. + +.. ocv:function:: SURF::SURF() + +.. ocv:function:: SURF::SURF(double hessianThreshold, int nOctaves=4, int nOctaveLayers=2, bool extended=false, bool upright=false) + +.. ocv:pyfunction:: cv2.SURF(_hessianThreshold[, _nOctaves[, _nOctaveLayers[, _extended[, _upright]]]]) -> + + :param hessianThreshold: Threshold for hessian keypoint detector used in SURF. + + :param nOctaves: Number of pyramid octaves the keypoint detector will use. + + :param nOctaveLayers: Number of octave layers within each octave. + + :param extended: Extended descriptor flag (true - use extended 128-element descriptors; false - use 64-element descriptors). + + :param upright: Up-right or rotated features flag (true - do not compute orientation of features; false - compute orientation). + + +SURF::operator() +---------------- +Detects keypoints and computes SURF descriptors for them. + +.. ocv:function:: void SURF::operator()(const Mat& image, const Mat& mask, vector& keypoints) +.. ocv:function:: void SURF::operator()(const Mat& image, const Mat& mask, vector& keypoints, vector& descriptors, bool useProvidedKeypoints=false) + +.. ocv:pyfunction:: cv2.SURF.detect(img, mask) -> keypoints +.. ocv:pyfunction:: cv2.SURF.detect(img, mask[, useProvidedKeypoints]) -> keypoints, descriptors + +.. ocv:cfunction:: void cvExtractSURF( const CvArr* image, const CvArr* mask, CvSeq** keypoints, CvSeq** descriptors, CvMemStorage* storage, CvSURFParams params ) + +.. ocv:pyoldfunction:: cv.ExtractSURF(image, mask, storage, params)-> (keypoints, descriptors) + + :param image: Input 8-bit grayscale image + + :param mask: Optional input mask that marks the regions where we should detect features. + + :param keypoints: The input/output vector of keypoints + + :param descriptors: The output concatenated vectors of descriptors. Each descriptor is 64- or 128-element vector, as returned by ``SURF::descriptorSize()``. So the total size of ``descriptors`` will be ``keypoints.size()*descriptorSize()``. + + :param useProvidedKeypoints: Boolean flag. If it is true, the keypoint detector is not run. Instead, the provided vector of keypoints is used and the algorithm just computes their descriptors. + + :param storage: Memory storage for the output keypoints and descriptors in OpenCV 1.x API. + + :param params: SURF algorithm parameters in OpenCV 1.x API. + diff --git a/modules/nonfree/doc/nonfree.rst b/modules/nonfree/doc/nonfree.rst new file mode 100644 index 0000000000..e524ea82f8 --- /dev/null +++ b/modules/nonfree/doc/nonfree.rst @@ -0,0 +1,10 @@ +******************************** +nonfree. Non-free functionality +******************************** + +The module contains algorithms that may be patented in some countries or have some other limitations on the use. + +.. toctree:: + :maxdepth: 2 + + feature_detection diff --git a/modules/nonfree/include/opencv2/nonfree/features2d.hpp b/modules/nonfree/include/opencv2/nonfree/features2d.hpp new file mode 100644 index 0000000000..c283f11d21 --- /dev/null +++ b/modules/nonfree/include/opencv2/nonfree/features2d.hpp @@ -0,0 +1,155 @@ +/*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_NONFREE_FEATURES_2D_HPP__ +#define __OPENCV_NONFREE_FEATURES_2D_HPP__ + +#include "opencv2/features2d/features2d.hpp" + +#ifdef __cplusplus + +namespace cv +{ + +/*! + SIFT implementation. + + The class implements SIFT algorithm by D. Lowe. +*/ +class CV_EXPORTS_W SIFT : public Feature2D +{ +public: + explicit SIFT( int _nfeatures=0, int _nOctaveLayers=3, + double _contrastThreshold=0.04, double _edgeThreshold=10, + double _sigma=1.6); + + //! returns the descriptor size in floats (128) + int descriptorSize() const; + + //! returns the descriptor type + int descriptorType() const; + + //! finds the keypoints using SIFT algorithm + void operator()(InputArray img, InputArray mask, + vector& keypoints) const; + //! finds the keypoints and computes descriptors for them using SIFT algorithm. + //! Optionally it can compute descriptors for the user-provided keypoints + void operator()(InputArray img, InputArray mask, + vector& keypoints, + OutputArray descriptors, + bool useProvidedKeypoints=false) const; + + AlgorithmInfo* info() const; + + void buildGaussianPyramid( const Mat& base, vector& pyr, int nOctaves ) const; + void buildDoGPyramid( const vector& pyr, vector& dogpyr ) const; + void findScaleSpaceExtrema( const vector& gauss_pyr, const vector& dog_pyr, + vector& keypoints ) const; + +protected: + void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; + void computeImpl( const Mat& image, vector& keypoints, Mat& descriptors ) const; + + CV_PROP_RW int nfeatures; + CV_PROP_RW int nOctaveLayers; + CV_PROP_RW double contrastThreshold; + CV_PROP_RW double edgeThreshold; + CV_PROP_RW double sigma; +}; + +typedef SIFT SiftFeatureDetector; +typedef SIFT SiftDescriptorExtractor; + +/*! + SURF implementation. + + The class implements SURF algorithm by H. Bay et al. + */ +class CV_EXPORTS_W SURF : public Feature2D +{ +public: + //! the default constructor + SURF(); + //! the full constructor taking all the necessary parameters + explicit SURF(double _hessianThreshold, + bool _extended=true, bool _upright=false, + int _nOctaves=4, int _nOctaveLayers=2); + + //! returns the descriptor size in float's (64 or 128) + int descriptorSize() const; + + //! returns the descriptor type + int descriptorType() const; + + //! finds the keypoints using fast hessian detector used in SURF + void operator()(InputArray img, InputArray mask, + CV_OUT vector& keypoints) const; + //! finds the keypoints and computes their descriptors. Optionally it can compute descriptors for the user-provided keypoints + void operator()(InputArray img, InputArray mask, + CV_OUT vector& keypoints, + OutputArray descriptors, + bool useProvidedKeypoints=false) const; + + AlgorithmInfo* info() const; + + CV_PROP_RW double hessianThreshold; + CV_PROP_RW int nOctaves; + CV_PROP_RW int nOctaveLayers; + CV_PROP_RW bool extended; + CV_PROP_RW bool upright; + +protected: + + void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; + void computeImpl( const Mat& image, vector& keypoints, Mat& descriptors ) const; +}; + +typedef SURF SurfFeatureDetector; +typedef SURF SurfDescriptorExtractor; + +} /* namespace cv */ + +#endif /* __cplusplus */ + +#endif + +/* End of file. */ diff --git a/modules/nonfree/include/opencv2/nonfree/nonfree.hpp b/modules/nonfree/include/opencv2/nonfree/nonfree.hpp new file mode 100644 index 0000000000..d6dc146202 --- /dev/null +++ b/modules/nonfree/include/opencv2/nonfree/nonfree.hpp @@ -0,0 +1,57 @@ +/*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-2012, 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_NONFREE_HPP__ +#define __OPENCV_NONFREE_HPP__ + +#include "opencv2/nonfree/features2d.hpp" + +namespace cv +{ + +CV_EXPORTS bool initModule_nonfree(void); + +} + +#endif + +/* End of file. */ diff --git a/modules/nonfree/perf/perf_main.cpp b/modules/nonfree/perf/perf_main.cpp new file mode 100644 index 0000000000..3fcca19c3c --- /dev/null +++ b/modules/nonfree/perf/perf_main.cpp @@ -0,0 +1,3 @@ +#include "perf_precomp.hpp" + +CV_PERF_TEST_MAIN(nonfree) diff --git a/modules/nonfree/perf/perf_precomp.cpp b/modules/nonfree/perf/perf_precomp.cpp new file mode 100644 index 0000000000..8552ac3d42 --- /dev/null +++ b/modules/nonfree/perf/perf_precomp.cpp @@ -0,0 +1 @@ +#include "perf_precomp.hpp" diff --git a/modules/nonfree/perf/perf_precomp.hpp b/modules/nonfree/perf/perf_precomp.hpp new file mode 100644 index 0000000000..d86769ceb0 --- /dev/null +++ b/modules/nonfree/perf/perf_precomp.hpp @@ -0,0 +1,12 @@ +#ifndef __OPENCV_PERF_PRECOMP_HPP__ +#define __OPENCV_PERF_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/nonfree/nonfree.hpp" +#include "opencv2/highgui/highgui.hpp" + +#if GTEST_CREATE_SHARED_LIBRARY +#error no modules except ts should have GTEST_CREATE_SHARED_LIBRARY defined +#endif + +#endif diff --git a/modules/features2d/perf/perf_surf.cpp b/modules/nonfree/perf/perf_surf.cpp similarity index 100% rename from modules/features2d/perf/perf_surf.cpp rename to modules/nonfree/perf/perf_surf.cpp diff --git a/modules/nonfree/src/precomp.cpp b/modules/nonfree/src/precomp.cpp new file mode 100644 index 0000000000..3e0ec42de9 --- /dev/null +++ b/modules/nonfree/src/precomp.cpp @@ -0,0 +1,44 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" + +/* End of file. */ diff --git a/modules/nonfree/src/precomp.hpp b/modules/nonfree/src/precomp.hpp new file mode 100644 index 0000000000..9d7c4f099c --- /dev/null +++ b/modules/nonfree/src/precomp.hpp @@ -0,0 +1,58 @@ +/*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_PRECOMP_H__ +#define __OPENCV_PRECOMP_H__ + +#if _MSC_VER >= 1200 +#pragma warning( disable: 4251 4512 4710 4711 4514 4996 ) +#endif + +#ifdef HAVE_CVCONFIG_H +#include "cvconfig.h" +#endif + +#include "opencv2/nonfree/nonfree.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/core/internal.hpp" + +#endif diff --git a/modules/nonfree/src/sift.cpp b/modules/nonfree/src/sift.cpp new file mode 100644 index 0000000000..0e289f14bb --- /dev/null +++ b/modules/nonfree/src/sift.cpp @@ -0,0 +1,772 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +/**********************************************************************************************\ + Implementation of SIFT is based on the code from http://blogs.oregonstate.edu/hess/code/sift/ + Below is the original copyright. + +// Copyright (c) 2006-2010, Rob Hess +// All rights reserved. + +// The following patent has been issued for methods embodied in this +// software: "Method and apparatus for identifying scale invariant features +// in an image and use of same for locating an object in an image," David +// G. Lowe, US Patent 6,711,293 (March 23, 2004). Provisional application +// filed March 8, 1999. Asignee: The University of British Columbia. For +// further details, contact David Lowe (lowe@cs.ubc.ca) or the +// University-Industry Liaison Office of the University of British +// Columbia. + +// Note that restrictions imposed by this patent (and possibly others) +// exist independently of and may be in conflict with the freedoms granted +// in this license, which refers to copyright of the program, not patents +// for any methods that it implements. Both copyright and patent law must +// be obeyed to legally use and redistribute this program and it is not the +// purpose of this license to induce you to infringe any patents or other +// property right claims or to contest validity of any such claims. If you +// redistribute or use the program, then this license merely protects you +// from committing copyright infringement. It does not protect you from +// committing patent infringement. So, before you do anything with this +// program, make sure that you have permission to do so not merely in terms +// of copyright, but also in terms of patent law. + +// Please note that this license is not to be understood as a guarantee +// either. If you use the program according to this license, but in +// conflict with patent law, it does not mean that the licensor will refund +// you for any losses that you incur if you are sued for your patent +// infringement. + +// 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 and +// patent notices, this list of conditions and the following +// disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Oregon State University nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER 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. +\**********************************************************************************************/ + +#include "precomp.hpp" +#include +#include + +namespace cv +{ + +/******************************* Defs and macros *****************************/ + +// default number of sampled intervals per octave +static const int SIFT_INTVLS = 3; + +// default sigma for initial gaussian smoothing +static const float SIFT_SIGMA = 1.6f; + +// default threshold on keypoint contrast |D(x)| +static const float SIFT_CONTR_THR = 0.04f; + +// default threshold on keypoint ratio of principle curvatures +static const float SIFT_CURV_THR = 10.f; + +// double image size before pyramid construction? +static const bool SIFT_IMG_DBL = true; + +// default width of descriptor histogram array +static const int SIFT_DESCR_WIDTH = 4; + +// default number of bins per histogram in descriptor array +static const int SIFT_DESCR_HIST_BINS = 8; + +// assumed gaussian blur for input image +static const float SIFT_INIT_SIGMA = 0.5f; + +// width of border in which to ignore keypoints +static const int SIFT_IMG_BORDER = 5; + +// maximum steps of keypoint interpolation before failure +static const int SIFT_MAX_INTERP_STEPS = 5; + +// default number of bins in histogram for orientation assignment +static const int SIFT_ORI_HIST_BINS = 36; + +// determines gaussian sigma for orientation assignment +static const float SIFT_ORI_SIG_FCTR = 1.5f; + +// determines the radius of the region used in orientation assignment +static const float SIFT_ORI_RADIUS = 3 * SIFT_ORI_SIG_FCTR; + +// orientation magnitude relative to max that results in new feature +static const float SIFT_ORI_PEAK_RATIO = 0.8f; + +// determines the size of a single descriptor orientation histogram +static const float SIFT_DESCR_SCL_FCTR = 3.f; + +// threshold on magnitude of elements of descriptor vector +static const float SIFT_DESCR_MAG_THR = 0.2f; + +// factor used to convert floating-point descriptor to unsigned char +static const float SIFT_INT_DESCR_FCTR = 512.f; + +static const int SIFT_FIXPT_SCALE = 48; + + +static Mat createInitialImage( const Mat& img, bool doubleImageSize, float sigma ) +{ + Mat gray, gray_fpt; + if( img.channels() == 3 || img.channels() == 4 ) + cvtColor(img, gray, COLOR_BGR2GRAY); + else + img.copyTo(gray); + gray.convertTo(gray_fpt, CV_16S, SIFT_FIXPT_SCALE, 0); + + float sig_diff; + + if( doubleImageSize ) + { + sig_diff = sqrtf( std::max(sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA * 4, 0.01f) ); + Mat dbl; + resize(gray_fpt, dbl, Size(gray.cols*2, gray.rows*2), 0, 0, INTER_LINEAR); + GaussianBlur(dbl, dbl, Size(), sig_diff, sig_diff); + return dbl; + } + else + { + sig_diff = sqrtf( std::max(sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA, 0.01f) ); + GaussianBlur(gray_fpt, gray_fpt, Size(), sig_diff, sig_diff); + return gray_fpt; + } +} + + +void SIFT::buildGaussianPyramid( const Mat& base, vector& pyr, int nOctaves ) const +{ + vector sig(nOctaveLayers + 3); + pyr.resize(nOctaves*(nOctaveLayers + 3)); + + // precompute Gaussian sigmas using the following formula: + // \sigma_{total}^2 = \sigma_{i}^2 + \sigma_{i-1}^2 + sig[0] = sigma; + double k = pow( 2., 1. / nOctaveLayers ); + for( int i = 1; i < nOctaveLayers + 3; i++ ) + { + double sig_prev = pow(k, (double)(i-1))*sigma; + double sig_total = sig_prev*k; + sig[i] = std::sqrt(sig_total*sig_total - sig_prev*sig_prev); + } + + for( int o = 0; o < nOctaves; o++ ) + { + for( int i = 0; i < nOctaveLayers + 3; i++ ) + { + Mat& dst = pyr[o*(nOctaveLayers + 3) + i]; + if( o == 0 && i == 0 ) + dst = base; + // base of new octave is halved image from end of previous octave + else if( i == 0 ) + { + const Mat& src = pyr[(o-1)*(nOctaveLayers + 3) + nOctaveLayers]; + resize(src, dst, Size(src.cols/2, src.rows/2), + 0, 0, INTER_NEAREST); + } + else + { + const Mat& src = pyr[o*(nOctaveLayers + 3) + i-1]; + GaussianBlur(src, dst, Size(), sig[i], sig[i]); + } + } + } +} + + +void SIFT::buildDoGPyramid( const vector& gpyr, vector& dogpyr ) const +{ + int nOctaves = (int)gpyr.size()/(nOctaveLayers + 3); + dogpyr.resize( nOctaves*(nOctaveLayers + 2) ); + + for( int o = 0; o < nOctaves; o++ ) + { + for( int i = 0; i < nOctaveLayers + 2; i++ ) + { + const Mat& src1 = gpyr[o*(nOctaveLayers + 3) + i]; + const Mat& src2 = gpyr[o*(nOctaveLayers + 3) + i + 1]; + Mat& dst = dogpyr[o*(nOctaveLayers + 2) + i]; + subtract(src2, src1, dst, noArray(), CV_16S); + } + } +} + + +// Computes a gradient orientation histogram at a specified pixel +static float calcOrientationHist( const Mat& img, Point pt, int radius, + float sigma, float* hist, int n ) +{ + int i, j, k, len = (radius*2+1)*(radius*2+1); + + float expf_scale = -1.f/(2.f * sigma * sigma); + AutoBuffer buf(len*4 + n+4); + float *X = buf, *Y = X + len, *Mag = X, *Ori = Y + len, *W = Ori + len; + float* temphist = W + len + 2; + + for( i = 0; i < n; i++ ) + temphist[i] = 0.f; + + for( i = -radius, k = 0; i <= radius; i++ ) + { + int y = pt.y + i; + if( y <= 0 || y >= img.rows - 1 ) + continue; + for( j = -radius; j <= radius; j++ ) + { + int x = pt.x + j; + if( x <= 0 || x >= img.cols - 1 ) + continue; + + float dx = img.at(y, x+1) - img.at(y, x-1); + float dy = img.at(y-1, x) - img.at(y+1, x); + + X[k] = dx; Y[k] = dy; W[k] = (i*i + j*j)*expf_scale; + k++; + } + } + + len = k; + + // compute gradient values, orientations and the weights over the pixel neighborhood + exp(W, W, len); + fastAtan2(Y, X, Ori, len, true); + magnitude(X, Y, Mag, len); + + for( k = 0; k < len; k++ ) + { + int bin = cvRound((n/360.f)*Ori[k]); + if( bin >= n ) + bin -= n; + if( bin < 0 ) + bin += n; + temphist[bin] += W[k]*Mag[k]; + } + + // smooth the histogram + temphist[-1] = temphist[n-1]; + temphist[-2] = temphist[n-2]; + temphist[n] = temphist[0]; + temphist[n+1] = temphist[1]; + for( i = 0; i < n; i++ ) + { + hist[i] = (temphist[i-2] + temphist[i+2])*(1.f/16.f) + + (temphist[i-1] + temphist[i+1])*(4.f/16.f) + + temphist[i]*(6.f/16.f); + } + + float maxval = hist[0]; + for( i = 1; i < n; i++ ) + maxval = std::max(maxval, hist[i]); + + return maxval; +} + + +// +// Interpolates a scale-space extremum's location and scale to subpixel +// accuracy to form an image feature. Rejects features with low contrast. +// Based on Section 4 of Lowe's paper. +static bool adjustLocalExtrema( const vector& dog_pyr, KeyPoint& kpt, int octv, + int& layer, int& r, int& c, int nOctaveLayers, + float contrastThreshold, float edgeThreshold, float sigma ) +{ + const float img_scale = 1.f/(255*SIFT_FIXPT_SCALE); + const float deriv_scale = img_scale*0.5f; + const float second_deriv_scale = img_scale; + const float cross_deriv_scale = img_scale*0.25f; + + float xi=0, xr=0, xc=0, contr; + int i = 0; + + for( ; i < SIFT_MAX_INTERP_STEPS; i++ ) + { + int idx = octv*(nOctaveLayers+2) + layer; + const Mat& img = dog_pyr[idx]; + const Mat& prev = dog_pyr[idx-1]; + const Mat& next = dog_pyr[idx+1]; + + Matx31f dD((img.at(r, c+1) - img.at(r, c-1))*deriv_scale, + (img.at(r+1, c) - img.at(r-1, c))*deriv_scale, + (next.at(r, c) - prev.at(r, c))*deriv_scale); + + float v2 = img.at(r, c)*2; + float dxx = (img.at(r, c+1) + img.at(r, c-1) - v2)*second_deriv_scale; + float dyy = (img.at(r+1, c) + img.at(r-1, c) - v2)*second_deriv_scale; + float dss = (next.at(r, c) + prev.at(r, c) - v2)*second_deriv_scale; + float dxy = (img.at(r+1, c+1) - img.at(r+1, c-1) - + img.at(r-1, c+1) + img.at(r-1, c-1))*cross_deriv_scale; + float dxs = (next.at(r, c+1) - next.at(r, c-1) - + prev.at(r, c+1) + prev.at(r, c-1))*cross_deriv_scale; + float dys = (next.at(r+1, c) - next.at(r-1, c) - + prev.at(r+1, c) + prev.at(r-1, c))*cross_deriv_scale; + + Matx33f H(dxx, dxy, dxs, + dxy, dyy, dys, + dxs, dys, dss); + + Matx31f X = H.solve<1>(dD, DECOMP_LU); + + xi = -X(2, 0); + xr = -X(1, 0); + xc = -X(0, 0); + + if( std::abs( xi ) < 0.5f && std::abs( xr ) < 0.5f && std::abs( xc ) < 0.5f ) + break; + + c += cvRound( xc ); + r += cvRound( xr ); + layer += cvRound( xi ); + + if( layer < 1 || layer > nOctaveLayers || + c < SIFT_IMG_BORDER || c >= img.cols - SIFT_IMG_BORDER || + r < SIFT_IMG_BORDER || r >= img.rows - SIFT_IMG_BORDER ) + return false; + } + + /* ensure convergence of interpolation */ + if( i >= SIFT_MAX_INTERP_STEPS ) + return false; + + { + int idx = octv*(nOctaveLayers+2) + layer; + const Mat& img = dog_pyr[idx]; + const Mat& prev = dog_pyr[idx-1]; + const Mat& next = dog_pyr[idx+1]; + Matx31f dD((img.at(r, c+1) - img.at(r, c-1))*deriv_scale, + (img.at(r+1, c) - img.at(r-1, c))*deriv_scale, + (next.at(r, c) - prev.at(r, c))*deriv_scale); + float t = dD.dot(Matx31f(xc, xr, xi)); + + contr = img.at(r, c)*img_scale + t * 0.5f; + if( std::abs( contr ) * nOctaveLayers < contrastThreshold ) + return false; + + /* principal curvatures are computed using the trace and det of Hessian */ + float v2 = img.at(r, c)*2; + float dxx = (img.at(r, c+1) + img.at(r, c-1) - v2)*second_deriv_scale; + float dyy = (img.at(r+1, c) + img.at(r-1, c) - v2)*second_deriv_scale; + float dxy = (img.at(r+1, c+1) - img.at(r+1, c-1) - + img.at(r-1, c+1) + img.at(r-1, c-1)) * cross_deriv_scale; + float tr = dxx + dyy; + float det = dxx * dyy - dxy * dxy; + + if( det <= 0 || tr*tr*edgeThreshold >= (edgeThreshold + 1)*(edgeThreshold + 1)*det ) + return false; + } + + kpt.pt.x = (c + xc) * (1 << octv); + kpt.pt.y = (r + xr) * (1 << octv); + kpt.octave = octv + (layer << 8) + (cvRound((xi + 0.5)*255) << 16); + kpt.size = sigma*powf(2.f, (layer + xi) / nOctaveLayers)*(1 << octv)*2; + + return true; +} + + +// +// Detects features at extrema in DoG scale space. Bad features are discarded +// based on contrast and ratio of principal curvatures. +void SIFT::findScaleSpaceExtrema( const vector& gauss_pyr, const vector& dog_pyr, + vector& keypoints ) const +{ + int nOctaves = (int)gauss_pyr.size()/(nOctaveLayers + 3); + int threshold = cvFloor(0.5 * contrastThreshold / nOctaveLayers * 255 * SIFT_FIXPT_SCALE); + const int n = SIFT_ORI_HIST_BINS; + float hist[n]; + KeyPoint kpt; + + keypoints.clear(); + + for( int o = 0; o < nOctaves; o++ ) + for( int i = 1; i <= nOctaveLayers; i++ ) + { + int idx = o*(nOctaveLayers+2)+i; + const Mat& img = dog_pyr[idx]; + const Mat& prev = dog_pyr[idx-1]; + const Mat& next = dog_pyr[idx+1]; + int step = (int)img.step1(); + int rows = img.rows, cols = img.cols; + + for( int r = SIFT_IMG_BORDER; r < rows-SIFT_IMG_BORDER; r++) + { + const short* currptr = img.ptr(r); + const short* prevptr = prev.ptr(r); + const short* nextptr = next.ptr(r); + + for( int c = SIFT_IMG_BORDER; c < cols-SIFT_IMG_BORDER; c++) + { + int val = currptr[c]; + + // find local extrema with pixel accuracy + if( std::abs(val) > threshold && + ((val > 0 && val >= currptr[c-1] && val >= currptr[c+1] && + val >= currptr[c-step-1] && val >= currptr[c-step] && val >= currptr[c-step+1] && + val >= currptr[c+step-1] && val >= currptr[c+step] && val >= currptr[c+step+1] && + val >= nextptr[c] && val >= nextptr[c-1] && val >= nextptr[c+1] && + val >= nextptr[c-step-1] && val >= nextptr[c-step] && val >= nextptr[c-step+1] && + val >= nextptr[c+step-1] && val >= nextptr[c+step] && val >= nextptr[c+step+1] && + val >= prevptr[c] && val >= prevptr[c-1] && val >= prevptr[c+1] && + val >= prevptr[c-step-1] && val >= prevptr[c-step] && val >= prevptr[c-step+1] && + val >= prevptr[c+step-1] && val >= prevptr[c+step] && val >= prevptr[c+step+1]) || + (val < 0 && val <= currptr[c-1] && val <= currptr[c+1] && + val <= currptr[c-step-1] && val <= currptr[c-step] && val <= currptr[c-step+1] && + val <= currptr[c+step-1] && val <= currptr[c+step] && val <= currptr[c+step+1] && + val <= nextptr[c] && val <= nextptr[c-1] && val <= nextptr[c+1] && + val <= nextptr[c-step-1] && val <= nextptr[c-step] && val <= nextptr[c-step+1] && + val <= nextptr[c+step-1] && val <= nextptr[c+step] && val <= nextptr[c+step+1] && + val <= prevptr[c] && val <= prevptr[c-1] && val <= prevptr[c+1] && + val <= prevptr[c-step-1] && val <= prevptr[c-step] && val <= prevptr[c-step+1] && + val <= prevptr[c+step-1] && val <= prevptr[c+step] && val <= prevptr[c+step+1]))) + { + int r1 = r, c1 = c, layer = i; + if( !adjustLocalExtrema(dog_pyr, kpt, o, layer, r1, c1, nOctaveLayers, + contrastThreshold, edgeThreshold, sigma) ) + continue; + float scl_octv = kpt.size*0.5f/(1 << o); + float omax = calcOrientationHist(gauss_pyr[o*(nOctaveLayers+3) + layer], + Point(c1, r1), + cvRound(SIFT_ORI_RADIUS * scl_octv), + SIFT_ORI_SIG_FCTR * scl_octv, + hist, n); + float mag_thr = (float)(omax * SIFT_ORI_PEAK_RATIO); + for( int j = 0; j < n; j++ ) + { + int l = j > 0 ? j - 1 : n - 1; + int r = j < n-1 ? j + 1 : 0; + + if( hist[j] > hist[l] && hist[j] > hist[r] && hist[j] >= mag_thr ) + { + float bin = j + 0.5f * (hist[l]-hist[r]) / (hist[l] - 2*hist[j] + hist[r]); + bin = bin < 0 ? n + bin : bin >= n ? bin - n : bin; + kpt.angle = (float)((360.f/n) * bin); + keypoints.push_back(kpt); + } + } + } + } + } + } +} + + +static void calcSIFTDescriptor( const Mat& img, Point2f ptf, float ori, float scl, + int d, int n, float* dst ) +{ + Point pt(cvRound(ptf.x), cvRound(ptf.y)); + float cos_t = cosf(ori*(float)(CV_PI/180)); + float sin_t = sinf(ori*(float)(CV_PI/180)); + float bins_per_rad = n / 360.f; + float exp_scale = -1.f/(d * d * 0.5f); + float hist_width = SIFT_DESCR_SCL_FCTR * scl; + int radius = cvRound(hist_width * 1.4142135623730951f * (d + 1) * 0.5f); + cos_t /= hist_width; + sin_t /= hist_width; + + int i, j, k, len = (radius*2+1)*(radius*2+1), histlen = (d+2)*(d+2)*(n+2); + int rows = img.rows, cols = img.cols; + + AutoBuffer buf(len*6 + histlen); + float *X = buf, *Y = X + len, *Mag = Y, *Ori = Mag + len, *W = Ori + len; + float *RBin = W + len, *CBin = RBin + len, *hist = CBin + len; + + for( i = 0; i < d+2; i++ ) + { + for( j = 0; j < d+2; j++ ) + for( k = 0; k < n+2; k++ ) + hist[(i*(d+2) + j)*(n+2) + k] = 0.; + } + + for( i = -radius, k = 0; i <= radius; i++ ) + for( j = -radius; j <= radius; j++ ) + { + /* + Calculate sample's histogram array coords rotated relative to ori. + Subtract 0.5 so samples that fall e.g. in the center of row 1 (i.e. + r_rot = 1.5) have full weight placed in row 1 after interpolation. + */ + float c_rot = j * cos_t - i * sin_t; + float r_rot = j * sin_t + i * cos_t; + float rbin = r_rot + d/2 - 0.5f; + float cbin = c_rot + d/2 - 0.5f; + int r = pt.y + i, c = pt.x + j; + + if( rbin > -1 && rbin < d && cbin > -1 && cbin < d && + r > 0 && r < rows - 1 && c > 0 && c < cols - 1 ) + { + float dx = img.at(r, c+1) - img.at(r, c-1); + float dy = img.at(r-1, c) - img.at(r+1, c); + X[k] = dx; Y[k] = dy; RBin[k] = rbin; CBin[k] = cbin; + W[k] = (c_rot * c_rot + r_rot * r_rot)*exp_scale; + k++; + } + } + + len = k; + fastAtan2(Y, X, Ori, len, true); + magnitude(X, Y, Mag, len); + exp(W, W, len); + + for( k = 0; k < len; k++ ) + { + float rbin = RBin[k], cbin = CBin[k]; + float obin = (Ori[k] - ori)*bins_per_rad; + float mag = Mag[k]*W[k]; + + int r0 = cvFloor( rbin ); + int c0 = cvFloor( cbin ); + int o0 = cvFloor( obin ); + rbin -= r0; + cbin -= c0; + obin -= o0; + + if( o0 < 0 ) + o0 += n; + if( o0 >= n ) + o0 -= n; + + // histogram update using tri-linear interpolation + float v_r1 = mag*rbin, v_r0 = mag - v_r1; + float v_rc11 = v_r1*cbin, v_rc10 = v_r1 - v_rc11; + float v_rc01 = v_r0*cbin, v_rc00 = v_r0 - v_rc01; + float v_rco111 = v_rc11*obin, v_rco110 = v_rc11 - v_rco111; + float v_rco101 = v_rc10*obin, v_rco100 = v_rc10 - v_rco101; + float v_rco011 = v_rc01*obin, v_rco010 = v_rc01 - v_rco011; + float v_rco001 = v_rc00*obin, v_rco000 = v_rc00 - v_rco001; + + int idx = ((r0+1)*(d+2) + c0+1)*(n+2) + o0; + hist[idx] += v_rco000; + hist[idx+1] += v_rco001; + hist[idx+(n+2)] += v_rco010; + hist[idx+(n+3)] += v_rco011; + hist[idx+(d+2)*(n+2)] += v_rco100; + hist[idx+(d+2)*(n+2)+1] += v_rco101; + hist[idx+(d+3)*(n+2)] += v_rco110; + hist[idx+(d+3)*(n+2)+1] += v_rco111; + } + + // finalize histogram, since the orientation histograms are circular + for( i = 0; i < d; i++ ) + for( j = 0; j < d; j++ ) + { + int idx = ((i+1)*(d+2) + (j+1))*(n+2); + hist[idx] += hist[idx+n]; + hist[idx+1] += hist[idx+n+1]; + for( k = 0; k < n; k++ ) + dst[(i*d + j)*n + k] = hist[idx+k]; + } + // copy histogram to the descriptor, + // apply hysteresis thresholding + // and scale the result, so that it can be easily converted + // to byte array + float nrm2 = 0; + len = d*d*n; + for( k = 0; k < len; k++ ) + nrm2 += dst[k]*dst[k]; + float thr = std::sqrt(nrm2)*SIFT_DESCR_MAG_THR; + for( i = 0, nrm2 = 0; i < k; i++ ) + { + float val = std::min(dst[i], thr); + dst[i] = val; + nrm2 += val*val; + } + nrm2 = SIFT_INT_DESCR_FCTR/std::max(std::sqrt(nrm2), FLT_EPSILON); + for( k = 0; k < len; k++ ) + { + dst[k] = saturate_cast(dst[k]*nrm2); + } +} + +static void calcDescriptors(const vector& gpyr, const vector& keypoints, + Mat& descriptors, int nOctaveLayers ) +{ + int d = SIFT_DESCR_WIDTH, n = SIFT_DESCR_HIST_BINS; + + for( size_t i = 0; i < keypoints.size(); i++ ) + { + KeyPoint kpt = keypoints[i]; + int octv=kpt.octave & 255, layer=(kpt.octave >> 8) & 255; + float scale = 1.f/(1 << octv); + float size=kpt.size*scale; + Point2f ptf(kpt.pt.x*scale, kpt.pt.y*scale); + const Mat& img = gpyr[octv*(nOctaveLayers + 3) + layer]; + + calcSIFTDescriptor(img, ptf, kpt.angle, size*0.5f, d, n, descriptors.ptr(i)); + } +} + +////////////////////////////////////////////////////////////////////////////////////////// + +SIFT::SIFT( int _nfeatures, int _nOctaveLayers, + double _contrastThreshold, double _edgeThreshold, double _sigma ) + : nfeatures(_nfeatures), nOctaveLayers(_nOctaveLayers), + contrastThreshold(_contrastThreshold), edgeThreshold(_edgeThreshold), sigma(_sigma) +{ +} + +int SIFT::descriptorSize() const +{ + return SIFT_DESCR_WIDTH*SIFT_DESCR_WIDTH*SIFT_DESCR_HIST_BINS; +} + +int SIFT::descriptorType() const +{ + return CV_32F; +} + +static Algorithm* createSIFT() +{ + return new SIFT; +} +static AlgorithmInfo sift_info("Feature2D.SIFT", createSIFT); + +AlgorithmInfo* SIFT::info() const +{ + static volatile bool initialized = false; + if( !initialized ) + { + sift_info.addParam(this, "nFeatures", nfeatures); + sift_info.addParam(this, "nOctaveLayers", nOctaveLayers); + sift_info.addParam(this, "contrastThreshold", contrastThreshold); + sift_info.addParam(this, "edgeThreshold", edgeThreshold); + sift_info.addParam(this, "sigma", sigma); + + initialized = true; + } + return &sift_info; +} + + +void SIFT::operator()(InputArray _image, InputArray _mask, + vector& keypoints) const +{ + (*this)(_image, _mask, keypoints, noArray()); +} + + +void SIFT::operator()(InputArray _image, InputArray _mask, + vector& keypoints, + OutputArray _descriptors, + bool useProvidedKeypoints) const +{ + Mat image = _image.getMat(), mask = _mask.getMat(); + + if( image.empty() || image.depth() != CV_8U ) + CV_Error( CV_StsBadArg, "image is empty or has incorrect depth (!=CV_8U)" ); + + if( !mask.empty() && mask.type() != CV_8UC1 ) + CV_Error( CV_StsBadArg, "mask has incorrect type (!=CV_8UC1)" ); + + Mat base = createInitialImage(image, false, sigma); + vector gpyr, dogpyr; + int nOctaves = cvRound(log( (double)std::min( base.cols, base.rows ) ) / log(2.) - 2); + + //double t, tf = getTickFrequency(); + //t = (double)getTickCount(); + buildGaussianPyramid(base, gpyr, nOctaves); + buildDoGPyramid(gpyr, dogpyr); + + //t = (double)getTickCount() - t; + //printf("pyramid construction time: %g\n", t*1000./tf); + + if( !useProvidedKeypoints ) + { + //t = (double)getTickCount(); + findScaleSpaceExtrema(gpyr, dogpyr, keypoints); + KeyPointsFilter::removeDuplicated( keypoints ); + + if( !mask.empty() ) + KeyPointsFilter::runByPixelsMask( keypoints, mask ); + + if( nfeatures > 0 ) + KeyPointsFilter::retainBest(keypoints, nfeatures); + //t = (double)getTickCount() - t; + //printf("keypoint detection time: %g\n", t*1000./tf); + } + else + { + // filter keypoints by mask + //KeyPointsFilter::runByPixelsMask( keypoints, mask ); + } + + if( _descriptors.needed() ) + { + //t = (double)getTickCount(); + int dsize = descriptorSize(); + _descriptors.create((int)keypoints.size(), dsize, CV_32F); + Mat descriptors = _descriptors.getMat(); + + calcDescriptors(gpyr, keypoints, descriptors, nOctaveLayers); + //t = (double)getTickCount() - t; + //printf("descriptor extraction time: %g\n", t*1000./tf); + } +} + +void SIFT::detectImpl( const Mat& image, vector& keypoints, const Mat& mask) const +{ + (*this)(image, mask, keypoints, noArray()); +} + +void SIFT::computeImpl( const Mat& image, vector& keypoints, Mat& descriptors) const +{ + (*this)(image, Mat(), keypoints, descriptors, true); +} + +} diff --git a/modules/features2d/src/surf.cpp b/modules/nonfree/src/surf.cpp similarity index 50% rename from modules/features2d/src/surf.cpp rename to modules/nonfree/src/surf.cpp index d544cbcdd7..61a248c1cc 100644 --- a/modules/features2d/src/surf.cpp +++ b/modules/nonfree/src/surf.cpp @@ -109,26 +109,32 @@ Modifications by Ian Mahon */ #include "precomp.hpp" -CvSURFParams cvSURFParams(double threshold, int extended) -{ - CvSURFParams params; - params.hessianThreshold = threshold; - params.extended = extended; - params.upright = 0; - params.nOctaves = 4; - params.nOctaveLayers = 2; - return params; -} +bool cv::initModule_nonfree(void) { return true; } -struct CvSurfHF +namespace cv +{ + +static const int SURF_ORI_SEARCH_INC = 5; +static const float SURF_ORI_SIGMA = 2.5f; +static const float SURF_DESC_SIGMA = 3.3f; + +// Wavelet size at first layer of first octave. +static const int SURF_HAAR_SIZE0 = 9; + +// Wavelet size increment between layers. This should be an even number, +// such that the wavelet sizes in an octave are either all even or all odd. +// This ensures that when looking for the neighbours of a sample, the layers +// above and below are aligned correctly. +static const int SURF_HAAR_SIZE_INC = 6; + + +struct SurfHF { int p0, p1, p2, p3; float w; - CvSurfHF(): p0(0),p1(0),p2(0),p3(0),w(0) {} }; -CV_INLINE float -icvCalcHaarPattern( const int* origin, const CvSurfHF* f, int n ) +inline float calcHaarPattern( const int* origin, const SurfHF* f, int n ) { double d = 0; for( int k = 0; k < n; k++ ) @@ -137,7 +143,7 @@ icvCalcHaarPattern( const int* origin, const CvSurfHF* f, int n ) } static void -icvResizeHaarPattern( const int src[][5], CvSurfHF* dst, int n, int oldSize, int newSize, int widthStep ) +resizeHaarPattern( const int src[][5], SurfHF* dst, int n, int oldSize, int newSize, int widthStep ) { float ratio = (float)newSize/oldSize; for( int k = 0; k < n; k++ ) @@ -158,47 +164,43 @@ icvResizeHaarPattern( const int src[][5], CvSurfHF* dst, int n, int oldSize, int * Calculate the determinant and trace of the Hessian for a layer of the * scale-space pyramid */ -CV_INLINE void -icvCalcLayerDetAndTrace( const CvMat* sum, int size, int sampleStep, CvMat *det, CvMat *trace ) +static void calcLayerDetAndTrace( const Mat& sum, int size, int sampleStep, + Mat& det, Mat& trace ) { const int NX=3, NY=3, NXY=4; const int dx_s[NX][5] = { {0, 2, 3, 7, 1}, {3, 2, 6, 7, -2}, {6, 2, 9, 7, 1} }; const int dy_s[NY][5] = { {2, 0, 7, 3, 1}, {2, 3, 7, 6, -2}, {2, 6, 7, 9, 1} }; const int dxy_s[NXY][5] = { {1, 1, 4, 4, 1}, {5, 1, 8, 4, -1}, {1, 5, 4, 8, -1}, {5, 5, 8, 8, 1} }; - CvSurfHF Dx[NX], Dy[NY], Dxy[NXY]; - double dx = 0, dy = 0, dxy = 0; - int i, j, samples_i, samples_j, margin; - int *sum_ptr; - float *det_ptr, *trace_ptr; + SurfHF Dx[NX], Dy[NY], Dxy[NXY]; - if( size>sum->rows-1 || size>sum->cols-1 ) + if( size > sum.rows-1 || size > sum.cols-1 ) return; - icvResizeHaarPattern( dx_s , Dx , NX , 9, size, sum->cols ); - icvResizeHaarPattern( dy_s , Dy , NY , 9, size, sum->cols ); - icvResizeHaarPattern( dxy_s, Dxy, NXY, 9, size, sum->cols ); + resizeHaarPattern( dx_s , Dx , NX , 9, size, sum.cols ); + resizeHaarPattern( dy_s , Dy , NY , 9, size, sum.cols ); + resizeHaarPattern( dxy_s, Dxy, NXY, 9, size, sum.cols ); /* The integral image 'sum' is one pixel bigger than the source image */ - samples_i = 1+(sum->rows-1-size)/sampleStep; - samples_j = 1+(sum->cols-1-size)/sampleStep; + int samples_i = 1+(sum.rows-1-size)/sampleStep; + int samples_j = 1+(sum.cols-1-size)/sampleStep; /* Ignore pixels where some of the kernel is outside the image */ - margin = (size/2)/sampleStep; + int margin = (size/2)/sampleStep; - for( i = 0; i < samples_i; i++ ) + for( int i = 0; i < samples_i; i++ ) { - sum_ptr = sum->data.i + (i*sampleStep)*sum->cols; - det_ptr = det->data.fl + (i+margin)*det->cols + margin; - trace_ptr = trace->data.fl + (i+margin)*trace->cols + margin; - for( j=0; j(i*sampleStep); + float* det_ptr = &det.at(i+margin, margin); + float* trace_ptr = &trace.at(i+margin, margin); + for( int j = 0; j < samples_j; j++ ) { - dx = icvCalcHaarPattern( sum_ptr, Dx , 3 ); - dy = icvCalcHaarPattern( sum_ptr, Dy , 3 ); - dxy = icvCalcHaarPattern( sum_ptr, Dxy, 4 ); + float dx = calcHaarPattern( sum_ptr, Dx , 3 ); + float dy = calcHaarPattern( sum_ptr, Dy , 3 ); + float dxy = calcHaarPattern( sum_ptr, Dxy, 4 ); sum_ptr += sampleStep; - *det_ptr++ = (float)(dx*dy - 0.81*dxy*dxy); - *trace_ptr++ = (float)(dx + dy); + det_ptr[j] = dx*dy - 0.81f*dxy*dxy; + trace_ptr[j] = dx + dy; } } } @@ -223,81 +225,75 @@ icvCalcLayerDetAndTrace( const CvMat* sum, int size, int sampleStep, CvMat *det, * * Return value is 1 if interpolation was successful, 0 on failure. */ -CV_INLINE int -icvInterpolateKeypoint( float N9[3][9], int dx, int dy, int ds, CvSURFPoint *point ) +static int +interpolateKeypoint( float N9[3][9], int dx, int dy, int ds, KeyPoint& kpt ) { - int solve_ok; - float A[9], x[3], b[3]; - CvMat matA = cvMat(3, 3, CV_32F, A); - CvMat _x = cvMat(3, 1, CV_32F, x); - CvMat _b = cvMat(3, 1, CV_32F, b); + Matx31f b(-(N9[1][5]-N9[1][3])/2, // Negative 1st deriv with respect to x + -(N9[1][7]-N9[1][1])/2, // Negative 1st deriv with respect to y + -(N9[2][4]-N9[0][4])/2); // Negative 1st deriv with respect to s - b[0] = -(N9[1][5]-N9[1][3])/2; /* Negative 1st deriv with respect to x */ - b[1] = -(N9[1][7]-N9[1][1])/2; /* Negative 1st deriv with respect to y */ - b[2] = -(N9[2][4]-N9[0][4])/2; /* Negative 1st deriv with respect to s */ + Matx33f A( + N9[1][3]-2*N9[1][4]+N9[1][5], // 2nd deriv x, x + (N9[1][8]-N9[1][6]-N9[1][2]+N9[1][0])/4, // 2nd deriv x, y + (N9[2][5]-N9[2][3]-N9[0][5]+N9[0][3])/4, // 2nd deriv x, s + (N9[1][8]-N9[1][6]-N9[1][2]+N9[1][0])/4, // 2nd deriv x, y + N9[1][1]-2*N9[1][4]+N9[1][7], // 2nd deriv y, y + (N9[2][7]-N9[2][1]-N9[0][7]+N9[0][1])/4, // 2nd deriv y, s + (N9[2][5]-N9[2][3]-N9[0][5]+N9[0][3])/4, // 2nd deriv x, s + (N9[2][7]-N9[2][1]-N9[0][7]+N9[0][1])/4, // 2nd deriv y, s + N9[0][4]-2*N9[1][4]+N9[2][4]); // 2nd deriv s, s - A[0] = N9[1][3]-2*N9[1][4]+N9[1][5]; /* 2nd deriv x, x */ - A[1] = (N9[1][8]-N9[1][6]-N9[1][2]+N9[1][0])/4; /* 2nd deriv x, y */ - A[2] = (N9[2][5]-N9[2][3]-N9[0][5]+N9[0][3])/4; /* 2nd deriv x, s */ - A[3] = A[1]; /* 2nd deriv y, x */ - A[4] = N9[1][1]-2*N9[1][4]+N9[1][7]; /* 2nd deriv y, y */ - A[5] = (N9[2][7]-N9[2][1]-N9[0][7]+N9[0][1])/4; /* 2nd deriv y, s */ - A[6] = A[2]; /* 2nd deriv s, x */ - A[7] = A[5]; /* 2nd deriv s, y */ - A[8] = N9[0][4]-2*N9[1][4]+N9[2][4]; /* 2nd deriv s, s */ - - solve_ok = cvSolve( &matA, &_b, &_x ); - if( solve_ok ) + Matx31f x = A.solve<1>(b, DECOMP_LU); + + bool ok = (x(0,0) != 0 || x(1,0) != 0 || x(2,0) != 0) && + std::abs(x(0,0)) <= 1 && std::abs(x(1,0)) <= 1 && std::abs(x(2,0)) <= 1; + + if( ok ) { - if (x[0] > 1 || x[0] <-1 || x[1] > 1 || x[1] <-1 || x[2] > 1 || x[2] <-1 ) - solve_ok = 0; - else - { - point->pt.x += x[0]*dx; - point->pt.y += x[1]*dy; - point->size = cvRound( point->size + x[2]*ds ); - } + kpt.pt.x += x(0,0)*dx; + kpt.pt.y += x(1,0)*dy; + kpt.size = cvRound( kpt.size + x(2,0)*ds ); } - return solve_ok; + return ok; } /* * Find the maxima in the determinant of the Hessian in a layer of the * scale-space pyramid */ -CV_INLINE void -icvFindMaximaInLayer( const CvMat *sum, const CvMat* mask_sum, const CvSURFParams* params, - CvMat **dets, CvMat **traces, const int *sizes, - int layer, int sampleStep, CvSeq* points ) +static void +findMaximaInLayer( const Mat& sum, const Mat& mask_sum, + const vector& dets, const vector& traces, + const vector& sizes, vector& keypoints, + int octave, int layer, float hessianThreshold, int sampleStep ) { - /* Wavelet Data */ + // Wavelet Data const int NM=1; const int dm[NM][5] = { {0, 0, 9, 9, 1} }; + SurfHF Dm; - CvSurfHF Dm; - int i, j, size, margin, layer_rows, layer_cols; - float *det_ptr, *trace_ptr; + int size = sizes[layer]; - size = sizes[layer]; + // The integral image 'sum' is one pixel bigger than the source image + int layer_rows = (sum.rows-1)/sampleStep; + int layer_cols = (sum.cols-1)/sampleStep; - /* The integral image 'sum' is one pixel bigger than the source image */ - layer_rows = (sum->rows-1)/sampleStep; - layer_cols = (sum->cols-1)/sampleStep; + // Ignore pixels without a 3x3x3 neighbourhood in the layer above + int margin = (sizes[layer+1]/2)/sampleStep+1; - /* Ignore pixels without a 3x3x3 neighbourhood in the layer above */ - margin = (sizes[layer+1]/2)/sampleStep+1; + if( !mask_sum.empty() ) + resizeHaarPattern( dm, &Dm, NM, 9, size, mask_sum.cols ); - if( mask_sum ) - icvResizeHaarPattern( dm, &Dm, NM, 9, size, mask_sum->cols ); - - for( i = margin; i < layer_rows-margin; i++ ) + int step = (int)(dets[layer].step/dets[layer].elemSize()); + + for( int i = margin; i < layer_rows - margin; i++ ) { - det_ptr = dets[layer]->data.fl + i*dets[layer]->cols; - trace_ptr = traces[layer]->data.fl + i*traces[layer]->cols; - for( j = margin; j < layer_cols-margin; j++ ) + const float* det_ptr = dets[layer].ptr(i); + const float* trace_ptr = traces[layer].ptr(i); + for( int j = margin; j < layer_cols-margin; j++ ) { float val0 = det_ptr[j]; - if( val0 > params->hessianThreshold ) + if( val0 > hessianThreshold ) { /* Coordinates for the start of the wavelet in the sum image. There is some integer division involved, so don't try to simplify this @@ -307,25 +303,25 @@ icvFindMaximaInLayer( const CvMat *sum, const CvMat* mask_sum, const CvSURFParam /* The 3x3x3 neighbouring samples around the maxima. The maxima is included at N9[1][4] */ - int c = dets[layer]->cols; - const float *det1 = dets[layer-1]->data.fl + i*c + j; - const float *det2 = dets[layer]->data.fl + i*c + j; - const float *det3 = dets[layer+1]->data.fl + i*c + j; - float N9[3][9] = { { det1[-c-1], det1[-c], det1[-c+1], + + const float *det1 = &dets[layer-1].at(i, j); + const float *det2 = &dets[layer].at(i, j); + const float *det3 = &dets[layer+1].at(i, j); + float N9[3][9] = { { det1[-step-1], det1[-step], det1[-step+1], det1[-1] , det1[0] , det1[1], - det1[c-1] , det1[c] , det1[c+1] }, - { det2[-c-1], det2[-c], det2[-c+1], + det1[step-1] , det1[step] , det1[step+1] }, + { det2[-step-1], det2[-step], det2[-step+1], det2[-1] , det2[0] , det2[1], - det2[c-1] , det2[c] , det2[c+1] }, - { det3[-c-1], det3[-c], det3[-c+1], + det2[step-1] , det2[step] , det2[step+1] }, + { det3[-step-1], det3[-step], det3[-step+1], det3[-1] , det3[0] , det3[1], - det3[c-1] , det3[c] , det3[c+1] } }; + det3[step-1] , det3[step] , det3[step+1] } }; /* Check the mask - why not just check the mask at the center of the wavelet? */ - if( mask_sum ) + if( !mask_sum.empty() ) { - const int* mask_ptr = mask_sum->data.i + mask_sum->cols*sum_i + sum_j; - float mval = icvCalcHaarPattern( mask_ptr, &Dm, 1 ); + const int* mask_ptr = &mask_sum.at(sum_i, sum_j); + float mval = calcHaarPattern( mask_ptr, &Dm, 1 ); if( mval < 0.5 ) continue; } @@ -342,15 +338,14 @@ icvFindMaximaInLayer( const CvMat *sum, const CvMat* mask_sum, const CvSURFParam val0 > N9[2][6] && val0 > N9[2][7] && val0 > N9[2][8] ) { /* Calculate the wavelet center coordinates for the maxima */ - double center_i = sum_i + (double)(size-1)/2; - double center_j = sum_j + (double)(size-1)/2; + float center_i = sum_i + (size-1)*0.5f; + float center_j = sum_j + (size-1)*0.5f; - CvSURFPoint point = cvSURFPoint( cvPoint2D32f(center_j,center_i), - CV_SIGN(trace_ptr[j]), sizes[layer], 0, val0 ); + KeyPoint kpt( center_j, center_i, sizes[layer], -1, val0, octave, CV_SIGN(trace_ptr[j]) ); /* Interpolate maxima location within the 3x3x3 neighbourhood */ - int ds = size-sizes[layer-1]; - int interp_ok = icvInterpolateKeypoint( N9, sampleStep, sampleStep, ds, &point ); + int ds = size - sizes[layer-1]; + int interp_ok = interpolateKeypoint( N9, sampleStep, sampleStep, ds, kpt ); /* Sometimes the interpolation step gives a negative size etc. */ if( interp_ok ) @@ -360,7 +355,7 @@ icvFindMaximaInLayer( const CvMat *sum, const CvMat* mask_sum, const CvSURFParam static tbb::mutex m; tbb::mutex::scoped_lock lock(m); #endif - cvSeqPush( points, &point ); + keypoints.push_back(kpt); } } } @@ -369,192 +364,153 @@ icvFindMaximaInLayer( const CvMat *sum, const CvMat* mask_sum, const CvSURFParam } -namespace cv -{ -/* Multi-threaded construction of the scale-space pyramid */ +// Multi-threaded construction of the scale-space pyramid struct SURFBuildInvoker { - SURFBuildInvoker( const CvMat *_sum, const int *_sizes, const int *_sampleSteps, - CvMat** _dets, CvMat** _traces ) + SURFBuildInvoker( const Mat& _sum, const vector& _sizes, + const vector& _sampleSteps, + vector& _dets, vector& _traces ) { - sum = _sum; - sizes = _sizes; - sampleSteps = _sampleSteps; - dets = _dets; - traces = _traces; + sum = &_sum; + sizes = &_sizes; + sampleSteps = &_sampleSteps; + dets = &_dets; + traces = &_traces; } void operator()(const BlockedRange& range) const { for( int i=range.begin(); i *sizes; + const vector *sampleSteps; + vector* dets; + vector* traces; }; -/* Multi-threaded search of the scale-space pyramid for keypoints */ +// Multi-threaded search of the scale-space pyramid for keypoints struct SURFFindInvoker { - SURFFindInvoker( const CvMat *_sum, const CvMat *_mask_sum, const CvSURFParams* _params, - CvMat** _dets, CvMat** _traces, const int *_sizes, - const int *_sampleSteps, const int *_middleIndices, CvSeq* _points ) - + SURFFindInvoker( const Mat& _sum, const Mat& _mask_sum, + const vector& _dets, const vector& _traces, + const vector& _sizes, const vector& _sampleSteps, + const vector& _middleIndices, vector& _keypoints, + int _nOctaveLayers, float _hessianThreshold ) { - sum = _sum; - mask_sum = _mask_sum; - params = _params; - dets = _dets; - traces = _traces; - sizes = _sizes; - sampleSteps = _sampleSteps; - middleIndices = _middleIndices; - points = _points; + sum = &_sum; + mask_sum = &_mask_sum; + dets = &_dets; + traces = &_traces; + sizes = &_sizes; + sampleSteps = &_sampleSteps; + middleIndices = &_middleIndices; + keypoints = &_keypoints; + nOctaveLayers = _nOctaveLayers; + hessianThreshold = _hessianThreshold; } void operator()(const BlockedRange& range) const { for( int i=range.begin(); i* dets; + const vector* traces; + const vector* sizes; + const vector* sampleSteps; + const vector* middleIndices; + vector* keypoints; + int nOctaveLayers; + float hessianThreshold; }; -} // namespace cv - - - -/* Wavelet size at first layer of first octave. */ -const int HAAR_SIZE0 = 9; - -/* Wavelet size increment between layers. This should be an even number, - such that the wavelet sizes in an octave are either all even or all odd. - This ensures that when looking for the neighbours of a sample, the layers - above and below are aligned correctly. */ -const int HAAR_SIZE_INC = 6; - - -static CvSeq* icvFastHessianDetector( const CvMat* sum, const CvMat* mask_sum, - CvMemStorage* storage, const CvSURFParams* params ) + +static void fastHessianDetector( const Mat& sum, const Mat& mask_sum, vector& keypoints, + int nOctaves, int nOctaveLayers, float hessianThreshold ) { - CvSeq* points = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSURFPoint), storage ); - /* Sampling step along image x and y axes at first octave. This is doubled for each additional octave. WARNING: Increasing this improves speed, however keypoint extraction becomes unreliable. */ const int SAMPLE_STEP0 = 1; - int nTotalLayers = (params->nOctaveLayers+2)*params->nOctaves; - int nMiddleLayers = params->nOctaveLayers*params->nOctaves; + int nTotalLayers = (nOctaveLayers+2)*nOctaves; + int nMiddleLayers = nOctaveLayers*nOctaves; - cv::AutoBuffer dets(nTotalLayers); - cv::AutoBuffer traces(nTotalLayers); - cv::AutoBuffer sizes(nTotalLayers); - cv::AutoBuffer sampleSteps(nTotalLayers); - cv::AutoBuffer middleIndices(nMiddleLayers); - int octave, layer, step, index, middleIndex; + vector dets(nTotalLayers); + vector traces(nTotalLayers); + vector sizes(nTotalLayers); + vector sampleSteps(nTotalLayers); + vector middleIndices(nMiddleLayers); - /* Allocate space and calculate properties of each layer */ - index = 0; - middleIndex = 0; - step = SAMPLE_STEP0; - for( octave=0; octavenOctaves; octave++ ) + // Allocate space and calculate properties of each layer + int index = 0, middleIndex = 0, step = SAMPLE_STEP0; + + for( int octave = 0; octave < nOctaves; octave++ ) { - for( layer=0; layernOctaveLayers+2; layer++ ) + for( int layer = 0; layer < nOctaveLayers+2; layer++ ) { /* The integral image sum is one pixel bigger than the source image*/ - dets[index] = cvCreateMat( (sum->rows-1)/step, (sum->cols-1)/step, CV_32FC1 ); - traces[index] = cvCreateMat( (sum->rows-1)/step, (sum->cols-1)/step, CV_32FC1 ); - sizes[index] = (HAAR_SIZE0+HAAR_SIZE_INC*layer)<nOctaveLayers+1 ) - middleIndices[middleIndex++] = index; + if( 0 < layer && layer <= nOctaveLayers ) + middleIndices[middleIndex++] = index; index++; } - step*=2; + step *= 2; } -#ifdef HAVE_TBB - /* Calculate hessian determinant and trace samples in each layer*/ - cv::parallel_for( cv::BlockedRange(0, nTotalLayers), - cv::SURFBuildInvoker(sum,sizes,sampleSteps,dets,traces) ); + // Calculate hessian determinant and trace samples in each layer + parallel_for( BlockedRange(0, nTotalLayers), + SURFBuildInvoker(sum, sizes, sampleSteps, dets, traces) ); - /* Find maxima in the determinant of the hessian */ - cv::parallel_for( cv::BlockedRange(0, nMiddleLayers), - cv::SURFFindInvoker(sum,mask_sum,params,dets,traces,sizes, - sampleSteps,middleIndices,points) ); -#else - cv::SURFBuildInvoker(sum,sizes,sampleSteps,dets,traces) - (cv::BlockedRange(0, nTotalLayers)); - - cv::SURFFindInvoker(sum,mask_sum,params,dets,traces,sizes, sampleSteps,middleIndices,points) - ( cv::BlockedRange(0, nMiddleLayers) ); -#endif - - /* Clean-up */ - for( layer = 0; layer < nTotalLayers; layer++ ) - { - cvReleaseMat( &dets[layer] ); - cvReleaseMat( &traces[layer] ); - } - - return points; + // Find maxima in the determinant of the hessian + parallel_for( BlockedRange(0, nMiddleLayers), + SURFFindInvoker(sum, mask_sum, dets, traces, sizes, + sampleSteps, middleIndices, keypoints, + nOctaveLayers, hessianThreshold) ); } -namespace cv -{ - -/* Methods to free data allocated in SURFInvoker constructor */ -template<> inline void Ptr::delete_obj() { cvFree(&obj); } -template<> inline void Ptr::delete_obj() { cvFree(&obj); } - struct SURFInvoker { enum { ORI_RADIUS = 6, ORI_WIN = 60, PATCH_SZ = 20 }; - static const int ORI_SEARCH_INC; - static const float ORI_SIGMA; - static const float DESC_SIGMA; - - SURFInvoker( const CvSURFParams* _params, - CvSeq* _keypoints, CvSeq* _descriptors, - const CvMat* _img, const CvMat* _sum ) + SURFInvoker( const Mat& _img, const Mat& _sum, + vector& _keypoints, Mat& _descriptors, + bool _extended, bool _upright ) { - params = _params; - keypoints = _keypoints; - descriptors = _descriptors; - img = _img; - sum = _sum; + keypoints = &_keypoints; + descriptors = &_descriptors; + img = &_img; + sum = &_sum; + extended = _extended; + upright = _upright; - /* Simple bound for number of grid points in circle of radius ORI_RADIUS */ + // Simple bound for number of grid points in circle of radius ORI_RADIUS const int nOriSampleBound = (2*ORI_RADIUS+1)*(2*ORI_RADIUS+1); - /* Allocate arrays */ - apt = (CvPoint*)cvAlloc(nOriSampleBound*sizeof(CvPoint)); - aptw = (float*)cvAlloc(nOriSampleBound*sizeof(float)); - DW = (float*)cvAlloc(PATCH_SZ*PATCH_SZ*sizeof(float)); + // Allocate arrays + apt.resize(nOriSampleBound); + aptw.resize(nOriSampleBound); + DW.resize(PATCH_SZ*PATCH_SZ); /* Coordinates and weights of samples used to calculate orientation */ - cv::Mat G_ori = cv::getGaussianKernel( 2*ORI_RADIUS+1, ORI_SIGMA, CV_32F ); + Mat G_ori = getGaussianKernel( 2*ORI_RADIUS+1, SURF_ORI_SIGMA, CV_32F ); nOriSamples = 0; for( int i = -ORI_RADIUS; i <= ORI_RADIUS; i++ ) { @@ -570,7 +526,7 @@ struct SURFInvoker CV_Assert( nOriSamples <= nOriSampleBound ); /* Gaussian used to weight descriptor samples */ - cv::Mat G_desc = cv::getGaussianKernel( PATCH_SZ, DESC_SIGMA, CV_32F ); + Mat G_desc = getGaussianKernel( PATCH_SZ, SURF_DESC_SIGMA, CV_32F ); for( int i = 0; i < PATCH_SZ; i++ ) { for( int j = 0; j < PATCH_SZ; j++ ) @@ -585,9 +541,8 @@ struct SURFInvoker const int dx_s[NX][5] = {{0, 0, 2, 4, -1}, {2, 0, 4, 4, 1}}; const int dy_s[NY][5] = {{0, 0, 4, 2, 1}, {0, 2, 4, 4, -1}}; - const int descriptor_size = params->extended ? 128 : 64; - /* Optimisation is better using nOriSampleBound than nOriSamples for - array lengths. Maybe because it is a constant known at compile time */ + // Optimisation is better using nOriSampleBound than nOriSamples for + // array lengths. Maybe because it is a constant known at compile time const int nOriSampleBound =(2*ORI_RADIUS+1)*(2*ORI_RADIUS+1); float X[nOriSampleBound], Y[nOriSampleBound], angle[nOriSampleBound]; @@ -596,77 +551,76 @@ struct SURFInvoker CvMat matX = cvMat(1, nOriSampleBound, CV_32F, X); CvMat matY = cvMat(1, nOriSampleBound, CV_32F, Y); CvMat _angle = cvMat(1, nOriSampleBound, CV_32F, angle); - CvMat _patch = cvMat(PATCH_SZ+1, PATCH_SZ+1, CV_8U, PATCH); + Mat _patch(PATCH_SZ+1, PATCH_SZ+1, CV_8U, PATCH); + int dsize = extended ? 128 : 64; + int k, k1 = range.begin(), k2 = range.end(); - int maxSize = 0; + float maxSize = 0; for( k = k1; k < k2; k++ ) { - maxSize = std::max(maxSize, ((CvSURFPoint*)cvGetSeqElem( keypoints, k ))->size); + maxSize = std::max(maxSize, (*keypoints)[k].size); } maxSize = cvCeil((PATCH_SZ+1)*maxSize*1.2f/9.0f); Ptr winbuf = cvCreateMat( 1, maxSize > 0 ? maxSize*maxSize : 1, CV_8U ); for( k = k1; k < k2; k++ ) { - const int* sum_ptr = sum->data.i; - int sum_cols = sum->cols; int i, j, kk, x, y, nangle; float* vec; - CvSurfHF dx_t[NX], dy_t[NY]; - CvSURFPoint* kp = (CvSURFPoint*)cvGetSeqElem( keypoints, k ); - int size = kp->size; - CvPoint2D32f center = kp->pt; + SurfHF dx_t[NX], dy_t[NY]; + KeyPoint& kp = (*keypoints)[k]; + float size = kp.size; + Point2f center = kp.pt; /* The sampling intervals and wavelet sized for selecting an orientation and building the keypoint descriptor are defined relative to 's' */ - float s = (float)size*1.2f/9.0f; + float s = size*1.2f/9.0f; /* To find the dominant orientation, the gradients in x and y are sampled in a circle of radius 6s using wavelets of size 4s. We ensure the gradient wavelet size is even to ensure the wavelet pattern is balanced and symmetric around its center */ int grad_wav_size = 2*cvRound( 2*s ); - if ( sum->rows < grad_wav_size || sum->cols < grad_wav_size ) + if( sum->rows < grad_wav_size || sum->cols < grad_wav_size ) { /* when grad_wav_size is too big, * the sampling of gradient will be meaningless * mark keypoint for deletion. */ - kp->size = -1; + kp.size = -1; continue; } float descriptor_dir = 90.f; - if (params->upright == 0) + if (upright == 0) { - icvResizeHaarPattern( dx_s, dx_t, NX, 4, grad_wav_size, sum->cols ); - icvResizeHaarPattern( dy_s, dy_t, NY, 4, grad_wav_size, sum->cols ); + resizeHaarPattern( dx_s, dx_t, NX, 4, grad_wav_size, sum->cols ); + resizeHaarPattern( dy_s, dy_t, NY, 4, grad_wav_size, sum->cols ); for( kk = 0, nangle = 0; kk < nOriSamples; kk++ ) { - const int* ptr; - float vx, vy; x = cvRound( center.x + apt[kk].x*s - (float)(grad_wav_size-1)/2 ); y = cvRound( center.y + apt[kk].y*s - (float)(grad_wav_size-1)/2 ); - if( (unsigned)y >= (unsigned)(sum->rows - grad_wav_size) || - (unsigned)x >= (unsigned)(sum->cols - grad_wav_size) ) + if( y < 0 || y >= sum->rows - grad_wav_size || + x < 0 || x >= sum->cols - grad_wav_size ) continue; - ptr = sum_ptr + x + y*sum_cols; - vx = icvCalcHaarPattern( ptr, dx_t, 2 ); - vy = icvCalcHaarPattern( ptr, dy_t, 2 ); - X[nangle] = vx*aptw[kk]; Y[nangle] = vy*aptw[kk]; + const int* ptr = &sum->at(y, x); + float vx = calcHaarPattern( ptr, dx_t, 2 ); + float vy = calcHaarPattern( ptr, dy_t, 2 ); + X[nangle] = vx*aptw[kk]; + Y[nangle] = vy*aptw[kk]; nangle++; } - if ( nangle == 0 ) + if( nangle == 0 ) { - /* No gradient could be sampled because the keypoint is too - * near too one or more of the sides of the image. As we - * therefore cannot find a dominant direction, we skip this - * keypoint and mark it for later deletion from the sequence. */ - kp->size = -1; + // No gradient could be sampled because the keypoint is too + // near too one or more of the sides of the image. As we + // therefore cannot find a dominant direction, we skip this + // keypoint and mark it for later deletion from the sequence. + kp.size = -1; continue; } matX.cols = matY.cols = _angle.cols = nangle; cvCartToPolar( &matX, &matY, 0, &_angle, 1 ); float bestx = 0, besty = 0, descriptor_mod = 0; - for( i = 0; i < 360; i += ORI_SEARCH_INC ) + for( i = 0; i < 360; i += SURF_ORI_SEARCH_INC ) { float sumx = 0, sumy = 0, temp_mod; for( j = 0; j < nangle; j++ ) @@ -686,22 +640,22 @@ struct SURFInvoker besty = sumy; } } - descriptor_dir = cvFastArctan( besty, bestx ); + descriptor_dir = fastAtan2( besty, bestx ); } - kp->dir = descriptor_dir; - if( !descriptors ) + kp.angle = descriptor_dir; + if( !descriptors || !descriptors->data ) continue; /* Extract a window of pixels around the keypoint of size 20s */ int win_size = (int)((PATCH_SZ+1)*s); CV_Assert( winbuf->cols >= win_size*win_size ); - CvMat win = cvMat(win_size, win_size, CV_8U, winbuf->data.ptr); + Mat win(win_size, win_size, CV_8U, winbuf->data.ptr); - if (params->upright == 0) + if( !upright ) { descriptor_dir *= (float)(CV_PI/180); - float sin_dir = sin(descriptor_dir); - float cos_dir = cos(descriptor_dir); + float sin_dir = std::sin(descriptor_dir); + float cos_dir = std::cos(descriptor_dir); /* Subpixel interpolation version (slower). Subpixel not required since the pixels will all get averaged when we scale down to 20 pixels */ @@ -712,11 +666,11 @@ struct SURFInvoker cvGetQuadrangleSubPix( img, &win, &W ); */ - /* Nearest neighbour version (faster) */ + // Nearest neighbour version (faster) float win_offset = -(float)(win_size-1)/2; float start_x = center.x + win_offset*cos_dir + win_offset*sin_dir; float start_y = center.y - win_offset*sin_dir + win_offset*cos_dir; - uchar* WIN = win.data.ptr; + uchar* WIN = win.data; for( i = 0; i < win_size; i++, start_x += sin_dir, start_y += cos_dir ) { float pixel_x = start_x; @@ -725,15 +679,15 @@ struct SURFInvoker { int x = std::min(std::max(cvRound(pixel_x), 0), img->cols-1); int y = std::min(std::max(cvRound(pixel_y), 0), img->rows-1); - WIN[i*win_size + j] = img->data.ptr[y*img->step + x]; + WIN[i*win_size + j] = img->at(y, x); } } } else { - /* extract rect - slightly optimized version of the code above - TODO: find faster code, as this is simply an extract rect operation, - e.g. by using cvGetSubRect, problem is the border processing */ + // extract rect - slightly optimized version of the code above + // TODO: find faster code, as this is simply an extract rect operation, + // e.g. by using cvGetSubRect, problem is the border processing // descriptor_dir == 90 grad // sin_dir == 1 // cos_dir == 0 @@ -741,26 +695,26 @@ struct SURFInvoker float win_offset = -(float)(win_size-1)/2; int start_x = cvRound(center.x + win_offset); int start_y = cvRound(center.y - win_offset); - uchar* WIN = win.data.ptr; + uchar* WIN = win.data; for( i = 0; i < win_size; i++, start_x++ ) { int pixel_x = start_x; int pixel_y = start_y; - for( j=0; jcols-1 ); y = MIN( y, img->rows-1 ); - WIN[i*win_size + j] = img->data.ptr[y*img->step+x]; + WIN[i*win_size + j] = img->at(y, x); } } } - /* Scale the window to size PATCH_SZ so each pixel's size is s. This - makes calculating the gradients with wavelets of size 2s easy */ - cvResize( &win, &_patch, CV_INTER_AREA ); + // Scale the window to size PATCH_SZ so each pixel's size is s. This + // makes calculating the gradients with wavelets of size 2s easy + resize(win, _patch, _patch.size(), 0, 0, INTER_AREA); - /* Calculate gradients in x and y with wavelets of size 2s */ + // Calculate gradients in x and y with wavelets of size 2s for( i = 0; i < PATCH_SZ; i++ ) for( j = 0; j < PATCH_SZ; j++ ) { @@ -771,14 +725,14 @@ struct SURFInvoker DY[i][j] = vy; } - /* Construct the descriptor */ - vec = (float*)cvGetSeqElem( descriptors, k ); - for( kk = 0; kk < (int)(descriptors->elem_size/sizeof(vec[0])); kk++ ) + // Construct the descriptor + vec = descriptors->ptr(k); + for( kk = 0; kk < dsize; kk++ ) vec[kk] = 0; double square_mag = 0; - if( params->extended ) + if( extended ) { - /* 128-bin descriptor */ + // 128-bin descriptor for( i = 0; i < 4; i++ ) for( j = 0; j < 4; j++ ) { @@ -812,7 +766,7 @@ struct SURFInvoker } else { - /* 64-bin descriptor */ + // 64-bin descriptor for( i = 0; i < 4; i++ ) for( j = 0; j < 4; j++ ) { @@ -831,143 +785,40 @@ struct SURFInvoker } } - /* unit vector is essential for contrast invariance */ - vec = (float*)cvGetSeqElem( descriptors, k ); - double scale = 1./(sqrt(square_mag) + DBL_EPSILON); - for( kk = 0; kk < descriptor_size; kk++ ) - vec[kk] = (float)(vec[kk]*scale); + // unit vector is essential for contrast invariance + vec = descriptors->ptr(k); + float scale = (float)(1./(sqrt(square_mag) + DBL_EPSILON)); + for( kk = 0; kk < dsize; kk++ ) + vec[kk] *= scale; } } - /* Parameters */ - const CvSURFParams* params; - const CvMat* img; - const CvMat* sum; - CvSeq* keypoints; - CvSeq* descriptors; + // Parameters + const Mat* img; + const Mat* sum; + vector* keypoints; + Mat* descriptors; + bool extended; + bool upright; - /* Pre-calculated values */ + // Pre-calculated values int nOriSamples; - cv::Ptr apt; - cv::Ptr aptw; - cv::Ptr DW; + vector apt; + vector aptw; + vector DW; }; -const int SURFInvoker::ORI_SEARCH_INC = 5; -const float SURFInvoker::ORI_SIGMA = 2.5f; -const float SURFInvoker::DESC_SIGMA = 3.3f; -} - - -CV_IMPL void -cvExtractSURF( const CvArr* _img, const CvArr* _mask, - CvSeq** _keypoints, CvSeq** _descriptors, - CvMemStorage* storage, CvSURFParams params, - int useProvidedKeyPts) -{ - CvMat *sum = 0, *mask1 = 0, *mask_sum = 0; - - if( _keypoints && !useProvidedKeyPts ) // If useProvidedKeyPts!=0 we'll use current contents of "*_keypoints" - *_keypoints = 0; - if( _descriptors ) - *_descriptors = 0; - - CvSeq *keypoints, *descriptors = 0; - CvMat imghdr, *img = cvGetMat(_img, &imghdr); - CvMat maskhdr, *mask = _mask ? cvGetMat(_mask, &maskhdr) : 0; - - int descriptor_size = params.extended ? 128 : 64; - const int descriptor_data_type = CV_32F; - int i, N; - - CV_Assert(img != 0); - CV_Assert(CV_MAT_TYPE(img->type) == CV_8UC1); - CV_Assert(mask == 0 || (CV_ARE_SIZES_EQ(img,mask) && CV_MAT_TYPE(mask->type) == CV_8UC1)); - CV_Assert(storage != 0); - CV_Assert(params.hessianThreshold >= 0); - CV_Assert(params.nOctaves > 0); - CV_Assert(params.nOctaveLayers > 0); - - sum = cvCreateMat( img->rows+1, img->cols+1, CV_32SC1 ); - cvIntegral( img, sum ); - - // Compute keypoints only if we are not asked for evaluating the descriptors are some given locations: - if (!useProvidedKeyPts) - { - if( mask ) - { - mask1 = cvCreateMat( img->height, img->width, CV_8UC1 ); - mask_sum = cvCreateMat( img->height+1, img->width+1, CV_32SC1 ); - cvMinS( mask, 1, mask1 ); - cvIntegral( mask1, mask_sum ); - } - keypoints = icvFastHessianDetector( sum, mask_sum, storage, ¶ms ); - } - else - { - CV_Assert(useProvidedKeyPts && (_keypoints != 0) && (*_keypoints != 0)); - keypoints = *_keypoints; - } - - N = keypoints->total; - if( _descriptors ) - { - descriptors = cvCreateSeq( 0, sizeof(CvSeq), - descriptor_size*CV_ELEM_SIZE(descriptor_data_type), storage ); - cvSeqPushMulti( descriptors, 0, N ); - } - - - if ( N > 0 ) - { -#ifdef HAVE_TBB - cv::parallel_for(cv::BlockedRange(0, N), - cv::SURFInvoker(¶ms, keypoints, descriptors, img, sum) ); -#else - cv::SURFInvoker invoker(¶ms, keypoints, descriptors, img, sum); - invoker(cv::BlockedRange(0, N)); -#endif - } - - - /* remove keypoints that were marked for deletion */ - for ( i = 0; i < N; i++ ) - { - CvSURFPoint* kp = (CvSURFPoint*)cvGetSeqElem( keypoints, i ); - if ( kp->size == -1 ) - { - cvSeqRemove( keypoints, i ); - if ( _descriptors ) - cvSeqRemove( descriptors, i ); - i--; - N--; - } - } - - if( _keypoints && !useProvidedKeyPts ) - *_keypoints = keypoints; - if( _descriptors ) - *_descriptors = descriptors; - - cvReleaseMat( &sum ); - if (mask1) cvReleaseMat( &mask1 ); - if (mask_sum) cvReleaseMat( &mask_sum ); -} - - -namespace cv -{ SURF::SURF() { hessianThreshold = 100; - extended = 1; - upright = 0; + extended = true; + upright = false; nOctaves = 4; nOctaveLayers = 2; } -SURF::SURF(double _threshold, int _nOctaves, int _nOctaveLayers, bool _extended, bool _upright) +SURF::SURF(double _threshold, bool _extended, bool _upright, int _nOctaves, int _nOctaveLayers) { hessianThreshold = _threshold; extended = _extended; @@ -977,91 +828,154 @@ SURF::SURF(double _threshold, int _nOctaves, int _nOctaveLayers, bool _extended, } int SURF::descriptorSize() const { return extended ? 128 : 64; } +int SURF::descriptorType() const { return CV_32F; } - -static int getPointOctave(const CvSURFPoint& kpt, const CvSURFParams& params) +void SURF::operator()(InputArray imgarg, InputArray maskarg, + CV_OUT vector& keypoints) const { - int octave = 0, layer = 0, best_octave = 0; - float min_diff = FLT_MAX; - for( octave = 1; octave < params.nOctaves; octave++ ) - for( layer = 0; layer < params.nOctaveLayers; layer++ ) + (*this)(imgarg, maskarg, keypoints, noArray(), false); +} + +void SURF::operator()(InputArray _img, InputArray _mask, + CV_OUT vector& keypoints, + OutputArray _descriptors, + bool useProvidedKeypoints) const +{ + Mat img = _img.getMat(), mask = _mask.getMat(), mask1, sum, msum; + bool doDescriptors = _descriptors.needed(); + + CV_Assert(!img.empty() && img.depth() == CV_8U); + if( img.channels() > 1 ) + cvtColor(img, img, COLOR_BGR2GRAY); + + CV_Assert(mask.empty() || (mask.type() == CV_8U && mask.size() == img.size())); + CV_Assert(hessianThreshold >= 0); + CV_Assert(nOctaves > 0); + CV_Assert(nOctaveLayers > 0); + + integral(img, sum, CV_32S); + + // Compute keypoints only if we are not asked for evaluating the descriptors are some given locations: + if( !useProvidedKeypoints ) + { + if( !mask.empty() ) { - float diff = std::abs(kpt.size - (float)((HAAR_SIZE0 + HAAR_SIZE_INC*layer) << octave)); - if( min_diff > diff ) + cv::min(mask, 1, mask1); + integral(mask1, msum, CV_32S); + } + fastHessianDetector( sum, msum, keypoints, nOctaves, nOctaveLayers, hessianThreshold ); + } + + int i, j, N = (int)keypoints.size(); + if( N > 0 ) + { + Mat descriptors; + if( doDescriptors ) + { + _descriptors.create((int)keypoints.size(), (extended ? 128 : 64), CV_32F); + descriptors = _descriptors.getMat(); + } + + parallel_for(BlockedRange(0, N), SURFInvoker(img, sum, keypoints, descriptors, extended, upright) ); + + size_t dsize = descriptors.cols*descriptors.elemSize(); + + // remove keypoints that were marked for deletion + for( i = j = 0; i < N; i++ ) + { + if( keypoints[i].size > 0 ) { - min_diff = diff; - best_octave = octave; - if( min_diff == 0 ) - return best_octave; + if( i > j ) + { + keypoints[j] = keypoints[i]; + if( doDescriptors ) + memcpy( descriptors.ptr(j), descriptors.ptr(i), dsize); + } + j++; } } - return best_octave; -} - - -void SURF::operator()(const Mat& img, const Mat& mask, - vector& keypoints) const -{ - CvMat _img = img, _mask, *pmask = 0; - if( mask.data ) - pmask = &(_mask = mask); - MemStorage storage(cvCreateMemStorage(0)); - Seq kp; - cvExtractSURF(&_img, pmask, &kp.seq, 0, storage, *(const CvSURFParams*)this, 0); - Seq::iterator it = kp.begin(); - size_t i, n = kp.size(); - keypoints.resize(n); - for( i = 0; i < n; i++, ++it ) - { - const CvSURFPoint& kpt = *it; - keypoints[i] = KeyPoint(kpt.pt, (float)kpt.size, kpt.dir, - kpt.hessian, getPointOctave(kpt, *this)); - } -} - -void SURF::operator()(const Mat& img, const Mat& mask, - vector& keypoints, - vector& descriptors, - bool useProvidedKeypoints) const -{ - CvMat _img = img, _mask, *pmask = 0; - if( mask.data ) - pmask = &(_mask = mask); - MemStorage storage(cvCreateMemStorage(0)); - Seq kp; - CvSeq* d = 0; - size_t i, n; - if( useProvidedKeypoints ) - { - kp = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSURFPoint), storage); - n = keypoints.size(); - for( i = 0; i < n; i++ ) + if( N > j ) { - const KeyPoint& kpt = keypoints[i]; - kp.push_back(cvSURFPoint(kpt.pt, 1, cvRound(kpt.size), kpt.angle, kpt.response)); + N = j; + keypoints.resize(N); + if( doDescriptors ) + { + Mat d = descriptors.rowRange(0, N); + d.copyTo(_descriptors); + } } } +} - cvExtractSURF(&_img, pmask, &kp.seq, &d, storage, - *(const CvSURFParams*)this, useProvidedKeypoints); - // input keypoints can be filtered in cvExtractSURF() - if( !useProvidedKeypoints || (useProvidedKeypoints && keypoints.size() != kp.size()) ) +void SURF::detectImpl( const Mat& image, vector& keypoints, const Mat& mask) const +{ + (*this)(image, mask, keypoints, noArray(), false); +} + +void SURF::computeImpl( const Mat& image, vector& keypoints, Mat& descriptors) const +{ + (*this)(image, Mat(), keypoints, descriptors, true); +} + +static Algorithm* createSURF() +{ + return new SURF; +} +static AlgorithmInfo surf_info("Feature2D.SURF", createSURF); + +AlgorithmInfo* SURF::info() const +{ + static volatile bool initialized = false; + if( !initialized ) { - Seq::iterator it = kp.begin(); - size_t i, n = kp.size(); - keypoints.resize(n); - for( i = 0; i < n; i++, ++it ) - { - const CvSURFPoint& kpt = *it; - keypoints[i] = KeyPoint(kpt.pt, (float)kpt.size, kpt.dir, - kpt.hessian, getPointOctave(kpt, *this), - kpt.laplacian); - } + surf_info.addParam(this, "hessianThreshold", hessianThreshold); + surf_info.addParam(this, "nOctaves", nOctaves); + surf_info.addParam(this, "nOctaveLayers", nOctaveLayers); + surf_info.addParam(this, "extended", extended); + surf_info.addParam(this, "upright", upright); + + initialized = true; } - descriptors.resize(d ? d->total*d->elem_size/sizeof(float) : 0); - if(descriptors.size() != 0) - cvCvtSeqToArray(d, &descriptors[0]); + return &surf_info; } - + +/* + + // SurfFeatureDetector + SurfFeatureDetector::SurfFeatureDetector( double hessianThreshold, int octaves, int octaveLayers, bool upright ) + : surf(hessianThreshold, octaves, octaveLayers, false, upright) + {} + + void SurfFeatureDetector::read (const FileNode& fn) + { + double hessianThreshold = fn["hessianThreshold"]; + int octaves = fn["octaves"]; + int octaveLayers = fn["octaveLayers"]; + bool upright = (int)fn["upright"] != 0; + + surf = SURF( hessianThreshold, octaves, octaveLayers, false, upright ); + } + + void SurfFeatureDetector::write (FileStorage& fs) const + { + //fs << "algorithm" << getAlgorithmName (); + + fs << "hessianThreshold" << surf.hessianThreshold; + fs << "octaves" << surf.nOctaves; + fs << "octaveLayers" << surf.nOctaveLayers; + fs << "upright" << surf.upright; + } + + void SurfFeatureDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask ) const + { + Mat grayImage = image; + if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); + + surf(grayImage, mask, keypoints); + } + + + +*/ } diff --git a/modules/features2d/test/test_detectors.cpp b/modules/nonfree/test/test_detectors.cpp similarity index 100% rename from modules/features2d/test/test_detectors.cpp rename to modules/nonfree/test/test_detectors.cpp diff --git a/modules/nonfree/test/test_features2d.cpp b/modules/nonfree/test/test_features2d.cpp new file mode 100644 index 0000000000..290f105100 --- /dev/null +++ b/modules/nonfree/test/test_features2d.cpp @@ -0,0 +1,1029 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" + +using namespace std; +using namespace cv; + +const string FEATURES2D_DIR = "features2d"; +const string DETECTOR_DIR = FEATURES2D_DIR + "/feature_detectors"; +const string DESCRIPTOR_DIR = FEATURES2D_DIR + "/descriptor_extractors"; +const string IMAGE_FILENAME = "tsukuba.png"; + +/****************************************************************************************\ +* Regression tests for feature detectors comparing keypoints. * +\****************************************************************************************/ + +class CV_FeatureDetectorTest : public cvtest::BaseTest +{ +public: + CV_FeatureDetectorTest( const string& _name, const Ptr& _fdetector ) : + name(_name), fdetector(_fdetector) {} + +protected: + bool isSimilarKeypoints( const KeyPoint& p1, const KeyPoint& p2 ); + void compareKeypointSets( const vector& validKeypoints, const vector& calcKeypoints ); + + void emptyDataTest(); + void regressionTest(); // TODO test of detect() with mask + + virtual void run( int ); + + string name; + Ptr fdetector; +}; + +void CV_FeatureDetectorTest::emptyDataTest() +{ + // One image. + Mat image; + vector keypoints; + try + { + fdetector->detect( image, keypoints ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "detect() on empty image must not generate exception (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + if( !keypoints.empty() ) + { + ts->printf( cvtest::TS::LOG, "detect() on empty image must return empty keypoints vector (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + // Several images. + vector images; + vector > keypointCollection; + try + { + fdetector->detect( images, keypointCollection ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "detect() on empty image vector must not generate exception (2).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } +} + +bool CV_FeatureDetectorTest::isSimilarKeypoints( const KeyPoint& p1, const KeyPoint& p2 ) +{ + const float maxPtDif = 1.f; + const float maxSizeDif = 1.f; + const float maxAngleDif = 2.f; + const float maxResponseDif = 0.1f; + + float dist = (float)norm( p1.pt - p2.pt ); + return (dist < maxPtDif && + fabs(p1.size - p2.size) < maxSizeDif && + abs(p1.angle - p2.angle) < maxAngleDif && + abs(p1.response - p2.response) < maxResponseDif && + p1.octave == p2.octave && + p1.class_id == p2.class_id ); +} + +void CV_FeatureDetectorTest::compareKeypointSets( const vector& validKeypoints, const vector& calcKeypoints ) +{ + const float maxCountRatioDif = 0.01f; + + // Compare counts of validation and calculated keypoints. + float countRatio = (float)validKeypoints.size() / (float)calcKeypoints.size(); + if( countRatio < 1 - maxCountRatioDif || countRatio > 1.f + maxCountRatioDif ) + { + ts->printf( cvtest::TS::LOG, "Bad keypoints count ratio (validCount = %d, calcCount = %d).\n", + validKeypoints.size(), calcKeypoints.size() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + int progress = 0, progressCount = (int)(validKeypoints.size() * calcKeypoints.size()); + int badPointCount = 0, commonPointCount = max((int)validKeypoints.size(), (int)calcKeypoints.size()); + for( size_t v = 0; v < validKeypoints.size(); v++ ) + { + int nearestIdx = -1; + float minDist = std::numeric_limits::max(); + + for( size_t c = 0; c < calcKeypoints.size(); c++ ) + { + progress = update_progress( progress, (int)(v*calcKeypoints.size() + c), progressCount, 0 ); + float curDist = (float)norm( calcKeypoints[c].pt - validKeypoints[v].pt ); + if( curDist < minDist ) + { + minDist = curDist; + nearestIdx = (int)c; + } + } + + assert( minDist >= 0 ); + if( !isSimilarKeypoints( validKeypoints[v], calcKeypoints[nearestIdx] ) ) + badPointCount++; + } + ts->printf( cvtest::TS::LOG, "badPointCount = %d; validPointCount = %d; calcPointCount = %d\n", + badPointCount, validKeypoints.size(), calcKeypoints.size() ); + if( badPointCount > 0.9 * commonPointCount ) + { + ts->printf( cvtest::TS::LOG, " - Bad accuracy!\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + return; + } + ts->printf( cvtest::TS::LOG, " - OK\n" ); +} + +void CV_FeatureDetectorTest::regressionTest() +{ + assert( !fdetector.empty() ); + string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; + string resFilename = string(ts->get_data_path()) + DETECTOR_DIR + "/" + string(name) + ".xml.gz"; + + // Read the test image. + Mat image = imread( imgFilename ); + if( image.empty() ) + { + ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + FileStorage fs( resFilename, FileStorage::READ ); + + // Compute keypoints. + vector calcKeypoints; + fdetector->detect( image, calcKeypoints ); + + if( fs.isOpened() ) // Compare computed and valid keypoints. + { + // TODO compare saved feature detector params with current ones + + // Read validation keypoints set. + vector validKeypoints; + read( fs["keypoints"], validKeypoints ); + if( validKeypoints.empty() ) + { + ts->printf( cvtest::TS::LOG, "Keypoints can not be read.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + compareKeypointSets( validKeypoints, calcKeypoints ); + } + else // Write detector parameters and computed keypoints as validation data. + { + fs.open( resFilename, FileStorage::WRITE ); + if( !fs.isOpened() ) + { + ts->printf( cvtest::TS::LOG, "File %s can not be opened to write.\n", resFilename.c_str() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + else + { + fs << "detector_params" << "{"; + fdetector->write( fs ); + fs << "}"; + + write( fs, "keypoints", calcKeypoints ); + } + } +} + +void CV_FeatureDetectorTest::run( int /*start_from*/ ) +{ + if( fdetector.empty() ) + { + ts->printf( cvtest::TS::LOG, "Feature detector is empty.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + emptyDataTest(); + regressionTest(); + + ts->set_failed_test_info( cvtest::TS::OK ); +} + +/****************************************************************************************\ +* Regression tests for descriptor extractors. * +\****************************************************************************************/ +static void writeMatInBin( const Mat& mat, const string& filename ) +{ + FILE* f = fopen( filename.c_str(), "wb"); + if( f ) + { + int type = mat.type(); + fwrite( (void*)&mat.rows, sizeof(int), 1, f ); + fwrite( (void*)&mat.cols, sizeof(int), 1, f ); + fwrite( (void*)&type, sizeof(int), 1, f ); + int dataSize = (int)(mat.step * mat.rows * mat.channels()); + fwrite( (void*)&dataSize, sizeof(int), 1, f ); + fwrite( (void*)mat.data, 1, dataSize, f ); + fclose(f); + } +} + +static Mat readMatFromBin( const string& filename ) +{ + FILE* f = fopen( filename.c_str(), "rb" ); + if( f ) + { + int rows, cols, type, dataSize; + fread( (void*)&rows, sizeof(int), 1, f ); + fread( (void*)&cols, sizeof(int), 1, f ); + fread( (void*)&type, sizeof(int), 1, f ); + fread( (void*)&dataSize, sizeof(int), 1, f ); + + uchar* data = (uchar*)cvAlloc(dataSize); + fread( (void*)data, 1, dataSize, f ); + fclose(f); + + return Mat( rows, cols, type, data ); + } + return Mat(); +} + +template +class CV_DescriptorExtractorTest : public cvtest::BaseTest +{ +public: + typedef typename Distance::ValueType ValueType; + typedef typename Distance::ResultType DistanceType; + + CV_DescriptorExtractorTest( const string _name, DistanceType _maxDist, const Ptr& _dextractor, float _prevTime, + Distance d = Distance() ): + name(_name), maxDist(_maxDist), prevTime(_prevTime), dextractor(_dextractor), distance(d) {} +protected: + virtual void createDescriptorExtractor() {} + + void compareDescriptors( const Mat& validDescriptors, const Mat& calcDescriptors ) + { + if( validDescriptors.size != calcDescriptors.size || validDescriptors.type() != calcDescriptors.type() ) + { + ts->printf(cvtest::TS::LOG, "Valid and computed descriptors matrices must have the same size and type.\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + CV_Assert( DataType::type == validDescriptors.type() ); + + int dimension = validDescriptors.cols; + DistanceType curMaxDist = std::numeric_limits::min(); + for( int y = 0; y < validDescriptors.rows; y++ ) + { + DistanceType dist = distance( validDescriptors.ptr(y), calcDescriptors.ptr(y), dimension ); + if( dist > curMaxDist ) + curMaxDist = dist; + } + + stringstream ss; + ss << "Max distance between valid and computed descriptors " << curMaxDist; + if( curMaxDist < maxDist ) + ss << "." << endl; + else + { + ss << ">" << maxDist << " - bad accuracy!"<< endl; + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + } + ts->printf(cvtest::TS::LOG, ss.str().c_str() ); + } + + void emptyDataTest() + { + assert( !dextractor.empty() ); + + // One image. + Mat image; + vector keypoints; + Mat descriptors; + + try + { + dextractor->compute( image, keypoints, descriptors ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "compute() on empty image and empty keypoints must not generate exception (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + } + + image.create( 50, 50, CV_8UC3 ); + try + { + dextractor->compute( image, keypoints, descriptors ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "compute() on nonempty image and empty keypoints must not generate exception (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + } + + // Several images. + vector images; + vector > keypointsCollection; + vector descriptorsCollection; + try + { + dextractor->compute( images, keypointsCollection, descriptorsCollection ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "compute() on empty images and empty keypoints collection must not generate exception (2).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + } + } + + void regressionTest() + { + assert( !dextractor.empty() ); + + // Read the test image. + string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; + + Mat img = imread( imgFilename ); + if( img.empty() ) + { + ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + vector keypoints; + FileStorage fs( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::READ ); + if( fs.isOpened() ) + { + read( fs.getFirstTopLevelNode(), keypoints ); + + Mat calcDescriptors; + double t = (double)getTickCount(); + dextractor->compute( img, keypoints, calcDescriptors ); + t = getTickCount() - t; + ts->printf(cvtest::TS::LOG, "\nAverage time of computing one descriptor = %g ms (previous time = %g ms).\n", t/((double)cvGetTickFrequency()*1000.)/calcDescriptors.rows, prevTime ); + + if( calcDescriptors.rows != (int)keypoints.size() ) + { + ts->printf( cvtest::TS::LOG, "Count of computed descriptors and keypoints count must be equal.\n" ); + ts->printf( cvtest::TS::LOG, "Count of keypoints is %d.\n", (int)keypoints.size() ); + ts->printf( cvtest::TS::LOG, "Count of computed descriptors is %d.\n", calcDescriptors.rows ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + if( calcDescriptors.cols != dextractor->descriptorSize() || calcDescriptors.type() != dextractor->descriptorType() ) + { + ts->printf( cvtest::TS::LOG, "Incorrect descriptor size or descriptor type.\n" ); + ts->printf( cvtest::TS::LOG, "Expected size is %d.\n", dextractor->descriptorSize() ); + ts->printf( cvtest::TS::LOG, "Calculated size is %d.\n", calcDescriptors.cols ); + ts->printf( cvtest::TS::LOG, "Expected type is %d.\n", dextractor->descriptorType() ); + ts->printf( cvtest::TS::LOG, "Calculated type is %d.\n", calcDescriptors.type() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + // TODO read and write descriptor extractor parameters and check them + Mat validDescriptors = readDescriptors(); + if( !validDescriptors.empty() ) + compareDescriptors( validDescriptors, calcDescriptors ); + else + { + if( !writeDescriptors( calcDescriptors ) ) + { + ts->printf( cvtest::TS::LOG, "Descriptors can not be written.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + } + } + else + { + ts->printf( cvtest::TS::LOG, "Compute and write keypoints.\n" ); + fs.open( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::WRITE ); + if( fs.isOpened() ) + { + SurfFeatureDetector fd; + fd.detect(img, keypoints); + write( fs, "keypoints", keypoints ); + } + else + { + ts->printf(cvtest::TS::LOG, "File for writting keypoints can not be opened.\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + } + } + + void run(int) + { + createDescriptorExtractor(); + if( dextractor.empty() ) + { + ts->printf(cvtest::TS::LOG, "Descriptor extractor is empty.\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + emptyDataTest(); + regressionTest(); + + ts->set_failed_test_info( cvtest::TS::OK ); + } + + virtual Mat readDescriptors() + { + Mat res = readMatFromBin( string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) ); + return res; + } + + virtual bool writeDescriptors( Mat& descs ) + { + writeMatInBin( descs, string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) ); + return true; + } + + string name; + const DistanceType maxDist; + const float prevTime; + Ptr dextractor; + Distance distance; + +private: + CV_DescriptorExtractorTest& operator=(const CV_DescriptorExtractorTest&) { return *this; } +}; + +/*template +class CV_CalonderDescriptorExtractorTest : public CV_DescriptorExtractorTest +{ +public: + CV_CalonderDescriptorExtractorTest( const char* testName, float _normDif, float _prevTime ) : + CV_DescriptorExtractorTest( testName, _normDif, Ptr(), _prevTime ) + {} + +protected: + virtual void createDescriptorExtractor() + { + CV_DescriptorExtractorTest::dextractor = + new CalonderDescriptorExtractor( string(CV_DescriptorExtractorTest::ts->get_data_path()) + + FEATURES2D_DIR + "/calonder_classifier.rtc"); + } +};*/ + +/****************************************************************************************\ +* Algorithmic tests for descriptor matchers * +\****************************************************************************************/ +class CV_DescriptorMatcherTest : public cvtest::BaseTest +{ +public: + CV_DescriptorMatcherTest( const string& _name, const Ptr& _dmatcher, float _badPart ) : + badPart(_badPart), name(_name), dmatcher(_dmatcher) + {} +protected: + static const int dim = 500; + static const int queryDescCount = 300; // must be even number because we split train data in some cases in two + static const int countFactor = 4; // do not change it + const float badPart; + + virtual void run( int ); + void generateData( Mat& query, Mat& train ); + + void emptyDataTest(); + void matchTest( const Mat& query, const Mat& train ); + void knnMatchTest( const Mat& query, const Mat& train ); + void radiusMatchTest( const Mat& query, const Mat& train ); + + string name; + Ptr dmatcher; + +private: + CV_DescriptorMatcherTest& operator=(const CV_DescriptorMatcherTest&) { return *this; } +}; + +void CV_DescriptorMatcherTest::emptyDataTest() +{ + assert( !dmatcher.empty() ); + Mat queryDescriptors, trainDescriptors, mask; + vector trainDescriptorCollection, masks; + vector matches; + vector > vmatches; + + try + { + dmatcher->match( queryDescriptors, trainDescriptors, matches, mask ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + try + { + dmatcher->knnMatch( queryDescriptors, trainDescriptors, vmatches, 2, mask ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + try + { + dmatcher->radiusMatch( queryDescriptors, trainDescriptors, vmatches, 10.f, mask ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + try + { + dmatcher->add( trainDescriptorCollection ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "add() on empty descriptors must not generate exception.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + try + { + dmatcher->match( queryDescriptors, matches, masks ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (2).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + try + { + dmatcher->knnMatch( queryDescriptors, vmatches, 2, masks ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (2).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + try + { + dmatcher->radiusMatch( queryDescriptors, vmatches, 10.f, masks ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (2).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + +} + +void CV_DescriptorMatcherTest::generateData( Mat& query, Mat& train ) +{ + RNG& rng = theRNG(); + + // Generate query descriptors randomly. + // Descriptor vector elements are integer values. + Mat buf( queryDescCount, dim, CV_32SC1 ); + rng.fill( buf, RNG::UNIFORM, Scalar::all(0), Scalar(3) ); + buf.convertTo( query, CV_32FC1 ); + + // Generate train decriptors as follows: + // copy each query descriptor to train set countFactor times + // and perturb some one element of the copied descriptors in + // in ascending order. General boundaries of the perturbation + // are (0.f, 1.f). + train.create( query.rows*countFactor, query.cols, CV_32FC1 ); + float step = 1.f / countFactor; + for( int qIdx = 0; qIdx < query.rows; qIdx++ ) + { + Mat queryDescriptor = query.row(qIdx); + for( int c = 0; c < countFactor; c++ ) + { + int tIdx = qIdx * countFactor + c; + Mat trainDescriptor = train.row(tIdx); + queryDescriptor.copyTo( trainDescriptor ); + int elem = rng(dim); + float diff = rng.uniform( step*c, step*(c+1) ); + trainDescriptor.at(0, elem) += diff; + } + } +} + +void CV_DescriptorMatcherTest::matchTest( const Mat& query, const Mat& train ) +{ + dmatcher->clear(); + + // test const version of match() + { + vector matches; + dmatcher->match( query, train, matches ); + + if( (int)matches.size() != queryDescCount ) + { + ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + else + { + int badCount = 0; + for( size_t i = 0; i < matches.size(); i++ ) + { + DMatch match = matches[i]; + if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor) || (match.imgIdx != 0) ) + badCount++; + } + if( (float)badCount > (float)queryDescCount*badPart ) + { + ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (1).\n", + (float)badCount/(float)queryDescCount ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + } + } + + // test version of match() with add() + { + vector matches; + // make add() twice to test such case + dmatcher->add( vector(1,train.rowRange(0, train.rows/2)) ); + dmatcher->add( vector(1,train.rowRange(train.rows/2, train.rows)) ); + // prepare masks (make first nearest match illegal) + vector masks(2); + for(int mi = 0; mi < 2; mi++ ) + { + masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1)); + for( int di = 0; di < queryDescCount/2; di++ ) + masks[mi].col(di*countFactor).setTo(Scalar::all(0)); + } + + dmatcher->match( query, matches, masks ); + + if( (int)matches.size() != queryDescCount ) + { + ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function (2).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + else + { + int badCount = 0; + for( size_t i = 0; i < matches.size(); i++ ) + { + DMatch match = matches[i]; + int shift = dmatcher->isMaskSupported() ? 1 : 0; + { + if( i < queryDescCount/2 ) + { + if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + shift) || (match.imgIdx != 0) ) + badCount++; + } + else + { + if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + shift) || (match.imgIdx != 1) ) + badCount++; + } + } + } + if( (float)badCount > (float)queryDescCount*badPart ) + { + ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (2).\n", + (float)badCount/(float)queryDescCount ); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + } + } + } +} + +void CV_DescriptorMatcherTest::knnMatchTest( const Mat& query, const Mat& train ) +{ + dmatcher->clear(); + + // test const version of knnMatch() + { + const int knn = 3; + + vector > matches; + dmatcher->knnMatch( query, train, matches, knn ); + + if( (int)matches.size() != queryDescCount ) + { + ts->printf(cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + else + { + int badCount = 0; + for( size_t i = 0; i < matches.size(); i++ ) + { + if( (int)matches[i].size() != knn ) + badCount++; + else + { + int localBadCount = 0; + for( int k = 0; k < knn; k++ ) + { + DMatch match = matches[i][k]; + if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor+k) || (match.imgIdx != 0) ) + localBadCount++; + } + badCount += localBadCount > 0 ? 1 : 0; + } + } + if( (float)badCount > (float)queryDescCount*badPart ) + { + ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (1).\n", + (float)badCount/(float)queryDescCount ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + } + } + + // test version of knnMatch() with add() + { + const int knn = 2; + vector > matches; + // make add() twice to test such case + dmatcher->add( vector(1,train.rowRange(0, train.rows/2)) ); + dmatcher->add( vector(1,train.rowRange(train.rows/2, train.rows)) ); + // prepare masks (make first nearest match illegal) + vector masks(2); + for(int mi = 0; mi < 2; mi++ ) + { + masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1)); + for( int di = 0; di < queryDescCount/2; di++ ) + masks[mi].col(di*countFactor).setTo(Scalar::all(0)); + } + + dmatcher->knnMatch( query, matches, knn, masks ); + + if( (int)matches.size() != queryDescCount ) + { + ts->printf(cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (2).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + else + { + int badCount = 0; + int shift = dmatcher->isMaskSupported() ? 1 : 0; + for( size_t i = 0; i < matches.size(); i++ ) + { + if( (int)matches[i].size() != knn ) + badCount++; + else + { + int localBadCount = 0; + for( int k = 0; k < knn; k++ ) + { + DMatch match = matches[i][k]; + { + if( i < queryDescCount/2 ) + { + if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + k + shift) || + (match.imgIdx != 0) ) + localBadCount++; + } + else + { + if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + k + shift) || + (match.imgIdx != 1) ) + localBadCount++; + } + } + } + badCount += localBadCount > 0 ? 1 : 0; + } + } + if( (float)badCount > (float)queryDescCount*badPart ) + { + ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (2).\n", + (float)badCount/(float)queryDescCount ); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + } + } + } +} + +void CV_DescriptorMatcherTest::radiusMatchTest( const Mat& query, const Mat& train ) +{ + dmatcher->clear(); + // test const version of match() + { + const float radius = 1.f/countFactor; + vector > matches; + dmatcher->radiusMatch( query, train, matches, radius ); + + if( (int)matches.size() != queryDescCount ) + { + ts->printf(cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + else + { + int badCount = 0; + for( size_t i = 0; i < matches.size(); i++ ) + { + if( (int)matches[i].size() != 1 ) + badCount++; + else + { + DMatch match = matches[i][0]; + if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor) || (match.imgIdx != 0) ) + badCount++; + } + } + if( (float)badCount > (float)queryDescCount*badPart ) + { + ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (1).\n", + (float)badCount/(float)queryDescCount ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + } + } + + // test version of match() with add() + { + int n = 3; + const float radius = 1.f/countFactor * n; + vector > matches; + // make add() twice to test such case + dmatcher->add( vector(1,train.rowRange(0, train.rows/2)) ); + dmatcher->add( vector(1,train.rowRange(train.rows/2, train.rows)) ); + // prepare masks (make first nearest match illegal) + vector masks(2); + for(int mi = 0; mi < 2; mi++ ) + { + masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1)); + for( int di = 0; di < queryDescCount/2; di++ ) + masks[mi].col(di*countFactor).setTo(Scalar::all(0)); + } + + dmatcher->radiusMatch( query, matches, radius, masks ); + + int curRes = cvtest::TS::OK; + if( (int)matches.size() != queryDescCount ) + { + ts->printf(cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + int badCount = 0; + int shift = dmatcher->isMaskSupported() ? 1 : 0; + int needMatchCount = dmatcher->isMaskSupported() ? n-1 : n; + for( size_t i = 0; i < matches.size(); i++ ) + { + if( (int)matches[i].size() != needMatchCount ) + badCount++; + else + { + int localBadCount = 0; + for( int k = 0; k < needMatchCount; k++ ) + { + DMatch match = matches[i][k]; + { + if( i < queryDescCount/2 ) + { + if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + k + shift) || + (match.imgIdx != 0) ) + localBadCount++; + } + else + { + if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + k + shift) || + (match.imgIdx != 1) ) + localBadCount++; + } + } + } + badCount += localBadCount > 0 ? 1 : 0; + } + } + if( (float)badCount > (float)queryDescCount*badPart ) + { + curRes = cvtest::TS::FAIL_INVALID_OUTPUT; + ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (2).\n", + (float)badCount/(float)queryDescCount ); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + } + } +} + +void CV_DescriptorMatcherTest::run( int ) +{ + Mat query, train; + generateData( query, train ); + + matchTest( query, train ); + + knnMatchTest( query, train ); + + radiusMatchTest( query, train ); +} + +/****************************************************************************************\ +* Tests registrations * +\****************************************************************************************/ + +/* + * Detectors + */ + + +TEST( Features2d_Detector_SIFT, regression ) +{ + CV_FeatureDetectorTest test( "detector-sift", FeatureDetector::create("SIFT") ); + test.safe_run(); +} + +TEST( Features2d_Detector_SURF, regression ) +{ + CV_FeatureDetectorTest test( "detector-surf", FeatureDetector::create("SURF") ); + test.safe_run(); +} + +/* + * Descriptors + */ +TEST( Features2d_DescriptorExtractor_SIFT, regression ) +{ + CV_DescriptorExtractorTest > test( "descriptor-sift", 0.03f, + DescriptorExtractor::create("SIFT"), 8.06652f ); + test.safe_run(); +} + +TEST( Features2d_DescriptorExtractor_SURF, regression ) +{ + CV_DescriptorExtractorTest > test( "descriptor-surf", 0.035f, + DescriptorExtractor::create("SURF"), 0.147372f ); + test.safe_run(); +} + +/*TEST( Features2d_DescriptorExtractor_OpponentSIFT, regression ) +{ + CV_DescriptorExtractorTest > test( "descriptor-opponent-sift", 0.18f, + DescriptorExtractor::create("OpponentSIFT"), 8.06652f ); + test.safe_run(); +}*/ + +TEST( Features2d_DescriptorExtractor_OpponentSURF, regression ) +{ + CV_DescriptorExtractorTest > test( "descriptor-opponent-surf", 0.18f, + DescriptorExtractor::create("OpponentSURF"), 0.147372f ); + test.safe_run(); +} + +/*#if CV_SSE2 +TEST( Features2d_DescriptorExtractor_Calonder_uchar, regression ) +{ + CV_CalonderDescriptorExtractorTest > test( "descriptor-calonder-uchar", + std::numeric_limits::epsilon() + 1, + 0.0132175f ); + test.safe_run(); +} + +TEST( Features2d_DescriptorExtractor_Calonder_float, regression ) +{ + CV_CalonderDescriptorExtractorTest > test( "descriptor-calonder-float", + std::numeric_limits::epsilon(), + 0.0221308f ); + test.safe_run(); +} +#endif*/ // CV_SSE2 diff --git a/modules/nonfree/test/test_main.cpp b/modules/nonfree/test/test_main.cpp new file mode 100644 index 0000000000..6b24993447 --- /dev/null +++ b/modules/nonfree/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("cv") diff --git a/modules/nonfree/test/test_precomp.cpp b/modules/nonfree/test/test_precomp.cpp new file mode 100644 index 0000000000..5956e13e3e --- /dev/null +++ b/modules/nonfree/test/test_precomp.cpp @@ -0,0 +1 @@ +#include "test_precomp.hpp" diff --git a/modules/nonfree/test/test_precomp.hpp b/modules/nonfree/test/test_precomp.hpp new file mode 100644 index 0000000000..01a3978bcf --- /dev/null +++ b/modules/nonfree/test/test_precomp.hpp @@ -0,0 +1,10 @@ +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/highgui/highgui.hpp" +#include "opencv2/nonfree/nonfree.hpp" +#include + +#endif diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 3211243ef6..e7ce46fdc1 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -586,58 +586,9 @@ public: CV_PROP int nlevels; }; -/****************************************************************************************\ -* Planar Object Detection * -\****************************************************************************************/ - -class CV_EXPORTS PlanarObjectDetector -{ -public: - PlanarObjectDetector(); - PlanarObjectDetector(const FileNode& node); - PlanarObjectDetector(const vector& pyr, int _npoints=300, - int _patchSize=FernClassifier::PATCH_SIZE, - int _nstructs=FernClassifier::DEFAULT_STRUCTS, - int _structSize=FernClassifier::DEFAULT_STRUCT_SIZE, - int _nviews=FernClassifier::DEFAULT_VIEWS, - const LDetector& detector=LDetector(), - const PatchGenerator& patchGenerator=PatchGenerator()); - virtual ~PlanarObjectDetector(); - virtual void train(const vector& pyr, int _npoints=300, - int _patchSize=FernClassifier::PATCH_SIZE, - int _nstructs=FernClassifier::DEFAULT_STRUCTS, - int _structSize=FernClassifier::DEFAULT_STRUCT_SIZE, - int _nviews=FernClassifier::DEFAULT_VIEWS, - const LDetector& detector=LDetector(), - const PatchGenerator& patchGenerator=PatchGenerator()); - virtual void train(const vector& pyr, const vector& keypoints, - int _patchSize=FernClassifier::PATCH_SIZE, - int _nstructs=FernClassifier::DEFAULT_STRUCTS, - int _structSize=FernClassifier::DEFAULT_STRUCT_SIZE, - int _nviews=FernClassifier::DEFAULT_VIEWS, - const LDetector& detector=LDetector(), - const PatchGenerator& patchGenerator=PatchGenerator()); - Rect getModelROI() const; - vector getModelPoints() const; - const LDetector& getDetector() const; - const FernClassifier& getClassifier() const; - void setVerbose(bool verbose); - - void read(const FileNode& node); - void write(FileStorage& fs, const String& name=String()) const; - bool operator()(const Mat& image, CV_OUT Mat& H, CV_OUT vector& corners) const; - bool operator()(const vector& pyr, const vector& keypoints, - CV_OUT Mat& H, CV_OUT vector& corners, - CV_OUT vector* pairs=0) const; - -protected: - bool verbose; - Rect modelROI; - vector modelPoints; - LDetector ldetector; - FernClassifier fernClassifier; -}; + + struct CV_EXPORTS DataMatrixCode { char msg[4]; //TODO std::string Mat original; diff --git a/modules/objdetect/src/planardetect.cpp b/modules/objdetect/src/planardetect.cpp index 3c78531f66..2fcbff7b8e 100644 --- a/modules/objdetect/src/planardetect.cpp +++ b/modules/objdetect/src/planardetect.cpp @@ -46,176 +46,4 @@ namespace cv { -////////////////////////////////////// Planar Object Detector //////////////////////////////////// - -PlanarObjectDetector::PlanarObjectDetector() -{ -} - -PlanarObjectDetector::PlanarObjectDetector(const FileNode& node) -{ - read(node); -} - -PlanarObjectDetector::PlanarObjectDetector(const vector& pyr, int npoints, - int patchSize, int nstructs, int structSize, - int nviews, const LDetector& detector, - const PatchGenerator& patchGenerator) -{ - train(pyr, npoints, patchSize, nstructs, - structSize, nviews, detector, patchGenerator); -} - -PlanarObjectDetector::~PlanarObjectDetector() -{ -} - -vector PlanarObjectDetector::getModelPoints() const -{ - return modelPoints; -} - -void PlanarObjectDetector::train(const vector& pyr, int npoints, - int patchSize, int nstructs, int structSize, - int nviews, const LDetector& detector, - const PatchGenerator& patchGenerator) -{ - modelROI = Rect(0, 0, pyr[0].cols, pyr[0].rows); - ldetector = detector; - ldetector.setVerbose(verbose); - ldetector.getMostStable2D(pyr[0], modelPoints, npoints, patchGenerator); - - npoints = (int)modelPoints.size(); - fernClassifier.setVerbose(verbose); - fernClassifier.trainFromSingleView(pyr[0], modelPoints, - patchSize, (int)modelPoints.size(), nstructs, structSize, nviews, - FernClassifier::COMPRESSION_NONE, patchGenerator); -} - -void PlanarObjectDetector::train(const vector& pyr, const vector& keypoints, - int patchSize, int nstructs, int structSize, - int nviews, const LDetector& detector, - const PatchGenerator& patchGenerator) -{ - modelROI = Rect(0, 0, pyr[0].cols, pyr[0].rows); - ldetector = detector; - ldetector.setVerbose(verbose); - modelPoints.resize(keypoints.size()); - std::copy(keypoints.begin(), keypoints.end(), modelPoints.begin()); - - fernClassifier.setVerbose(verbose); - fernClassifier.trainFromSingleView(pyr[0], modelPoints, - patchSize, (int)modelPoints.size(), nstructs, structSize, nviews, - FernClassifier::COMPRESSION_NONE, patchGenerator); -} - -void PlanarObjectDetector::read(const FileNode& node) -{ - FileNodeIterator it = node["model-roi"].begin(), it_end; - it >> modelROI.x >> modelROI.y >> modelROI.width >> modelROI.height; - ldetector.read(node["detector"]); - fernClassifier.read(node["fern-classifier"]); - cv::read(node["model-points"], modelPoints); - CV_Assert(modelPoints.size() == (size_t)fernClassifier.getClassCount()); -} - - -void PlanarObjectDetector::write(FileStorage& fs, const String& objname) const -{ - WriteStructContext ws(fs, objname, CV_NODE_MAP); - - { - WriteStructContext wsroi(fs, "model-roi", CV_NODE_SEQ + CV_NODE_FLOW); - cv::write(fs, modelROI.x); - cv::write(fs, modelROI.y); - cv::write(fs, modelROI.width); - cv::write(fs, modelROI.height); - } - ldetector.write(fs, "detector"); - cv::write(fs, "model-points", modelPoints); - fernClassifier.write(fs, "fern-classifier"); -} - - -bool PlanarObjectDetector::operator()(const Mat& image, Mat& H, vector& corners) const -{ - vector pyr; - buildPyramid(image, pyr, ldetector.nOctaves - 1); - vector keypoints; - ldetector(pyr, keypoints); - - return (*this)(pyr, keypoints, H, corners); -} - -bool PlanarObjectDetector::operator()(const vector& pyr, const vector& keypoints, - Mat& matH, vector& corners, vector* pairs) const -{ - int i, j, m = (int)modelPoints.size(), n = (int)keypoints.size(); - vector bestMatches(m, -1); - vector maxLogProb(m, -FLT_MAX); - vector signature; - vector fromPt, toPt; - - for( i = 0; i < n; i++ ) - { - KeyPoint kpt = keypoints[i]; - CV_Assert(0 <= kpt.octave && kpt.octave < (int)pyr.size()); - kpt.pt.x /= (float)(1 << kpt.octave); - kpt.pt.y /= (float)(1 << kpt.octave); - int k = fernClassifier(pyr[kpt.octave], kpt.pt, signature); - if( k >= 0 && (bestMatches[k] < 0 || signature[k] > maxLogProb[k]) ) - { - maxLogProb[k] = signature[k]; - bestMatches[k] = i; - } - } - - if(pairs) - pairs->resize(0); - - for( i = 0; i < m; i++ ) - if( bestMatches[i] >= 0 ) - { - fromPt.push_back(modelPoints[i].pt); - toPt.push_back(keypoints[bestMatches[i]].pt); - } - - if( fromPt.size() < 4 ) - return false; - - vector mask; - matH = findHomography(fromPt, toPt, RANSAC, 10, mask); - if( matH.data ) - { - const Mat_& H = matH; - corners.resize(4); - for( i = 0; i < 4; i++ ) - { - Point2f pt((float)(modelROI.x + (i == 0 || i == 3 ? 0 : modelROI.width)), - (float)(modelROI.y + (i <= 1 ? 0 : modelROI.height))); - double w = 1./(H(2,0)*pt.x + H(2,1)*pt.y + H(2,2)); - corners[i] = Point2f((float)((H(0,0)*pt.x + H(0,1)*pt.y + H(0,2))*w), - (float)((H(1,0)*pt.x + H(1,1)*pt.y + H(1,2))*w)); - } - } - - if( pairs ) - { - for( i = j = 0; i < m; i++ ) - if( bestMatches[i] >= 0 && mask[j++] ) - { - pairs->push_back(i); - pairs->push_back(bestMatches[i]); - } - } - - return matH.data != 0; -} - - -void PlanarObjectDetector::setVerbose(bool _verbose) -{ - verbose = _verbose; -} - } diff --git a/modules/photo/CMakeLists.txt b/modules/photo/CMakeLists.txt new file mode 100644 index 0000000000..08a72ea928 --- /dev/null +++ b/modules/photo/CMakeLists.txt @@ -0,0 +1,2 @@ +set(the_description "Computational Photography") +ocv_define_module(photo opencv_imgproc) diff --git a/modules/photo/doc/inpainting.rst b/modules/photo/doc/inpainting.rst new file mode 100644 index 0000000000..3fb81ea1df --- /dev/null +++ b/modules/photo/doc/inpainting.rst @@ -0,0 +1,33 @@ +Inpainting +========== + +.. highlight:: cpp + +inpaint +----------- +Restores the selected region in an image using the region neighborhood. + +.. ocv:function:: void inpaint( InputArray src, InputArray inpaintMask, OutputArray dst, double inpaintRadius, int flags ) + +.. ocv:pyfunction:: cv2.inpaint(src, inpaintMask, inpaintRange, flags[, dst]) -> dst + +.. ocv:cfunction:: void cvInpaint( const CvArr* src, const CvArr* mask, CvArr* dst, double inpaintRadius, int flags) +.. ocv:pyoldfunction:: cv.Inpaint(src, mask, dst, inpaintRadius, flags) -> None + + :param src: Input 8-bit 1-channel or 3-channel image. + + :param inpaintMask: Inpainting mask, 8-bit 1-channel image. Non-zero pixels indicate the area that needs to be inpainted. + + :param dst: Output image with the same size and type as ``src`` . + + :param inpaintRadius: Radius of a circlular neighborhood of each point inpainted that is considered by the algorithm. + + :param flags: Inpainting method that could be one of the following: + + * **INPAINT_NS** Navier-Stokes based method. + + * **INPAINT_TELEA** Method by Alexandru Telea [Telea04]_. + +The function reconstructs the selected image area from the pixel near the area boundary. The function may be used to remove dust and scratches from a scanned photo, or to remove undesirable objects from still images or video. See +http://en.wikipedia.org/wiki/Inpainting +for more details. diff --git a/modules/photo/doc/photo.rst b/modules/photo/doc/photo.rst new file mode 100644 index 0000000000..9d8636ee73 --- /dev/null +++ b/modules/photo/doc/photo.rst @@ -0,0 +1,10 @@ +******************************** +photo. Computational Photography +******************************** + +.. highlight:: cpp + +.. toctree:: + :maxdepth: 2 + + inpainting diff --git a/modules/photo/include/opencv2/photo/photo.hpp b/modules/photo/include/opencv2/photo/photo.hpp new file mode 100644 index 0000000000..dee812bc0d --- /dev/null +++ b/modules/photo/include/opencv2/photo/photo.hpp @@ -0,0 +1,72 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2008-2012, 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_PHOTO_HPP__ +#define __OPENCV_PHOTO_HPP__ + +#include "opencv2/core/core.hpp" +#include "opencv2/imgproc/imgproc.hpp" + +#ifdef __cplusplus + +/*! \namespace cv + Namespace where all the C++ OpenCV functionality resides + */ +namespace cv +{ + +//! the inpainting algorithm +enum +{ + INPAINT_NS=CV_INPAINT_NS, // Navier-Stokes algorithm + INPAINT_TELEA=CV_INPAINT_TELEA // A. Telea algorithm +}; + +//! restores the damaged image areas using one of the available intpainting algorithms +CV_EXPORTS_W void inpaint( InputArray src, InputArray inpaintMask, + OutputArray dst, double inpaintRange, int flags ); + +} + +#endif + +#endif diff --git a/modules/imgproc/perf/perf_inpaint.cpp b/modules/photo/perf/perf_inpaint.cpp similarity index 96% rename from modules/imgproc/perf/perf_inpaint.cpp rename to modules/photo/perf/perf_inpaint.cpp index ca07c7dafb..6f039b4194 100644 --- a/modules/imgproc/perf/perf_inpaint.cpp +++ b/modules/photo/perf/perf_inpaint.cpp @@ -1,38 +1,38 @@ -#include "perf_precomp.hpp" - -using namespace std; -using namespace cv; -using namespace perf; -using std::tr1::make_tuple; -using std::tr1::get; - -CV_ENUM(InpaintingMethod, INPAINT_NS, INPAINT_TELEA) -typedef std::tr1::tuple InpaintArea_InpaintingMethod_t; -typedef perf::TestBaseWithParam InpaintArea_InpaintingMethod; - - -PERF_TEST_P(InpaintArea_InpaintingMethod, inpaint, - testing::Combine( - SZ_ALL_SMALL, - testing::ValuesIn(InpaintingMethod::all()) - ) - ) -{ - Mat src = imread(getDataPath("gpu/hog/road.png")); - - Size sz = get<0>(GetParam()); - int inpaintingMethod = get<1>(GetParam()); - - Mat mask(src.size(), CV_8UC1, Scalar(0)); - Mat result(src.size(), src.type()); - - Rect inpaintArea(src.cols/3, src.rows/3, sz.width, sz.height); - mask(inpaintArea).setTo(255); - - declare.in(src, mask).out(result).time(30); - - TEST_CYCLE() inpaint(src, mask, result, 10.0, inpaintingMethod); - - Mat inpaintedArea = result(inpaintArea); - SANITY_CHECK(inpaintedArea); -} +#include "perf_precomp.hpp" + +using namespace std; +using namespace cv; +using namespace perf; +using std::tr1::make_tuple; +using std::tr1::get; + +CV_ENUM(InpaintingMethod, INPAINT_NS, INPAINT_TELEA) +typedef std::tr1::tuple InpaintArea_InpaintingMethod_t; +typedef perf::TestBaseWithParam InpaintArea_InpaintingMethod; + + +PERF_TEST_P(InpaintArea_InpaintingMethod, inpaint, + testing::Combine( + SZ_ALL_SMALL, + testing::ValuesIn(InpaintingMethod::all()) + ) + ) +{ + Mat src = imread(getDataPath("gpu/hog/road.png")); + + Size sz = get<0>(GetParam()); + int inpaintingMethod = get<1>(GetParam()); + + Mat mask(src.size(), CV_8UC1, Scalar(0)); + Mat result(src.size(), src.type()); + + Rect inpaintArea(src.cols/3, src.rows/3, sz.width, sz.height); + mask(inpaintArea).setTo(255); + + declare.in(src, mask).out(result).time(30); + + TEST_CYCLE() inpaint(src, mask, result, 10.0, inpaintingMethod); + + Mat inpaintedArea = result(inpaintArea); + SANITY_CHECK(inpaintedArea); +} diff --git a/modules/photo/perf/perf_main.cpp b/modules/photo/perf/perf_main.cpp new file mode 100644 index 0000000000..f5863c1974 --- /dev/null +++ b/modules/photo/perf/perf_main.cpp @@ -0,0 +1,3 @@ +#include "perf_precomp.hpp" + +CV_PERF_TEST_MAIN(photo) diff --git a/modules/photo/perf/perf_precomp.cpp b/modules/photo/perf/perf_precomp.cpp new file mode 100644 index 0000000000..8552ac3d42 --- /dev/null +++ b/modules/photo/perf/perf_precomp.cpp @@ -0,0 +1 @@ +#include "perf_precomp.hpp" diff --git a/modules/photo/perf/perf_precomp.hpp b/modules/photo/perf/perf_precomp.hpp new file mode 100644 index 0000000000..02b29450e0 --- /dev/null +++ b/modules/photo/perf/perf_precomp.hpp @@ -0,0 +1,12 @@ +#ifndef __OPENCV_PERF_PRECOMP_HPP__ +#define __OPENCV_PERF_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/photo/photo.hpp" +#include "opencv2/highgui/highgui.hpp" + +#if GTEST_CREATE_SHARED_LIBRARY +#error no modules except ts should have GTEST_CREATE_SHARED_LIBRARY defined +#endif + +#endif diff --git a/modules/imgproc/src/inpaint.cpp b/modules/photo/src/inpaint.cpp similarity index 98% rename from modules/imgproc/src/inpaint.cpp rename to modules/photo/src/inpaint.cpp index 2ca3170d5c..a1f6d8a5df 100644 --- a/modules/imgproc/src/inpaint.cpp +++ b/modules/photo/src/inpaint.cpp @@ -46,6 +46,7 @@ // */ #include "precomp.hpp" +#include "opencv2/imgproc/imgproc_c.h" #undef CV_MAT_ELEM_PTR_FAST #define CV_MAT_ELEM_PTR_FAST( mat, row, col, pix_size ) \ @@ -383,8 +384,7 @@ icvTeleaInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQu } sat = (float)((Ia/s+(Jx+Jy)/(sqrt(Jx*Jx+Jy*Jy)+1.0e-20f)+0.5f)); { - int isat = cvRound(sat); - CV_MAT_3COLOR_ELEM(*out,uchar,i-1,j-1,color) = CV_CAST_8U(isat); + CV_MAT_3COLOR_ELEM(*out,uchar,i-1,j-1,color) = cv::saturate_cast(sat); } } @@ -496,8 +496,7 @@ icvTeleaInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQu } sat = (float)((Ia/s+(Jx+Jy)/(sqrt(Jx*Jx+Jy*Jy)+1.0e-20f)+0.5f)); { - int isat = cvRound(sat); - CV_MAT_ELEM(*out,uchar,i-1,j-1) = CV_CAST_8U(isat); + CV_MAT_ELEM(*out,uchar,i-1,j-1) = cv::saturate_cast(sat); } } @@ -594,10 +593,7 @@ icvNSInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQueue } } } - { - int out_val = cvRound((double)Ia/s); - CV_MAT_3COLOR_ELEM(*out,uchar,i-1,j-1,color) = CV_CAST_8U(out_val); - } + CV_MAT_3COLOR_ELEM(*out,uchar,i-1,j-1,color) = cv::saturate_cast((double)Ia/s); } CV_MAT_ELEM(*f,uchar,i,j) = BAND; @@ -685,10 +681,7 @@ icvNSInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQueue } } } - { - int out_val = cvRound((double)Ia/s); - CV_MAT_ELEM(*out,uchar,i-1,j-1) = CV_CAST_8U(out_val); - } + CV_MAT_ELEM(*out,uchar,i-1,j-1) = cv::saturate_cast((double)Ia/s); } CV_MAT_ELEM(*f,uchar,i,j) = BAND; @@ -724,7 +717,7 @@ icvNSInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQueue } -CV_IMPL void +void cvInpaint( const CvArr* _input_img, const CvArr* _inpaint_mask, CvArr* _output_img, double inpaintRange, int flags ) { diff --git a/modules/photo/src/precomp.cpp b/modules/photo/src/precomp.cpp new file mode 100644 index 0000000000..3e0ec42de9 --- /dev/null +++ b/modules/photo/src/precomp.cpp @@ -0,0 +1,44 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" + +/* End of file. */ diff --git a/modules/photo/src/precomp.hpp b/modules/photo/src/precomp.hpp new file mode 100644 index 0000000000..2bf3705e91 --- /dev/null +++ b/modules/photo/src/precomp.hpp @@ -0,0 +1,56 @@ +/*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_PRECOMP_H__ +#define __OPENCV_PRECOMP_H__ + +#if _MSC_VER >= 1200 +#pragma warning( disable: 4251 4512 4710 4711 4514 4996 ) +#endif + +#ifdef HAVE_CVCONFIG_H +#include "cvconfig.h" +#endif + +#include "opencv2/photo/photo.hpp" + +#endif diff --git a/modules/imgproc/test/test_inpaint.cpp b/modules/photo/test/test_inpaint.cpp similarity index 100% rename from modules/imgproc/test/test_inpaint.cpp rename to modules/photo/test/test_inpaint.cpp diff --git a/modules/photo/test/test_main.cpp b/modules/photo/test/test_main.cpp new file mode 100644 index 0000000000..6b24993447 --- /dev/null +++ b/modules/photo/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("cv") diff --git a/modules/photo/test/test_precomp.cpp b/modules/photo/test/test_precomp.cpp new file mode 100644 index 0000000000..5956e13e3e --- /dev/null +++ b/modules/photo/test/test_precomp.cpp @@ -0,0 +1 @@ +#include "test_precomp.hpp" diff --git a/modules/photo/test/test_precomp.hpp b/modules/photo/test/test_precomp.hpp new file mode 100644 index 0000000000..215b5e5c83 --- /dev/null +++ b/modules/photo/test/test_precomp.hpp @@ -0,0 +1,9 @@ +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include "opencv2/photo/photo.hpp" +#include "opencv2/highgui/highgui.hpp" +#include + +#endif diff --git a/modules/python/CMakeLists.txt b/modules/python/CMakeLists.txt index aa02db0b10..6a9e43310f 100644 --- a/modules/python/CMakeLists.txt +++ b/modules/python/CMakeLists.txt @@ -10,7 +10,7 @@ if(ANDROID OR IOS OR NOT PYTHONLIBS_FOUND OR NOT PYTHON_USE_NUMPY) endif() set(the_description "The python bindings") -ocv_add_module(python BINDINGS opencv_core opencv_flann opencv_imgproc opencv_video opencv_ml opencv_features2d opencv_highgui opencv_calib3d opencv_objdetect opencv_legacy opencv_contrib) +ocv_add_module(python BINDINGS opencv_core opencv_flann opencv_imgproc opencv_video opencv_ml opencv_features2d opencv_highgui opencv_calib3d opencv_photo opencv_nonfree opencv_objdetect opencv_legacy opencv_contrib) ocv_include_directories(${PYTHON_INCLUDE_PATH}) ocv_include_directories( @@ -19,12 +19,14 @@ ocv_include_directories( "${OpenCV_SOURCE_DIR}/modules/flann/include" "${OpenCV_SOURCE_DIR}/modules/imgproc/include" "${OpenCV_SOURCE_DIR}/modules/video/include" + "${OpenCV_SOURCE_DIR}/modules/photo/include" "${OpenCV_SOURCE_DIR}/modules/highgui/include" "${OpenCV_SOURCE_DIR}/modules/ml/include" "${OpenCV_SOURCE_DIR}/modules/features2d/include" "${OpenCV_SOURCE_DIR}/modules/flann/include" "${OpenCV_SOURCE_DIR}/modules/calib3d/include" "${OpenCV_SOURCE_DIR}/modules/objdetect/include" + "${OpenCV_SOURCE_DIR}/modules/nonfree/include" "${OpenCV_SOURCE_DIR}/modules/legacy/include" "${OpenCV_SOURCE_DIR}/modules/contrib/include" ) @@ -36,9 +38,11 @@ set(opencv_hdrs "${OpenCV_SOURCE_DIR}/modules/core/include/opencv2/core/core.hpp "${OpenCV_SOURCE_DIR}/modules/imgproc/include/opencv2/imgproc/imgproc.hpp" "${OpenCV_SOURCE_DIR}/modules/video/include/opencv2/video/background_segm.hpp" "${OpenCV_SOURCE_DIR}/modules/video/include/opencv2/video/tracking.hpp" + "${OpenCV_SOURCE_DIR}/modules/photo/include/opencv2/photo/photo.hpp" "${OpenCV_SOURCE_DIR}/modules/highgui/include/opencv2/highgui/highgui.hpp" "${OpenCV_SOURCE_DIR}/modules/ml/include/opencv2/ml/ml.hpp" "${OpenCV_SOURCE_DIR}/modules/features2d/include/opencv2/features2d/features2d.hpp" + "${OpenCV_SOURCE_DIR}/modules/nonfree/include/opencv2/nonfree/features2d.hpp" "${OpenCV_SOURCE_DIR}/modules/calib3d/include/opencv2/calib3d/calib3d.hpp" "${OpenCV_SOURCE_DIR}/modules/objdetect/include/opencv2/objdetect/objdetect.hpp") @@ -71,7 +75,7 @@ add_custom_command( set(cv2_target "opencv_python") add_library(${cv2_target} SHARED src2/cv2.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated0.i ${cv2_generated_hdrs} src2/cv2.cv.hpp) -target_link_libraries(${cv2_target} ${PYTHON_LIBRARIES} opencv_core opencv_flann opencv_imgproc opencv_video opencv_ml opencv_features2d opencv_highgui opencv_calib3d opencv_objdetect opencv_legacy opencv_contrib) +target_link_libraries(${cv2_target} ${PYTHON_LIBRARIES} opencv_core opencv_flann opencv_imgproc opencv_video opencv_ml opencv_features2d opencv_highgui opencv_calib3d opencv_objdetect opencv_legacy opencv_contrib opencv_photo) set_target_properties(${cv2_target} PROPERTIES PREFIX "") set_target_properties(${cv2_target} PROPERTIES OUTPUT_NAME "cv2") diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index b50eb2d79d..2fdc4097f7 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -17,6 +17,8 @@ #include "opencv2/objdetect/objdetect.hpp" #include "opencv2/video/tracking.hpp" #include "opencv2/video/background_segm.hpp" +#include "opencv2/photo/photo.hpp" +#include "opencv2/nonfree/nonfree.hpp" #include "opencv2/highgui/highgui.hpp" using cv::flann::IndexParams; diff --git a/modules/refman.rst b/modules/refman.rst index 394f616f78..e9c08b8f37 100644 --- a/modules/refman.rst +++ b/modules/refman.rst @@ -16,5 +16,7 @@ OpenCV API Reference ml/doc/ml.rst flann/doc/flann.rst gpu/doc/gpu.rst + photo/doc/photo.rst stitching/doc/stitching.rst + nonfree/doc/nonfree.rst diff --git a/modules/stitching/include/opencv2/stitching/detail/matchers.hpp b/modules/stitching/include/opencv2/stitching/detail/matchers.hpp index 9ae5760bde..5d77ad996e 100644 --- a/modules/stitching/include/opencv2/stitching/detail/matchers.hpp +++ b/modules/stitching/include/opencv2/stitching/detail/matchers.hpp @@ -87,13 +87,13 @@ private: Ptr detector_; Ptr extractor_; - Ptr surf; + Ptr surf; }; class CV_EXPORTS OrbFeaturesFinder : public FeaturesFinder { public: - OrbFeaturesFinder(Size _grid_size = Size(3,1), size_t n_features = 1500, const ORB::CommonParams & detector_params = ORB::CommonParams(1.3f, 5)); + OrbFeaturesFinder(Size _grid_size = Size(3,1), int nfeatures=1500, float scaleFactor=1.3f, int nlevels=5); private: void find(const Mat &image, ImageFeatures &features); diff --git a/modules/stitching/src/matchers.cpp b/modules/stitching/src/matchers.cpp index 9b6759b04c..048de6ce28 100644 --- a/modules/stitching/src/matchers.cpp +++ b/modules/stitching/src/matchers.cpp @@ -315,12 +315,27 @@ SurfFeaturesFinder::SurfFeaturesFinder(double hess_thresh, int num_octaves, int { if (num_octaves_descr == num_octaves && num_layers_descr == num_layers) { - surf = new SURF(hess_thresh, num_octaves, num_layers); + surf = Algorithm::create("Feature2D.SURF"); + if( surf.empty() ) + CV_Error( CV_StsNotImplemented, "OpenCV was built without SURF support" ); + surf->set("hessianThreshold", hess_thresh); + surf->set("nOctaves", num_octaves); + surf->set("nOctaveLayers", num_layers); } else { - detector_ = new SurfFeatureDetector(hess_thresh, num_octaves, num_layers); - extractor_ = new SurfDescriptorExtractor(num_octaves_descr, num_layers_descr); + detector_ = Algorithm::create("Feature2D.SURF"); + extractor_ = Algorithm::create("Feature2D.SURF"); + + if( detector_.empty() || extractor_.empty() ) + CV_Error( CV_StsNotImplemented, "OpenCV was built without SURF support" ); + + detector_->set("hessianThreshold", hess_thresh); + detector_->set("nOctaves", num_octaves); + detector_->set("nOctaveLayers", num_layers); + + extractor_->set("nOctaves", num_octaves_descr); + extractor_->set("nOctaveLayers", num_layers_descr); } } @@ -342,10 +357,10 @@ void SurfFeaturesFinder::find(const Mat &image, ImageFeatures &features) } } -OrbFeaturesFinder::OrbFeaturesFinder(Size _grid_size, size_t n_features, const ORB::CommonParams & detector_params) +OrbFeaturesFinder::OrbFeaturesFinder(Size _grid_size, int n_features, float scaleFactor, int nlevels) { grid_size = _grid_size; - orb = new ORB(n_features * (99 + grid_size.area())/100/grid_size.area(), detector_params); + orb = new ORB(n_features * (99 + grid_size.area())/100/grid_size.area(), scaleFactor, nlevels); } void OrbFeaturesFinder::find(const Mat &image, ImageFeatures &features) diff --git a/modules/video/include/opencv2/video/background_segm.hpp b/modules/video/include/opencv2/video/background_segm.hpp index 717d42c824..6a1b3131fa 100644 --- a/modules/video/include/opencv2/video/background_segm.hpp +++ b/modules/video/include/opencv2/video/background_segm.hpp @@ -416,7 +416,7 @@ public: //! the default constructor BackgroundSubtractorMOG2(); //! the full constructor that takes the length of the history, the number of gaussian mixtures, the background ratio parameter and the noise strength - BackgroundSubtractorMOG2(int history, float varThreshold, bool bShadowDetection=1); + BackgroundSubtractorMOG2(int history, float varThreshold, bool bShadowDetection=true); //! the destructor virtual ~BackgroundSubtractorMOG2(); //! the update operator diff --git a/samples/c/CMakeLists.txt b/samples/c/CMakeLists.txt index 572f32581b..68ae9be435 100644 --- a/samples/c/CMakeLists.txt +++ b/samples/c/CMakeLists.txt @@ -3,8 +3,9 @@ # # ---------------------------------------------------------------------------- -SET(OPENCV_C_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) +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_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 693e16b364..f285981b24 100644 --- a/samples/c/find_obj.cpp +++ b/samples/c/find_obj.cpp @@ -8,7 +8,10 @@ #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 @@ -214,6 +217,7 @@ int main(int argc, char** argv) const char* object_filename = argc == 3 ? argv[1] : "box.png"; const char* scene_filename = argc == 3 ? argv[2] : "box_in_scene.png"; + cv::initModule_nonfree(); help(); IplImage* object = cvLoadImage( object_filename, CV_LOAD_IMAGE_GRAYSCALE ); diff --git a/samples/c/find_obj_calonder.cpp b/samples/c/find_obj_calonder.cpp index ec2f87e08b..25eb136fbc 100644 --- a/samples/c/find_obj_calonder.cpp +++ b/samples/c/find_obj_calonder.cpp @@ -2,6 +2,8 @@ #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 @@ -123,7 +125,7 @@ void testCalonderClassifier( const string& classifierFilename, const string& img Mat descriptors2; de.compute( img2, keypoints2, descriptors2 ); // Match descriptors - BruteForceMatcher > matcher; + BFMatcher matcher(NORM_L1); vector matches; matcher.match( descriptors1, descriptors2, matches ); diff --git a/samples/c/find_obj_ferns.cpp b/samples/c/find_obj_ferns.cpp index efb6670525..81ebe1db45 100644 --- a/samples/c/find_obj_ferns.cpp +++ b/samples/c/find_obj_ferns.cpp @@ -3,6 +3,7 @@ #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/features2d/features2d.hpp" #include "opencv2/objdetect/objdetect.hpp" +#include "opencv2/legacy/legacy.hpp" #include #include diff --git a/samples/c/mser_sample.cpp b/samples/c/mser_sample.cpp index 9aa0e33461..32204df768 100644 --- a/samples/c/mser_sample.cpp +++ b/samples/c/mser_sample.cpp @@ -1,134 +1,86 @@ /* This sample code was originally provided by Liu Liu - * Copyright� 2009, Liu Liu All rights reserved. + * Copyright (C) 2009, Liu Liu All rights reserved. */ #include "opencv2/highgui/highgui.hpp" #include "opencv2/features2d/features2d.hpp" -#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/imgproc/imgproc.hpp" +#include #include +using namespace cv; +using namespace std; + void help() { - printf("\nThis program demonstrates the Maximal Extremal Region interest point detector.\n" - "It finds the most stable (in size) dark and white regions as a threshold is increased.\n" - "\nCall:\n" - "./mser_sample \n\n"); + cout << "\nThis program demonstrates the Maximal Extremal Region interest point detector.\n" + "It finds the most stable (in size) dark and white regions as a threshold is increased.\n" + "\nCall:\n" + "./mser_sample \n\n"; } -static CvScalar colors[] = +static const Vec3b bcolors[] = { - {{0,0,255}}, - {{0,128,255}}, - {{0,255,255}}, - {{0,255,0}}, - {{255,128,0}}, - {{255,255,0}}, - {{255,0,0}}, - {{255,0,255}}, - {{255,255,255}}, - {{196,255,255}}, - {{255,255,196}} + Vec3b(0,0,255), + Vec3b(0,128,255), + Vec3b(0,255,255), + Vec3b(0,255,0), + Vec3b(255,128,0), + Vec3b(255,255,0), + Vec3b(255,0,0), + Vec3b(255,0,255), + Vec3b(255,255,255) }; -static uchar bcolors[][3] = -{ - {0,0,255}, - {0,128,255}, - {0,255,255}, - {0,255,0}, - {255,128,0}, - {255,255,0}, - {255,0,0}, - {255,0,255}, - {255,255,255} -}; - - int main( int argc, char** argv ) { - char path[1024]; - IplImage* img; + string path; + Mat img0, img, yuv, gray, ellipses; help(); - if (argc!=2) + + img0 = imread( argc != 2 ? "puzzle.png" : argv[1], 1 ); + if( img0.empty() ) + { + if( argc != 2 ) + cout << "\nUsage: mser_sample \n"; + else + cout << "Unable to load image " << argv[1] << endl; + return 0; + } + + cvtColor(img0, yuv, COLOR_BGR2YCrCb); + cvtColor(img0, gray, COLOR_BGR2GRAY); + cvtColor(gray, img, COLOR_GRAY2BGR); + img.copyTo(ellipses); + + vector > contours; + double t = (double)getTickCount(); + MSER()(yuv, contours); + t = (double)getTickCount() - t; + printf( "MSER extracted %d contours in %g ms.\n", (int)contours.size(), + t*1000./getTickFrequency() ); + + // draw mser's with different colors + for( int i = (int)contours.size()-1; i >= 0; i-- ) { - strcpy(path,"puzzle.png"); - img = cvLoadImage( path, CV_LOAD_IMAGE_GRAYSCALE ); - if (!img) + const vector& r = contours[i]; + for ( int j = 0; j < (int)r.size(); j++ ) { - printf("\nUsage: mser_sample \n"); - return 0; + Point pt = r[j]; + img.at(r[j]) = bcolors[i%9]; } + + // find ellipse (it seems cvfitellipse2 have error or sth?) + RotatedRect box = fitEllipse( r ); + + box.angle=(float)CV_PI/2-box.angle; + ellipse( ellipses, box, Scalar(196,255,255), 2 ); } - else - { - strcpy(path,argv[1]); - img = cvLoadImage( path, CV_LOAD_IMAGE_GRAYSCALE ); - } - - if (!img) - { - printf("Unable to load image %s\n",path); - return 0; - } - IplImage* rsp = cvLoadImage( path, CV_LOAD_IMAGE_COLOR ); - IplImage* ellipses = cvCloneImage(rsp); - cvCvtColor(img,ellipses,CV_GRAY2BGR); - CvSeq* contours; - CvMemStorage* storage= cvCreateMemStorage(); - IplImage* hsv = cvCreateImage( cvGetSize( rsp ), IPL_DEPTH_8U, 3 ); - cvCvtColor( rsp, hsv, CV_BGR2YCrCb ); - CvMSERParams params = cvMSERParams();//cvMSERParams( 5, 60, cvRound(.2*img->width*img->height), .25, .2 ); - - double t = (double)cvGetTickCount(); - cvExtractMSER( hsv, NULL, &contours, storage, params ); - t = cvGetTickCount() - t; - printf( "MSER extracted %d contours in %g ms.\n", contours->total, t/((double)cvGetTickFrequency()*1000.) ); - uchar* rsptr = (uchar*)rsp->imageData; - // draw mser with different color - for ( int i = contours->total-1; i >= 0; i-- ) - { - CvSeq* r = *(CvSeq**)cvGetSeqElem( contours, i ); - for ( int j = 0; j < r->total; j++ ) - { - CvPoint* pt = CV_GET_SEQ_ELEM( CvPoint, r, j ); - rsptr[pt->x*3+pt->y*rsp->widthStep] = bcolors[i%9][2]; - rsptr[pt->x*3+1+pt->y*rsp->widthStep] = bcolors[i%9][1]; - rsptr[pt->x*3+2+pt->y*rsp->widthStep] = bcolors[i%9][0]; - } - } - // find ellipse ( it seems cvfitellipse2 have error or sth? - for ( int i = 0; i < contours->total; i++ ) - { - CvContour* r = *(CvContour**)cvGetSeqElem( contours, i ); - CvBox2D box = cvFitEllipse2( r ); - box.angle=(float)CV_PI/2-box.angle; - - if ( r->color > 0 ) - cvEllipseBox( ellipses, box, colors[9], 2 ); - else - cvEllipseBox( ellipses, box, colors[2], 2 ); - - } - - cvSaveImage( "rsp.png", rsp ); - - cvNamedWindow( "original", 0 ); - cvShowImage( "original", img ); - - cvNamedWindow( "response", 0 ); - cvShowImage( "response", rsp ); - - cvNamedWindow( "ellipses", 0 ); - cvShowImage( "ellipses", ellipses ); - - cvWaitKey(0); - - cvDestroyWindow( "original" ); - cvDestroyWindow( "response" ); - cvDestroyWindow( "ellipses" ); - cvReleaseImage(&rsp); - cvReleaseImage(&img); - cvReleaseImage(&ellipses); - + + imshow( "original", img0 ); + imshow( "response", img ); + imshow( "ellipses", ellipses ); + + waitKey(0); } diff --git a/samples/c/one_way_sample.cpp b/samples/c/one_way_sample.cpp index 8907cbfde4..fdbe1f74bc 100644 --- a/samples/c/one_way_sample.cpp +++ b/samples/c/one_way_sample.cpp @@ -11,6 +11,9 @@ #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 @@ -25,8 +28,8 @@ void help() using namespace cv; -IplImage* DrawCorrespondences(IplImage* img1, const vector& features1, IplImage* img2, - const vector& features2, const vector& desc_idx); +Mat DrawCorrespondences(const Mat& img1, const vector& features1, const Mat& img2, + const vector& features2, const vector& desc_idx); int main(int argc, char** argv) { @@ -45,8 +48,8 @@ int main(int argc, char** argv) std::string img2_name = path_name + "/" + std::string(argv[3]); printf("Reading the images...\n"); - IplImage* img1 = cvLoadImage(img1_name.c_str(), CV_LOAD_IMAGE_GRAYSCALE); - IplImage* img2 = cvLoadImage(img2_name.c_str(), CV_LOAD_IMAGE_GRAYSCALE); + Mat img1 = imread(img1_name, CV_LOAD_IMAGE_GRAYSCALE); + Mat img2 = imread(img2_name, CV_LOAD_IMAGE_GRAYSCALE); // extract keypoints from the first image SURF surf_extractor(5.0e3); @@ -61,7 +64,9 @@ int main(int argc, char** argv) // create descriptors OneWayDescriptorBase descriptors(patch_size, pose_count, OneWayDescriptorBase::GetPCAFilename(), path_name, images_list); - descriptors.CreateDescriptorsFromImage(img1, keypoints1); + IplImage img1_c = img1; + IplImage img2_c = img2; + descriptors.CreateDescriptorsFromImage(&img1_c, keypoints1); printf("done\n"); // extract keypoints from the second image @@ -77,43 +82,37 @@ int main(int argc, char** argv) { int pose_idx = 0; float distance = 0; - descriptors.FindDescriptor(img2, keypoints2[i].pt, desc_idx[i], pose_idx, distance); + descriptors.FindDescriptor(&img2_c, keypoints2[i].pt, desc_idx[i], pose_idx, distance); } printf("done\n"); - IplImage* img_corr = DrawCorrespondences(img1, keypoints1, img2, keypoints2, desc_idx); + Mat img_corr = DrawCorrespondences(img1, keypoints1, img2, keypoints2, desc_idx); - cvNamedWindow("correspondences", 1); - cvShowImage("correspondences", img_corr); - cvWaitKey(0); - - cvReleaseImage(&img1); - cvReleaseImage(&img2); - cvReleaseImage(&img_corr); + imshow("correspondences", img_corr); + waitKey(0); } -IplImage* DrawCorrespondences(IplImage* img1, const vector& features1, IplImage* img2, - const vector& features2, const vector& desc_idx) +Mat DrawCorrespondences(const Mat& img1, const vector& features1, const Mat& img2, + const vector& features2, const vector& desc_idx) { - IplImage* img_corr = cvCreateImage(cvSize(img1->width + img2->width, MAX(img1->height, img2->height)), - IPL_DEPTH_8U, 3); - cvSetImageROI(img_corr, cvRect(0, 0, img1->width, img1->height)); - cvCvtColor(img1, img_corr, CV_GRAY2RGB); - cvSetImageROI(img_corr, cvRect(img1->width, 0, img2->width, img2->height)); - cvCvtColor(img2, img_corr, CV_GRAY2RGB); - cvResetImageROI(img_corr); - + Mat part, img_corr(Size(img1.cols + img2.cols, MAX(img1.rows, img2.rows)), CV_8UC3); + img_corr = Scalar::all(0); + part = img_corr(Rect(0, 0, img1.cols, img1.rows)); + cvtColor(img1, part, COLOR_GRAY2RGB); + part = img_corr(Rect(img1.cols, 0, img2.cols, img2.rows)); + cvtColor(img1, part, COLOR_GRAY2RGB); + for (size_t i = 0; i < features1.size(); i++) { - cvCircle(img_corr, features1[i].pt, 3, CV_RGB(255, 0, 0)); + circle(img_corr, features1[i].pt, 3, CV_RGB(255, 0, 0)); } - + for (size_t i = 0; i < features2.size(); i++) { - CvPoint pt = cvPoint((int)features2[i].pt.x + img1->width, (int)features2[i].pt.y); - cvCircle(img_corr, pt, 3, CV_RGB(255, 0, 0)); - cvLine(img_corr, features1[desc_idx[i]].pt, pt, CV_RGB(0, 255, 0)); + Point pt((int)features2[i].pt.x + img1.cols, (int)features2[i].pt.y); + circle(img_corr, pt, 3, Scalar(0, 0, 255)); + line(img_corr, features1[desc_idx[i]].pt, pt, Scalar(0, 255, 0)); } - + return img_corr; } diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index 5e85871689..69ac690ae7 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -3,8 +3,9 @@ # # ---------------------------------------------------------------------------- -SET(OPENCV_CPP_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_stitching) +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) ocv_check_dependencies(${OPENCV_CPP_SAMPLES_REQUIRED_DEPS}) diff --git a/samples/cpp/brief_match_test.cpp b/samples/cpp/brief_match_test.cpp index 8a6574a7d3..67d17648c8 100644 --- a/samples/cpp/brief_match_test.cpp +++ b/samples/cpp/brief_match_test.cpp @@ -105,7 +105,7 @@ int main(int argc, const char ** argv) //Do matching using features2d cout << "matching with BruteForceMatcher" << endl; - BruteForceMatcher matcher_popcount; + BFMatcher matcher_popcount(NORM_HAMMING); vector matches_popcount; double pop_time = match(kpts_1, kpts_2, matcher_popcount, desc_1, desc_2, matches_popcount); cout << "done BruteForceMatcher matching. took " << pop_time << " seconds" << endl; diff --git a/samples/cpp/detector_descriptor_evaluation.cpp b/samples/cpp/detector_descriptor_evaluation.cpp index f75bef7d6b..11f4f3ca2f 100644 --- a/samples/cpp/detector_descriptor_evaluation.cpp +++ b/samples/cpp/detector_descriptor_evaluation.cpp @@ -42,6 +42,7 @@ #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/features2d/features2d.hpp" +#include "opencv2/legacy/legacy.hpp" #include #include @@ -879,7 +880,7 @@ public: { string classifierFile = data_path + "/features2d/calonder_classifier.rtc"; defaultDescMatcher = new VectorDescriptorMatch( new CalonderDescriptorExtractor( classifierFile ), - new BruteForceMatcher > ); + new BFMatcher(NORM_L2) ); specificDescMatcher = defaultDescMatcher; } }; diff --git a/samples/cpp/generic_descriptor_match.cpp b/samples/cpp/generic_descriptor_match.cpp index 6b804acd1c..eeab4d77d0 100644 --- a/samples/cpp/generic_descriptor_match.cpp +++ b/samples/cpp/generic_descriptor_match.cpp @@ -1,7 +1,8 @@ #include "opencv2/calib3d/calib3d.hpp" #include "opencv2/features2d/features2d.hpp" #include "opencv2/highgui/highgui.hpp" -#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/nonfree/nonfree.hpp" #include @@ -14,8 +15,8 @@ void help() printf("For example: ./generic_descriptor_match ../c/scene_l.bmp ../c/scene_r.bmp FERN fern_params.xml\n"); } -IplImage* DrawCorrespondences(IplImage* img1, const vector& features1, IplImage* img2, - const vector& features2, const vector& desc_idx); +Mat DrawCorrespondences(const Mat& img1, const vector& features1, const Mat& img2, + const vector& features2, const vector& desc_idx); int main(int argc, char** argv) { @@ -38,8 +39,8 @@ int main(int argc, char** argv) } //printf("Reading the images...\n"); - IplImage* img1 = cvLoadImage(img1_name.c_str(), CV_LOAD_IMAGE_GRAYSCALE); - IplImage* img2 = cvLoadImage(img2_name.c_str(), CV_LOAD_IMAGE_GRAYSCALE); + Mat img1 = imread(img1_name, CV_LOAD_IMAGE_GRAYSCALE); + Mat img2 = imread(img2_name, CV_LOAD_IMAGE_GRAYSCALE); // extract keypoints from the first image SURF surf_extractor(5.0e3); @@ -60,38 +61,32 @@ int main(int argc, char** argv) descriptorMatcher->match( img2, keypoints2, img1, keypoints1, matches2to1 ); printf("Done\n"); - IplImage* img_corr = DrawCorrespondences(img1, keypoints1, img2, keypoints2, matches2to1); + Mat img_corr = DrawCorrespondences(img1, keypoints1, img2, keypoints2, matches2to1); - cvNamedWindow("correspondences", 1); - cvShowImage("correspondences", img_corr); - cvWaitKey(0); - - cvReleaseImage(&img1); - cvReleaseImage(&img2); - cvReleaseImage(&img_corr); + imshow("correspondences", img_corr); + waitKey(0); } -IplImage* DrawCorrespondences(IplImage* img1, const vector& features1, IplImage* img2, - const vector& features2, const vector& desc_idx) +Mat DrawCorrespondences(const Mat& img1, const vector& features1, const Mat& img2, + const vector& features2, const vector& desc_idx) { - IplImage* img_corr = cvCreateImage(cvSize(img1->width + img2->width, MAX(img1->height, img2->height)), - IPL_DEPTH_8U, 3); - cvSetImageROI(img_corr, cvRect(0, 0, img1->width, img1->height)); - cvCvtColor(img1, img_corr, CV_GRAY2RGB); - cvSetImageROI(img_corr, cvRect(img1->width, 0, img2->width, img2->height)); - cvCvtColor(img2, img_corr, CV_GRAY2RGB); - cvResetImageROI(img_corr); + Mat part, img_corr(Size(img1.cols + img2.cols, MAX(img1.rows, img2.rows)), CV_8UC3); + img_corr = Scalar::all(0); + part = img_corr(Rect(0, 0, img1.cols, img1.rows)); + cvtColor(img1, part, COLOR_GRAY2RGB); + part = img_corr(Rect(img1.cols, 0, img2.cols, img2.rows)); + cvtColor(img1, part, COLOR_GRAY2RGB); for (size_t i = 0; i < features1.size(); i++) { - cvCircle(img_corr, features1[i].pt, 3, CV_RGB(255, 0, 0)); + circle(img_corr, features1[i].pt, 3, CV_RGB(255, 0, 0)); } for (size_t i = 0; i < features2.size(); i++) { - CvPoint pt = cvPoint(cvRound(features2[i].pt.x + img1->width), cvRound(features2[i].pt.y)); - cvCircle(img_corr, pt, 3, CV_RGB(255, 0, 0)); - cvLine(img_corr, features1[desc_idx[i].trainIdx].pt, pt, CV_RGB(0, 255, 0)); + Point pt(cvRound(features2[i].pt.x + img1.cols), cvRound(features2[i].pt.y)); + circle(img_corr, pt, 3, Scalar(0, 0, 255)); + line(img_corr, features1[desc_idx[i].trainIdx].pt, pt, Scalar(0, 255, 0)); } return img_corr; diff --git a/samples/cpp/inpaint.cpp b/samples/cpp/inpaint.cpp index 85fea6b2f7..32a2844da2 100644 --- a/samples/cpp/inpaint.cpp +++ b/samples/cpp/inpaint.cpp @@ -1,5 +1,6 @@ #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/photo/photo.hpp" #include diff --git a/samples/cpp/matcher_simple.cpp b/samples/cpp/matcher_simple.cpp index 1f1ded6611..fb04f7ac3b 100644 --- a/samples/cpp/matcher_simple.cpp +++ b/samples/cpp/matcher_simple.cpp @@ -2,6 +2,7 @@ #include "opencv2/core/core.hpp" #include "opencv2/features2d/features2d.hpp" #include "opencv2/highgui/highgui.hpp" +#include "opencv2/nonfree/nonfree.hpp" using namespace cv; @@ -42,7 +43,7 @@ int main(int argc, char** argv) extractor.compute(img2, keypoints2, descriptors2); // matching descriptors - BruteForceMatcher > matcher; + BFMatcher matcher(NORM_L2); vector matches; matcher.match(descriptors1, descriptors2, matches); diff --git a/samples/cpp/video_homography.cpp b/samples/cpp/video_homography.cpp index cee6b9b01e..76e937d323 100644 --- a/samples/cpp/video_homography.cpp +++ b/samples/cpp/video_homography.cpp @@ -141,7 +141,7 @@ int main(int ac, char ** av) vector matches; - BruteForceMatcher desc_matcher; + BFMatcher desc_matcher(NORM_HAMMING); vector train_pts, query_pts; vector train_kpts, query_kpts; diff --git a/samples/gpu/CMakeLists.txt b/samples/gpu/CMakeLists.txt index 51b95f4a88..0f4ec251b1 100644 --- a/samples/gpu/CMakeLists.txt +++ b/samples/gpu/CMakeLists.txt @@ -1,6 +1,7 @@ SET(OPENCV_GPU_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_gpu) + opencv_calib3d opencv_legacy opencv_contrib opencv_gpu + opencv_nonfree) ocv_check_dependencies(${OPENCV_GPU_SAMPLES_REQUIRED_DEPS}) diff --git a/samples/gpu/performance/tests.cpp b/samples/gpu/performance/tests.cpp index d89b7d1c16..29639e751e 100644 --- a/samples/gpu/performance/tests.cpp +++ b/samples/gpu/performance/tests.cpp @@ -4,6 +4,7 @@ #include "opencv2/calib3d/calib3d.hpp" #include "opencv2/video/video.hpp" #include "opencv2/gpu/gpu.hpp" +#include "opencv2/nonfree/nonfree.hpp" #include "performance.h" using namespace std; @@ -352,7 +353,7 @@ TEST(BruteForceMatcher) int desc_len = 64; - BruteForceMatcher< L2 > matcher; + BFMatcher matcher(NORM_L2); Mat query; gen(query, 3000, desc_len, CV_32F, 0, 1);