2011-01-26 17 views
10

Estoy tratando de aprender Shaders para implementar algo en mi aplicación de iPhone. Hasta ahora he entendido ejemplos fáciles como hacer una imagen a escala de grises, umbralización, etc. La mayoría de los ejemplos involucran operaciones simples en donde el procesamiento de la imagen de entrada píxel I(x,y) resulta en una simple modificación de los colores del mismo píxelFiltro gaussiano con OpenGL Shaders

Pero, ¿qué hay de las circunvoluciones ?. Por ejemplo, el ejemplo más fácil sería el filtro gaussiano,

en donde la imagen de salida pixel O(x,y) depende no sólo de I(x,y) sino también en circundante 8 píxeles.

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

Normalmente, esto no se puede hacer con un único buffer de imagen o de entrada pixeles cambiará a medida que se realiza el filtro. ¿Cómo puedo hacer esto con sombreadores? Además, ¿debería manejar las fronteras yo mismo? o hay una función incorporada o algo que verifica el acceso a píxeles no válidos como I(-1,-1)?

Gracias de antemano

PD: Voy a ser generoso (es decir: dar un montón de puntos);)

Respuesta

8

Un enfoque basado en el shader altamente optimizada para realizar un desenfoque gaussiano de nueve golpear was presented by Daniel Rákos . Su proceso utiliza la interpolación subyacente proporcionada por el filtrado de textura en el hardware para realizar un filtro de nueve hits utilizando solo cinco lecturas de textura por pase. Esto también se divide en pases horizontales y verticales separados para reducir aún más el número de lecturas de textura requeridas.

Lancé una implementación de esto, sintonizada para OpenGL ES y las GPU de iOS, en my image processing framework (en la clase GPUImageFastBlurFilter). En mis pruebas, puede realizar un solo pase de desenfoque de un fotograma de 640x480 en 2.0 ms en un iPhone 4, lo cual es bastante rápido.

I utilizó la siguiente shader vértice:

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

y la siguiente shader fragmento:

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

Para realizar esta. Las dos pasadas se pueden lograr enviando un valor 0 para texelWidthOffset (para el pase vertical), y luego alimentando ese resultado en una ejecución donde se obtiene un valor 0 para texelHeightOffset (para el pase horizontal).

También tengo algunos ejemplos más avanzados de convoluciones en el marco anteriormente vinculado, incluida la detección de bordes Sobel.

+0

Usar la ventaja de la interpolación lineal no es tan nuevo. ¿Dónde está el beneficio de la forma más fácil? Buscar coordenadas [-4, -2, 0, 2, 4] para obtener un rango de 10 píxeles y luego aplicar la ponderación de cada dos téxeles? – djmj

+0

@djmj - No estoy seguro de que te sigo. ¿A qué manera más fácil te refieres? ¿Cuáles son los dos téxeles a los que te refieres? ¿Tiene una referencia que puede señalar que describe este enfoque? –

+0

Ver: http://drilian.com/journal/images/TextureGrid.png. Si toma muestras en (2, 0.5) obtendrá el valor interpolado bilineal de píxel (1, 0) y (2, 0). Muestreo en (2, 1) obtendrá el valor promedio de los 4 píxeles vecinos.Entonces use una compensación de 2 píxeles y luego aplique la ponderación gaussiana. – djmj

2

Desenfoque horizontal utilizando la ventaja de la interpolación bilineal. El pase de desenfoque vertical es analógico. Desenrollar para optimizar

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