FREAK算法是ICCV 2012上的一篇关于特征点检测与匹配的论文《FREAK: Fast Retina Keypoint》上提出的,从文章标题中可以看出来该算法的一个特点是快速,另外一个特点就是该算法是被人眼识别物体的原理上得到启发提出的。
看过我之前博文的可能知道,我到现在已经把SIFT算法、ORB算法、BRIEF算法和BRISK算法都进行了介绍。可以看出BRIEF、ORB和BRISK都是特征点周围的邻域像素点对之间的比较形成的二进制串作为特征点的描述符,这种做法有着快速和占用内存低的优点,在如今的移动计算中有很大的优势,但是也遗留了一些问题。比如,如何确定特征点邻域中哪些像素点对进行比较,如何匹配它们呢?作者提出,这种优化的趋势与自然界中通过简单的规则解决复杂的问题是相符的。作者提出的FREAK算法就是通过模仿人眼视觉系统而完成的,下面我们来介绍一下FREAK算法。

一、特征点检测

特征点检测是特征点匹配的第一个阶段,FAST算法是一种可以快速检测特征点的算法,而且有AGAST算法对FAST算法的加速版本。本文中特征点检测方法与BRISK中特征点检测方法相同,都是建立多尺度的图像,在不同图像中分别使用FAST算法检测特征点,具体的做法见我的博文特征点检测——BRISK算法介绍,这里就不在详细的说明了。

二、二进制串特征描述符

既然FREAK算法是通过人眼的视觉系统得到启发提出的算法,那么我们首先来看看人眼的视觉系统。

2.1 Human retina

作者提出,在人眼的视网膜区域中,感受光线的细胞的密度是不相同的。人眼的视网膜根据感光细胞的密度分成了四个区域:foveola、fovea、parafoveal和perifoveal,如下图所示:

这里面fovea区域是负责接收高分辨率的图像,而低分辨率的图像是perfoveal区域中形成的。

2.2 采样模式

在我以前博文中提到的BRIEF、ORB算法中特征点邻域中的采样点对是随机生成的,而BRISK算法则是采用平均采样的方式生成的这些采样点。FREAK算法中采样模式与BRISK算法中采样模式类似,但是它的模式与人眼视觉系统中的模式更为接近,如下图所示:

上图中每一个黑点代表一个采样点,每个圆圈代表一块感受野,具体在处理时时对该部分图像进行高斯模糊处理,以降低噪声的影响,而且每个圆圈的半径表示了高斯模糊的标准差。这种采样模式与BRISK算法的不同之处在于,每个感受野与感受野之间有重叠的部分。与ORB和BRIEF算法的不同之处在于,ORB和BRIEF算法中的高斯模糊半径都是相同的,而这里采用了这种不同大小的高斯模糊的核函数。作者提出,正是这些不同之处,导致最后的结果更加优秀。通过重叠的感受野,可以获得更多的信息,这些信息可以使最终的描述符更具有独特性。而不同大小的感受野在人体的视网膜中也有这样类似的结构。
最终FREAK算法的采样结构为6、6、6、6、6、6、6、1,这里的6代表每层中有6个采样点并且这6个采样点在一个同心圆中,一共有7个同心圆,最后的1代表的是特征点。

2.3 Coarse-to-fine descriptor

如上所述,FREAK算法也是用二进制串对特征点进行描述,这里用 F F进行表示,则有

F=∑0≤a<N2aT(Pa)

