KAZE FEATURES「建议收藏」

KAZE FEATURES「建议收藏」KAZE系列笔记:1.  OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波2.  OpenCV学习笔记(28)KAZE 算法原理与源码分析(二)非线性尺度空间构建3.  OpenCV学习笔记(29)KAZE 算法原理与源码分析(三)特征检测与描述4.  OpenCV学习笔记(30)KAZE 算法原理与源码分析(四)KAZE特征的性能分析与比较5.  

大家好,又见面了,我是你们的朋友全栈君。

 

KAZE系列笔记:

1.  OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波

2.  OpenCV学习笔记(28)KAZE 算法原理与源码分析(二)非线性尺度空间构建

3.  OpenCV学习笔记(29)KAZE 算法原理与源码分析(三)特征检测与描述

4.  OpenCV学习笔记(30)KAZE 算法原理与源码分析(四)KAZE特征的性能分析与比较

5.  OpenCV学习笔记(31)KAZE 算法原理与源码分析(五)KAZE的性能优化及与SIFT的比较

 

KAZE算法资源:

1.  论文:  http://www.robesafe.com/personal/pablo.alcantarilla/papers/Alcantarilla12eccv.pdf

2.  项目主页:http://www.robesafe.com/personal/pablo.alcantarilla/kaze.html

3.  作者代码:http://www.robesafe.com/personal/pablo.alcantarilla/code/kaze_features_1_4.tar 
(需要boost库,另外其计时函数的使用比较复杂,可以用OpenCV的cv::getTickCount代替)

4.  Computer Vision Talks的评测:http://computer-vision-talks.com/2013/03/porting-kaze-features-to-opencv/

5.  Computer Vision Talks 博主Ievgen Khvedchenia将KAZE集成到OpenCV的cv::Feature2D类,但需要重新编译OpenCV,并且没有实现算法参数调整和按Mask过滤特征点的功能:https://github.com/BloodAxe/opencv/tree/kaze-features

6.  我在Ievgen的项目库中提取出KAZE,封装成继承cv::Feature2D的类,无需重新编译OpenCV,实现了参数调整和Mask过滤的功能:https://github.com/yuhuazou/kaze_opencv (2013-03-28更新,对KAZE代码进行了优化)

7.  Matlab 版的接口程序,封装了1.0版的KAZE代码:https://github.com/vlfeat/vlbenchmarks/blob/unstable/%2BlocalFeatures/Kaze.m

 

2.3 与其他特征算法的比较

2.3.1 与OpenCV API的融合

KAZE算法作者在其项目主页提供了源码,其中包括KAZE的核心算法库以及KAZE特征的提取、匹配和比较等例程,是基于OpenCV实现的。Computer Vision Talks的博主Ievgen Khvedchenia不久前将KAZE代码融合到OpenCV的cv::Feature2D API中,不过他是OpenCV项目的维护者之一,他的目标是在未来的OpenCV版本中加入KAZE。使用他的KAZE需要重新编译OpenCV,并且目前只是简单地嵌入、还不能调整KAZE类的参数,也不支持Mask过滤。

因为想尽快测试和比较KAZE算法的性能,又不想重新编译OpenCV,我在Ievgen的项目库中将KAZE相关的代码抽离出来,改造为一个相对独立的cv::KAZE,继承于cv::Feature2D类。这样就可以方便地在OpenCV中使用,并能够通过一致的接口与其它特征算法进行比较。cv::KAZE类包括如下文件:

 

[plain] 
view plain
copy

  1. |–KAZE  
  2.     |   kaze_features.cpp               // Class that warps KAZE to cv::Feature2D  
  3.     |   kaze_features.h  
  4.     |   kaze.cpp                        // Implementation of KAZE  
  5.     |   kaze.h  
  6.     |   kaze_config.cpp                 // Configuration variables and options  
  7.     |   kaze_config.h  
  8.     |   kaze_ipoint.cpp                 // Class that defines a point of interest  
  9.     |   kaze_ipoint.h  
  10.     |   kaze_nldiffusion_functions.cpp  // Functions for non-linear diffusion applications  
  11.     |   kaze_nldiffusion_functions.h  
  12.     |   kaze_utils.cpp                  // Some useful functions  
  13.     |   kaze_utils.h  

 

其中kaze_feature.h和kaze_feature.cpp是继承cv::Feature2D的cv::KAZE类,通过这个类将KAZE核心算法库与OpenCV的Feature2D类关联起来。其具体代码如下:

