Il rilevamento del volto di Viola-Jones rivendica 180.000 funzionalità


85

Sto implementando un adattamento dell'algoritmo di rilevamento dei volti di Viola-Jones . La tecnica si basa sul posizionamento di un subframe di 24x24 pixel all'interno di un'immagine e successivamente sul posizionamento di elementi rettangolari al suo interno in ogni posizione con ogni dimensione possibile.

Queste caratteristiche possono essere costituite da due, tre o quattro rettangoli. Viene presentato il seguente esempio.

Caratteristiche rettangolari

Affermano che il set completo è più di 180k (sezione 2):

Dato che la risoluzione di base del rilevatore è 24x24, l'insieme esauriente di funzioni rettangolari è piuttosto ampio, oltre 180.000. Si noti che a differenza della base Haar, l'insieme di elementi rettangolari è troppo completo.

Le seguenti affermazioni non sono esplicitamente dichiarate nel documento, quindi sono ipotesi da parte mia:

  1. Ci sono solo 2 elementi a due rettangoli, 2 elementi a tre rettangoli e 1 elementi a quattro rettangoli. La logica alla base di questo è che stiamo osservando la differenza tra i rettangoli evidenziati, non esplicitamente il colore o la luminanza o qualcosa del genere.
  2. Non possiamo definire il tipo di elemento A come un blocco di pixel 1x1; deve essere almeno 1x2 pixel. Inoltre, il tipo D deve essere almeno 2x2 pixel e questa regola vale di conseguenza per le altre funzionalità.
  3. Non possiamo definire il tipo di elemento A come un blocco di 1x3 pixel poiché il pixel centrale non può essere partizionato e sottraendolo da se stesso è identico a un blocco di 1x2 pixel; questo tipo di caratteristica è definito solo per larghezze pari. Inoltre, la larghezza dell'elemento di tipo C deve essere divisibile per 3 e questa regola vale di conseguenza per gli altri elementi.
  4. Non possiamo definire una caratteristica di larghezza e / o altezza di 0. Pertanto, iterare x di e y a 24 meno la dimensione della funzione.

Sulla base di questi presupposti, ho contato il set completo:

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

Il risultato è 162.336 .

L'unico modo che ho trovato per approssimare gli "oltre 180.000" di cui parlano Viola & Jones è stato quello di eliminare l'ipotesi n. 4 e di introdurre bug nel codice. Ciò comporta la modifica di quattro righe rispettivamente in:

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

Il risultato è quindi 180.625 . (Notare che questo impedirà efficacemente alle caratteristiche di toccare mai la destra e / o la parte inferiore del telaio ausiliario.)

Ora ovviamente la domanda: hanno commesso un errore nella loro implementazione? Ha senso considerare elementi con una superficie pari a zero? O la vedo nel modo sbagliato?


Perché ottengo count = 114829 quando eseguo il codice?
Niki

Perché i tuoi cicli x / y iniziano da 1? Presumo che x / y sia la coordinata in alto a sinistra del rettangolo della caratteristica. Allora x / y non dovrebbe iniziare da 0/0?
Niki

A prescindere dal fatto che inizi da 0 o 1, finisce a x < sizeha a che fare con l'ipotesi n. 4: voglio che la caratteristica rimanga all'interno del sottoframe, ma abbia una dimensione di almeno 1x1. Quanto al fatto che la dimensione della caratteristica non debba estendersi al di fuori del telaio ausiliario, beh, forse anche questo è un presupposto.
Paul Lammertsma

Allo stesso modo, se iniziassi x da 0, dovrebbe correre a x < size - 1, quindi non c'è guadagno.
Paul Lammertsma

Ho fatto un'infinità di loop. questo mi sembra sbagliato. <size eviterebbe che x diventi mai 24, iniziando da 0 otterrai 0 ... 23, Con una dimensione di 1 pixel di larghezza, il rettangolo non lascerà mai il fotogramma.
Breton

Risposte:


41

Ad uno sguardo più attento, il tuo codice mi sembra corretto; il che fa pensare se gli autori originali avessero un bug off-by-one. Immagino che qualcuno dovrebbe guardare a come OpenCV lo implementa!

