Archivi tag: Processing

Grafico a barre II

Nell’articolo precedente abbiamo cominciato a lavorare al nostro primo grafico a barre utilizzando come fonte i dati presenti all’interno di un file CSV.

In questo articolo andremo a completare la visualizzazione inserendo anche le temperature massime e dei riferimenti.

Array delle temperature massime

I dati relativi alle temperature massime sono già presenti dentro al file CSV. Come abbiamo già visto, per comodità inseriamo questi dati in un array.

Dove abbiamo dichiarato le variabili, aggiungiamo:

float tempMin[], tempMax[];

Inizializziamo l’array:

tempMax = new float[0];

Inseriamo i dati utilizzando lo stesso ciclo for per non appesantire troppo il programma

for (int i = 1; i < csv.getColumnCount(); i++) {
  tempMin = append(tempMin, csv.getFloat(0, i));
  tempMax = append(tempMax, csv.getFloat(3, i));
}

Grafico a barre delle temperature massime

Utilizzando sempre il codice che abbiamo già scritto, aggiungiamo nel ciclo for in cui andavamo a disegnare i rettangoli delle temperature minime, quelli relativi alle massime. Per differenziarle a livello visivo utilizzeremo il colore rosso.

fill(255, 100, 100);
rect(x, height - 50, 36, tempMax[i] * - 20);

Siccome i rettangoli rossi saranno più alti rispetto a quelli azzurri, assicuriamoci di disegnarli per primi.

Nel mio codice ho aggiunto anche un noStroke() per eliminare il bordo.

Grafico a barre con temperature massime

Aggiungiamo dei riferimenti

Quando si creano delle visualizzazioni di dati può essere utile dare dei riferimenti a chi sta osservando il nostro lavoro per aiutarli nella comprensione. L’immagine qui sopra presa singolarmente potrebbe rappresentare migliaia di cose differenti.

Aggiungere delle linee di riferimento è questione di un semplice ciclo for:

for (int j = 0; j <= 20; j++) {
  stroke(0, 30);
  line(30, height - 50 + (j * - 20), 470, height - 50 + (j * - 20));
}

Grafico a barre con riferimenti in Processing

Ora non ci resta che aggiungere il testo (aggiungiamo un paio di linee di codice al precedente ciclo for):

textAlign(RIGHT, CENTER);
for (int j = 0; j <= 20; j++) {
  fill(0, 127);
  text(j + "°", 25, height - 52 + (j * - 20));
  stroke(0, 30);
  line(30, height - 50 + (j * - 20), 470, height - 50 + (j * - 20));
}

Grafico a barre in Processing

Per rendere il grafico più leggibile facciamo dei piccoli miglioramenti: scriviamo il valore numerico ogni cinque gradi e schiariamo leggermente le altre linee:

textAlign(RIGHT, CENTER);

for (int j = 0; j <= 20; j++) {
  if (j % 5 == 0) {
    fill(0, 127);
    text(j + "°", 25, height - 52 + (j * - 20));
    stroke(0, 30);
  } else {
    stroke(0, 15);
  }
  line(30, height - 50 + (j * - 20), 470, height - 50 + (j * - 20));
}

Realizzare un grafico a barre in Processing

Non ci resta che aggiungere l'anno di riferimento in fondo al grafico: avendo aggiunto l'opzione header nella lettura del file CSV viene saltata completamente la prima riga che include proprio questo dato. Potremmo riscrivere il codice ma, per questa volta, adotteremo una soluzione più semplice anche se non proprio elegante. Per completezza includo tutto il codice del programma:

/*
 * Grafico a barre, 2
 * Federico Pepe, 29.04.2018
 * http://blog.federicopepe.com/processing
 */

Table csv;

float tempMin[], tempMax[];

