Post on 17-Feb-2019
transcript
Programmazione di applicazioni multimediali
in Processing
Progettazione e produzione multimediale - 2008Walter Nunziati - nunziati@dsi.unifi.it
Sommario
Introduzione al linguaggio e all’ambiente di sviluppo Processing
Elaborazione e analisi di immagini e video in Processing
Esercitazioni pratiche
IntroduzioneLa programmazione è diventata un metodo per creare contenuti multimediali.
In particolare, le Rich Internet Application (RIA) prevedono una complessa interazione con l’utente, che richiede l’uso di strumenti di programmazione al fianco dell’XHTML.
Vari ambienti di programmazione sono oggi usati dai progettisti di contenuti a fianco dei tradizionali strumenti di authoring.
Processing
Processing is an open source programming language and environment for people who want to program images, animation, and sound. It is used by students, artists, designers, architects, researchers, and hobbyists for learning, prototyping, and production. It is created to teach fundamentals of computer programming within a visual context and to serve as a software sketchbook and professional production tool. Processing is developed by artists and designers as an alternative to proprietary software tools in the same domain.
Cosa è Processing in pratica?
Un linguaggio di programmazione derivato da Java
Un insieme di librerie dedicate in particolare allo sviluppo di applicazioni multimediali (gestione suono, immagini, video, primitive grafiche, ecc.)
Un ambiente di sviluppo semplice e intuitivo
Riferimenti
Sito web:
www.processing.org/learning/index.html
www.processing.org/reference/index.html
Libri:
Processing: A Programming Handbook for Visual Designers and ArtistsCasey Reas and Ben Fry, MIT Press 2007
Processing: Creative Coding and Computational Art (Foundation)Ira Greenberg
Quick tour
Esempio: helloworld
Why processing?Open-source, gratuito e multi piattaforma
Ampiamente supportato
Non richiede un ambiente di run-time esterno (es. il browser)
Nasconde molti “aspetti noiosi” del processo di sviluppo
Produce eseguibili multi piattaforma e applet web
Struttura di un programma processingI programmi processing sono composti da moduli chiamati Sketch
Ogni modulo è essenzialmente un file di testo, e contiene istruzioni e funzioni del programma
Ogni programma ha un modulo principale, che viene lanciato al momento dell’esecuzione
Ogni programma contiene (nel modulo principale) tipicamente le seguenti funzioni (in continuous programming mode):
setup() - eseguita all’inizio del programma per le operazioni di inizializzazione
draw() - eseguita continuamente in loop (è il ciclo principale del programma)
Tale comportamento può essere alterato attraverso le funzioni loop(), noloop(), e redraw().
Esempio: ContinuousModeExample1
Cenni di programmazione strutturata
Sintassi
Dichiarazione variabili
Tipi di dato e scope delle variabili
Istruzioni di assegnamento
Costrutti di selezione e iterazione
Dichiarazione e uso di funzioni
Sintassi (base)
Ogni riga che inizia con // è un commento
Ogni blocco compreso tra /* e */ è un commento
Ogni istruzione deve terminare con ;
Le istruzioni sono case-sensitive
Istruzioni
Un programma è composto da una sequenza di istruzioni. Si hanno:
Istruzioni di assegnamento
Istruzioni di selezione
Istruzioni di iterazione
Variabili e istruzioni di assegnamento
Usate per memorizzare dei valoriHanno un nome e un valoreCase-sensitiveUsate nomi descrittivi, e siate coerenti con il naming!
String name = "Daniel"; // Declare and assignint number = 32; // Declare and assignint counter = 12; // Assign variableprint(number);print(name);
Tipi di dato di base
int //Integer: 1, 2, 3, ...float //0.1, 2.747, ...char // “$”, “A”, stores one character.String //”Daniel”, stores a series of characters.boolean //true or false.// ARRAY (VETTORI):int[] vettore = new int[];
Nota: non tutti i linguaggi sono “tipizzati”
Scope delle variabili
Una variabile dichiarata all’interno di una funzione è visibile solo in quella funzione!
Una variabile dichiarata all’esterno di tutte le funzioni, e visibile a tutto il programma (si dice GLOBALE)
E’ buona norma non abusare delle variabili globali!
Tipi non primitiviAccanto ai tipi primitivi che abbiamo visto all’inizio (int,char,float,ecc.), ne esistono molti altri definiti all’interno di varie librerie.
Ad esempio, una variabile di tipo PImage è utilizzata in processing per memorizzare immagini
L’uso dei tipi non primitivi segue (quasi) le stesse regole di dichiarazione dei tipi primitivi:
PImage img;
ArrayUn array (vettore) è un contenitore di oggetti dello stesso tipo, indicizzati da un numero intero.
Il primo elemento ha indice 0. L’ultimo n-1, dove n è il numero di elementi dell’array
Dichiarazione:
int[] myArray = new int[10];
int[] myArray = {1919, 1940, 1975, 1976, 1990};
Un’array può essere inizializzato al momento della dichiarazione:
int myArray = {100,200,400};
Si accede all’elemento i-esimo dell’array usando le parentesi quadre:
int a = myArray[5];
Es (disegno di una forma):
Funzioni
Definizione di funzioniLe istruzioni vengono tipicamente raggruppate in funzioni, che eseguono in blocco una particolare sequenza di istruzioni ogni volta che vengono chiamate Una funzione si DEFINISCE con
tiporitornato nomefunzione(par1,pa2,..) {
// corpo della funzione
}
ES (dichiarazione):
int addizione(int i1, int i2) {
int risultato = i1 + i2;
return risultato;
}
Chiamata di funzioni
Una funzione precedentemente definita si richiama con:
valore-ritornato = nome-funzione(par1,pa2,..);
Es:
int valoreSomma = addizione(v1,v2);
Operatori logici
&& (and)
|| (or)
! (not)
Es.
if (a==true && i>10) {
}
Selezione - if/then/else// statements within the if condition are only// executed when if trueif (i < 35) {
line( 30, i, 80, i );} else {
line( 20, i, 90, i );}// else is executed if condition is false
Iterazione (for)
// for() loop: repeat with the defined conditions// parameters: init, test, updatefor(int i=0; i<10; i++) {
line(30, i, 80, i);}
Iterazione (while)
// while() loop: repeat as long as condition is trueint i=0;while(i<10) {
line(30, i, 80, i);i++;
}
Sistema di coordinateProcessing uses a Cartesian coordinate system with the origin in the upper-left corner. If your program is 320 pixels wide and 240 pixels high, coordinate [0, 0] is the upper-left pixel and coordinate [320, 240] is in the lower-right. The last visible pixel in the lower-right corner of the screen is at position [319,239]
Disegno di primitive grafiche
//draws a point at x=10 and y=20point(10, 20);// draws a line from x1, y1 to x2, y2line(20, 20, 160, 120);// draws a rectangle: x1, y1, width, heightrect(20, 20, 160, 120);// draws an ellipse: x, y, width, heightellipse(20, 20, 40, 40);// draws a four sided polygon x1, y1, x2, y2, x3, y3, x4, y4quad(38, 31, 86, 20, 69, 63, 30, 76)
Pennello e riempimento
stroke(120); // gray strokestroke(0, 0, 255); // blue strokestroke (255, 0, 0, 122); // red transparentnoStroke(); // no Strokefill(100);fill(255, 0, 0);fill(200, 200, 200, 100); // grey transparentnoFill();Note: A fourth parameter for fill and stroke sets the transparency of the color value.
Trasformazioni del sistema di riferimentoLe funzioni translate(), rotate() e scale() permettono di applicare delle trasformazioni euclidee al sistema di riferimento (e quindi alle figure disegnate)
rect(0, 0, 50, 50); translate(30, 20); fill(0); rect(0, 0, 50, 50); fill(102); rect(15, 10, 50, 50);
Esempio: TranslateExample
Push/Pop MatrixpushMatrix() e popMatrix() permettono di isolare le trasformazioni e raggrupparle in uno stack (matrix stack)
In questo modo, è possibile memorizzare e richiamare lo stato del sistema di riferimento.
Esempio: MatrixStackExample
Esercizio - drawing shapesint i = 0;int windowSize = 400;
void setup() { size(windowSize, windowSize);
background(0); frameRate(10);}
void draw() { i++;
fill(255); float posX = width/2; float posY = height/2; float radius = random(0, 100); ellipse(posX, posY, radius, radius);
}
Esempio: Esercizio1
Scopi del primo esercizioFamiliarizzare con le funzioni setup(), draw(), frameRate(), size(), background(), fill(), random(), ellipse().
Uso delle COSTANTI built-in (width, height)
Usare il costrutto di selezione per controllare l’evoluzione delle forme disegnate.
“Building on existing code”
Esplorare le funzioni di disegno primitive grafiche.
Image processing and analysis
Processing: applicare algoritmi a un’immagine per ottenere degli effetti (aka photoshopping)
Analysis: applicare algoritmi a un’immagine per capirne il contenuto
Caricamento di un’immagine
Le immagini sono memorizzate all’interno di variabili di tipo PImage (tipo non primitivo)
Per aggiungere un’immagine allo sketch, basta trascinarla sulla finestra di editing
Per caricare un’immagine nel programma, si usa la funzione loadImage():
PImage img;
img = loadImage(“nomefile.jpg”);
Display di un’immagine
Per mostrare un’immagine a schermo, si usa la funzione image(). Es:
image(img, 0, 0);
Mostra l’immagine contenuta in img a partire dall’angolo in alto a sinistra.
• tint() colora l’immagine del colore specificato.
Elaborazione di immaginiLe funzioni get() e set() possono essere usate per uno sporadico accesso ai pixel
Per un uso più intensivo, si usano le funzioni loadPixels() e updatePixels()
loadPixels() ha l’effetto di mettere tutti i pixel dell’immagine in un array di dimensione ROWS*COLS, di nome pixels[].
updatePixels() “restituisce” i pixel all’immagine
Struttura di elaborazione tipica
// carica l’immaginePImage myImage = loadImage("topanga.jpg");image(myImage, 0, 0);
//elabora i pixelloadPixels();for (int i = 0; i < width*height; i++) { pixels[i] = color(200,20,30);}updatePixels();
Esempio: ImageExample
La funzione filter
filter(MODE) (o filter(MODE, level)) applica un algoritmo all’immagine
Es. filter(THRESHOLD, 0.5) converte l’immagine in bianco e nero
Filtri disponibili: THRESHOLD, GRAY, INVERT, POSTERIZE, BLUR, OPAQUE, ERODE, DILATE
La funzione blend
Sovrappone una regione di una immagine su un’altra (o su se stessa)
blend(x, y, width, height, dx, dy, dwidth, dheight, MODE)
blend(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE)
MODE: BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST..
Eventi
Sono una importante funzionalità messa a disposizione dell’ambiente processing
Un evento è una funzione che viene chiamata al verificarsi di certe condizioni
Es. mousePressed() è chiamata ogni volta che viene premuto un bottone del mouse
void setup()
{
size(200,200);
fill(0);
}
void draw() {}
void mousePressed()
{
rect(mouseX,mouseY,20,20);
}
Esempio: MousePressedExample
Eventi disponibili (input)
mouseDragged()
mouseMoved()
mouseReleased()
mousePressed()
keyReleased()
keyPressed()
Variabili predefinitemouseX, mouseY: coordinate del mouse nel frame corrente
pmouseX, pmouseY: coordinate del mouse nel frame precedente
mouseButton: LEFT, RIGHT, CENTER
mousePressed: vera se il mouse è correntemente premuto. NB: si chiama come la funzione mousePressed()!!!
Analoghe variabili sono disponibili per gli input da tastiera
Uso dei font
I font devono preventivamente essere “creati” usando l’apposito strumento dal menu Tools/Create fonts..
Una volta creato, il font viene messo nella cartella “data” dello sketch
Per utilizzarlo, si usa il tipo PFont:
PFont font = loadFont(“Courier-48.vlw”);
VideoLe funzioni per l’uso dei video sono in processing.video.*;
I video vengono gestiti attraverso il tipo di variabile Movie
I video devono essere in formato mov. Non sono molti i codec supportati (mpeg4 ok, ffmpeg può essere usato per la conversione)
Ad ogni frame viene chiamato l’evento movieEvent()
I singoli frame possono essere trattati e elaborati come immagini.
Riproduzione di un filmato:
import processing.video.*;Movie myMovie;void setup() { size(360,240); myMovie = new Movie(this, "ronaldinho.avi"); myMovie.loop();}
void draw() { image(myMovie, 0,0); myMovie.read();}
Esempio: VideoExample1
Elaborazione dei framevoid draw() {
// Read the new frame from the movie
video.read();
// make its pixels array available
video.loadPixels();
for (int i = 0; i < pixels.length; i++) {
color currColor = video.pixels[i];
// Render the grayscale image to the screen
video.pixels[i] = color(brightness(currColor));
}
video.updatePixels();
}Esempio: VideoExample2
Video from cameraSono supportate le telecamere USB e Firewire, ma il supporto varia a seconda dei sistemi operativi (viene utilizzato QuickTime per l’acquisizione, linux è MOLTO penalizzato)
La classe utilizzata è Capture
Capture.list() mostra i dispositivi di input video disponibili.
La gestione è simile a quella dei video, seppure (ovviamente) con meno controlli.
movieEvent e captureEvent
Chiamati ad ogni frame decodificato
void movieEvent(Movie m) { m.read();}
void captureEvent(Capture myCapture) {myCapture.read();
}
Classi e oggetti
Non tutte le funzionalità e le librerie di Processing possono essere utilizzate tramite l’approccio “a funzioni”.
Alcune parti sono implementate dal linguaggio secondo un modello “a oggetti”, con cui a volte è necessario avere a che fare.
In generale, tutto ciò che sta fuori da Processing-core deve essere usato “a oggetti”.
Classe
Una classe definisce un tipo di dato non primitivo, sia in termine dei dati che contiene, che delle operazioni che si possono svolgere su di esso.
Es. PImage è una classe, che serve per gestire le immagini
Variabili e oggetti
Quando si dichiara una variabile di un tipo di dato non primitivo, si dice che si definisce un oggetto di quella classe. Es.
PImage img;
Dichiara un’oggetto di classe (tipo) PImage.
L’operatore new
Per creare (istanziare) un’oggetto, ci sono di solito due modi:
Usare una funzione che “fa il lavoro” per noi. Es.
PImage img = loadImage(“path”);
Usare l’operatore new:
PImage img = new PImage();
Metodi
Gli oggetti hanno tipicamente delle funzioni che possono essere invocate su di essi. Tali funzioni si chiamano metodi. Es:
// dichiarazione e creazione di una variabile di classe MovieMovie movie = new Movie(this, “videofile”);
// Chiamata del metodo play();
movie.play();
Librerie
Built-InVideoNetworkingSerial CommunicationImporting XML, SVGExporting PDF, DXF, etc.
External ContributionsSound: Ess, SoniaComputer Vision: JMyron, ReacTIVision,BlobDetectionInterface: proCONTROLL, Interfascia...
Installazione di librerie
Le librerie aggiuntive devono essere scaricate a parte
Tipicamente, è sufficiente copiare il file .jar fornito all’interno della cartella libraries contenuta nell’installazione del processing
In alcuni casi vengono forniti anche dei componenti nativi (.dll, .so) che devono essere aggiunte al library path di java
Uso di librerie
L’istruzione import all’inizio di un modulo permette di utilizzare le librerie definite nella clausola import in quel modulo.
Es. per importare la libreria del video:
import processing.video.*;
Ess library (sound)
Ess r2 is a sound library that allows sound sample data to be loaded or streamed (AIFF, WAVE, AU, MP3), generated in real-time (sine, square, triangle and sawtooth waves, white and pink noise), manipulated (raw or via built-in filters), saved (AIFF, WAVE), analyzed (FFT) or simply played back.
ESS - generazione
Classi usate:
Ess (metodi statici per il controllo generale del device audio)
AudioStream (Stream audio di output monofonico)
SineWave (generatore di forma d’onda)
Esempio: EssExample1
ESS - riproduzione
Per aprire un file mp3 è necessario scaricare e installare 3 componenti aggiuntivi (3 file jar relativi a librerie java per l’mp3).
La classe che permette di aprire un file mp3 è AudioFile
Esempio: EssExample2
Svg library
Scalable Vector Graphics (SVG) is an XML specification and file format for describing two-dimensional vector graphics, both static and animated. SVG can be purely declarative or may include scripting. Images can contain hyperlinks using outbound simple XLinks. It is an open standard created by the World Wide Web Consortium's SVG Working Group.
Apertura e manipolazione di un file
E’ garantito il funzionamento con file creati da AI.
Viene usata la classe SVG
SVG svgImage = new SVG(this, “file.svg”);
Il metodo draw() disegna l’immagine a schermo
Essendo un immagine vettoriale, può essere trasformata in modo molto efficiente.
Esempio: SvgExample
Interfascia
Libreria per la realizzazione di interfacce grafiche.
Fornisce numerosi controlli: pulsanti, radio buttons, check box, slider, ecc.
Gestisce automaticamente l’interazione attraverso un meccanismo a eventi.
Esempio - pulsante
Ogni widget è gestito da un tipo di variabile: IFButton, IFLabel, ecc.
L’oggetto GUIController supervisiona il meccanismo degli eventi. Deve essere costruito a partire dalla applet che deve ricevere gli eventi (tipicamente, this):
GUIController controller = new GUIController(this)
Esempio: InterfasciaExample
Listener e EventHandler
Ad ogni controllo, deve essere associato un oggetto actionListener. In pratica, questo associa un contenitore (che è di soltio la applet stessa) al controllo.
Quando avviene un evento, viene chiamato automaticamente l’eventHandler actionPerformed, con il parametro GUIEvent che contiene varie informazioni tra cui l’oggetto che ha scatenato l’evento.
Gestione dell’evento
La funzione actionPerformed contiene le istruzioni che devono essere eseguite quando si scatena l’evento.
Il parametro GUIEvent ha un metodo getSource() che viene utilizzato per capire qual’è il controllo che ha originato l’evento.
BlobDetectionTrova i “blob” presenti nell’immagine. Un blob è una regione a intensità costante presente nell’immagine.
L’immagine viene prima convertita in un’immagine b/n (attraverso un’operazione di sogliatura), e successivamente elaborata per trovare tutte le regioni a intensità costante.
L’algoritmo restituisce i bordi e la bounding box di ogni regione trovata.
Può servire come primo stadio di un sistema di rilevamento/riconoscimento oggetti.
La classe BlobDetection fa tutto il lavoro.
Il metodo setThreshold(float) setta il valore da utilizzare per la sogliatura
Il metodo computeBlobs(PImage) esegue il calcolo dei blob
Il risultato è una lista di oggetti Blob, che contengono le informazioni di ogni regione trovata.
L’algoritmo non è molto efficiente, e funziona solo su immagini di piccole dimensioni.
Esempio: BolobDetectionExample
JMyron library
Una interessante alternativa per l’analisi del video da telecamera è la libreria JMyron
Non dipende da qt4java, e implementa un certo numero di algoritmi per l’analisi video
[vedi reference]
Hardware processingEsistono delle schede hardware (wiring, $80, arduino, $30) a micrcontrollore programmabili tramite un linguaggio (wiring) che è costruito a partire dal processing.
Wiring condivide molte delle feature del processing, in più mette a disposizione gli strumenti per generare il codice nativo per i microcontrollori, e per scaricare il codice oggetto sulle schede.
Mobile processing
Esiste un versione del processing (mobile.processing.org) che può essere eseguita su qualsiasi dispositivo mobile dotato di java virtual machine.
Processing mette a disposizione un simulatore di per sviluppare su tali dispositivi. Lo sviluppo prevede l’utilizzo di un subset delle feature del java.
Temi per elaborati
Sviluppo di un modulo per l’acquisizione video da telecamera IP per flusso di tipo mjpeg (funzionante con telecamere axis/sony)
Sviluppo di un modulo per l’acquisizione video sotto linux da file o da sorgenti v4l/v4l2 tramite ffmpeg.