ORB + OPENCV
一、介绍
假如有两张人物图片,我们的目标是要确认这两张图片中的人物是否是同一个人。如果人来判断,这太简单了。但是让计算机来完成这个功能就困难重重。一种可行的方法是:
- 分别找出两张图片中的特征点
- 描述这些特征点的属性,
- 比较这两张图片的特征点的属性。如果有足够多的特征点具有相同的属性,那么就可以认为两张图片中的人物就是同一个人。
ORB(Oriented FAST and Rotated BRIEF)就是一种特征提取并描述的方法。ORB是由Ethan Rublee, Vincent Rabaud, Kurt Konolige以及Gary R.Bradski在2011年提出,论文名称为"ORB:An Efficient Alternative to SIFTor SURF",(http://www.willowgarage.com/sites/default/files/orb_final.pdf)。
ORB分两部分,即特征点提取和特征点描述。特征提取是由FAST(Features from Accelerated Segment Test)算法发展来的,特征点描述是根据BRIEF(Binary Robust Independent Elementary Features)特征描述算法改进的。ORB特征是将FAST特征点的检测方法与BRIEF特征描述子结合起来,并在它们原来的基础上做了改进与优化。据说ORB算法的速度是sift的100倍,是surf的10倍。
二、Oriented FAST(oFast)特征点的提取
oFast就是在使用FAST提取特征点之后,给其定义一个该特征点的放向,并以此来实现该特征点的旋转不变形。
2.1、粗提取
图像的特征点可以简单的理解为图像中比较显著显著的点,如轮廓点,较暗区域中的亮点,较亮区域中的暗点等。
FAST的核心思想是找出那些卓尔不群的点。即拿一个点跟它周围的点比较,如果它和其中大部分的点都不一样,就人物它是一个特征点。
如上图,假设图像中的一点P,及其一个邻域。右半拉是放大的图,每个小方格代表一个像素,方格内的颜色只是为了便于区分,不代表该像素点的颜色。判断该点是不是特征点的方法是,以P为圆心画一个半径为3pixel的圆(周长为16pixel)。圆周上如果有连续n个像素点的灰度值比P点的灰度值大或者小(需事先设定一个阈值T),则认为P为特征点。一般n设置为12。
为了加快特征点的提取,快速排除非特征点,首先检测1、9、5、13位置上的灰度值,如果P是特征点,那么这四个位置上有3个或3个以上的的像素值都大于或者小于P点的灰度值。如果不满足,则直接排除此点。
2.2、使用ID3决策树,将特征点圆周上的16个像素输入决策树中,以此来筛选出最优的FAST特征点。
2.3、使用非极大值抑制算法去除临近位置多个特征点的。具体:为每一个特征点计算出其响应大小(特征点P和其周围16个特征点偏差的绝对值和)。在比较临近的特征点中,保留响应值较大的特征点,删除其余的特征点。
2.4、特征点的尺度不变性
建立金字塔,来实现特征点的多尺度不变性。设置一个比例因子scaleFactor(opencv默认为1.2)和金字塔的层数nlevels(pencv默认为8)。将原图像按比例因子缩小成nlevels幅图像。缩放后的图像为:I’= I/scaleFactork(k=1,2,…, nlevels)。nlevels幅不同比例的图像提取特征点总和作为这幅图像的oFAST特征点。
2.5、特征点的旋转不变形
oFast用矩(moment)法来确定FAST特征点的方向。即计算特征点以r为半径范围内的质心,特征点坐标到质心形成一个向量作为该特征点的方向。矩定义如下:
三、Rotated BRIEF(rBRIEF)特征点的描述
3.1、BRIEF算法
BRIEF算法计算出来的是一个二进制串的特征描述符。它是在一个特征点的邻域内,选择n对像素点pi、qi(i=1,2,…,n)。然后比较每个点对的灰度值的大小。如果I(pi)> I(qi),则生成二进制串中的1,否则为0。所有的点对都进行比较,则生成长度为n的二进制串。一般n取128、256或512(opencv默认为256)。
另外,为了增加特征描述符的抗噪性,算法需要先对图像进行高斯平滑处理。在ORB算法中,在这个地方进行了改进,在使用高斯函数进行平滑后又用了其他操作,使其更加的具有抗噪性。具体方法下面将会描述。
在特征点SxS的区域内选取点对的方法,BRIEF论文中测试了5种方法:
- 在图像块内平均采样;
- p和q都符合(0,S2/25)的高斯分布;
- p符合(0,S2/25)的高斯分布,而q符合(0,S2/100)的高斯分布;
- 在空间量化极坐标下的离散位置随机采样;
- 把p固定为(0,0),q在周围平均采样。
3.2、rBRIEF算法
3.2.1、steered BRIEF(旋转不变性改进):
在使用oFast算法计算出的特征点中包括了特征点的方向角度。假设原始的BRIEF算法在特征点SxS(一般S取31)邻域内选取n对点集。
经过旋转角度θ旋转,得到新的点对:
在新的点集位置上比较点对的大小形成二进制串的描述符。这里需要注意的是,在使用oFast算法是在不同的尺度上提取的特征点。因此,在使用BRIEF特征描述时,要将图像转换到相应的尺度图像上,然后在尺度图像上的特征点处取SxS邻域,然后选择点对并旋转,得到二进制串描述符。
3.2.2、rBRIEF-改进特征点描述子的相关性
使用steeredBRIEF方法得到的特征描述子具有旋转不变性,但是却在另外一个性质上不如原始的BRIEF算法,即描述符的可区分性(相关性)。为了解决描述子的可区分性和相关性的问题,ORB论文中没有使用原始BRIEF算法中选取点对时的5种方法中的任意一种,而是使用统计学习的方法来重新选择点对集合。
对每个特征点选取31x31领域,每个领域选择5x5的平均灰度值代替原来单个像素值进行比对,因此可以得到N=(31-5+1)x(31-5+1) = 729个可以比对的子窗口(patch),可以使用积分图像加快求取5x5邻域灰度平均值的速度。一共有M = 1+2+3+...+N = 265356种点对组合,也就是一个长度为M的01字符串。显然M远大于256,我们得筛选。
筛选方法如下:
- 重组所有点以及对应的初始二值串得到矩阵O,行数为提取得到的点数,每行是每个点对应的初始二值描述子
- 对重组后的矩阵O,按照每列均值与0.5的绝对差从小到大排序,得到矩阵T
- 贪心选择:把T中第一列放进矩阵R(一开始为空)中,并从T中移除依次选择T的每列,与R中所有的列进行比较,如果相似度超过一定阈值,忽略,进行下一列,否则放进R中,并从T中移除重复以上过程直到选择256个列,这样每个特征点就有256个0,1组成的描述子。如果不足256个,则降低阈值直到满足256就可,R即为最终特征描述矩阵。
三、特征点匹配
这部分是另外一个话题了。ORB算法最大的特点就是计算速度快 。这得益于使用FAST检测特征点,FAST的检测速度正如它的名字一样是出了名的快。再就是是使用BRIEF算法计算描述子,该描述子特有的2进制串的表现形式不仅节约了存储空间,而且大大缩短了匹配的时间。
例如特征点A、B的描述子如下。
A:10101011
B:10101010
设定一个阈值,比如80%。当A和B的描述子的相似度大于90%时,我们判断A,B是相同的特征点,即这2个点匹配成功。在这个例子中A,B只有最后一位不同,相似度为87.5%,大于80%。则A和B是匹配的。
将A和B进行异或操作就可以轻松计算出A和B的相似度。而异或操作可以借助硬件完成,具有很高的效率,加快了匹配的速度。
四、OpenCV实验(OpenCV3.0以上版本,包含contrib模块)
[cpp] view plain copy
- #include <iostream>
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #include <string>
- #include <dirent.h>
- #include <unistd.h>
- #include <vector>
- #include <sstream>
- #include <fstream>
- #include <sys/io.h>
- #include <sys/times.h>
- #include <iomanip>
- #include <tuple>
- #include <cstdlib>
- using namespace std;
- #include "opencv2/imgproc.hpp"
- #include "opencv2/imgcodecs.hpp"
- #include "opencv2/highgui.hpp"
- #include "opencv2/stitching.hpp"
- #include "opencv2/xfeatures2d/nonfree.hpp"
- using namespace cv;
- #define ENABLE_LOG
- bool PreapreImg(vector<Mat> &imgs);
- bool Match(vector<cv::detail::MatchesInfo> &pairwise_matches,
- const vector<cv::detail::ImageFeatures> &features,
- const cv::String matcher_type = "homography",
- const int range_width = -1,
- const bool try_cuda = false,
- const double match_conf = 0.3f);
- void demo();
- int main(int argc, char** argv)
- {
- cout << "# STA ##############################" << endl;
- cout << "\n" << endl;
- int64 app_start_time = getTickCount();
- demo();
- cout << "\n" << endl;
- cout << "# END ############################## Time: "
- << ((getTickCount() - app_start_time) / getTickFrequency())
- << " sec" << endl;
- return 0;
- }
- void demo()
- {
- vector<Mat> imgs;
- PreapreImg(imgs);
- // define feature finder
- Ptr<cv::detail::FeaturesFinder> finder =
- cv::makePtr<cv::detail::OrbFeaturesFinder>();
- // detect features
- int num_images = static_cast<int>(imgs.size());
- vector<cv::detail::ImageFeatures> features(num_images);
- for (int i = 0; i < num_images; i++) {
- (*finder)(imgs[i], features[i]);
- features[i].img_idx = i;
- #ifdef ENABLE_LOG
- cout << ">> features number: " << setw(4) << features[i].img_idx
- << setw(5) << static_cast<int>(features[i].keypoints.size())
- << endl;
- Mat tmp;
- cv::drawKeypoints(imgs[i], features[i].keypoints, tmp);
- stringstream ss;
- ss << i;
- cv::imwrite(("./img" + string(ss.str()) + "_keypoints.jpg").c_str(), tmp);
- #endif
- }
- // Frees unused memory allocated before if there is any
- finder->collectGarbage();
- // Pairwise matching
- vector<cv::detail::MatchesInfo> pairwise_matches;
- Match(pairwise_matches, features);
- #ifdef ENABLE_LOG
- cout << ">> pairwise matches: "
- << setw(5) << static_cast<int>(pairwise_matches.size())
- << endl;
- cout << ">> Saving matches graph..." << endl;
- ofstream f("./matchGraph.txt");
- vector<cv::String> img_names;
- for (int i = 0; i < num_images; i++) {
- stringstream ss; ss << i;
- img_names.push_back(ss.str());
- }
- f << matchesGraphAsString(img_names, pairwise_matches, 1.0f);
- cout << ">> Saving matches graph OK. Position: ./matchGraph.txt" << endl;
- Mat tmp;
- cv::drawMatches(imgs[0], features[0].keypoints,
- imgs[1], features[1].keypoints,
- pairwise_matches[1].matches,
- tmp);
- cv::imwrite("./matches0_1.jpg", tmp);
- #endif
- }
- bool PreapreImg(vector<Mat> &imgs)
- {
- Mat image0 = imread("./0.jpg", IMREAD_GRAYSCALE);
- Mat image1 = imread("./1.jpg", IMREAD_GRAYSCALE);
- imgs.push_back(image0);
- imgs.push_back(image1);
- // Check if have enough images
- int num_images = static_cast<int>(imgs.size());
- if (num_images < 2)
- {
- cout << ">> error. num_images < 2" << endl;
- return false;
- }
- #ifdef ENABLE_LOG
- for (int i = 0; i < num_images; i++) {
- cout << ">> image " << setw(2) << i << ": "
- << setw(5) << imgs[i].rows
- << setw(5) << imgs[i].cols
- << setw(5) << imgs[i].channels()
- << endl;
- }
- #endif
- return true;
- }
- /************************************************
- * There are 3 kinds of feature matchers offered by "matchers.hpp"
- */
- bool Match(vector<cv::detail::MatchesInfo> &pairwise_matches,
- const vector<cv::detail::ImageFeatures> &features,
- const cv::String matcher_type = "homography",
- const int range_width = -1,
- const bool try_cuda = false,
- const double match_conf = 0.3f)
- {
- Ptr<cv::detail::FeaturesMatcher> matcher;
- if (matcher_type == "affine")
- {
- bool full_affine = false;
- int num_matches_thresh1 = 6;
- matcher = makePtr<cv::detail::AffineBestOf2NearestMatcher>(
- full_affine, try_cuda, match_conf, num_matches_thresh1);
- }
- else if (matcher_type == "homography")
- {
- int num_matches_thresh1 = 6;
- int num_matches_thresh2 = 6;
- if (range_width == -1)
- matcher = makePtr<cv::detail::BestOf2NearestMatcher>(
- try_cuda, match_conf, num_matches_thresh1, num_matches_thresh2);
- else
- matcher = makePtr<cv::detail::BestOf2NearestRangeMatcher>(
- range_width, try_cuda, match_conf, num_matches_thresh1, num_matches_thresh2);
- }
- (*matcher)(features, pairwise_matches);
- matcher->collectGarbage();
- return true;
- }
实验代码:https://code.csdn.net/guoyunfei20/orb.git
实验结果:
输入图像1:
输入图像2:
图像1的ORB特征点位置:
图像2的ORB特征点位置:
利用cv::detail::BestOf2NearestMatcher匹配算法得到的能匹配上的特征点(图像0 -> 图像1):
相关文章:

【Codeforces】967C Stairs and Elevators (二分)。
http://codeforces.com/contest/967/problem/C 从一个地方到另一个地方,可以选择爬楼梯或者坐电梯 ,前提是楼梯或者电梯存在。 n 楼层数 m 每一层的房间数 s 楼梯的数量 e 电梯的数量 v 电梯的速度 第2行是s个楼梯的位置 第3行是e个电梯的位置 …

webpack 最简打包结果分析
原文链接:https://ssshooter.com/2019-02... 现在的 webpack 不再是入门噩梦,过去 webpack 最让人心塞的莫过于配置文件,而 webpack4 诞生随之而来的是无配置 webpack。 使用 webpack4,至少只需要安装 webpack 和 webpack cli。所以大家完全可…

orb-slam2(学习笔记)+相机
单目(Monocular)、双目(Stereo)、深度相机(RGB-D) 深度相机能够读取每个像素离相机的距离 ,单目相机 只使用一个摄像头进行SLAM的做法叫做单目SLAM(Monocular SLAM),结构…

如何在nuget上传自己的包+搭建自己公司的NuGet服务器(新方法)
运维相关:http://www.cnblogs.com/dunitian/p/4822808.html#iis 先注册一个nuget账号https://www.nuget.org/ 下载并安装一下NuGetPackageExplorer:https://github.com/NuGetPackageExplorer/NuGetPackageExplorer 创建一个包(vs其实也是可以…

【Codeforces】1080C Masha and two friends (棋盘染色)
http://codeforces.com/problemset/problem/1080/C 给定一个棋盘,(1,1)的位置是白色,观察可以知道,如果横纵坐标之和是偶数,那么是白色,奇数的话就是黑色。 只要算出染色以后白色方…
Java多线程001——一图读懂线程与进程
本博客 猫叔的博客,转载请申明出处前言 本系列将由浅入深,学习Java并发多线程。 一图读懂线程与进程 1、一个进程可以包含一个或多个线程。(其实你经常听到“多线程”,没有听过“多进程”嘛)2、进程存在堆和方法区 3、…

TermCriteria模板类
学习写vo过程中遇到的 类功能:模板类,作为迭代算法的终止条件。 构造函数: TermCriteria(int type,int maxCount,double epsilon); 参数说明: type 迭代终止条件类型 typeTermCriteria::MAX_ITER/TermCrite…

SQL优化快速入门
最近遇到一个专门进行SQL技术优化的项目,对很多既有的老存储过程进行调优(现在已经不再新增任何存储过程),因此系统的对SQL语句编写进行一次科学的学习变得很有必要。这儿将基于黄德承大神的Oracle SQL语句优化一书,选…
【HDU】4509 湫湫系列故事——减肥记II (区间覆盖 暴力)
http://acm.hdu.edu.cn/showproblem.php?pid4509 给出的时间段是被占用的时间,24h 1440 min,求出这些区间以外的区间长度 把00:00 - 23:59 变成0-1440 1-5都是被占用的区域,暴力很好理解 #include <iostream>…

Java并发编程71道面试题及答案
Java并发编程71道面试题及答案 1、在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User)。 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDa…

