opencv检测相交点_OpenCV特征点检测------ORB特征

0c5e807ea28f6a8970add7e3417f3849.png

ORB是是ORiented Brief的简称。ORB的描述在下面文章中:

Ethan Rublee and Vincent Rabaud and Kurt Konolige and Gary Bradski, ORB: an efcient alternative to SIFT or SURF, ICCV 2011

没有加上链接是因为作者确实还没有放出论文,不过OpenCV2.3RC中已经有了实现,WillowGarage有一个talk也提到了这个算法,因此我不揣浅陋,在这里总结一下。

Brief是Binary Robust Independent Elementary Features的缩写。这个特征描述子是由EPFL的Calonder在ECCV2010上提出的。主要思路就是在特征点附近随机选取若干点对,将这些点对的灰度值的大小,组合成一个二进制串,并将这个二进制串作为该特征点的特征描述子。详细算法描述参考如下论文:

注意在BRIEF eccv2010的文章中,BRIEF描述子中的每一位是由随机选取的两个像素点做二进制比较得来的。文章同样提到,在此之前,需要选取合适的gaussian kernel对图像做平滑处理。(为什么要强调这一点,因为下述的ORB对此作了改进。)

BRIEF的优点在于速度,缺点也相当明显:

1:不具备旋转不变性。

2:对噪声敏感

3:不具备尺度不变性。

ORB就是试图解决上述缺点中的1和2.

如何解决旋转不变性:

在ORB的方案中,是采用了FAST作为特征点检测算子。FAST应用的很多了,是出名的快,以防有人不知道,请看这里:

e38f41fc30c1b198d719ba1653c85ab1.png

在Sift的方案中,特征点的主方向是由梯度直方图的最大值和次大值所在的bin对应的方向决定的。略嫌耗时。

在ORB的方案中,特征点的主方向是通过矩(moment)计算而来,公式如下:

63f041f0185d039ece7a0a197350cb3f.png

有了主方向之后,就可以依据该主方向提取BRIEF描述子。但是由此带来的问题是,由于主方向会发生变化,随机点对的相关性会比较大,从而降低描述子的判别性。解决方案也很直接,采取贪婪的,穷举的方法,暴力找到相关性较低的随机点对。

4d912522fe4beb0cc817ea2208b314ac.png

如何解决对噪声敏感的问题:

在前面提到过,在最早的eccv2010的文章中,BRIEF使用的是pixel跟pixel的大小来构造描述子的每一个bit。这样的后果就是对噪声敏感。因此,在ORB的方案中,做了这样的改进,不再使用pixel-pair,而是使用9×9的patch-pair,也就是说,对比patch的像素值之和。(可以通过积分图快速计算)。

关于尺度不变性:

ORB没有试图解决尺度不变性,(因为FAST本身就不具有尺度不变性。)但是这样只求速度的特征描述子,一般都是应用在实时的视频处理中的,这样的话就可以通过跟踪还有一些启发式的策略来解决尺度不变性的问题。

关于计算速度:

ORB是sift的100倍,是surf的10倍。

关于性能:

下面是一个性能对比,ORB还是很给力。点击看大图。

ebe7820e95713bc8bb573c5c4b1875b1.png

Related posts

看到OpenCV2.3.1里面ORB特征提取算法也在里面了,套用给的SURF特征例子程序改为ORB特征一直提示错误,类型不匹配神马的,由于没有找到示例程序,只能自己找答案。

(ORB特征论文:ORB: an efficient alternative to SIFT or SURF.点击下载论文)

经过查找发现:

描述符数据类型有是float的,比如说SIFT,SURF描述符,还有是uchar的,比如说有ORB,BRIEF

对于float 匹配方式有:

FlannBased

BruteForce >

BruteForce >

BruteForce >

对于uchar有:

BruteForce

BruteForce

BruteForceMatcher< L2 > matcher;//改动的地方

完整代码如下:

#include

#include "opencv2/core/core.hpp"

#include "opencv2/features2d/features2d.hpp"

#include "opencv2/highgui/highgui.hpp"

#include

#include

using namespace cv;

using namespace std;

int main()

