Détection de visage de Viola-Jones: 180 000 fonctionnalités

J’ai implémenté une adaptation de l’ algorithme de détection de visage de Viola-Jones . La technique consiste à placer une sous-trame de 24×24 pixels dans une image, puis à placer des éléments rectangulars à l’intérieur de celle-ci dans toutes les positions possibles.

Ces fonctionnalités peuvent être composées de deux, trois ou quatre rectangles. L’exemple suivant est présenté.

Caractéristiques du rectangle

Ils affirment que le jeu exhaustif est supérieur à 180k (section 2):

Étant donné que la résolution de base du détecteur est de 24×24, l’ensemble exhaustif de fonctions rectangle est assez important, plus de 180 000. Notez que contrairement à la base de Haar, l’ensemble des fonctions du rectangle 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 fonctions rectangle, 2 fonctions rectangle et 1 rectangle. 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×1 pixel; il doit au moins être d’au moins 1×2 pixels. De plus, le type D doit avoir au moins 2 x 2 pixels et cette règle correspond aux autres fonctionnalités.
  3. Nous ne pouvons pas définir le type d’entité A comme un bloc de 1×3 pixels car le pixel du milieu ne peut pas être partitionné et le soustraire de lui-même est identique à un bloc de 1×2 pixel; Ce type d’entité n’est défini que pour les largeurs paires. En outre, la largeur du type d’entité C doit être divisible par 3, et cette règle s’applique aux autres entités.
  4. Nous ne pouvons pas définir une entité avec une largeur et / ou une hauteur de 0. Nous itérons donc 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 je me suis approché des «plus de 180 000» de Viola & Jones, c’est de supprimer l’hypothèse n ° 4 et d’introduire des bogues dans le code. Cela implique de changer respectivement quatre lignes 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 effectivement les fonctionnalités de toucher la droite et / ou le bas du sous-cadre.)

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 ayant une surface de zéro? Ou est-ce que je le vois mal?

En regardant de plus près, votre code me semble correct; ce qui fait qu’on se demande si les auteurs originaux ont eu un bug par-en-un. Je suppose que quelqu’un devrait regarder comment OpenCV l’implémente!

Néanmoins, une suggestion pour le rendre plus facile à comprendre consiste à retourner l’ordre des boucles for en parcourant d’abord toutes les tailles, puis en parcourant les emplacements possibles en fonction de la taille:

 #include  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 les précédents 162336


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

 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 

tout. Il y a encore de la confusion dans les papiers de Viola et Jones.

Dans leur document CVPR’01, il est clairement indiqué que

«Plus précisément, nous utilisons trois types d’entités. La valeur d’une entité à deux rectangle est la différence entre la sum des pixels dans deux régions rectangulars. Les régions ont la même taille et la même forme et sont adjacentes horizontalement ou verticalement (voir la figure). 1) Une entité à trois rectangle calcule la sum à l’intérieur de deux rectangles extérieurs soustraits de la sum dans un rectangle central.

Dans le document IJCV’04, exactement la même chose est dite. Donc, en tout, 4 caractéristiques . Mais curieusement, ils ont déclaré cette fois que le jeu de fonctionnalités exhaustif est le 45396! Cela ne semble pas être la version finale.Ici, je suppose que certaines contraintes supplémentaires ont été introduites, telles que min_width, min_height, ratio largeur / hauteur et même position.

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

N’ayant pas lu l’intégralité de l’article, le libellé de votre citation me tient à coeur

Étant donné que la résolution de base du détecteur est de 24×24, l’ensemble exhaustif de fonctions rectangle est assez important, plus de 180 000. Notez que contrairement à la base de Haar, l’ensemble des fonctions du rectangle est trop complet.

“L’ensemble des fonctions du rectangle est trop complet” “Ensemble exhaustif”

Cela me semble être une mise en place, où je m’attends à ce que l’auteur écrive une explication sur la manière dont il ramène l’espace de recherche à un ensemble plus efficace, par exemple en éliminant les cas sortingviaux tels que les rectangles à zéro. surface

edit: ou en utilisant une sorte d’algorithme d’apprentissage automatique, comme l’indique le résumé. L’ensemble exhaustif implique toutes les possibilités, pas seulement celles “raisonnables”.

Il n’y a aucune garantie qu’un auteur de quelque papier que ce soit ait raison dans toutes ses hypothèses et conclusions. Si vous pensez que cette hypothèse n ° 4 est valide, gardez cette hypothèse et testez votre théorie. Vous pouvez avoir plus de succès que les auteurs originaux.

Assez bonne observation, mais ils pourraient implicitement mettre à zéro le cadre 24×24, ou “débordement” et commencer à utiliser les premiers pixels quand ils sont hors limites, comme dans les décalages de rotation, ou puis jetez-les avec l’AdaBoost.

De plus, j’ai écrit les versions Python et Matlab de votre code pour pouvoir tester le code moi-même (plus facile à déboguer et à suivre pour moi) et je les publie ici si quelqu’un les trouve utiles.

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)