Come posso eseguire queste attività di elaborazione delle immagini usando gli shader OpenGL ES 2.0?

Come posso eseguire le seguenti attività di elaborazione delle immagini usando gli shader OpenGL ES 2.0?

  • Trasforma Colorspace (RGB / YUV / HSL / Lab)
  • Vortice dell’immagine
  • Conversione in uno schizzo
  • Conversione in un dipinto a olio

Ho appena aggiunto dei filtri al mio framework GPUImage open source che esegue tre delle quattro attività di elaborazione che descrivi (swirling, sketch filtering e conversione in un dipinto ad olio). Anche se non ho ancora trasformazioni di colorspace come filtri, ho la possibilità di applicare una matrice per trasformare i colors.

Come esempi di questi filtri in azione, ecco una conversione del colore dei toni seppia:

Immagine tono seppia

una distorsione a spirale

Immagine di distorsione a spirale

un filtro schizzo:

Filtro schizzo

e infine una conversione della pittura ad olio:

Conversione della pittura ad olio

Nota che tutti questi filtri sono stati fatti su fotogrammi video dal vivo, e tutti tranne l’ultimo filtro possono essere eseguiti in tempo reale su video da fotocamere di dispositivi iOS. L’ultimo filtro è piuttosto intensivo dal punto di vista computazionale, quindi anche come shader ci vogliono circa 1 secondo per eseguire il rendering su un iPad 2.

Il filtro tonalità seppia si basa sul seguente shader del frammento di matrice colore:

varying highp vec2 textureCoordinate; uniform sampler2D inputImageTexture; uniform lowp mat4 colorMatrix; uniform lowp float intensity; void main() { lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate); lowp vec4 outputColor = textureColor * colorMatrix; gl_FragColor = (intensity * outputColor) + ((1.0 - intensity) * textureColor); } 

con una matrice di

 self.colorMatrix = (GPUMatrix4x4){ {0.3588, 0.7044, 0.1368, 0}, {0.2990, 0.5870, 0.1140, 0}, {0.2392, 0.4696, 0.0912 ,0}, {0,0,0,0}, }; 

Lo shader del frammento di turbolenza si basa su questo esempio di Geeks 3D e ha il seguente codice:

  varying highp vec2 textureCoordinate; uniform sampler2D inputImageTexture; uniform highp vec2 center; uniform highp float radius; uniform highp float angle; void main() { highp vec2 textureCoordinateToUse = textureCoordinate; highp float dist = distance(center, textureCoordinate); textureCoordinateToUse -= center; if (dist < radius) { highp float percent = (radius - dist) / radius; highp float theta = percent * percent * angle * 8.0; highp float s = sin(theta); highp float c = cos(theta); textureCoordinateToUse = vec2(dot(textureCoordinateToUse, vec2(c, -s)), dot(textureCoordinateToUse, vec2(s, c))); } textureCoordinateToUse += center; gl_FragColor = texture2D(inputImageTexture, textureCoordinateToUse ); } 