{

Mat img_1 = imread("D:\\image\\img1.jpg");

Mat img_2 = imread("D:\\image\\img2.jpg");

if (!img_1.data || !img_2.data)

{

cout << "error reading images " << endl;

return -1;

}

ORB orb;

vector keyPoints_1, keyPoints_2;

Mat descriptors_1, descriptors_2;

orb(img_1, Mat(), keyPoints_1, descriptors_1);

orb(img_2, Mat(), keyPoints_2, descriptors_2);

BruteForceMatcher matcher;

vector matches;

matcher.match(descriptors_1, descriptors_2, matches);

double max_dist = 0; double min_dist = 100;

//-- Quick calculation of max and min distances between keypoints

for( int i = 0; i < descriptors_1.rows; i++ )

{

double dist = matches[i].distance;

if( dist < min_dist ) min_dist = dist;

if( dist > max_dist ) max_dist = dist;

}

printf("-- Max dist : %f \n", max_dist );

printf("-- Min dist : %f \n", min_dist );

//-- Draw only "good" matches (i.e. whose distance is less than 0.6*max_dist )

//-- PS.- radiusMatch can also be used here.

std::vector< DMatch > good_matches;

for( int i = 0; i < descriptors_1.rows; i++ )

{

if( matches[i].distance < 0.6*max_dist )

{

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(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

imshow( "Match", img_matches);

cvWaitKey();

return 0;

}

另外: SURF SIFT

/*

SIFT sift;

sift(img_1, Mat(), keyPoints_1, descriptors_1);

sift(img_2, Mat(), keyPoints_2, descriptors_2);

BruteForceMatcher >  matcher;

*/

/*

SURF surf;

surf(img_1, Mat(), keyPoints_1);

surf(img_2, Mat(), keyPoints_2);

SurfDescriptorExtractor extrator;

extrator.compute(img_1, keyPoints_1, descriptors_1);

extrator.compute(img_2, keyPoints_2, descriptors_2);

BruteForceMatcher >  matcher;

*/

效果:

99acfa07f1fc9e3bf944fabef2c1a954.gif

另外一个是寻找目标匹配

在右边的场景图里面寻找左边那幅图的starbucks标志

效果如下:

9f99d7b8f8f1b7dae9b2cfab6178a341.gif

需要在之前的那个imshow之前加上如下代码即可完成一个简单的功能展示:

// localize the object

std::vector obj;

std::vector scene;

for (size_t 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

std::vector 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);

std::vector scene_corners(4);

perspectiveTransform( obj_corners, scene_corners, H);

// draw lines between the corners (the mapped object in the scene - image_2)

line( img_matches, scene_corners[0] + Point2f( img_1.cols, 0), scene_corners[1] + Point2f( img_1.cols, 0),Scalar(0,255,0));

line( img_matches, scene_corners[1] + Point2f( img_1.cols, 0), scene_corners[2] + Point2f( img_1.cols, 0),Scalar(0,255,0));

line( img_matches, scene_corners[2] + Point2f( img_1.cols, 0), scene_corners[3] + Point2f( img_1.cols, 0),Scalar(0,255,0));

line( img_matches, scene_corners[3] + Point2f( img_1.cols, 0), scene_corners[0] + Point2f( img_1.cols, 0),Scalar(0,255,0));

代码片:

#include "opencv2/highgui/highgui.hpp"

#include "opencv2/features2d/features2d.hpp"

#include

int main( )

{

cv::Ptr<:featuredetector> detector = cv::FeatureDetector::create( "SIFT" );

cv::Ptr<:descriptorextractor> extractor = cv::DescriptorExtractor::create("SIFT" );

cv::Mat im = cv::imread("box.png", CV_LOAD_IMAGE_COLOR );

std::vector<:keypoint> keypoints;

cv::Mat descriptors;

detector->detect( im, keypoints);

extractor->compute( im,keypoints,descriptors);

int duplicateNum = 0;

for (int i=0;i

{

for (int j=i+1;j

{

float dist = abs((keypoints[i].pt.x-keypoints[j].pt.x))+abs((keypoints[i].pt.y-keypoints[j].pt.y));

if (dist == 0)

{

cv::Mat descriptorDiff = descriptors.row(i)-descriptors.row(j);

double diffNorm = cv::norm(descriptorDiff);

std::cout<

duplicateNum++;

}

}

}

std::cout<

return 1;

}

本文同步分享在 博客“shiter”(CSDN)。

如有侵权,请联系 support@oschina.cn 删除。

本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/359955.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务器物理部署拓扑图,【网络】叶脊(Spine-Leaf)网络拓扑下全三层网络设计与实践(五)-物理服务器路由方案及配置...

5.1 本节目标理解物理机的业务ip之间的通信过程&#xff1b;了解如何在leaf交换机上配置业务ip的静态路由&#xff1b;了解如何在物理机上配置业务ip及其他相关配置项&#xff1b;5.2 拓扑结构本节拓扑结构与上节相比没有变化&#xff0c;如下图&#xff1a;5.3 物理机业务网络…

axios队列 vue_vue axios全攻略

不再继续维护vue-resource&#xff0c;并推荐大家使用 axios 开始&#xff0c;axios 被越来越多的人所了解。本来想在网上找找详细攻略&#xff0c;突然发现&#xff0c;axios 的官方文档本身就非常详细&#xff01;&#xff01;有这个还要什么自行车&#xff01;&#xff01;所…

探测服务器操作系统,探测服务器操作系统工具

探测服务器操作系统工具 内容精选换一换lib库获取方式不同&#xff0c;编译环境配置方法不同&#xff0c;下面分别介绍。根据Mind Studio安装服务器操作系统及架构与硬件环境是否相同&#xff0c;编译环境分为如下两种场景&#xff1a;若Mind Studio安装服务器操作系统及架构与…

Java Web App体系结构

我曾经利用Servlet&#xff0c;JSP&#xff0c;JAX-RS&#xff0c;Spring框架&#xff0c;Play框架&#xff0c;带有Facelets的JSF和一些Spark框架。 以我的拙见&#xff0c;所有这些解决方案都远非面向对象和优雅的。 它们都充满了静态方法&#xff0c;无法测试的数据结构和肮…

Mono for Android 篇二 使用Spinner 实现下拉列表读取Browser.BookmarksUri

http://www.cnblogs.com/ivanyb/archive/2013/03/05/2944818.html 1、首先在VS2010里面创建一个Android Application 简单说明一下解决方案中各个文件的作用&#xff1a; AndroidManifest.xml&#xff1a;项目环境配置文件&#xff0c;指明了使用Android SDK的版本&#xff0c;…

mysql磁盘临时表清理_mysql 创建大量磁盘临时表

发现 mysql 中创建的临时表基本都是磁盘临时表&#xff0c;对此进行排查。最后为临时表机制说明。通过查看 mysql 状态变量&#xff0c;看到创建的临时表基本都是磁盘临时表。查看临时表配置&#xff0c;默认配置 16M&#xff0c;配置并没有问题。创建两个表&#xff0c;做一个…

吃鸡服务器8月10日维护,《黑潮之上》2021年8月10日不停服维护公告

在黑潮之上手游中2021年8月10日更新了哪些有趣的内容呢&#xff1f;想了解本次更新情况如何的小伙伴们&#xff0c;接下来就让我们一起来看一下吧&#xff01;各位接触者:为了提供良好的游戏体验&#xff0c;保证服务器稳定运行&#xff0c;《黑潮之上》将于2021年8月10日早5:0…

资格赛:题目1:同构

时间限制:2000ms单点时限:1000ms内存限制:256MB描述 给定2个树A和B&#xff0c;保证A的节点个数>B的节点个数。 现在你需要对树A的边进行二染色。 一个好的染色方案&#xff0c;指不存在一个树A中的连通块&#xff0c;同时满足以下2个条件 1. 其中只有同色的边 2. 和B同构。…

java 获取mysql链接_Java中如何获取mysql连接的3种方法总结

前言本文主要来说说三种 Java 中获取 mysql 连接的方式&#xff0c;分享出来供大家参考学习&#xff0c;下面话不多说了&#xff0c;来一起看看详细的介绍&#xff1a;第一种&#xff1a;传统的连接方式&#xff1b;第二种&#xff1a;读取配置文件方式&#xff1b;第三种&…

JavaScript学习总结(六)——JavaScript判断数据类型总结

最近做项目中遇到了一些关于javascript数据类型的判断处理&#xff0c;上网找了一下资料&#xff0c;并且亲自验证了各种数据类型的判断&#xff0c;在此做一个总结吧&#xff01; 一、JS中的数据类型 1.数值型&#xff08;Number&#xff09;&#xff1a;包括整数、浮点数。 2…

material 项目_Web开发必备的 10 个开源项目,不用自己亲自造轮子!

来自&#xff1a;Java and Python君Web 开发中几乎的平台都需要一个后台管理&#xff0c;但是从零开发一套后台控制面板并不容易&#xff0c;幸运的是有很多开源免费的后台控制面板可以给开发者使用&#xff0c;那么有哪些优秀的开源免费的控制面板呢&#xff1f;我在 Github 上…

SVN部署(本地)

1.安装TortoiseSVN 2.建立Repository。在F:/下建立文件夹SVN_NATIVE_REPOSITORY&#xff0c; —— 》 3.在其他任意路径建立任意名称的路径&#xff0c;这里为D:\MSVC Project\SVN_WORK&#xff0c;确保该文件夹为空 右键单击&#xff0c;点击SVN Checkout&#xff0c; 第一行 …

mysql sqlserver schema_MySQL数据库数据迁移到SQLserver

最近因工作需要&#xff0c;需要将mysql数据库迁移到sqlserver&#xff0c;仅仅是数据迁移&#xff0c;因此相对比较简单。对于mysql迁移到sqlserver&#xff0c;我们需要使用到mysql odbc驱动&#xff0c;然后透过sqlserver链接服务器的方式来访问mysql。具体见下文描述。一、…

mysql客户端安装错误_windows下mysql 5.7以上版本安装及遇到的问题

(原)早些前用window安装mysql挺简单的&#xff0c;一个安装程序&#xff0c;一路下一步。2006的5.0版本&#xff0c;确实太早了点。于是官网上又下了一个版本&#xff0c;windows也是提供了二个版本Installer(安装)版和Archive(文档)版。Installer版本的后缀是.msi&#xff0c;…

sklearn中eof报错_sklearn中的数据预处理和特征工程

小伙伴们大家好~o(&#xffe3;▽&#xffe3;)ブ&#xff0c;今天我们看一下Sklearn中的数据预处理和特征工程&#xff0c;老规矩还是先强调一下&#xff0c;我的开发环境是Jupyter lab&#xff0c;所用的库和版本大家参考&#xff1a;Python 3.7.1&#xff08;你的版本至少要…

sql显示前10行数据_SPL 简化 SQL 案例详解:计算各组前 N 行

取出各组的前N行数据是较常见的运算&#xff0c;比如&#xff1a;每个月每种产品销量最高的五天是哪五天&#xff0c;每位员工涨薪最多的一次是哪次&#xff0c;高尔夫会员成绩最差的三次是哪三次&#xff0c;等等。在SQL中&#xff0c;这类运算要用窗口函数以及keep/top/rownu…

4月21日会议总结(整理—祁子梁)

会议成果&#xff1a; 1.今天我们确定了软件版本的时间alphe版在12周做出来&#xff0c;在我们内部测试基本通过。 bate版在13周发布和其他组作交换测试&#xff0c;在14周release版发布并给其他人使用体验准备15周的演讲。 2.同时确定了部分功能实现顺序&#xff0c;”谁是卧底…

如何查看mysql的gtid_汇总丨MySQL GTID技术点,看这一篇就够了!

mysql> SELECT * FROM mysql.gtid_executed;mysql.gtid_executed表是由MySQL服务器提供给内部使用的。它允许副本在副本上禁用二进制日志记录时使用GTIDs&#xff0c;并允许在二进制日志丢失时保留GTID状态。RESET MASTER命令&#xff0c;gtid_executed表将被清除。服务意外…

vb.net详解MDI窗体操作方法

MDI窗体可以避免打开窗体的时候被无数个子窗体困扰&#xff0c;我将为大家一一的介绍一下vb.net中MDI窗体的操作方法 一、如何创建MDI窗体&#xff1f; 1、创建mdi主窗体 新建建立一个默认空白的Windows应用程序&#xff0c;在Form1窗体的属性窗口中找到IsMDIContainer 属性&am…

lokijs可以用mysql_JavaScript实现的内存数据库LokiJS介绍和入门实例_javascript技巧

LokiJS是一个内存数据库&#xff0c;将性能考虑放在第一位。LokiJS支持索引和更快的文档访问&#xff0c;执行性能非常好(近50万OPS/秒)。其内置DynamicView类可以用于数据子集的索引&#xff0c;甚至获取更快的性能。*阅读这篇文章来看一看LokiJS的性能表现。LokiJS支持collec…