Обработка изображений 7. Гистограмма
от aNNiMON

Содержание:
1. Введение
2. Изображения. Простая трансформация
3. Негатив, извлечение и инверсия каналов
4. Обесцвечивание
5. Цветовые модели
6. Яркость, насыщенность, контрастность, гамма-коррекция
7. Гистограмма
8. Масштабирование изображения
9. Размытие
10. Свёртка
В RGB-представлении изображения 256 значений яркости для каждого канала. Что, если подсчитать количество пикселей для каждого такого значения? Мы получим некоторую статистику распределения яркости, сможем узнать, какая яркость преобладает в изображении, а какой, напротив, мало. Вот это и есть гистограмма изображения.
Если подсчитывать значения каждого цветового канала как будто это один канал, либо преобразовывать цвет в оттенки серого, а потом подсчитывать статистику, то получится гистограмма яркости. По ней можно определить, что фотография недостаточно яркая и её нужно осветлить (повысить яркость при пост-обработке, либо увеличить экспозицию при съёмке), либо она, напротив, засвечена.
Если строить график для каждой RGB компоненты, то получится RGB-гистограмма. Такая гистограмма позволяет узнать, какой цветовой канал преобладает на изображении и принять соответствующие меры. Если гистограмма построена фотокамерой в момент фокусировки, то по ней можно определить, что на фотографии, допустим, преобладает красный цвет и возможно стоит настроить баланс белого на более холодные тона. Если же гистограмма построена на этапе пост-обработки, то можно попытаться скомпенсировать засвет соответствующим цветовым фильтром.
Для желающих больше узнать о цветовой температуре и балансе белого, рекомендую ознакомиться со статьёй Баланс белого.Но, хватит теории, перейдём к практике.
Построение гистограммыГистограмму будем рисовать на канвасе. Сначала создадим массив из 256 элементов и заполним его нулями. Затем подсчитаем количество вхождений каждого значения яркости.
- let histBrightness = (new Array(256)).fill(0);
- for (let i = 0; i < src.length; i++) {
- let r = src[i] & 0xFF;
- let g = (src[i] >> 8) & 0xFF;
- let b = (src[i] >> 16) & 0xFF;
- histBrightness[r]++;
- histBrightness[g]++;
- histBrightness[b]++;
- }
- let maxBrightness = 0;
- for (let i = 1; i < 256; i++) {
- if (maxBrightness < histBrightness[i]) {
- maxBrightness = histBrightness[i]
- }
- }
- const canvas = document.getElementById('canvasHistogram');
- const ctx = canvas.getContext('2d');
- let dx = canvas.width / 256;
- let dy = canvas.height / maxBrightness;
- ctx.lineWidth = dx;
- ctx.fillStyle = "#fff";
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- for (let i = 0; i < 256; i++) {
- let x = i * dx;
- ctx.strokeStyle = "#000000";
- ctx.beginPath();
- ctx.moveTo(x, canvas.height);
- ctx.lineTo(x, canvas.height - histBrightness[i] * dy);
- ctx.closePath();
- ctx.stroke();
- }
See the Pen Image processing 7.1. Histogram by aNNiMON (@aNNiMON) on CodePen.
Возьмём обычную фотографию:


По этой гистограмме видно, что изображение достаточно контрастное, в нём присутствуют и тёмные, и светлые тона.
А вот фотография того же места с экспозицией -2.0:


По гистограмме видно, что светлых участков здесь практически нет, а большой пик в левой части говорит, что фотография затемнена.

Наконец, фотография с экспозицией +2.0:


Здесь настолько много пересвета, что средние тона на гистограмме практически не видны – всё ушло в правый край.

Пора исправлять! Для этого добавим корректировку яркости и контрастности из предыдущей статьи и будем смотреть, как изменяется гистограмма.
See the Pen Image processing 7.2. Histogram + Corrections by aNNiMON (@aNNiMON) on CodePen.
Как видно, яркость перемещает гистограмму влево/вправо, а контрастность сужает/расширяет диапазон.
Если настроить параметры так, чтобы слева и справа не было пустого пространства и при этом не было большого затемнения и пересвета, то мы получим чуть более приемлемую картинку:

Именно по такому принципу работает коррекция Автоконтраст в Photoshop:

Получился почти такой же результат, что и при ручном выравнивании в js.
8. Масштабирование изображений