matlab提取特征描述子_OPENCV特征点提取算法对比

7468029c12586102870dbd3f0300eefa.gif

OPENCV特征点提取算法对比

除了我们熟知的SIFT、SURF、ORB等特征点提取算法,OpenCV中还提供了十余种特征点提取算法。最近在整理以往的ppt和报告,看到其中一页ppt,发现已经忘得差不多了,就再写篇博客复习下好了,这篇博客注重对比,技术方面的内容不会太过细致,希望能有帮助。当然,文章末尾会提供这些算法OpenCV调用的实例代码。

首先,引发内容的就是下面这张ppt:(不要注意Why Surf了,当时根据具体应用场景所做的选择)左边一栏是特征点的数目,右边一栏是检测的速度,数据是在博主自己的测试集(大概四五百张图片)上测试的。

ed4737de7920198739090f5c78285373.png

SIFT

SIFT的论文在谷歌学术上单片引用超过四万次,算得上那个年代特征提取技术的里程碑了。一改以往特征点提取不稳定,特征不足的弱点,SIFT可以稳定的提取出特征点,并且对旋转平移缩放是鲁棒的。可以说是SIFT开启了鲁棒特征提取研究,下面是SIFT特征提取的主要步骤:

特征点检测:

  1. 构建尺度空间

  2. 在Dog高斯差分空间计算极值点,二次曲线插值获得亚像素精度的特征点

  3. 去除低对比度点,通过计算Hessian矩阵去除边缘响应点

特征描述:

  1. 统计特征点周围16*16区域的直方图,并选取直方图峰值作为改关键点的主方向

  2. 将坐标轴旋转为关键点的方向,保证旋转不变性,根据梯度计算128维特征描述子

SIFT的鲁棒性是有特征点检测和特征描述两个部分决定的,在这页最开始的PPT中,都是只统计了特征点检测算法,没有考虑特征描述(因为有许多算法不具备描述能力)。

这里额外解释下Dog,对不同模糊度的高斯滤波图像相减,得到的Dog,事实上就是不同尺度边缘的Laplace边缘检测,Dog计算比逐个进行Laplace滤波要快。数学推导参见Paper,在高斯滤波sigma计算时需要一定条件,才能近似的得到拉普拉斯滤波图像。

3470c6d2561334ab14c7269de84709e5.png

SURF

SURF算法可以看做是对SIFT算法的重要改进,全称Speeded-Up Robust Features,解决了SIFT计算复杂度高、耗时长的确定。SURF算法在光照和形变方面的鲁棒性要逊色于SIFT,但是带来了三倍的速度提升。

特征点检测:

  1. 计算积分图(为了加速Box滤波Harr小波的计算)

  2. 改变Box滤波器的尺度来得到不同的尺度空间

  3. 使用Hessian矩阵行列式的特征值的符号来确定极值

特征点描述:

  1. 根据像素在x,y方向上的Haar小波响应值来确定极值点方向

  2. 采用扇形区域扫描来统计得到64维或128维的直方图特征

FAST:BRISK/ORB/KAZE…

FAST是今年来最成功的特征点提取算法之一,其思想非常精妙。ORB算法、KAZE算法等都是使用FAST作为特征点检测算法,区别在于特征描述部分。

  1. 在图像中任选一点p, 假定其像素(亮度)值为 Ip

  2. 以3为半径画圆,覆盖p点周围的16个像素,如下图所示

  3. 设定阈值t,如果这周围的16个像素中有连续的n个像素的像素值都小于 Ip−t 或者有连续的n个像素都大于 Ip+t, 那么这个点就被判断为角点。 在OpenCV的实现中n取值为12(16个像素周长的 3/4).

  4. 一种更加快的改进是: 首先检测p点周围的四个点,即1, 5, 9, 12四个点中是否有三个点满足超过Ip+t, 如果不满足,则直接跳过,如果满足,则继续使用前面的算法,全部判断16个点中是否有12个满足条件。

