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

от
Работа с графикой    image processing, обработка изображений, javascript, js, desaturate, обесцвечивание

4.png
На очереди фильтр Обесцвечивание, превращающий цветную картинку в чёрно-белую.

Содержание:
  1. Введение
  2. Изображения. Простая трансформация
  3. Негатив, извлечение и инверсия каналов
  4. Обесцвечивание
  5. Цветовые модели
  6. Яркость, насыщенность, контрастность, гамма-коррекция
  7. Гистограмма
  8. Масштабирование изображения
  9. Размытие
  10. Свёртка

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


Обесцвечивание (desaturate)Суть этого фильтра заключается в том, чтобы превратить цветную картинку в чёрно-белую таким образом, чтобы не потерять изначальную яркость. Сразу же напрашивается одно из возможных решений: взять сумму цветовых компонент и разделить на 3:
  1. for (let i = 0; i < dst.length; i++) {
  2.   let r = src[i] & 0xFF;
  3.   let g = (src[i] >> 8) & 0xFF;
  4.   let b = (src[i] >> 16) & 0xFF;
  5.   let gray = (r + g + b) / 3;
  6.   dst[i] = 0xFF000000 | (gray << 16) | (gray << 8) | gray;
  7. }
Это, пожалуй, самый быстрый способ, но для человеческого восприятия он не очень подходит. И вот почему.

Не все цвета человек воспринимает равномерно (нет, я не про различные нарушения работы зрения). К зелёному цвету наши глаза более восприимчивы, к синему наименее чувствительны. Это значит, что применяя фильтр Обесцвечивание, если мы хотим точнее передать яркость картинки, нужно учитывать степень чувствительности к каждой компоненте цвета.

Для телевизионного формата NTSC:

  1. for (let i = 0; i < dst.length; i++) {
  2.   let r = src[i] & 0xFF;
  3.   let g = (src[i] >> 8) & 0xFF;
  4.   let b = (src[i] >> 16) & 0xFF;
  5.   let gray = (r * 0.3 + g * 0.59 + b * 0.11);
  6.   dst[i] = (src[i] & 0xFF000000) | (gray << 16) | (gray << 8) | gray;
  7. }

Для цветового профиля sRGB (профили — тема другой статьи):

  1. for (let i = 0; i < dst.length; i++) {
  2.   let r = src[i] & 0xFF;
  3.   let g = (src[i] >> 8) & 0xFF;
  4.   let b = (src[i] >> 16) & 0xFF;
  5.   let gray = (r * 0.2126 + g * 0.7152 + b * 0.0722);
  6.   dst[i] = (src[i] & 0xFF000000) | (gray << 16) | (gray << 8) | gray;
  7. }
grayscale_alg.png
Первый вариант со средним арифметическим заметно темнее остальных, а вот различие между коэффициентами для NTSC и sRGB так сходу не заметить — в sRGB щёчки и язык Пикачу стали темнее, что, как мне кажется, больше соответствует яркости цветного изображения.
grayscale.gif

Вот ещё несколько примеров:
grayscale2.gif grayscale3.gif
Какой из алгоритмов применять, зависит от задачи. Если нужна скорость, то быстрее будет взять среднее арифметическое. Если нужно качество, то берём последний, так как он больше подходит для компьютерной среды.

See the Pen Image processing 4.1. Desaturate by aNNiMON (@aNNiMON) on CodePen.

  • +5
  • views 4948