【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑

本系列文章由@浅墨_毛星云 出品,转载请注明出处。  

文章链接: http://blog.csdn.net/poem_qianmo/article/details/30974513

作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442

知乎:http://www.zhihu.com/people/mao-xing-yun

邮箱: [email protected]

写作当前博文时配套使用的OpenCV版本: 2.4.9



本篇文章中,我们一起探讨了OpenCV中重映射和SURF特征点检测相关的知识点,主要一起了解OpenCV中重映射相关的函数remap,SURF算法在OpenCV中的体现与应用。此博文一共有三个配套的麻雀虽小但五脏俱全的示例程序,其经过浅墨详细注释过的代码都在文中贴出,且文章最后提供了综合示例程序的下载。

 

依然是先看看程序运行截图。

重映射:

【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第1张图片  【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第2张图片

SURF特征点检测:

【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第3张图片  【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第4张图片

 






一、OpenCV重映射

 



1.1 重映射的概念简析

 

重映射,就是把一幅图像中某位置的像素放置到另一个图片指定位置的过程。为了完成映射过程, 我们需要获得一些插值为非整数像素的坐标,因为源图像与目标图像的像素坐标不是一一对应的。一般情况下,我们通过重映射来表达每个像素的位置 (x,y),像这样 :


g(x,y) = f ( h(x,y) )

 

在这里, g( ) 是目标图像, f() 是源图像, 而h(x,y) 是作用于 (x,y) 的映射方法函数。

 

来看个例子。 若有一幅图像 I ,想满足下面的条件作重映射:

 

h(x,y) = (I.cols - x, y )

 

这样的话,图像会按照 x 轴方向发生翻转。那么,源图像和效果图分别如下:

 【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第5张图片 【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第6张图片


在OpenCV中,我们用函数remap( )来实现简单重映射,下面我们就一起来看看这个函数。

 

 



1.2 remap( )函数解析

 


remap( )函数会根据我们指定的映射形式,将源图像进行重映射几何变换,基于的式子如下:


 


需要注意,此函数不支持就地(in-place)操作。看看其原型和参数。


[cpp]  view plain copy print ?
  1. C++: void remap(InputArray src, OutputArraydst, InputArray map1, InputArray map2, int interpolation, intborderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())  


    • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位或者浮点型图像。
    • 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放函数调用后的输出结果,需和源图片有一样的尺寸和类型。
    • 第三个参数,InputArray类型的map1,它有两种可能的表示对象。
      • 表示点(x,y)的第一个映射。
      • 表示CV_16SC2 , CV_32FC1 或CV_32FC2类型的X值。
    • 第四个参数,InputArray类型的map2,同样,它也有两种可能的表示对象,而且他是根据map1来确定表示那种对象。
      • 若map1表示点(x,y)时。这个参数不代表任何值。
      • 表示CV_16UC1 , CV_32FC1类型的Y值(第二个值)。

  • 第五个参数,int类型的interpolation,插值方式,之前的resize( )函数中有讲到,需要注意,resize( )函数中提到的INTER_AREA插值方式在这里是不支持的,所以可选的插值方式如下:
  • INTER_NEAREST - 最近邻插值
  • INTER_LINEAR – 双线性插值(默认值)
  • INTER_CUBIC – 双三次样条插值(逾4×4像素邻域内的双三次插值)
  • INTER_LANCZOS4 -Lanczos插值(逾8×8像素邻域的Lanczos插值)

  • 第六个参数,int类型的borderMode,边界模式,有默认值BORDER_CONSTANT,表示目标图像中“离群点(outliers)”的像素值不会被此函数修改。
  • 第七个参数,const Scalar&类型的borderValue,当有常数边界时使用的值,其有默认值Scalar( ),即默认值为0。





1.3 详细注释的重映射示例程序



下面放出精简后的以remap函数为核心的示例程序,方便大家快速掌握remap函数的使用方法。

[cpp]  view plain copy print ?
  1. //-----------------------------------【程序说明】----------------------------------------------  
  2. //      程序名称::《【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑 》 博文配套源码   
  3. //      开发所用IDE版本:Visual Studio 2010  
  4. //      开发所用OpenCV版本:   2.4.9  
  5. //      2014年5月26日 Created by 浅墨  
  6. //      配套博文链接: http://blog.csdn.net/poem_qianmo/article/details/26977557  
  7. //      PS:程序结合配合博文学习效果更佳  
  8. //      浅墨的微博:@浅墨_毛星云 http://weibo.com/1723155442  
  9. //      浅墨的知乎:http://www.zhihu.com/people/mao-xing-yun  
  10. //      浅墨的豆瓣:http://www.douban.com/people/53426472/  
  11. //----------------------------------------------------------------------------------------------  
  12.   
  13. //-----------------------------------【头文件包含部分】---------------------------------------  
  14. //      描述:包含程序所依赖的头文件  
  15. //----------------------------------------------------------------------------------------------   
  16. #include "opencv2/highgui/highgui.hpp"  
  17. #include "opencv2/imgproc/imgproc.hpp"  
  18. #include <iostream>  
  19.   
  20. //-----------------------------------【命名空间声明部分】--------------------------------------  
  21. //          描述:包含程序所使用的命名空间  
  22. //-----------------------------------------------------------------------------------------------  
  23. using namespace cv;  
  24.   
  25. //-----------------------------------【main( )函数】--------------------------------------------  
  26. //          描述:控制台应用程序的入口函数,我们的程序从这里开始执行  
  27. //-----------------------------------------------------------------------------------------------  
  28. int main(  )  
  29. {  
  30.     //【0】变量定义  
  31.     Mat srcImage, dstImage;  
  32.     Mat map_x, map_y;  
  33.   
  34.     //【1】载入原始图  
  35.     srcImage = imread( "1.jpg", 1 );  
  36.     if(!srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; }    
  37.     imshow("原始图",srcImage);  
  38.   
  39.     //【2】创建和原始图一样的效果图,x重映射图,y重映射图  
  40.     dstImage.create( srcImage.size(), srcImage.type() );  
  41.     map_x.create( srcImage.size(), CV_32FC1 );  
  42.     map_y.create( srcImage.size(), CV_32FC1 );  
  43.   
  44.     //【3】双层循环,遍历每一个像素点,改变map_x & map_y的值  
  45.     forint j = 0; j < srcImage.rows;j++)  
  46.     {   
  47.         forint i = 0; i < srcImage.cols;i++)  
  48.         {  
  49.             //改变map_x & map_y的值.   
  50.             map_x.at<float>(j,i) = static_cast<float>(i);  
  51.             map_y.at<float>(j,i) = static_cast<float>(srcImage.rows - j);  
  52.         }   
  53.     }  
  54.   
  55.     //【4】进行重映射操作  
  56.     remap( srcImage, dstImage, map_x, map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0,0, 0) );  
  57.   
  58.     //【5】显示效果图  
  59.     imshow( "【程序窗口】", dstImage );  
  60.     waitKey();  
  61.   
  62.     return 0;  
  63. }  


