在很多领域经常会涉及到影像匹配的相关内容。从早期的基于灰度值的影像匹配发展到如今的特征匹配,可以说影像匹配在计算机视觉、摄影测量、遥感等领域起着很重要的作用。特征匹配总体包括三个步骤:特征提取、特征描述、特征匹配。今天我们主要讨论的是特征提取与特征描述的相关算法。
已有很多优秀的算法来处理特征提取与描述方面的相关问题,最具有代表性的便是SIFT算法,它有着很好的鲁棒性,这也是我们今天所要讨论的算法。当然,除了SIFT算法之外还有很多其他优秀的算法,如SURF、ASIFT、BRISK、ORB等,它们都有着自己的特点。
我们今天主要是利用opencv库中的SIFT算法来实现c++环境下的单点特征匹配。在日常的应用中,我们可以直接调用SIFT相关的函数来实现特征提取与描述,代码如下:
- SIFT特征提取:
CV_WRAP virtual void detect( InputArray image,
CV_OUT std::vector& keypoints,
InputArray mask=noArray() );
//images为输入影像
//keypoints为关键点序列
//mask为掩膜,指定探测关键点的区域,一般情况下可以忽略
- SIFT特征描述:
CV_WRAP virtual void compute( InputArray image,
CV_OUT CV_IN_OUT std::vector& keypoints,
OutputArray descriptors );
//images为输入影像
//keypoints为关键点序列
//descriptors为描述子序列
- 特征提取与描述实例
cv::Ptr f2d = cv::SIFT::create();//实例化SIFT
std::vector kpts;//关键点序列
cv::Mat desc;//定义描述子
cv::Mat img = cv::imread("Your filename");//读取影像
f2d->detect(img, kpts);//计算关键点kpts
f2d->compute(img, kpts, desc);//计算描述子desc
也可以直接提取关键点和计算描述子
f2d->detectAndCompute(img, cv::Mat(), kpts,desc);
以上是一般情况下进行特征提取与描述的过程,程序会自动根据影像来提取特征较强的点作为特征点再对其进行描述,以便进一步的进行影像匹配。但在一些有特殊需求的地方,如对于两幅影像而言,我们需要将左影像上某一指定点与右影像进行匹配,从而获得唯一的匹配点对。这种情况下,便不能让程序提取特征较强的点作为特征点,因为此时对于左影像而言,特征点的位置已经指定。观察一下KeyPoint
类型的结构:
KeyPoint(Point2f _pt, float _size, float _angle=-1, float _response=0, int _octave=0, int _class_id=-1);
//_pt 关键点的x,y坐标
//_size SIFT尺度参数
//_angle 关键点的方向参数
//_response 关键点检测器对关键点的响应参数
//_octave 被检测到的关键点在影像金字塔中的层数
//_class_id 编号
通过上面的结构可以看出,若要指定特定点为关键点,则只需要将_pt
参数中的x,y坐标指定,并同时设置一个SIFT的尺度参数_size
即可。一般情况下,_size
的值设置为3-5之间效果较好。
- 单点特征匹配的实例
cv::Ptr f2d = cv::SIFT::create();//实例化SIFT
std::vector kpts1, kpts2;//关键点序列
cv::Mat desc1, desc2;//定义描述子
std::vector matches;//定义匹配点对
int SIFT_Size = 5;//SIFT尺度参数(3-5)
cv::Mat img1 = cv::imread("Your filename1");//读取影像1
cv::Mat img2 = cv::imread("Your filename2");//读取影像2
cv::Point2f pt(231, 321);//指定影像1特征点坐标
cv::KeyPoint kpt(pt, SIFT_Size);//转化为特征点
kpts1.push_back(kpt);
f2d->compute(img1, kpts1, desc1);//计算描述子desc1
//遍历影像2的每一像素点,并设置为特征点
for (int i = 0; i < img2.rows; i++)
for (int j = 0; j < img2.cols; j++)
{
cv::Point2f pt(i, j);
cv::KeyPoint kpt(pt, 5);
kpts2.push_back(kpt);
}
f2d->compute(img2, kpts2, desc2);//计算影像2所有关键点的描述子
//将影像1的指定特征点与影像2所有特征点进行匹配,获取最佳匹配点对
cv::FlannBasedMatcher matcher;
matcher.match(desc1, desc2, matches);
cv::Mat outimg;
cv::drawMatches(img1, kpts1, img2, kpts2, matches, outimg, cv::Scalar(0, 255, 0), cv::Scalar(0, 255, 0), std::vector(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
cv::imshow("SIFT", outimg);
cv::waitKey(0);
以上就是单点特征匹配的相关实现代码,不过它也有一些缺陷:
- 在设置关键点的参数时除了SIFT尺度参数以外其余的参数都是利用的默认参数,而实际上对于不同的关键点,它们的参数都是不一样的,这会对匹配的结果产生一定程度的影响。当然,大家可以去看一下SIFT的源码,从而在程序计算中将这些参数一并计算出来,由于作者水平有限,此处就不再过多说明;
- 在遍历影像2的像素点时实际上是将每一点都设置成关键点,这在计算描述子和匹配时运算量很大,对程序的速度有一些影响;
- 对于指定点而言,有时它的特征并不是十分明显,所以会影响匹配的精度,这就对于描述子的要求很高;大家也可以去尝试不同的描述子。
最后,附上一幅我在做单点匹配实验时生成的一幅影像效果图。