77ebf0620126403843c54a6e1b5ab1da.png

在OpenCV中提供了一个参数,可以选取三种FAST计算方法。

Brisk、ORB、KAZE等算法都采用了FAST做特征点检测,BRIEF做特征描述,它们的不同之处比较少。

速度大致为:SIFT

ORB

ORB速度极快,ORB是SIFT和SURF之外非常好的选择,因为SIFT和SURF是有专利的,在商业软件中使用需要付费(当然,但多数开发者选择无视,但是如果你是巨头,就要避免踩雷)。ORB是采用rBRIEF进行描述的,优点就是速度快。

BRISK

BRISK算法同样是免费的,也是规避SIFT和SURF专业的好选择,同样是采用FAST检测特征点,BRIEF描述。BRISK只是在尺度空间构建的参数和BRIEF描述方式上有所不同。在BRIEF描述时采用均匀采样模式,考虑512个短距离子集,形成64bit的描述符,采用汉明距离来度量相似性。

22bc08dc7e2552b511686f4380dd862d.png

KAZE

日语“风”“かぜ”,KAZE是一种和SIFT很像的方法,今年来收到广泛关注,因为它是近年来效果最好的方法。在构建尺度空间方面,KAZE采用AOS算法来对高斯模糊的图像进行非线性扩散滤波,通过不同的进化时间t来构建非线性尺度空间(与SIFT相似,只不过sigma换成了t)。在特征点检测方面,也是采用和SIFT相似的的方式,26邻域极值点加上海森矩阵检测的方式来检测出特征点。在特征描述方面,和SURF比较相似,采用扇形区域扫描的方式确定主方向,然后对于周围区域划分成4*4的区域,然后每个区域计算4维特征向量,总共得到64维的特征向量。与前面的算法最重要的不同在于,使用了非线性扩散滤波,在滤除噪声的同时保留了边缘和特征点,不会像高斯滤波之类的算法一样无差别的模糊图像,使得特征点检测更为稳定和有效。该算法在尺度鲁棒性上要逊色与SIFT,但是在速度和稳定性方面要强于SIFT,是目前最常用的特征点提取算法。该算法和SIFT算法在时间消耗上是差不多的,可以通过精简KAZE算法来提高速度。

cc60c56bb73dae1b9330bd445607d33a.png

Dense SIFT

Dense SIFT算法没有特征点提取过程,通过稠密无差别的采集样本点,计算SIFT特征来形成整幅图片的特征描述,这种特征常常在早期目标识别使用,现在已经被淘汰。

GFTT

Good Feature To Track,正式名称是shi_tomasi角点检测算法,这种算法可以提取出大量的特征点。一般在需要大量特征点时,可以使用GFTT。

MSER

最大稳定极值区域(MSER:Maximally Stable Extremal Regions)常被用于提取图像中的斑点,是一种分水岭算法。通过阈值分割,不断提升分割阈值,连通域变化稳定的区域被称为最大稳定极值区域。

综合对比

特征点检测速度:FAST > SURF > SIFT

特征点检测数量:根据阈值调整,博主的结果是: FAST > SURF > SIFT

ed4737de7920198739090f5c78285373.png

OpenCV Code

bool CVPointLayer::Fast(vector& images, vector& disp){

vector pt;

FastFeatureDetector fast(layerparam.fast_param().thresh(), layerparam.fast_param().isnms());

for (int i = 0; i < images.size(); ++i){

pt.clear();

fast.detect(images[i].image, pt);

images[i].keypoint.insert(images[i].keypoint.begin(), pt.begin(), pt.end());

LOG(INFO) << featname << " Detected points";

}

return true;

}