[cpp] 
view plain
copy

  1. #ifndef _KAZE_FEATURES_H_  
  2. #define _KAZE_FEATURES_H_  
  3.   
  4.   
  5. // Extract from ..\opencv\modules\features2d\src\precomp.hpp  
  6. //  
  7. #ifdef HAVE_CVCONFIG_H  
  8. #include “cvconfig.h”  
  9. #endif  
  10.   
  11. #include “opencv2/features2d/features2d.hpp”  
  12. #include “opencv2/imgproc/imgproc.hpp”  
  13. #include “opencv2/imgproc/imgproc_c.h”  
  14. #include “opencv2/core/internal.hpp”  
  15.   
  16. #include <algorithm>  
  17.   
  18. #ifdef HAVE_TEGRA_OPTIMIZATION  
  19. #include “opencv2/features2d/features2d_tegra.hpp”  
  20. #endif  
  21. //  
  22.   
  23.   
  24. #include “kaze_config.h”  
  25.   
  26. /*! 
  27.  KAZE features implementation. 
  28.  !! Note that it has NOT been warped to cv::Algorithm in oder to avoid rebuilding OpenCV 
  29.     So most functions of cv::Algorithm can not be used in cv::KAZE 
  30.  http://www.robesafe.com/personal/pablo.alcantarilla/papers/Alcantarilla12eccv.pdf 
  31. */  
  32. namespace cv  
  33. {  
  34.     class CV_EXPORTS_W KAZE : public Feature2D  
  35.     {  
  36.     public:  
  37.   
  38.         CV_WRAP explicit KAZE();  
  39.         KAZE(toptions &_options);  
  40.   
  41.         // returns the descriptor size in bytes  
  42.         int descriptorSize() const;  
  43.   
  44.         // returns the descriptor type  
  45.         int descriptorType() const;  
  46.   
  47.         // Compute the KAZE features and descriptors on an image  
  48.         void operator()( InputArray image, InputArray mask, vector<KeyPoint>& keypoints,  
  49.             OutputArray descriptors, bool useProvidedKeypoints=false ) const;  
  50.   
  51.         // Compute the KAZE features with mask  
  52.         void operator()(InputArray image, InputArray mask, vector<KeyPoint>& keypoints) const;  
  53.   
  54.         // Compute the KAZE features and descriptors on an image WITHOUT mask  
  55.         void operator()(InputArray image, vector<KeyPoint>& keypoints, OutputArray descriptors) const;  
  56.   
  57.         //AlgorithmInfo* info() const;  
  58.   
  59.     protected:  
  60.   
  61.         void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;  
  62.   
  63.         // !! NOT recommend to use because KAZE descriptors ONLY work with KAZE features  
  64.         void computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;  
  65.   
  66.         CV_PROP_RW int nfeatures;  
  67.   
  68.     private:  
  69.         toptions options;  
  70.     };  
  71.   
  72.     typedef KAZE KazeFeatureDetector;  
  73.     //typedef KAZE KazeDescriptorExtractor; // NOT available because KAZE descriptors ONLY work with KAZE features  
  74. }  
  75.   
  76. #endif  

