Обработка изображений на GPU L J Обработка изображений (и видео) довольно часто встречается в раз-личных областях. При этом она зачастую требует проведения весьма сложных вычислений для большого количества пикселов. Кроме того, во многих случаях требуется высокая точность (т.е. 8 бит на компоненту явно недостаточно Все это очень хорошо ложится на модель программирования GPU. Использование GPU позволяет параллельно обрабатывать огромное количество пикселов, задавая их как четырехмерные вещественные вектора Обработка изображений на GPU Операционная система Mac OS X содержит модуль CoreImage, обеспечивающий обработку изображений средствами GPU. В него входят десятки стандартных фильтров и возможность написания своих фильтров на специальном языке подмножестве GLSL Сама система Mac OS X уже давно использует GPU для вывода данных на экран, используя мощные возможности современных GPU для полноценной поддержки полупрозрачности, преобразований и многих других эффектов. Обработка изображений на GPU - общая схема Помещаем исходные изображения в текстуры (можно с floatформатом), вывод осуществляется в р-буфер или FBO. Средствами OpenGL осуществляется вывод прямоугольника, соответствующего выходному изображению. image1 image2 imageN Фрагментная программа outImage Обработка изображений на GPU - вершинная программа struct InData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; }; struct OutData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; }; OutData main ( InData IN, uniform float4x4 ModelViewProj ) { OutData OUT; OUT.pos = mul ( ModelViewProj, IN.pos ); OUT.texCoord = IN.texCoord; return OUT; } Обработка изображений на GPU Пример: перевод в оттенки серого цвета Формула преобразования: С = 0.3 * Red +0.59 * Green + 0.11 * Blue Обработка изображений на GPU Пример: перевод в оттенки серого цвета struct InData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; }; float4 main ( InData IN, uniform sampler2D tex0 ) : COLOR { const float3 luminance = float3 ( 0.3, 0.59, 0.11 ); float4 color = tex2D ( tex0, IN.texCoord ); return float4 ( float3 ( dot ( color.rgb, luminance ) ), 1.0 ); } Обработка изображений на GPU Пример: Sepia Формула преобразования: С = sepiaColor*(0.3 * Red +0.59 * Green + 0.11 * Blue) Обработка изображений на GPU Пример: Sepia struct InData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; }; float4 main ( InData IN, uniform sampler2D tex0 ) : COLOR { const float3 luminance = float3 ( 0.3, 0.59, 0.11 ); const float3 sepiaColor = float3 ( 1, 0.89, 0.54 ); float4 color = tex2D ( tex0, IN.texCoord ); return float4 ( dot ( color.rgb, luminance ) * sepiaColor, 1.0 ); } Обработка изображений на GPU Пример: гамма-коррекция Формула преобразования: С = pow ( C, 1 / gamma ) Обработка изображений на GPU Пример: гамма-коррекция struct InData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; }; float4 main ( InData IN, uniform float gamma, uniform sampler2D tex0 ) : COLOR { float3 color = tex2D ( tex0, IN.texCoord ).rgb; return float4 ( pow ( color, 1.0 / gamma ), 1.0 ); } Обработка изображений на GPU Свертка при помощи ядра Общий случай: cˆi , j r r k l r m r l ,m ci l , j m ci , j - элементы исходного изображения cˆi , j - элементы выходного изображения kl , m - ядро свертки (3*3,5*5,….N*N) Обработка изображений на GPU Пример - фильтр выделения границ (edge-detect) Преобразование интенсивности: Iˆi , j k I i 1, j I i 1, j I i , j 1 I i , j 1 Обработка изображений на GPU Пример - фильтр выделения границ (edge-detect) float4 main ( InData IN, uniform sampler2D tex0 { const float3 lum = float3 ( const float2 d01 = float2 ( const float2 d10 = float2 ( const float scale = 1.0; float float float float c1 c2 c3 c4 = = = = dot( dot( dot( dot( lum, lum, lum, lum, tex2D( tex2D( tex2D( tex2D( ) : COLOR 0.3, 0.59, 0.11 ); 0, 1.0 / 512.0 ); 1.0 / 512.0, 0 ); tex0, tex0, tex0, tex0, IN.texCoord+d01 IN.texCoord-d01 IN.texCoord+d10 IN.texCoord-d10 ).rgb ).rgb ).rgb ).rgb ); ); ); ); return float4 ( float3 ( scale * ( abs( c1 - c2 ) + abs( c2 - c3 ) ) ), 1 ); } Обработка изображений на GPU Пример - Blur (размытие) Обработка изображений на GPU Пример - Blur (размытие) Размытие с ядром Гаусса: Заметим, что: ki , j C exp k i 2 k j 2 ki , j C exp k i 2 C exp k j 2 Отсюда получаем: ˆ r ˆ cˆi , j kl k m ci l , j m l r m r r kl C exp k l 2 Обработка изображений на GPU Пример - Blur (размытие) Таким образом можно 2-мерное размытие свести к 2 двум одномерным - сперва по х, а потом - по у. Ядро, обладающее подобным свойством называется разделяемым (separable). При этом понадобится вспомогательный буфер y-blur x-blur buffer1 buffer2 buffer1 Обработка изображений на GPU Пример - Blur (размытие) // // x-blur fragment shader // float4 main ( InData IN, uniform sampler2D tex0 ) : COLOR { float3 sum = float3 ( 0.0 ); float2 dx = float2 ( 1.0 / 512.0, 0.0 ); float2 tx = IN.texCoord - 3.0 * dx; for ( int i = 0; i < 7; i++ ) { sum += tex2D ( tex0, tx ).rgb; tx += dx; } return float4 ( sum / 7.0, 1.0 ); } Обработка изображений на GPU Пример - emboss Ядро (применяемое к интенсивности): 1 1 1 1 1 1 1 1 1 Обработка изображений на GPU Пример - emboss float4 main ( InData { const float3 lum const float2 d01 const float2 d10 float float float float float float float float float float = = = = = = = = = = = float3 ( 0.3, 0.59, 0.11 ); = float2 ( 0, 1.0 / 512.0 ); = float2 ( 1.0 / 512.0, 0 ); dot(lum, tex2D(tex, IN.texCoord-d01-d10).rgb); dot(lum, tex2D(tex, IN.texCoord-d10).rgb); dot(lum, tex2D(tex, IN.texCoord+d01-d10).rgb ); dot(lum, tex2D(tex, IN.texCoord-d01).rgb ); dot(lum, tex2D(tex, IN.texCoord).rgb ); dot(lum, tex2D(tex, IN.texCoord+d01).rgb ); dot(lum, tex2D(tex, IN.texCoord+d10-d01).rgb ); dot(lum, tex2D(tex, IN.texCoord+d10).rgb ); dot(lum, tex2D(tex, IN.texCoord+d10+d01).rgb ); c00 + c01 - c02 + c10 + c11 - c12 + c20 - c21 - c22; return float4 ( res, res, res, 1.0 ); } c00 c01 c02 c10 c11 c12 c20 c21 c22 res IN, uniform sampler2D tex ) : COLOR Обработка изображений на GPU Пример - enhance Обработка изображений на GPU Пример - enhance Применяемое преобразование: Cˆ i , j Ci , j 1 k Ci , j Разностный аналог оператора Лапласа: Ci , j Ci 1, j Ci 1, j Ci , j 1 Ci1, j 1 4 Ci , j Обработка изображений на GPU Преобразования цветов в пространстве HSV V Зеленый 1.0 Голубой Синий Желтый Красный Модель HSV: • H (Hue) - тон ([0,360)), • S (Saturation) - насыщенность ([0,1]), • V (Value) - интенсивность (яркость) [0,1]) Малиновый R, G, B H , S ,V k H , k S , k V R, G, B 0.0 S Черный Обработка изображений на GPU Преобразования цветов в пространстве HSV Обработка изображений на GPU Преобразования цветов в пространстве HSV Обработка изображений на GPU Преобразования цветов в пространстве HSV float3 rgbToHsv ( const in float3 c ) { float mn = min ( min ( c.r, c.g ), c.b ); float mx = max ( max ( c.r, c.g ), c.b ); float delta = mx - mn; float v = mx, h = 0.0, s = 0.0; if ( mx > EPS ) { s = delta / mx; if ( c.r == mx ) h = ( c.g - c.b ) / delta; else if ( c.g == mx ) h = 2.0 + ( c.b - c.r ) / delta; else h = 4.0 + ( c.r - c.g ) / delta; } return float3 ( h / 6.0, s, v ); Обработка изображений на GPU Пример - эффект старого фильма Обработка изображений на GPU Пример - эффект старого фильма Основные элементы: • дергание (shudder) исходного изображения, • изменения яркости, • царапины, • волоски, пылинки и прочий мусор Обработка изображений на GPU Эффект старого фильма, вершинная программа struct InData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; }; struct OutData { float4 pos : POSITION; float2 texCoord : TEXCOORD0; float2 shudder; float2 blipCoord; float2 scratchCoord; float brightness; }; Обработка изображений на GPU Эффект старого фильма, вершинная программа OutData main ( InData IN,uniform float time, uniform float4x4 ModelViewProj ) { OutData OUT; OUT.shudder = IN.texCoord + C1* float2 ( sin(time*C2), cos(time*C3)); OUT.brightness = clamp(3.0*sin(time*C4), 0.7, 1.0 ); OUT.blipCoord = C5*IN.texCoord + float2(sin(C6*time)+cos(C7*time ), cos(C8*time)+sin(C9*time)); OUT.scratchCoord= IN.texCoord+float2(C10*cos(C11*time), C12*sin(C13*time)); OUT.pos = mul ( ModelViewProj, IN.pos ); OUT.texCoord = IN.texCoord; return OUT; } Обработка изображений на GPU Эффект старого фильма, вершинная программа float4 main ( OutData IN, uniform sampler2D uniform sampler2D uniform sampler2D { const float3 luminance = float4 float4 float4 float4 color scratch blip outColor = = = = mainTex, scratchTex, blipTex ) : COLOR float3 ( 0.3, 0.59, 0.11 ); tex2D ( mainTex, IN.shudder ); tex2D ( scratchTex, IN.scratchCoord ); tex2D ( blipTex, IN.blipCoord ); dot ( color.rgb, luminance ) * IN.brightness * scratch * blip; return float4 ( outColor.r, outColor.g, outColor.b, 1.0 ); } Обработка изображений на GPU Пример - эффект перехода к другому видеоряду Программа смешения Видеоряд 1 Выходной видеоряд 1 Видеоряд 2 Задача: организовать плавный переход от одного видеоряда к другому Обработка изображений на GPU Пример - эффект перехода к другому видеоряду Используем вспомогательную текстуру transMap: R-компонента - момент начала перехода к второму ряду G-компонента - конец перехода к второму ряду Закон смешения потоков: float3 float float4 float4 trans p c1 c2 = = = = tex2D clamp tex2D tex2D ( ( ( ( transMap, IN.texCoord time, trans.r, trans.g image1Map, IN.texCoord image2Map, IN.texCoord ).rgb; ); ); ); return mix ( c1, c2, (p - trans.r)/(trans.g - trans.r) ); Обработка изображений на GPU Пример - эффект перехода к другому видеоряду J В результате мы получили очень гибкий механизм перехода от одного видеоряда к другом - для каждого пиксела задается начало и конец перехода. Внутри интервала переходя происходит линейная интерполяция между цветами