Come rilevare angoli in immagini binarie con OpenGL?


13

Ho immagini binarie 160x120 come:

immagine originale

Vorrei rilevare gli angoli di quelle macchie bianche. In precedenza sono stati chiusi dalla morfologia matematica, quindi non dovrebbero esserci angoli interni. In questo caso specifico, vorrei 16 angoli, come:

esempio di rilevamento degli angoli

Il mio primo tentativo è stato quello di utilizzare alcune funzioni OpenCV come goodFeaturesToTrack o FAST ma sono particolarmente lente (inoltre FAST è molto instabile). La mia idea sarebbe quella di fare un tale calcolo sulla GPU, dal momento che la mia immagine sorgente ne deriva. Ho cercato idee sul web su come scrivere tali shader (sto usando OpenGL ES 2.0), ma non ho trovato nulla di concreto. Hai idea di come potrei iniziare un simile algoritmo?


2
VELOCE è lento? :)
endolith

1
si, divertente vero? infatti, è più veloce degli algoritmi precedenti come SURF o SIFT, ma è meno preciso, abbastanza instabile da un'immagine all'altra e non è ancora abbastanza veloce da essere eseguito sulla CPU
Stéphane Péchard,

Quanto è importante rilevarli accuratamente su ogni frame? Con quale velocità si muovono i rettangoli? È corretto rilevare gli angoli sulla maggior parte dei frame e interpolarli sui frame in cui manca l'algoritmo?
justis,

