Archivi categoria: Immagini

Articoli, esempi ed esercizi sull’utilizzo di immagini in Processing.

Glitch di un’immagine con Processing

Breve introduzione alla Glitch Art

Per Glitch Art intendiamo la pratica sfruttare e/o introdurre in un’opera degli errori analogici o digitali per fini estetici. Nel mondo dell’analogico si interviene direttamente sugli apparecchi elettronici che registrano o riproducono l’opera. Per quanto riguarda il digitale, invece, tale manipolazione è resa ancora più semplice dagli strumenti tecnologici di cui disponiamo che ci consentono di accedere direttamente ai dati e di farne ciò che vogliamo. Questa pratica è anche chiamata databending.

Non si tratta di un movimento artistico nato, come si potrebbe pensare, negli ultimi anni con la democratizzazione della tecnologia e degli strumenti creativi ma, al contrario, affonda le sue radici nella prima metà del novecento: il filmato qui sotto A Colour Box dell’artista neo-zelandese Len Lye e datato 1935 è considerato uno dei primi esempi di glitch art.

Benché negli ultimi anni abbia riscosso un sempre maggiore successo, questo movimento artistico non è mai diventato mainstream. Possiamo sfruttare quello che abbiamo imparato a fare con Processing per fare i nostri esperimenti di glitch art e per studiare qualche nuova funzione del linguaggio.

Usiamo Processing per creare un effetto glitch

Lo scopo di oggi è prendere una foto e glitcharla. Nell’esempio proposto non andrò a introdurre un vero e proprio errore nell’immagine ma andrò a manipolare direttamente l’array di pixel per ricreare un effetto artistico simil-glitch. Il motivo è presto detto: per ottenere un effetto artistico godibile è meglio evitare di usare variabile non controllabili.

Glitch fotografia Processing
Immagine di partenza

L’immagine di partenza non è stata scelta in modo casuale: dal momento che useremo la funzione brightness(), che restituisce il valore della luminosità di un colore, la foto presenta un forte contrasto tra lo sfondo e le sagome nere e la parte centrale illuminata dalle fiamme.

/*
 * Glitch di un'immagine con Processing
 * Federico Pepe, 23.09.2017
 * http://blog.federicopepe.com/processing
 */

PImage img;
int minThreshold = 200;
int maxThreshold = 255;

void setup() {
  size(1, 1);
  surface.setResizable(true);
  img = loadImage("fire.jpg");
  surface.setSize(img.width, img.height);  

  noStroke();
  noLoop();
}

void draw() {
  background(255);
  image(img, 0, 0);
  img.loadPixels();

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

      if (brightness(img.pixels[y*width+x]) > minThreshold && brightness(img.pixels[y*width+x]) < maxThreshold) {
        color c = get(x, y);
        stroke(c);
        line(x, y, x, y+2);
        noFill();
      }
    }
  }
}

void mousePressed() {
  save("export/export.png");
}

Rispetto al codice visto negli ultimi post, ho aggiunto due variabili di soglia minTresholdmaxThreshold e, all'interno del classico ciclo for che va ad analizzare ogni singolo pixel dell'immagine, introdotto un semplice controllo condizionale che, qualora il risultato sua true disegna una linea verticale di 2 pixel dello stesso colore del pixel analizzato.

Glitch immagine esportata

Il risultato è sicuramente particolare e d'effetto. Potete provare a giocare un po' con i parametri di soglia oppure sostituendo il disegno della linea con un quadrato. Un'altra possibilità potrebbe essere quella di non utilizzare il parametro della luminosità ma andare a scegliere la saturazione o il colore stesso.

Glitch with squares
Lo stesso identico codice di prima ma, in questo esempio, ho sostituito line() con rect()

Una palette di colori da un’immagine

Nelle ultime settimane mi è capitato di fare diversi lavori di grafica – che non è propriamente il campo in cui sono più ferrato – e mi sono sempre trovato in crisi nella scelta della palette di colori più adatta a quello che stavo facendo.

Mentre ero alla ricerca della giusta combinazioni di colori su siti come Coolors e Adobe Color mi è tornata in mente una frase di Joshua Davis detta in uno dei suoi video (non cito testualmente, vado a memoria): le palette di colori che funzionano meglio sono quelle legate a immagini a cui siamo abituati, come un tramonto sul mare oppure le gradazioni di verde in un bosco in montagna. Ecco perché estrarre i colori da una foto è uno dei modi migliori per trovare delle combinazioni che funzionano.

Da qui l’illuminazione: perché non creare uno sketch in Processing per aiutarmi?

Leggere i colori da un’immagine

