Viola-Jones的人脸检测宣称拥有18万个功能


84

我一直在实施Viola-Jones的人脸检测算法的改编。该技术依赖于在图像内放置一个24x24像素的子帧,然后在每个位置以各种尺寸放置矩形特征。

这些特征可以由两个,三个或四个矩形组成。提供以下示例。

矩形特征

他们声称穷举集超过18万(第2节):

假设检测器的基本分辨率为24x24,则详尽的矩形特征集非常大,超过180,000。请注意,与Haar基础不同,矩形要素集过于完整。

本文中未明确陈述以下陈述,因此它们是我的假设:

  1. 只有2个两个矩形的特征,2个三个矩形的特征和1个四个矩形的特征。其背后的逻辑是,我们正在观察突出显示的矩形之间的差异,而不是显式地观察颜色或亮度或任何类似的东西。
  2. 我们无法将要素类型A定义为1x1像素块;它必须至少为1x2像素。同样,类型D必须至少为2x2像素,并且该规则也适用于其他功能。
  3. 我们无法将要素类型A定义为1x3像素块,因为无法对中间像素进行分区,并且从自身中减去它就等于1x2像素块;仅为均匀宽度定义此要素类型。同样,要素类型C的宽度必须被3整除,并且该规则也适用于其他要素。
  4. 我们无法定义宽度和/或高度为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?
Niki

为什么您的x / y循环从1开始?我假设x / y是要素矩形的左上角坐标。那么x / y应该不是从0/0开始吗?
Niki

除了开始于0还是1之外,结束于x < size假设4:我希望特征保留在子帧中,但尺寸至少为1x1。关于特征的尺寸是否不应延伸到子帧之外,也许,这也是一个假设。
Paul Lammertsma,2009年

同样,如果我从0开始x,则必须运行到x < size - 1,所以没有收益。
Paul Lammertsma,2009年

我已经完成了无数循环。这对我来说似乎是错误的。<size将使x不再变为24,从0开始将为您提供0 ... 23,尺寸为1像素宽,矩形将永远不会离开框架。
布列塔尼

Answers:


40

仔细看看,您的代码对我来说是正确的;这使人们想知道原始作者是否有一个单一的错误。我想有人应该看看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

令人信服。非常有说服力的是,我很确定我们是对的。我已经发送了一封电子邮件给作者,以查看我在推理中是否犯了一些根本性的错误。我们将看看一个忙碌的人是否有时间回应。
Paul Lammertsma,2009年

请记住,这件事已经问世两年了,此后进行了许多改进
Amro

25
指出180k的原始论文来自2001年计算机视觉与模式识别会议的论文集。经修订的论文于2003年被接受,并于2004年在《国际计算机视觉杂志》上发表。139(第2节末尾):“详尽的矩形集非常大,为160,000个”。看起来我们是对的!
Paul Lammertsma,2009年

3
太好了,谢谢您的更新。对于那些感兴趣的人,我找到了IJCV'04论文的链接:lear.inrialpes.fr/people/triggs/student/vj/viola-ijcv04.pdf
Amro,2009年

对,就是那样。160k,而不是180k。
Paul Lammertsma,2009年

9

所有。中提琴和琼斯的论文仍然有些混乱。

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

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

在IJCV'04论文中,说的完全相同。因此,共有4个功能。但是很奇怪的是,他们这次说详尽的功能集是45396!那似乎不是最终版本,在这里我猜想在那里引入了一些其他约束,例如min_width,min_height,宽度/高度比甚至位置。

请注意,这两篇论文都可以在他的网页上下载。


3

在没有阅读整篇论文的情况下,您的报价措词向我伸出来

假设检测器的基本分辨率为24x24,则详尽的矩形特征集非常大,超过180,000。请注意,与Haar基础不同,矩形要素集过于完整。

“矩形特征集太完备”“详尽集”

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

编辑:或使用某种机器学习算法,如摘要所示。穷举集意味着所有可能性,而不仅仅是“合理”的可能性。


我应该在“过度完成”之后添加脚注:“完全基础在基础元素之间不具有线性相关性,并且与图像空间具有相同数量的元素,在这种情况下为576。全套180,000千个特征是许多次过分的-完成。” 他们没有明确地摆脱没有表面的分类器,而是使用AdaBoost来确定“可以将非常少量的这些特征组合起来以形成有效的分类器”。好的,因此零表面特征将被立即删除,但是为什么要首先考虑它们呢?
Paul Lammertsma,2009年

听起来好像有人真的进入了集合论。
布列塔尼

我同意,详尽的清单将暗示所有可能性。但是请考虑一下,如果x的宽度取1到24,且宽度<= x,则该功能将在子帧之外扩展1个像素!
Paul Lammertsma'2009年

您确定您的代码没有被“一对一”的错误困扰吗?我只是仔细看了一下,您肯定有一种有趣的方式编写for循环。
布列塔尼

我应该对此加以说明-我只是想了一下,如果您有一个矩形,该矩形高1像素,2像素高,3像素高,一直到24像素高,则有24种矩形,所有适用于24像素高的子帧。什么突出?
布列塔尼

2

不能保证任何论文的任何作者的所有假设和发现都是正确的。如果您认为假设4是正确的,则保留该假设,然后尝试您的理论。您可能比原始作者更成功。


实验表明,它的性能看似完全相同。我相信AdaBoost在第一个循环中只是删除了这些额外的零表面特征,但是我实际上并没有对此进行研究。
Paul Lammertsma,2009年

Viola和Jones在计算机视觉领域非常知名。实际上,该特定论文被认为是开创性的。每个人都会犯错误,但是事实证明这种特定算法可以很好地工作。
Dima

1
当然,我完全不怀疑他们的方法。它高效且效果​​很好!这个理论是合理的,但是我相信他们可能会错误地将其检测器剪短一个像素,并包括不必要的零表面特征。如果没有,我向您挑战以演示180k功能!
Paul Lammertsma,2009年

事实是每个人都是人。每个人都会犯错误。当一个大人物犯错时,他们往往世代相传,因为人们害怕质疑所接受的智慧。但是真正的科学,遵循科学的方法,无论他们的名字有多大,都不会崇拜任何人。如果这是科学,那么凡人就能付出努力,了解其运作方式并使其适应自己的情况。
Michael Dillon

我们拭目以待; 我已经给作者发了一封电子邮件。
Paul Lammertsma'2009年

1

很好的观察,但是他们可能会隐式填充24x24帧,或者“溢出”,并在超出范围时使用第一个像素,例如在旋转移位中,或者正如Breton所说,他们可能会将某些特征视为“琐碎的特征”然后使用AdaBoost丢弃它们。

另外,我编写了您的代码的Python和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 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版本。
卡斯帕罗夫92年

0

他们在2001年的原始论文中仅声明使用了三种功能:

我们使用三种功能

区域具有相同的大小和形状

由于每种类型都有两个方向,因此可以合理地假设它们总共使用6个特征(至少用于计算特征总数):2个两个矩形特征,2个三个矩形特征和2个四个矩形特征。在这种假设下,确实有超过18万种功能:

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。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.