Обработка изображений 3. Негатив, извлечение и инверсия каналов

от
Работа с графикой    image processing, обработка изображений, javascript, js, инверсия, негатив, negate, invert, extract channel

3.png
В этой статье разделяем изображение на цветовые компоненты, делаем эффект Негатив и инвертируем каналы по отдельности. Наконец-то будет манипуляция с RGB-компонентами!

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


Домашнее заданиеВернёмся к домашнему заданию, которое я дал в прошлой статье. Необходимо было реализовать отражение одновременно по горизонтали и по вертикали. Вот решение:
  1. for (let i = 0; i < dst.length; i++) {
  2.   dst[i] = src[dst.length - i - 1];
  3. }
Да, это просто реверс массива, но именно он в контексте изображения способен отразить пиксели по двум осям сразу. Вот наглядный пример:
https://codepen.io/aNNiMON/pen/EMYyVJ


Извлечение канала (extract channel)Разделим полноценное изображение на четыре отдельных изображения.
Первое будет содержать только красные компоненты, второе только зелёные, третье только синие:
  1. // Red
  2. for (let i = 0; i < dst.length; i++) {
  3.   let r = src[i] & 0xFF;
  4.   dst[i] = 0xFF000000 | 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 << 8);
  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);
  15. }
Прозрачность, которая, напомню, хранится в старших 8 битах, здесь принудительно задана в 255 (0xFF).
Можно было бы написать так:
  1. dst[i] = (255 << 24) | (b << 16);

Четвёртое изображение будет содержать только канал прозрачности, цвета будут чёрными:
  1. for (let i = 0; i < dst.length; i++) {
  2.   let a = (src[i] >> 24) & 0xFF;
  3.   dst[i] = (a << 24);
  4. }

Для демонстрации возьмём фотографию, которую любезно нам предоставил vl@volk:
sample1.jpg
Так как на фотографии преобладает зелёный цвет, то зелёный канал ярче и детальнее остальных.
Яркие светлые участки видны на всех каналах, потому что они близки к оттенкам серого, а серый — объединение всех трёх цветов в равных пропорциях.

Вот другой пример (оригинал фотографии):
sample2.jpg
Первое, что бросается в глаза — красная подсветка на колесе обозрения. Она отчётливо видна на красном канале, и практически незаметна на зелёном и синем. Только там, где красный переходит в более светлый, появляются и зелёный, и синий цвет.
Жёлтое отражение на дороге хорошо видно на красном и зелёном каналах, потому что смешение этих цветов даёт жёлтый цвет.

Домашнее задание
Сделайте так, чтобы красный, зелёный и синий каналы выводились в оттенках серого.

See the Pen Image processing 3.1. Channels by aNNiMON (@aNNiMON) on CodePen.



Негатив и инверсия каналовДля реализации эффекта Негатив, нужно инвертировать каналы R, G и B. Чёрный (0,0,0) должен превратиться в белый (255,255,255) и наоборот.
  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 a = (src[i] >> 24) & 0xFF;
  6.  
  7.   r = 255 - r;
  8.   g = 255 - g;
  9.   b = 255 - b;
  10.  
  11.   dst[i] = (src[i] & 0xFF000000) | (b << 16) | (g << 8) | r;
  12. }

Также реализуем возможность инвертировать отдельный канал, поставив соответствующую галочку.
  1. let invRed = isChecked('cbRed');
  2. let invGreen = isChecked('cbGreen');
  3. let invBlue = isChecked('cbBlue');
  4. let invAlpha = isChecked('cbAlpha');
  5. for (let i = 0; i < dst.length; i++) {
  6.   let r = src[i] & 0xFF;
  7.   let g = (src[i] >> 8) & 0xFF;
  8.   let b = (src[i] >> 16) & 0xFF;
  9.   let a = (src[i] >> 24) & 0xFF;
  10.  
  11.   if (invRed) r = 255 - r;
  12.   if (invGreen) g = 255 - g;
  13.   if (invBlue) b = 255 - b;
  14.   if (invAlpha) a = 255 - a;
  15.  
  16.   dst[i] = (a << 24) | (b << 16) | (g << 8) | r;
  17. }
  18.  
  19. function isChecked(el) {
  20.   return document.getElementById(el).checked;
  21. }

See the Pen Image processing 3.2. Negate filter by aNNiMON (@aNNiMON) on CodePen.

  • +8
  • views 7856