一、Brief算法

1、基本原理

BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,Brief为特征描述子,对已检测到的特征点进行描述,是一种二进制编码描述子,摒弃了区域灰度直方图描述特征点的传统方法,加快特征描述子建立速度,降低特征匹配时间。因为需要事先得到特征点的位置,可以利用Fast特征点检测算法或Harris角点检测算法或者SIFT、Surf等算法检测特征点的位置,接下来利用特征点邻域BRIEF算法建立特征描述符。

在特征点附近随机的选取若干点对,将这些点对的灰度值大小组合成一个长为256的二进制字串,并将这个二进制字串作为该特征点的特征描述子。

构造BRIEF特征只有两个关键步骤:

1)如何对图像对平滑处理;算法是对特征点对进行描述子生成,所以对特征点要求较高,因此在进行生成前进行平滑操作。盒状滤波器的处理方法来代替高斯平滑处理方法。由于可以采用积分图像的方法,所以盒状滤波器比高斯滤波器更快,而且两者的准确性几乎相同。

2)如果选择[x,y]对。由于tao测试是根据单像素的亮度进行判别的,非常被噪声影响,做图像平滑可以消除噪声影响。匹配的难度越大,图像的平滑也越重要。

关于特征点的附近区域选择,作者提供五种方法:

其中方法(2)比较好。

由于其描述子利用二进制(“0”和“1”)编码,因此在特征匹配时只需计算2个特征点描述子的Hamming距离。大量实验表明,不匹配特征点的描述子的Hamming距离在128左右,匹配点对描述子的Hamming距离则远小于128。由于BRIEF的匹配速度远高于SURF和SIFT,因此应用较为广泛。

1、两个特征编码对应bit位上相同元素的个数小于128的,一定不是配对的。

2、一幅图上特征点与另一幅图上特征编码对应bit位上相同元素的个数最多的特征点配成一对。

2、算法流程

1、为减少噪声干扰,先对图像进行高斯滤波(方差为2,高斯窗口为9x9)。

2、以特征点为中心,取SxS的邻域大窗口。在大窗口中随机选取一对(两个)5x5的子窗口,比较子窗口内的像素和(可用积分图像完成),进行二进制赋值。(一般S=31)

其中,p(x),p(y)分别随机点x=(u1,v1),y=(u2,v2)所在5x5子窗口的像素和。

3、在大窗口中随机选取N对子窗口,重复步骤2的二进制赋值,形成一个二进制编码,这个编码就是对特征点的描述,即特征描述子。(一般N=256)

构造一个512个bit的BRIEF,就需要512对[x,y],且需要注意,它们是有序的,每次计算位置都相同,否则影响最终结果。也就说说,一旦选定了512对[x,y],那么,无论是提取特征,还是匹配特征,都要按照这512对进行计算。512/8=64就是存储BRIEF所需的字节数,论文将512个bit的BRIEF又称作BRIEF-64。

在opencn2.4.9中,该区域的大小为48×48。再在该区域内,以某种特定的方式选择nd个像素点对。然后比较像素点对的灰度值:  I(pi)和I(qi)分别表示第i个像素点对的两个像素piqi的灰度值。最后把补丁区域内所有点对的比较结果串成一个二值位字符串的形式,从而形成了该特征点的描述符。

B = b0b1bibnd (2)

通过实验对比可知,nd = 128,256和512时,在运算速度,空间占用和准确性上可以达到最佳的效果。如果用字节型来表示描述符的话,那么

k =nd / 8 (3)

k就表示为描述符的字节数。

源码

1、程序源码

对于这算法,在opencv中已经有很好的源码实现,算法亮点有:

a、构造函数

BRIEF描述符创建的类是BriefDescriptorExtractor,它的构造函数为:

typedef void(*PixelTestFn)(InputArray, const std::vector<KeyPoint>&, OutputArray, bool use_orientation ); //函数指针,分别针对不同类型调用不同函数,三个函数区别在于所需要的像素点对的数量就不同 PixelTestFn test_fn_; BriefDescriptorExtractorImpl::BriefDescriptorExtractorImpl(int bytes, bool use_orientation) : bytes_(bytes), test_fn_(NULL) { use_orientation_ = use_orientation; switch (bytes) { case : test_fn_ = pixelTests16; break; case : test_fn_ = pixelTests32; break; case : test_fn_ = pixelTests64; break; default: CV_Error(Error::StsBadArg, "bytes must be 16, 32, or 64"

);
}
}