void setup() {
  size(500, 500);
  background(255);
  noStroke();
  csv = loadTable("data.csv", "header");

  println("Numero righe: " + csv.getRowCount());
  println("Numero colonne: " + csv.getColumnCount());

  tempMin = new float[0];
  tempMax = new float[0];

  for (int i = 1; i < csv.getColumnCount(); i++) {
    tempMin = append(tempMin, csv.getFloat(0, i));
    tempMax = append(tempMax, csv.getFloat(3, i));
  }

  printArray(tempMin);

  println("Il valore minimo è: " + min(tempMin));
  println("Il valore massimo è: " + max(tempMin));

  noLoop();

  int x = 50;
  int year = 2008;

  for (int i = 0; i < tempMin.length; i++) {
    fill(255, 100, 100);
    rect(x, height - 50, 36, tempMax[i] * - 20);
    fill(100, 190, 255);
    rect(x, height - 50, 36, tempMin[i] * - 20);
    fill(0, 127);
    text(year, x + 2, height - 30);
    x += 40;
    year++;
  }

  textAlign(RIGHT, CENTER);

  for (int j = 0; j <= 20; j++) {
    if (j % 5 == 0) {
      fill(0, 127);
      text(j + "°", 25, height - 52 + (j * - 20));
      stroke(0, 30);
    } else {
      stroke(0, 15);
    }
    line(30, height - 50 + (j * - 20), 470, height - 50 + (j * - 20));
  }
}

void draw() {
}

Bar Graph in Processing

Coding Rescue #3 – Cambiare la tonalità di alcuni pixel

Negli ultimi giorni ho ricevuto diverse richieste di aiuto per problemi di codice in Processing; ecco la soluzione a uno dei problemi che mi sono stati posti.

Ho deciso di chiamare questo Coding Rescuecambiare la tonalità di alcuni pixel.

Il problema

Il quesito era piuttosto articolato:

  • Il programma deve caricare un’immagine dal disco.
  • Il programma decide se modificare o no il pixel in base ad un valore casuale.
  • Il processo di trasformazione viene controllato dal click  del mouse: un primo click avvia la trasformazione che, con un successivo click, viene messa in pausa. Un ulteriore click fa ripartire la trasformazione e così via.
  • I pixel devono essere modificati di continuo.
  • Il compito deve essere svolto mediante la funzione creaImmagine(), che accetta in ingresso un oggetto di tipo PImage e rende in uscita un oggetto di tipo PImage, che sarà l’immagine modificata come da specifiche, e la funzione calcolaPixel(), che accetta in ingresso un oggetto di tipo color e rende in uscita un oggetto di tipo color, che sarà il pixel modificato come da specifiche.
  • creaImmagine() crea una nuova immagine utilizzando su ciascun pixel la funzione calcolaPixel().
  • calcolaPixel() calcola il nuovo pixel nel seguente modo: siano r, g, b i livelli di canale di un pixel. Sia treshold un valore numerico posto inizialmente a 0.5. Sia r un valore casuale compreso tra 0 e 1, generato per ogni pixel. Se r>soglia, allora la funzione lascia il pixel inalterato, in caso contrario il programma calcola i nuovi valori rm, gm, bm (che definiscono il pixel
    modificato) nel seguente modo:

    • rm = 0.393∗r+0.769∗g+0.189∗b
    • gm = 0.349∗r+0.686∗g+0.168∗b
    • bm = 0.272∗r+0.534∗g+0.131∗b

    In ogni caso i valori finali di r, g, b devono essere valori leciti, cioè compresi tra 0 e 255.

  • Quando l’utente preme il tasto “+” la soglia viene incrementata di 0.1 mentre, con la pressione del tasto “-” la soglia viene decrementata di 0.1.

La soluzione


/*
 * Coding Rescue #3 - Cambiare la tonalità di alcuni pixel
 * Federico Pepe, 21.01.2018
 * http://blog.federicopepe.com/processing
 */

// Creo le variabili necessarie;
float threshold = 0.5;
boolean work = true;
PImage original, edited;

void setup() {
  // Nella funzione di setup carico il file dall'hard disk;
  size(1, 1);
  surface.setResizable(true);
  selectInput("Select a file to process:", "fileSelected");
}

