最大稳定极值区域(MSER)检测
Posted zizi7
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最大稳定极值区域(MSER)检测相关的知识,希望对你有一定的参考价值。
Lowe和Bay提出的SIFT和SURF算法高效实现了具有尺度和旋转不变性的特征检测,但这些特征不具有仿射不变性。
区域检测针对各种不同形状的图像区域,通过对区域的旋转和尺寸归一化,可以实现仿射不变性。
MSER(Maximally Stable Extrernal Regions)是区域检测中影响最大的算法
1. 原理
MSER基于分水岭的概念:对图像进行二值化,二值化阈值取[0, 255],这样二值化图像就经历一个从全黑到全白的过程(就像水位不断上升的俯瞰图)。在这个过程中,有些连通区域面积随阈值上升的变化很小,这种区域就叫MSER。
,其中Qi表示第i个连通区域的面积,Δ表示微小的阈值变化(注水),当vi小于给定阈值时认为该区域为MSER。
显然,这样检测得到的MSER内部灰度值是小于边界的,想象一副黑色背景白色区域的图片,显然这个区域是检测不到的。因此对原图进行一次MSER检测后需要将其反转,再做一次MSER检测,两次操作又称MSER+和MSER-
2. 算法步骤
从上节可以看到,MSER的基本思路很简单,但编码实现是很需要算法和编程技巧的
以下算法步骤基于改进的分水岭算法:注水的地方固定,只有当该处的沟壑水漫出来后才能注入到另一个沟壑
此外,为方便编程,面积变化的计算方式也从双边改为单边检测,即
具体步骤(摘自Opencv2.4.9源码分析——MSER)
1、初始化栈和堆,栈用于存储组块(组块就是区域,就相当于水面,水漫过的地方就会出现水面,水面的高度就是图像的灰度值,因此用灰度值来表示组块的值),堆用于存储组块的边界像素,相当于水域的岸边,岸边要高于水面的,因此边界像素的灰度值一定不小于它所包围的区域(即组块)的灰度值。首先向栈内放入一个虚假的组块,当该组块被弹出时意味着程序的结束;
2、把图像中的任意一个像素(一般选取图像的左上角像素)作为源像素,标注该像素为已访问过,并且把该像素的灰度值作为当前值。这一步相当于往源像素这一地点注水;
3、向栈内放入一个空组块,该组块的值是当前值;
4、按照顺序搜索当前值的4-领域内剩余的边缘,对于每一个邻域,检查它是否已经被访问过,如果没有,则标注它为已访问过并检索它的灰度值,如果灰度值不小于当前值,则把它放入用于存放边界像素的堆中。另一方面,如果领域灰度值小于当前值,则把当前值放入堆中,而把领域值作为当前值,并回到步骤3;
5、累计栈顶组块的像素个数,即计算区域面积,这是通过循环累计得到的,这一步相当于水面的饱和;
6、弹出堆中的边界像素。如果堆是空的,则程序结束;如果弹出的边界像素的灰度值等于当前值,则回到步骤4;
7、从堆中得到的像素值会大于当前值,因此我们需要处理栈中所有的组块,直到栈中的组块的灰度值大于当前边界像素灰度值为止。然后回到步骤4。
至于如何处理组块,则需要进入处理栈子模块中,传入该子模块的值为步骤7中从堆中提取得到的边界像素灰度值。子模块的具体步骤为:
1)、处理栈顶的组块,即根据公式2计算最大稳定区域,判断其是否为极值区域;
2)、如果边界像素灰度值小于距栈顶第二个组块的灰度值,那么设栈顶组块的灰度值为边界像素灰度值,并退出该子模块。之所以会出现这种情况,是因为在栈顶组块和第二个组块之间还有组块没有被检测处理,因此我们需要改变栈顶组块的灰度值为边界像素灰度值(相当于这两层的组块进行了合并),并回到主程序,再次搜索组块;
3)、弹出栈顶组块,并与目前栈顶组块合并;
4)、如果边界像素灰度值大于栈顶组块的灰度值,则回到步骤1。
注:MSER算法参数较多,默认值如图3-1所示(摘自《图像局部不变性特征与描述》)
图3-1. MSER参数标准取值
3. MSER区域拟合
为了进一步对MSER得到的不规则区域进行描述和处理,需要对其进行椭圆拟合(椭圆可以反映区域的位置、尺寸、方向)
1)椭圆的重心
对区域内的每个点,计算整个区域的几何0阶矩和几何一阶矩
得到整个区域的重心位置
2) 椭圆的长半轴、短半轴、角度(长半轴与x轴顺时针)
计算中心二阶矩
其中
计算该二阶矩的两个特征值,有
于是可以分别得到其长半轴、短半轴、角度
4. 代码
目前MSER的代码实现有3:OpenCV(目前操作手册上还没有这个接口的介绍)、IDIAP的实现(C++)、VLFeat的实现(C)
其中OpenCV的代码解读可参考Opencv2.4.9源码分析——MSER,其和IDIAP的实现都严格遵循着上一节的算法步骤,
IDIAP的版本只有一个mser.cpp和mser.h,很干净便于移植(如果是C实现的就更好了),其在Github上声称比VLFeat快几倍。
VLFeat是一个C实现的图像特征检测算法库,包含了SIFT、HOG、K-Means、MSER等算法
贴上IDIAP和OpenCV实现的调用代码
#IDIAP
clock_t start = clock();
_MSER mser8(5, 0.0005, 0.5, 0.25, 0.2, true);
_MSER mser4(5, 0.0005, 0.5, 0.25, 0.2, false);
std::vector<_MSER::Region> regions[2];
mser8(imgBuffer, grayImg.cols, grayImg.rows, regions[0]);
// Invert the pixel values
for (int i = 0; i < grayImg.cols*grayImg.rows; ++i)
imgBuffer[i] = ~imgBuffer[i];
mser4(imgBuffer, grayImg.cols, grayImg.rows, regions[1]);
clock_t stop = clock();
for (int i = 0; i < 2; ++i)
for (int j = 0; j < regions[i].size(); ++j)
//IDIAP自己实现的椭圆拟合函数
drawEllipse(regions[i][j], img.cols, img.rows, 3, imgBufferRGB, colors[i]);
#OpenCV
cv::MSER ms(5, 0.0005, 1440);
std::vector<std::vector<cv::Point>> regions;
clock_t start = clock();
ms(grayImg, regions, cv::Mat());
clock_t stop = clock();
for (int i = 0; i < regions.size(); i++)
cv::ellipse(img, cv::fitEllipse(regions[i]), cv::Scalar(0, 0, 255));
从实际效果来看,运行效率 OpenCV > IDIAP,VLFeat没测试
图4-1左是IDIAP的实现效果(参数与代码中一致),右是OpenCV的实现效果(其余参数隐含在features2d.hpp内)
图4-1. MSER检测
以上是关于最大稳定极值区域(MSER)检测的主要内容,如果未能解决你的问题,请参考以下文章
m基于MSER最大稳定极值区域和SVM的交通标志检测识别算法的matlab仿真
mser 最大稳定极值区域(文字区域定位)算法 附完整C代码