b、接口函数

void

BriefDescriptorExtractorImpl::compute(InputArray image, std::vector<KeyPoint>& keypoints, OutputArray descriptors) { // Construct integral image for fast smoothing (box filter) Mat sum; Mat grayImage = image.getMat(); //计算时为灰度图 if( image.type() != CV_8U ) cvtColor( image, grayImage, COLOR_BGR2GRAY ); ///TODO allow the user to pass in a precomputed integral image //if(image.type() == CV_32S) // sum = image; //else //创建积分图像 integral( grayImage, sum, CV_32S); //Remove keypoints very close to the border // PATCH_SIZE = 48;表示补丁区域的边长,KERNEL_SIZE = 9;表示盒状滤波器的边长 //根据补丁区域和盒状滤波器的尺寸大小,去掉那些过于靠近图像边界的特征点 KeyPointsFilter::runByImageBorder(keypoints, image.size(), PATCH_SIZE/ + KERNEL_SIZE/); //描述符矩阵变量清零 descriptors.create((int)keypoints.size(), bytes_, CV_8U); descriptors.setTo(Scalar::all()); //调用test_fn_指向的函数,创建BRIEF描述符

    test_fn_(sum, keypoints, descriptors, use_orientation_);
}

3、Brief描述子产生

static void pixelTests16(InputArray _sum, const std::vector<KeyPoint>& keypoints, OutputArray _descriptors, bool use_orientation ) { Matx21f R; Mat sum = _sum.getMat(), descriptors = _descriptors.getMat(); //遍历特征点 for (size_t i = ; i < keypoints.size(); ++i) { //描述子首地址 uchar* desc = descriptors.ptr(static_cast<int>(i)); //获取特征点 const KeyPoint& pt = keypoints[i]; //如果有旋转 if ( use_orientation ) { float angle = pt.angle; angle *= (float)(CV_PI/.f); R(,) = sin(angle); R(,) = cos(angle); } //开始平滑产生描述子 #include "generated_16.i"

    }
}

4、smoothedSum函数

对点进行盒装高斯平滑作用,函数是根据像素点位置,返回此区域的差分和

//sum为积分图像,pt为特征点变量,x和y表示点对中某一个像素相对于特征点的坐标,函数返回滤波的结果 inline int smoothedSum(const Mat& sum, const KeyPoint& pt, int y, int x) { //盒状滤波器边长的一半 static const int HALF_KERNEL = BriefDescriptorExtractor::KERNEL_SIZE / ; //计算点对中某一个像素的绝对坐标 int img_y = (int)(pt.pt.y + 0.5) + y; int img_x = (int)(pt.pt.x + 0.5) + x; //计算以该像素为中心,以KERNEL_SIZE为边长的正方形内所有像素灰度值之和,本质上是均值滤波 return sum.at<int>(img_y + HALF_KERNEL + , img_x + HALF_KERNEL + ) - sum.at<int>(img_y + HALF_KERNEL + , img_x - HALF_KERNEL) - sum.at<int>(img_y - HALF_KERNEL, img_x + HALF_KERNEL + ) + sum.at<int>(img_y - HALF_KERNEL, img_x -

 HALF_KERNEL);
}

5、根据每个点差分和结合公式形成描述子,其中是16字节表示一个特征点(描述子),每个字节通过比较积分图像灰度值得到(8位)

//定义宏SMOOTHED,作用就是调用smoothedSum函数,SMOOTHED中的参数y和x表示相对于特征点的坐标 #define SMOOTHED(y,x) smoothedSum(sum, pt, y, x) //该描述符需要16个字节型变量,所以从desc[0]到desc[15] desc[] = (uchar)( //每个字节型变量由8位组成 //比较平滑处理以后的坐标为(-2, -1)和(7, -1)的两个像素的灰度值,如公式1,并把结果移位到第7位上 ((SMOOTHED(-, -) < SMOOTHED(, -)) << ) + ((SMOOTHED(-, -) < SMOOTHED(-, )) << ) + //第6位 ((SMOOTHED(, -) < SMOOTHED(, )) << ) + //第5位 ((SMOOTHED(, ) < SMOOTHED(-, -)) << ) + //第4位 ((SMOOTHED(, ) < SMOOTHED(-, )) << ) + //第3位 ((SMOOTHED(-, ) < SMOOTHED(, -)) << ) + //第2位 ((SMOOTHED(-, ) < SMOOTHED(, )) << ) + //第1位 ((SMOOTHED(-, ) < SMOOTHED(-, )) << )); //第0位 //以下省略 desc[] = …… …… desc[] = ……