void draw() {
  if (original != null) {
    edited = original.copy();
    /*
     * Se la variabile work, il cui valore dipende dal click del mouse è true, mostro
     * l'immagine originale, altrimenti avvio la modifica
     */
    if (work) {
      image(edited, 0, 0);
    } else {
      image(creaImmagine(edited), 0, 0);
    }
  }
}

// Funzione per gestire il caricamento dei file da HD
// https://processing.org/reference/selectInput_.html
void fileSelected(File selection) {
  if (selection == null) {
    println("Window was closed or the user hit cancel.");
  } else {
    println("User selected " + selection.getAbsolutePath());
    original = loadImage(selection.getAbsolutePath());
    surface.setSize(original.width, original.height);
  }
}

// Funzione creaImmagine che, come da specifiche, accetta in ingresso un oggetto PImage
// e restituisce un oggetto PImage;
PImage creaImmagine(PImage img) {
  // Carico i pixel in un array
  img.loadPixels();
  for (int y = 0; y < img.height; y++) {
    for (int x = 0; x < img.width; x++) {
      int loc = x + y * img.width;
      // Ottengo i valori R, G, B di ciascun pixel;
      float r = red(img.pixels[loc]);
      float g = green(img.pixels[loc]);
      float b = blue(img.pixels[loc]);
      // Lancio la funziona calcolaPixel
      img.pixels[loc] = calcolaPixel(color(r, g, b));
    }
  }
  img.updatePixels();
  return img;
}

color calcolaPixel(color c) {
  // Valore casuale, come da specifiche
  float r = random(0, 1);

  if (r > threshold) {
    // Se r è maggiore della soglia, restituisco il colore originale
    return c;
  } else {
    // Altrimenti calcolo il nuovo valore del pixel come richiesto
    // la funzione constrain mi assicura che il valore calcolato sia compreso tra 0 e 255;
    float rm = constrain(0.393*red(c)+0.769*green(c)+0.189*blue(c), 0, 255);
    float gm = constrain(0.349*red(c)+0.686*green(c)+0.168*blue(c), 0, 255);
    float bm = constrain(0.272*red(c)+0.534*green(c)+0.131*blue(c), 0, 255);
    // Restituisco i nuovi valori
    return color(rm, gm, bm);
  }
}

// Con la funzione keyPressed determino la pressione dei tasti + e -
void keyPressed() {
  if (key == '+') {
    threshold += 0.1;
  }
  if (key == '-') {
    threshold -= 0.1;
  }
  // Utilizzo la funzione round (di seguito) per arrotondare il valore a 2 decimali;
  round(threshold, 2);
}
// Alla pressione del mouse, cambio il valore di work da true a false e viceversa
void mouseClicked() {
  work = !work;
}
// Funzione utile per limitare il numero di decimali in un float
// https://stackoverflow.com/questions/9627182/how-do-i-limit-decimal-precision-in-processing
float round(float number, float decimal) {
  return (float)(round((number*pow(10, decimal))))/pow(10, decimal);
} 

Quick, Draw! + Processing: lavorare con il dataset di Google con più di 50 milioni di disegni

Quick, Draw! in Processing

A fine 2016 Google ha messo on-line Quick, Draw! uno dei suoi esperimenti di intelligenza artificiale e machine learning. Il sito chiede alle persone di disegnare delle forme: un maiale, un tubo per annaffiare il prato, una padella, ecc… e in meno di 20 secondi, attraverso una rete neurale, il computer prova a indovinare cosa è stato disegnato.

Più di 15 milioni di persone hanno partecipato al gioco e, così facendo, Google è riuscita a raccogliere un dataset di più di 50 milioni di disegni che, qualche giorno fa, è stato rilasciato pubblicamente.

Il dataset si compone di 345 categorie e, oltre ai dati vettoriali delle immagini sono inclusi anche una serie di metadati.

L’obiettivo del rilascio di questi dati è:

We’re sharing them here for developers, researchers, and artists to explore, study, and learn from.

