Обработка изображений 4. Обесцвечивание
от aNNiMON

На очереди фильтр Обесцвечивание, превращающий цветную картинку в чёрно-белую.
Содержание:
1. Введение
2. Изображения. Простая трансформация
3. Негатив, извлечение и инверсия каналов
4. Обесцвечивание
5. Цветовые модели
6. Яркость, насыщенность, контрастность, гамма-коррекция
7. Гистограмма
8. Масштабирование изображения
9. Размытие
10. Свёртка
Домашнее заданиеВ прошлой статье было задание сделать вывод каждого отдельного канала в виде оттенков серого. Также в статье я дал подсказку, что серый — объединение всех трёх цветов в равных пропорциях. Значит, нужно было просто продублировать яркость одного из каналов на остальные два:
- // Red
- for (let i = 0; i < dst.length; i++) {
- let r = src[i] & 0xFF;
- dst[i] = 0xFF000000 | (r << 16) | (r << 8) | r;
- }
- // Green
- for (let i = 0; i < dst.length; i++) {
- let g = (src[i] >> 8) & 0xFF;
- dst[i] = 0xFF000000 | (g << 16) | (g << 8) | g;
- }
- // Blue
- for (let i = 0; i < dst.length; i++) {
- let b = (src[i] >> 16) & 0xFF;
- dst[i] = 0xFF000000 | (b << 16) | (b << 8) | b;
- }
https://codepen.io/aNNiMON/pen/moyqaE?editors=1010

Обесцвечивание (desaturate)Суть этого фильтра заключается в том, чтобы превратить цветную картинку в чёрно-белую таким образом, чтобы не потерять изначальную яркость. Сразу же напрашивается одно из возможных решений: взять сумму цветовых компонент и разделить на 3:
- for (let i = 0; i < dst.length; i++) {
- let r = src[i] & 0xFF;
- let g = (src[i] >> 8) & 0xFF;
- let b = (src[i] >> 16) & 0xFF;
- let gray = (r + g + b) / 3;
- dst[i] = 0xFF000000 | (gray << 16) | (gray << 8) | gray;
- }
Не все цвета человек воспринимает равномерно (нет, я не про различные нарушения работы зрения). К зелёному цвету наши глаза более восприимчивы, к синему наименее чувствительны. Это значит, что применяя фильтр Обесцвечивание, если мы хотим точнее передать яркость картинки, нужно учитывать степень чувствительности к каждой компоненте цвета.
Для телевизионного формата NTSC:
- for (let i = 0; i < dst.length; i++) {
- let r = src[i] & 0xFF;
- let g = (src[i] >> 8) & 0xFF;
- let b = (src[i] >> 16) & 0xFF;
- let gray = (r * 0.3 + g * 0.59 + b * 0.11);
- dst[i] = (src[i] & 0xFF000000) | (gray << 16) | (gray << 8) | gray;
- }
Для цветового профиля sRGB (профили — тема другой статьи):
- for (let i = 0; i < dst.length; i++) {
- let r = src[i] & 0xFF;
- let g = (src[i] >> 8) & 0xFF;
- let b = (src[i] >> 16) & 0xFF;
- let gray = (r * 0.2126 + g * 0.7152 + b * 0.0722);
- dst[i] = (src[i] & 0xFF000000) | (gray << 16) | (gray << 8) | gray;
- }

Первый вариант со средним арифметическим заметно темнее остальных, а вот различие между коэффициентами для NTSC и sRGB так сходу не заметить — в sRGB щёчки и язык Пикачу стали темнее, что, как мне кажется, больше соответствует яркости цветного изображения.

Вот ещё несколько примеров:


Какой из алгоритмов применять, зависит от задачи. Если нужна скорость, то быстрее будет взять среднее арифметическое. Если нужно качество, то берём последний, так как он больше подходит для компьютерной среды.
See the Pen Image processing 4.1. Desaturate by aNNiMON (@aNNiMON) on CodePen.
5. Цветовые модели