surf特征+FLANN特征匹配+knn筛选匹配点+单应性矩阵映射

简介: surf特征+FLANN特征匹配+knn筛选匹配点+单应性矩阵映射

surf特征+FLANN特征匹配+knn筛选匹配点+单应性矩阵映射

#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <opencv2/core/core.hpp>
#include "opencv2/nonfree/features2d.hpp"
#include<opencv2/legacy/legacy.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;
int main( )
{
  Mat img_1 = imread( "C:\\temp\\PyramidPattern.jpg", CV_LOAD_IMAGE_GRAYSCALE );
  Mat img_2 = imread( "C:\\temp\\PyramidPatternTest.bmp", CV_LOAD_IMAGE_GRAYSCALE );
  if( !img_1.data || !img_2.data )
  { return -1; }
  //-- Step 1: Detect the keypoints using SURF Detector
  int minHessian = 400;
  SurfFeatureDetector detector( minHessian );
  vector<KeyPoint> keypoints_1, keypoints_2;
  detector.detect( img_1, keypoints_1 );
  detector.detect( img_2, keypoints_2 );
  //-- Step 2: Calculate descriptors (feature vectors)
  SurfDescriptorExtractor extractor;
  Mat descriptors_1, descriptors_2;
  extractor.compute( img_1, keypoints_1, descriptors_1 );
  extractor.compute( img_2, keypoints_2, descriptors_2 );
  //-- Step 3: Matching descriptor vectors using FLANN matcher
  FlannBasedMatcher matcher;
  vector< DMatch > matches;
  vector<vector<DMatch>> m_knnMatches;
  matches.clear();
  const float minRatio=1.f / 1.5f;
  matcher.knnMatch(descriptors_1,descriptors_2,m_knnMatches,2);
  for (int i=0; i<m_knnMatches.size(); i++)
  {
    const DMatch& bestMatch=m_knnMatches[i][0];
    const DMatch& betterMatch=m_knnMatches[i][1];
    float distanceRatio=bestMatch.distance/betterMatch.distance;
    if (distanceRatio<minRatio)
    {
      matches.push_back(bestMatch);
    }
  }
  vector< DMatch > good_matches;
  if(!matches.size())
  {
    cout<<"matches is empty! "<<endl;
    return -1;
  }
  else if (matches.size()<4)
  {
    cout<<matches.size()<<" points matched is not enough "<<endl;
  }
  else //单应性矩阵的计算最少得使用4个点
  {
    for( int i = 0; i < matches.size(); i++ )
    { 
      good_matches.push_back(matches[i]);
    }  
    Mat img_matches;
    drawMatches( img_1, keypoints_1, img_2, keypoints_2, 
      good_matches, img_matches, Scalar::all(-1), Scalar::all(-1), 
      vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); 
    //☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★
    //-- Localize the object from img_1 in img_2 
    vector<Point2f> obj;
    vector<Point2f> scene;
    for( int i = 0; i < good_matches.size(); i++ )
    {
      //-- Get the keypoints from the good matches
      obj.push_back( keypoints_1[ good_matches[i].queryIdx ].pt );
      scene.push_back( keypoints_2[ good_matches[i].trainIdx ].pt ); 
    }
    Mat H = findHomography( obj, scene, CV_RANSAC );
    //-- Get the corners from the image_1 ( the object to be "detected" )
    vector<Point2f> obj_corners(4);
    obj_corners[0]=cvPoint(0,0);
    obj_corners[1]=cvPoint( img_1.cols, 0 );
    obj_corners[2]=cvPoint(img_1.cols,img_1.rows);
    obj_corners[3]=cvPoint(0,img_1.rows);
    vector<Point2f> scene_corners(4);
    perspectiveTransform( obj_corners, scene_corners, H); 
    for( int i = 0; i < 4; i++ )
    {
      /* 作用和perspectiveTransform一样
      double x = obj_corners[i].x; 
      double y = obj_corners[i].y;
      double Z = 1./( H.at<double>(2,0)*x + H.at<double>(2,1)*y + H.at<double>(2,2) );
      double X = ( H.at<double>(0,0)*x + H.at<double>(0,1)*y + H.at<double>(0,2) )*Z;
      double Y = ( H.at<double>(1,0)*x + H.at<double>(1,1)*y + H.at<double>(1,2) )*Z;
      scene_corners[i] = cvPoint( cvRound(X) + img_1.cols, cvRound(Y) );*/
      scene_corners[i].x+=img_1.cols;
    } 
    line( img_matches, scene_corners[0], scene_corners[1], Scalar(0, 255, 0), 2 );
    line( img_matches, scene_corners[1], scene_corners[2], Scalar( 0, 255, 0), 2 );
    line( img_matches, scene_corners[2], scene_corners[3], Scalar( 0, 255, 0), 2 );
    line( img_matches, scene_corners[3], scene_corners[0], Scalar( 0, 255, 0), 2 );
    imshow( "Good Matches & Object detection", img_matches );
  }
  waitKey(0);
  return 0;
}

附带一些知识点:

图像特征:

1.边界


2.角点(兴趣点)


3.斑点(兴趣区域)


角点是图像的一个局部特征,常用的有harris角点,其算法是一种直接基于灰度图像的,稳定性高,尤其对L型角点检测精度高,但由于采用了高斯滤波,运算速度相对较慢,角点信息有丢失和位置偏移的现象,而且角点提取有聚簇现象。具体在OpenCV中的实现就是使用函数cornerHarris。