Nel giro di poche ore sul mio feed twitter sono comparsi i primi esperimenti (i più interessanti sono quelli di @frauzufall che trovate qui) la maggior parte dei quali, però, generati con OpenFrameworks.

Non trovando nessuno che stesse utilizzando Processing, ho deciso di scrivere un piccolo programma in questo linguaggio per dare la possibilità a tutti di utilizzare questo dataset

Struttura dei dati

I file che si scaricano dal repository rilasciato da Google sono file di tipo .ndjson ovvero file con un oggetto di tipo JSON per ogni riga di file.

Ciascun oggetto è composto dai seguenti dati:

  • key_id (integer): numero univoco che identifica il disegno
  • word (string): la categoria che era stata richiesta
  • recognized (boolean): se il disegno era stato riconosciuto (true) oppure no (false)
  • timestamp (datetime): quando il disegno è stato creato
  • countrycode (string): due lettere per identificare la nazione del giocatore
  • drawing (string): un array JSON contenente i dati vettoriali del disegno
{ 
    "key_id":"5891796615823360",
    "word":"nose",
    "countrycode":"AE",
    "timestamp":"2017-03-01 20:41:36.70725 UTC",
    "recognized":true,
    "drawing":[[[129,128,129,129,130,130,131,132,132,133,133,133,133,...]]]
  }

Per semplificarmi la vita, mi sono concentrato solo sull’ultimo tipo di dato: il mio obiettivo era caricare i dati del dataset e ridisegnare le forme con Processing.

L’array contenente i dati del disegno è a sua volta strutturato in questo modo:

[ 
  [  // First stroke 
    [x0, x1, x2, x3, ...],
    [y0, y1, y2, y3, ...],
    [t0, t1, t2, t3, ...]
  ],
  [  // Second stroke
    [x0, x1, x2, x3, ...],
    [y0, y1, y2, y3, ...],
    [t0, t1, t2, t3, ...]
  ],
  ... // Additional strokes
]

Per fortuna la documentazione di Google è molto chiara: x e y sono le coordinate e t è il tempo trascorso in millisecondi dal primo punto.

Dati preprocessati

I dati contenuti nel dataset sono molti e, infatti, ciascun file pesa diverse centinaia di megabyte. Per fortuna il team di Google ha messo a disposizione anche dei file preprocessati: sono state rimosse le informazioni temporali e tutti i dati sono stati allineati e ridimensionali in un quadrato di 256×256 pixel.

I file .ndjson prepocessati sono disponibili a questo link e, come potete notare, la dimensione è notevolmente ridotta anche se si tratta, pur sempre, di almeno 40-50 megabyte per file.

Importare i dati in Processing

Primo problema: in Processing non esistono funzioni che ci permettono di lavorare su oggetti di tipo JSON differenti a meno che non siano inclusi in un array. All’inizio avevo, dunque, optato per modificare il file dei dati ma, grazie al suggerimento di un utente su Facebook, ho scoperto l’esistenza dell’oggetto BufferedReader che si usa per leggere un file una riga alla volta.

Una volta risolto questo inghippo, tutto è diventato più semplice: le linee restituite dall’oggetto sono di tipo String. Con la funzione parseJSONObject() ho convertito la stringa in un oggetto JSON.

Quick, Draw!

Quick, Draw: il dataset di Google in Processing
Vorrei conoscere chi ha disegnato questa “incudine”

Avendo accesso all’oggetto JSON, l’unico passaggio da fare era estrarre dall’array drawing i dati x e y e assegnarli ai vertici di una forma per poterla disegnare a schermo.

Per farli ho usato un’insieme di funzioni di cui parleremo in futuro.

Per i più curiosi, il codice è disponibile su questo repository su GitHub.

Processing e Ableton: sincronizzare musica e visual via MIDI

Oggi sfrutteremo Processing per creare dei semplici visual sincronizzati con la musica attraverso il protocollo MIDI. Il software musicale che userò per fare gli esempi è Ableton Live 9 ma è possibile usare qualsiasi Digital Audio Workstation in grado di inviare messaggi MIDI.

