16.7.1. Сглаживание
Операции сглаживания изображений нужны для того, чтобы приглушить высокие частоты. Общая операция сглаживания называется усреднение соседних элементов. Этот метод использует ядро свертки со значением 1,0 в каждой точке. Окончательная сумма делится на количество точек в ядре. Например, сверточный фильтр для усреднения размером 3x3 выглядит примерно так:
1 1 1
1 1 1
1 1 1
Окончательная сумма делится на 9 (или умножается на 1 /9). Этим методом вычисляется среднее значение окружающих заданную точку пикселов. В результате изображение получается более мягким, размытым.
Так как все элементы ядра равны 1, можно написать упрощенный фрагментный шейдер для усреднения (листинг 16.7). Этот шейдер можно использовать для ядра любого размера, где width ■ height < 25 (например, 5 х 5). Результаты работы этого шейдера показаны на рис. 16.1, б.
Листинг 16.7. Фрагментный шейдер для свертки методом усреднения
// Максимальный размер для данного шейдера
const int MaxKernelSize = 25:
// Массив со значениями сдвигов для доступа к основному изображению uniform vec2 Offset[MaxKernelSize]:
// Размеры ядра (width * height) uniform int Kernel Size:
// Масштаб
uniform vec4 ScaleFactor:
// Основное изображение uniform sampler2D Baselmage;
void main(void)
{
int i:
vec4 sum = vec4 (0.0):
for (i = 0: i < KernelSize: i++)
sum += texture2D(BaseImage. gl_TexCoord[0],st + Offset[i]):
gl_FragColor = sum * ScaleFactor:
}
Этот способ сглаживания обычно применяется для уменьшения шума. Прием хорошо работает в областях сплошного цвета или интенсивности, однако получается побочный эффект размывания краев областей. Избежать этой проблемы поможет сверточный фильтр, например фильтр Гаусса, который можно задать в ядре таким образом:
1/273 |
4/273 |
7/273 |
4/273 |
1/273 |
4/273 |
16/273 |
26/273 |
16/273 |
4/273 |
7/273 |
26/273 |
41/273 |
26/273 |
7/273 |
4/273 |
16/273 |
26/273 |
16/273 |
4/273 |
1/273 |
4/273 |
7/273 |
4/273 |
1/273 |
В листинге 16.8 приведен код для более общего шейдера свертки. Этот шейдер может работать с ядрами, содержащими до 25 элементов. В этом шейдере каждый элемент ядра должен умножаться на коэффициент масштабирования, поэтому не нужно масштабировать сумму.
Листинг 16.8. Фрагментный шейдер для общих вычислений свертки
// Максимальный размер ядра, поддерживаемый этим шейдером const int MaxKernelSize = 25:
// Массив со значениями сдвигов для доступа к основному изображению uniform vec2 Offset[MaxKernelSize];
// Размеры ядра (width * height) uniform int Kernel Size:
// Значение для каждой точки ядра uniform vec4 KernelValue[MaxKernelSize]:
// Основное изображение uniform sampler2D Baselmage; Листинг 16.8 (продолжение)
void main(void)
{
int i:
vec4 sum = vec4 (0.0);
for (i =0: i < Kernel Size; i++)
{
vec4 tmp = texture2D(BaseImage, gl_TexCoord[0].st + Offset[i]): sum +- tmp * KernelValue[i]:
}
gl_FragColor = sum;
}
К исходному изображению (рис. 16.1, а) добавили шум, и получилось то, что изображено на рис. 16.1, в. Затем шум был удален с помощью фильтра Гаусса, результат показан на рис. 16.1, г. Следует заметить, что шум значительно уменьшился в областях с относительно постоянной интенсивностью.