[cpp] 
view plain
copy

  1. /********************************************************************* 
  2. * Software License Agreement (BSD License) 
  3. * 
  4. *  Copyright (c) 2009, Willow Garage, Inc. 
  5. *  All rights reserved. 
  6. * 
  7. *  Redistribution and use in source and binary forms, with or without 
  8. *  modification, are permitted provided that the following conditions 
  9. *  are met: 
  10. * 
  11. *   * Redistributions of source code must retain the above copyright 
  12. *     notice, this list of conditions and the following disclaimer. 
  13. *   * Redistributions in binary form must reproduce the above 
  14. *     copyright notice, this list of conditions and the following 
  15. *     disclaimer in the documentation and/or other materials provided 
  16. *     with the distribution. 
  17. *   * Neither the name of the Willow Garage nor the names of its 
  18. *     contributors may be used to endorse or promote products derived 
  19. *     from this software without specific prior written permission. 
  20. * 
  21. *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
  22. *  “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
  23. *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
  24. *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
  25. *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
  26. *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
  27. *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
  28. *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
  29. *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
  30. *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
  31. *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
  32. *  POSSIBILITY OF SUCH DAMAGE. 
  33. *********************************************************************/  
  34.   
  35. /** Authors: Ievgen Khvedchenia */  
  36. /** Modified: Yuhua Zou, 2013-03-20 */  
  37.   
  38. #include <iterator>  
  39. #include “kaze_features.h”  
  40. #include “kaze.h”  
  41.   
  42.   
  43.   
  44. #define DEGREE_TO_RADIAN(x) ((x) * CV_PI / 180.0)  
  45. #define RADIAN_TO_DEGREE(x) ((x) * 180.0 / CV_PI)  
  46.   
  47. namespace cv  
  48. {  
  49.     /*** 
  50.      *  Convertions between cv::Keypoint and KAZE::Ipoint 
  51.      */  
  52.     static inline void convertPoint(const cv::KeyPoint& kp, Ipoint& aux)  
  53.     {  
  54.         aux.xf = kp.pt.x;  
  55.         aux.yf = kp.pt.y;  
  56.         aux.x = fRound(aux.xf);  
  57.         aux.y = fRound(aux.yf);  
  58.   
  59.         //cout << “SURF size: ” << kpts_surf1_[i].size*.5 << endl;  
  60.         aux.octave = kp.octave;  
  61.   
  62.         // Get the radius for visualization  
  63.         aux.scale = kp.size*.5/2.5;  
  64.         aux.angle = DEGREE_TO_RADIAN(kp.angle);  
  65.   
  66.         //aux.descriptor_size = 64;  
  67.     }  
  68.   
  69.     static inline void convertPoint(const Ipoint& src, cv::KeyPoint& kp)  
  70.     {  
  71.         kp.pt.x = src.xf;  
  72.         kp.pt.y = src.yf;  
  73.   
  74.         kp.angle    = RADIAN_TO_DEGREE(src.angle);  
  75.         kp.response = src.dresponse;  
  76.   
  77.         kp.octave = src.octave;      
  78.         kp.size = src.scale;  
  79.     }  
  80.   
  81.     /*** 
  82.      *  runByPixelsMask() for KAZE Ipoint 
  83.      */  
  84.     class MaskPredicate  
  85.     {  
  86.     public:  
  87.         MaskPredicate( const Mat& _mask ) : mask(_mask) {}  
  88.         bool operator() (const Ipoint& key_pt) const  
  89.         {  
  90.             return mask.at<uchar>( (int)(key_pt.yf + 0.5f), (int)(key_pt.xf + 0.5f) ) == 0;  
  91.         }  
  92.   
  93.     private:  
  94.         const Mat mask;  
  95.         MaskPredicate& operator=(const MaskPredicate&);  
  96.     };  
  97.   
  98.     void runByPixelsMask( std::vector<Ipoint>& keypoints, const Mat& mask )  
  99.     {  
  100.         if( mask.empty() )  
  101.             return;  
  102.   
  103.         keypoints.erase(std::remove_if(keypoints.begin(), keypoints.end(), MaskPredicate(mask)), keypoints.end());  
  104.     }  
  105.   
  106.     /*** 
  107.      *  Implementation of cv::KAZE 
  108.      */  
  109.     KAZE::KAZE()  
  110.     {  
  111.     }  
  112.   
  113.     KAZE::KAZE(toptions &_options)  
  114.     {  
  115.         options = _options;  
  116.     }  
  117.   
  118.     int KAZE::descriptorSize() const  
  119.     {  
  120.         return options.extended ? 128 : 64;  
  121.     }  
  122.   
  123.     int KAZE::descriptorType() const  
  124.     {  
  125.         return CV_32F;  
  126.     }  
  127.   
  128.     void KAZE::operator()(InputArray _image, InputArray _mask, vector<KeyPoint>& _keypoints,  
  129.         OutputArray _descriptors, bool useProvidedKeypoints) const  
  130.     {  
  131.   
  132.         bool do_keypoints = !useProvidedKeypoints;  
  133.         bool do_descriptors = _descriptors.needed();  
  134.   
  135.         if( (!do_keypoints && !do_descriptors) || _image.empty() )  
  136.             return;  
  137.           
  138.         cv::Mat img1_8, img1_32;  
  139.   
  140.         // Convert to gray scale iamge and float image  
  141.         if (_image.getMat().channels() == 3)  
  142.             cv::cvtColor(_image, img1_8, CV_RGB2GRAY);  
  143.         else  
  144.             _image.getMat().copyTo(img1_8);  
  145.   
  146.         img1_8.convertTo(img1_32, CV_32F, 1.0/255.0,0);  
  147.   
  148.         // Construct KAZE  
  149.         toptions opt = options;  
  150.         opt.img_width = img1_32.cols;  
  151.         opt.img_height = img1_32.rows;  
  152.   
  153.         ::KAZE kazeEvolution(opt);  
  154.   
  155.         // Create nonlinear scale space  
  156.         kazeEvolution.Create_Nonlinear_Scale_Space(img1_32);          
  157.   
  158.         // Feature detection  
  159.         std::vector<Ipoint> kazePoints;  
  160.   
  161.         if (do_keypoints)  
  162.         {  
  163.             kazeEvolution.Feature_Detection(kazePoints);  
  164.   
  165.             if (!_mask.empty())  
  166.             {  
  167.                 runByPixelsMask(kazePoints, _mask.getMat());  
  168.             }  
  169.         }  
  170.         else  
  171.         {  
  172.             kazePoints.resize(_keypoints.size());  
  173.             for (size_t i = 0; i < kazePoints.size(); i++)  
  174.             {  
  175.                 convertPoint(_keypoints[i], kazePoints[i]);      
  176.             }  
  177.         }  
  178.           
  179.         // Descriptor generation  
  180.         if (do_descriptors)  
  181.         {  
  182.             kazeEvolution.Feature_Description(kazePoints);  
  183.   
  184.             cv::Mat& descriptors = _descriptors.getMatRef();  
  185.             descriptors.create(kazePoints.size(), descriptorSize(), descriptorType());  
  186.   
  187.             for (size_t i = 0; i < kazePoints.size(); i++)  
  188.             {  
  189.                 std::copy(kazePoints[i].descriptor.begin(), kazePoints[i].descriptor.end(), (float*)descriptors.row(i).data);  
  190.             }  
  191.         }  
  192.   
  193.         // Transfer from KAZE::Ipoint to cv::KeyPoint  
  194.         if (do_keypoints)  
  195.         {  
  196.             _keypoints.resize(kazePoints.size());  
  197.             for (size_t i = 0; i < kazePoints.size(); i++)  
  198.             {  
  199.                 convertPoint(kazePoints[i], _keypoints[i]);  
  200.             }  
  201.         }  
  202.     }  
  203.   
  204.     void KAZE::operator()(InputArray image, InputArray mask, vector<KeyPoint>& keypoints ) const  
  205.     {  
  206.         (*this)(image, mask, keypoints, noArray(), false);  
  207.     }  
  208.   
  209.     void KAZE::operator()(InputArray image, vector<KeyPoint>& keypoints, OutputArray descriptors) const  
  210.     {  
  211.         (*this)(image, noArray(), keypoints, descriptors, false);  
  212.     }  
  213.   
  214.     void KAZE::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask) const  
  215.     {  
  216.         (*this)(image, mask, keypoints, noArray(), false);  
  217.     }  
  218.   
  219.     void KAZE::computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors) const  
  220.     {  
  221.         (*this)(image, Mat(), keypoints, descriptors, false);       // Regenerate keypoints no matter keypoints is empty or not  
  222.     }  
  223.   
  224. }  

 