Lo schema delle connessioni, dunque, sarà: Ableton Live gestirà la musica e invierà, attraverso dei messaggi MIDI, delle informazioni che Processing acquisirà in input per generare i visual.

MIDI e the MidiBus

Perché ho deciso di sfruttare il protocollo MIDI? Le ragioni sono molto semplici:

  1. è uno standard per le applicazioni musicali
  2. è un protocollo flessibile
  3. è semplice creare, inviare e monitorare i messaggi MIDI.
  4. rende molto semplice la sincronizzazione

Per questioni di spazio, non mi dilungherò in questo post sulla spiegazione di come funziona questo protocollo; trattandosi di uno standard sviluppato negli anni ’80, ci sono numerose risorse on-line che vi permettono di approfondire.

Libreria: the MidiBus

Anche se Java supporta nativamente il MIDI attraverso il pacchetto java.sound.midi, per lavorare con questi tipi di messaggi in Processing consiglio di installare una libreria.

Processing Contribution Manager MIDI Bus

Aprite Processing, andate nel menu Tool > Add new tool. Nel pannello Libraries digitate MIDI, selezionate The Midi Bus e cliccate sul pulsante Install in basso a destra.

macOS: IAC Driver

Di norma per inviare e ricevere messaggi MIDI è necessario dotarsi di una scheda e dei relativi cablaggi. Nel nostro caso, però, dovendo inviare e ricevere messaggi tra due applicazioni possiamo sfruttare lo IAC Driver: una scheda virtuale che ci permette di gestire tutte le connessioni internamente al computer.

Nota: Per Windows esistono delle applicazioni da installare che hanno la stessa funzione del driver IAC. Con una veloce ricerca su Google ho trovato LoopBe1 e MIDI Yoke che non ho provato personalmente ma sono consigliate su diversi forum.

Per configurare lo IAC Driver su macOS aprite Applicazioni > Utility > Configurazione MIDI Audio. Cliccate su Finestra > Mostra Studio MIDI ⌘2

Studio MIDI macOS IAC Driver

Fate doppio click su IAC Driver e mettete la spunta su Il dispositivo è acceso.

mac OS accendere IAC Driver

Ora che abbiamo attivato il canale di comunicazione interno per inviare e ricevere messaggi MIDI, possiamo procedere con Processing.

Ricezione messaggi MIDI in Processing

Apriamo un nuovo sketch e verifichiamo subito che Processing veda lo IAC Driver.

/*
 * Processing e Ableton: sincronizzare musica e visual via MIDI
 * Federico Pepe, 05.03.2017
 * http://blog.federicopepe.com/processing
*/

import themidibus.*;

void setup() {
  size(100, 100);
  background(0);

  MidiBus.list(); 
}

void draw() {
  
}

Con queste poche righe di codice abbiamo importato la libreria all’interno del nostro programma e attraverso il comando Midibus.list() possiamo verificare nella console tutte le periferiche MIDI attive.

Questo è il risultato che ottengo lanciando il programma sul mio Mac. Ovviamente potrebbe cambiare sul vostro; l’importante è che sia presente IAC Bus.

Available MIDI Devices:
----------Input----------
[0] "IAC Bus 1"
[1] "Real Time Sequencer"
----------Output----------
[0] "Gervill"
[1] "IAC Bus 1"
[2] "Real Time Sequencer"

Progetto Ableton Live: invio del messaggio

Apriamo Ableton Live e creiamo un nuovo progetto. Apriamo le preferenze e verifichiamo che il driver IAC sia selezionato come scheda sia per l’input che per l’output.

Ableton Live 9 MIDI Preferences

Nella prima traccia MIDI nella parte di input/output selezioniamo MIDI ToIAC Driver. In questo modo tutti i messaggi che saranno presenti su questa traccia saranno inviati come output al driver e, di conseguenza, saranno ricevuti da Processing.

Ableton MIDI Out Tracks

Creiamo una Clip MIDI nel primo slot contenente un C3 lungo 1/4. Assicuriamoci che la clip sia il loop quando premiamo il tasto play su di essa.

