Comment puis-je effectuer ces tâches de traitement d’image avec les shaders OpenGL ES 2.0?

Comment puis-je effectuer les tâches de traitement d’image suivantes à l’aide des shaders OpenGL ES 2.0?

  • Transformation de l’espace colorimésortingque (RVB / YUV / HSL / Lab)
  • Tourbillonnant de l’image
  • Conversion en croquis
  • Conversion à une peinture à l’huile

Je viens d’append des filtres à mon framework GPUImage open source pour effectuer trois des quatre tâches de traitement que vous décrivez (tourbillonnement, filtrage d’esquisse et conversion en peinture à l’huile). Bien que je ne dispose pas encore de transformations d’espace de couleur en tant que filtres, j’ai la possibilité d’appliquer une masortingce pour transformer les couleurs.

Comme exemples de ces filtres en action, voici une conversion de couleur sépia:

Image sépia

une distorsion de tourbillon:

Image de distorsion de tourbillon

un filtre d’esquisse:

Filtre d'esquisse

et enfin, une conversion de peinture à l’huile:

Conversion de peinture à l'huile

Notez que tous ces filtres ont été effectués sur des images vidéo en direct et que tous les filtres, à l’exception du dernier, peuvent être exécutés en temps réel sur les vidéos des appareils iOS. Le dernier filtre nécessite beaucoup de calculs, donc même en tant que shader, il faut environ 1 seconde pour effectuer un rendu sur un iPad 2.

Le filtre sépia est basé sur le shader de fragment de masortingce de couleur suivant:

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

avec une masortingce de

 self.colorMasortingx = (GPUMasortingx4x4){ {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}, }; 

Le fragment swirl fragment est basé sur cet exemple 3D de Geeks et a le code suivant:

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

Le filtre d'esquisse est généré à l'aide de la détection d'arête Sobel, les bords étant représentés en différentes nuances de gris. Le shader pour cela est comme suit:

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

Enfin, l'aspect de la peinture à l'huile est généré à l'aide d'un filtre de Kuwahara. Ce filtre provient en particulier du travail exceptionnel de Jan Eric Kyprianidis et de ses collègues chercheurs, tel que décrit dans l'article "Anisotropic Kuwahara Filtering on the GPU" du livre GPU Pro . Le code shader à partir de cela est le suivant:

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

Encore une fois, ce sont tous des filtres intégrés à GPUImage , vous pouvez donc simplement déposer cette structure dans votre application et commencer à les utiliser sur des images, des vidéos et des films sans avoir à toucher à OpenGL ES. Tout le code du framework est disponible sous licence BSD, si vous souhaitez voir comment cela fonctionne ou le modifier.

Vous pouvez commencer par consulter cette liste de shaders ici . Si vous voulez creuser un peu plus, je vous recommande de vérifier le livre orange trouvé ici .