-- If(){design}else{art} » Non classé

Implémentation de l’algorithme de diffusion Floyd-Steinberg

Article publié le : mardi 28 mars 2017. Rédigé par : Jean-no

L’algorithme de diffusion Floyd-Steinberg, inventé en 1976, permet de tramer une image de manière intelligente. On peut lire son fonctionnement en détails sur Wikipédia.
L’image qui suit nous permet de comparer un détail d’image transformée en image 1bit (noir ou blanc) avec cet algorithme (gauche) et sans (droite).

Le programme qui suit est une implémentation de cet algorithme pour Processing. Il n’est pas forcément rapide mais il fonctionne bien, en niveaux de gris. Lorsque la variable colorsNumber est 2, il n’y a que deux couleurs qui seront employées : le blanc et le noir.


En entrant l’image en couleurs ci-dessus (« montagne.png »), on obtient l’image en noir et blanc qui suit et qui sera enregistrée sous le nom « floyd_steinberg.png ».

float palette[];
float mu1 = 7.0/16, mu2 = 3.0/16, mu3=5.0/16, mu4=1.0/16;


void setup() {
  size(600, 480);
  background(255);
  int colorsNumber = 2; // ici on décide le nombre de couleurs de la palette
  palette=getPalette(0,255,colorsNumber);  
}

void draw() {
  // l'image à charger s'appelle "montagne.jpg" et doit 
  // se trouver dans le dossier "data"
  PGraphics g= floydSteinberg(loadImage("montagne.jpg")); 
  image(g,0,0);
  g.save("floyd_steinberg.png");
  noLoop();
}

PGraphics floydSteinberg(PImage img) { 
  PGraphics g = createGraphics(img.width, img.height);
  g.beginDraw();
  g.background(0);
  float[][] pixelz;
  pixelz = new float[img.width][img.height];
  for (int y=0; y<img.height; y++) {
    for (int x=0; x<img.width; x++) {
      pixelz[x][y]= brightness(img.get(x, y));
    }
  }
  for (int y=0; y<img.height; y++) {
    for (int x=0; x<img.width; x++) {
      float ancien=pixelz[x][y];  
      float[] nouveau = couleur_la_plus_proche(ancien);

      pixelz[x][y] = nouveau[0];
      float error= nouveau[1];
      boolean xMax=(x==img.width-1);
      boolean xMin=(x==0);
      boolean yMax=(y==img.height-1);
      if (!xMax) pixelz[x+1][y] += mu1* error;
      if (!xMin && !yMax) pixelz[x-1][y+1] += mu2* error;
      if (!yMax)pixelz[x][y+1] += mu3* error;
      if (!xMax && !yMax)pixelz[x+1][y+1] += mu4* error;
    }
  }
  for (int y=0; y<img.height; y++) {
    for (int x=0; x<img.width; x++) {
      float b=pixelz[x][y];
      g.stroke(b);
      g.point(x, y);
    }
  }
  g.endDraw();
  return g;
}

float[] couleur_la_plus_proche(float b) {
  float[] paire = {0, 0};
  float min=256; 
  for (int i=0; i<palette.length; i++) {
    float c=palette[i];
    float dis = abs(b-c);
    if (dis<min) {
      min=dis;
      paire[0]=c;
    }
  } 
  paire[1]=b-paire[0];
  return paire;
}

float[] getPalette(float depart, float fin, int n){
  if(n<2)n=2;
  float[] toreturn = new float[0];
  float diff = (fin-depart)/(n-1);
  for(int a=0;a<n;a++){
    toreturn = (float[]) append (toreturn, a*diff);
  } 
  return toreturn;
}

La valeur de chaque pixel (ligne par ligne depuis le haut jusqu’en bas et de gauche à droite à l’intérieur de chaque ligne) est lue et comparé à sa valeur indexée la plus proche. Si la valeur du pixel est 120 et que la valeur la plus proche dans ma palette est 128, la marge (dite « erreur ») sera de -8. Nous allons alors ajouter :
7/16e de -8 à la valeur du pixel (x+1, y)
3/16e de -8 au pixel (x-1, y+1)
5/16e de -8 à (x,y+1)
1/16e de -8 à (x+1,y+1)

L’enregistrement au format SVG

Article publié le : samedi 13 février 2016. Rédigé par : Jean-no

Processing permet désormais l’enregistrement au format vectoriel SVG.
Le format SVG ne contient pas une matrice de points, comme les formats bitmap Jpeg, png, gif, bmp ou tiff, mais des commandes vectorielles. Pour cela, il serait impossible de se contenter d’une capture de l’espace d’affichage le font les commandes save() et saveFrame(). L’enregistrement au format SVG fonctionne en fait comme l’enregistrement au format PDF, qui est lui aussi un format de document vectoriel.

Voici un exemple qui commence par importer la librairie SVG, crée un écran au format 400×400 pixels, puis, à l’aide de la commande beginRecord(), démarre l’enregistrement au format SVG d’une image que nous choisissons de nommer « image.svg ». Ensuite, nous dessinons une ellipse de 100×100 pixels tous les 25 pixels, à l’aide d’une boucle.
Enfin, nous interrompons l’enregistrement avec endRecord().

