Die Gesichtserkennung von Viola-Jones beansprucht 180.000 Funktionen

83

Ich habe eine Anpassung des Gesichtserkennungsalgorithmus von Viola-Jones implementiert . Die Technik beruht darauf, dass ein Teilrahmen mit 24 x 24 Pixeln in einem Bild platziert wird und anschließend rechteckige Merkmale an jeder Position mit jeder möglichen Größe darin platziert werden.

Diese Features können aus zwei, drei oder vier Rechtecken bestehen. Das folgende Beispiel wird vorgestellt.

Rechteckfunktionen

Sie behaupten, dass der vollständige Satz mehr als 180.000 beträgt (Abschnitt 2):

Angesichts der Grundauflösung des Detektors von 24 x 24 ist der erschöpfende Satz von Rechteckmerkmalen mit über 180.000 recht groß. Beachten Sie, dass im Gegensatz zur Haar-Basis der Satz von Rechteckmerkmalen übervollständig ist.

Die folgenden Aussagen werden in dem Papier nicht explizit angegeben, daher handelt es sich um Annahmen meinerseits:

  1. Es gibt nur 2 Zwei-Rechteck-Features, 2 Drei-Rechteck-Features und 1 Vier-Rechteck-Feature. Die Logik dahinter ist, dass wir den Unterschied zwischen den hervorgehobenen Rechtecken beobachten, nicht explizit die Farbe oder Luminanz oder irgendetwas in dieser Art.
  2. Wir können den Feature-Typ A nicht als 1x1-Pixelblock definieren. Es muss mindestens 1x2 Pixel betragen. Außerdem muss Typ D mindestens 2 x 2 Pixel groß sein, und diese Regel gilt entsprechend für die anderen Merkmale.
  3. Wir können den Merkmalstyp A nicht als 1x3-Pixelblock definieren, da das mittlere Pixel nicht partitioniert werden kann und das Subtrahieren von sich selbst mit einem 1x2-Pixelblock identisch ist. Dieser Feature-Typ ist nur für gerade Breiten definiert. Außerdem muss die Breite des Merkmalstyps C durch 3 teilbar sein, und diese Regel gilt entsprechend für die anderen Merkmale.
  4. Wir können kein Feature mit einer Breite und / oder Höhe von 0 definieren. Daher iterieren wir x und y bis 24 abzüglich der Größe des Features.

Basierend auf diesen Annahmen habe ich die erschöpfende Menge gezählt:

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

Das Ergebnis ist 162.336 .

Der einzige Weg, den ich gefunden habe, um die "über 180.000", von denen Viola & Jones sprechen, zu approximieren, besteht darin, die Annahme Nr. 4 fallen zu lassen und Fehler in den Code einzuführen. Dies beinhaltet das Ändern von vier Zeilen in:

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

Das Ergebnis ist dann 180.625 . (Beachten Sie, dass dadurch effektiv verhindert wird, dass die Features jemals die rechte und / oder untere Seite des Hilfsrahmens berühren.)

Nun natürlich die Frage: Haben sie einen Fehler bei ihrer Implementierung gemacht? Ist es sinnvoll, Features mit einer Oberfläche von Null zu berücksichtigen? Oder sehe ich das falsch?

Paul Lammertsma
quelle
Warum erhalte ich count = 114829, wenn ich Ihren Code ausführe?
Niki
Warum beginnen Ihre x / y-Schleifen bei 1? Ich gehe davon aus, dass x / y die obere linke Koordinate des Feature-Rechtecks ​​ist. Sollte x / y dann nicht bei 0/0 beginnen?
Niki
Abgesehen davon, ob es bei 0 oder 1 beginnt, hat das Ende bei x < sizemit der Annahme Nr. 4 zu tun: Ich möchte, dass das Feature innerhalb des Subframes bleibt, aber eine Dimension von mindestens 1x1 hat. Nun, vielleicht ist dies auch eine Annahme, ob sich die Dimension des Features nicht außerhalb des Hilfsrahmens erstrecken soll.
Paul Lammertsma
Wenn ich x bei 0 starten würde, müsste es ebenfalls laufen x < size - 1, sodass es keinen Gewinn gibt.
Paul Lammertsma
Ich habe zig für Schleifen gemacht. das scheint mir falsch zu sein. <Größe würde verhindern, dass x jemals 24 wird. Wenn Sie bei 0 beginnen, erhalten Sie 0 ... 23. Bei einer Abmessung von 1 Pixel Breite verlässt das Rechteck niemals den Rahmen.
Bretonischer

Antworten:

40

Bei näherer Betrachtung sieht Ihr Code für mich korrekt aus. was einen wundern lässt, ob die ursprünglichen Autoren einen Fehler nach dem anderen hatten. Ich denke, jemand sollte sich ansehen, wie OpenCV es implementiert!

