【OpenCV入门教程之十八】OpenCV仿射变换 SURF特征点描述合辑

本文深入介绍了OpenCV中的仿射变换,包括初识仿射变换、变换求法、相关函数的使用,如warpAffine和getRotationMatrix2D。此外,文章还探讨了SURF特征点描述,讲解了drawMatches函数和BruteForceMatcher类的源码分析,以及如何利用这些工具进行特征匹配和图像示例程序。
摘要由CSDN通过智能技术生成
               


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

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

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

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

 邮箱: happylifemxy@163.com

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

本篇文章中,我们一起探讨了OpenCV中仿射变换和SURF特征点描述相关的知识点,主要一起了解OpenCV中仿射变换相关的函数warpAffine和 getRotationMatrix2D,SURF算法在OpenCV中进一步的体现与应用。此博文一共有两个配套的麻雀虽小但五脏俱全的示例程序,其经过浅墨详细注释过的代码都在文中贴出,且文章最后提供了综合示例程序的下载。
依然是先看看示例程序截图:










一、仿射变换



1.1 初识仿射变换


仿射变换(Affine Transformation或 Affine Map),又称仿射映射,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间的过程。它保持了二维图形的“平直性”(即:直线经过变换之后依然是直线)和“平行性”(即:二维图形之间的相对位置关系保持不变,平行线依然是平行线,且直线上点的位置顺序不变)。

一个任意的仿射变换都能表示为乘以一个矩阵(线性变换)接着再加上一个向量(平移)的形式。

那么, 我们能够用仿射变换来表示如下三种常见的变换形式:

  • 旋转,rotation (线性变换)
  • 平移,translation(向量加)
  • 缩放,scale(线性变换)

如果进行更深层次的理解,仿射变换代表的是两幅图之间的一种映射关系。

而我们通常使用2 x 3的矩阵来表示仿射变换。


                             

 

 

考虑到我们要使用矩阵 A 和 B 对二维向量 做变换, 所以也能表示为下列形式:


  或者     


 

即:       

 

 


1. 2 仿射变换的求法


我们知道,仿射变换表示的就是两幅图片之间的一种联系 . 关于这种联系的信息大致可从以下两种场景获得:

  • <1>已知 X和T,而且我们知道他们是有联系的. 接下来我们的工作就是求出矩阵 M
  • <2>已知 M和X,要想求得 T. 我们只要应用算式即可. 对于这种联系的信息可以用矩阵 M 清晰的表达 (即给出明确的2×3矩阵) 或者也可以用两幅图片点之间几何关系来表达。
我们形象地说明一下,因为矩阵 M 联系着两幅图片, 我们就以其表示两图中各三点直接的联系为例。

见下图:


其中,点1, 2 和 3 (在图一中形成一个三角形) 与图二中三个点是一一映射的关系, 且他们仍然形成三角形, 但形状已经和之前不一样了。我们能通过这样两组三点求出仿射变换 (可以选择自己喜欢的点), 接着就可以把仿射变换应用到图像中去。

 

 




1.3 仿射变换相关的函数使用


OpenCV仿射变换相关的函数一般涉及到warpAffine和getRotationMatrix2D这两个:

  • 使用OpenCV函数warpAffine 来实现一些简单的重映射.
  • 使用OpenCV函数getRotationMatrix2D 来获得旋转矩阵。

下面分别对其进行讲解。

 



1.3.1 warpAffine函数详解



warpAffine函数的作用是依据如下式子,对图像做仿射变换。




函数原型如下:

C++: void warpAffine(InputArray src,OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, intborderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())

  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。
  • 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,需和源图片有一样的尺寸和类型。
  • 第三个参数,InputArray类型的M,2×3的变换矩阵。
  • 第四个参数,Size类型的dsize,表示输出图像的尺寸。
  • 第五个参数,int类型的flags,插值方法的标识符。此参数有默认值INTER_LINEAR(线性插值),可选的插值方式如下:
    • INTER_NEAREST - 最近邻插值
    • INTER_LINEAR - 线性插值(默认值)
    • INTER_AREA - 区域插值
    • INTER_CUBIC –三次样条插值
    • INTER_LANCZOS4 -Lanczos插值
    • CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval.
    • CV_WARP_INVERSE_MAP –表示M为输出图像到输入图像的反变换,即 。因此可以直接用来做象素插值。否则, warpAffine函数从M矩阵得到反变换。
  • 第六个参数,int类型的borderMode,边界像素模式,默认值为BORDER_CONSTANT。
  • 第七个参数,const Scalar&类型的borderValue,在恒定的边界情况下取的值,默认值为Scalar(),即0。

