[学习笔记-图像处理篇]梳理图像匹配算法细则

参考
KeyPoint
特征提取与匹配—SURF;SIFT;ORB;FAST;Harris角点
opencv中DMatch解释


1. 特征提取及描述

这一部分opencv都是有现成的,首先是初始化关键点和描述子变量。

vector<Keypoint> keypoints1, keypoints2;
Mat descriptors1, descriptors2;

关键点为元素组成的向量,其中类内容如下:

class KeyPoint
{
Point2f pt;//在图像中的像素坐标,xy分别对应uv方向
float size;//点的邻域直径
float angle;//点的方向
float response;响应程度,代表该点的强壮程度,也就是该点角点程度,用于后期使用和排序
int octave;//特征点所在的图像金字塔的组
int  class_id;//用于聚类的id
}

描述子就是一个数字矩阵。

随后要设置特征点,比如SIFTORB

Ptr<ORB> orb = ORB::create();

然后就是常规操作,特征提取描述,直接写就行。

orb->detect(img1, keypoints1);
orb->detect(img2, keypoints2);
//描述
orb->compute(img1, keypoints1, descriptors1);
orb->compute(img2, keypoints2, descriptors2);

2. 特征匹配

同样直接调用指令,但在此之前需要确定一下特征匹配的算法,比如暴力匹配BFMatchFlann最近邻匹配FlannBasedMatcher等等。下面以暴力匹配为例。

BFMatcher bfMatcher(NORM_HAMMING);
vector<DMatch> matches;
bfMatcher.match(descriptors1, descriptors2, matches);

首先定义一个容器bfMatcher,然后定义一下保存匹配结果的容器matches,获得匹配结果的语句就一句话。下面说一下匹配结果保存的容器matches,它是类型元素组成的向量,内容如下:

struct CV_EXPORTS_W_SIMPLE DMatch
{
    CV_WRAP DMatch() : queryIdx(-1), trainIdx(-1), imgIdx(-1), distance(FLT_MAX) {}
    CV_WRAP DMatch( int _queryIdx, int _trainIdx, float _distance ) :
            queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(-1), distance(_distance) {}
    CV_WRAP DMatch( int _queryIdx, int _trainIdx, int _imgIdx, float _distance ) :
            queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(_imgIdx), distance(_distance) {}

    CV_PROP_RW int queryIdx; // query descriptor index
    CV_PROP_RW int trainIdx; // train descriptor index
    CV_PROP_RW int imgIdx;   // train image index

    CV_PROP_RW float distance;

    // less is better
    bool operator<( const DMatch &m ) const
    {
        return distance < m.distance;
    }
};

简单解释一下,就是说,两幅图像的匹配结果matches中含有一对对的特征点信息,其中每个特征点信息都保存在中;而每个类型数据共有n行,每一行保存一对匹配特征点相关信息;每一行数据含有4个参数,第一个参数是左图第i个特征点的索引(就是说在特征提取之后,会对每张图像中的特征点进行排序,序号就对应索引号),第二个参数是与左图第i个特征点相匹配的右图第j个特征点索引,第三个参数是图像索引,第四个参数是匹配的特征点之间的距离(不是整数)。

非常冗杂的解释,附一张大佬的图就好理解了。
[学习笔记-图像处理篇]梳理图像匹配算法细则_第1张图片

3. 类型转换

特征点一般就保存在Vector类型里,但在求解基础矩阵(RANSAC需要)或单应性矩阵的时候,需要转换为vector,有时候甚至要转换为Mat类型。前者还好说,一句指令就可以,后者就需要自己写代码一步步转换了,不过其实就是把点的坐标提取出来,也就还好。
刚才说的那句指令是

KeyPoint::convert(参数1,参数2);

可以实现之间的相互转换,将参数1转换为参数2格式。

下面说一下vector类型的数据,vector内容很简单,n行对应n个特征点,每行2个参数,就是特征点在图像中的xy像素坐标,名字也是xy

Vector转换Mat类型,贴在下面。

Mat p1(keypoints1.size(),2,CV_32F);  
Mat p2(keypoints1.size(),2,CV_32F);  
vector<Point2f> ps1, ps2;

for(int i=0;i<keypoints1.size();i++)  
{  
  ps1=keypoints1[matches[i].queryIdx].pt;
  p1.at<float>(i,0)=ps1.x;  
  p1.at<float>(i,1)=ps1.y;  
  ps2=keypoints2[matches[i].trainIdx].pt;
  p2.at<float>(i,0)=ps2.x;  
  p2.at<float>(i,1)=ps2.y;  
} 

4. 单应性矩阵求解

等等吧,懒得写。
参考findFundamentalMat和findHomography的比较
使用cv::findFundamentalMat要注意的几点

上面链接2说使用cv::findFundamentalMat的时候前两个参数不要用vector,而是要自己事先转化为Mat,且类型使用CV_32F,参考3中给出的程序。转换的时候注意匹配的索引和特征点对应。

其他参考opencv_C++ FlannBasedMatcher() FLANN特征匹配、OpenCV2:特征匹配及其优化。

你可能感兴趣的:(图像处理篇,算法)