bool CVPointLayer::FastX(vector& images, vector& disp){

vector pt;

int xtype = 0;

auto ftype = layerparam.fast_param().ftype();

switch (ftype)

{

case svaf::FastParamCP_FastType_T_9_16:

xtype = FastFeatureDetector::TYPE_9_16;

break;

case svaf::FastParamCP_FastType_T_7_12:

xtype = FastFeatureDetector::TYPE_7_12;

break;

case svaf::FastParamCP_FastType_T_5_8:

xtype = FastFeatureDetector::TYPE_5_8;

break;

default:

break;

}

for (int i = 0; i < images.size(); ++i){

pt.clear();

cv::FASTX(images[i].image, pt, layerparam.fast_param().thresh(), 

layerparam.fast_param().isnms(), xtype);

images[i].keypoint.insert(images[i].keypoint.begin(), pt.begin(), pt.end());

LOG(INFO) << featname << " Detected points";

}

return true;

}

bool CVPointLayer::MSER(vector& images, vector& disp){

vector pt;

auto param = layerparam.mser_param();

cv::MSER mser(param.delta(), param.min_area(), param.max_area(), 

param.max_varia(), param.min_divers(), param.max_evolution(), 

param.area_thresh(), param.min_margin(), param.edgeblur_size());

for (int i = 0; i < images.size(); ++i){

pt.clear();

mser.detect(images[i].image, pt);

images[i].keypoint.insert(images[i].keypoint.begin(), pt.begin(), pt.end());

LOG(INFO) << featname << " Detected points";

}

return true;

}

bool CVPointLayer::ORB(vector& images, vector& disp){

vector pt;

auto param = layerparam.orb_param();

auto obrtype = param.scoretype();

int otype = 0;

switch (obrtype)

{

case svaf::ORBParamCP_OBRScoreType_HARRIS_SCORE:

otype = ORB::HARRIS_SCORE;

break;

case svaf::ORBParamCP_OBRScoreType_FAST_SCORE:

otype = ORB::FAST_SCORE;

break;

default:

break;

}

cv::ORB orb(param.nfeatures(), param.scalefactor(), param.nlevels(), param.edgethresh(), 

param.firstlevel(), param.wta_k(), otype, param.patchsize());

for (int i = 0; i < images.size(); ++i){

pt.clear();

orb.detect(images[i].image, pt);

images[i].keypoint.insert(images[i].keypoint.begin(), pt.begin(), pt.end());

LOG(INFO) << featname << " Detected points";

}

return true;

}

bool CVPointLayer::Brisk(vector& images, vector& disp){

vector pt;

auto param = layerparam.brisk_param();

BRISK brisk(param.thresh(), param.octaves(), param.ptscale());

for (int i = 0; i < images.size(); ++i){

pt.clear();

brisk.detect(images[i].image, pt);

images[i].keypoint.insert(images[i].keypoint.begin(), pt.begin(), pt.end());

LOG(INFO) << featname << " Detected points";

}

return true;

}

bool CVPointLayer::Freak(vector& images, vector& disp){

vector pt;

FREAK freak;

LOG(FATAL) << "No Impliment!";

return false;

}

bool CVPointLayer::Star(vector& images, vector& disp){

vector pt;

auto param = layerparam.star_param();

cv::StarDetector star(param.maxsize(), param.response_thresh(), param.projected_thresh(), 

param.binarized_thresh(), param.nms_size());

for (int i = 0; i < images.size(); ++i){

pt.clear();

star.detect(images[i].image, pt);

images[i].keypoint.insert(images[i].keypoint.begin(), pt.begin(), pt.end());

LOG(INFO) << featname << " Detected points";

}

return true;

}

bool CVPointLayer::Sift(vector& images, vector& disp){

vector pt;

auto param = layerparam.sift_param();

SIFT sift(param.nfeatures(), param.octaves(), param.contrast_thresh(), 

param.edge_thresh(), param.sigma());

for (int i = 0; i < images.size(); ++i){

pt.clear();

sift.detect(images[i].image, pt);

images[i].keypoint.insert(images[i].keypoint.begin(), pt.begin(), pt.end());

LOG(INFO) << featname << " Detected points";

}

return true;

}