显示效果图:

【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第7张图片

【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第8张图片

 最近世界杯正如火如荼地进行着,这里的图片素材就是巴西队的球星们~

 



1.4 OpenCV2.X中remap函数源代码

 


这里我们放出remap函数的源码,供需要了解其实现细节的朋友们观看,浅墨在这里不花时间对其进行剖析。

[cpp]  view plain copy print ?
  1. void cv::remap( InputArray _src, OutputArray _dst,  
  2.                 InputArray _map1, InputArray _map2,  
  3.                 int interpolation, int borderType, const Scalar& borderValue )  
  4. {  
  5.     static RemapNNFunc nn_tab[] =  
  6.     {  
  7.         remapNearest<uchar>, remapNearest<schar>, remapNearest<ushort>, remapNearest<short>,  
  8.         remapNearest<int>, remapNearest<float>, remapNearest<double>, 0  
  9.     };  
  10.   
  11.     static RemapFunc linear_tab[] =  
  12.     {  
  13.         remapBilinear<FixedPtCast<int, uchar, INTER_REMAP_COEF_BITS>, RemapVec_8u, short>, 0,  
  14.         remapBilinear<Cast<float, ushort>, RemapNoVec, float>,  
  15.         remapBilinear<Cast<floatshort>, RemapNoVec, float>, 0,  
  16.         remapBilinear<Cast<floatfloat>, RemapNoVec, float>,  
  17.         remapBilinear<Cast<doubledouble>, RemapNoVec, float>, 0  
  18.     };  
  19.   
  20.     static RemapFunc cubic_tab[] =  
  21.     {  
  22.         remapBicubic<FixedPtCast<int, uchar, INTER_REMAP_COEF_BITS>, short, INTER_REMAP_COEF_SCALE>, 0,  
  23.         remapBicubic<Cast<float, ushort>, float, 1>,  
  24.         remapBicubic<Cast<floatshort>, float, 1>, 0,  
  25.         remapBicubic<Cast<floatfloat>, float, 1>,  
  26.         remapBicubic<Cast<doubledouble>, float, 1>, 0  
  27.     };  
  28.   
  29.     static RemapFunc lanczos4_tab[] =  
  30.     {  
  31.         remapLanczos4<FixedPtCast<int, uchar, INTER_REMAP_COEF_BITS>, short, INTER_REMAP_COEF_SCALE>, 0,  
  32.         remapLanczos4<Cast<float, ushort>, float, 1>,  
  33.         remapLanczos4<Cast<floatshort>, float, 1>, 0,  
  34.         remapLanczos4<Cast<floatfloat>, float, 1>,  
  35.         remapLanczos4<Cast<doubledouble>, float, 1>, 0  
  36.     };  
  37.   
  38.     Mat src = _src.getMat(), map1 = _map1.getMat(), map2 = _map2.getMat();  
  39.   
  40.     CV_Assert( map1.size().area() > 0 );  
  41.     CV_Assert( !map2.data || (map2.size() == map1.size()));  
  42.   
  43.     _dst.create( map1.size(), src.type() );  
  44.     Mat dst = _dst.getMat();  
  45.     if( dst.data == src.data )  
  46.         src = src.clone();  
  47.   
  48.     int depth = src.depth();  
  49.     RemapNNFunc nnfunc = 0;  
  50.     RemapFunc ifunc = 0;  
  51.     const void* ctab = 0;  
  52.     bool fixpt = depth == CV_8U;  
  53.     bool planar_input = false;  
  54.   
  55.     if( interpolation == INTER_NEAREST )  
  56.     {  
  57.         nnfunc = nn_tab[depth];  
  58.         CV_Assert( nnfunc != 0 );  
  59.     }  
  60.     else  
  61.     {  
  62.         if( interpolation == INTER_AREA )  
  63.             interpolation = INTER_LINEAR;  
  64.   
  65.         if( interpolation == INTER_LINEAR )  
  66.             ifunc = linear_tab[depth];  
  67.         else if( interpolation == INTER_CUBIC )  
  68.             ifunc = cubic_tab[depth];  
  69.         else if( interpolation == INTER_LANCZOS4 )  
  70.             ifunc = lanczos4_tab[depth];  
  71.         else  
  72.             CV_Error( CV_StsBadArg, "Unknown interpolation method" );  
  73.         CV_Assert( ifunc != 0 );  
  74.         ctab = initInterTab2D( interpolation, fixpt );  
  75.     }  
  76.   
  77.     const Mat *m1 = &map1, *m2 = &map2;  
  78.   
  79.     if( (map1.type() == CV_16SC2 && (map2.type() == CV_16UC1 || map2.type() == CV_16SC1 || !map2.data)) ||  
  80.         (map2.type() == CV_16SC2 && (map1.type() == CV_16UC1 || map1.type() == CV_16SC1 || !map1.data)) )  
  81.     {  
  82.         if( map1.type() != CV_16SC2 )  
  83.             std::swap(m1, m2);  
  84.     }  
  85.     else  
  86.     {  
  87.         CV_Assert( ((map1.type() == CV_32FC2 || map1.type() == CV_16SC2) && !map2.data) ||  
  88.             (map1.type() == CV_32FC1 && map2.type() == CV_32FC1) );  
  89.         planar_input = map1.channels() == 1;  
  90.     }  
  91.   
  92.     RemapInvoker invoker(src, dst, m1, m2, interpolation,  
  93.                          borderType, borderValue, planar_input, nnfunc, ifunc,  
  94.                          ctab);  
  95.     parallel_for_(Range(0, dst.rows), invoker, dst.total()/(double)(1<<16));  
  96. }  

