Как я могу выполнить эти задачи обработки изображений с помощью OpenGL ES 2.0 шейдеров?
Как я могу выполнить следующие задачи обработки изображений с помощью шейдеров OpenGL ES 2.0?
- преобразование цветового пространства (RGB / YUV / HSL / Lab)
- вращение изображения
- преобразовать в эскиз
- преобразование в картину маслом
2 ответов:
Я только что добавил фильтры в мой открытый исходный код GPUImage framework которые выполняют три из четырех описанных задач обработки (закручивание, фильтрация эскиза и преобразование в масляную живопись). Хотя у меня еще нет преобразований цветового пространства в качестве фильтров, у меня есть возможность применить матрицу для преобразования цветов.
в качестве примеров этих фильтров в действии, вот преобразование цвета тона сепии:
вихрем искажение:
Фильтр Эскиз:
и, наконец, преобразование масляной живописи:
обратите внимание, что все эти фильтры были сделаны на живых видеокадрах, и все, кроме последнего фильтра, можно запускать в режиме реального времени на видео с камер устройств iOS. Последний фильтр довольно интенсивен в вычислительном отношении, поэтому даже в качестве шейдера требуется ~1 секунда или около того для визуализации на iPad 2.
в sepia tone filter основан на следующем шейдере фрагментов цветовой матрицы:
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); }матрица
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}, };шейдер фрагмента вихря основан на это вундеркинды 3D пример и имеет следующий код:
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 ); }фильтр эскиза создается с помощью sobel edge detection, при этом края отображаются в различных серых оттенках. Шейдер для этого выглядит следующим образом:
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); }в конце концов, взгляд картины маслом произведен используя а Фильтр Кувахара. Этот конкретный фильтр из выдающейся работы Ян Эрик Kyprianidis и его коллеги-исследователи, как описано в статье "Анизотропная фильтрация Кувахары на GPU" в пределах GPU Pro book. Код шейдера из этого выглядит следующим образом:
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); } } }опять же, это все встроенные фильтры внутри GPUImage, так что вы можете просто бросить эту структуру в свое приложение и начать использовать их на изображениях, видео и фильмах без необходимости прикасаться к любым OpenGL ES. Весь код для фреймворка доступен под лицензией BSD, если вы хотите посмотреть, как он работает или настроить его.




Comments