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);
} 

Ableton Live 10 in public beta: le principali novità.

Aggiornamento 17 gennaio 2017: Ableton 10 arriverà il 6 febbraio.

Si è cominciato a parlare di Ableton 10 da fine agosto quando, in un video di DJ Jazzy Jeff, qualcuno aveva notato alcune differenze rispetto alla versione 9.

Le speculazioni sono andate avanti per diverse settimane finché, il 2 novembre, Ableton ha annunciato ufficialmente che la nuova release del software era in lavorazione e che sarebbe stata disponibile a inizio 2018.

Arriviamo a oggi o, meglio, ad un paio di giorni fa: Ableton 10 è entrato nella fase di public beta per chi possiede una licenza standard suite di Live 9.

È un segnale chiaro che ci stiamo avvicinando alla release finale del programma e Ableton ha bisogno che gli utenti lo testino e scovino bug prima del rilascio ufficiale.

Dall’annuncio della beta pubblica ho letto parecchi commenti critici nei confronti dell’aggiornamento: molti ritengono che il prezzo sia sproporzionato rispetto alle novità introdotte. Io ho avuto la fortuna di essere selezionato tra i beta tester a inizio novembre e, superato lo scetticismo iniziale, posso dirvi che questo aggiornamento vale ogni centesimo.

In questo post troverete una panoramica sulle novità che, personalmente, ho trovato più interessanti.

Una nuova interfaccia

Interfaccia Live 10

Partiamo dalla cosa più ovvia: la grafica. Una nuova icona, un nuovo font – Ableton Sans – che rende più leggibile l’intera interfaccia del programma, e dei nuovi temi: Light, Mid Light, Mid Dark, DarkLive 9.

Su schermi ad alta risoluzione (HiDPI) risulta tutto molto leggibile e definito; ricordo ancora quanto ho benedetto l’aggiornamento di Live 9 che ha introdotto il supporto al mio retina display.

Capture: non perdere mai un’idea

Una delle novità più attese e che, dal mio punto di vista, giustifica l’investimento per l’aggiornamento è  la funzione Capture. Ableton sarà perennemente in “ascolto” di tutti gli input che arrivano sulle tracce MIDI. Alla pressione del tasto capture, inserito accanto al pulsante di registrazione della sessione, viene creata automaticamente una clip.

La cosa che più mi ha colpito di questa funzione quando l’ho provata la prima volta è che Ableton cerca di mantenere sia il tempo che il groove della nostra performance adattando, di conseguenza, le impostazioni del progetto.

Nuovi device

Wavetable - Ableton 10

Come ogni aggiornamento che si rispetti, sono stati introdotti dei nuovi dispositivi (di cui parleremo in modo più esteso prossimamente):

  • Wavetable: un nuovo sintetizzare a due oscillatori che, com’è facilmente intuibile dal nome, sfrutta i principi della la sintesi wavetable.
  • Echo: un nuovo effetto delay con molte possibilità di modulazione.
  • Drum Buss: un processore di dinamica progettate per le batterie dal carattere analogico.
  • Pedal: un distorsore/overdrive/fuzz che si ispira a tre classici pedali da chitarra.

Sono stati migliorati anche due device già presenti nelle versioni precedenti: Utility ed EQ Eight.

Arrangiamento, automazioni e navigazione

I programmatori di Ableton hanno concentrato i loro sforzi sul miglioramento della modalità arrangiamento: ora è più facile fare editing, arrangiare e creare automazioni.

Queste le novità principali e il relativo shortcut, se presente:

  • È possibile fare lo stretch delle clip audio prendendo Shift e trascinando il bordo.
  • È possibile muovere il contenuto interno di una clip premendo Ctrl+Shift (PC) / Alt+Shift (Mac) senza spostare la clip. Tenendo premuto Alt (PC) / Cmd (Mac) si disattiva temporaneamente la griglia.
  • Le note MIDI vengono riprodotte anche se il playhead è partito dopo l’effettivo inizio della nota (MIDI Note Chasing).
  • Il doppio click su una traccia MIDI crea una clip.
  • È possibile zoomare le tracce premendo Alt e verticalmente premendo Ctrl (PC) / Cmd (Mac).
  • Zoom in/out di una porzione di tempo selezionata (Z / Shift+Z).
  • Aggiunta funzione mostra tutte le tracce (S).
  • È possibile mostrare/nascondere tutte le automazioni (A).
  • Segmenti di automazione possono essere spostati orizzontalmente

