一、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个像素点对的两个像素pi和qi的灰度值。最后把补丁区域内所有点对的比较结果串成一个二值位字符串的形式,从而形成了该特征点的描述符。
B = b0b1…bi…bnd (2)
通过实验对比可知,nd = 128,256和512时,在运算速度,空间占用和准确性上可以达到最佳的效果。如果用字节型来表示描述符的话,那么
k =nd / 8 (3)
k就表示为描述符的字节数。
源码
1、程序源码
对于这算法,在opencv中已经有很好的源码实现,算法亮点有:
a、构造函数
BRIEF描述符创建的类是BriefDescriptorExtractor,它的构造函数为:
);
}
}
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描述子产生
}
}
4、smoothedSum函数
对点进行盒装高斯平滑作用,函数是根据像素点位置,返回此区域的差分和
HALF_KERNEL);
}
5、根据每个点差分和结合公式形成描述子,其中是16字节表示一个特征点(描述子),每个字节通过比较积分图像灰度值得到(8位)
上述源码相对比较简单大体流程分为:转化为灰度图,求取积分图,去掉边界干扰点,遍历特征点根据特征点获取盒装区域内每个小块差分值,由此生成描述子。在实际应用中,虽然点对都是按一定规则随机选择的,但在确定了补丁区域大小S的情况下,点对的坐标位置一旦随机选定,就不再更改,自始自终都用这些确定下来的点对坐标位置。也就是说这些点对的坐标位置其实是已知的,在编写程序的时候,这些坐标事先存储在系统中,在创建描述符时,只要调用这些坐标即可。
2、opencv调用函数
{
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
...
...
...
}
;
}
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是一种对特征点产生描述子的算法,用二进制表示,其占用内存空间小,计算快。
由于在计算中,是对一副图像进行测试、计算,不具备尺度不变形以及旋转不变性,同时对噪声鲁棒性较差
五、参考文献
2、特征描述子总结