OpenGL GLSL - Sobel Edge Detection Filter

9

Sehubungan dengan topik ini, saya telah berhasil mengimplementasikan filter Sobel Edge Detection di GLSL. Berikut ini adalah kode shader fragmen filter:

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

Dan ini adalah hasil dari sebuah kubus dengan deteksi tepi Sobel:

Kubus dengan filter deteksi tepi Sobel

Jika Anda memperbesar gambar, Anda akan melihat bahwa ada banyak "noise" yang dihasilkan oleh Sobel: Ada garis-garis horizontal abu-abu di seluruh adegan karena gradien biru / putih. Selanjutnya, kerucut cahaya menghasilkan pola yang tidak diinginkan pada kubus. Tepi hitam di sebelah kiri kubus juga tampaknya memudar karena kerucut cahaya di bagian kiri kubus.

Jadi saya membaca artikel ini yang menyatakan bahwa orang harus menggunakan skala abu-abu terlebih dahulu dan menggunakan filter Gaussian blur untuk membuat bagian tepi lebih jelas. Di bagian bawah artikel, ada juga filter deteksi tepi cerdik yang tampaknya menghasilkan hasil yang lebih baik.

Sekarang saya punya dua pertanyaan:

  1. Apakah langkah-langkah berikut ini benar untuk menghasilkan hasil deteksi tepi sebaik mungkin:

    • Skala abu-abu
    • Gaussian Blur
    • Deteksi tepi Sobel / Canny
  2. Jika ya, bagaimana saya menggabungkan gambar asli dengan gambar yang diproses? Maksudku, setelah memproses langkah-langkah yang disebutkan di atas, saya mendapatkan gambar yang benar-benar hitam dengan tepi putih atau sebaliknya. Bagaimana saya menempatkan ujung pada gambar / tekstur asli saya?

Terima kasih atas bantuan Anda!

enne87
sumber
Saya ingin tahu apakah Anda mungkin mendapatkan hasil yang Anda inginkan dengan menggunakan flag edge OpenGL dalam beberapa cara. Sepertinya hanya tepi antara simpul dengan bendera tepi menyala yang digambar saat dalam mode garis. Ada uraian di sini . (Saya tidak menggunakannya sendiri atau saya akan memposting contoh.)
user1118321

Jawaban:

9
  1. Hasil terbaik sangat tergantung pada kasus penggunaan Anda. Mereka juga bergantung pada efek apa yang ingin Anda capai. Sobel hanyalah filter deteksi tepi: tepi akan tergantung pada sinyal input, memilih sinyal input terserah Anda.

    Di sini Anda menggunakan gambar warna sebagai input, dan filter dengan tepat mendeteksi tepi yang samar dalam gradien biru, sedangkan tepi kubus terganggu di mana warnanya terlalu dekat dari warna latar belakang.

    Karena saya menganggap program Anda juga bertanggung jawab untuk menggambar kubus, Anda memiliki akses ke informasi lain yang dapat Anda masukkan ke filter Sobel Anda. Misalnya kedalaman dan normals adalah kandidat yang baik untuk deteksi tepi. Albedo sebelum penerangan bisa digunakan untuk itu. Tes dengan input berbeda dan putuskan mana yang akan digunakan tergantung pada hasil yang Anda dapatkan.

  2. Mengenai pertanyaan Anda tentang cara menggabungkan informasi tepi, saya sarankan Anda menyaring gsedikit sebelum menggunakannya. Kemudian Anda dapat menggunakannya untuk menginterpolasi antara warna asli dan warna tepi yang diinginkan.

    Misalnya, Anda dapat mencoba sesuatu seperti ini:

    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.);

Tambahan

Untuk menggunakan kedalaman atau normals, Anda harus menyimpannya dalam tekstur jika itu belum dilakukan. Saat Anda membuat buffer bingkai untuk pass rendering reguler Anda, Anda dapat melampirkan berbagai tekstur padanya (lihat glFramebufferTexture2D) dan menulis informasi lain kepada mereka daripada hanya warna adegan.

Jika Anda memasang tekstur kedalaman (dengan GL_DEPTH_ATTACHMENT), itu akan digunakan secara otomatis untuk kedalaman. Jika Anda melampirkan satu atau beberapa tekstur warna (dengan GL_COLOR_ATTACHMENTi), Anda dapat menulis kepada mereka dengan mendeklarasikan beberapa output ke shader fragmen Anda (ini biasanya dilakukan dengan gl_FragData; bagaimanapun, lihat glDrawBuffers).

Untuk informasi lebih lanjut tentang topik ini, lihat "Multi Render Target" (MRT).

Julien Guertault
sumber
Informasi yang bagus, terima kasih Julien. Satu pertanyaan lagi: Bagaimana saya bisa menggunakan kedalaman atau informasi normal di fragmen shader saya? Saya masih cukup baru dengan GLSL dan karena itu tidak tahu bagaimana memasukkan nilai-nilai dalam algoritma Sobel.
enne87
2
Saat Anda membuat framebuffer untuk pass rendering Anda, Anda dapat melampirkan lebih dari satu tekstur. Jika Anda memasang tekstur kedalaman, itu akan digunakan secara otomatis untuk kedalaman. Jika Anda melampirkan tekstur warna lain, Anda dapat menuliskannya secara terpisah (lihat opengl.org/sdk/docs/man/html/glDrawBuffers.xhtml ), fitur yang disebut "Multi Render Target" (MRT).
Julien Guertault
@ enne87: Senang membantu. :)
Julien Guertault
@trichoplax: terima kasih atas sarannya; selesai
Julien Guertault