bool CVPointLayer::Surf(vector& images, vector& disp){

vector pt;

auto param = layerparam.surf_param();

SURF surf(param.hassian_thresh(), param.octaves(), param.intevals(), 

param.extended(), param.upright());

for (int i = 0; i < images.size(); ++i){

pt.clear();

surf.detect(images[i].image, pt);

images[i].keypoint.insert(images[i].keypoint.begin(), pt.begin(), pt.end());

LOG(INFO) << featname << " Detected points";

}

return true;

}

bool CVPointLayer::GFTT(vector& images, vector& disp){

vector pt;

auto param = layerparam.gftt_param();

GoodFeaturesToTrackDetector gftt(param.maxcornners(), param.quality(), param.mindist(),

param.blocksize(), param.useharris(), param.k());

for (int i = 0; i < images.size(); ++i){

pt.clear();

gftt.detect(images[i].image, pt);

images[i].keypoint.insert(images[i].keypoint.begin(), pt.begin(), pt.end());

LOG(INFO) << featname << " Detected points";

}

return true;

}

bool CVPointLayer::Harris(vector& images, vector& disp){

/*vector pt;

Ptr detector = FeatureDetector::create("HARRIS");

for (int i = 0; i < images.size(); ++i){

pt.clear();

detector->detect(images[i].image, pt);

for (int j = 0; j < pt.size(); ++j){

images[i].keypoint.insert(images[i].keypoint.begin(), pt.begin(), pt.end());

}

}

return true;*/

vector pt;

auto param = layerparam.gftt_param();

GoodFeaturesToTrackDetector gftt(param.maxcornners(), param.quality(), param.mindist(),

param.blocksize(), true, param.k());

for (int i = 0; i < images.size(); ++i){

pt.clear();

gftt.detect(images[i].image, pt);

images[i].keypoint.insert(images[i].keypoint.begin(), pt.begin(), pt.end());

LOG(INFO) << featname << " Detected points";

}

return true;

}

bool CVPointLayer::Dense(vector& images, vector& disp){

vector pt;

auto param = layerparam.dense_param();

DenseFeatureDetector dense(param.initfeatscale(), param.featscalelevel(), param.featscalemul(),

param.initxystep(), param.initbound(), param.varyxyscale(), param.varybdscale());

for (int i = 0; i < images.size(); ++i){

pt.clear();

dense.detect(images[i].image, pt);

images[i].keypoint.insert(images[i].keypoint.begin(), pt.begin(), pt.end());

LOG(INFO) << featname << " Detected points";

}

return true;

}

bool CVPointLayer::SimpleBlob(vector& images, vector& disp){

vector pt;

SimpleBlobDetector sb;

for (int i = 0; i < images.size(); ++i){

pt.clear();

sb.detect(images[i].image, pt);

images[i].keypoint.insert(images[i].keypoint.begin(), pt.begin(), pt.end());

LOG(INFO) << featname << " Detected points";

}

return true;

}

bool CVPointLayer::Grid(vector& images, vector& disp){

Ptr detector = FeatureDetector::create("Grid");

return false;

}

bool CVPointLayer::Pyramid(vector& images, vector& disp){

Ptr detector = FeatureDetector::create("Pyramid");

return false;

}

关注本公众号

可查阅更多图像知识信息,合作共享!

7d027dc9f759079a9904ffa8db18ac86.png

声明:

此资料部分转载自网络,仅供学习参考。http://www.p-chao.com/2017-06-14/opencv%E7%89%B9%E5%BE%81%E7%82%B9%E6%8F%90%E5%8F%96%E7%AE%97%E6%B3%95%E5%AF%B9%E6%AF%94/

7a0267aa46778347e383fcfff09564b4.gifa4fa95a4085ee92954121f169cd3f09f.png

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值