Обработка изображений 10. Свёртка
от aNNiMON

Содержание:
1. Введение
2. Изображения. Простая трансформация
3. Негатив, извлечение и инверсия каналов
4. Обесцвечивание
5. Цветовые модели
6. Яркость, насыщенность, контрастность, гамма-коррекция
7. Гистограмма
8. Масштабирование изображения
9. Размытие
10. Свёртка
Если модифицировать предыдущий пример с эффектом размытия и радиусом 1 (то есть просмотр 3х3 пикселей) так, чтобы каждая цветовая компонента из девяти просматриваемых пикселей умножалась на соответствующие девять коэффициентов, всё так же суммировалась, а потом делилась на какое-то значение, то мы получим свёртку.
А чтобы было универсальнее, помимо массива коэффициентов 3x3, именуемого ядром свёртки, и делителя div, также будет задаваться некоторое смещение offset. offset будет суммироваться с каждым цветовым компонентом, позволяя увеличить или уменьшить яркость.
Разумеется, можно не ограничиваться ядром 3x3, а сделать его 5x5 или даже больше.
В фотошопе в Фильтр - Другое - Заказная реализовано ядро 5x5.

- const kernelSize = 3;
- const halfSize = Math.floor(kernelSize / 2);
- let kernel = [/* 3x3 */];
- let div = 1;
- let offset = 0;
- let dstIndex = 0;
- for (let y = 0; y < height; y++) {
- for (let x = 0; x < width; x++) {
- let r = 0, g = 0, b = 0;
- for (let sy = 0; sy < kernelSize; sy++) {
- const yy = Math.min(height - 1, Math.max(0, y + sy - halfSize));
- for (let sx = 0; sx < kernelSize; sx++) {
- const xx = Math.min(width - 1, Math.max(0, x + sx - halfSize));
- let pix = src[yy * width + xx];
- r += (pix & 0xFF) * kernel[sy][sx];
- g += ((pix >> 8) & 0xFF) * kernel[sy][sx];
- b += ((pix >> 16) & 0xFF) * kernel[sy][sx];
- }
- }
- const a = src[y * width + x] & 0xFF000000;
- r = Math.min(255, Math.max(0, offset + (r / div))) & 0xFF;
- g = Math.min(255, Math.max(0, offset + (g / div))) & 0xFF;
- b = Math.min(255, Math.max(0, offset + (b / div))) & 0xFF;
- dst[dstIndex++] = a | (b << 16) | (g << 8) | r;
- }
- }
See the Pen Image processing 10.1. Convolution matrix by aNNiMON (@aNNiMON) on CodePen.
По умолчанию ядро задано в таком виде:
- 0 0 0
- 0 1 0
- 0 0 0
- div: 1, offset: 0

- 1 1 1
- 1 1 1
- 1 1 1
- div: 9, offset: 0

- 1 2 1
- 2 4 2
- 1 2 1
- div: 16, offset: 0

- 0 -1 0
- -1 5 -1
- 0 -1 0
- div: 1, offset: 0

Коэффициенты можно уменьшить для ослабления эффекта резкости.
- 0 -0.2 0
- -0.2 1.8 -0.2
- 0 -0.2 0
- div: 1, offset: 0

- -2 -1 0
- -1 1 1
- 0 1 2
- div: 1, offset: 0

- -2 -1 0
- -1 0 1
- 0 1 2
- div: 1, offset: 0

А если задать offset=128, то получится эффект Рельеф
- -2 -1 0
- -1 0 1
- 0 1 2
- div: 1, offset: 128

А ещё при помощи свёртки можно сделать инверсию изображения.
- 0 0 0
- 0 -1 0
- 0 0 0
- div: 1, offset: 255

Более наглядную демонстрацию работы свёртки с просмотром коэффициентов и их сумм, но на одноканальном изображении, можно посмотреть здесь http://setosa.io/ev/image-kernels/