仿射变换和SURF特征点匹配

来源:互联网 发布:angularjs.js 2.0下载 编辑:程序博客网 时间:2024/05/16 06:31

一、仿射变换
1.1、仿射变换简介
  仿射变换(Affine Transformation)是空间直角坐标系的变换,从一个二维坐标变换到另一个二维坐标,仿射变换是一个线性变换,他保持了图像的“平行性”和“平直性”,即图像中原来的直线和平行线,变换后仍然保持原来的直线和平行线,仿射变换比较常用的特殊变换有平移(Translation)、缩放(Scale)、翻转(Flip)、旋转(Rotation)和剪切(Shear)。一个任意的仿射变换都能表示为乘以一个矩阵(线性变换)接着再加上一个向量(平移)的形式。
  在进行仿射变换的关键在于求解放射矩阵M,通过其对原图像进行变换得到最后的目标图像。在OpenCV中,与仿射变换相关的函数一般涉及到warpAffine( )和getRotationMatrix2D( )这两个。下面对这两个函数进行解析。
1.2、warpAffine( )函数解析
  在OpenCV中,函数warpAffine( )是来实现一些简单的重映射。它是依据下式对图像做仿射变换。

![这里写图片描述](http://img.blog.csdn.net/20170709204747763?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hlbnl1a3VhaTY2MjU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
void warpAffine(InputArray src,                OutputArray dst,                 Size dsize,                 int flags=INTER_LINEAR,                int borderMode=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。
1.3、getRotationMatrix2D( )函数
  getRotationMatrix2D( )函数是用来计算二维旋转的变换矩阵。

 Mat getRotationMatrix2D(Point2fcenter,                          double angle,                          double scale)  

  第一个参数,Point2f类型的center,表示源图像的旋转中心。
  第二个参数,double类型的angle,旋转角度。角度为正值表示向逆时针旋转(坐标原点是左上角)。
  第三个参数,double类型的scale,缩放系数。
1.4、仿射变换实例
  1、代码

#include "opencv2/highgui/highgui.hpp"  #include "opencv2/imgproc/imgproc.hpp"  #include <iostream>  using namespace cv;using namespace std;#define WINDOW_NAME1 "【原始图窗口】"                  //为窗口标题定义的宏   #define WINDOW_NAME2 "【经过Warp后的图像】"        //为窗口标题定义的宏   #define WINDOW_NAME3 "【经过Warp和Rotate后的图像】"        //为窗口标题定义的宏   int main(){    //【0】改变console字体颜色      system("color 1A");    //【1】参数准备      //定义两组点,代表两个三角形      Point2f srcTriangle[3];    Point2f dstTriangle[3];    //定义一些Mat变量      Mat rotMat(2, 3, CV_32FC1);    Mat warpMat(2, 3, CV_32FC1);    Mat srcImage, dstImage_warp, dstImage_warp_rotate;    //【2】加载源图像并作一些初始化      srcImage = imread("1.jpg", 1);    if (!srcImage.data) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; }    // 设置目标图像的大小和类型与源图像一致      dstImage_warp = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());    //【3】设置源图像和目标图像上的三组点以计算仿射变换      srcTriangle[0] = Point2f(0, 0);    srcTriangle[1] = Point2f(static_cast<float>(srcImage.cols - 1), 0);    srcTriangle[2] = Point2f(0, static_cast<float>(srcImage.rows - 1));    dstTriangle[0] = Point2f(static_cast<float>(srcImage.cols*0.0), static_cast<float>(srcImage.rows*0.33));    dstTriangle[1] = Point2f(static_cast<float>(srcImage.cols*0.65), static_cast<float>(srcImage.rows*0.35));    dstTriangle[2] = Point2f(static_cast<float>(srcImage.cols*0.15), static_cast<float>(srcImage.rows*0.6));    //【4】求得仿射变换      warpMat = getAffineTransform(srcTriangle, dstTriangle);    //【5】对源图像应用刚刚求得的仿射变换      warpAffine(srcImage, dstImage_warp, warpMat, dstImage_warp.size());    //【6】对图像进行缩放后再旋转      // 计算绕图像中点顺时针旋转50度缩放因子为0.6的旋转矩阵      Point center = Point(dstImage_warp.cols / 2, dstImage_warp.rows / 2);    double angle = -30.0;    double scale = 0.8;    // 通过上面的旋转细节信息求得旋转矩阵      rotMat = getRotationMatrix2D(center, angle, scale);    // 旋转已缩放后的图像      warpAffine(dstImage_warp, dstImage_warp_rotate, rotMat, dstImage_warp.size());    //【7】显示结果      imshow(WINDOW_NAME1, srcImage);    imshow(WINDOW_NAME2, dstImage_warp);    imshow(WINDOW_NAME3, dstImage_warp_rotate);    // 等待用户按任意按键退出程序      waitKey(0);    return 0;}

  2、运行结果
  (1)原图片

![这里写图片描述](http://img.blog.csdn.net/20170709205715827?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hlbnl1a3VhaTY2MjU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)  (2)经过warp后的图片
![这里写图片描述](http://img.blog.csdn.net/20170709205726310?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hlbnl1a3VhaTY2MjU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)  (3)经过warp和rotate后的图片
![这里写图片描述](http://img.blog.csdn.net/20170709205737570?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ2hlbnl1a3VhaTY2MjU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)二、特征匹配2.1、SURF特征点描述  在opencv中,使用SURF算法进行特征点描述主要用到了drawMatches方法、SurfDescriptorExtractor类和BruteForceMatcher类。其中drawMatches( )函数用于绘制出相匹配的两个图像的关键点,利用SurfDescriptorExtractor类进行特征向量的相关计算,利用BruteForceMatcher类主要用于特征匹配。下面对drawMatches( )函数进行分析。
 void drawMatches(const Mat& img1,  constvector<KeyPoint>& keypoints1,   const Mat& img2,   constvector<KeyPoint>& keypoints2,   constvector<DMatch>& matches1to2,   Mat& outImg,   const Scalar&matchColor=Scalar::all(-1),   const Scalar&singlePointColor=Scalar::all(-1),   const vector<char>&matchesMask=vector<char>(),   intflags=DrawMatchesFlags::DEFAULT )  

  第一个参数,const Mat&类型的img1,第一幅源图像。
  第二个参数,const vector&类型的keypoints1,根据第一幅源图像得到的特征点,它是一个输出参数。
  第三个参数,const Mat&类型的img2,第二幅源图像。
  第四个参数,const vector&类型的keypoints2,根据第二幅源图像得到的特征点,它是一个输出参数。
  第五个参数,matches1to2,第一幅图像到第二幅图像的匹配点,即表示每一个图1中的特征点都在图2中有一一对应的点、
  第六个参数,Mat&类型的outImg,输出图像,其内容取决于第五个参数标识符falgs。
  第七个参数,const Scalar&类型的matchColor,匹配的输出颜色,即线和关键点的颜色。它有默认值Scalar::all(-1),表示颜色是随机生成的。
  第八个参数,const Scalar&类型的singlePointColor,单一特征点的颜色,它也有表示随机生成颜色的默认值Scalar::all(-1)。
  第九个参数,matchesMask,确定哪些匹配是会绘制出来的掩膜,如果掩膜为空,表示所有匹配都进行绘制。
  第十个参数,int类型的flags,特征绘制的标识符,有默认值DrawMatchesFlags::DEFAULT。
3.2、图像特征匹配实例
  1、实现图像特征匹配的步骤如下:
  (1)使用 DescriptorExtractor 接口来寻找关键点对应的特征向量。
  (2)使用 SurfDescriptorExtractor 以及它的函数 compute( )来完成特定的计算。
  (3)使用 BruteForceMatcher 来匹配特征向量。
  (4)使用函数 drawMatches 来绘制检测到的匹配点。
  2、代码

#include "opencv2/core/core.hpp"  #include "opencv2/features2d/features2d.hpp"  #include "opencv2/highgui/highgui.hpp"  #include <opencv2/nonfree/nonfree.hpp>  #include<opencv2/legacy/legacy.hpp>  #include <iostream>  using namespace cv;using namespace std;int main(){    //【0】改变console字体颜色      system("color 1A");    //【1】载入素材图      Mat srcImage1 = imread("1.jpg", 1);    Mat srcImage2 = imread("2.jpg", 1);    if (!srcImage1.data || !srcImage2.data)    {        printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false;    }    //【2】使用SURF算子检测关键点      int minHessian = 2000;//SURF算法中的hessian阈值      SurfFeatureDetector detector(minHessian);//定义一个SurfFeatureDetector(SURF) 特征检测类对象        std::vector<KeyPoint> keyPoint1, keyPoints2;//vector模板类,存放任意类型的动态数组      //【3】调用detect函数检测出SURF特征关键点,保存在vector容器中      detector.detect(srcImage1, keyPoint1);    detector.detect(srcImage2, keyPoints2);    //【4】计算描述符(特征向量)      SurfDescriptorExtractor extractor;    Mat descriptors1, descriptors2;    extractor.compute(srcImage1, keyPoint1, descriptors1);    extractor.compute(srcImage2, keyPoints2, descriptors2);    //【5】使用BruteForce进行匹配      // 实例化一个匹配器      BruteForceMatcher< L2<float> > matcher;    std::vector< DMatch > matches;    //匹配两幅图中的描述子(descriptors)      matcher.match(descriptors1, descriptors2, matches);    //【6】绘制从两个图像中匹配出的关键点      Mat imgMatches;    drawMatches(srcImage1, keyPoint1, srcImage2, keyPoints2, matches, imgMatches);//进行绘制      //【7】显示效果图      imshow("匹配图", imgMatches);    waitKey(0);    return 0;}

  3、运行效果
  (1)原图

这里写图片描述

这里写图片描述

  (2)特征匹配效果图

这里写图片描述

阅读全文
0 0