Processing : créer une vidéo ou un gif animé à partir d’un sketch

Processing : créer une vidéo ou un gif animé à partir d’un sketch

Dans l’article Un sketch interactif (clavier ou souris) et des sauvegardes d’écran sous Processing 3 , nous avons vu comment réaliser des images à partir d’un sketch Processing. Maintenant nous allons voir comment créer une vidéo.

Le principe est simple :

  • Dans le sketch Processing, l’appui sur une touche (v ou V) déclenche l’enregistrement d’une image à chaque fois que la fonction draw se termine (une frame).
  • Avec les outils de Processing, création d’un fichier MOV à partir de toutes les images générées
  • Réduction du volume de la vidéo en la convertissant en mp4 avec VLC

Le sketch dont nous voulons réaliser une vidéo

Nous repartons de l’exemple très simple présenté dans l’autre article déjà cité. Nous y avons simplement ajouté la possibilité de changer de couleur au clic de souris, et de remettre l’écran à zéro en préssant z ou Z. Pour des raisons évidentes, ce sketch en ligne ne fait pas les enregistrements :

Créer les images, dans le sketch

Je me suis inspirée de ce chapitre du manuel Floss de Processing.

Le code complet est :

/*******
* un sketch tout simple qui permet de dessiner à la souris
* si on clique sur la souris, la couleur du cercle change
* si on tape sur z ou Z, l'écran est effacé
* si on tape sur v ou V, le sketch sauvegarde des images pour générer une vidéo
* pour créer la vidéo à partir des images 
*  ----> voir mon tutoriel sur https://knowledge.parcours-performance.com
*******/

int r ;            // radius
float hue ;        // HUE of shape
float compHue;      // complementary hue
float sat ;        // saturation of shape's color

// Pour les interactions 
boolean makeMovie = false ; // sauvegarder images en rafale / vidéo
// int movStart = 0 ;       // pour compter l'intervalle de frame entre deux images
String dest = "vid/infini" ; // destination des images pour faire un film
// String dest = "E:/--- PLANCTON -----/vid1440/infini" ;

 
void setup() {
 
  size(1440, 1080, JAVA2D) ;    // ATTENTION la taille de l'écran doit être celle qu'on prévoit pour la vidéo
  frameRate(30) ;               // idéalement 24 ou 30 qui sont les framerate habituels des vidéos
  colorMode(HSB, 360, 100, 100, 100);  
  r = 55 ;
  hue = random(360) ;
  compHue = 0 ;
  sat = 50 ;
  background(0,0,99,100) ;  // white
 
}
 
void draw() {
 
    smooth();
    strokeWeight(3);
    compHue = (hue + 180) % 360 ;  // modulo 360 to turn in a circle !
    stroke( compHue, sat, 100, 70 );
    fill( hue, sat, 100, 70 );
    ellipse( mouseX, mouseY, r, r );

    if( makeMovie == true) {
      
      // infini-00000nnn.tif
        saveFrame ( dest + "-########.tif"); 
      
    }  
 
}

void mousePressed () {
 
        hue = random(360) ; 
 
}

void keyPressed() {
  
  if (key=='z' || key=='Z') {
    background(0,0,99,100) ;  // white   
  }
  
  if (key=='v' || key=='V') {
    println("!!!! enregistrement images pour movie !!!!") ;  
    background(0,0,99,100) ;  // white
    makeMovie = !makeMovie ;
  }  
}

Les points importants sont :

  • bien définir la taille des images (et donc de la vidéo) en définissant screen ;
  • Définir le nombre d’image par seconde que l’on veut générer en utilisant framerate()
  • Faire bien attention au lieu de sauvegarde des images car elles peuvent être très nombreuses et très volumineuses
  • déclencher la génération d’images avec une touche

bien définir la taille des images (et donc de la vidéo) en définissant la taille de l’écran  ;

Dans le setup(), on règle l »écran à la taille du film souhaité, ici 1440 x 1080 pixels : size(1440, 1080, JAVA2D) ;

Définir le nombre d’image par seconde que l’on veut générer en utilisant framerate()

Ici j’ai choisi un rythme de 30 images par secondes avec frameRate(30) ;  dans le setup().

Remarque pour youtube, les frame rates courants sont 24, 25, 30, 48, 50, 60 images par seconde si l’on en croit le support technique Google.

Attention images nombreuses et volumineuses !!!!

String dest = « vid/infini » ;  ou String dest = « E:/— PLANCTON —–/vid1440/infini » ;  , permettent de dire où placer les images créées.