Partiamo riciclando il codice visto nell’ultimo post: Modificare la dimensione della finestra in base all’immagine caricata. Creiamo un oggetto di tipo PImage e facciamo in mondo che la finestra si ridimensioni automaticamente al caricamento del file. Nel codice sotto riportato noterete un +50 pixel nell’altezza che servirà come spazio per rappresentare i colori della palette in tempo reale.

Per questo sketch ho deciso di utilizzare l’immagine di un prato fiorito:
Leggere i colori da un'immagine con Processing

Ricordatevi di inserire l’immagine nella cartella /data come spiegato qui.

/*
 * Una palette di colori da un'immagine
 * Federico Pepe, 28.08.2017
 * http://blog.federicopepe.com/processing
*/

PImage img;

void setup() {
  size(1, 1);
  surface.setResizable(true);
  img = loadImage("flowers.jpg");
  surface.setSize(img.width, img.height+50); 
}

void draw() {
}

Creiamo due variabili di tipo integer: la prima, numPoints indica il numero di colori di cui sarà composta la nostra palette. Ho deciso di chiamarla points perché prenderemo i dati colori da dei punti nell’immagine. La seconda pointX la utilizzeremo come riferimento per calcolare il valore di x dei punti scelti che, ai fini di questo esempio, saranno equidistanti.

Aggiungiamo un po’ di codice nel ciclo draw(): mostriamo l’immagine come sfondo e carichiamo l’array di pixel. Come anticipato, assegniamo a pointX il valore della larghezza della foto caricata diviso il numero di punti.

Utilizziamo il classico ciclo for per trovare i colori dei punti. Creo un minimo di interfaccia per aiutarmi nel lavoro: intorno ai punti di riferimento disegno un cerchio.

/*
 * Una palette di colori da un'immagine
 * Federico Pepe, 28.08.2017
 * http://blog.federicopepe.com/processing
*/

PImage img;
// Numero di "punti" della nostra palette
int numPoints = 5;
// Valore x di riferimento
int pointX;

void setup() {
  size(1, 1);
  surface.setResizable(true);
  img = loadImage("flowers.jpg");
  surface.setSize(img.width, img.height+50); 
}

void draw() {
  image(img, 0, 0);
  img.loadPixels();
  pointX = img.width / numPoints;
  
  for(int i = 0; i <= numPoints-1; i++) {
    int x = pointX*i;
    int y = mouseY;
    
    if(mouseY <= 0 || mouseY >= img.height) {
      y = img.height/2;
    }
    stroke(255);
    noFill();
    ellipse(x, y, 25, 25);
    color c = img.pixels[x + y * img.width];
    println(c);
  }
  
}

Avendo deciso di lasciare il valore di y variabile e dipendente dalla posizione y del mouse, ho deciso di inserire un controllo condizionale per verificare di non uscire dall’immagine stessa causando un errore Array out of bounds sull’array di pixel.

Nella console vengono mostrati i valori di c ma non sono utilizzabili in alcun programma di grafica perché non sono i classici valori HEX, RGB o HSB.

Mostriamo la palette di colori

Sfrutto i 50 pixel di margine di abbondanza rispetto alla foto per mostrare in tempo reale i colori dei pixel di riferimento. Farlo è molto semplice: creo dei rettangoli con, come fill, il colore estratto in precedenza. Sfrutto questo passaggio per centrare i punti di analisi rispetto alla larghezza della finestra e ai rettangoli in basso.

Palette di colori da immagine

/*
 * Una palette di colori da un'immagine
 * Federico Pepe, 28.08.2017
 * http://blog.federicopepe.com/processing
 */

PImage img;
// Numero di "punti" della nostra palette
int numPoints = 5;
// Valore x di riferimento
int pointX;

void setup() {
  size(1, 1);
  surface.setResizable(true);
  img = loadImage("flowers.jpg");
  surface.setSize(img.width, img.height+50);
}

void draw() {
  image(img, 0, 0);
  img.loadPixels();
  pointX = img.width / numPoints;

  for (int i = 0; i <= numPoints-1; i++) {
    int x = pointX/2+pointX*i;
    int y = mouseY;

    if (mouseY <= 0 || mouseY >= img.height) {
      y = img.height/2;
    }

    stroke(255);
    noFill();
    ellipse(x, y, 25, 25);

    color c = img.pixels[x + y * img.width];

    fill(c);
    rect(pointX*i, img.height, pointX, 50);
  }
}

Otteniamo i valori HEX dei colori

