Обробка зображень (зображення як масив пікселів)


Кожне растрове зображення складається із пікселів. Колір кожного пікселя визначається комбінацією значень кольорів, котрі його утворюють. Ці числові значення можна обробляти програмно, змінюючи зовнішній вигляд зображення.


Функція img.get(x, y) дозволяє отримати код кольору пікселя у координатах х,у.

Функція set(x, y, color(255-red(c), 255-green(c), 255-blue(c))); дозволяє встановити колір для пікселя.

 

Ефект пікселяції

файл

Ефект полягає у тому, що ми встановлюємо масштаб нового пікселя (у прикладі - 4), і перебираємо кожен 4 піксель по горизонталі та по вертикалі. Взявши колір цього пікселя (img.get(x,y)) ми малюємо прямокутник розміром 4,4 цього кольору. Таким чином замість зображення кожного пікселя певного кольору, ми малюємо квадратики, зафарбовані кольором першого пікселя їх лівого верхнього кутика.


PImage img;
int blok, xPlus,yPlus;
void setup() {
  img=loadImage("lena.jpg");
  size(500,500);
//кількість блоків у рядку та стовпці
	blok = 50;
//масштабування пікселя відповідно до розмірів початкового зображення
  xPlus = width/blok;
  yPlus = height/blok;
}
void draw(){
//для кожного пікселя по рядках
  for (int y=0; y< img.height; y+=yPlus) {
//для кожного пікселя по стовпцях
    for (int x=0; x< img.width; x+=xPlus) {
//колір зафарбування встановити кольором першого пікселя цієї області
      fill(img.get(x, y));
//намалювати прямокутник розміру нового пікселя у координатах х,у
      rect(x, y, xPlus, yPlus);
    }
  }
}
   

Замість квадратів, зображення можна перетворити на сукупність кружечків:

noStroke();

ellipse(x, y, xPlus, yPlus);


Інший результат отримаємо, якщо обиратимемо не всі пікселі методично, а випадковим чином

PImage img;
void setup() {
  img = loadImage("lena.jpg");
  size(500,500);
}
void draw(){
  image(img, 0,0);
  noStroke();
//завантажуємо всі пікселі у масив pixels, всі пікселі послідовно в один рядок
  loadPixels();
  // Утворення 10000 кружечків
  for (int i=0; i< 10000; i++) {
//беремо випадковий піксель зображення
    int x = (int)random(width);
    int y = (int)random(height);
// змінна k зберігає номер пікселя 
    int k = x + width*y;
// змінна с містить колір пікселя k
    color c = pixels[k];
//вказуємо колір заливки
    fill(c);
//малюємо кружечок на місці вибраного пікселя
    ellipse(x, y, 7, 7);
  }
}
 

 

Негатив

Ефект полягає у тому, що колір кожного пікселя змінюється на протилежний за шкалою від 0 до 255.

PImage img;
void setup() {
  img=loadImage("lena.jpg");
  size(500,500);
}
void draw(){
  image (img,0,0);
//для кожного пікселя по рядках
  for (int y=0; y < img.height; y++) {
//для кожного пікселя по стовпцях
    for (int x=0; x < img.width; x++) {
      //змінна с типу color зберігає значення коду кольору пікселя
      color c = img.get(x, y);
      //функції color(255-red(c) змінюють значення кодів RGB на протилежні
      set(x, y, color(255-red(c), 255-green(c), 255-blue(c)));
    }
  }
}
 

Можна застосувати інші математичні операції, наприклад обернути червоні та сині тони, але залишити незмінними зелені:

set(x, y, color(255-red(c), green(c), 255-blue(c)));

 

set(x, y, color(255-red(c), 255-green(c), 0));

set(x, y, color(red(c), 255-green(c), 255-blue(c)));

 

Чорно-біле зображення

Для переведення зображення у чорно-біле слід скористатись формулою переведення кольору: (0.3 * червоний) + (0.59 * зелений) + (0.11 * синій). Бачимо, що колір має виражатись одним числом (сумою колірних компонентів, а не окремими значеннями). Тож для такого ефекту нам потрібно завантажити пікселі, а точніше їх кольори у масив/список.

Пікселі можна завантажити у масив та виконувати над ними операції, як із числовими масивами. Значеннями є колірні коди пікселів зображення. Якщо у зображенні пікселі утворювали прямокутник, то у списку - всі вони розташовані послідовно:



Так, піксель з номером 16 знаходиться у 2 стовпці 4 рядка, тобто його x=1, y=3. А піксель номер 13 має x=3, y=2.


13 = 2*5 +3, де 5 - ширина зображення, 3 та 2 - координати пікселя.

16 = 3*5 +1, де 5 - ширина зображення, 1 та 3 - координати пікселя.


Після виконання необхідних операцій пікселі зображення оновлюються командою updatePixels();

PImage img;
void setup() {
  img=loadImage("lena.jpg");
  size(500,500);
}
void draw(){
  image (img,0,0);
//завантажуємо пікселі у масив
  loadPixels();
//перебираємо усі пікселі по рядках та стовпцях
	for (int x = 0; x < img.width; x++) {
		for (int y = 0; y < img.height; y++) {
                       //змінна і визначає номер поточного пікселя у списку
			i = x + y*img.width;
                        //змінна с містить значення поточного пікселя
			color c = pixels[i];
                        //корекція кольору для пікселя
			pixels[i] = color(red(c)*0.3+green(c)*0.59+blue(c)*0.11);
  	        }
	}
//оновлення пікселів зображення відповідно до змін у масиві
  updatePixels();
}



