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é.
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:
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?
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
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 .
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".
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.
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)