A questo punto il programma funziona nel modo corretto, non ci resta che fare dei piccoli miglioramenti all’interfaccia: grazie alla funzione hex(value, digits) possiamo ottenere il valore esadecimale del colore. Come indicato nel reference, il numero viene rappresentato con un massimo di 8 caratteri ma possiamo ottenerne 6, come siamo abituati, aggiungendo indicando il secondo parametro alla funzione.

Palette colori GIF

Ecco qui il codice:

/*
 * Una palette di colori da un'immagine
 * Federico Pepe, 28.08.2017
 * http://blog.federicopepe.com/processing
 */

PImage img;
// Numero di "punti" della nostra palette
int numPoints = 5;
// Valore x di riferimento
int pointX;

void setup() {
  size(1, 1);
  surface.setResizable(true);
  img = loadImage("flowers.jpg");
  surface.setSize(img.width, img.height+50);
}

void draw() {
  image(img, 0, 0);
  img.loadPixels();
  pointX = img.width / numPoints;

  for (int i = 0; i <= numPoints-1; i++) {
    int x = pointX/2+pointX*i;
    int y = mouseY;

    if (mouseY <= 0 || mouseY >= img.height) {
      y = img.height/2;
    }

    stroke(255);
    noFill();
    ellipse(x, y, 25, 25);

    color c = img.pixels[x + y * img.width];

    fill(c);
    rect(pointX*i, img.height, pointX, 50);
    
    fill(255);
    textAlign(CENTER);
    text("#"+hex(c,6), x, img.height+30);
    
  }
}

Salvare la palette di colori in un file di testo

Siamo quasi alla fine del nostro lavoro: l’unico passaggio che ci manca è salvare la palette di colori in un file .txt in modo da poterli usare a nostro piacimento anche in programmi esterni.

Aggiungiamo qualche riga di codice: creiamo una nuova variabile di tipo StringList il cui contenuto verrà inizializzato a ogni ciclo di draw con la funzione .clear();

Alla pressione del tasto del mouse, verrà salvato il file chiamato palette.txt. La conversione della StringList in array è necessaria perché la funzione saveStrings si aspetta come secondo parametro un array e non una lista.

/*
 * Una palette di colori da un'immagine
 * Federico Pepe, 28.08.2017
 * http://blog.federicopepe.com/processing
 */

PImage img;
// Numero di "punti" della nostra palette
int numPoints = 5;
// Valore x di riferimento
int pointX;
// Salviamo i valori HEX in una lista di stringhe
StringList palette = new StringList();

void setup() {
  size(1, 1);
  surface.setResizable(true);
  img = loadImage("flowers.jpg");
  surface.setSize(img.width, img.height+50);
}

void draw() {
  image(img, 0, 0);
  
  img.loadPixels();
  pointX = img.width / numPoints;
  
  palette.clear();
  
  for (int i = 0; i <= numPoints-1; i++) {
    int x = pointX/2+pointX*i;
    int y = mouseY;

    if (mouseY <= 0 || mouseY >= img.height) {
      y = img.height/2;
    }

    stroke(255);
    noFill();
    ellipse(x, y, 25, 25);

    color c = img.pixels[x + y * img.width];

    fill(c);
    rect(pointX*i, img.height, pointX, 50);
    
    fill(255);
    textAlign(CENTER);
    text("#"+hex(c,6), x, img.height+30);
    // Aggiungiamo il colore come stringa all'array
    palette.append("#"+hex(c,6));
    
  }
}

void mousePressed() {
  saveStrings("palette.txt", palette.array());
}

Modificare la dimensione della finestra in base all’immagine caricata

Negli esempi che abbiamo visto fino ad ora relativi all’uso delle immagini all’interno di Processing abbiamo sempre impostato a priori la grandezza della finestra del nostro sketch in base alla dimensione dell’immagine caricata.

È un metodo molto semplice ma decisamente scomodo se dobbiamo utilizzare tante immagini diverse oppure se vogliamo rendere il nostro programma universale.

Nella versione precedente di Processing era possibile utilizzare delle variabili all’interno della funzione size(). Questa opzione, però, benché fortemente osteggiata dagli sviluppatori fin dal 2009 è stata definitivamente rimossa con l’aggiornamento a Processing 3 perché impediva miglioramenti in termini di performance, velocità e compatibilità cross-platform.

Non disperate! Esiste una soluzione alternativa che, però, non è ben descritta nel reference del linguaggio. Ecco perché ho pensato fosse interessante scrivere un breve articolo a riguardo.

Ecco come fare:

  • All’interno di setup() è comunque necessario indicare una dimensione di partenza con la funzione size(). Per semplicità possiamo scrivere: size(1, 1);
  • Dopodiché aggiungiamo la seguente linea di codice: surface.setResizable(true); mi raccomando, fate attenzione alle maiuscole!
  • A questo punto, nel punto in cui abbiamo la necessità di reimpostare la dimensione è sufficiente scrivere: surface.setSize(larghezza, altezza); dove larghezza altezza sono i nostri parametri.