上述源码相对比较简单大体流程分为:转化为灰度图,求取积分图,去掉边界干扰点,遍历特征点根据特征点获取盒装区域内每个小块差分值,由此生成描述子。在实际应用中,虽然点对都是按一定规则随机选择的,但在确定了补丁区域大小S的情况下,点对的坐标位置一旦随机选定,就不再更改,自始自终都用这些确定下来的点对坐标位置。也就是说这些点对的坐标位置其实是已知的,在编写程序的时候,这些坐标事先存储在系统中,在创建描述符时,只要调用这些坐标即可。

2、opencv调用函数

class CV_EXPORTS BriefDescriptorExtractor : public DescriptorExtractor
{
public:
static const int PATCH_SIZE = ; //S为48即窗口为48x48
static const int KERNEL_SIZE = ; //高斯滤波器窗口为9x9

// bytes is a length of descriptor in bytes. It can be equal 16, 32 or 64 bytes.
BriefDescriptorExtractor( int bytes = );//保存特征的空间为32字节,即32x8=256bit

    ...
...
...
}
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/features2d/features2d.hpp> using namespace cv; int main(int argc, char** argv) { Mat img_1 = imread("img1.png"); Mat img_2 = imread("img2.png"); // -- Step 1: Detect the keypoints using STAR Detector std::vector<KeyPoint> keypoints_1,keypoints_2; StarDetector detector; detector.detect(img_1, keypoints_1); detector.detect(img_2, keypoints_2); // -- Stpe 2: Calculate descriptors (feature vectors) BriefDescriptorExtractor brief; Mat descriptors_1, descriptors_2; brief.compute(img_1, keypoints_1, descriptors_1); brief.compute(img_2, keypoints_2, descriptors_2); //-- Step 3: Matching descriptor vectors with a brute force matcher BFMatcher matcher(NORM_HAMMING); std::vector<DMatch> mathces; matcher.match(descriptors_1, descriptors_2, mathces); // -- dwaw matches Mat img_mathes; drawMatches(img_1, keypoints_1, img_2, keypoints_2, mathces, img_mathes); // -- show imshow("Mathces", img_mathes); waitKey(); return

;
}

3、实验结果分析

下面的内容是参考http://blog.csdn.net/hujingshuang/article/details/46910259,中,其中讲解特征描述的特点,通过编译器看到描述子。在img1中检测到14个特征点,img2中检测到76个:

以img1为例,14个特征点,每个特征点用一个32字节(256bit)的二进制编码描述,则有:

进一步查看上图中第二个红框的信息,这表明特征描述子存放的位置,起始地址是:0x01c08030,结束地址为:0x01c081f0。

可以看到,这就是最终提取到的特征点描述编码了,一行32个字节,共14行,刚好对应img1的14个特征点。同理可知img2中得到了76个特征编码。假如以img1中的特征为准,在img2中寻找与其匹配的特征点,结果如下:

可以看到,img1中的14个特征点依次与img2中的第74、47、... 、29特征点配成一对,最终匹配效果见上面的实验结果图。

四、总结

Brief是一种对特征点产生描述子的算法,用二进制表示,其占用内存空间小,计算快。

由于在计算中,是对一副图像进行测试、计算,不具备尺度不变形以及旋转不变性,同时对噪声鲁棒性较差

五、参考文献

1、BRIEF特征点描述算法

2、特征描述子总结

3、源码分析