Tuttavia, un suggerimento per renderlo più facile da capire è di capovolgere l'ordine dei cicli for andando prima su tutte le dimensioni, quindi ripetendo le possibili posizioni date le dimensioni:

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

con gli stessi risultati del precedente 162336


Per verificarlo, ho testato il caso di una finestra 4x4 e controllato manualmente tutti i casi (facile da contare poiché le forme 1x2 / 2x1 e 1x3 / 3x1 sono le stesse ruotate di soli 90 gradi):

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

Convincente. Così convincente che sono abbastanza sicuro che abbiamo ragione. Ho inviato una e-mail all'autore per vedere se ho commesso qualche errore fondamentale nel mio ragionamento. Vedremo se un ragazzo così impegnato ha il tempo di rispondere.
Paul Lammertsma

tieni presente che questa cosa è uscita da un paio d'anni e da allora sono stati apportati molti miglioramenti
Amro

26
Il documento originale in cui è stato dichiarato il 180k proviene dagli atti della Conferenza del 2001 sulla visione artificiale e il riconoscimento dei modelli. Un documento revisionato, accettato nel 2003 e pubblicato sull'International Journal of Computer Vision nel 2004, afferma a p. 139 (fine della sezione 2): "l'insieme esaustivo di rettangoli è abbastanza grande, 160.000". Sembra che avessimo ragione!
Paul Lammertsma,

4
Fantastico, grazie per l'aggiornamento. Per chi fosse interessato, ho trovato un collegamento al documento IJCV'04
Amro

Sì è quello. 160k, non 180k.
Paul Lammertsma

9

tutti. C'è ancora un po 'di confusione nelle carte di Viola e Jones.

Nel loro documento CVPR'01 si afferma chiaramente che

"Più specificamente, utilizziamo tre tipi di elementi. Il valore di un elemento a due rettangoli è la differenza tra la somma dei pixel all'interno di due regioni rettangolari. Le regioni hanno la stessa dimensione e forma e sono adiacenti orizzontalmente o verticalmente (vedi Figura 1). Una feature a tre rettangoli calcola la somma all'interno di due rettangoli esterni sottratta dalla somma in un rettangolo centrale. Infine una feature a quattro rettangoli ".

Nel documento IJCV'04 si dice esattamente la stessa cosa. Quindi, in tutto, 4 funzionalità . Ma stranamente, questa volta hanno affermato che il set completo di funzionalità è 45396! Questa non sembra essere la versione finale.Qui immagino che siano stati introdotti alcuni vincoli aggiuntivi, come min_width, min_height, rapporto larghezza / altezza e persino posizione.

Nota che entrambi i documenti sono scaricabili sulla sua pagina web .


3

Non avendo letto l'intero giornale, la formulazione della tua citazione mi colpisce

Dato che la risoluzione di base del rilevatore è 24x24, l'insieme esauriente di funzioni rettangolari è piuttosto ampio, oltre 180.000. Si noti che a differenza della base Haar, l'insieme di elementi rettangolari è troppo completo.

"L'insieme di elementi rettangolari è troppo completo" "Insieme esaustivo"

mi suona come una configurazione, in cui mi aspetto che l'autore della carta segua una spiegazione su come abbattere lo spazio di ricerca in un insieme più efficace, ad esempio eliminando casi banali come i rettangoli con zero superficie.

modifica: o utilizzando una sorta di algoritmo di apprendimento automatico, come suggerisce l'abstract. Un insieme esaustivo implica tutte le possibilità, non solo quelle "ragionevoli".


Dovrei includere la nota a piè di pagina dopo "overcomplete": "Una base completa non ha una dipendenza lineare tra gli elementi di base e ha lo stesso numero di elementi dello spazio dell'immagine, in questo caso 576. Il set completo di 180.000.000 caratteristiche è molte volte completare." Non eliminano esplicitamente i classificatori privi di superficie, usano AdaBoost per determinare che "un numero molto piccolo di queste caratteristiche può essere combinato per formare un classificatore efficace". Ok, quindi le caratteristiche a superficie zero verranno eliminate immediatamente, ma perché considerarle in primo luogo?
Paul Lammertsma