Ein Vorschlag, der das Verständnis erleichtert, besteht darin, die Reihenfolge der for- Schleifen umzudrehen, indem zuerst alle Größen und dann die möglichen Positionen in Anbetracht der Größe durchlaufen werden:

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

mit den gleichen Ergebnissen wie die vorherigen 162336


Um dies zu überprüfen, habe ich den Fall eines 4x4-Fensters getestet und alle Fälle manuell überprüft (einfach zu zählen, da die Formen 1x2 / 2x1 und 1x3 / 3x1 nur um 90 Grad gedreht gleich sind):

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
Amro
quelle
Überzeugend. So überzeugend, dass ich mir ziemlich sicher bin, dass wir Recht haben. Ich habe eine E-Mail an den Autor gesendet, um festzustellen, ob ich in meiner Argumentation einen grundlegenden Fehler gemacht habe. Wir werden sehen, ob ein Mann, der beschäftigt ist, Zeit hat zu antworten.
Paul Lammertsma
Denken Sie daran, dass dieses Ding seit ein paar Jahren nicht mehr erhältlich ist und seitdem viele Verbesserungen vorgenommen wurden
Amro
24
Das Originalpapier, in dem die 180.000 angegeben wurden, stammt aus dem Verfahren für die Konferenz 2001 über Computer Vision und Mustererkennung. In einem überarbeiteten Papier, das 2003 angenommen und 2004 im International Journal of Computer Vision veröffentlicht wurde, heißt es auf S. 22. 139 (Ende von Abschnitt 2): "Der erschöpfende Satz von Rechtecken ist ziemlich groß, 160.000". Sieht so aus, als hätten wir recht gehabt!
Paul Lammertsma
3
Super, danke für das Update. Für Interessierte fand ich einen Link zum IJCV'04-Artikel: lear.inrialpes.fr/people/triggs/student/vj/viola-ijcv04.pdf
Amro
Ja das ist es. 160k, nicht 180k.
Paul Lammertsma
9

alles. Es gibt immer noch einige Verwirrung in den Papieren von Viola und Jones.

In ihrem CVPR'01-Papier wird klargestellt, dass

"Insbesondere verwenden wir drei Arten von Merkmalen. Der Wert eines Merkmals mit zwei Rechtecken ist die Differenz zwischen der Summe der Pixel in zwei rechteckigen Bereichen. Die Bereiche haben dieselbe Größe und Form und sind horizontal oder vertikal benachbart (siehe Abbildung) 1) Ein Drei-Rechteck-Merkmal berechnet die Summe innerhalb von zwei äußeren Rechtecken, die von der Summe in einem mittleren Rechteck abgezogen werden. Schließlich ein Vier-Rechteck-Merkmal ".

In der IJCV'04-Zeitung wird genau dasselbe gesagt. Also insgesamt 4 Features . Aber seltsamerweise gaben sie diesmal an, dass der umfassende Funktionsumfang 45396 beträgt! Dies scheint nicht die endgültige Version zu sein. Hier wurden vermutlich einige zusätzliche Einschränkungen eingeführt, wie z. B. min_width, min_height, width / height ratio und sogar position.

Beachten Sie, dass beide Artikel auf seiner Webseite heruntergeladen werden können .

Laoma aus Singapur
quelle
3

Nachdem ich nicht die ganze Zeitung gelesen habe, fällt mir der Wortlaut Ihres Zitats auf

Angesichts der Grundauflösung des Detektors von 24 x 24 ist der erschöpfende Satz von Rechteckmerkmalen mit über 180.000 recht groß. Beachten Sie, dass im Gegensatz zur Haar-Basis der Satz von Rechteckmerkmalen übervollständig ist.

"Der Satz von Rechteckmerkmalen ist übervollständig" "Vollständiger Satz"

Es klingt für mich wie ein Setup, bei dem ich erwarte, dass der Papierschreiber eine Erklärung dafür abgibt, wie er den Suchraum auf ein effektiveres Set reduziert, indem er beispielsweise triviale Fälle wie Rechtecke mit Null beseitigt Oberfläche.

edit: oder mit einer Art maschinellem Lernalgorithmus, wie die Zusammenfassung andeutet. Vollständiges Set beinhaltet alle Möglichkeiten, nicht nur "vernünftige".