好了,重映射先就讲这么多,在文章末尾还有一个综合一点的示例程序供大家学习。下面我们开始讲解SURF相关的内容。

 

 


二.SURF特征点检测



SURF算法有一些不错的内容和用法,OpenCV中使用颇多,浅墨会花一些篇幅对其进行讲解。今天的这篇文章只是一个小小的开头,主要介绍SURF特征点检测。

先简单了解一下SURF算法的大概内容吧。




2.1 SURF算法概览


SURF,我们简单介绍一下,英语全称为SpeededUp Robust Features,直译的话就是“加速版的具有鲁棒性的特征“算法,由Bay在2006年首次提出。SURF是尺度不变特征变换算法(SIFT算法)的加速版。一般来说,标准的SURF算子比SIFT算子快好几倍,并且在多幅图片下具有更好的稳定性。SURF最大的特征在于采用了harr特征以及积分图像的概念,这大大加快了程序的运行时间。SURF可以应用于计算机视觉的物体识别以及3D重构中。

 

PS: 由于我们的专栏侧重点是教大家如何快速入门OpenCV编程,不是来进行图像处理科普的,所以原理部分不会花笔墨多讲。一方面是浅墨也不喜欢讲这些枯燥的概念,另一方面是大家肯定应该也不喜欢看这些枯燥的原理,大家是喜欢看代码的〜( ̄▽ ̄〜)。就像小魏CPU童鞋在博客上写的,“Talk is cheap. Show me thecode.”

所以原理部分大家就自行用搜索引擎去学习吧,浅墨会将更多的笔墨用来分享网络上独一无二的干货。



2.2 前世今生——SURF类相关OpenCV源码剖析


OpenCV中关于SURF算法的部分,常常涉及到的是SURF、SurfFeatureDetector、SurfDescriptorExtractor这三个类,这一小节我们就来对他们进行人肉,挖挖其背景,看看他们究竟是什么来头。

 

在D:\Program Files (x86)\opencv\sources\modules\nonfree\include\opencv2\nonfree下的features2d.hpp头文件中,我们可以发现这样两句定义:

[cpp]  view plain copy print ?
  1. typedef SURF SurfFeatureDetector;  
  2. typedef SURF SurfDescriptorExtractor;  

我们都知道,typedef声明是为现有类型创建一个新的名字,类型别名。这就表示,SURF类忽然同时有了两个新名字SurfFeatureDetector以及SurfDescriptorExtractor。

也就是说,我们平常使用的SurfFeatureDetector类和SurfDescriptorExtractor类,其实就是SURF类,他们三者等价。

 

然后在这两句定义的上方,我们可以看到SURF类的类声明全貌:

[cpp]  view plain copy print ?
  1. class CV_EXPORTS_W SURF : public Feature2D  
  2. {  
  3. public:  
  4.     //! the default constructor  
  5.     CV_WRAP SURF();  
  6.     //! the full constructor taking all the necessary parameters  
  7.     explicit CV_WRAP SURF(double hessianThreshold,  
  8.                   int nOctaves=4, int nOctaveLayers=2,  
  9.                   bool extended=truebool upright=false);  
  10.   
  11.     //! returns the descriptor size in float's (64 or 128)  
  12.     CV_WRAP int descriptorSize() const;  
  13.   
  14.     //! returns the descriptor type  
  15.     CV_WRAP int descriptorType() const;  
  16.   
  17.     //! finds the keypoints using fast hessian detector used in SURF  
  18.     void operator()(InputArray img, InputArray mask,  
  19.                     CV_OUT vector<KeyPoint>& keypoints) const;  
  20.     //! finds the keypoints and computes their descriptors. Optionally it can compute descriptors for the user-provided keypoints  
  21.     void operator()(InputArray img, InputArray mask,  
  22.                     CV_OUT vector<KeyPoint>& keypoints,  
  23.                     OutputArray descriptors,  
  24.                     bool useProvidedKeypoints=falseconst;  
  25.   
  26.     AlgorithmInfo* info() const;  
  27.   
  28.     CV_PROP_RW double hessianThreshold;  
  29.     CV_PROP_RW int nOctaves;  
  30.     CV_PROP_RW int nOctaveLayers;  
  31.     CV_PROP_RW bool extended;  
  32.     CV_PROP_RW bool upright;  
  33.   
  34. protected:  
  35.   
  36.     void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;  
  37.     void computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;  
  38. };  


可以观察到,SURF类公共继承自Feature2D类,我们再次进行转到,可以在路径d:\Program Files(x86)\opencv\build\include\opencv2\features2d\features2d.hpp看到Feature2D类的声明:

[cpp]  view plain copy print ?
  1. class CV_EXPORTS_W Feature2D : public FeatureDetector, public DescriptorExtractor  
  2. {  
  3. public:  
  4.     /* 
  5.      * Detect keypoints in an image. 
  6.      * image        The image. 
  7.      * keypoints    The detected keypoints. 
  8.      * mask         Mask specifying where to look for keypoints (optional). Must be a char 
  9.      *              matrix with non-zero values in the region of interest. 
  10.      * useProvidedKeypoints If true, the method will skip the detection phase and will compute 
  11.      *                      descriptors for the provided keypoints 
  12.      */  
  13.     CV_WRAP_AS(detectAndCompute) virtual void operator()( InputArray image, InputArray mask,  
  14.                                      CV_OUT vector<KeyPoint>& keypoints,  
  15.                                      OutputArray descriptors,  
  16.                                      bool useProvidedKeypoints=false ) const = 0;  
  17.   
  18.     CV_WRAP void compute( const Mat& image, CV_OUT CV_IN_OUT std::vector<KeyPoint>& keypoints, CV_OUT Mat& descriptors ) const;  
  19.   
  20.     // Create feature detector and descriptor extractor by name.  
  21.     CV_WRAP static Ptr<Feature2D> create( const string& name );  
  22. };  


显然,Feature2D类又是公共继承自FeatureDetector以及 DescriptorExtractor类。继续刨根问底,我们看看其父类FeatureDetector以及 DescriptorExtractor类的定义。

首先是FeatureDetector类:

[cpp]  view plain copy print ?
  1. /************************************ Base Classes ************************************/  
  2.   
  3. /* 
  4.  * Abstract base class for 2D image feature detectors. 
  5.  */  
  6. class CV_EXPORTS_W FeatureDetector : public virtual Algorithm  
  7. {  
  8. public:  
  9.     virtual ~FeatureDetector();  
  10.   
  11.     /* 
  12.      * Detect keypoints in an image. 
  13.      * image        The image. 
  14.      * keypoints    The detected keypoints. 
  15.      * mask         Mask specifying where to look for keypoints (optional). Must be a char 
  16.      *              matrix with non-zero values in the region of interest. 
  17.      */  
  18.     CV_WRAP void detect( const Mat& image, CV_OUT vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;  
  19.   
  20.     /* 
  21.      * Detect keypoints in an image set. 
  22.      * images       Image collection. 
  23.      * keypoints    Collection of keypoints detected in an input images. keypoints[i] is a set of keypoints detected in an images[i]. 
  24.      * masks        Masks for image set. masks[i] is a mask for images[i]. 
  25.      */  
  26.     void detect( const vector<Mat>& images, vector<vector<KeyPoint> >& keypoints, const vector<Mat>& masks=vector<Mat>() ) const;  
  27.   
  28.     // Return true if detector object is empty  
  29.     CV_WRAP virtual bool empty() const;  
  30.   
  31.     // Create feature detector by detector name.  
  32.     CV_WRAP static Ptr<FeatureDetector> create( const string& detectorType );  
  33.   
  34. protected:  
  35.     virtual void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const = 0;  
  36.   
  37.     /* 
  38.      * Remove keypoints that are not in the mask. 
  39.      * Helper function, useful when wrapping a library call for keypoint detection that 
  40.      * does not support a mask argument. 
  41.      */  
  42.     static void removeInvalidPoints( const Mat& mask, vector<KeyPoint>& keypoints );  
  43. };  


这里,我们看到了我们以后经常会用到的detect( )方法重载的两个原型,原来是SURF类经过两层的继承,从FeatureDetector类继承而来的。

[cpp]  view plain copy print ?
  1. /* 
  2.    * Detect keypoints in an image. 
  3.    * image        The image. 
  4.    * keypoints    The detected keypoints. 
  5.    * mask         Mask specifying where to look for keypoints (optional). Must be a char 
  6.    *              matrix with non-zero values in the region of interest. 
  7.    */  
  8.   CV_WRAP void detect( const Mat& image, CV_OUT vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;  
  9.   
  10.   /* 
  11.    * Detect keypoints in an image set. 
  12.    * images       Image collection. 
  13.    * keypoints    Collection of keypoints detected in an input images. keypoints[i] is a set of keypoints detected in an images[i]. 
  14.    * masks        Masks for image set. masks[i] is a mask for images[i]. 
  15.    */  
  16.   void detect( const vector<Mat>& images, vector<vector<KeyPoint> >& keypoints, const vector<Mat>& masks=vector<Mat>() ) const;  

同样,看看SURF类的另一个“爷爷”DescriptorExtractor类的声明。

[cpp]  view plain copy print ?
  1. /* 
  2.  * Abstract base class for computing descriptors for image keypoints. 
  3.  * 
  4.  * In this interface we assume a keypoint descriptor can be represented as a 
  5.  * dense, fixed-dimensional vector of some basic type. Most descriptors used 
  6.  * in practice follow this pattern, as it makes it very easy to compute 
  7.  * distances between descriptors. Therefore we represent a collection of 
  8.  * descriptors as a Mat, where each row is one keypoint descriptor. 
  9.  */  
  10. class CV_EXPORTS_W DescriptorExtractor : public virtual Algorithm  
  11. {  
  12. public:  
  13.     virtual ~DescriptorExtractor();  
  14.   
  15.     /* 
  16.      * Compute the descriptors for a set of keypoints in an image. 
  17.      * image        The image. 
  18.      * keypoints    The input keypoints. Keypoints for which a descriptor cannot be computed are removed. 
  19.      * descriptors  Copmputed descriptors. Row i is the descriptor for keypoint i. 
  20.      */  
  21.     CV_WRAP void compute( const Mat& image, CV_OUT CV_IN_OUT vector<KeyPoint>& keypoints, CV_OUT Mat& descriptors ) const;  
  22.   
  23.     /* 
  24.      * Compute the descriptors for a keypoints collection detected in image collection. 
  25.      * images       Image collection. 
  26.      * keypoints    Input keypoints collection. keypoints[i] is keypoints detected in images[i]. 
  27.      *              Keypoints for which a descriptor cannot be computed are removed. 
  28.      * descriptors  Descriptor collection. descriptors[i] are descriptors computed for set keypoints[i]. 
  29.      */  
  30.     void compute( const vector<Mat>& images, vector<vector<KeyPoint> >& keypoints, vector<Mat>& descriptors ) const;  
  31.   
  32.     CV_WRAP virtual int descriptorSize() const = 0;  
  33.     CV_WRAP virtual int descriptorType() const = 0;  
  34.   
  35.     CV_WRAP virtual bool empty() const;  
  36.   
  37.     CV_WRAP static Ptr<DescriptorExtractor> create( const string& descriptorExtractorType );  
  38.   
  39. protected:  
  40.     virtual void computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const = 0;  
  41.   
  42.     /* 
  43.      * Remove keypoints within borderPixels of an image edge. 
  44.      */  
  45.     static void removeBorderKeypoints( vector<KeyPoint>& keypoints,  
  46.                                       Size imageSize, int borderSize );  
  47. };  

上述代码表明FeatureDetector 类和DescriptorExtractor类都虚继承自Algorithm基类。

呼,历经千辛万苦,终于,我们找到SURF类德高望重的祖先——OpenCV中的Algorithm基类。看看其原型声明:

[cpp]  view plain copy print ?
  1. /*! 
  2.   Base class for high-level OpenCV algorithms 
  3. */  
  4. class CV_EXPORTS_W Algorithm  
  5. {  
  6. public:  
  7.     Algorithm();  
  8.     virtual ~Algorithm();  
  9.     string name() const;  
  10.   
  11.     template<typename _Tp> typename ParamType<_Tp>::member_type get(const string& name) const;  
  12.     template<typename _Tp> typename ParamType<_Tp>::member_type get(const char* name) const;  
  13.   
  14.     CV_WRAP int getInt(const string& name) const;  
  15.     CV_WRAP double getDouble(const string& name) const;  
  16.     CV_WRAP bool getBool(const string& name) const;  
  17.     CV_WRAP string getString(const string& name) const;  
  18.     CV_WRAP Mat getMat(const string& name) const;  
  19.     CV_WRAP vector<Mat> getMatVector(const string& name) const;  
  20.     CV_WRAP Ptr<Algorithm> getAlgorithm(const string& name) const;  
  21.   
  22.     void set(const string& name, int value);  
  23.     void set(const string& name, double value);  
  24.     void set(const string& name, bool value);  
  25.     void set(const string& name, const string& value);  
  26.     void set(const string& name, const Mat& value);  
  27.     void set(const string& name, const vector<Mat>& value);  
  28.     void set(const string& name, const Ptr<Algorithm>& value);  
  29.     template<typename _Tp> void set(const string& name, const Ptr<_Tp>& value);  
  30.   
  31.     CV_WRAP void setInt(const string& name, int value);  
  32.     CV_WRAP void setDouble(const string& name, double value);  
  33.     CV_WRAP void setBool(const string& name, bool value);  
  34.     CV_WRAP void setString(const string& name, const string& value);  
  35.     CV_WRAP void setMat(const string& name, const Mat& value);  
  36.     CV_WRAP void setMatVector(const string& name, const vector<Mat>& value);  
  37.     CV_WRAP void setAlgorithm(const string& name, const Ptr<Algorithm>& value);  
  38.     template<typename _Tp> void setAlgorithm(const string& name, const Ptr<_Tp>& value);  
  39.   
  40.     void set(const char* name, int value);  
  41.     void set(const char* name, double value);  
  42.     void set(const char* name, bool value);  
  43.     void set(const char* name, const string& value);  
  44.     void set(const char* name, const Mat& value);  
  45.     void set(const char* name, const vector<Mat>& value);  
  46.     void set(const char* name, const Ptr<Algorithm>& value);  
  47.     template<typename _Tp> void set(const char* name, const Ptr<_Tp>& value);  
  48.   
  49.     void setInt(const char* name, int value);  
  50.     void setDouble(const char* name, double value);  
  51.     void setBool(const char* name, bool value);  
  52.     void setString(const char* name, const string& value);  
  53.     void setMat(const char* name, const Mat& value);  
  54.     void setMatVector(const char* name, const vector<Mat>& value);  
  55.     void setAlgorithm(const char* name, const Ptr<Algorithm>& value);  
  56.     template<typename _Tp> void setAlgorithm(const char* name, const Ptr<_Tp>& value);  
  57.   
  58.     CV_WRAP string paramHelp(const string& name) const;  
  59.     int paramType(const char* name) const;  
  60.     CV_WRAP int paramType(const string& name) const;  
  61.     CV_WRAP void getParams(CV_OUT vector<string>& names) const;  
  62.   
  63.   
  64.     virtual void write(FileStorage& fs) const;  
  65.     virtual void read(const FileNode& fn);  
  66.   
  67.     typedef Algorithm* (*Constructor)(void);  
  68.     typedef int (Algorithm::*Getter)() const;  
  69.     typedef void (Algorithm::*Setter)(int);  
  70.   
  71.     CV_WRAP static void getList(CV_OUT vector<string>& algorithms);  
  72.     CV_WRAP static Ptr<Algorithm> _create(const string& name);  
  73.     template<typename _Tp> static Ptr<_Tp> create(const string& name);  
  74.   
  75.     virtual AlgorithmInfo* info() const /* TODO: make it = 0;*/ { return 0; }  
  76. };  

关于这几个类缠绵悱恻的关系,画个图就一目了然了,也就是这样的过程:


【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第9张图片


 


3.3 drawKeypoints函数详解

 

因为接下来的示例程序需要用到drawKeypoints函数,我们在这里顺便讲一讲。

顾名思义,此函数用于绘制关键点。

[cpp]  view plain copy print ?
  1. C++: void drawKeypoints(const Mat&image, const vector<KeyPoint>& keypoints, Mat& outImage, constScalar& color=Scalar::all(-1), int flags=DrawMatchesFlags::DEFAULT )  

  • 第一个参数,const Mat&类型的src,输入图像。
  • 第二个参数,const vector<KeyPoint>&类型的keypoints,根据源图像得到的特征点,它是一个输出参数。
  • 第三个参数,Mat&类型的outImage,输出图像,其内容取决于第五个参数标识符falgs。
  • 第四个参数,const Scalar&类型的color,关键点的颜色,有默认值Scalar::all(-1)。
  • 第五个参数,int类型的flags,绘制关键点的特征标识符,有默认值DrawMatchesFlags::DEFAULT。可以在如下这个结构体中选取值。
[cpp]  view plain copy print ?
  1. struct DrawMatchesFlags  
  2. {  
  3.     enum  
  4.     {  
  5.         DEFAULT = 0, // Output image matrix will be created (Mat::create),  
  6.                      // i.e. existing memory of output image may be reused.  
  7.                      // Two source images, matches, and single keypoints  
  8.                      // will be drawn.  
  9.                      // For each keypoint, only the center point will be  
  10.                      // drawn (without a circle around the keypoint with the  
  11.                      // keypoint size and orientation).  
  12.         DRAW_OVER_OUTIMG = 1, // Output image matrix will not be  
  13.                        // created (using Mat::create). Matches will be drawn  
  14.                        // on existing content of output image.  
  15.         NOT_DRAW_SINGLE_POINTS = 2, // Single keypoints will not be drawn.  
  16.         DRAW_RICH_KEYPOINTS = 4 // For each keypoint, the circle around  
  17.                        // keypoint with keypoint size and orientation will  
  18.                        // be drawn.  
  19.     };  
  20. };  



三、综合示例部分

 

因为这次的两个知识点关联度不大,所以不方便组织起来成为一个综合示例程序。在这里我们分开将其放出。

 

3.1  重映射综合示例程序

 

先放出以remap为核心的综合示例程序,可以用按键控制四种不同的映射模式。且利用了OpenCV版本标识宏“CV_VERSION”,在帮助文字相关代码中加入了一句:

[cpp]  view plain copy print ?
  1. printf("\t当前使用的OpenCV版本为 OpenCV "CV_VERSION);   

便可以智能检测出当前使用的OpenCV版本,并输出。如图:

 【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第10张图片

按键说明也可以由上图看出。

放出这个程序详细注释的源代码:

[cpp]  view plain copy print ?
  1. //-----------------------------------【程序说明】----------------------------------------------  
  2. //      程序名称::《【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑 》 博文配套源码   
  3. //      开发所用IDE版本:Visual Studio 2010  
  4. //      开发所用OpenCV版本:   2.4.9  
  5. //      2014年6月15日 Created by 浅墨  
  6. //      配套博文链接: http://blog.csdn.net/poem_qianmo/article/details/30974513  
  7. //      PS:程序结合配合博文学习效果更佳  
  8. //      浅墨的微博:@浅墨_毛星云 http://weibo.com/1723155442  
  9. //      浅墨的知乎:http://www.zhihu.com/people/mao-xing-yun  
  10. //      浅墨的豆瓣:http://www.douban.com/people/53426472/  
  11. //----------------------------------------------------------------------------------------------  
  12.   
  13. //-----------------------------------【头文件包含部分】---------------------------------------  
  14. //      描述:包含程序所依赖的头文件  
  15. //----------------------------------------------------------------------------------------------   
  16. #include "opencv2/highgui/highgui.hpp"  
  17. #include "opencv2/imgproc/imgproc.hpp"  
  18. #include <iostream>  
  19.   
  20. //-----------------------------------【命名空间声明部分】--------------------------------------  
  21. //          描述:包含程序所使用的命名空间  
  22. //-----------------------------------------------------------------------------------------------  
  23. using namespace cv;  
  24. using namespace std;  
  25.   
  26. //-----------------------------------【宏定义部分】--------------------------------------------   
  27. //  描述:定义一些辅助宏   
  28. //------------------------------------------------------------------------------------------------   
  29. #define WINDOW_NAME "【程序窗口】"        //为窗口标题定义的宏   
  30.   
  31.   
  32. //-----------------------------------【全局变量声明部分】--------------------------------------  
  33. //          描述:全局变量的声明  
  34. //-----------------------------------------------------------------------------------------------  
  35. Mat g_srcImage, g_dstImage;  
  36. Mat g_map_x, g_map_y;  
  37.   
  38.   
  39. //-----------------------------------【全局函数声明部分】--------------------------------------  
  40. //          描述:全局函数的声明  
  41. //-----------------------------------------------------------------------------------------------  
  42. int update_map( int key);  
  43. static void ShowHelpText( );//输出帮助文字  
  44.   
  45. //-----------------------------------【main( )函数】--------------------------------------------  
  46. //          描述:控制台应用程序的入口函数,我们的程序从这里开始执行  
  47. //-----------------------------------------------------------------------------------------------  
  48. int main( int argc, char** argv )  
  49. {  
  50.     //改变console字体颜色  
  51.     system("color 2F");   
  52.   
  53.     //显示帮助文字  
  54.     ShowHelpText();  
  55.   
  56.     //【1】载入原始图  
  57.     g_srcImage = imread( "1.jpg", 1 );  
  58.     if(!g_srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; }    
  59.     imshow("原始图",g_srcImage);  
  60.   
  61.     //【2】创建和原始图一样的效果图,x重映射图,y重映射图  
  62.     g_dstImage.create( g_srcImage.size(), g_srcImage.type() );  
  63.     g_map_x.create( g_srcImage.size(), CV_32FC1 );  
  64.     g_map_y.create( g_srcImage.size(), CV_32FC1 );  
  65.   
  66.     //【3】创建窗口并显示  
  67.     namedWindow( WINDOW_NAME, CV_WINDOW_AUTOSIZE );  
  68.     imshow(WINDOW_NAME,g_srcImage);  
  69.   
  70.     //【4】轮询按键,更新map_x和map_y的值,进行重映射操作并显示效果图  
  71.     while( 1 )  
  72.     {  
  73.         //获取键盘按键    
  74.         int key = waitKey(0);    
  75.   
  76.         //判断ESC是否按下,若按下便退出    
  77.         if( (key & 255) == 27 )    
  78.         {    
  79.             cout << "程序退出...........\n";    
  80.             break;    
  81.         }    
  82.   
  83.         //根据按下的键盘按键来更新 map_x & map_y的值. 然后调用remap( )进行重映射  
  84.         update_map(key);  
  85.         remap( g_srcImage, g_dstImage, g_map_x, g_map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0,0, 0) );  
  86.   
  87.         //显示效果图  
  88.         imshow( WINDOW_NAME, g_dstImage );  
  89.     }  
  90.     return 0;  
  91. }  
  92.   
  93. //-----------------------------------【update_map( )函数】--------------------------------  
  94. //          描述:根据按键来更新map_x与map_x的值  
  95. //----------------------------------------------------------------------------------------------  
  96. int update_map( int key )  
  97. {  
  98.     //双层循环,遍历每一个像素点  
  99.     forint j = 0; j < g_srcImage.rows;j++)  
  100.     {   
  101.         forint i = 0; i < g_srcImage.cols;i++)  
  102.         {  
  103.             switch(key)  
  104.             {  
  105.             case '1'// 键盘【1】键按下,进行第一种重映射操作  
  106.                 if( i > g_srcImage.cols*0.25 && i < g_srcImage.cols*0.75 && j > g_srcImage.rows*0.25 && j < g_srcImage.rows*0.75)  
  107.                 {  
  108.                     g_map_x.at<float>(j,i) = static_cast<float>(2*( i - g_srcImage.cols*0.25 ) + 0.5);  
  109.                     g_map_y.at<float>(j,i) = static_cast<float>(2*( j - g_srcImage.rows*0.25 ) + 0.5);  
  110.                 }  
  111.                 else  
  112.                 {   
  113.                     g_map_x.at<float>(j,i) = 0;  
  114.                     g_map_y.at<float>(j,i) = 0;  
  115.                 }  
  116.                 break;  
  117.             case '2':// 键盘【2】键按下,进行第二种重映射操作  
  118.                 g_map_x.at<float>(j,i) = static_cast<float>(i);  
  119.                 g_map_y.at<float>(j,i) = static_cast<float>(g_srcImage.rows - j);  
  120.                 break;  
  121.             case '3':// 键盘【3】键按下,进行第三种重映射操作  
  122.                 g_map_x.at<float>(j,i) = static_cast<float>(g_srcImage.cols - i);  
  123.                 g_map_y.at<float>(j,i) = static_cast<float>(j);  
  124.                 break;  
  125.             case '4':// 键盘【4】键按下,进行第四种重映射操作  
  126.                 g_map_x.at<float>(j,i) = static_cast<float>(g_srcImage.cols - i);  
  127.                 g_map_y.at<float>(j,i) = static_cast<float>(g_srcImage.rows - j);  
  128.                 break;  
  129.             }   
  130.         }  
  131.     }  
  132.     return 1;  
  133. }  
  134.   
  135. //-----------------------------------【ShowHelpText( )函数】----------------------------------    
  136. //      描述:输出一些帮助信息    
  137. //----------------------------------------------------------------------------------------------    
  138. static void ShowHelpText()    
  139. {    
  140.     //输出一些帮助信息    
  141.     printf("\n\n\n\t欢迎来到重映射示例程序~\n\n");    
  142.     printf("\t当前使用的OpenCV版本为 OpenCV "CV_VERSION);    
  143.     printf( "\n\n\t按键操作说明: \n\n"    
  144.         "\t\t键盘按键【ESC】- 退出程序\n"    
  145.         "\t\t键盘按键【1】-  第一种映射方式\n"    
  146.         "\t\t键盘按键【2】- 第二种映射方式\n"    
  147.         "\t\t键盘按键【3】- 第三种映射方式\n"    
  148.         "\t\t键盘按键【4】- 第四种映射方式\n"    
  149.         "\n\n\t\t\t\t\t\t\t\t by浅墨\n\n\n"    
  150.         );    
  151. }    