@justis bene, il modo in cui lo faccio ora (attraverso l'uso delle funzioni cvFindContours () e cvApproxPoly () di OpenCV non è molto stabile nel tempo, quindi filtro il risultato con un filtro passa-basso, introducendo lag. Pensi che posso ottenere un risultato più stabile con un'interpolazione?
Stéphane Péchard,

Risposte:


3

Su quali dimensioni delle immagini stai operando? Con quale frame rate? Su quale hardware? VELOCE è bello, erm, veloce nella mia esperienza.

Ho anche visto FAST usato come rilevatore di ROI con buone Funzionalità ToTrack eseguito sulle ROI identificate per fornire una migliore stabilità senza eseguire la penalità di gFTT sull'intera immagine.

Il rilevatore di angoli "Harris" è anche potenzialmente molto veloce in quanto è costituito da operazioni molto semplici (ad esempio senza sqrt () per pixel!) - non è stabile come gFTT, ma probabilmente più di FAST.

(In termini di implementazione della GPU, Google gpu cornersembra presentare un sacco di collegamenti, ma non ho idea di quanto possano essere adatti - tendo a implementare in FPGA.)


Le mie immagini sono 160x120, presumibilmente a 30fps, su un iPhone, ma ovviamente l'applicazione ha molto altro da fare :-) Ho visto un'app che implementa FAST abbastanza rapidamente su un dispositivo del genere, ma era solo una demo farlo ... Ecco perché sto cercando soluzioni basate su GPU.
Stéphane Péchard,

15

Mi è capitato di implementare qualcosa di simile su OpenGL ES 2.0 usando il rilevamento degli angoli di Harris, e mentre non ho ancora finito, ho pensato di condividere l'implementazione basata su shader che ho finora. L'ho fatto come parte di un framework open source basato su iOS , quindi puoi controllare il codice se sei curioso di sapere come funziona un determinato passaggio.

Per fare ciò, utilizzo i seguenti passi:

  • Ridurre l'immagine ai valori di luminanza usando un prodotto punto dei valori RGB con il vettore (0,2125, 0,7154, 0,0721).
  • Calcola le derivate X e Y sottraendo i valori del canale rosso dai pixel sinistro e destro e sopra e sotto il pixel corrente. Quindi memorizzo il derivato x al quadrato nel canale rosso, il derivato Y al quadrato nel canale verde e il prodotto dei derivati ​​X e Y nel canale blu. Lo shader di frammenti per questo assomiglia al seguente:

    precision highp float;
    
    varying vec2 textureCoordinate;
    varying vec2 leftTextureCoordinate;
    varying vec2 rightTextureCoordinate;
    
    varying vec2 topTextureCoordinate; 
    varying vec2 bottomTextureCoordinate;
    
    uniform sampler2D inputImageTexture;
    
    void main()
    {
     float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;
     float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;
     float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;
     float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;
    
     float verticalDerivative = abs(-topIntensity + bottomIntensity);
     float horizontalDerivative = abs(-leftIntensity + rightIntensity);
    
     gl_FragColor = vec4(horizontalDerivative * horizontalDerivative, verticalDerivative * verticalDerivative, verticalDerivative * horizontalDerivative, 1.0);
    }
    

    dove le variazioni sono solo le coordinate della trama di offset in ciascuna direzione. Li precalcolo nello shader di vertici per eliminare le letture di texture dipendenti, che sono notoriamente lente su queste GPU mobili.

  • Applica una sfocatura gaussiana a questa immagine derivata. Ho usato una sfocatura orizzontale e verticale separata e ho approfittato del filtro delle trame hardware per eseguire una sfocatura a nove colpi con solo cinque letture delle trame su ogni passaggio. Descrivo questo shader in questa risposta Stack Overflow .

  • Eseguire il calcolo del rilevamento dell'angolo Harris effettivo utilizzando i valori della derivata di input offuscata. In questo caso, sto effettivamente utilizzando il calcolo descritto da Alison Noble nel suo dottorato di ricerca. tesi di laurea "Descrizioni delle superfici delle immagini". Lo shader che gestisce questo assomiglia al seguente:

    varying highp vec2 textureCoordinate;
    
    uniform sampler2D inputImageTexture;
    
    const mediump float harrisConstant = 0.04;
    
    void main()
    {
     mediump vec3 derivativeElements = texture2D(inputImageTexture, textureCoordinate).rgb;
    
     mediump float derivativeSum = derivativeElements.x + derivativeElements.y;
    
     // This is the Noble variant on the Harris detector, from 
     // Alison Noble, "Descriptions of Image Surfaces", PhD thesis, Department of Engineering Science, Oxford University 1989, p45.     
     mediump float harrisIntensity = (derivativeElements.x * derivativeElements.y - (derivativeElements.z * derivativeElements.z)) / (derivativeSum);
    
     // Original Harris detector
     //     highp float harrisIntensity = derivativeElements.x * derivativeElements.y - (derivativeElements.z * derivativeElements.z) - harrisConstant * derivativeSum * derivativeSum;
    
     gl_FragColor = vec4(vec3(harrisIntensity * 10.0), 1.0);
    }
    
  • Esegui la soppressione non massima locale e applica una soglia per evidenziare i pixel che passano. Uso il seguente shader di frammento per campionare gli otto pixel nelle vicinanze di un pixel centrale e identificare se è il massimo in quel raggruppamento:

    uniform sampler2D inputImageTexture;
    
    varying highp vec2 textureCoordinate;
    varying highp vec2 leftTextureCoordinate;
    varying highp vec2 rightTextureCoordinate;
    
    varying highp vec2 topTextureCoordinate;
    varying highp vec2 topLeftTextureCoordinate;
    varying highp vec2 topRightTextureCoordinate;
    
    varying highp vec2 bottomTextureCoordinate;
    varying highp vec2 bottomLeftTextureCoordinate;
    varying highp vec2 bottomRightTextureCoordinate;
    
    void main()
    {
        lowp float bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).r;
        lowp float bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;
        lowp float bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;
        lowp vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
        lowp float leftColor = texture2D(inputImageTexture, leftTextureCoordinate).r;
        lowp float rightColor = texture2D(inputImageTexture, rightTextureCoordinate).r;
        lowp float topColor = texture2D(inputImageTexture, topTextureCoordinate).r;
        lowp float topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).r;
        lowp float topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).r;
    
        // Use a tiebreaker for pixels to the left and immediately above this one
        lowp float multiplier = 1.0 - step(centerColor.r, topColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, topLeftColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, leftColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, bottomLeftColor);
    
        lowp float maxValue = max(centerColor.r, bottomColor);
        maxValue = max(maxValue, bottomRightColor);
        maxValue = max(maxValue, rightColor);
        maxValue = max(maxValue, topRightColor);
    
        gl_FragColor = vec4((centerColor.rgb * step(maxValue, centerColor.r) * multiplier), 1.0);
    }
    