Brief描述子的更多相关文章

  1. BRIEF 特征描述子

    Binary Robust Independent Elementary Features www.cnblogs.com/ronny 1. BRIEF的基本原理 我们已经知道SIFT特征采用了128 ...

  2. 基于HOG-3D的时空描述子

    作者提出一种新的基于局部描述子的行为识别算法.

  3. SIFT算法:特征描述子

    SIFT算法:DoG尺度空间生产  SIFT算法:KeyPoint找寻.定位与优化 SIFT算法:确定特征点方向  SIFT算法:特征描述子 目录: 1.确定描述子采样区域 2.生成描述子 2.1 旋 ...

  4. SIFT解析(三)生成特征描述子

    以上两篇文章中检测在DOG空间中稳定的特征点,lowe已经提到这些特征点是比Harris角点等特征还要稳定的特征.下一步骤我们要考虑的就是如何去很好地描述这些DOG特征点. 下面好好说说如何来描述这些 ...

  5. (二)ORB描述子提取源码思路与实现

    ORBSLAM2中ORB特征提取的特点 ORBSLAM2中通过对OpenCV中的ORB特征点提取类进行修改,对图像进行分块提取,而后划分节点,使得每个节点中保存的特征点性能是该节点所有特征点中最好的. ...

  6. (一)ORB描述子提取

    ORBSLAM2中使用ORB描述子的方法 经典的视觉SLAM系统大体分为两种:其一是基于特征点法的,其二是基于直接法的.那么本文主要就讲特征点法的SLAM. 基于特征点法的视觉SLAM系统典型的有PT ...

  7. BRIEF特征点描述子

    简介 BRIEF是2010年的一篇名为<BRIEF:Binary Robust Independent Elementary Features>的文章中提出,BRIEF是对已检测到的特征点 ...

  8. 图像特征与描述子(直方图, 聚类, 边缘检测, 兴趣点/关键点, Harris角点, 斑点(Blob), SIFI, 纹理特征)

    1.直方图 用于计算图片特征,表达, 使得数据具有总结性, 颜色直方图对数据空间进行量化,好比10个bin 2. 聚类 类内对象的相关性高 类间对象的相关性差 常用算法:kmeans, EM算法, m ...

  9. 二进制描述子 BRIEF(ORB), BRISK, FREAK

    二进制描述子设计原则体现在三个部分: 采样pattern 方向orientation compensation 配对sampling pairs ORB基于BRIEF: BRISK是用于OKVIS的描 ...

随机推荐

  1. Automotive Security的一些资料和心得(5):Privacy

    1. Introduction 1.1 "Customers own their data and we can be no more than the trsted stewards of ...

  2. OpenVPN下载、安装、配置及使用详解

    OpenVPN下载.安装.配置及使用详解   OpenVPN简介 OpenVPN是一个用于创建虚拟专用网络(Virtual Private Network)加密通道的免费开源软件.使用OpenVPN可 ...

  3. BT5之网络配置

    输入ifconfig命令,可以查看当前IP地址设置情况.查看路由表的命令(用来检查默认网关是否设置正确):netstat -r 一.让BT5自动获取IP地址 自动获取IP地址,使用dhclient命令 ...

  4. linux下查看文件编码及修改编码

    http://blog.csdn.net/jnbbwyth/article/details/6991425 查看文件编码在Linux中查看文件编码可以通过以下几种方式:1.在Vim中可以直接查看文件编 ...

  5. .NET(C#):灵活运用CryptoStream,加密不是必须用CryptoStreamMode.Write

    首先.NET中的ICryptoTransform是单向的,也就是只能从一个状态将数据转化成另一个状态,反之是不可以的.当然手动 操作ICryptoTransform还是比较繁琐的,通过CryptoSt ...

  6. HDU1632+半平面交

    模板题 题意:给定两个凸多边形,求出合并后的面积,这个合并后的面积不包括重叠部分. #include<stdio.h> #include<string.h> #include& ...

  7. Jmeter 执行java脚本结束时提示:he JVM should have exitted but did not

    使用jmeter执行java协议测试结束时会提示:he JVM should have exitted but did not ,jmeter2.11以后的可以 通过设置:      jmeteren ...

  8. HDU - 3594 Cactus

    这是一个有向仙人掌的题目,要求判定给定的图是不是强连通图,而且每一条边只能出现在一个环中,这里有一个介绍有向仙人掌的文档:http://files.cnblogs.com/ambition/cactu ...

  9. 产品不应该大而全,而是应该小而精(DropBox有感,产品要1分钟学会)

    昨天试用了一下DROPBOX的个人版,对它的功能与界面简单深感震惊. 后来与一位业内朋友交流了一下,他说: 产品一般都是通过一个点来做.把一个点做到最好有可能会成为平台.另外还要在合适的时间做合适的事 ...

  10. Android开发:Translucent System Bar 的最佳实践

    Translucent System Bar 的最佳实践 近几天准备抽空总结Android一些系统UI的实践使用,于是开始动手建了一个库AndroidSystemUiTraining ,边撸代码边写总 ...