OpenGL GLSL - фільтр виявлення країв Sobel


9

Що стосується цієї теми, я успішно застосував фільтр Sobel Edge Detekction в GLSL. Ось фрагмент шейдерного коду фільтра:

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

І ось результат куба з виявленням краю Собеля:

Куб з фільтром виявлення краю Собеля

Якщо ви збільшите картинку, ви побачите, що в компанії Sobel багато "шуму": по всій сцені є сірі горизонтальні смуги через синій / білий градієнт. Крім того, світлі шишки створюють небажаний візерунок на кубі. Чорні краї зліва від куба також схожі на вицвітання через світлий конус на лівій половині куба.

Тому я прочитав цю статтю, в якій сказано, що слід спершу розмалювати масштаб зображення і використовувати фільтр розмитості Гаусса, щоб зробити краї більш очевидними. Внизу статті є також фільтр виявлення реберних ребер, який, здається, дає кращі результати.

Зараз у мене є два питання:

  1. Чи правильні наступні кроки для отримання найкращих можливих результатів виявлення краю:

    • Відтінки сірого
    • Gaussian Blur
    • Виявлення краю Собеля / Кенні
  2. Якщо так, то як би я об'єднав оригінальне зображення з обробленим зображенням? Я маю на увазі, обробляючи описані вище етапи, я отримую зображення, яке є повністю чорним з білими краями або віце-веркою. Як би я наклав краї на моє оригінальне зображення / текстуру?

Спасибі за вашу допомогу!


Цікаво, чи зможете ви отримати потрібні результати, використовуючи прапор краю OpenGL певним чином. Схоже, що лише в режньому режимі малюються краї між вершинами з увімкненим прапором краю Там же опис тут . (Я цього не використовував і не розміщував би приклад.)
user1118321

Відповіді:


9
  1. Найкращі результати сильно залежать від Вашого випадку використання. Вони також залежать від того, якого ефекту ви хочете досягти. Sobel - це лише фільтр виявлення ребер: краї залежатимуть від вхідного сигналу, вибираючи, що вхідний сигнал залежить від вас.

    Тут ви використовуєте кольорове зображення як вхідне, і фільтр правомірно виявляє слабкі краї в синьому градієнті, в той час як краї куба перериваються там, де його колір занадто близький від кольору фону.

    Оскільки я припускаю, що ваша програма також відповідає за малювання куба, у вас є доступ до іншої інформації, яку ви можете подати у ваш фільтр Sobel. Наприклад, глибина та нормалі є хорошими кандидатами для виявлення ребер. Альбедо перед освітленням можна було використовувати. Тестуйте з різними вхідними даними та вирішуйте, який використовувати, залежно від отриманих результатів.

  2. Що стосується вашого запитання про поєднання крайової інформації, пропоную вам gтрохи відфільтрувати, перш ніж використовувати її. Потім ви можете використовувати його для інтерполяції між початковим кольором та бажаним кольором краю.

    Наприклад, ви можете спробувати щось подібне:

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

Додаток

Щоб використовувати глибину або нормали, вам потрібно буде зберегти їх у текстурі, якщо це вже не зроблено. Коли ви створюєте буфер кадру для вашого звичайного пропуску, ви можете приєднати до нього різні текстури (див. glFramebufferTexture2D) Та записати до них іншу інформацію, а не лише колір сцени.

Якщо ви додасте текстуру глибини (з GL_DEPTH_ATTACHMENT), вона буде використана автоматично для глибини. Якщо ви додасте одну або кілька кольорових текстур (з GL_COLOR_ATTACHMENTi), ви можете записати на них, оголосивши декілька виходів у ваші фрагментальні шейдери (це робилося за допомогою gl_FragData; так чи інакше, див. glDrawBuffers).

Для отримання додаткової інформації по темі знайдіть "Цільова поліреалізація" (MRT).


Приємна інформація, дякую Жюльєну. Ще одне запитання: як я міг використовувати глибину чи нормальну інформацію у своєму фрагменті шейдера? Я ще зовсім новачок у GLSL і тому не маю уявлення, як включити значення в алгоритм Sobel.
enne87

2
Коли ви створюєте фреймбуфер для передавального пропуску, ви можете прикріпити до нього кілька текстур. Якщо ви додасте текстуру глибини, вона буде використана автоматично для глибини. У ньому ви додаєте іншу кольорову текстуру, яку ви можете записати в неї окремо (див. Opengl.org/sdk/docs/man/html/glDrawBuffers.xhtml ), функцію під назвою "Multi Render Target" (MRT).
Жюльєн Гурто

@ enne87: Раді допомогти. :)
Жюльєн Герто

@trichoplax: дякую за пропозицію; зроблено.
Жульєн Герто
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.