除了Harris角点检测,还有Shi-Tomasi角点检测,goodFeaturesToTrack角点检测,将找到的点再用FindCornerSubPix()来找出强特征点。也可以自己制作角点检测的函数,需要用到cornerMinEigenVal函数和minMaxLoc函数,最后的特征点选取,判断条件要根据自己的情况编辑。如果对特征点,角点的精度要求更高,可以用cornerSubPix函数将角点定位到子像素。



OpenCV仿射变换、投影变换的重要函数

estimateRigidTransform():计算多个二维点对或者图像之间的最优仿射变换矩阵 (2行x3列),H可以是部分自由度,比如各向一致的切变。

getAffineTransform():计算3个二维点对之间的仿射变换矩阵H(2行x3列),自由度为6.

warpAffine():对输入图像进行仿射变换

findHomography: 计算多个二维点对之间的最优单映射变换矩阵 H(3行x3列) ,使用最小均方误差或者RANSAC方法 。

getPerspectiveTransform():计算4个二维点对之间的透射变换矩阵 H(3行x3列)

warpPerspective(): 对输入图像进行透射变换

perspectiveTransform():对二维或者三维矢量进行透射变换,也就是对输入二维坐标点或者三维坐标点进行投射变换。

estimateAffine3D:计算多个三维点对之间的最优三维仿射变换矩阵H (3行x4列)

transform():对输入的N维矢量进行变换,可用于进行仿射变换、图像色彩变换.

findFundamentalMat:计算多个点对之间的基矩阵H。


cvStereoCalibrate():中T类型要求了3*1,对与其他形参float和double都支持


cvStereoRectigy():只支持double类型


cvStereoRectifyUncalibrated():立体校正算法Hartley算法效果和F矩阵及图像数量有关,


ps:

【如果用cvStereoCalibrate()函数计算处理的F矩阵效果和Bouguet算法(cvStereoRectigy())效果一样】


【如果用cvFindFundamentalMat()函数计算F矩阵,没有Bougut算法好】


【用Hartley算法(cvStereoRectifyUncalibrated())校正时,别忘了实现要用cvUndistortPoints()去除相机畸变,Bouguet算法(cvStereoRectigy())没有这个要求,实际上它在函数内部校正了相机的畸变。】



Q&A:

问题1:如何计算3个二维点对之间的仿射变换矩阵?

答:使用getAffineTransform()。

问题2:如何计算多个二维点对之间的仿射变换矩阵(使用误差最小准则 )?

答:使用estimateRigidTransform()。


问题3:如何计算多个二维点对之间的投影变换矩阵(使用误差最小准则 )


答:findHomography()。


问题4:如何计算4个二维点对之间的透射变换?

答:使用getPerspectiveTransform()。

问题5:如何计算多个三维点对之间的仿射变换?

答:使用estimateAffine3D。

问题6:如何对输入图像进行仿射变换?

答:使用warpAffine()。

问题7:如何对输入图像进行透射变换?

答:使用perspectiveTransform()。

问题8:如何对输入的二维点对进行仿射变换?

答:使用transform()。

问题9:如何对输入的三维点对进行投影变换?

答:使用perspectiveTransform()。


注意:

1. 使用findHomography接口获取两张图之间的单应性矩阵H,获得的结果不具有可逆性。举例来说,两张图M1和M2,那么调用findHomography(M1, M2, CV_RANSAC, 4)得到的矩阵H12和findHomography(M2, M1, CV_RANSAC, 4)得到的矩阵H21不具有可逆关系,即:H12的逆不等于H21,H12和H21的逆存在一定的误差,这个误差的产生是因为这个单应性矩阵的求取本身就是采用了RANSAC算法,得到的估算矩阵,是个估算值,所以存在误差。


目录
相关文章
|
2月前
|
算法 测试技术 C++
【动态规划】【矩阵快速幂】【滚动向量】C++算法552. 学生出勤记录 II
【动态规划】【矩阵快速幂】【滚动向量】C++算法552. 学生出勤记录 II
|
2月前
|
算法 Java C语言
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-48 算法训练 关联矩阵
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-48 算法训练 关联矩阵
37 0
|
1月前
|
机器学习/深度学习 算法 数据库
KNN和SVM实现对LFW人像图像数据集的分类应用
KNN和SVM实现对LFW人像图像数据集的分类应用
34 0
|
3月前
|
算法 测试技术 C#
【动态规划】【矩阵快速幂】【滚动向量】C++算法552. 学生出勤记录 II
【动态规划】【矩阵快速幂】【滚动向量】C++算法552. 学生出勤记录 II
|
3月前
|
算法
KNN分类算法
KNN分类算法
107 47
|
4月前
|
算法 搜索推荐 Python
Python高级算法——K近邻算法(K-Nearest Neighbors,KNN)
Python高级算法——K近邻算法(K-Nearest Neighbors,KNN)
112 1
|
1月前
|
机器学习/深度学习 数据采集 监控
机器学习-特征选择:如何使用递归特征消除算法自动筛选出最优特征?
机器学习-特征选择:如何使用递归特征消除算法自动筛选出最优特征?
70 0
|
3月前
|
算法
KNN算法及其优缺点
KNN算法及其优缺点
76 0
|
4月前
|
算法 测试技术 编译器
【算法 | 实验18】在字符矩阵中查找给定字符串的所有匹配项
题目描述 题目 在字符矩阵中查找给定字符串的所有匹配项 给定一个M×N字符矩阵,以及一个字符串S,找到在矩阵中所有可能的连续字符组成的S的次数。所谓的连续字符,是指一个字符可以和位于其上下左右,左上左下,右上右下8个方向的字符组成字符串。用回溯法求解。
35 1
|
9天前
电信公司churn数据客户流失k近邻(knn)模型预测分析
电信公司churn数据客户流失k近邻(knn)模型预测分析
18 0