import processing.svg.*;
size(400,400);
beginRecord(SVG, "image.svg");
  for(int a=50;a<350;a+=25){
     ellipse(a, a, 100,100);
  }
endRecord();

On le voit, l’image qui en résulte est bien une image vectorielle, qui peut être ouverte et éditée à l’aide d’un logiciel de dessin vectoriel tel que Adobe Illustrator ou que le logiciel libre Inkscape :

exemple_svg

Voici un second exemple, qui permet de remplacer la commande saveFrame() :

import processing.svg.*;

void setup(){
  size(500,500);
}

void draw(){
  beginRecord(SVG, "images/image"+nf(frameCount, 5)+".svg");
    ellipse(mouseX, mouseY, 100,100);
  endRecord();
}

À chaque rafraîchissement de l’écran (draw()), on enregistre ce qui se passe sur l’écran (en l’occurrence, le dessin d’une ellipse à la position de la souris) à l’intérieur d’un fichier.
nf(frameCount, 5) renvoie le numéro de frame, écrit avec cinq chiffre : 00001, 00002, 00003, etc. Les fichiers auront donc pour nom « image00001.svg », image00002.svg », « image00003.svg » et ainsi de suite.
Le nom du fichier est précédé de « images/ », ce qui est une astuce pour que toutes les images ne se retrouvent pas à l’intérieur du dossier du programme, mais à l’intérieur d’un de ses sous-dossiers, que nous choisissons de nommer « images ».

Placer un point sur un cercle et sur une sphère

Article publié le : dimanche 12 octobre 2014. Rédigé par : Jean-no

Il est facile de placer un point sur un cercle, par exemple avec les lignes qui suivent :

void setup(){
  size(500,500);
  point(250+cos(PI)*100, 250+sin(PI)*100);
}

…qui consistent à placer un point à 100 pixels du centre de l’écran (250, 250) et à l’angle PI (donc la moitié d’un cercle).
La formule est :
– abscisse de départ + cosinus (angle) * rayon // pour l’abscisse (x)
– ordonnée de départ + sinus (angle) * rayon // pour l’ordonnée (y)

Dans l’exemple qui suit, nous dessinons un trait de 200 pixels, partant du centre de l’écran, et aboutissant successivement à 0°, 1°, 2°, 3°,… et jusqu’à 359°.

float angle=0;

void setup(){
  size(500,500);
}

void draw(){
  background(255);
  angle+=TWO_PI/360;
  translate(width/2, height/2);
  line(0, 0, cos(angle)*200, sin(angle)*200);
}

Pour placer un point sur une sphère, qui sera repéré par trois coordonnées (abscisse, ordonnée et cote, ou x, y, z), il faut deux angles, que nous nommerons phi et theta.
La formule à appliquer est alors

– abscisse de départ + (sin(phi)*cos(theta))*rayon // pour l’abscisse (x)
– ordonnée de départ + (sin(phi)*sin(theta))*rayon // pour l’ordonnée (y)
– cote de départ + (cos(phi))*rayon // pour la cote (z)

Un exemple d’utilisation avec le programme qui suit, qui affiche une sphère de 24 longitudes x 24 latitudes, tournant sur elle-même :

float rx=0, ry=0;

void setup() {
  size(500, 500, P3D);
  strokeWeight(4);
}

void draw() {
  background(255);
  ry+=0.01;
  rx+=0.031;
  float r=200;
  translate(width/2, height/2);
  rotateX(rx);
  rotateY(ry);
  for (float phi=0; phi<TWO_PI; phi+=TWO_PI/24) {
    for (float theta=0; theta<TWO_PI; theta+=TWO_PI/24) { 
      float x=(sin(phi)*cos(theta))*r;
      float  y=(sin(phi)*sin(theta))*r;
      float z=(cos(phi))*r; 
      point(x, y, z);
    }
  }
}

Voici une capture d’écran :

points

les variables rx et ry correspondent à l’angle de rotation qui est appliqué à l’environnement, à partir du point de référence (width/2, height/2), c’est à dire le centre de l’écran.

Fichiers d’exemples à télécharger

Article publié le : mercredi 9 février 2011. Rédigé par : Jean-Michel

Télécharger les fichiers d’exemples (7.8Mo).

2 chapitres en téléchargement

Article publié le : mercredi 9 février 2011. Rédigé par : Jean-Michel

Les chapitres relatifs à l’interactivité souris et à la programmation objet ainsi que le sommaire détaillé de l’ouvrage « Processing, le code informatique comme outil de création » sont disponibles dans leur intégralité au format pdf aux adresses suivantes :

Chapitre 8: Interactivité Souris

Chapitre 16: Classes

Table des matières

Bientôt

Article publié le : mercredi 5 janvier 2011. Rédigé par : Jean-no

Sortie le 4 février 2010 de Processing : Le code informatique comme outil de création, par Jean-Michel Géridan et Jean-Noël Lafargue, éd. Pearson.

Le présent site (en préparation) sera un complément au livre et permettra, s’il en est besoin, de fournir à ses lecteurs des rectificatifs