另外提一点,我们的WarpAffine函数与一个叫做cvGetQuadrangleSubPix( )的函数类似,但是不完全相同。 WarpAffine要求输入和输出图像具有同样的数据类型,有更大的资源开销(因此对小图像不太合适)而且输出图像的部分可以保留不变。而 cvGetQuadrangleSubPix 可以精确地从8位图像中提取四边形到浮点数缓存区中,具有比较小的系统开销,而且总是全部改变输出图像的内容。

 

 



1.3.2 getRotationMatrix2D



计算二维旋转变换矩阵。变换会将旋转中心映射到它自身。

C++: Mat getRotationMatrix2D(Point2fcenter, double angle, double scale)

  • 第一个参数,Point2f类型的center,表示源图像的旋转中心。
  • 第二个参数,double类型的angle,旋转角度。角度为正值表示向逆时针旋转(坐标原点是左上角)。
  • 第三个参数,double类型的scale,缩放系数。

 

此函数计算以下矩阵:

 

其中:


 

 


1.4 仿射变换相关核心函数在OpenCV中的实现源代码

 


这个部分贴出OpenCV中本节相关函数的源码实现细节,来给想了解实现细节的小伙伴们参考。浅墨暂时不在源码的细节上挖深作详细注释。

 

1.4.1 OpenCV2.X中warpAffine函数源代码


首先,是warpAffine函数的实现源码。

void cv::warpAffine( InputArray _src,OutputArray _dst,                     InputArray _M0, Sizedsize,                     int flags, int borderType,const Scalar& borderValue ){   Mat src = _src.getMat(), M0 = _M0.getMat();   _dst.create( dsize.area() == 0 ? src.size() : dsize, src.type() );   Mat dst = _dst.getMat();   CV_Assert( src.cols > 0 && src.rows > 0 );   if( dst.data == src.data )       src = src.clone();    double M[6];   Mat matM(2, 3, CV_64F, M);   int interpolation = flags & INTER_MAX;   if( interpolation == INTER_AREA )       interpolation = INTER_LINEAR;    CV_Assert( (M0.type() == CV_32F || M0.type() == CV_64F) &&M0.rows == 2 && M0.cols == 3 );   M0.convertTo(matM, matM.type()); #ifdef HAVE_TEGRA_OPTIMIZATION   if( tegra::warpAffine(src, dst, M, flags, borderType, borderValue) )       return;#endif    if( !(flags & WARP_INVERSE_MAP) )    {       double D = M[0]*M[4] - M[1]*M[3];       D = D != 0 ? 1./D : 0;       double A11 = M[4]*D, A22=M[0]*D;       M[0] = A11; M[1] *= -D;       M[3] *= -D; M[4] = A22;       double b1 = -M[0]*M[2] - M[1]*M[5];       double b2 = -M[3]*M[2] - M[4]*M[5];       M[2] = b1; M[5] = b2;    }    int x;   AutoBuffer<int> _abdelta(dst.cols*2);   int* adelta = &_abdelta[0], *bdelta = adelta + dst.cols;   const int AB_BITS = MAX(10, (int)INTER_BITS);   const int AB_SCALE = 1 << AB_BITS;/*#if defined (HAVE_IPP) &&(IPP_VERSION_MAJOR >= 7)   int depth = src.depth();   int channels = src.channels();    if( ( depth == CV_8U || depth == CV_16U ||depth == CV_32F ) &&       ( channels == 1 || channels == 3 || channels == 4 ) &&       ( borderType == cv::BORDER_TRANSPARENT || ( borderType ==cv::BORDER_CONSTANT ) ) )    {       int type = src.type();       ippiWarpAffineBackFunc ippFunc =           type == CV_8UC1 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_8u_C1R :           type == CV_8UC3 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_8u_C3R :           type == CV_8UC4 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_8u_C4R :           type == CV_16UC1 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_16u_C1R :           type == CV_16UC3 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_16u_C3R :           type == CV_16UC4 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_16u_C4R :           type == CV_32FC1 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_32f_C1R :           type == CV_32FC3 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_32f_C3R :           type == CV_32FC4 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_32f_C4R :           0;       int mode =           flags == INTER_LINEAR ? IPPI_INTER_LINEAR : 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值