web-dev-qa-db-fra.com

La détection de visage de Viola-Jones revendique 180 000 fonctionnalités

J'ai implémenté une adaptation de l'algorithme de détection de visage de Viola-Jones . La technique repose sur le placement d'une sous-trame de 24x24 pixels dans une image, puis sur le placement d'éléments rectangulaires à l'intérieur de celle-ci dans toutes les positions avec toutes les tailles possibles.

Ces fonctionnalités peuvent consister en deux, trois ou quatre rectangles. L'exemple suivant est présenté.

Rectangle features

Ils affirment que l'ensemble exhaustif est supérieur à 180k (section 2):

Étant donné que la résolution de base du détecteur est de 24x24, l'ensemble exhaustif des caractéristiques de rectangle est assez grand, plus de 180 000. Notez que contrairement à la base de Haar, l'ensemble des fonctions rectangulaires est trop complet.

Les déclarations suivantes ne sont pas explicitement énoncées dans le document, ce sont donc des hypothèses de ma part:

  1. Il n'y a que 2 entités à deux rectangles, 2 entités à trois rectangles et 1 entité à quatre rectangles. La logique derrière cela est que nous observons la différence entre les rectangles en surbrillance, pas explicitement la couleur ou la luminance ou quoi que ce soit de ce genre.
  2. Nous ne pouvons pas définir le type d'entité A comme un bloc de 1 x 1 pixel; il doit être au moins d'au moins 1x2 pixels. En outre, le type D doit être d'au moins 2x2 pixels, et cette règle s'applique en conséquence aux autres fonctionnalités.
  3. Nous ne pouvons pas définir le type d'entité A comme un bloc de 1x3 pixels car le pixel du milieu ne peut pas être partitionné, et le soustraire de lui-même est identique à un bloc de 1x2 pixels; ce type d'entité n'est défini que pour des largeurs paires. De plus, la largeur du type d'entité C doit être divisible par 3, et cette règle s'applique en conséquence aux autres entités.
  4. Nous ne pouvons pas définir une entité avec une largeur et/ou une hauteur de 0. Par conséquent, nous itérons x et y à 24 moins la taille de l'entité.

Sur la base de ces hypothèses, j'ai compté l'ensemble exhaustif:

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++;
                }
            }
        }
    }
}

Le résultat est 162 336 .

La seule façon dont j'ai trouvé d'approcher les "plus de 180 000" dont Viola & Jones parle, c'est de supprimer l'hypothèse n ° 4 et en introduisant des bogues dans le code. Cela implique de changer quatre lignes respectivement pour:

for (int width = 0; width < frameSize-x; width+=sizeX)
for (int height = 0; height < frameSize-y; height+=sizeY)

Le résultat est alors 180 625 . (Notez que cela empêchera efficacement les fonctions de toucher le droit et/ou le bas du sous-châssis.)

Maintenant, bien sûr, la question: ont-ils fait une erreur dans leur mise en œuvre? Est-il judicieux de considérer des entités avec une surface de zéro? Ou est-ce que je le vois dans le mauvais sens?

78
Paul Lammertsma

En y regardant de plus près, votre code me semble correct; ce qui fait que l'on peut se demander si les auteurs originaux avaient un bug off-by-one. Je suppose que quelqu'un devrait regarder comment OpenCV l'implémente!

Néanmoins, une suggestion pour faciliter la compréhension consiste à inverser l'ordre des boucles for en parcourant d'abord toutes les tailles, puis en bouclant sur les emplacements possibles en fonction de la taille:

#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;
}

avec les mêmes résultats que le précédent 162336


Pour le vérifier, j'ai testé le cas d'une fenêtre 4x4 et vérifié manuellement tous les cas (facile à compter car les formes 1x2/2x1 et 1x3/3x1 sont les mêmes à seulement 90 degrés de rotation):

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
38
Amro

tout. Il y a encore une certaine confusion dans les journaux de Viola et Jones.

Dans leur article CVPR'01, il est clairement indiqué que

"Plus précisément, nous utilisons trois types de fonctionnalités. La valeur d'une fonction à deux rectangles est la différence entre la somme des pixels dans deux régions rectangulaires. Les régions ont la même taille et la même forme et sont adjacentes horizontalement ou verticalement (voir la figure 1). Une fonction à trois rectangles calcule la somme dans deux rectangles extérieurs soustraits de la somme dans un rectangle central. Enfin une fonction à quatre rectangles ".

Dans le document IJCV'04, exactement la même chose est dite. Donc, au total, 4 fonctionnalités . Mais étrangement, ils ont déclaré cette fois que l'ensemble complet de fonctionnalités est 45396! Cela ne semble pas être la version finale, je suppose que des contraintes supplémentaires y ont été introduites, telles que min_width, min_height, le rapport largeur/hauteur et même la position.

Notez que les deux articles sont téléchargeables sur sa page Web .

8

N'ayant pas lu l'intégralité du document, le libellé de votre citation me dépasse

Étant donné que la résolution de base du détecteur est de 24x24, l'ensemble exhaustif des caractéristiques de rectangle est assez grand, plus de 180 000. Notez que contrairement à la base de Haar, l'ensemble des fonctions rectangulaires est trop complet.

"L'ensemble des fonctions rectangulaires est trop complet" "Ensemble exhaustif"

cela me semble être un ensemble, où je m'attends à ce que le rédacteur papier donne une explication de la façon dont il réduit l'espace de recherche à un ensemble plus efficace, par exemple, en se débarrassant des cas triviaux tels que les rectangles avec zéro surface.

modifier: ou en utilisant une sorte d'algorithme d'apprentissage automatique, comme le suggère le résumé. L'ensemble exhaustif implique toutes les possibilités, pas seulement celles "raisonnables".

3
Breton

Il n'y a aucune garantie que tout auteur d'un article soit correct dans toutes ses hypothèses et conclusions. Si vous pensez que l'hypothèse n ° 4 est valide, alors gardez cette hypothèse et essayez votre théorie. Vous pouvez avoir plus de succès que les auteurs originaux.

2
Michael Dillon

Assez bonne observation, mais ils pourraient implicitement mettre à zéro le cadre 24x24, ou "déborder" et commencer à utiliser les premiers pixels lorsqu'il sort des limites, comme dans les décalages de rotation, ou comme Breton l'a dit, ils pourraient considérer certaines fonctionnalités comme des "fonctionnalités triviales" puis jetez-les avec l'AdaBoost.

De plus, j'ai écrit Python et les versions Matlab de votre code afin que je puisse tester le code moi-même (plus facile à déboguer et à suivre pour moi) et donc je les poste ici si quelqu'un les trouve utiles à un moment donné.

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)