Viola-Jones 的人脸检测声称拥有 18 万个特征

Posted

技术标签:

【中文标题】Viola-Jones 的人脸检测声称拥有 18 万个特征【英文标题】:Viola-Jones' face detection claims 180k features 【发布时间】:2010-12-15 00:34:30 【问题描述】:

我一直在实施Viola-Jones' face detection algorithm 的改编。该技术依赖于在图像中放置一个 24x24 像素的子帧,然后将矩形特征放置在图像内的每个位置,并且可能具有各种尺寸。

这些特征可以由两个、三个或四个矩形组成。下面给出一个例子。

他们声称详尽的集合超过 180k(第 2 部分):

鉴于检测器的基本分辨率为 24x24,详尽的矩形特征集相当大,超过 180,000 个。请注意,与 Haar 基不同,矩形的集合 功能过于完善。

以下陈述在论文中没有明确说明,因此它们是我的假设:

    只有 2 个二矩形特征、2 个三矩形特征和 1 个四矩形特征。这背后的逻辑是我们正在观察突出显示的矩形之间的差异,而不是明确地观察颜色或亮度或类似的任何东西。 我们不能将特征类型 A 定义为 1x1 像素块;它必须至少为 1x2 像素。此外,类型 D 必须至少为 2x2 像素,此规则适用于其他特征。 我们不能将特征类型 A 定义为 1x3 像素块,因为中间像素无法分割,从自身中减去它与 1x2 像素块相同;此特征类型仅针对偶数宽度定义。此外,特征类型 C 的宽度必须能被 3 整除,此规则也适用于其他特征。 我们无法定义宽度和/或高度为 0 的特征。因此,我们将 xy 迭代为 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。 (请注意,这将有效地防止特征接触子框架的右侧和/或底部。)

现在当然是问题:他们在实施过程中是否犯了错误?考虑表面为零的特征是否有意义?还是我看错了?

【问题讨论】:

为什么我在运行您的代码时得到 count=114829? 为什么你的 x/y 循环从 1 开始?我假设 x/y 是特征矩形的左上角坐标。那么 x/y 不应该从 0/0 开始吗​​? 除了它是从 0 还是 1 开始之外,以 x &lt; size 结束与假设 #4 有关:我希望该功能保留在子帧内,但尺寸至少为 1x1。至于特征的维度是否不应该延伸到子帧之外,嗯,也许这也是一个假设。 同样,如果我从 0 开始 x,它必须运行到 x &lt; size - 1,所以没有增益。 我已经完成了无数次循环。这对我来说似乎是错误的。 【参考方案1】:

仔细一看,你的代码对我来说是正确的;这让人想知道原始作者是否有一个错误的错误。我想有人应该看看OpenCV是如何实现的!

不过,为了更容易理解,一个建议是翻转 for 循环的顺序,首先遍历所有大小,然后遍历给定大小的可能位置:

#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

【讨论】:

令人信服。如此令人信服,我相当确定我们是对的。我已经给作者发了一封电子邮件,看看我的推理是否犯了一些根本性的错误。我们会看看一个忙碌的人是否有时间回复。 请记住,这个东西已经问世好几年了,从那时起进行了许多改进 声明 180k 的原始论文来自 2001 年计算机视觉和模式识别会议的会议记录。 2003 年接受并于 2004 年在 International Journal of Computer Vision 上发表的修订论文在 p. 139(第 2 节结束):“详尽的矩形集非常大,160,000”。看起来我们是对的! 太好了,感谢您的更新。对于那些感兴趣的人,我找到了 IJCV'04 论文的链接:lear.inrialpes.fr/people/triggs/student/vj/viola-ijcv04.pdf 是的,就是这样。 160k,不是 180k。【参考方案2】:

全部。 Viola 和 Jones 的论文中仍然存在一些混乱。

在他们的 CVPR'01 论文中明确指出

“更具体地说,我们使用三个 种特征。一个值 两个矩形特征是两者之和的差 两个矩形区域内的像素。 这些区域具有相同的大小和 形状和水平或 垂直相邻(见图1)。 一个三矩形特征计算两个外的和 从总和中减去的矩形 一个中心矩形。最后一个 四矩形特征”。

在 IJCV'04 论文中,也说了同样的话。 总共有 4 个功能。但奇怪的是,他们这次表示详尽的功能集是 45396!那似乎不是最终版本。这里我猜那里引入了一些额外的约束,例如 min_width、min_height、宽/高比,甚至是位置。

请注意,这两篇论文都可以在 his webpage 上下载。

【讨论】:

【参考方案3】:

没有读完整篇论文,你引述的措辞让我印象深刻

