仿射变换和SURF特征点匹配

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

这里写图片描述

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   

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)原图片

仿射变换和SURF特征点匹配_第1张图片

  (2)经过warp后的图片

仿射变换和SURF特征点匹配_第2张图片

  (3)经过warp和rotate后的图片

仿射变换和SURF特征点匹配_第3张图片

二、特征匹配
2.1、SURF特征点描述
  在opencv中,使用SURF算法进行特征点描述主要用到了drawMatches方法、SurfDescriptorExtractor类和BruteForceMatcher类。其中drawMatches( )函数用于绘制出相匹配的两个图像的关键点,利用SurfDescriptorExtractor类进行特征向量的相关计算,利用BruteForceMatcher类主要用于特征匹配。下面对drawMatches( )函数进行分析。

 void drawMatches(const Mat& img1,  
constvector& keypoints1,  
 const Mat& img2,  
 constvector& keypoints2,  
 constvector& 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   
#include  
#include   

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 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)原图

仿射变换和SURF特征点匹配_第4张图片

仿射变换和SURF特征点匹配_第5张图片

  (2)特征匹配效果图

仿射变换和SURF特征点匹配_第6张图片

你可能感兴趣的:(仿射变换和SURF特征点匹配)