下面是基于cv::KAZE类的特征提取与图像匹配例程及结果图:

[cpp] 
view plain
copy

  1. // KazeOpenCV.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include “predep.h”  
  5.   
  6. #include “opencv2/imgproc/imgproc.hpp”  
  7. #include “opencv2/highgui/highgui.hpp”  
  8. #include “opencv2/calib3d/calib3d.hpp”  
  9.   
  10. #include “KAZE/kaze_features.h”  
  11.   
  12. #pragma comment( lib, cvLIB(“core”) )  
  13. #pragma comment( lib, cvLIB(“imgproc”) )  
  14. #pragma comment( lib, cvLIB(“highgui”) )  
  15. #pragma comment( lib, cvLIB(“flann”) )  
  16. #pragma comment( lib, cvLIB(“features2d”) )  
  17. #pragma comment( lib, cvLIB(“calib3d”) )  
  18.   
  19.   
  20. using namespace std;  
  21. using namespace cv;  
  22.   
  23.   
  24. int main(int argc, char** argv[])  
  25. {  
  26.     Mat img_1 = imread(“box.png”);  
  27.     Mat img_2 = imread(“box_in_scene.png”);  
  28.   
  29.     std::vector<KeyPoint> keypoints_1, keypoints_2;  
  30.     Mat descriptors_1, descriptors_2;  
  31.   
  32.     toptions opt;  
  33.     opt.extended = true;        // 1 – 128-bit vector, 0 – 64-bit vector, default: 0  
  34.     opt.verbosity = true;       // 1 – show detail information while caculating KAZE, 0 – unshow, default: 0  
  35.   
  36.     KAZE detector_1(opt);  
  37.     KAZE detector_2(opt);  
  38.   
  39.     double t2 = 0.0, t1 = 0.0, tkaze = 0.0;  
  40.     int64 start_t1 = cv::getTickCount();  
  41.   
  42.     //– Detect keypoints and calculate descriptors  
  43.     detector_1(img_1, keypoints_1, descriptors_1);  
  44.     detector_2(img_2, keypoints_2, descriptors_2);  
  45.   
  46.     t2 = cv::getTickCount();  
  47.     tkaze = 1000.0 * (t2 – start_t1) / cv::getTickFrequency();  
  48.   
  49.     cout << “\n\n– Total detection time (ms): “ << tkaze << endl;  
  50.     printf(“– Keypoint number of img_1 : %d \n”, keypoints_1.size() );  
  51.     printf(“– Keypoint number of img_2 : %d \n”, keypoints_2.size() );  
  52.   
  53.     //– Matching descriptor vectors using FLANN matcher  
  54.     FlannBasedMatcher matcher;  
  55.     vector< DMatch > matches;  
  56.     matcher.match( descriptors_1, descriptors_2, matches );  
  57.     double max_dist = 0; double min_dist = 100;  
  58.   
  59.     //– Quick calculation of max and min distances between keypoints  
  60.     forint i = 0; i < descriptors_1.rows; i++ )  
  61.     {   
  62.         double dist = matches[i].distance;  
  63.         if( dist < min_dist ) min_dist = dist;  
  64.         if( dist > max_dist ) max_dist = dist;  
  65.     }  
  66.   
  67.     //– Find initial good matches (i.e. whose distance is less than 2*min_dist )  
  68.     vector< DMatch > good_matches, inliers;  
  69.     forint i = 0; i < descriptors_1.rows; i++ )  
  70.     {   
  71.         if( matches[i].distance < 2*min_dist )     
  72.         {   
  73.             good_matches.push_back( matches[i]);   
  74.         }  
  75.     }  
  76.   
  77.     cout << “– Computing homography (RANSAC)…” << endl;  
  78.     //– Get the keypoints from the good matches  
  79.     vector<Point2f> points1( good_matches.size() );   
  80.     vector<Point2f> points2( good_matches.size() );   
  81.     forsize_t i = 0; i < good_matches.size(); i++ )  
  82.     {  
  83.         points1[i] = keypoints_1[ good_matches[i].queryIdx ].pt;  
  84.         points2[i] = keypoints_2[ good_matches[i].trainIdx ].pt;  
  85.     }  
  86.   
  87.     //– Computing homography (RANSAC) and find inliers  
  88.     vector<uchar> flags(points1.size(), 0);  
  89.     Mat H = findHomography( points1, points2, CV_RANSAC, 3.0, flags );  
  90.     //cout << H << endl << endl;  
  91.     for (int i = 0; i < good_matches.size(); i++)  
  92.     {  
  93.         if (flags[i])  
  94.         {  
  95.             inliers.push_back( good_matches[i] );  
  96.         }  
  97.     }  
  98.   
  99.     //– Draw Keypoints  
  100.     Mat img_1k, img_2k;  
  101.     drawKeypoints(img_1, keypoints_1, img_1k, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);  
  102.     drawKeypoints(img_2, keypoints_2, img_2k, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);  
  103.   
  104.     //– Draw inliers  
  105.     Mat img_matches;  
  106.     drawMatches( img_1, keypoints_1, img_2, keypoints_2,  
  107.         inliers, img_matches, Scalar::all(-1), Scalar::all(-1),  
  108.         vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );  
  109.   
  110.     printf(“– Number of Matches : %d \n”, good_matches.size() );  
  111.     printf(“– Number of Inliers : %d \n”, inliers.size() );  
  112.     printf(“– Match rate : %f \n”, inliers.size() / (float)good_matches.size() );  
  113.   
  114.     //– Localize the object  
  115.     //– Get the corners from the image_1 ( the object to be “detected” )  
  116.     vector<Point2f> obj_corners;  
  117.     obj_corners.push_back( Point2f(0,0) );  
  118.     obj_corners.push_back( Point2f(img_1.cols,0) );  
  119.     obj_corners.push_back( Point2f(img_1.cols,img_1.rows) );  
  120.     obj_corners.push_back( Point2f(0,img_1.rows) );  
  121.   
  122.     if (!H.empty())  
  123.     {  
  124.         vector<Point2f> scene_corners;  
  125.         perspectiveTransform(obj_corners, scene_corners, H);  
  126.   
  127.         //– Draw lines between the corners (the mapped object in the scene – image_2 )  
  128.         int npts = scene_corners.size();  
  129.         for (int i=0; i<npts; i++)  
  130.             line( img_matches, scene_corners[i] + Point2f( img_1.cols, 0),   
  131.                 scene_corners[(i+1)%npts] + Point2f( img_1.cols, 0), Scalar(0,0,255), 2 );  
  132.     }  
  133.   
  134.     //– Show detected matches  
  135.     cout << “– Show detected matches.” << endl;  
  136.     namedWindow(“Image 1”,CV_WINDOW_NORMAL);  
  137.     namedWindow(“Image 2”,CV_WINDOW_NORMAL);  
  138.     namedWindow(“Good Matches”,CV_WINDOW_NORMAL);  
  139.     imshow( “Image 1”, img_1k );  
  140.     imshow( “Image 2”, img_2k );  
  141.     imshow( “Good Matches”, img_matches );  
  142.     waitKey(0);  
  143.     destroyAllWindows();  
  144.   
  145.     return 0;  
  146. }  