USB获取图像实时处理
手写VO的准备工作,调用UAB或者本地相机获取视频图像,读取并保存视频。 #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "openc…

常见数据库 性能
2019独角兽企业重金招聘Python工程师标准>>> 转载于:https://my.oschina.net/u/582827/blog/778908

【Codeforces】1093C Mishka and the Last Exam
http://codeforces.com/problemset/problem/1093/C 已知b[i]求a[i]和a[n-i1],n是a数组中元素的个数,a数组的下标从1开始 输出b[1]的时候,直接让a[1] 0,a[n] b[0] 接下来的每一组,先让a[i] b[i],然后求…
PHP面试常考内容之Memcache和Redis(2)
你好,是我琉忆。继周一(2019.2-18)发布的“PHP面试常考内容之Memcache和Redis(1)”后,这是第二篇,感谢你的支持和阅读。本周(2019.2-18至2-22)的文章内容点为以下几点&am…

毫米波雷达、ADAS中的应用以及毫米波雷达的检测、测距、测速和角度测量
毫米波雷达的检测、测距、测速和角度测量 毫米波(Millimeter-Wave,缩写:MMW),是指长度在1~10mm的电磁波,对应的频率范围为30~300GHz.根据波的传播理论,频率越高,波长越短,分辨率越高…