Miglioramenti del workflow

In generale questo aggiornamento di Ableton 10 si caratterizza per una serie di piccoli e quasi invisibili miglioramenti che, però, cambieranno e velocizzeranno il vostro modo di lavorare:

  • Multi-Clip Editing: è possibile editare fino a otto clip midi nella Vista dettagliata. Se le clip sono di lunghezza differente, Ableton creerà automaticamente un loop per allineare tutte le clip.
  • Gruppi annidati: i gruppi possono essere inseriti in altri gruppi. Non c’è limite al numero di gruppi annidati gli uni negli altri.
  • Reset con doppio click: facendo doppio click su qualsiasi parametro, il suo valore viene resettato (finalmente!).
  • Raccolte nel browser: è stata aggiunta nel browser la sezione Raccolte che permette di categorizzare il contenuto della libreria per tag colori.
  • Metronomo: non solo sono stati aggiunti dei nuovi suoni tra cui scegliere ma è anche possibile impostare il metronomo per contare le suddivisioni ritmiche.
  • Backup Folder: ogni volta che salviamo il progetto, viene creata una copia di backup della versione precedente. In questo modo è possibile risalire fino a 10 salvataggi precedenti.
  • Pan Split-stereo: facendo Ctrl+Click sul knob del Pan è possibile impostarlo come Stereo Split.

Max 8 e Max for Live

Max for Live è ora integrato direttamente nel programma: non è necessario installare alcun pacchetto esterno. Nella versione beta di Ableton 10 è presente una versione beta di Max 8. Beta-inception.

Tra le novità: nuovi strumenti e il supporto multi input/output.

Conclusione

Quelle descritte in questo post sono solo una parte delle novità presenti nella versione beta attuale di Ableton Live 10. Per farvi un’idea, nel manuale ufficiale il capitolo What’s New in Live 10 occupa ben 10 pagine!

Ormai dovrebbero mancare poche settimane al rilascio della nuova versione; come già detto, io non vedo l’ora.

Lavorare con i dati

Nel mondo digitale tutto è un insieme di dati: che si tratti di un testo, una foto oppure un video, stiamo parlando di una sequenza di 1 e 0 che chiamiamo comunemente file.

Con questo post inauguro una nuovo capitolo su questo blog in cui scopriremo insieme come utilizzare Processing per lavorare con file e dati.

Quale potrebbe essere il vantaggio di utilizzare un linguaggio di programmazione per questo genere di attività? Perché non possiamo utilizzare strumenti per grafici o designer come Photoshop o Illustrator? La risposta è semplice: uno dei vantaggi dei computer rispetto alla mente umana è che sono in grado di processare velocemente numeri ed eseguire operazioni ripetitive e complesse molto velocemente.

La vista di un file di Excel pieno di righe e colonne può scatenare il panico nelle persone sane di mente ma per un software non servono altro che due cicli for per accedere al contenuto di ciascuna cella. Quello che impareremo a fare sarà, poi, assegnare quel contenuto a una (o più) variabili e il gioco è fatto.

Pensiamo alle visualizzazioni di dati che negli ultimi anni sono diventate onnipresenti su siti web, riviste e quotidiani. Il loro successo è dovuto al fatto che, grazie a un linguaggio di tipo visuale, rendono più accessibile il contenuto della storia, anche quando include una grande mole di dati. A differenza della carta stampata realizzando un’infografica con Processing si apre un ventaglio di possibilità: possiamo renderla interattiva, in 3D, può essere su uno schermo oppure proiettata su una parete.

Tipi di dato

