Рис. 16.1. Результаты различных операций: а- исходное черно-белое изображение 512 х 512; б- усреднение 5x5 соседних пикселов; в- добавление шума к оригиналу; г- понижение шума фильтром Гаусса С увеличением размеров ядра свертки количество необходимых обращений к текстуре увеличивается, как квадрат этих размеров. При использовании боль ших ядер это может сильно повлиять на производительность. Некоторые ядра можно назвать делимыми, так как все действия с ядром размером widthxheight можно выполнить за два прохода, одномерными операциями widthxl и lxheight. При этом для каждого пиксела будет сделана лишняя запись, но количество обращений к текстуре уменьшается с width • height до width + height.
16.7.2. Выделение края
Еще одно довольно распространенное применение операции свёртки - выделение края, то есть поиск разрывов в изменении уровня интенсивности.
Один из методов выделения края подразумевает использование оператора Лапласа
0 1 О
1 -4 1
О 1 О
Можно поместить лапласовское ядро свертки во фрагментный шейдер, приведенный в листинге 16.8. Результат работы этого шейдера показан на рис. 16.2.
Рис. 16.2. Выделение края с помощью лапласовского ядра свертки (изображение масштабировано)
16.7.3. Увеличение резкости
Распространенный способ увеличения резкости изображения - добавление результата наложения фильтра выделения края на исходное изображение. Для управления степенью резкости используется коэффициент.
Один из способов увеличения резкости изображения - применение отрицательного оператора Лапласа. Фильтр будет выглядеть так:
0-10 -1 4 -1
0-10
Фрагментный шейдер, реализующий этот способ, почти совпадает с шейдером из предыдущего раздела (листинг 16.9). Единственное отличие в том, что результат выполнения операции добавляется к исходному изображению. Перед добавлением он корректируется с помощью коэффициента, передаваемого приложением через uniform-переменную. Результаты изменения контрастности показаны на рис. 16.3.
Листинг 16.9. Фрагментный шейдер для изменения резкости (контрастности)
// Максимальный размер ядра, поддерживаемый шейдером const int MaxKernelSize = 25;
// Массив значений сдвигов для доступа к основному изображению uniform vec2 Offset[MaxKernelSize]:
// Размеры ядра (width * height) uniform int Kernel Size;
// Значение для каждой точки ядра uniform vec4 KernelValue[MaxKernelSize]:
// Коэффициент uniform vec4 ScaleFactor:
// Исходное изображение uniform sampler2D Baselmage:
void main(void)
{
int i;
vec4 sum = vec4 (0.0);
for (i = 0; i < KernelSize: i++)
{
vec4 tmp = texture2D(BaseImage. gl_TexCoord[0].st + OffsetCi]): sum += tmp * KernelValue[i]:
}
vec4 baseColor = texture2D(BaseImage. vec2(gl_TexCoord[0])): gl_FragColor = ScaleFactor * sum + baseColor;
}
Рис. 16.3. Результат работы шейдера для изменения резкости (лапласовское изображение в центре масштабировано)
16.8. Итоги В дополнение к работе с трехмерными объектами, OpenGL может работать и с изображениями. Язык шейдеров OpenGL расширил возможности стандартного OpenGL, позволив выполнять программируемую обработку изображений. Программируемость и возможность параллельной обработки информации на графическом аппаратном обеспечении позволяют выполнять операции над изображениями гораздо быстрее, чем на обычном процессоре. Можно запрограммировать множество операций обработки изображений: размывание, контрастность, удаление шума; цветокоррекцию; изменение насыщенности цвета, яркости, контраста, - а также геометрические преобразования, такие как повороты и деформирование, смещение и множество других операций. Приложения больше не ограничены в манипулировании цветными и одноцветными изображениями. Можно выполнять также их спектральный анализ и обработку.