【VMC实验室】在QCloud上创建您的SQL Cluster(1)
在国内公有云厂商上搭建一套SQL Cluster的难度相信做Windows的童鞋都会很清楚,并非它的搭建有多少难度,只是很多细节需要注意。腾讯云,QCloud,为什么选择QCloud来做这个实验,除了QCloud是我的东家(啊呸&…

【Codeforces】808D Array Division(前后两部分和相等)
http://codeforces.com/contest/808/problem/D 给你一个数组,问:是否可以通过移动一个数字的位置,求只能移动一次,使得这个数组前后部分的和相等,前后部分不一定等长 一个a数组储存数据,另一个b数组b[i]表…

想要确保架构目标达成?适合度函数了解一下
Paula Paul和Rosemary Wang撰写的一篇博文中介绍了适应度函数(fitness function)的基本概念、入门方法,并给出了如何验证各种架构质量的一些实例。文中提出,适应度函数驱动开发的方法可用于编写测定系统符合架构目标的测试&#x…

标定(二)----------双目相机与IMU联合标定(工具箱使用方法——Kalibr)
16个相机参数: Overview ethz-asl/kalibr is a toolbox that solves the following calibration problems: Multiple camera calibration: intrinsic and extrinsic calibration of a camera-systems with non-globally shared overlapping fields of view Cam…

【Codeforces】659B Qualifying Contest (sort)
http://codeforces.com/problemset/problem/659/B n个人,m个地区,选出每个地区分数最高的两个人 下面有n行,每一行的第一个数表示姓名,第二个数是地区的序号,第三个数是分数 It is guaranteed that all surnames of…

Protractor测试环境搭建
2019独角兽企业重金招聘Python工程师标准>>> 安装node.js.然后,在cmd下,进入E盘下的,我们自己新建的protractor文件夹下, npm install -g protractor 这样就会在本地安装好两个命令行工具:protractor和webd…

Promise和Promise的方法
(Promise和Promise的方法) Promise是ES6一个伟大的发明,他使我们从回调地狱中能够走出来。 什么是Promise 从字面上来看,Promise就是一个承诺。那么,在ES6当中,Promise通常用来控制异步操作。当一个异步操作…

镜头评价指标及测试方法(三)--------测量原理及3D相机调查
1.测量原理: 1.1、通过红外结构光(Structured Light)来测量像素距离,如Kinect1、Project Tango1、Intel Realsense等; 通过近红外激光器,将具有一定结构特征的光(比如离散光斑、条纹光、编码结构光等)投射到…

Android--Retrofit的简单使用(一)
1,如果不太了解retrofit的同学可以先去官网学习一下简单使用:http://square.github.io/retrofit/,这里我们以一个简单的Get请求的例子来练习一下 2,https://api.douban.com/v2/movie/top250?start0&count10 目标地址…

【Codeforces】920A Water The Garden(浇花)
http://codeforces.com/problemset/problem/920/A 给你花坛的数目,花坛都是连续的,某一些花坛里有水龙头,给出了这些有水龙头的位置 The garden from test 1. White colour denotes a garden bed without a tap, red colour — a garden be…
Work with Alexa :Echo匹配连接到Alexa
背景: 通过蓝牙可以让你的智能家居设备和Echo设备连接起来,从而达到Echo通过语音控制智能家居设备。 什么事Alexa Gadgets? 我的理解Alexa Gadgets是智能家居设备的一部分,包含硬件和软件。硬件:蓝牙模块;软…

Maven 学习Tips
2019独角兽企业重金招聘Python工程师标准>>> 使用 deploy 应该注意的地方 在项目的pom文件中增加如下节点:<project><distributionManagement><repository><id>releases</id><url>http://192.168.1.99:8081/nexus/c…

【Codeforces】1015B Obtaining the String(字符串 交换)
http://codeforces.com/contest/1015/problem/B n 表示 字符串的长度 然后是两个字符串,每一次只能交换相邻的两个,如果选择第i个字符的话,只能交换第i个和第i1个 然后输出每一次交换选定的位置 暴力,模拟,queue&am…

07 分支管理 —— Feature分支
2019独角兽企业重金招聘Python工程师标准>>> 07 分支管理 —— Feature分支 软件开发中,总有无穷无尽的新的功能要不断添加进来。添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以࿰…

适用于0基础小伙伴的HTML知识点总结 先到先得哟
HTML基础知识点分享 前段时间一直忙于私下学习消化一些PHP中的知识体系,导致博客开通至今一直没发表相关技能贴。最近呢~博主夏天我正在进行前半段知识体系和框架的总结阶段。借此,抽空给大家分享一下私下自己学习时的一些心得体会以及知识体系ÿ…