Questo processo genera una mappa senza angoli dai tuoi oggetti che assomiglia a questo:

Mappa di cornerness

I seguenti punti sono identificati come angoli in base alla soppressione e alla soglia non massime:

Angoli identificati

Con le soglie appropriate impostate per questo filtro, è in grado di identificare tutti i 16 angoli in questa immagine, anche se tende a posizionare gli angoli di circa un pixel all'interno dei bordi effettivi dell'oggetto.

Su un iPhone 4, questo rilevamento dell'angolo può essere eseguito a 20 FPS su fotogrammi 640x480 di video provenienti dalla fotocamera e un iPhone 4S può facilmente elaborare video di quella dimensione a oltre 60 FPS. Questo dovrebbe essere molto più veloce dell'elaborazione associata alla CPU per un'attività come questa, anche se in questo momento il processo di lettura dei punti è associato alla CPU e un po 'più lento di quanto dovrebbe essere.

Se vuoi vederlo in azione puoi prendere il codice per il mio framework ed eseguire l'esempio FilterShowcase che ne deriva. L'esempio di rilevamento dell'angolo di Harris viene eseguito sul video in diretta dalla fotocamera del dispositivo, anche se, come ho già detto, la lettura dei punti d'angolo si verifica attualmente sulla CPU, il che sta rallentando. Anche per questo sto passando a un processo basato su GPU.


1
Molto bella! Seguo il tuo framework su github, sembra davvero interessante, complimenti!
Stéphane Péchard

Hai un esempio da qualche parte su come riportare effettivamente le coordinate d'angolo nella CPU? Esiste un modo GPU intelligente o richiede una rilettura e quindi il looping sulla CPU attraverso la bitmap restituita alla ricerca di pixel contrassegnati?
Quasimondo

@Quasimondo - Ho lavorato sull'utilizzo dell'istogramma piramidi per l'estrazione dei punti: tevs.eu/files/vmv06.pdf per evitare l'iterazione legata alla CPU sui pixel per il rilevamento degli angoli. Ultimamente sono stato un po 'distratto, quindi non ho ancora finito, ma mi piacerebbe presto.
Brad Larson,

Ciao @BradLarson, so che questo è un thread molto vecchio e grazie per la tua risposta. Ho appena controllato KGPUImageHarrisCornerDetection.m nel framework GPUImage. Per estrarre la posizione dell'angolo dall'immagine, hai usato glReadPixels per leggere l'immagine nel buffer e quindi in loop sul buffer per memorizzare i punti con colotByte> 0 in un array. C'è un modo per farlo tutto in GPU in cui non dobbiamo leggere l'immagine in buffer e loop?
Sahil Bajaj,

1
@SahilBajaj - Una tecnica che ho visto (e non ho ancora avuto il tempo di implementare) è usare l' istogramma piramidi per estrarre rapidamente punti da immagini sparse come questa. Ciò accelererebbe notevolmente questo.
Brad Larson il

3

Rilevatori d'angolo "robusti" come Shi-Tomasi e Moravec sono notoriamente lenti. controllali qui - http://it.wikipedia.org/wiki/Corner_detection Probabilmente FAST è l'unico rilevatore di angoli leggero abbastanza buono. Puoi migliorare FAST eseguendo una soppressione non massima - scegli l'output FAST con il miglior punteggio di "spensieratezza" (ci sono diversi modi intuitivi per calcolarlo, tra cui Shi-Tomasi e Moravec come punteggio di spensieratezza) Puoi anche scegliere tra diversi rilevatori FAST - da FAST-5 a FAST-12 e FAST_ER (l'ultimo è probabilmente troppo grande per i dispositivi mobili) Un altro modo è generare FAST: ottenere il generatore di codice FAST dal sito dell'autore e addestrarlo sul set di immagini probabili. http://www.edwardrosten.com/work/fast.html


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.