Viola-Jones 的人脸检测声称拥有 18 万个特征
-
19-09-2019 - |
题
我一直在实施改编 Viola-Jones 的人脸检测算法. 。该技术依赖于在图像内放置 24x24 像素的子帧,然后将矩形特征放置在其中的每个位置、每个可能的尺寸。
这些特征可以由两个、三个或四个矩形组成。下面给出一个例子。
他们声称详尽的集合超过 180k(第 2 部分):
鉴于检测器的基本分辨率为 24x24,详尽的矩形特征集相当大,超过 180,000 个。请注意,与HAAR不同,矩形特征集超过了。
以下陈述在论文中没有明确说明,因此它们是我的假设:
- 只有 2 个二矩形特征、2 个三矩形特征和 1 个四矩形特征。这背后的逻辑是我们正在观察 不同之处 在突出显示的矩形之间,没有明确的颜色或亮度或任何此类内容。
- 我们不能将要素类型 A 定义为 1x1 像素块;它必须至少为 1x2 像素。此外,类型 D 必须至少为 2x2 像素,并且此规则相应地适用于其他功能。
- 我们不能将特征类型 A 定义为 1x3 像素块,因为中间像素无法分割,并且从自身中减去它与 1x2 像素块相同;此特征类型仅针对偶数宽度定义。此外,特征类型 C 的宽度必须能被 3 整除,并且此规则相应地适用于其他特征。
- 我们不能定义宽度和/或高度为 0 的特征。因此,我们迭代 X 和 y 到 24 减去特征的大小。
基于这些假设,我计算了详尽的集合:
const int frameSize = 24;
const int features = 5;
// All five feature types:
const int feature[features][2] = {{2,1}, {1,2}, {3,1}, {1,3}, {2,2}};
int count = 0;
// Each feature:
for (int i = 0; i < features; i++) {
int sizeX = feature[i][0];
int sizeY = feature[i][1];
// Each position:
for (int x = 0; x <= frameSize-sizeX; x++) {
for (int y = 0; y <= frameSize-sizeY; y++) {
// Each size fitting within the frameSize:
for (int width = sizeX; width <= frameSize-x; width+=sizeX) {
for (int height = sizeY; height <= frameSize-y; height+=sizeY) {
count++;
}
}
}
}
}
结果是 162,336.
我发现近似 Viola 和 Jones 所说的“超过 180,000”的唯一方法是放弃假设 #4 并在代码中引入错误。这涉及将四行分别更改为:
for (int width = 0; width < frameSize-x; width+=sizeX)
for (int height = 0; height < frameSize-y; height+=sizeY)
那么结果就是 180,625. 。(请注意,这将有效防止这些特征接触副车架的右侧和/或底部。)
现在当然有问题:他们在执行过程中犯了错误吗?考虑表面为零的特征是否有意义?还是我看错了?
解决方案
仔细一看,你的代码对我来说看起来是正确的;这让人怀疑原作者是否存在相差一的错误。我想有人应该看看 OpenCV 是如何实现它的!
尽管如此,为了更容易理解,一项建议是翻转顺序 为了 首先循环遍历所有大小,然后循环遍历给定大小的可能位置:
#include <stdio.h>
int main()
{
int i, x, y, sizeX, sizeY, width, height, count, c;
/* All five shape types */
const int features = 5;
const int feature[][2] = {{2,1}, {1,2}, {3,1}, {1,3}, {2,2}};
const int frameSize = 24;
count = 0;
/* Each shape */
for (i = 0; i < features; i++) {
sizeX = feature[i][0];
sizeY = feature[i][1];
printf("%dx%d shapes:\n", sizeX, sizeY);
/* each size (multiples of basic shapes) */
for (width = sizeX; width <= frameSize; width+=sizeX) {
for (height = sizeY; height <= frameSize; height+=sizeY) {
printf("\tsize: %dx%d => ", width, height);
c=count;
/* each possible position given size */
for (x = 0; x <= frameSize-width; x++) {
for (y = 0; y <= frameSize-height; y++) {
count++;
}
}
printf("count: %d\n", count-c);
}
}
}
printf("%d\n", count);
return 0;
}
与之前的结果相同 162336
为了验证这一点,我测试了 4x4 窗口的情况,并手动检查了所有情况(很容易计数,因为 1x2/2x1 和 1x3/3x1 形状相同,只是旋转了 90 度):
2x1 shapes:
size: 2x1 => count: 12
size: 2x2 => count: 9
size: 2x3 => count: 6
size: 2x4 => count: 3
size: 4x1 => count: 4
size: 4x2 => count: 3
size: 4x3 => count: 2
size: 4x4 => count: 1
1x2 shapes:
size: 1x2 => count: 12 +-----------------------+
size: 1x4 => count: 4 | | | | |
size: 2x2 => count: 9 | | | | |
size: 2x4 => count: 3 +-----+-----+-----+-----+
size: 3x2 => count: 6 | | | | |
size: 3x4 => count: 2 | | | | |
size: 4x2 => count: 3 +-----+-----+-----+-----+
size: 4x4 => count: 1 | | | | |
3x1 shapes: | | | | |
size: 3x1 => count: 8 +-----+-----+-----+-----+
size: 3x2 => count: 6 | | | | |
size: 3x3 => count: 4 | | | | |
size: 3x4 => count: 2 +-----------------------+
1x3 shapes:
size: 1x3 => count: 8 Total Count = 136
size: 2x3 => count: 6
size: 3x3 => count: 4
size: 4x3 => count: 2
2x2 shapes:
size: 2x2 => count: 9
size: 2x4 => count: 3
size: 4x2 => count: 3
size: 4x4 => count: 1
其他提示
全部。维奥拉和琼斯的论文中仍然存在一些混乱。
在他们的 CVPR'01 论文中明确指出:
“更具体地说,我们使用 三种特征。的值 两个矩形特征 是两个矩形区域内像素之和之间的差。这些区域的大小和形状相同,并且在水平或垂直方面相邻(见图1)。 三矩形特征 计算从中心矩形中总和减去两个外部矩形内的总和。最后 四个矩形功能".
在 IJCV'04 论文中,也说了完全相同的事情。 总共有 4 个功能. 。但奇怪的是,他们这次表示,详尽的功能集是 45396!这似乎不是最终版本。这里我猜想那里引入了一些额外的约束,例如 min_width、min_height、宽高比,甚至位置。
请注意,这两篇论文均可在以下网址下载 他的网页.
没有阅读整篇论文,你引用的措辞让我印象深刻
鉴于检测器的基础分辨率为24x24,详尽的矩形特征集非常大,超过180,000。请注意,与HAAR不同,矩形特征集超过了。
“矩形特征集超出了”,“详尽的集”
在我看来,这听起来像是一个设置,我希望论文作者能够跟进解释他们如何将搜索空间剔除为更有效的集合,例如,通过消除琐碎的情况,例如带有零的矩形表面积。
编辑:或者使用某种机器学习算法,正如摘要所暗示的那样。穷举集意味着所有可能性,而不仅仅是“合理”的可能性。
无法保证任何论文的任何作者的所有假设和发现都是正确的。如果您认为假设#4 有效,则保留该假设并尝试您的理论。您可能比原作者更成功。
相当好的观察,但他们可能会隐式地对 24x24 帧进行零填充,或者“溢出”,并在超出范围时开始使用第一个像素,如旋转移位,或者正如 Breton 所说,他们可能会将某些特征视为“琐碎特征”然后用 AdaBoost 丢弃它们。
此外,我编写了您的代码的 Python 和 Matlab 版本,这样我就可以自己测试代码(对我来说更容易调试和遵循),因此如果有人发现它们有用,我会将它们发布在这里。
Python:
frameSize = 24;
features = 5;
# All five feature types:
feature = [[2,1], [1,2], [3,1], [1,3], [2,2]]
count = 0;
# Each feature:
for i in range(features):
sizeX = feature[i][0]
sizeY = feature[i][1]
# Each position:
for x in range(frameSize-sizeX+1):
for y in range(frameSize-sizeY+1):
# Each size fitting within the frameSize:
for width in range(sizeX,frameSize-x+1,sizeX):
for height in range(sizeY,frameSize-y+1,sizeY):
count=count+1
print (count)
MATLAB:
frameSize = 24;
features = 5;
% All five feature types:
feature = [[2,1]; [1,2]; [3,1]; [1,3]; [2,2]];
count = 0;
% Each feature:
for ii = 1:features
sizeX = feature(ii,1);
sizeY = feature(ii,2);
% Each position:
for x = 0:frameSize-sizeX
for y = 0:frameSize-sizeY
% Each size fitting within the frameSize:
for width = sizeX:sizeX:frameSize-x
for height = sizeY:sizeY:frameSize-y
count=count+1;
end
end
end
end
end
display(count)