Маска

Виділення областей може бути рутинною задачею, наприклад при обробці відео. Для швидшого її виконання можна застосовувати програмування та команди маскування (накладання зображення та відсікання усього, що виходить за його межі).

Маска - це зображення у чорно-білих тонах, яке при накладанні на оригінальне зображення приховує області, зображені чорним і показує області, зображені білим.

файл

void setup() {
  PImage img = loadImage("prinzipal.jpg");
  size(650, 500);
  PImage msk = createImage(img.width, img.height, RGB); //маскою буде зображення такого ж розміру, як початкове
  background(0,255,0); // колір, який проявиться замість маски
  img.loadPixels();
  msk.loadPixels();
  for (int y=0; y< img.height; y++){
    for (int x=0; x< img.width; x++){
      color c = img.pixels[y*img.width+x];
      if (red(c) + green(c) + blue(c) > 600 || blue(c) > 190) {
// вказуємо, які пікселі потрібно буде приховати: білі хмари та блакитне небо - замінити на чорний колір у масці
        msk.pixels[y*img.width+x] = 0;}
      else { //решту кольорів замінити на білий колір у масці
        msk.pixels[y*img.width+x] = 255;}
     }
  }
//оновлюємо пікселі
  msk.updatePixels();
//виводимо початкове зображення
  image(img, 0, 0);
//застосовуємо маску (вона містить пікселі чорного та білого кольору, які приховують або показують початкове зображення відповідно)
  img.mask(msk);
//виводимо кінцеве зображення поряд
  image(img, img.width, 0);
}

 

Накладання зображень

файл

void setup() {
  PImage src = loadImage("woods2.jpg"); // зображення, яке накладається
  PImage dest = loadImage("lena.jpg"); // тло - зверніть увагу, що розмір цього зображення має бути таким же, як розмір робочої області!- у даному прикладі 500 на 500
  size(500,500);
  background(dest); // завантаження тла
//команда накладання
  blend(src, 0, 0, src.width, src.height, 0, 0, dest.width, dest.height, LIGHTEST);
}
 

 Варіанти накладання: BLEND, ADD, SUBTRACT, DARKEST, LIGHTESTDIFFERENCE, EXCLUSION, MULTIPLY, SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, and BURN.

 

Градієнт

Можна створювати градієнт на основі масиву пікселів.

void setup() {

  size(400, 400);

  loadPixels();

  for (int i=0; i<pixels.length; i++) {

//формула відтінку пікселя

    float gray = map(i, 0, pixels.length-1, 0, 255);

    pixels[i] = color(gray);

  }

updatePixels();

}

 

float gray = map(i, 0, pixels.length-1, 255, 0);


    pixels[i] = color(gray*2);

Радіальний градієнт

void setup() {

  float distance, r, g, b;

  size(400, 400);

  loadPixels();

  // доступ до пікселів

  for (int y=0; y<height; y++) {

    for (int x=0; x<width; x++) {

  // розрахунок відстані до центру поля

      distance = dist(x, y, width/2, height/2);

      r = map(distance, 0, width/2, 20, 200);

      g = map(distance, 0, width/2, 20, 100);

      b = map(distance, 0, width/2, 140, 0);

//формування кольору на основі відстані до центру

      pixels[y*width+x] = color(r, g, b);

     }

   }

  updatePixels();

}


 

Сепія

Ефект тонування зображення у відтінки коричневого.


void setup() {

  PImage img=loadImage("1.png");

  size(500,500);

  image (img,0,0);

//створюється палетка переведення кольорів

  color[] palette = new color[256];

//зміна констант змінює відтінок сепії. ці числа - найсвітліші тони кольорів у новій палетці

  int r = 255;

  int g = 240;

  int b = 192;

  for (int i=0; i<palette.length; i++) {

    palette[i] = color(r*i/255, g*i/255, b*i/255);

  }

//завантажуємо пікселі у масив

  loadPixels();

//перебираємо усі пікселі

  for (int i = 0; i < pixels.length; i++) {

//змінна с містить значення поточного пікселя

    color c = pixels[i];

//змінна gray перетворює значення поточного пікселя за формулою

    float gray = red(c)*0.3+green(c)*0.59+blue(c)*0.11;

//колір поточного пікселя визначається як значення масиву palette

    pixels[i] = palette[int(gray)];

  }

//оновлення пікселів зображення відповідно до змін у масиві

  updatePixels();

}


 

Фільтри

Фільтри застосовуються до зображення, змінюючи всі його пікселі: 

GRAY переведення кожного пікселя у градації чірого

INVERT інверсія кольору кожного пікселя

OPAQUE альфа-канал кожного пікселя у повністю непрозорий

THRESHOLD переводить кожен піксель у чорний чи білий, залежно від встановленого порогового значення 

POSTERIZE обмежує кожен канал кожного пікселя до встановленої кількості кольорів 

BLUR виконує Гаусове розмиття зображення відповідно до встановленого параметра 

DILATE збільшує світлі області зображення 

ERODE зменшує світлі області зображення

Остання зміна: п'ятниця 17 квітня 2020 5:09