OpenGL GLSL - Filtro rilevamento bordi Sobel


9

Per quanto riguarda questo argomento, ho implementato con successo il filtro Rilevamento bordi Sobel in GLSL. Ecco il codice shader frammento del filtro:

#version 330 core
in vec2 TexCoords;
out vec4 color;

uniform sampler2D screenTexture;

mat3 sx = mat3( 
    1.0, 2.0, 1.0, 
    0.0, 0.0, 0.0, 
   -1.0, -2.0, -1.0 
);
mat3 sy = mat3( 
    1.0, 0.0, -1.0, 
    2.0, 0.0, -2.0, 
    1.0, 0.0, -1.0 
);

void main()
{
    vec3 diffuse = texture(screenTexture, TexCoords.st).rgb;
    mat3 I;
    for (int i=0; i<3; i++) {
        for (int j=0; j<3; j++) {
            vec3 sample  = texelFetch(screenTexture, ivec2(gl_FragCoord) + ivec2(i-1,j-1), 0 ).rgb;
            I[i][j] = length(sample); 
    }
}

float gx = dot(sx[0], I[0]) + dot(sx[1], I[1]) + dot(sx[2], I[2]); 
float gy = dot(sy[0], I[0]) + dot(sy[1], I[1]) + dot(sy[2], I[2]);

float g = sqrt(pow(gx, 2.0)+pow(gy, 2.0));
color = vec4(diffuse - vec3(g), 1.0);
} 

Ed ecco il risultato di un cubo con rilevamento dei bordi di Sobel:

Cubo con filtro rilevamento bordi Sobel

Se ingrandisci l'immagine, vedrai che c'è molto "rumore" prodotto da Sobel: ci sono strisce orizzontali grigie su tutta la scena a causa del gradiente blu / bianco. Inoltre, i coni di luce producono uno schema indesiderato sul cubo. Anche i bordi neri a sinistra del cubo sembrano sbiadire a causa del cono di luce nella metà sinistra del cubo.

Quindi ho letto questo articolo in cui si afferma che si dovrebbe prima scala di grigi l'immagine e utilizzare un filtro di sfocatura gaussiana per rendere più evidenti i bordi. Nella parte inferiore dell'articolo, c'è anche il filtro di rilevamento del bordo astuto che sembra produrre risultati migliori.

Ora ho due domande:

  1. I seguenti passaggi sono corretti per produrre i migliori risultati possibili di rilevamento dei bordi:

    • Scala di grigi
    • Sfocatura gaussiana
    • Rilevamento dei bordi di Sobel / Canny
  2. Se sì, come unirei l'immagine originale con l'immagine elaborata? Voglio dire, dopo aver elaborato i passaggi sopra indicati, ottengo un'immagine completamente nera con bordi bianchi o vice verca. Come metterei i bordi sulla mia immagine / trama originale?

Grazie per l'aiuto!


Mi chiedo se potresti ottenere i risultati desiderati utilizzando in qualche modo il flag edge di OpenGL . Sembra che solo i bordi tra i vertici con la bandiera dei bordi siano disegnati in modalità linea. C'è una descrizione qui . (Non l'ho usato da solo o
pubblicherei

Risposte:


9
  1. I risultati migliori dipendono fortemente dal caso d'uso. Dipendono anche dall'effetto che si desidera ottenere. Sobel è solo un filtro di rilevamento dei bordi: i bordi dipenderanno dal segnale di ingresso, scegliendo che il segnale di ingresso dipende da te.

    Qui stai usando l'immagine a colori come input, e il filtro rileva giustamente i bordi sbiaditi nel gradiente blu, mentre i bordi del cubo vengono interrotti dove il suo colore è troppo vicino al colore di sfondo.

    Dato che presumo che anche il tuo programma sia responsabile del disegno del cubo, hai accesso ad altre informazioni che puoi alimentare con il tuo filtro Sobel. Ad esempio la profondità e le normali sono buoni candidati per il rilevamento dei bordi. L'albedo prima dell'illuminazione potrebbe essere usato per. Prova con input diversi e decidi quali utilizzare in base ai risultati ottenuti.

  2. Per quanto riguarda la tua domanda su come combinare le informazioni sui bordi, ti suggerisco di filtrare gun po 'prima di usarle. Quindi puoi usarlo per interpolare tra il colore originale e il colore del bordo desiderato.

    Ad esempio potresti provare qualcosa del genere:

    float g = sqrt(pow(gx, 2.0)+pow(gy, 2.0));

    // Try different values and see what happens
    g = smoothstep(0.4, 0.6, g);

    vec3 edgeColor = vec3(1., 0., 0.2);
    color = vec4(mix(diffuse, edgeColor, g), 1.);

appendice

Per usare la profondità o le normali, è necessario salvarle in una trama se ciò non è già stato fatto. Quando si crea il frame buffer per il normale passaggio di rendering, è possibile allegare varie trame (vedere glFramebufferTexture2D) e scrivere loro altre informazioni oltre al colore della scena.

Se si collega una trama di profondità (con GL_DEPTH_ATTACHMENT), verrà utilizzata automaticamente per la profondità. Se si collegano una o più trame di colore (con GL_COLOR_ATTACHMENTi), è possibile scrivere a esse dichiarando più output ai propri shader di frammento (questo era solito fare con gl_FragData; in entrambi i casi, vedere glDrawBuffers).

Per ulteriori informazioni sull'argomento, cercare "Multi Render Target" (MRT).


Bella informazione, grazie Julien. Un'altra domanda: come posso utilizzare la profondità o le informazioni normali nel mio shader di frammenti? Sono ancora abbastanza nuovo con GLSL e quindi non ho idea di come includere i valori nell'algoritmo di Sobel.
enne87,

2
Quando si crea il framebuffer per il passaggio di rendering, è possibile allegare più di una trama. Se si collega una trama di profondità, verrà utilizzata automaticamente per la profondità. Allega un'altra trama di colore che puoi scrivere separatamente (vedi opengl.org/sdk/docs/man/html/glDrawBuffers.xhtml ), una funzione chiamata "Multi Render Target" (MRT).
Julien Guertault,

@ enne87: felice di aiutarti. :)
Julien Guertault,

@trichoplax: grazie per il suggerimento; fatto.
Julien Guertault,
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.