本文为原创,欢迎转载,转载请注明出处:
在我看来,首先要说明的一点就是,BING这个方法与其称之为一种新的一般物体识别方法,倒不如说是一种加速方法。其方法的成功之处就是在于其较快的处理速度,即其文章中所说的1000fps。目前,大部分的物体识别方法都是先学习大量的样本,获得学习结果,然后用不同的框遍历需要测试的图片,将遍历的框中内容依次与学习结果比对,然后确定框中是否存在此物体。然而,对于一幅N*N图像而言,要遍历所有可能的框,则其需要遍历的次数大约为N的4次方数量级。所以说,这个方框的筛选就变成了一个很大的问题,也是限制速度的主要因素。而BING这种方法就是一种快速挑出候选框的方法。下面就解释下,为何我认为其不是一种新的方法,而是一种加速方法。
1、其算法最终实现的功能并非物体识别,而是物体候选框的筛选。即其所说的每秒1000张图片的处理速度,并非真正找出图片中的物体,而是找出可能包含物体的候选框。这个候选框的数量大小一般是千位量级。即使如此,这也大大减少了后续物体识别的工作量。
2、其方法中有很多地方都用到了加速过程,下面一一解释:
(1)其大胆的将图像缩小成36中不同大小的图像,然后选用8*8模板进行匹配。一般来说,我们在处理图像时,都尽可能的希望图像质量好,并且在处理的过程中不会轻易的修改原图像,而他这里却将原图像给缩小。现假设有一1000*1000的图像,假设我要用100*100的框进行遍历,则遍历的次数为900*900=810000次,而若将图像缩小为500*500的图像,则此时采用50*50的方框遍历,其效果与原来的100*100的方框是一样的,但是其遍历的次数却变为了450*450次,整整减少了4倍。另外,图像虽然缩小,会损失一些图像的信息,但是物体的结构轮廓仍旧是存在的,故不影响我的检测结果。
至于选用36中不同大小,其选取的依据也是根据图像包含物体的可能性来选择的,即选取的带下都是最有可能完整包含物体的。
(2)其选取的特征是物体在梯度图下的闭合轮廓。梯度图的好处是能够完整的表征物体的形状,且数据量较小,因为其是单通道的图像,每位也是一个8位的unsigned char的数,在这里作者做了第二次加速。即将数据进一步简化,舍去8位数据的后四位,选取数据的前四位代替其本身。这里,我分析里一下的这个舍去方法所可能产生的误差影响。假设某位像素的值为0001 1111(31),而舍去后四位后变成0001 0000(16),这种情况将是相对误差和绝对误差均为最大的情况。这里的相对误差为(31-16)/31=48.4%,绝对误差为15,。这些都是误差最大时的情况,而误差最小则为0。因此这些都是可以接受的,且在图像中也看不到很明显的特征,
这个数据减少的过过程为后面的位操作减少了一般的位移运算量。
亲测如下:
上面两张为原梯度图,第二张为舍去后四位的梯度图。(可以看出只是图像变的稍微暗了一些,别的没有影响)
(3)将所有对图像像素的位移,转换为对位的操作,数据存储在64位的整型数据中。数据转换如下:
Mat FilterBING::matchTemplate(const Mat &mag1u){ const int H = mag1u.rows, W = mag1u.cols; const Size sz(W+1, H+1); // Expand original size to avoid dealing with boundary conditions Mat_<INT64> Tig1 = Mat_<INT64>::zeros(sz), Tig2 = Mat_<INT64>::zeros(sz); Mat_<INT64> Tig4 = Mat_<INT64>::zeros(sz), Tig8 = Mat_<INT64>::zeros(sz); Mat_<byte> Row1 = Mat_<byte>::zeros(sz), Row2 = Mat_<byte>::zeros(sz); Mat_<byte> Row4 = Mat_<byte>::zeros(sz), Row8 = Mat_<byte>::zeros(sz); Mat_<float> scores(sz); for(int y = 1; y <= H; y++){ const byte* G = mag1u.ptr<byte>(y-1); INT64* T1 = Tig1.ptr<INT64>(y); // Binary TIG of current row INT64* T2 = Tig2.ptr<INT64>(y); INT64* T4 = Tig4.ptr<INT64>(y); INT64* T8 = Tig8.ptr<INT64>(y); INT64* Tu1 = Tig1.ptr<INT64>(y-1); // Binary TIG of upper row INT64* Tu2 = Tig2.ptr<INT64>(y-1); INT64* Tu4 = Tig4.ptr<INT64>(y-1); INT64* Tu8 = Tig8.ptr<INT64>(y-1); byte* R1 = Row1.ptr<byte>(y); byte* R2 = Row2.ptr<byte>(y); byte* R4 = Row4.ptr<byte>(y); byte* R8 = Row8.ptr<byte>(y); float *s = scores.ptr<float>(y); for (int x = 1; x <= W; x++) { byte g = G[x-1]; R1[x] = (R1[x-1] << 1) | ((g >> 4) & 1); R2[x] = (R2[x-1] << 1) | ((g >> 5) & 1); R4[x] = (R4[x-1] << 1) | ((g >> 6) & 1); R8[x] = (R8[x-1] << 1) | ((g >> 7) & 1); T1[x] = (Tu1[x] << 8) | R1[x]; T2[x] = (Tu2[x] << 8) | R2[x]; T4[x] = (Tu4[x] << 8) | R4[x]; T8[x] = (Tu8[x] << 8) | R8[x]; s[x] = dot(T1[x], T2[x], T4[x], T8[x]); } } Mat matchCost1f; scores(Rect(8, 8, W-7, H-7)).copyTo(matchCost1f); return matchCost1f; }
上面三个方面就是其加速的过程,其中第二步中的8*8模板的选取与第三步数据的转换是相关的。
此种方法对于候选框的筛选方法很快,且其基于物体的闭合性这一性质,能够对几乎所有的物体进行预判,因此若想识别某一特定物体,则在获取候选窗口的时候,可以先用此法剔除大部分的窗口,得到少数的候选窗口,然后在将候选窗口的内容,送入分类器,进行区分,这样可以很大的提高效率。
另外,因为此种方法中没有很多复杂图片操作,故可移植性比较强。且速度较快,则可以针对很多实时的场景进行实践。
对于基于嵌入式的图像处理,运用此种方法可以加快处理速度,完成针对实时性的要求。并且,其关于位的操作,能够更加将嵌入式的硬件特性运用充分,即减少不必要的冗杂运算,使用尽可能最直接最有效的操作。
原文地址:http://blog.csdn.net/autocyz/article/details/43305767