KAZE FEATURES「建议收藏」
 

 

 

2.3.2 KAZE特征的性能测试与比较

KAZE论文中给出了若干实验图表数据,与SURF、SIFT和STAR相比,KAZE有更好的尺度和旋转不变性,并且稳定、可重复检测。主要的实验包括:

(1)   重复检测试验

这里主要从旋转缩放、视角变换、噪声干扰、模糊图像、压缩图像等方面进行了测试,可以看出KAZE的可重复性明显优于其它特征。

KAZE FEATURES「建议收藏」 

(2)   特征检测与匹配试验

这里也是从旋转缩放、视角变换、噪声干扰、模糊图像、压缩图像等方面进行了测试,给出了特征匹配的Precision-Recall图。使用的匹配算法是最近邻匹配。这里可以看出,在图像模糊、噪声干扰和压缩重构等造成的信息丢失的情况下,KAZE特征的鲁棒性明显优于其它特征。

KAZE FEATURES「建议收藏」 

(3)   表面形变目标的特征匹配

这里可以看出基于g2传导函数的KAZE特征性能最好。

KAZE FEATURES「建议收藏」 

(4)   检测效率测试

这里可以看出KAZE的特征检测时间高于SURF和STAR,但与SIFT相近。这里比较花时间的是非线性尺度空间的构建。