Il filtro dello schizzo viene generato utilizzando il rilevamento del bordo Sobel, con i bordi mostrati in diverse sfumature di grigio. Lo shader per questo è il seguente:

  varying highp vec2 textureCoordinate; uniform sampler2D inputImageTexture; uniform mediump float intensity; uniform mediump float imageWidthFactor; uniform mediump float imageHeightFactor; const mediump vec3 W = vec3(0.2125, 0.7154, 0.0721); void main() { mediump vec3 textureColor = texture2D(inputImageTexture, textureCoordinate).rgb; mediump vec2 stp0 = vec2(1.0 / imageWidthFactor, 0.0); mediump vec2 st0p = vec2(0.0, 1.0 / imageHeightFactor); mediump vec2 stpp = vec2(1.0 / imageWidthFactor, 1.0 / imageHeightFactor); mediump vec2 stpm = vec2(1.0 / imageWidthFactor, -1.0 / imageHeightFactor); mediump float i00 = dot( textureColor, W); mediump float im1m1 = dot( texture2D(inputImageTexture, textureCoordinate - stpp).rgb, W); mediump float ip1p1 = dot( texture2D(inputImageTexture, textureCoordinate + stpp).rgb, W); mediump float im1p1 = dot( texture2D(inputImageTexture, textureCoordinate - stpm).rgb, W); mediump float ip1m1 = dot( texture2D(inputImageTexture, textureCoordinate + stpm).rgb, W); mediump float im10 = dot( texture2D(inputImageTexture, textureCoordinate - stp0).rgb, W); mediump float ip10 = dot( texture2D(inputImageTexture, textureCoordinate + stp0).rgb, W); mediump float i0m1 = dot( texture2D(inputImageTexture, textureCoordinate - st0p).rgb, W); mediump float i0p1 = dot( texture2D(inputImageTexture, textureCoordinate + st0p).rgb, W); mediump float h = -im1p1 - 2.0 * i0p1 - ip1p1 + im1m1 + 2.0 * i0m1 + ip1m1; mediump float v = -im1m1 - 2.0 * im10 - im1p1 + ip1m1 + 2.0 * ip10 + ip1p1; mediump float mag = 1.0 - length(vec2(h, v)); mediump vec3 target = vec3(mag); gl_FragColor = vec4(mix(textureColor, target, intensity), 1.0); } 

Infine, l'aspetto della pittura a olio viene generato utilizzando un filtro Kuwahara. Questo particolare filtro proviene dal lavoro eccezionale di Jan Eric Kyprianidis e dei suoi colleghi ricercatori, come descritto nell'articolo "Filtro anisotropico Kuwahara sulla GPU" all'interno del libro GPU Pro . Il codice shader da questo è il seguente:

  varying highp vec2 textureCoordinate; uniform sampler2D inputImageTexture; uniform int radius; precision highp float; const vec2 src_size = vec2 (768.0, 1024.0); void main (void) { vec2 uv = textureCoordinate; float n = float((radius + 1) * (radius + 1)); vec3 m[4]; vec3 s[4]; for (int k = 0; k < 4; ++k) { m[k] = vec3(0.0); s[k] = vec3(0.0); } for (int j = -radius; j <= 0; ++j) { for (int i = -radius; i <= 0; ++i) { vec3 c = texture2D(inputImageTexture, uv + vec2(i,j) / src_size).rgb; m[0] += c; s[0] += c * c; } } for (int j = -radius; j <= 0; ++j) { for (int i = 0; i <= radius; ++i) { vec3 c = texture2D(inputImageTexture, uv + vec2(i,j) / src_size).rgb; m[1] += c; s[1] += c * c; } } for (int j = 0; j <= radius; ++j) { for (int i = 0; i <= radius; ++i) { vec3 c = texture2D(inputImageTexture, uv + vec2(i,j) / src_size).rgb; m[2] += c; s[2] += c * c; } } for (int j = 0; j <= radius; ++j) { for (int i = -radius; i <= 0; ++i) { vec3 c = texture2D(inputImageTexture, uv + vec2(i,j) / src_size).rgb; m[3] += c; s[3] += c * c; } } float min_sigma2 = 1e+2; for (int k = 0; k < 4; ++k) { m[k] /= n; s[k] = abs(s[k] / n - m[k] * m[k]); float sigma2 = s[k].r + s[k].g + s[k].b; if (sigma2 < min_sigma2) { min_sigma2 = sigma2; gl_FragColor = vec4(m[k], 1.0); } } } 

Anche in questo caso, questi sono tutti filtri incorporati all'interno di GPUImage , quindi è sufficiente rilasciare tale framework nell'applicazione e iniziare a utilizzarli su immagini, video e filmati senza dover toccare alcun OpenGL ES. Tutto il codice per il framework è disponibile con una licenza BSD, se vuoi vedere come funziona o modificarlo.

Potresti iniziare controllando questo elenco di shader qui . Se vuoi approfondire un po ‘, ti consiglio di dare un’occhiata al libro arancione che trovi qui .