Ecco un esempio con un’immagine:

/*
 * Modificare la dimensione della finestra in base all'immagine caricata
 * Federico Pepe, 25.06.2017
 * http://blog.federicopepe.com/processing
*/

PImage img;

void setup() {
  size(1, 1);
  surface.setResizable(true);
  img = loadImage("immagine.jpg");
  surface.setSize(img.width, img.height);  
}

void draw() {
}

Per cambiare la dimensione a ogni click del mouse:

/*
 * Modificare la dimensione della finestra in base all'immagine caricata
 * Federico Pepe, 25.06.2017
 * http://blog.federicopepe.com/processing
*/

void setup() {
  size(100, 100);
  surface.setResizable(true);
}

void draw() {
}

void mousePressed() {
  surface.setSize(round(random(100, 500)), round(random(100, 500)));  
}

Processare le immagini in Processing

In questo post vedremo come unire le nozioni che abbiamo appreso relativamente al caricamento e all’ utilizzo delle immagini in Processing e alle possibilità creative che abbiamo a disposizione lavorando sui singoli pixel per imparare a processore le immagini a nostro piacimento.

Riprendiamo l’immagine del gatto che abbiamo già usato in precedenza e carichiamola all’interno di un nuovo sketch. Invece di utilizzare la funzione image() per mostrarla nella finestra, questa volta andremo a caricare tutti i pixel che compongono l’immagine.

/*
 * Processare le immagini in Processing
 * Federico Pepe, 29.04.2017
 * http://blog.federicopepe.com/processing
 */

PImage img;

void setup() {
  size(640, 536);
  img = loadImage("cat-300572_640.jpg");
}

void draw() {
  // Carichiamo i pixel della finestra
  loadPixels();
  // Carichiamo i pixel dell'immagine
  img.loadPixels();
  for(int y = 0; y < height; y++) {
    for(int x = 0; x < width; x++) {
      int pos = x + y * width;
      
      float r = red(img.pixels[pos]);
      float g = green(img.pixels[pos]);
      float b = blue(img.pixels[pos]);
      
      pixels[pos] = color(r, g, b);
    }
  }
  updatePixels();
}

Analizziamo velocemente il codice qui sopra concentrandoci, in particolare, sulle cose che non abbiamo mai visto prima: come indicato nei commenti, oltre a caricare i pixel della finestra dobbiamo caricare anche quelli relativi all'immagine. Per farlo utilizziamo img.loadPixels(); dove img fa riferimento al nome della variabile che abbiamo dichiarato all'inizio del programma.

Con due loop for andiamo a caricare ogni singolo pixel e, utilizzando le funzioni red()green()blue() otteniamo i valori R, G e B. Attraverso pixels[pos] = color(r, g, b); assegniamo i valori al pixel sullo schermo.

Il nostro programma, in pratica, lavora come segue: che valori di rossoverdeblu ha il pixel nell'immagine che ha coordinate x = 0, y = 0? Una volta ottenuti, applica quei valori al pixel con posizione x = 0 e y = 0 della finestra. Dopodiché, il ciclo for prosegue e analizzerà il pixel con coordinate x = 1 e y = 0 e avanti così.

Con l'updatePixel() finale aggiorniamo tutti i valori presenti nell'array della finestra. Il risultato finale sarà, dunque, vedere visualizzata sullo schermo l'immagine.

La domanda che sorge spontanea è: perché dovrei scrivere tutte queste righe di codice quando avrei potuto usare la funzione image() e risparmiare un sacco di fatica? La risposta è semplice: avendo accesso ai dati grezzi dell'immagine possiamo modificarli a nostro piacimento.

Proviamo, ad esempio, ad aggiungere la riga in grassetto al nostro codice:

r = constrain(r, 0, 100);
 
pixels[pos] = color(r, g, b);

Il risultato sarà che il valore del rosso non potrà avere un valore compreso tra 0 e 255 come previsto normalmente ma sarà limitato a valori compresi tra 0, 100 grazie alla funzione constrain.

Processare le immagini in Processing

Siamo riusciti a creare il nostro primo filtro personalizzato per le immagini. Ora possiamo dare sfogo alla nostra fantasia.

Facciamo un altro esperimento: rimpiazziamo l'ultima riga di codice che abbiamo aggiunto con quella seguente:

r = map(mouseX, 0, width, 0, 255);

Il rosso ora è controllato dalla posizione x del mouse.

