2011-01-26 2 views
10

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

Но, как насчет Convolution ?. Например, самым простым примером будет гауссовский фильтр:

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

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

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

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

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

ответ

8

Высоко оптимизированные шейдеры подход, основанный на выполнении девять пострадавших Гауссу was presented by Daniel Rákos , Его процесс использует базовую интерполяцию, обеспечиваемую фильтрацией текстур в аппаратных средствах, для выполнения фильтра с девятью ударами, используя только пять текстур для каждого прохода. Это также разделяется на отдельные горизонтальные и вертикальные проходы, чтобы дополнительно уменьшить количество требуемых текстурных чтений.

Я выполнил реализацию этого, настроенного для OpenGL ES и графических процессоров iOS, в my image processing framework (под классом 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 (для горизонтального прохода).

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

+0

Использование преимуществ линейной интерполяции не является чем-то новым. Где преимущество обычного более простого способа? Поиск в координатах [-4, -2, 0, 2, 4], чтобы получить 10-пиксельный диапазон, а затем применить взвешивание каждых двух текселей? – djmj

+0

@djmj - Я не уверен, что я последую за тобой. Что более простой способ вы имеете в виду? Назовите те тексели, о которых вы говорите? У вас есть ссылка, на которую вы можете указать, что описывает этот подход? –

+0

См .: http://drilian.com/journal/images/TextureGrid.png. Если вы пробуете на (2, 0,5), вы получите билинейное интерполированное значение пикселя (1, 0) и (2, 0). Сэмплирование в (2, 1) вы получите среднее значение 4 соседних пикселей!Поэтому просто используйте смещение в 2 пикселя, а затем примените гауссовский вес. – djmj

2

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

//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];