Beh, suona come il ragionamento di qualcuno che si interessa davvero alla teoria degli insiemi.
Bretone

Sono d'accordo, l'insieme esauriente implicherebbe tutte le possibilità. Ma considera che se prendi da 1 a 24 per x e larghezza <= x, la funzione si estenderà di 1 pixel al di fuori del subframe!
Paul Lammertsma

Sei sicuro che il tuo codice non sia pieno di bug "off by one"? Ho appena dato un'occhiata più da vicino e sicuramente hai un modo divertente di scrivere un ciclo for.
Bretone

Dovrei qualificarlo: ci ho pensato un po 'e se hai un rettangolo alto 1 pixel, alto 2 pixel, alto 3 pixel, alto fino a 24 pixel, hai 24 tipi di rettangolo, tutti che si inseriscono in un subframe alto 24 pixel. Quali strapiombi?
Bretone

2

Non vi è alcuna garanzia che qualsiasi autore di qualsiasi articolo sia corretto in tutte le sue ipotesi e risultati. Se pensi che l'ipotesi n. 4 sia valida, mantienila e prova la tua teoria. Potresti avere più successo degli autori originali.


La sperimentazione mostra che funziona apparentemente esattamente lo stesso. Credo che AdaBoost abbandoni semplicemente quelle funzionalità aggiuntive a superficie zero nel primo ciclo, ma in realtà non l'ho esaminato.
Paul Lammertsma

Viola e Jones sono nomi molto grandi nella visione artificiale. In effetti, questo particolare articolo è considerato seminale. Tutti commettono errori, ma questo particolare algoritmo ha dimostrato di funzionare molto bene.
Dima

1
Sicuramente, e non dubito affatto del loro metodo. È efficiente e funziona molto bene! La teoria è valida, ma credo che potrebbero aver ritagliato per errore il loro rilevatore di un pixel e includere inutili funzioni a superficie zero. In caso contrario, ti sfido a dimostrare le funzionalità 180k!
Paul Lammertsma

Il fatto è che tutti sono umani. Tutti fanno degli errori. Quando un grande nome commette errori, spesso rimangono nascosti per generazioni perché le persone hanno paura di mettere in discussione la saggezza ricevuta. Ma la vera scienza, segue il metodo scientifico e non adora nessuno, non importa quanto grande sia il loro nome. Se è scienza, allora i comuni mortali possono impegnarsi, capire come funziona e adattarlo alle loro circostanze.
Michael Dillon

Vedremo; Ho inviato una e-mail all'autore.
Paul Lammertsma

1

Osservazione abbastanza buona, ma potrebbero implicitamente azzerare il fotogramma 24x24 o "overflow" e iniziare a utilizzare i primi pixel quando esce dai limiti, come negli spostamenti rotazionali, o come ha detto Breton, potrebbero considerare alcune caratteristiche come "caratteristiche banali" e poi scartali con AdaBoost.

Inoltre, ho scritto versioni Python e Matlab del tuo codice in modo da poter testare il codice da solo (più facile da eseguire il debug e da seguire per me) e quindi le pubblico qui se qualcuno le trova utili prima o poi.

Pitone:

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)

Perché usi 5 funzionalità, solo 4 sono pubblicate nella domanda principale. Ma grazie comunque per la versione in python.
Kasparov92

0

Nel loro documento originale del 2001 affermano solo che vengono utilizzati tre tipi di funzionalità:

usiamo tre tipi di funzionalità

Anche

Le regioni hanno la stessa dimensione e forma

Poiché ogni tipo ha due orientamenti, è ragionevole presumere che utilizzino 6 funzioni in totale (almeno per il calcolo del numero totale di elementi): 2 elementi a due rettangoli, 2 elementi a tre rettangoli e 2 elementi a quattro rettangoli. Con questo presupposto ci sono infatti oltre 180.000 funzionalità:

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

Se si elimina un tipo di elementi a quattro rettangoli (che sembra essere il caso nella loro pubblicazione successiva), il numero totale di elementi è 162.336.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.