Usare un solo ciclo for

Prima di concludere questo post ci tengo a fare una precisazione: volendo è possibile utilizzare un solo ciclo for per ottenere lo stesso effetto riducendo, così, le righe di codice del nostro programma:

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

Se decidiamo di intraprendere questa strada dobbiamo eliminare la variabile pos e sostituirla con i:

float r = red(img.pixels[i]);

Come avevamo discusso nel post relativo all'array di pixel la differenza tra i due approcci dipende se consideriamo l'immagine un array monodimensionale o bidimensionale.

Array di pixel: loadPixels() e updatePixels()

Avevo parlato di pixel in uno dei primissimi post su Processing in questo blog. Grazie alle competenze che abbiamo acquisito nelle ultime settimane, possiamo fare un ulteriore passo in avanti.

Le funzioni che abbiamo usato fino a oggi ci hanno permesso di disegnare linee e forme sullo schermo o, come visto di recente, di mostrare un’immagine. Queste funzioni che, all’apparenza, sembrano eseguire operazioni molto semplici, in realtà nascondono un principio molto complesso: stabilire se ciascun pixel sullo schermo deve essere accesso o spento e, se acceso, il colore che deve rappresentare.

Array di pixel

Siamo abituati a pensare ai pixel come ad una griglia sullo schermo; quindi ad un array bidimensionale.

In Processing, però, il valore di ciascuno di essi viene salvato in un classico array monodimensionale.

È possibile accedere a queste informazioni e modificarle a nostro piacimento? Ovviamente si, utilizzando le funzioni loadPixels()updatePixels().

loadPixels() e updatePixels()

Con la prima funzione stiamo dicendo a Processing che intendiamo lavorare sull’array di pixel e che, quindi, deve caricarlo in una variabile. updatePixels(), invece, la usiamo quando abbiamo apportato tutte le modifiche e vogliamo che il programma aggiorni le informazioni sullo schermo.

Facciamo un esempio pratico:

/*
 * Array di pixel: loadPixels() e updatePixels()
 * Federico Pepe, 26.03.2017
 * http://blog.federicopepe.com/processing
 */

void setup() {
  size(500, 500);
  loadPixels();  
 
  for (int i = 0; i < pixels.length; i++) {
    float rand = random(255);
    color c = color(0, 0, rand);
    pixels[i] = c;
  }
  
  updatePixels();
}

In questo programma carichiamo tutti i valori di una finestra di 500x500 pixel. Con il ciclo for partiamo dal primo valore presente nel nostro array e arriviamo fino all'ultimo, identificato, per comodità, dalla funzione pixel.length e assegniamo a ciascuno un colore casuale float rand = random(255); nella scala dei blu color c = color(0, 0, rand);.

Assegniamo il nuovo colore al pixel pixels[i] = c; e, una volta usciti dal loop, chiediamo a Processing di aggiornare e mostrare tutte le modifiche che abbiamo effettuato updatePixels();.

Il risultato sarà il seguente:

Pixel Array Blu

Modificare un pixel conoscendone la posizione x e y

A questo punto, immagino, potrà esservi sorta spontaneamente una domanda: è possibile accedere a un determinato pixel conoscendone la posizione x e y all'interno della finestra come siamo stati abituati a fare fino a ora?

Riprendendo l'immagine della griglia più sopra, proviamo a capire come accedere al pixel 19: la sua posizione è x = 5, y = 2. A questo punto è importante ricordare che, per questi valori, partiamo a contare da 0 mentre, per la larghezza della finestra, da 1.

Ora dobbiamo sommare il valore di x al valore di y moltiplicato alla larghezza.

La formula è, dunque: x + (y * width)

5 + (2 * 7) = 19

I conti tornano!

Proviamo a modificare il codice di prima come segue:

/*
 * Array di pixel: loadPixels() e updatePixels()
 * Federico Pepe, 26.03.2017
 * http://blog.federicopepe.com/processing
 */

void setup() {
  size(500, 500);
  loadPixels();  
  color c;
  for (int x = 0; x < width; x++) {
    for (int y = 0; y < height; y++) {
      float rand = random(255);
      int pos = x + y * width;
      if(x % 2 == 0) {
        c = color(0, 0, rand);
      } else {
        c = color(rand, 0, 0);
      }
      pixels[pos] = c;
    }
  }
  
  updatePixels();
}

Zoomando l'immagine noterete che una riga è rossa e una riga è, invece, blu. Il prossimo passo sarà applicare quello che abbiamo appena imparato su un'immagine. Provate a pensare a tutte le operazioni che potremmo compiere andando a modificare individualmente ogni singolo pixel.