鉴于基本分辨率 检测器是 24x24,穷举集 的矩形特征相当大, 超过 180,000 。请注意,与 哈尔基,矩形的集合 功能过于完善。

“矩形特征集过度完备” “穷举”

对我来说这听起来像是一个设置,我希望论文作者跟进解释他们如何将搜索空间缩减为更有效的设置,例如,摆脱琐碎的情况,例如表面积为零的矩形。

编辑:或使用某种机器学习算法,作为抽象提示。详尽的集合意味着所有的可能性,而不仅仅是“合理”的可能性。

【讨论】:

我应该在“overcomplete”之后加上脚注:“一个完整的基在基元素之间没有线性依赖关系,并且具有与图像空间相同的元素数量,在本例中为 576。 180,000,000 个功能已经完成了很多倍。”他们并没有明确摆脱没有表面的分类器,他们使用 AdaBoost 来确定“可以将这些特征中的极少数特征组合起来形成一个有效的分类器”。好的,所以零表面特征将立即被丢弃,但为什么要首先考虑它们呢? 嗯,这听起来像是一个真正喜欢集合论的人的推理。 我同意,详尽的集合意味着所有的可能性。但考虑一下,如果您将 x 和宽度 您确定您的代码没有“被一个”错误所困扰吗?我只是仔细看了看,你肯定有一种有趣的方式来编写 for 循环。 我应该有资格 - 我只是想了一下,如果你有一个 1 像素高、2 像素高、3 像素高、一直到 24 像素高的矩形,你有 24 种矩形,它们都适合一个 24 像素高的子帧。有什么悬垂的?【参考方案4】:

不保证任何论文的任何作者的所有假设和发现都是正确的。如果您认为假设 #4 是有效的,那么请保留该假设,并尝试您的理论。你可能比原作者更成功。

【讨论】:

实验表明它的表现似乎完全相同。我相信 AdaBoost 只是在第一个周期中丢弃了那些额外的零表面特征,但我还没有真正研究过这个。 Viola 和 Jones 是计算机视觉领域的知名人士。事实上,这篇特别的论文被认为是开创性的。每个人都会犯错,但这种特殊的算法已被证明非常有效。 当然,我一点也不怀疑他们的方法。它很有效,而且效果很好!这个理论是合理的,但我相信他们可能错误地将检测器裁剪了一个像素,并包含了不必要的零表面特征。如果没有,我挑战你展示 180k 功能! 事实上,每个人都是人。每个人都会犯错误。当一个大人物犯错时,他们往往会隐藏几代人,因为人们害怕质疑公认的智慧。但真正的科学,遵循科学的方法,不崇拜任何人,无论他们的名字有多大。如果它是科学,那么凡人也可以付出努力,了解它的工作原理并使其适应他们的环境。 我们拭目以待;我已经给作者发了一封电子邮件。【参考方案5】:

很好的观察,但他们可能会隐含地对 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)

【讨论】:

为什么要使用 5 个功能,主要问题中只发布了 4 个。但无论如何感谢python版本。【参考方案6】:

在他们 2001 年的原始论文中,他们只声明他们使用了三种特征:

我们使用三种特征

分别有两个、三个和四个矩形。

由于每种类型都有两个方向(相差 90 度),也许为了计算他们使用 2*3 类型的特征的总特征数:2 个二矩形特征、2 个三矩形特征和 2 个四-矩形特征。有了这个假设,确实有超过 180,000 个特征:

feature_types = [(1,2), (2,1), (1,3), (3,1), (2,2), (2,2)]
window_size = (24,24)

total_features = 0
for f_type in feature_types:
    for f_height in range(f_type[0], window_size[0] + 1, f_type[0]):
        for f_width in range(f_type[1], window_size[1] + 1, f_type[1]):
            total_features += (window_size[0] - f_height + 1) * (window_size[1] - f_width + 1)
            
print(total_features)
# 183072

第二个四矩形特征与第一个仅相差一个符号,因此无需保留它,如果我们删除它,则特征总数会减少到 162,336 个。

【讨论】:

以上是关于Viola-Jones 的人脸检测声称拥有 18 万个特征的主要内容,如果未能解决你的问题,请参考以下文章

Viola-Jones 人脸检测器如何处理多种尺寸的人脸?

Viola-Jones(人脸检测)

Viola-Jones 人脸检测:像素之间的高对比度

有没有办法在不使用 viola-jones 算法的情况下用 opencv 检测人脸? [关闭]

如果在 Viola-jones 人脸检测方法中,每一轮 boosting 都选择相同的 Haar 特征怎么办?

收集adaboost算法的负样本进行人脸检测