Quali saranno i tipi di dati su cui lavoreremo? Abbiamo già avuto modo di parlare di immagini e pixel (tema che approfondiremo), ma in questa sezione ci concentreremo su:

  • File di testo (.txt)
  • Tabelle di dati di Excel (.csv, .tsv)
  • Pagine web (.html)
  • Feed (.xml)
  • API (.json)

2018

Da qualche giorno ho aggiornato la grafica del sito che giunge, così, dopo il reboot nel 2013, alla sua terza o quarta versione. L’obiettivo, come sempre, è quello di rendere tutti i contenuti – presenti e futuri – più fruibili.

Ho cercato di curare nei minimi dettagli la leggibilità sia su dispositivi mobili che su schermi più grandi e ho prestato particolare attenzione al miglioramento dei tempi di caricamento pagina.

Sempre sull’onda del minimalismo assoluto, ho eliminato tutti i fronzoli non necessari: barre laterali,  header, immagini, ecc.

Tutto il codice pubblicato nei vari articoli che era hostato su GitHub è stato riportato sul sito, la pagina di Processing è stata completamente ristrutturata. Ho riordinato tutte le categorie del blog e riassegnato gli articoli alla giusta sotto-categoria.

Oltre alla forma si evolveranno anche i contenuti. In generale spero di riprendere un ritmo di pubblicazione più alto: nel 2017 mi ero imposto almeno un post al mese e mi piacerebbe arrivare, nel 2018, ad almeno uno a settimana.

Oltre alla parte relativa alla programmazione – che rimarrà e che si amplierà nel prossimo futuro – entreranno anche nuove categorie.

Proverò ad esplorare anche nuovi mezzi e, una volta per tutte, cercherò di curare quelli che ho già attivato [Facebook, Newsletter] e che non ho mai seguito nel modo giusto.

Non volevo fare il classico post di bilancio del 2017 e di buoni propositi per il 2018. Ho deciso di pubblicare questo post per fissare un punto di inizio e la strada che intendo seguire.

Buona lettura.

Coding Rescue #2 – Binarizza un’immagine

Secondo capitolo della rubrica Coding Rescue dove provo a risolvere i vostri problemi con Processing. Questa volta la consegna è piuttosto articolata, riassumo i punti salienti:

  • Lo scopo del programma è quello di “binarizzare” un’immagine ovvero fare in modo che, al click del mouse, i colori dell’immagine vengano modificati utilizzando la funzione binarize(). La funzione imposta un pixel bianco se la sua luminosità è maggiore di una soglia impostata e nero se inferiore.
  • per calcolare la luminosità, si deve utilizzare la funzione brightness().
  • impostare la soglia iniziale a un valore predefinito (a scelta) ma implementare una funzione calculateThreshold() con cui ricalcolarla dinamicamente utilizzando al distanza percorsa dal mouse tra due frame successivi.

Binarizza un’immagine, la soluzione

binarizza un'immagine con Processing

Ecco qui il codice:


/*
 * Coding Rescue #2 - Binarizzare un'immagine
 * Federico Pepe, 26.10.2017
 * http://blog.federicopepe.com/processing
 */
float threshold = 150;
PImage immagine, imgbin;
boolean bin = true;

void setup() {
  size(700, 542);
  immagine = loadImage("corgi-photo.jpg");
  image(immagine, 0, 0);
  frameRate(2);
} 

void draw() {
  threshold = calculateThreshold();
  if (bin) {
    imgbin = immagine.copy();
    image(binarize(imgbin), 0, 0);
  } else {
    image(immagine, 0, 0);
  }
}

void mouseClicked() {
  bin = !bin;
}  

PImage binarize(PImage img) {
  img.loadPixels();
  for (int y = 0; y < img.height; y++) {
    for (int x = 0; x < img.width; x++) { int loc = x + y * img.width; if (brightness(img.pixels[loc]) > threshold) {
        img.pixels[loc] = color(0);
      } else {
        img.pixels[loc] = color(255);
      }
    }
  }
  img.updatePixels();
  return img;
}

float calculateThreshold() {
  float d = dist(pmouseX, pmouseY, mouseX, mouseY);
  threshold = d;
  return threshold;
}