KAZE FEATURES「建议收藏」 

作者提出通过多线程并行计算进行AOS求解的方法来加快运行速度,在实现代码中,他们用boost/thread库进行AOS求解和寻找局部极大值点。不过我通过测试发现这并没有明显提高运行速度,可能是因为他们的代码中,分发的多个线程最后要用thread.join()等待所有计算线程结束,然后才能继续后续运算。这个join的使用反而可能会降低运行速度。

KAZE FEATURES「建议收藏」 

 

Computer Vision Talks博客不久前KAZE算法进行了评测,并与其它特征进行了性能比较。这里我根据Ievgen在github上的OpenCV-Features-Comparison代码进行了更深入的测试,进一步显示了KAZE特征在尺度缩放、旋转变换、亮度变化和高斯模糊等情况下的优良性能。

 

(1) Percent of correct matches

KAZE FEATURES「建议收藏」

(2) Percent of matches

KAZE FEATURES「建议收藏」

(3) Match ratio

KAZE FEATURES「建议收藏」

(4) Mean distance

KAZE FEATURES「建议收藏」

(5) Homography error

KAZE FEATURES「建议收藏」  

不过KAZE在运行时间上的短板的确很明显,远高于其他特征。特别是,论文的实验显示KAZE和SIFT的检测速度相差并不大。但在我的实验中,KAZE的检测时间是SIFT的10倍,而且SIFT比SURF还快一倍!这可能是OpenCV的实现代码中对SIFT做了较大的优化。具体还需要再研究下OpenCV的代码。

KAZE FEATURES「建议收藏」 

 

最后分享一下上述图表的Matlab代码:

[plain] 
view plain
copy

  1. %%   
  2. % MATLAB script for the visualization of the results of OpenCV-Features-Comparison  
  3. % Copyright (c) by Yuhua Zou.   
  4. % Email: yuhuazou AT gmail DOT com OR chenyusiyuan AT 126 DOT com  
  5. %  
  6.   
  7. close all;  
  8. clear all;  
  9. clc;  
  10.   
  11. % workroot: directory which contains files as follows:  
  12. %     HomographyError.txt  
  13. %     MatchingRatio.txt  
  14. %     MeanDistance.txt  
  15. %     PercentOfCorrectMatches.txt  
  16. %     PercentOfMatches.txt  
  17. %     Performance.txt  
  18. %  
  19. workroot=’.\5\’;  
  20. files=dir([workroot,’*.txt’]);  
  21.   
  22. % use the file name as the figure name, stored in a cell ‘nameFigure’  
  23. nameFigure = cell(1,length(files));  
  24.   
  25. for i=1:length(files),  
  26.     % get file name and create a correspoinding figure  
  27.     filename = files(i,1).name;  
  28.     nameFigure{i} = filename(1:end-4);  
  29.     figure(‘Name’,nameFigure{i},’Position’,[20 40 1240 780]);   
  30.       
  31.     % initialize 2 cells to store title name and legends of each plot  
  32.     nameTitle{1} = ”;  
  33.     nameLegend{1} = ”;     
  34.       
  35.     % open file  
  36.     file = fullfile(workroot,filename);  
  37.     fid = fopen(file,’r’);  
  38.       
  39.     % process ‘Performance.txt’ individually   
  40.     if strcmp(nameFigure{i},’Performance’) ,  
  41.         nl = 0;  
  42.         data = 0;  
  43.           
  44.         %% analyze each line  
  45.         tline = fgetl(fid);  
  46.         while ischar(tline),  
  47.             nl = nl + 1;          
  48.             tline(tline == ‘”‘) = ”;      
  49.             if nl == 1,  
  50.                 nameTitle{ 1 } = tline;  
  51.             elseif nl == 2,  
  52.                 args = regexp(tline,’\t’,’split’);  
  53.                 nameLegend = args(2:end);  
  54.             elseif ~isempty(tline),  
  55.                 args = regexp(tline,’\t’,’split’);  
  56.                 cols = length(args) – 1;  
  57.                 tick = args{1};   
  58.                 nameTick{nl-2} = tick;  
  59.                 for n = 1:cols, data(nl-2,n) = str2num( args{n+1} ); end  
  60.             end  
  61.             tline = fgetl(fid);  
  62.         end  
  63.           
  64.         % plotting  
  65.         for k=1:2,  
  66.             subplot(2,1,k);  
  67.             [data_sorted,idx] = sort(data(:,k),’ascend’);  
  68.             h = barh( data_sorted ); % get the handle to change bar color              
  69.             xlabel(‘Time (ms)’); ylabel(‘Algorithms’);  
  70.             title(nameLegend{ k }, ‘FontWeight’, ‘bold’);  
  71.             set(gca, ‘yticklabel’, nameTick(idx), ‘FontSize’, 7);  
  72. %             set(gca,’yticklabel’,”,’FontSize’,7); % unshow y-axis ticks  
  73.   
  74.             %% attach the value to the right side of each bar  
  75.             x = get(h, ‘XData’);  
  76.             y = get(h, ‘YData’);  
  77.             horiGap = 0.01 * ( max(y) – min(y) );  
  78.             for c=1:length(x),  
  79.                 text( y(c) + horiGap, x(c), num2str(y(c), ‘%0.3f’),…  
  80.                     ‘HorizontalAlignment’,’left’,’VerticalAlignment’,’middle’,…  
  81.                     ‘FontSize’,7);                  
  82.             end  
  83.               
  84.             %% Change the color of each bar  
  85.             ch = get(h,’Children’); % get children of the bar group  
  86.             fvd = get(ch,’Faces’); % get faces data  
  87.             fvcd = get(ch,’FaceVertexCData’); % get face vertex cdata  
  88. %             [zs, izs] = sortrows(datak,1); % sort the rows ascending by first columns  
  89.             for c = 1:length(data_sorted)  
  90.                 fvcd(fvd(c,:)) = idx(c); % adjust the face vertex cdata to be that of the row  
  91.             end  
  92.             set(ch,’FaceVertexCData’,fvcd) % set to new face vertex cdata  
  93.             % you can search ‘FaceVertexCData’ in MATLAB Help for more info.  
  94.         end  
  95.     else  
  96.     %% process other documents  
  97.         nDataRow = 0;   % rows of numerical data in each plot  
  98.         nPlot = 0;      % number of plots  
  99.         data{1} = 0;    % all numerical data in current document  
  100.           
  101.         %% analyze each line  
  102.         tline = fgetl(fid);  
  103.         while ischar(tline) && ~strcmp(tline, -1),    
  104.             % split the line into strings by ‘\t’      
  105.             args = regexp(tline,’\t’,’split’);  
  106.             if strcmp(args{end},”), args = args(1:end-1); end; % remove the last empty one  
  107.               
  108.             % the line which contains only one string   
  109.             % is recognized as the beginning of a new plot  
  110.             % the string is stored as plot title  
  111.             % which represents the transformation type  
  112.             if length(args) == 1,  
  113.                 nDataRow = 0;  
  114.                 nPlot = nPlot + 1;  
  115.                 tline(tline == ‘”‘) = ”;  
  116.                 nameTitle{ nPlot } = tline;  
  117.             else  
  118.                 % the line with several ‘”‘s under the ‘plot title’ line  
  119.                 % stores legends of the plot  
  120.                 % which represent feature methods  
  121.                 if ~isempty( find( tline=='”‘, 1 ) ),  
  122.                     tline(tline == ‘”‘) = ”;   
  123.                     nameLegend{ nPlot } = args(2:end);  
  124.                 else  
  125.                 % the line without ‘””‘s contains numerical data  
  126.                 % which represent experiment data  
  127.                     nDataRow = nDataRow + 1;  
  128.                     for n = 1:length(args),   
  129.                         data{ nPlot }(nDataRow,n) = str2double( args{n} );   
  130.                     end  
  131.                 end  
  132.             end  
  133.             tline = fgetl(fid);  
  134.         end            
  135.           
  136.         %% plotting  
  137.         cmap = colormap( jet( length( nameLegend{1} ) ) ); % cmap: table of line color  
  138.         for p = 1:nPlot,  
  139.             subplot(ceil(nPlot/2), 2, p);   
  140.             xdata = data{p}(:,1);  
  141.             ydata = data{p}(:,2:end);  
  142.             for r=1:size(ydata,2)  
  143.                 plot(xdata, ydata(:,r), ‘Color’, cmap(r,:), ‘LineWidth’,2); hold on; % draw each line with different color  
  144.             end  
  145.             title(nameTitle{p},’FontWeight’,’bold’);  
  146.             if p == 1, legend(nameLegend{p},’Location’,’Best’,’FontSize’,7); end  
  147.             xlim([min(xdata(:)-0.1*max(xdata(:))), 1.1*max(xdata(:))]);  
  148.             ylim([0, 1.1*max(ydata(:))]);  
  149.         end  
  150.     end     
  151.       
  152.     fclose(fid);  
  153. end  

 

其中bar的颜色设置参考自:
http://www.mathworks.cn/support/solutions/en/data/1-4LDEEP/index.html?solution=1-4LDEEP

 

KAZE特征分析的系列笔记到此暂告一段落了,我觉得如果能够在非线性尺度空间的构建和特征检测方面对算法做出优化和改进、提高其实时性,KAZE 将大有用武之地。笔记仓促写完,还有很多不足和问题,欢迎大家指正和讨论,谢谢!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/148686.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • android轮播图实现_ajax异步加载

    android轮播图实现_ajax异步加载这个图片异步加载并缓存的类已经被很多开发者所使用,是最常用的几个开源库之一,主流的应用,随便反编译几个火的项目,都可以见到它的身影。    可是有的人并不知道如何去使用这库如何进行配置,网上查到的信息对于刚接触的人来说可能太少了,下面我就把我使用过程中所知道的写了下来,希望可以帮助自己和别人更深入了解这个库的使用和配置。     GITHUB上的下载路径为:https:/

    2022年10月27日
  • mysql分页查询实例_mysql分页查询实例讲解「建议收藏」

    LIMIT子句可以被用于强制SELECT语句返回指定的记录数。LIMIT接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的偏移量是0(而不是1)。下面,我们针对特例对mysql分页查询进行总结。mysql提供分页的功能:SELECT*FROMtableLIMIT[offset…

  • UE4/UE5 代理使用介绍[通俗易懂]

    UE4/UE5 代理使用介绍[通俗易懂]原创文章,转载请注明出处。UE4有一套代理机制,整理了一下做个介绍。也请大家做补充。有了代理,方便我们做代码设计,减轻耦合。文章里面的代码下载链接:代理单播代理二级目录三级目录多播代理二级目录三级目录单播代理二级目录三级目录多播代理二级目录三级目录…

  • glPushMatrix 与 glPopMatrix[通俗易懂]

    glPushMatrix 与 glPopMatrix[通俗易懂]1.原理讲解 终于明白为什么使用glPushMatrix()和glPopMatrix()的原因了。将本次需要执行的缩放、平移等操作放在glPushMatrix和glPopMatrix之间。glPushMatrix()和glPopMatrix()的配对使用可以消除上一次的变换对本次变换的影响。使本次变换是以世界坐标系的原点为参考点进行。下面对上述结论做进一步的解释:1)OpenGL中

    2022年10月28日
  • springboot事务管理详解

    springboot事务管理详解1、隔离级别隔离级别是指若干个并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读取、重复读、幻读。我们可以看org.springframework.transaction.annotation.Isolation枚举类中定义了五个表示隔离级别的值:publicenumIsolation{DEFAULT(-1),READ_UNCOMMITTED(…

  • 数学建模-二胎政策对中国人口的影响[通俗易懂]

    数学建模-二胎政策对中国人口的影响[通俗易懂]  研一的数学建模课上的关于“二胎政策对中国人口的影响”。研究中国人口,发现中国人口老龄化非常严重,如今假设不放开全面二胎,那么中国的未来真的非常令人堪忧。事实上,如今的单独二胎对人口的影响不是太多,每年添加100多万的人口,对于中国日益下降的人口增长率影响根本不大。   首先,本文通过搜集第六次人口普查的资料,将中国人口依照年龄组(5岁为一个年龄组)分为21个组,实行单独…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号