Attention, on crée autant d’image par seconde que le framerate (ici 30 , dont 30 images par seconde, 1800 par minute ! Et pour cet exemple, chaque image fait environ 4 Mo.

En 14 secondes, j’ai généré 694 Mo d’images ! 

déclencher la génération d’images avec une touche

On dessine le contenu de l’écran avant la sauvegarde de l’image. On place donc à la fin de la boucle draw le code suivant :

    if( makeMovie == true) {     
        saveFrame ( dest + "-########.tif"); 
    }

Ce code sauvegarde l’écran sous la forme d’une image  dans le sous-répertoire vid du répertoire contenant notre sketch (pour moi \create_video_1\vid\infini-00000485.tif par exemple). Et makeMovie devient vrai lorsqu’on appuie sur la touche v (ou V) et redevient faux avec la même touche car

void keyPressed() {
  
  if (key=='z' || key=='Z') {
    background(0,0,99,100) ;  // white   
  }
  
  if (key=='v' || key=='V') {
    println("!!!! enregistrement images pour movie !!!!") ;  
    background(0,0,99,100) ;  // white
    makeMovie = !makeMovie ;
  }  
}

Et voici le sketch complet : Sketch create_video_1 (changer l’extension en .pde)

Créer une image animée (gif) avec Gimp

Je suis partie de « Quick Tip: Combine images to gif animation in Gimp » :

  1. Ouvrir Gimp puis dans le menu fichier, choisir « ouvrir en tant que calques » et sélectionner toutes les images qui devront constituer notre image gif (j’ai sélectionné les 415 images !) ;
  2. Dans le menu fichier, choisir « export as » puis donner un nom finissant par gif (par exemple gif-tout-1.gif) au fichier à créer;
  3. Dans la boîte de dialogue, cocher « as animation » puis régler le délai entre frame pour que la vitesse soit convenable. Dans la mesure où je veux 30 images par seconde, je régle à 30 millisecondes entre frames.
  4. Ca prend un bon bout de temps.
Gif créé avec Gimp a partir d'images d'un sketch Processing

Gif créé avec Gimp a partir d’images d’un sketch Processing

Créer une vidéo, avec les outils de Processing

Je me suis inspirée de ce chapitre du manuel Floss de Processing.

Dans le menu Outils de Processing, choisir « Movie Maker ». Attention à sélectionner le même framerate que celui d’enregistrement des écrans. Et cocher « same size as original » pour créer une vidéo de la taille de screen.

Créer une vidéo avec le Movie Maker dans Processing

Créer une vidéo avec le Movie Maker dans Processing

Lorsqu’on clique sur le bouton « create movie », Processing nous demande où l’on veut enregistrer le fichier .mov qu’il va créer. Ensuite, la création se fait.

Conversion en MP4 avec VLC

Ouvrir la vidéo avec VLC. Dans le menu Média, choisir « Convertir / Enregistrer . Indiquer de nouveau le fichier à convertir, éventuellement indiquer à quel moment de la vidéo il faut commencer ou terminer. Ensuite cliquer sur « convertir ».

VLC convertir une vidéo en mp4 - 1

VLC convertir une vidéo en mp4 – 1

Choisir le format (ici h264) puis définir le fichier de destination.

VLC convertir une vidéo en mp4 - 2

VLC convertir une vidéo en mp4 – 2

Le fichier créé est énormément plus léger que le fichier MOV d’origine (100 fois mois d’octets parfois !) et la qualité reste très bonne.

Pour la vidéo correspondant au fichier gif ci-dessus, on a :

  • une seule image fait 4 457 ko ;
  • fichier gif réalisé avec les 415 images 6 655 ko ;
  • vidéo .MOV réalisée avec les 415 images 1 614 ko ;
  • vidéo .mp4 80 ko.
  • Et voici la vidéo en mp4

Un exemple plus important et utile

Pour information, j’ai généré une demi-heure de vidéo d’un travail artistique collectif :

  • Processing a tourné au moins 5 heures pour générer les 54000 images correspondant à 30 minutes (30 minutes * 60 secondes * 30 images par seconde).
  • Le répertoire (un disque dur externe pour ne pas prendre de risque) a reçu 297 Go de fichiers. Chaque image au format tif faisait 1440 x 1080 pixel, 4.45 Mo ;
  • La vidéo résultante en .MOV fait 104 Go (111 902 148 526 octets) !
  • La vidéo enregistrée en MP4 avec VLC fait 949 Mo (995 936 674 octets).

Et maintenant ?

On dispose d’un moyen pratique de montrer ce que produit un sketch processing sans la lourdeur de devoir lancer le sketch sur le bon ordinateur.

Processing : afficher et modifier des images

Processing : afficher et modifier des images

Je veux pouvoir charger des images (png ou jpg en principe) et pouvoir les positionner et les dimensionner comme je le veux. Je veux aussi pouvoir les teinter comme je l’entend.

J’explore donc ici les moyens de :

  • utiliser la classe PImage ;
  • charger une image avec loadImage() et éventuellement la redimensionner ;
  • attraper une portion d’une image, avec get() ;
  • utiliser des filtres, par exemple pour mettre en noir et blanc ou en niveau de gris ;
  • Rendre l’image transparente, avec ce qui est opaque soit blanc, soit en niveaux de gris clair ;
  • donner une couleur à cette image transparente ;
  • utiliser les images comme fond (background) ou les positionner à divers endroits.

charger une image et l’afficher comme fond d’écran

Pour qu’une image puisse être utilisée comme fond d’écran, elle doit impérativement être exactement à la taille de cet écran.

Dans le sketch ci-dessous, je réalise les opérations suivantes :

Dans setup()

  1. définir la taille de l’écran, ici 720 x 540 px ;
  2. définir un mode couleur HSB (voir l’article Processing : exploration des couleurs HSB et de l’ordre des dessins)
  3. Charger une image qui s’appelle test-fond-AL-2017-12.png et se trouve sur mon ordinateur dans le répertoire « C:/Processing Scripts/Patrice 2018 04/img »
  4. redimensionner l’image pour qu’elle fasse la largeur de l’écran et la hauteur de l’écran

Dans draw(), qui s’éxécute en boucle (noter que j’aurais pu tout mettre dans setup) :

  1. Définir une couleur de remplissage en mode HSB ;
  2. définir différentes variables aléatoires
  3. tous les 50 counter (sinon ça défile trop vite)
    1. redéfinir que le fond de l’écran est constitué par l’image ;
    2. tracer un rectangle sur ce fond, de position et dimension aléatoire
    3. par dessus ce rectangle, dessiner l’image, redimensionnée pour que le rectangle soit comme un cadre.
/* tests_images --------------------- 
* V0-A 
*/

String repertoire = "c:/REPERTOIRE/img" ;      // CHANGER pour le bon répertoire !!!!
String fileFond= "test-fond-AL-2017-12.png" ;
PImage imgbkg ; 

float xpos, ypos, w, h ;
color c ;
int counter = 0 ;

void setup() {
  size(720, 540, JAVA2D); // 16/9 : 960x540 et 4/3 720*540

  colorMode(HSB, 360, 100, 100, 100);   

  // voir fichier exemple processing /images/BackgroundImage
  imgbkg = loadImage( repertoire + "/" + fileFond );
  imgbkg.resize( width, height) ;
    
}


void draw() {
  
  c = color (random(360), 70, 70, 100) ; 
  fill(c) ;
  
  w = random( 20, width / 2 ) ;
  xpos = random ( 10, width - w - 10 ) ;
  h = random( 20, height / 2 ) ;
  ypos = random (10,  height - h - 10 ) ;  

  
  if (counter % 50 == 0 ) {
    background( imgbkg ) ;
    rect( xpos, ypos, w + 10, h + 10 ) ;
    image( imgbkg, xpos + 5, ypos + 5, w, h ) ;
  }
  
  counter ++ ;
}

Et voici ce que celà donne (ici en gif) : l’image est affichée comme fond et aussi dans un encart. Dans l’encart, elle est retaillée à la taille du rectangle.

Afficher seulement une partie de l’image avec get()

get() permet de récupérer des pixels d’une l’image (cf l’aide de Processing.org) .

On peut récupérer tous les pixels, donc toute l’image ou seulement une partie.

Dans le sketch qui suit, j’utilise newimg = img.get(50, 50, w, h );  pour créer une image newimg qui correspond à un rectangle positionné en (50,50), de largeur w et de hauteur l.

J’utilise aussi c = img.get(160, 190); pour lire la couleur d’un pixel spécifique (coordonnées 160,190) de l’image img.

Processing : utilisation de PImage et get()

Processing : utilisation de PImage et get()

Cette image est produite par ce sketch :

/* tests_images --------------------- 
* V0-A 
*/

String repertoire = "c:/REPERTOIRE/img" ; // CHANGER pour le bon répertoire !!!!
String fileFond= "test-fond-AL-2017-12.png" ;
PImage img, imgbkg, newimg ; 
color c ;
int w, h ;

void setup() {
  size(720, 540, JAVA2D); // 16/9 : 960x540 et 4/3 720*540

  colorMode(HSB, 360, 100, 100, 100);   

  // l'image d'origine, sans modification
  img = loadImage( repertoire + "/" + fileFond );
  
  // une image identique mais simplement redimensionnée
  imgbkg = img ;
  imgbkg.resize( width, height) ;  
  
  // une portion de l'image d'origine
  w = int( img.width * 0.3 ) ;
  h = int( img.height * 0.8 ) ;
  newimg = img.get(50, 50, w, h );
  
  // cette couleur sera celle du pixel (160,190) de l'image img
  c = img.get(160, 190);
  
}


void draw() {

  noStroke() ;
  fill(120,70,70,100) ;
  background( imgbkg ) ;

  rect(width/10-5, height/10-5, width/2+10, height/2+10);
  image( img, width/10, height/10, width/2, height/2) ; 

  fill(c) ;  
  rect( width/10-5, height*3/4, 200, 55 ) ;

  rect( width * 6/10 + 15, height/10 - 5, w + 10, h +10 );  
  image( newimg, width * 6/10 + 20, height/10 ) ;
}

background( imgbkg ) ;  produit le fond correspondant à l’image (qui a été redimensionnée à la taille de l’écran dans le setup).

image( img, width/10, height/10, width/2, height/2) ;  produit l’image encadrée de vert, en haut de la moitié gauche de l’écran.

fill(c) ;   et  rect( width/10-5, height*3/4, 200, 55 ) ;  produisent le rectangle de couleur rose. La couleur rose a été extraite du pixel de coordonnées  (160,190) de l’image par la commande c = img.get(160, 190);  dans setup().

Enfin image( newimg, width * 6/10 + 20, height/10 ) ; produit l’image à droite, c’est une portion de l’image d’origine. Elle a été obtenue par la commande newimg = img.get(50, 50, w, h );  dans setup().

tint, filter, pixel

Pour ce qui suit, je me suis aidée des informations du site processing.org et de deux pages d’un tutoriel, en anglais, sur la gestion des images avec Processing : cette page sur la teinte des images et cette page sur les filtres de transformation. Quant à l’image d’une oeuvre de Kandinsky, elle provient aussi de ces tutoriels. On peut la télécharger ici.

explication des différentes images obtenues

Le sketch qui suit illustre différentes possibilités de traitement d’une même image. Toutes ces possibilités sont visualisées sur un même écran de Processing :

Processing : utilisation de PImage et filter()

Processing : utilisation de PImage et filter()

Processing : utilisation de PImage et filter() - Positions

J’appelle A à D les lignes, 1 à 5 les colonnes et les différentes images correspondent à :

  • A1 l’image telle quelle, redimensionnée ;
  • A2 l’image avec une transparence ;
  • A3 un rectangle de couleur mauve qui est utilisée ensuite pour teinter des images ;
  • B1 l’image source passée en noir et blanc avec le filtre filter(THRESHOLD, 0.5)
  • B2 l’image précédente inversée avec le filtre filter(INVERT)
  • B3 l’image inversée teintée avec avec le filtre tint(196,85,189, 255);  (un mauve) puis tint(255, 255);  qui remet la teinte à neutre (pas de teinte, pas de transparence)
  • B4 l’image inversée passée dans une « moulinette » qui transforme tous les pixels noirs en pixels noirs et transparents. On a alors uniquement les pixels blancs qui sont visibles lors de l’affichage
  • B5 l’image inversée et transparente de B4 est tintée en mauve.
  • Les différentes colonnes de la ligne C sont les mêmes qu’en ligne B sauf qu’au départ on met l’image en niveau de gris avec le filtre filter(GRAY)  .  La « moulinette » pour transformer en image transparente est différente.
  • En ligne D, on observe deux images transparentes. La première en D1 est le résultat d’une fonction créée dans le sketch, pour les images en noir et blanc. La deuxième, en D3 est une autre fonction qui passe en niveaux de gris.  Ces deux fonctions font la même chose que ce qui est réalisé de manière décomposée en lignes B et C.

Le code complet pour obtenir ce résultat est le suivant (attention ne pas oublier de placer l’image kandinsky dans le répertoire data du script) :

/* tests_images --------------------- 
* Source http://www.cs.sfu.ca/CourseCentral/166/tjd/using_images.html
* et     http://www.cs.sfu.ca/CourseCentral/166/tjd/image_tint.html
* kandinsky.jpg utilisée http://www.cs.sfu.ca/CourseCentral/166/tjd/_downloads/kandinsky.jpg

*
* résultat = image img-img-filter-3.png
*/

PImage img ;
PImage imgBw, imgBwInvert, imgBwTransp ; 
PImage imgGrey, imgGreyInvert, imgGreyTransp ; 
PImage testBW, testGrey ;

int w, h ;
int xpos, ypos, interval ;


void setup() {
  // load the image file from the "data" folder
  img = loadImage("kandinsky.jpg");

  // set the window to be the same dimensions as the image
  size(1098, 757);  

  // l'image devient noir et blanc
  imgBw = img.get() ; // a copy of original
  imgBw.filter(THRESHOLD, 0.5);
  
  // L'image noir et blanc est inversée
  imgBwInvert =  img.get() ; // a copy of original
  imgBwInvert.filter(THRESHOLD, 0.5);
  imgBwInvert.filter(INVERT) ;
  
  // créer une image transparente à partir de imgBwInvert
  /*
  * https://forum.processing.org/one/topic/turn-white-pixels-transparent.html
  */
  int x, y , i ; // pour lire les pixels
  imgBwTransp = createImage( imgBwInvert.width, imgBwInvert.height, ARGB );
  
  for( x = 0; x < imgBwInvert.width; x++ ){
    for( y = 0; y < imgBwInvert.height; y++ ){
      i = ( ( y * imgBwInvert.width ) + x );
      if( imgBwInvert.pixels[i] == color( 0, 0, 0 ) ){
        // pixel noir devient transparent
        imgBwTransp.pixels[i] = color( 0, 0, 0, 0 );
      } 
      else {
        // autre couleur reste en l'état
        imgBwTransp.pixels[i] = imgBwInvert.pixels[i];
      }
    }
  }
  
  
  

  // l'image devient en grey scale
  imgGrey = img.get() ; // a copy of original
  imgGrey.filter(GRAY);
  
  // L'image grise est inversée
  imgGreyInvert = img.get() ; // a copy of original
  imgGreyInvert.filter(GRAY);
  imgGreyInvert.filter(INVERT) ;

  // créer une image transparente à partir de imgGreyInvert
  /*
  * https://forum.processing.org/one/topic/turn-white-pixels-transparent.html
  * avec des ajustements pour conserver les niveaux de gris
  */

  imgGreyTransp = createImage( imgGreyInvert.width, imgGreyInvert.height, ARGB );
  float red , green , blue, alpha ;
  boolean lightGrey = false ;
  
  for( x = 0; x < imgGreyInvert.width; x++ ){
    
    for( y = 0; y < imgGreyInvert.height; y++ ){

      i = ( ( y * imgGreyInvert.width ) + x );
      
      red = red(imgGreyInvert.pixels[i]) ;
      green = green(imgGreyInvert.pixels[i]) ;
      blue = blue(imgGreyInvert.pixels[i]) ;
      alpha = alpha(imgGreyInvert.pixels[i]) ;
      
      if ( red < 150 ) {
        
        if ( ( red == green ) && ( red == blue ) ) {
        
          lightGrey = true ;
        
        } 
        
      } else if (red >= 150)  {      
          
        // it's either not grey or it's dark grey
        lightGrey = false ;
      }

      if( !lightGrey ){
        
        // dark grey turned to same color without transparency
        imgGreyTransp.pixels[i] = color( red, green, blue, 255 );
      } 
      else {
        // la couleur est transformée en noire transparent
        imgGreyTransp.pixels[i] = color( 0, 0, 0, 0 );
      }
    }
  }

  // utiliser la fonction pour rendre transparente et blanche une image
  testBW = img.get() ; // a copy of original  
  testBW = fnbwTransp(testBW) ;

  // utiliser la fonction pour rendre transparente et en niveau de gris clair une image
  testGrey = img.get() ; // a copy of original  
  testGrey = fnGreyTransp(testGrey) ;
  
  
  // pour positionner et dimensionner les images
  w = width / 6 ;
  h = height / 5 ;
  interval = 20 ;
  xpos = interval ;
  ypos = interval ;
}

void draw() {
  
  background(img) ;

  // ----------------------------------- LIGNE 1 -----
  ypos = interval ;
  // l'image d'origine en petite taille
  xpos = interval ;
  image(img, xpos, ypos, w, h);  

  // ne change pas la couleur mais met de la transparence
  tint(255, 126);
  xpos =  2 * interval + w ;
  image(img, xpos, ypos, w, h);  
  // remet la couleur et la transparence normale
  tint(255, 255);

  // pour avoir une idée de la couleur de tint
  fill (196,85,189, 255); // un mauve 
  xpos =  3 * interval + 2 * w ;
  rect (   xpos, ypos, w, h );  

  xpos =   4 * interval + 3 * w ;


  // ----------------------------------- LIGNE 2 -----
  ypos = 2 * interval + h ;
  
  // les images noir et blanc

  xpos =  interval ;
  image(imgBw, xpos, ypos, w, h);  
  xpos =  2 * interval + w ;
  image(imgBwInvert, xpos, ypos, w, h);  
  // colorer l'image
  tint(196,85,189, 255); // un mauve 
  xpos =  3 * interval + 2 * w ;
  image(imgBwInvert, xpos, ypos, w, h);  
  // remet la couleur et la transparence normale
  tint(255, 255);
  
  xpos =  4 * interval + 3 * w ;
  image(imgBwTransp, xpos, ypos, w, h);  

  // colorer l'image transparente
  tint(196,85,189, 255); // un mauve 
  xpos =  5 * interval + 4 * w ;
  image(imgBwTransp, xpos, ypos, w, h);  
  // remet la couleur et la transparence normale
  tint(255, 255);  
  
  // ----------------------------------- LIGNE 3 -----
  // les images grises
  xpos =  interval ;
  ypos = 3 * interval + 2 * h ;
  image(imgGrey, xpos, ypos, w, h);  
  xpos =  2 * interval + w ;
  image(imgGreyInvert, xpos, ypos, w, h); 
  // ne change pas la couleur mais met de la transparence
  tint(255, 126);
  xpos =  3 * interval + 2 * w ;
  // colorer l'image
  tint(196,85,189, 255); // un mauve 
  image(imgGreyInvert, xpos, ypos, w, h);  
  // remet la couleur et la transparence normale
  tint(255, 255);  
  
  xpos =  4 * interval + 3 * w ;
  image(imgGreyTransp, xpos, ypos, w, h);  

  // colorer l'image transparente
  tint(196,85,189, 255); // un mauve 
  xpos =  5 * interval + 4 * w ;
  image(imgGreyTransp, xpos, ypos, w, h);  
  // remet la couleur et la transparence normale
  tint(255, 255);  
  
  // ----------------------------------- LIGNE 4 -----
  ypos = 4 * interval + 3 * h ;
  
  // via les fonctions
  xpos =  interval ;
  image(testBW, xpos, ypos, w, h);  

  xpos =  3 * interval + 2 * w ;
  image(testGrey, xpos, ypos, w, h);  

}

PImage fnbwTransp( PImage img ) {
  
  PImage result ; 
  
  img.filter(THRESHOLD, 0.5);    // now black & white
  img.filter(INVERT) ;      // black becomes white and white becomes black

  // source https://forum.processing.org/one/topic/turn-white-pixels-transparent.html    
  int x, y , i ; // pour lire les pixels  

  // must be ARGB so that transparency may be added
  result = createImage( img.width, img.height, ARGB ); 

    for( x = 0; x < img.width; x++ ){
    for( y = 0; y < img.height; y++ ){
      i = ( ( y * img.width ) + x );
      if( img.pixels[i] == color( 0, 0, 0 ) ){
      // pixel noir devient transparent
      result.pixels[i] = color( 0, 0, 0, 0 );
      } 
      else {
      // autre couleur reste en l'état
      result.pixels[i] = img.pixels[i];
      }
    }
    }

  return result ; // une image blanche avec tout le reste transparent
  
}


/*
* fonction pour retourner une image avec niveaux de gris clair transparente
* on peut ensuite la teinter comme on veut
*/

PImage fnGreyTransp( PImage img ) {
  
  PImage result = createImage( img.width, img.height, ARGB ); 
  
  img.filter(GRAY);    // now grey scale (niveau de gris)
  img.filter(INVERT) ;  // black becomes white and white becomes black

  // source https://forum.processing.org/one/topic/turn-white-pixels-transparent.html  
  //  avec des ajustements pour conserver les niveaux de gris
  
  int x, y , i ; // pour lire les pixels  
  float red , green , blue,  alpha  ;
  boolean lightGrey = false ;
  
  for( x = 0; x < img.width; x++ ){
    
    for( y = 0; y < img.height; y++ ){

      i = ( ( y * img.width ) + x );
      
      red = red(img.pixels[i]) ;
      green = green(img.pixels[i]) ;
      blue = blue(img.pixels[i]) ;
      // alpha = alpha(img.pixels[i]) ;
      
      if ( red < 150 ) {
      
        if ( ( red == green ) && ( red == blue ) ) {
      
          lightGrey = true ;
      
        } 
      
      } else if (red >= 150)  {      
        
        // it's either not grey or it's dark grey
        lightGrey = false ;
      }

      if( !lightGrey ){
      
        // dark grey turned to same color without transparency
        result.pixels[i] = color( red, green, blue, 255 );
      } else {
        // la couleur est transformée en noire transparent
        result.pixels[i] = color( 0, 0, 0, 0 );
      }
    }
  }

  return result ; // une image blanche avec tout le reste transparent
  
}

Aparté théorique sur la notion de « niveau de gris »

Lorsqu’on parle de niveau de gris, on a une couleur qui a deux caractéristiques :

  • les valeurs de R, G et B sont identiques ;
  • le niveau correspond au ratio entre la valeur de R (ou G ou B) et 256 : R = 126 correspond à un niveau de gris de 50%.

Pour une couleur (obtenue par exemple avec c = img.get(160, 190);  comme précédemment), on peut savoir quelle sont les valeurs de rouge, vert et bleu, et même transparence, avec les fonctions respectivement red(), green(c), blue(c), alpha(c).

Ce sont ces caractéristiques qu’on utilise pour rendre une image teintable, avec nuances.

Rendre une image teintable et transparente

C’est réalisé avec la fonction fnbwTransp. Cette fonction est réalisée sur une image PImage img et renvoie une PImage puisqu’elle est définie comme PImage fnbwTransp( PImage img ) {} .

La « moulinette » qui transforme les pixels noirs en pixels transparents est inspirée d’une discussion sur le forum processing.  L’idée générale en est la suivante :

  • on crée une image vide appelée result, de dimensions celles de img (notre source) et au format ARGB, donc avec prise en compte de la transparence Alpha. Cette image est créée avec result = createImage( img.width, img.height, ARGB );
  • on balaie l’image source, img, pixel par pixel.
    • Si la couleur du pixel est noir – color( 0, 0, 0 )  – alors la couleur du pixel correspondant dans l’image result est  color( 0, 0, 0, 0 )  – noir aussi mais transparent.
    • Si la couleur du pixel n’est pas le noir, alors la couleur du du pixel correspondant dans l’image result est la couleur du pixel d’origine de img.

La fonction retourne l’image résultante, une image dont tous les pixels noirs sont devenus transparents.

Rendre une image teintable, avec nuances, et transprente

On utilise là la fonction fnGreyTransp. Elle est semblable à fnbwTransp mais elle gère des niveaux d’intensité de la couleur. On utilise le même principe que dans fnbwTransp  mais la différence est dans les décisions lors du balayage des pixels d’img :

  • si le niveau de rouge est inférieur à 150 (on a un gris variant de très clair à assez clair)
    • si rouge = vert = bleu, alors on est en niveau de gris et  lightGrey = true  ;
  • si le niveau de rouge est > 150 ou bien rouge n’est pas identique à vert et bleu, alors   lightGrey = false  ;

Et ensuite on a deux possibilités :

  • si   lightGrey = true , alors le pixel correspondant de result est noir et totalement transparent (valeur 0 de transparence)
  • si   lightGrey = false , alors on donne au pixel correspondant de result l’exacte valeur de la couleur du pixel de img, avec une transparence nulle (valeur 255 de transparence).

Et maintenant !?

J’aimerais bien faire la même chose en p5.js pour avoir des sketch plus facilement présentables en ligne.

Mais avant, je vais probablement explorer la gestion des couches avec PGraphics, et aussi des masques, avec mask().

Une extension pour afficher des sketch Processing

Une extension pour afficher des sketch Processing

J’essaie ici d’afficher un visuel écrit en processing (fichier .pde) tel quel sur mon site WordPress. J’utilise l’extension ProcessingJS for WordPress

Installer et activer l’extension

Installer et activer l’extension ProcessingJS for WordPress

Sous le nom de l’extension, dans la page des extensions, il y a un lien vers une page « instructions ». C’est là que j’ai copié puis collé les shortcodes à utiliser. Mais c’est la même chose que sur la page de présentation de l’extension.

Essai n°1 : taper le code directement dans le contenu WordPress

Je copie et colle ce qui est proposé par l’auteur de l’extension :

[pjs4wp] void setup()
{
size(200, 200);
text(« Hello world! », 50, 50);
}
[/pjs4wp]

Et voilà le résultat, moche mais simple !

Essai n°2 : lire un fichier processing (.pde)

Le sketch Processing qui suit est inspiré d’un remake de Pol Guezennec de l’oeuvre #118 de Sol Lewitt.

J’ai un fichier d’essai sollewitt4.pde. Je le renomme sollewitt4.js sans rien changer d’autre que le type de fichier (pde devient js).

Dans la bibliothèque de média, je charge ce fichier puis je note son url (https://knowledge.parcours-performance.com/wp-content/uploads/2017/11/sollewitt4.js).

Ensuite je saisis le shortcode suivant :

[pjs4wp url="/wp-content/uploads/2017/11/sollewitt4.js" bordercolor="#000"][/pjs4wp].

Voici le résultat :

On voit que l’écran ne s’adapte pas à la page (pas responsive) – L’écran fait 1200 px par 800 px comme défini dans le sketch.

Avec un éditeur de texte type notepad++, je modifie donc la taille de l’écran défini dans setup, pour le ramener à 600px par 400 px, je charge le nouveau fichier dans les médias, sollewitt4-600-400.js  et j’utilise le shortcode [pjs4wp url="/wp-content/uploads/2017/11/sollewitt4-600-400.js" bordercolor="#000"][/pjs4wp]

Et voilà !

Les limites de l’extension

L’écran de visualisation du sketch Processing ne paraît pas responsive, en tous cas pas lorsqu’il est défini en « dur », en disant explicitement size(1200, 800);

Si j’essaie en remplaçant la taille de l’écran par fullScreen(); 

Ca ne fonctionne pas du tout…

Donc pour l’instant les limites de cette extension sont :

  • impossible d’utiliser une fenêtre responsive ;
  • pas de possibilité d’avoir des fichiers associés au script.

Mais je suis déjà très contente d’obtenir ce résultat.

Je ferai d’autres essais ultérieurement.

Découverte de Processing

Découverte de Processing

J’ai entendu parler plusieurs fois de Processing, sans trop savoir à quoi ça pouvait me servir ni comment l’utiliser. Et puis je me suis inscrite au cours « Art Numérique » des Beaux Arts de Quimper (site de l’école ou site de partage du cours). Et je m’y met.

Dans ce premier article sur Processing, dans la série , je vais simplement mettre des liens qui m’ont paru intéressants vers d’autres sites qui parlent de Processing.

Que peut-on faire avec Processing ?

Une réalisation d’une ancienne élève du cours Art numérique : ici sur le site du cours.

Le site Beautiful Programming qui présente de très beaux exemples d’art génératif et interactif. J’aime tout particulièrement le « Infinite Arboretum » qui dessine automatiquement un arbre et le modifie selon ce que l’on fait avec la souris ou le clavier :

Un exemple d’arbre généré automatiquement. En cliquant, vous allez sur le site de l’auteur.

 

Un site regroupe de nombreux exemples avec le code : OpenProcessing

Algorithm Ink de Aza Raskin montre une réalisation intéressante et son code (en cliquant sur le bouton edit)

Processing drawings présente des projets visuels et leur code.

Generative Art Links contient des liens vers des projets visuels mais aussi des logiciels utilisables pour de l’art numérique.

La page « processing : Ressources Processing et tutoriels en ligne » contient de nombreux liens vers des sites intéressants.

Le site GuruBlog contient une galerie de sketch Processing.

Le site de Jm Commenge contient des exemples intéressants.

Ce site présente un projet associant Processing et Twitter pour animer du plancton.

Sur CodePen.io on peut voir de nombreuses réalisations utilisant Processing.js et taggée « processing »., ou contenant « p5 » Par exemple cette réalisation de Kultur Design, une animation de polygones SVG :

See the Pen SVG Polygon Animation by Kultur Design (@kulturdesign) on CodePen.

Sur CodePen, Lionel Radisson a réuni une collection liée à Nature of Code (le livre je pense).

J’ai entamé une collection de travaux sur CodePen en lien avec p5.js, visible ici.

Références sur le code Processing

Processing.org est le site de référence pour Processing, et aussi pour p5.js une bibliothèque JavaScript qui lit le code Processing sur un site ou une application web.

The Nature of Code permet d’acheter un livre très intéressant sur Processing et aussi de le lire en ligne.

Contributed Tools, Projects, Demos · processing/p5.js est le Wiki pour p5.js, .

Education · processing/p5.js est un Wiki consacré à l’enseignement de p5.js pour Processing.

Des cours sur Processing sont disponibles sur le site de Electronic Media Studio. Le menu « Lectures » contient plusieurs cours et dans la barre latérale, on trouve de nombreux exemples d’exercices.

Le site Arts Numériques contient de nombreux tutoriels sur Processing.

Processing Quick Start nous parle de Processing.js, une autre bibliothèque JavaScript pour Processing.

Le site du livre Design Génératif : un livre excellent, mais cher. J’ai pu l’emprunter à la bibliothèque de l’école des Beaux Arts de Quimper. On peut télécharger tous les exemples de codes sur le site. Beaucoup sont très très intéressants.

Pour démarrer dans l’utilisation de svg créés avec Inkscape et utilisés dans Processing : how to use svg for rigged 2D-animations in processing.

Un lycée a mis en ligne quelques exemples et exercices lié à des oeuvres d’art.

Un cours en ligne, en anglais, très bien fait pour apprendre rapidement les rudiments de Processing : Le cours CS1335 de Karen Doore

Placer un sketch Processing dans un site WordPress

Embedding p5.js processing sketches in WordPress posts sur le site weegreenblobbie.com

Embedding p5.js · processing/p5.js sur GitHub explique comment on peut visualiser du code  Processing / p5.js sur un navigateur avec iframe dans une page html ou des sites de partage et visualisation de code tels que CodePen ou JSFiddle.

Creative Computation » Embedding a Processing Sketch expose diverses solutions pour placer un sketch Processing dans une page html.

Enfin, deux extensions WordPress paraissent intéressantes pour intégrer du Processing à un site :

Processing.js for WordPress et ProcessingJS for WordPress. Elles sont toutes deux gratuites et ont été mises à jour récemment.

Javascript ou Processing ?

Les bibliothèques javascript citées plus haut (P5.js ou processing.js) permettent de créer des scripts visualisables en ligne. C’est quand même très pratique….

Si on veut apprendre P5.js, un excellent tutoriel en français a été réalisé par B2renger.

Processing et Arduino ou Raspberry Pi

Ce tutoriel, en anglais sur Instructables, explique comment utiliser un Raspberry Pi pour des sketchs Processing.  Adafruit a également fait un tutoriel sur l’utilisation du Pi avec Processing 3.

L’article « Récupérer une valeur analogique Arduino avec Processing » me parait être une bonne base pour démarrer. On peut remplacer les photo-résistances par d’autres capteurs analogiques.

Il y a aussi un manuel Floss spécifique à Arduino et un Chapitre Arduino du Manuel Floss sur Processing.

Pour le Raspberry Pi, on peut lire Introduction to Processing sur le site Raspberry Pi Projects, ou Processing on the Raspberry Pi & PiTFT  sur le site Adafruit Learning System.

Et pour un ensemble Arduino Uno, Raspberry Pi utilisant Processing : Raspberry Pi + Processing + CT UNO (Part 2) , un tutorial de Cytron.

On peut contrôler l’arduino en C++ et simplement le faire communiquer avec Processing ou le contrôler en Processing. Voir les explications de Arduino Playground ou Learn Sparkfun.

Le site Arts Numériques contient  également des tutoriels sur Arduino.

Art et Arduino

Mes projets

Me familiariser avec Processing :

Et je veux également voir comment intégrer ces travaux dans des pages web. J’essaierai donc CodePen, JSFiddle et les deux extensions WordPress ci-dessus.

Ca m’intéresserait aussi de savoir faire les images suivantes, il me semble que Processing est bien adapté pour :

geralt / Pixabay

intographics / Pixabay

 

 

geralt / Pixabay

 

Vous verrez la suite dans les prochains articles de cette série !