MIDI Clip

Ricezione del messaggio

Torniamo su Processing e modifichiamo il nostro programma come segue:

/*
 * Processing e Ableton: sincronizzare musica e visual via MIDI
 * Federico Pepe, 05.03.2017
 * http://blog.federicopepe.com/processing
*/

import themidibus.*;

MidiBus myBus;

void setup() {
  size(100, 100);
  background(0);

  MidiBus.list(); 
  
  myBus = new MidiBus(this, 0, 1);
}

void draw() {
  
}

void noteOn(int channel, int pitch, int velocity) {
  println();
  println("Note On:" + " Channel: "+channel + " Pitch: "+pitch + " Velocity: "+velocity);
  println("--------");
}

Rispetto al codice scritto in precedenza, abbiamo aggiunto e inizializzato un oggetto di tipo MidiBus: myBus = new MidiBus(this, 0, 1);. In questa fase è fondamentale inserire i parametri corretti: il primo è il parent a cui la libreria è collegata, il secondo è l’input e l’ultimo l’output. I dati sono facilmente individuabili grazie al comando .list() dato in precedenza.

Abbiamo, infine, aggiunto una funzione che è presente all’interno della libreria: noteOn() e che restituisce i valori MIDI: canale, pitch e velocity di tutte le note ricevute.

Verifichiamo il funzionamento

Facciamo partire la nostra clip su Ableton Live e avviamo il nostro sketch su Processing. Se in console appare un messaggio come quello qui di seguito, siamo riusciti nel nostro intento:

Available MIDI Devices:
----------Input----------
[0] "IAC Bus 1"
[1] "Real Time Sequencer"
----------Output----------
[0] "Gervill"
[1] "IAC Bus 1"
[2] "Real Time Sequencer"

Note On: Channel: 0 Pitch: 60 Velocity: 127
--------

Processing sta ricevendo tramite IAC Driver un messaggio Note On con i seguenti parametri:

  • Canale: 0
  • Pitch: 60 (che corrisponde al C3)
  • Velocity: 127

Adesso, se andiamo a modificare la clip su Ableton inserendo nuovi messaggi, possiamo verificare che anche questi siano ricevuti da Processing.

Creare i visual

A questo punto non ci resta che sviluppare in Processing i nostri visual sfruttando i parametri MIDI inviati da Ableton. Nell’esempio molto semplice che trovate di seguito, viene disegnato un quadrato al centro dello schermo la cui dimensione dipende dal valore di pitch ricevuto via MIDI. In aggiunta a quanto visto in questo post, sfrutto la funzione noteOff() per impostare la grandezza del quadrato a 0. Di certo non è il metodo più elegante ma di sicuro funziona.

/*
 * Processing e Ableton: sincronizzare musica e visual via MIDI
 * Federico Pepe, 05.03.2017
 * http://blog.federicopepe.com/processing
*/

import themidibus.*;

MidiBus myBus;

int size;

void setup() {
  size(400, 400);
  background(0);

  MidiBus.list(); 
  
  myBus = new MidiBus(this, 0, 1);
}

void draw() {
  background(0);
  rectMode(CENTER);
  rect(width/2, height/2, size, size);
}

void noteOn(int channel, int pitch, int velocity) {
  println();
  println("Note On:" + " Channel: "+channel + " Pitch: "+pitch + " Velocity: "+velocity);
  println("--------");
  size = pitch;
}

void noteOff(int channel, int pitch, int velocity) {
  size = 0;
}

Ed ecco il risultato:

Array bidimensionali: esercizio alta difficoltà (soluzione)

Ecco la soluzione all’esercizio di difficoltà media proposto nell’articolo Array bidimensionali.

Esercizio

Partendo dalla soluzione all’esercizio di difficoltà media, sareste in grado di modificarlo ulteriormente per fare in modo che la griglia non sia necessariamente 8×8?

Continua la lettura di Array bidimensionali: esercizio alta difficoltà (soluzione)