Bretonisch
quelle
Ich sollte die Fußnote nach "übervollständig" einfügen: "Eine vollständige Basis hat keine lineare Abhängigkeit zwischen Basiselementen und die gleiche Anzahl von Elementen wie der Bildraum, in diesem Fall 576. Der vollständige Satz von 180.000.000 Merkmalen ist um ein Vielfaches über- Komplett." Sie entfernen Klassifikatoren ohne Oberfläche nicht explizit. Sie verwenden AdaBoost, um festzustellen, dass "eine sehr kleine Anzahl dieser Funktionen zu einem effektiven Klassifikator kombiniert werden kann". Ok, die Zero-Surface-Features werden sofort gelöscht, aber warum sollten Sie sie überhaupt in Betracht ziehen?
Paul Lammertsma
Nun, es klingt wie die Argumentation von jemandem, der sich wirklich für die Mengenlehre interessiert.
Breton
Ich stimme zu, das erschöpfende Set würde alle Möglichkeiten implizieren. Beachten Sie jedoch , dass sich die Funktion um 1 Pixel außerhalb des Hilfsrahmens erstreckt , wenn Sie für x und die Breite <= x 1 bis 24 nehmen !
Paul Lammertsma
Sind Sie sicher, dass Ihr Code nicht mit "off by one" -Fehlern durchsetzt ist? Ich habe es mir nur genauer angesehen, und Sie haben sicher eine lustige Art, eine for-Schleife zu schreiben.
Breton
Ich sollte das qualifizieren - ich habe nur ein bisschen darüber nachgedacht, und wenn Sie ein Rechteck haben, das 1 Pixel hoch, 2 Pixel hoch, 3 Pixel hoch, bis zu 24 Pixel hoch ist, haben Sie 24 Arten von Rechtecken die in einen 24 Pixel hohen Subframe passen. Welche Überhänge?
Breton
2

Es gibt keine Garantie dafür, dass ein Autor eines Papiers in all seinen Annahmen und Ergebnissen korrekt ist. Wenn Sie der Meinung sind, dass die Annahme Nr. 4 gültig ist, behalten Sie diese Annahme bei und probieren Sie Ihre Theorie aus. Sie sind möglicherweise erfolgreicher als die ursprünglichen Autoren.

Michael Dillon
quelle
Experimente zeigen, dass es scheinbar genau gleich funktioniert. Ich glaube, AdaBoost lässt diese zusätzlichen Zero-Surface-Features im ersten Zyklus einfach fallen, aber ich habe mich nicht wirklich damit befasst.
Paul Lammertsma
Viola und Jones sind sehr große Namen in der Computer Vision. In der Tat wird dieses spezielle Papier als wegweisend angesehen. Jeder macht Fehler, aber dieser spezielle Algorithmus funktioniert nachweislich sehr gut.
Dima
1
Auf jeden Fall, und ich bezweifle ihre Methode überhaupt nicht. Es ist effizient und funktioniert sehr gut! Die Theorie ist solide, aber ich glaube, sie haben ihren Detektor möglicherweise fälschlicherweise um ein Pixel verkürzt und unnötige Nulloberflächenmerkmale hinzugefügt. Wenn nicht, fordere ich Sie auf, die 180k-Funktionen zu demonstrieren!
Paul Lammertsma
Tatsache ist, dass jeder Mensch ist. Jeder macht Fehler. Wenn ein großer Name Fehler macht, liegen sie oft über Generationen hinweg verborgen, weil die Menschen Angst haben, die empfangene Weisheit in Frage zu stellen. Aber wahre Wissenschaft folgt der wissenschaftlichen Methode und verehrt niemanden, egal wie groß ihr Name ist. Wenn es Wissenschaft ist, können bloße Sterbliche sich anstrengen, verstehen, wie es funktioniert, und es an ihre Umstände anpassen.
Michael Dillon
Wir werden sehen; Ich habe eine E-Mail an den Autor gesendet.
Paul Lammertsma
1

Ziemlich gute Beobachtung, aber sie könnten den 24x24-Frame implizit auf Null setzen oder "überlaufen" und die ersten Pixel verwenden, wenn sie außerhalb der Grenzen liegen, wie bei Rotationsverschiebungen oder wie Breton sagte, sie könnten einige Features als "triviale Features" betrachten. und verwerfen Sie sie dann mit dem AdaBoost.

Außerdem habe ich Python- und Matlab-Versionen Ihres Codes geschrieben, damit ich den Code selbst testen kann (einfacher zu debuggen und für mich zu befolgen), und ich poste sie hier, wenn jemand sie irgendwann nützlich findet.

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)
Бојан Матовски
quelle
Warum verwenden Sie 5 Funktionen, in der Hauptfrage sind nur 4 aufgeführt. Aber trotzdem danke für die Python-Version.
Kasparov92
0

In ihrer ursprünglichen Arbeit von 2001 geben sie nur an, dass drei Arten von Merkmalen verwendet werden:

Wir verwenden drei Arten von Funktionen

Ebenfalls

Die Regionen haben die gleiche Größe und Form

Da jede Art zwei Ausrichtungen hat, ist anzunehmen, dass sie insgesamt 6 Merkmale verwenden (zumindest für die Berechnung der Gesamtzahl der Merkmale): 2 Merkmale mit zwei Rechtecken, 2 Merkmale mit drei Rechtecken und 2 Merkmale mit vier Rechtecken. Mit dieser Annahme gibt es tatsächlich über 180.000 Merkmale:

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

Wenn Sie einen Feature-Typ mit vier Rechtecken löschen (was in der späteren Veröffentlichung der Fall zu sein scheint), beträgt die Gesamtzahl der Features 162.336.

Andreas K.
quelle