运行效果图。首先是原始图:

 【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第11张图片

第一种重映射:

【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第12张图片

第二种重映射:

【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第13张图片

第三种重映射:

【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第14张图片

第四种重映射:

【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第15张图片

 



3.2 SURF特征点检测综合示例程序


这个示例程涉及到如下三个方面:

 

  • 使用 FeatureDetector 接口来发现感兴趣点。
  • 使用 SurfFeatureDetector 以及其函数 detect 来实现检测过程
  • 使用函数 drawKeypoints 绘制检测到的关键点。

详细注释的源代码:

[cpp]  view plain copy print ?
  1. //-----------------------------------【程序说明】----------------------------------------------  
  2. //      程序名称::《【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑 》 博文配套源码 之【SURF特征点检测】  
  3. //      开发所用IDE版本:Visual Studio 2010  
  4. //      开发所用OpenCV版本:   2.4.9  
  5. //      2014年6月15日 Created by 浅墨  
  6. //      配套博文链接: http://blog.csdn.net/poem_qianmo/article/details/30974513  
  7. //      PS:程序结合配合博文学习效果更佳  
  8. //      浅墨的微博:@浅墨_毛星云 http://weibo.com/1723155442  
  9. //      浅墨的知乎:http://www.zhihu.com/people/mao-xing-yun  
  10. //      浅墨的豆瓣:http://www.douban.com/people/53426472/  
  11. //----------------------------------------------------------------------------------------------  
  12.   
  13. //-----------------------------------【头文件包含部分】---------------------------------------  
  14. //      描述:包含程序所依赖的头文件  
  15. //----------------------------------------------------------------------------------------------  
  16. #include "opencv2/core/core.hpp"  
  17. #include "opencv2/features2d/features2d.hpp"  
  18. #include "opencv2/highgui/highgui.hpp"  
  19. #include "opencv2/nonfree/nonfree.hpp"  
  20. #include <iostream>  
  21.   
  22.   
  23. //-----------------------------------【命名空间声明部分】--------------------------------------  
  24. //          描述:包含程序所使用的命名空间  
  25. //-----------------------------------------------------------------------------------------------  
  26. using namespace cv;  
  27.   
  28. //-----------------------------------【全局函数声明部分】--------------------------------------  
  29. //          描述:全局函数的声明  
  30. //-----------------------------------------------------------------------------------------------  
  31. static void ShowHelpText( );//输出帮助文字  
  32.   
  33. //-----------------------------------【main( )函数】--------------------------------------------  
  34. //   描述:控制台应用程序的入口函数,我们的程序从这里开始执行  
  35. //-----------------------------------------------------------------------------------------------  
  36. int main( int argc, char** argv )  
  37. {  
  38.     //【0】改变console字体颜色      
  39.     system("color 2F");      
  40.   
  41.     //【0】显示帮助文字    
  42.     ShowHelpText( );    
  43.   
  44.     //【1】载入源图片并显示  
  45.     Mat srcImage1 = imread("1.jpg", 1 );  
  46.     Mat srcImage2 = imread("2.jpg", 1 );  
  47.     if( !srcImage1.data || !srcImage2.data )//检测是否读取成功  
  48.     { printf("读取图片错误,请确定目录下是否有imread函数指定名称的图片存在~! \n"); return false; }   
  49.     imshow("原始图1",srcImage1);  
  50.     imshow("原始图2",srcImage2);  
  51.   
  52.     //【2】定义需要用到的变量和类  
  53.     int minHessian = 400;//定义SURF中的hessian阈值特征点检测算子  
  54.     SurfFeatureDetector detector( minHessian );//定义一个SurfFeatureDetector(SURF) 特征检测类对象  
  55.     std::vector<KeyPoint> keypoints_1, keypoints_2;//vector模板类是能够存放任意类型的动态数组,能够增加和压缩数据  
  56.   
  57.     //【3】调用detect函数检测出SURF特征关键点,保存在vector容器中  
  58.     detector.detect( srcImage1, keypoints_1 );  
  59.     detector.detect( srcImage2, keypoints_2 );  
  60.   
  61.     //【4】绘制特征关键点  
  62.     Mat img_keypoints_1; Mat img_keypoints_2;  
  63.     drawKeypoints( srcImage1, keypoints_1, img_keypoints_1, Scalar::all(-1), DrawMatchesFlags::DEFAULT );  
  64.     drawKeypoints( srcImage2, keypoints_2, img_keypoints_2, Scalar::all(-1), DrawMatchesFlags::DEFAULT );  
  65.   
  66.     //【5】显示效果图  
  67.     imshow("特征点检测效果图1", img_keypoints_1 );  
  68.     imshow("特征点检测效果图2", img_keypoints_2 );  
  69.   
  70.     waitKey(0);  
  71.     return 0;  
  72. }  
  73.   
  74.   
  75. //-----------------------------------【ShowHelpText( )函数】----------------------------------  
  76. //          描述:输出一些帮助信息  
  77. //----------------------------------------------------------------------------------------------  
  78. void ShowHelpText()  
  79. {   
  80.     //输出一些帮助信息    
  81.     printf("\n\n\n\t欢迎来到【SURF特征点检测】示例程序~\n\n");      
  82.     printf("\t当前使用的OpenCV版本为 OpenCV "CV_VERSION);    
  83.     printf( "\n\n\t按键操作说明: \n\n"       
  84.                 "\t\t键盘按键任意键- 退出程序\n\n"  
  85.                 "\n\n\t\t\t\t\t\t\t\t by浅墨\n\n\n");    
  86.   
  87. }  


这里的图片素材是浅墨自己用手机拍的自己写的书:)

第一组图片对比效果:

【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第16张图片 【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第17张图片

第二组图片对比效果:

【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第18张图片 【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第19张图片



本篇文章的配套源代码请点击这里下载:

【浅墨OpenCV入门教程之十七】配套源代码之【重映射】 下载

------------------------------------------------------------------------

【浅墨OpenCV入门教程之十七】配套源代码之【SURF特征点检测】下载


 【OpenCV入门教程之十七】OpenCV重映射 & SURF特征点检测合辑_第20张图片

OK,今天的内容大概就是这些,我们下篇文章见:)

 

你可能感兴趣的:(C++,opencv)