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,一经查实,立即删除!

相关文章

用Java创建MongoDB上限集合

在MongoDB中&#xff0c;可以以循环方式将文档的插入顺序保留到集合中。 这些类型的集合在MongoDB中称为上限集合。 MongoDB文档描述了上限集合&#xff1a; 上限集合是固定大小的集合&#xff0c;它们支持高吞吐量操作&#xff0c;这些操作根据插入顺序来插入&#xff0c;检索…

服务器物理部署拓扑图,【网络】叶脊(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;所…

linux内核模块简介

linux内核是一个整体是结构。因此向内核添加任何东西。或者删除某些功能 ,都十分困难。为了解决这个问题。引入了内核机制。从而可以动态的想内核中添加或者删除模块。模块不被编译在内核中,因而控制了内核的大小。然而模块一旦被插入内核,他就和内核其他部分一样。这样一来 就…

移动app显示远程服务器错误,KIS移动应用常见问题汇总

1、移动应用平台正式版登录后&#xff0c;变成试用版/已过期答&#xff1a;更新移动应用平台到最新版本V4.0.0.2&#xff0c;下载地址&#xff1a;http://dwz.cn/kis0268多次退出移动应用平台&#xff0c;直到变成正式版&#xff0c;重新启动移动应用。2、移动应用使用过程中提…

在@NamedQueries中枚举@NamedQuery

介绍 如果您是使用JPA的Java开发人员&#xff0c;则很可能在实体上声明了一个或多个NamedQuery对象。 要在类上声明NamedQuery &#xff0c;必须使用查询名称及其JPQL对该类进行简单注释&#xff0c;例如&#xff1a; Entity NamedQuery(name "findAllProjects",q…

csv转json文件

今天因为需要帮一个同事的新闻内容录入为html, 每次手改不方便&#xff0c;所以就弄了个csv(excel)转json的c程序&#xff0c;然后再利用ejs把它渲染成网页&#xff0c;打开渲染好的网页再保存(不能保存源文件&#xff0c;不然还是空的)&#xff0c;就可以把内容弄成一个html了…

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

探测服务器操作系统工具 内容精选换一换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;无法测试的数据结构和肮…

android listview使用自定义的adapter没有了OnItemClickListener事件解决办法

在使用listview的时用使用自定义的adapter的时候&#xff0c;如果你的item布局中包含有Button&#xff0c;Checkable继承来的所有控件&#xff0c;那么你将无法获取listview的onItemClickListener事件是由于Button&#xff0c;Checkable继承来的所有控件或默认获取焦点。解决办…

mysql用的是标准sql_标准SQL语言的用法_MySQL

SQL语言是目前最通用的关系数据库语言。ANSI SQL是指由美国国家标准局(ANSI)的数据库委员会制定的标准SQL语言&#xff0c;多数关系数据库产品支持标准SQL语言&#xff0c;但是它们也往往有各自的SQL方言。在分层的软件结构中&#xff0c;关系数据库位于最底层&#xff0c;它的…

三星有网络显示无网络连接到服务器,三星s5296连接了网络为什么不能用 看完这些原因就知道了...

1、无线网络本身有问题&#xff1a;如果手机连接上WiFi但却上不了网&#xff0c;首先要检查一下无线网络本身是否存在问题。这时候可以检查一下其他联网设备是否可以正常上网&#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不复制数据_windows mysql不停止服务复制数据

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云数据库专家保驾护航&#xff0c;为用户…

C++ 中的类模板

原文连接&#xff1a;http://see.xidian.edu.cn/cpp/biancheng/view/213.html 有时&#xff0c;有两个或多个类&#xff0c;其功能是相同的&#xff0c;仅仅是数据类型不同&#xff0c;如下面语句声明了一个类&#xff1a;class Compare_int{ public : Compare(int a,int b…

学生机房管理服务器系统设计,广东工业大学数据库课程设计机房管理系统设计...

《广东工业大学数据库课程设计机房管理系统设计》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《广东工业大学数据库课程设计机房管理系统设计(27页珍藏版)》请在人人文库网上搜索。1、课程设计个人报告课程名称 数据库应用与设计 _题目名称 机房管理系统 _学生学院 …

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 8 FlatMap JDBC ResultSet?

您还不喜欢机能吗&#xff1f; 这样标题可能不会引起您的共鸣-但文章会&#xff01; 相信我。 本质上&#xff0c;我们想要这样&#xff1a; ------------------ | col1 | col2 | col3 | ------------------ | A | B | C | row 1 | D | E | F | row 2 | G …