Гауссов фильтр с шейдерами OpenGL - PullRequest
10 голосов
/ 26 января 2011

Я пытаюсь научить шейдеров реализовать что-то в моем приложении для iPhone. До сих пор я разбирался с простыми примерами, такими как приведение цветного изображения к серой шкале, определение порога и т. Д. Большинство примеров включают простые операции, в которых обработка пикселя входного изображения I(x,y) приводит к простой модификации цветов одного и того же пикселя

Но как насчет Сверток? Например, самым простым примером будет фильтр Гаусса,

, где пиксель выходного изображения O(x,y) зависит не только от I(x,y), но и от окружающих 8 пикселей.

O(x,y) = (I(x,y)+ surrounding 8 pixels values)/9;

Обычно это невозможно сделать с одним единственным буфером изображения, иначе входные пиксели изменятся при выполнении фильтра. Как я могу сделать это с шейдерами? Кроме того, я должен обращаться с границами самостоятельно? или есть встроенная функция или что-то, что проверяет недопустимый пиксельный доступ как I(-1,-1)?

Заранее спасибо

PS: я буду щедрым (читай: дай много очков);)

Ответы [ 2 ]

9 голосов
/ 07 марта 2012

Даниэль Ракос представил высокооптимизированный шейдерный подход для выполнения размытия по Гауссу с девятью ударами .Его процесс использует базовую интерполяцию, обеспечиваемую аппаратной фильтрацией текстур, для выполнения фильтра из девяти попаданий, используя только пять операций чтения текстур за проход.Это также разделено на отдельные горизонтальные и вертикальные проходы для дальнейшего уменьшения необходимого количества чтений текстур.

Я свернул реализацию этого, настроенную для OpenGL ES и графических процессоров iOS, в мою среду обработки изображений (в классе GPUImageFastBlurFilter).В моих тестах он мог выполнить один проход размытия кадра 640x480 за 2,0 мс на iPhone 4, что довольно быстро.

Я использовал следующий вершинный шейдер:

 attribute vec4 position;
 attribute vec2 inputTextureCoordinate;

 uniform mediump float texelWidthOffset; 
 uniform mediump float texelHeightOffset; 

 varying mediump vec2 centerTextureCoordinate;
 varying mediump vec2 oneStepLeftTextureCoordinate;
 varying mediump vec2 twoStepsLeftTextureCoordinate;
 varying mediump vec2 oneStepRightTextureCoordinate;
 varying mediump vec2 twoStepsRightTextureCoordinate;

 void main()
 {
     gl_Position = position;

     vec2 firstOffset = vec2(1.3846153846 * texelWidthOffset, 1.3846153846 * texelHeightOffset);
     vec2 secondOffset = vec2(3.2307692308 * texelWidthOffset, 3.2307692308 * texelHeightOffset);

     centerTextureCoordinate = inputTextureCoordinate;
     oneStepLeftTextureCoordinate = inputTextureCoordinate - firstOffset;
     twoStepsLeftTextureCoordinate = inputTextureCoordinate - secondOffset;
     oneStepRightTextureCoordinate = inputTextureCoordinate + firstOffset;
     twoStepsRightTextureCoordinate = inputTextureCoordinate + secondOffset;
 }

и следующий фрагментный шейдер:

 precision highp float;

 uniform sampler2D inputImageTexture;

 varying mediump vec2 centerTextureCoordinate;
 varying mediump vec2 oneStepLeftTextureCoordinate;
 varying mediump vec2 twoStepsLeftTextureCoordinate;
 varying mediump vec2 oneStepRightTextureCoordinate;
 varying mediump vec2 twoStepsRightTextureCoordinate;

// const float weight[3] = float[]( 0.2270270270, 0.3162162162, 0.0702702703 );

 void main()
 {
     lowp vec3 fragmentColor = texture2D(inputImageTexture, centerTextureCoordinate).rgb * 0.2270270270;
     fragmentColor += texture2D(inputImageTexture, oneStepLeftTextureCoordinate).rgb * 0.3162162162;
     fragmentColor += texture2D(inputImageTexture, oneStepRightTextureCoordinate).rgb * 0.3162162162;
     fragmentColor += texture2D(inputImageTexture, twoStepsLeftTextureCoordinate).rgb * 0.0702702703;
     fragmentColor += texture2D(inputImageTexture, twoStepsRightTextureCoordinate).rgb * 0.0702702703;

     gl_FragColor = vec4(fragmentColor, 1.0);
 }

, чтобы выполнить это.Два прохода могут быть достигнуты путем отправки значения 0 для texelWidthOffset (для вертикального прохода), а затем подачи этого результата в цикл, где вы задаете значение 0 для texelHeightOffset (для горизонтального прохода).

У меня также есть несколько более продвинутых примеров извилин в вышеупомянутой интегрированной структуре, включая определение края Собеля.

2 голосов
/ 07 марта 2012

Горизонтальное размытие с использованием билинейной интерполяции. Вертикальное размытие прохода является аналоговым. Разверните для оптимизации.

//5 offsets for 10 pixel sampling!
float[5] offset = [-4.0f, -2.0f, 0.0f, 2.0f, 4.0f];
//int[5] weight = [1, 4, 6, 4, 1]; //sum = 16
float[5] weightInverse = [0.0625f, 0.25f, 0.375, 0.25f, 0.0625f];

vec4 finalColor = vec4(0.0f);

for(int i = 0; i < 5; i++)
    finalColor += texture2D(inputImage, vec2(offset[i], 0.5f)) * weightInverse[i];
...