F = \sum_{0 \leq a 这里的 T(Pa) T(P_a)是一个函数, Pa P_a是一个采样点对,如果 I(Pr1a)>I(Pr2a) I(P_a^{r_1}) > I(P_a^{r_2}),那么 T(Pa)=1 T(P_a) = 1,否则, T(Pa)=0 T(P_a) = 0。 I(Pr1a) I(P_a^{r_1})表示的采样点经过高斯模糊后得到的灰度值。
由于一个特征点中有数十个采样点,那么就有上千个候选的采样点对,然后有些采样点对对于特征描述并没有什么用处,因此需要对特征点对进行筛选,作者采用了一种与ORB算法类似的算法对特征点对进行筛选:
(1)作者利用50000个特征点建立了一个矩阵 D D,矩阵中的每一行表示一个特征点,这一行中使用的是上面提到的特征点建立的所有可能的特征点对的比较结果。如我们对每个特征点提取了43个采样点,那么43个采样点可能构建43*42 = 903个采样点对,那么矩阵DD中一行有903列;
(2)计算矩阵 D D中每一列的平均值,为了得到独特性好的特征,方差应该较大,这就要求该列的平均值应该接近于0.5;
(3)根据各列中的方差由大到小进行排序;
(4)取该矩阵的前kk列,在论文中取了前512列,因为作者发现前512列是最相关的,且再增加列数对于结果提升也不是很大。
作者把得到的这512个采样点对分成了4组,每128个为一组,把这些采样点对进行连线,得到的效果如下:

作者发现这四组的连线中第一组主要在外围,之后的每一组连线逐渐内缩,最后一组的连线主要在中央部分,这与人眼视觉系统很相似。人眼视觉系统中也是首先通过perifoveal区域对感兴趣的物体的位置进行估计,然后通过感光细胞更加密集的fovea区域进行验证,最终确定物体的位置。

2.4 Saccadic search

人眼的fovea区域由于有比较密集的感光细胞,因此可以捕捉更加高分辨率的图像,因此fovea区域在识别和匹配的过程中起着关键的作用。perifoveal区域的感光细胞则没有那么密集,因此它只能捕捉到模糊的图像,因此首先用他们来进行物体位置的估计。这就是人眼识别和匹配的大体流程,而作者就模仿这种流程对特征点进行匹配。
首先使用前128个匹配点对,作为粗略信息,如果距离小于某个阈值,再使用剩余的匹配点对(精密信息)进行匹配。这种级联的操作在很大程度上加速了匹配的速度,大约有超过90%的候选点可以通过前128个匹配点对进行排除。这种级联的操作的图示如下图所示:

2.5 Orientation

为了保证算法具有方向不变性,需要为每个特征点增加方向信息,由于BRISK算法与FREAK算法对特征点邻域的采样点格式相近,因此FREAK算法特征点方向的计算与BRISK算法也比较相近。BRISK算法是通过计算具有长距离的采样点对的梯度来表示特征点的方向(具体描述见我之前介绍BRISK算法的博文),FREAK算法则采用其中45个距离长的、对称的采样点计算其梯度,如下图所示:

计算公式为

O=1M∑Po∈G(I(Pr1o)−I(Pr2o))Pr1o−Pr2o||Pr1o−Pr2o||

O = \frac{1}{M} \sum_{P_o \in G} (I(P_o^{r1}) - I(P_o^{r2})) \frac{P_o^{r1}-P_o^{r2}}{||P_o^{r1} - P_o^{r2}||}

三、Opencv代码实现

下面是调用Opencv的FREAK算法进行特征点匹配的代码示例,使用了双向验证(即图1中第i个点匹配图2中第j个点,同时要求图二中第j个点也要匹配图1中的第i个点)以及RANSAN去除错配点。

#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\features2d\features2d.hpp>
#include <opencv2\legacy\legacy.hpp>
#include <time.h>
using namespace std;
using namespace cv;void detectFREAK(Mat& img1, Mat& img2, vector<KeyPoint>& kp1,vector<KeyPoint>& kp2, Mat& des1, Mat& des2)
{//使用BRISK算法中的特征点检测算法检测特征点,存入kp1和kp2中BRISK brisk(80,4);      //40表示阈值,4表示有4个octavebrisk(img1, Mat(), kp1);brisk(img2, Mat(), kp2);//FREAK算法,得到特征点描述符,存入des1和des2中FREAK freak;freak.compute(img1, kp1, des1);freak.compute(img2, kp2, des2);
}void runRANSAC(vector<DMatch>& good_matches, const vector<KeyPoint>& kp1, const vector<KeyPoint>& kp2)
{Mat image1Points(good_matches.size(), 2, CV_32F);//存放第一张图像的匹配点的坐标Mat image2Points(good_matches.size(), 2, CV_32F);for (size_t i = 0; i < good_matches.size(); i++){int sub_des1 = good_matches[i].queryIdx;int sub_des2 = good_matches[i].trainIdx;float *ptr1 = image1Points.ptr<float>(i);float *ptr2 = image2Points.ptr<float>(i);ptr1[0] = kp1[sub_des1].pt.x;   ptr1[1] = kp1[sub_des1].pt.y;ptr2[0] = kp2[sub_des2].pt.x;   ptr2[1] = kp2[sub_des2].pt.y;}Mat fundamental;//基础矩阵vector<uchar> RANSAC_state;//RANSAC过滤后各匹配点对的状态//计算基础矩阵,并记录各个匹配点对的状态,0为匹配不正确,1为正确fundamental = cv::findFundamentalMat(image1Points, image2Points, RANSAC_state, cv::FM_RANSAC);vector<DMatch>::iterator iter;int i = 0;for (iter = good_matches.begin(); iter != good_matches.end();){if (RANSAC_state[i] == 0){iter = good_matches.erase(iter);}elseiter++;i++;}
}void matchFREAK(Mat& img1, Mat& img2, vector<KeyPoint>& kp1, vector<KeyPoint>& kp2,Mat& des1, Mat& des2, string &str)
{BruteForceMatcher<HammingLUT> matcher;vector<DMatch> matches1to2;vector<DMatch> matches2to1;matcher.match(des1, des2, matches1to2);matcher.match(des2, des1, matches2to1);int* flag = new int[des2.rows];for (int i = 0; i < des2.rows; i++){flag[i] = matches2to1[i].trainIdx;}double max_dist = 0; double min_dist = 100;for (int i = 0; i < des1.rows; i++){double dist = matches1to2[i].distance;if (dist < min_dist) min_dist = dist;if (dist > max_dist) max_dist = dist;}cout << str << " " << max_dist << " " << min_dist << endl;std::vector< DMatch > good_matches;for (int i = 0; i < des1.rows; i++){if (matches1to2[i].distance < max_dist){if (matches2to1[matches1to2[i].trainIdx].trainIdx == i)good_matches.push_back(matches1to2[i]);}}for (size_t i = 0; i < kp1.size(); i++){circle(img1, Point(kp1[i].pt.x, kp1[i].pt.y), 2, Scalar(0, 0, 255), 2);}for (size_t i = 0; i < kp2.size(); i++){circle(img2, Point(kp2[i].pt.x, kp2[i].pt.y), 2, Scalar(0, 0, 255), 2);}runRANSAC(good_matches, kp1, kp2);Mat img_matches;drawMatches(img1, kp1, img2, kp2,good_matches, img_matches, Scalar(0, 255, 0), Scalar(0, 0, 255),vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);imwrite(str, img_matches);
}int main()
{Mat img1 = imread("1.jpg");Mat img2 = imread("2.jpg");vector<KeyPoint> kp1, kp2;Mat des1, des2;detectFREAK(img1, img2, kp1, kp2, des1, des2);matchFREAK(img1, img2, kp1, kp2, des1, des2, string("matchImage.jpg"));return 0;
}

FREAK匹配的结果并不好,结果如下图所示:

特征点匹配——FREAK算法介绍相关推荐

  1. 特征点匹配——ORB算法介绍

    <ORB: an efficient alternative to SIFT or SURF>是Rublee等人在2011年的ICCV上发表的一篇有关于特征点提取和匹配的论文,这篇论文介绍 ...

  2. 二分图匹配Hopcroft-Carp算法介绍

    我们在做二分图匹配的时候,最喜欢选择的就是匈牙利算法,但是我们可以知道匈牙利算法的复杂度是O(n*e),那么如果对于一个点和边比较多的图,匈牙利算法很容易超时,所以我们采用Hopcroft-Carp算 ...

  3. C#图片对比,支持图片局部和整体图片比对,FREAK算法,《FREAK: Fast Retina Keypoint》,特征匹配

    文章目录 前言 一.FREAK算法 二.使用步骤 1.引入库 2.提交数据 3.获取识别结果 4.识别结果定义 示例资源 前言 C#图片特征比对: 通过AI 算法,查找两张图片的相似点,特征点的比对比 ...

  4. 【OpenCV 例程 300篇】249. 特征描述之视网膜算法(FREAK)

    『youcans 的 OpenCV 例程300篇 - 总目录』 [youcans 的 OpenCV 例程 300篇]249. 特征检测之视网膜算法(FREAK) 1. FREAK 算法简介 快速视网膜 ...

  5. SIFT特征点匹配中KD-tree与Ransac算法的使用

    转自:http://blog.csdn.net/ijuliet/article/details/4471311 Step1:BBF算法,在KD-tree上找KNN.第一步做匹配咯~ 1.什么是KD-t ...

  6. 基于特征点匹配的自适应目标跟踪算法

    基于特征点匹配的自适应目标跟踪算法 2016-01-29 13:11 摘 要:由于实际场景复杂多变,目标在运动过程中往往会出现形变.遮挡等问题,增加了跟踪的难度.为了解决上述问题,提出一种基于特征点匹 ...

  7. 道路匹配MapMatching:GPS轨迹点常用聚类算法介绍(K-Means聚类、蚁群算法等)

    道路匹配MapMatching:GPS轨迹点常用聚类算法介绍(K-Means聚类.蚁群算法等) 前言 一.聚类算法是什么? 二.道路匹配中常见聚类算法介绍 1.K-Means算法 2.基于时间和距离的 ...

  8. 结合openCV学习DIP之传统图像特征与匹配

    前言 关于图像的预处理部分参考  结合opencv学习DIP​​​​​​​ 概述 该笔记主要是基于DIP理论➕openCV实现,学习该笔记首先要确保通读DIP理论,并由自己的话描述相关知识,并且掌握o ...

  9. OpneCV3特征提取及匹配SURF算法(一)——SURF概述与代码演示

    前言 1.在目标检测.识别,匹配的应用中,特征点的提取是不可缺少的部分的步骤,在之前博文我演示了HOG特性提取与使用HOG与SVM训练自己的样本进行行人检测. 2.我的编程环境是Windows 7 6 ...

最新文章

  1. rate-limit
  2. AI如何反低俗?今日头条推内容检测工具“灵犬”3.0,首次公开其技术原理
  3. jsp页面展示更加商品的分类,控制商品的显示
  4. 深入剖析Redis系列(三) - Redis集群模式搭建与原理详解
  5. vue导入html登陆页,Vue 实现 登陆后打开主页面(登陆组件 + 主页面组件)
  6. android访问百度新闻,百度新闻(com.baidu.news) - 8.3.6.0 - 应用 - 酷安
  7. saltstack grains
  8. Lucence.net索引技术 一
  9. Mysql存时间不一致_node mysql 存在数据库中的时间和取到前端的时间不一致,请问怎么解决...
  10. pycharm error:no module named caffe
  11. 会c语言如何快速入门python,初学者如何从C语言到Python的转化(北大陈斌老师的举例 )...
  12. windows 系统文件 —— 特殊文件及文件类型
  13. tutte定理证明hall定理_县级数学教研网课:有趣的勾股定理证明,课件用几何画板制作...
  14. android WebKit实例
  15. SQL十进制和十六进制相互转换
  16. MDK5之.map文件解析
  17. 极速office(Word)文件怎么在方框里面打对勾
  18. python中用于绘制各种图形、标注文本_在python中的图形上绘制常量文本-问答-阿里云开发者社区-阿里云...
  19. 覃小龙课堂:小白如何影视剪辑月入10W,24小时全天带你
  20. JAVA面向对象(2)

热门文章

  1. css 中动态获取宽度,css中的calc动态计算长度值
  2. 24届近3年南昌大学自动化考研院校分析
  3. 值得珍藏一辈子的80句话(经典语句)
  4. 【WB32库开发】第10章 TIM1高级定时器——PWM互补输出
  5. android 字母索引三方,Android ListView字母索引(仿微信通讯录列表)
  6. [完美解决]如何在windows安装docker toolbox,使用tensorflow,Jupyter Notebook,各种问题的解决方案
  7. python中xlrd官方_Python中如何用xlrd读取
  8. 【cmd】快速打开文件夹
  9. ipad在线看html5,在iPad上播放HTML5视频并寻找(Playing HTML5 Video on IPad and seeking)
  10. Android 系统(52)---O1关机闹钟