====== Comment transformer une image figurative (.jpg) en image abstraite (.svg) ====== * Nom de l'étudiant: Bonnet Jean-Alexis * Date: 2020-2021 * Lien vers le karma: [[wiki:karma:jean-alexis-bonnet:accueil|Jean-Alexis Bonnet]] ---- ===== Commentaire général - Damien MUTI DESGROUAS ===== Vous avez fait un gros travail de recherche. Il faut expliquer la démarche en tant que Designer : Que cherchez-vous à montrer ? On ne comprend pas bien pourquoi vous proposez ces programmes. Préciser l'apport personnel dans les programmes. IMPORTANT : Faire un lien sur un lien sur un fichier .zip contenant vos programme et vos images. Il faut que le programme soit intégralement téléchargeable et fonctionnel, ce qui n'est pas le cas dans votre tutoriel. Étape 1 : Vous expliquez brièvement le fonctionnement du programme qui est composé de deux onglets. Vous devrez mettre des images d'illustration. Le programme est bien commenté. Cependant, le fonctionnement du programme est délicat et ne fonctionne pas toujours. Les images rendus sont quasiment toujours blanches lorsqu'on modifie la valeur des curseurs. Vous devriez mettre une image d'illustration pour expliquer le fonctionnement de chacun des curseurs. Étape 2: Vous expliques brièvement vos intensions. Il manque des illustrations. Le programme est donnée en 3 partie et n'est pas commenté. Après reconstruction du programme (qui devrait être fourni d'un bloc au format .zip), celui-ci n'est pas fonctionnel. C'est dommage. Vous ne donnez aucune source pour votre travail et vos programmes. On ne sais pas quel est l'apport personnel aux programmes. ===== Etape 1 : jpg to svg ===== Pour transformer l'image bitmap en image vectorielle, j'ai travaillé sur deux codages Processing. ==== 1er codage: ==== Le premier transforme cette image en un tracé unique type sillon de vinyle. Il utilise pour cela les noirs de l'image et les interprète en variation du tracé (plus l'échantillon est sombre, plus l'amplitude est importante) il est compris en deux onglets: **Onglet1_a:** d'après la retranscription du code, cette partie sert génère un éditeur d'image permettant l'import d'une image, la visualisation de l'effets appliqué et quatre contrôleurs (un pour importer une image, un d’amplitude de l'onde, un autre d'approche de l'onde et le dernier pour générer le visuel). import controlP5.*; // Import de P5.js comme interface graphique import java.io.File; // Api d'import et d'export de fichier pour java ControlP5 cp5; File file; Textarea feedbackText; String locImg = ""; // Localisation absolue de l'image d'import PImage sourceImg; // Localisation de l'export en SVG PImage displayImg; // Choix de l'image comme fond color c; // Variable de couleur (afin de transformer les noirs en tracés) float b; // Nombre à virgule pour échantillonner la brillance. float dist = 5; // Distance entre les cercles float radius = dist/2; // Rayon actuel float aradius; // Rayon (amplitude) avec une luminosité supérieur (blanc) float bradius; // Rayon (amplitude) avec une luminosité inférieure (noir) float alpha = 0; // Rotation initiale float density = 75; // Densité int counter=0; // Compteur d’échantillons float ampScale = 2.4; // Contrôleur d'amplitude float x, y, xa, ya, xb, yb; // Nombre à virgule x, y, xa, ya, xb et yb float k; // Rayon actuel float endRadius; // Plus grande valeur dont la spirale a besoin pour couvrir l'image color mask = color (255, 255, 255); // Couleur non prise en compte par la spirale (blanc) (gamme de gris) PShape outputSVG; // Forme SVG de sortie int c1, c2; // Entiers colorimétrique float n, n1; // ?? String outputSVGName; // Nom de la sortie SVG String imageName; // Nom de l'image importé String imagePath; // Chemin de l'image importé //Initialisation void setup() { size(1024, 800); background(235); noStroke(); fill(245); rect(25, 25, 125, 750); fill(245); rect(175, 25, 537, 750); cp5 = new ControlP5(this); // Créer nouveau bouton nommé 'open' cp5.addButton("Open") .setLabel("Open File") .setBroadcast(false) .setValue(0) .setPosition(37, 37) .setSize(100, 19) .setBroadcast(true) ; // Créer nouveau bouton nommé 'Draw' cp5.addButton("Draw") .setLabel("Generate SVG") .setBroadcast(false) .setValue(100) .setPosition(37, 62) .setSize(100, 19) .setBroadcast(true) ; // Créer nouveau bouton nommé 'cleardisplay' cp5.addButton("ClearDisplay") .setLabel("Clear Display") .setBroadcast(false) .setValue(200) .setPosition(37, 87) .setSize(100, 19) .setBroadcast(true) ; //Créez un nouveau champ de texte pour afficher les commentaires du contrôleur feedbackText = cp5.addTextarea("feedback") .setSize(512, 37) .setText("Load image to start") //.setFont(createFont("arial", 12)) .setLineHeight(14) .setColor(color(128)) .setColorBackground(color(235, 100)) .setColorForeground(color(245, 100)) .setPosition(187, 37) ; //Create a new slider to set amplitude of waves drawn: default value is 2.4 cp5.addSlider("amplitudeSlider") .setBroadcast(false) .setLabel("Wave amplitude") .setRange(1, 8) .setValue(2.4) .setPosition(37, 125) .setSize(100, 19) .setSliderMode(Slider.FLEXIBLE) .setDecimalPrecision(1) .setBroadcast(true) ; // repositionner le 'potentiomètre' du curseur du contrôleur 'slider' cp5.getController("amplitudeSlider").getCaptionLabel().align(ControlP5.LEFT, ControlP5.TOP_OUTSIDE).setPaddingX(0).setColor(color(128)); cp5.addSlider("distanceSlider") .setBroadcast(false) .setLabel("Distance between rings") .setRange(5, 10) .setValue(5) .setNumberOfTickMarks(6) .setPosition(37, 163) .setSize(100, 19) .setSliderMode(Slider.FLEXIBLE) .setBroadcast(true) ; // repositionner le 'potentiomètre' du curseur du contrôleur 'slider' cp5.getController("distanceSlider").getCaptionLabel().align(ControlP5.LEFT, ControlP5.TOP_OUTSIDE).setPaddingX(0).setColor(color(128)); } //Gestionnaire d'événements de contrôle de bouton public void controlEvent(ControlEvent theEvent) { println(theEvent.getController().getName()); n = 0; } // Événement de bouton - Ouvrir: boîte de dialogue Ouvrir le fichier image public void Open(int theValue) { clearDisplay(); locImg=""; selectInput("Select a file to process:", "fileSelected"); } // Bouton événement - Dessiner: convertir un fichier image en SVG public void Draw(int theValue) { if (locImg == "") { feedbackText.setText("no image file is currently open!"); feedbackText.update(); } else { resizeImg(); // Enregistrement dans le même dossier que l'image d'origine outputSVGName=imageName+".svg"; drawSVG(); // Doesn't work displaySVG(); } } // Effacer l'affichage de toutes les images chargées (bouton) public void ClearDisplay(int theValue) { clearDisplay(); } //Recevoir la valeur d'amplitude du curseur public void amplitudeSlider(float theValue) { ampScale = theValue; println(ampScale); } //Recevoir la valeur de la distance d'onde du curseur public void distanceSlider(int theValue) { dist = theValue; println(dist); } //Redessiner les éléments d'arrière-plan pour supprimer le précédent PImage chargé void drawBackground () { noStroke(); background(235); fill(245); rect(25, 25, 125, 750); fill(245); rect(175, 25, 537, 750); } void draw() { //System.gc(); } //Ouvre la fenêtre de sélection du fichier d'entrée et dessine l'image sélectionnée à l'écran void fileSelected(File selection) { if (selection == null) { feedbackText.setText("Window was closed or the user hit cancel."); feedbackText.update(); } else { locImg=selection.getAbsolutePath(); feedbackText.setText(locImg+" was succesfully opened"); feedbackText.update(); sourceImg=loadImage(locImg); displayImg=loadImage(locImg); drawImg(); // Obtenir le nom de fichier de l'image et supprimer l'extension // Aucune vérification si l'extension existe file = new File(locImg); imageName = file.getName(); imageName = imageName.substring(0, imageName.lastIndexOf(".")); } } // Fonction pour créer un fichier SVG à partir d'un fichier image chargé void drawSVG() { // Calcule le premier point // Le centre k = density/radius ; alpha += k; radius += dist/(360/k); x = aradius*cos(radians(alpha))+sourceImg.width/2; y = -aradius*sin(radians(alpha))+sourceImg.height/2; // Coin le plus éloigné de l'image? endRadius = sqrt(pow((sourceImg.width/2), 2)+pow((sourceImg.height/2), 2)); shapeOn = false; openSVG (); // Avons-nous atteint le coin le plus éloigné de l'image? while (radius < endRadius) { k = (density/2)/radius ; alpha += k; radius += dist/(360/k); x = radius*cos(radians(alpha))+sourceImg.width/2; y = -radius*sin(radians(alpha))+sourceImg.height/2; // Image ouverte? // Si c'est le cas, vérifiez si la forme est créé. if ((x>=0) && (x00) && (y"); } shapeOn = false; } else { // Add vertices to shape if (shapeOn == false) { openPolyline (); shapeOn = true; } vertexPolyline (xa, ya); vertexPolyline (xb, yb); } } else { // Nous sommes en dehors de l'image donc fermez la forme si elle est ouverte (la ligne) if (shapeOn == true) { closePolyline (); output.println(""); shapeOn = false; } } } if (shapeOn) closePolyline(); closeSVG (); println(locImg+" was processed and saved as "+outputSVGName); feedbackText.setText(locImg+" was processed and saved as "+outputSVGName); feedbackText.update(); System.gc(); } void resizeImg() { if ( sourceImg.width > sourceImg.height) { sourceImg.resize (1200, 0); } else { sourceImg.resize (0, 1200); } } void resizedisplayImg() { if ( displayImg.width > displayImg.height) { displayImg.resize (512, 0); } else { displayImg.resize (0, 512); } } // Clear et modifier l'extension pour les prochains export ? void displaySVG () { clearDisplay(); String svgLocation = sketchPath("")+""+"\\"+imageName+".svg"; outputSVG = loadShape(svgLocation); println("loaded SVG: "+sketchPath("")+"Output"+"\\"+outputSVGName+".svg"); shape(outputSVG, 187, 85, outputSVG.width/2, outputSVG.height/2); feedbackText.setText(locImg+" was processed and saved as "+outputSVGName); feedbackText.update(); } void drawImg () { resizedisplayImg(); //background(255); set(187, 85, displayImg); } void clearDisplay() { background(235); drawBackground(); feedbackText.setText("Load image to start"); System.gc(); } **Onglet1_b:** Gestion de l'encodage SVG. PrintWriter output; // Flux de sortie pour l'exportation SVG int shapeLen = 1000; // Nombre maximum de sommets par forme int shapeCount = 1; boolean shapeOn = false; // Garde la trace d'une forme ouverte ou fermée void openSVG () { output = createWriter(outputSVGName); output.println(""); output.println(""); output.println(""); } void openPolyline () { output.println(" "); beginShape (); openPolyline (); } output.print(" "); output.print(x); output.print(","); output.println(y); shapeCount++; } void closePolyline () { output.println(" \" />"); shapeCount = 1; } void closeSVG () { output.println(""); output.flush(); // Écrit les données restantes dans le fichier (le conclut) output.close(); // Ferme le fichier } ==== 2eme codage: ==== Le second programme remplace les zones sombre de l'image par des gribouillis aléatoire de tracés, plus la zone est sombre plus ils sont proches, moins elle est sombre moins nombreux ils sont. ils se composent de 3 onglets. final int squiggle_total = 400; // Total times to pick up the pen final int squiggle_length = 600; // Too small will fry your servo final int half_radius = 3; // How grundgy final int adjustbrightness = 8; // How fast it moves from dark to light, over draw final float sharpie_dry_out = 0.25; // Simulate the death of sharpie, zero for super sharpie final String pic_path = "pics/jah.jpg"; //Every good program should have a shit pile of badly named globals. int screen_offset = 4; float screen_scale = 1.0; int steps_per_inch = 25; int x_old = 0; int y_old = 0; PImage img; int darkest_x = 60; int darkest_y = 60; float darkest_value; int squiggle_count; int x_offset = 0; int y_offset = 0; float drawing_scale; float drawing_scale_x; float drawing_scale_y; int drawing_min_x = 9999999; int drawing_max_x = -9999999; int drawing_min_y = 9999999; int drawing_max_y = -9999999; int center_x; int center_y; boolean is_pen_down; PrintWriter OUTPUT; // instantiation of the JAVA PrintWriter object. import processing.pdf.*; /////////////////////////////////////////////////////////////////////////////////////////////////////// void setup() { size(900, 975, P2D); noSmooth(); colorMode(HSB, 360, 100, 100, 100); background(0, 0, 100); frameRate(120); OUTPUT = createWriter("gcode.txt"); beginRecord(PDF, "output.pdf"); pen_up(); setup_squiggles(); img.loadPixels(); } /////////////////////////////////////////////////////////////////////////////////////////////////////// void draw() { noFill(); scale(screen_scale); random_darkness_walk(); if (squiggle_count >= squiggle_total) { endRecord(); grid(); dump_some_useless_stuff_and_close(); noLoop(); } } /////////////////////////////////////////////////////////////////////////////////////////////////////// void setup_squiggles() { img = loadImage(sketchPath("") + pic_path); // Load the image into the program img.loadPixels(); drawing_scale_x = image_size_x / img.width; drawing_scale_y = image_size_y / img.height; drawing_scale = min(drawing_scale_x, drawing_scale_y); println("Picture: " + pic_path); println("Image dimensions: " + img.width + " by " + img.height); println("adjustbrightness: " + adjustbrightness); println("squiggle_total: " + squiggle_total); println("squiggle_length: " + squiggle_length); println("Paper size: " + nf(paper_size_x,0,2) + " by " + nf(paper_size_y,0,2) + " " + nf(paper_size_x/25.4,0,2) + " by " + nf(paper_size_y/25.4,0,2)); println("Max image size: " + nf(image_size_x,0,2) + " by " + nf(image_size_y,0,2) + " " + nf(image_size_x/25.4,0,2) + " by " + nf(image_size_y/25.4,0,2)); println("Calc image size " + nf(img.width * drawing_scale,0,2) + " by " + nf(img.height * drawing_scale,0,2) + " " + nf(img.width * drawing_scale/25.4,0,2) + " by " + nf(img.height * drawing_scale/25.4,0,2)); println("Drawing scale: " + drawing_scale); // Used only for gcode, not screen. x_offset = int(-img.width * drawing_scale / 2.0); y_offset = - int(paper_top_to_origin - (paper_size_y - (img.height * drawing_scale)) / 2.0); println("X offset: " + x_offset); println("Y offset: " + y_offset); // Used only for screen, not gcode. center_x = int(width / 2 * (1 / screen_scale)); center_y = int(height / 2 * (1 / screen_scale) - (steps_per_inch * screen_offset)); } /////////////////////////////////////////////////////////////////////////////////////////////////////// void grid() { // This will give you a rough idea of the size of the printed image, in inches. // Some screen scales smaller than 1.0 will sometimes display every other line // It looks like a big logic bug, but it just can't display a one pixel line scaled down well. stroke(0, 50, 100, 30); for (int xy = -30*steps_per_inch; xy <= 30*steps_per_inch; xy+=steps_per_inch) { line(xy + center_x, 0, xy + center_x, 200000); line(0, xy + center_y, 200000, xy + center_y); } stroke(0, 100, 100, 50); line(center_x, 0, center_x, 200000); line(0, center_y, 200000, center_y); } /////////////////////////////////////////////////////////////////////////////////////////////////////// void dump_some_useless_stuff_and_close() { println ("Extreams of X: " + drawing_min_x + " thru " + drawing_max_x); println ("Extreams of Y: " + drawing_min_y + " thru " + drawing_max_y); OUTPUT.flush(); OUTPUT.close(); } /////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////// void pen_up() { String buf = "G1 Z0"; is_pen_down = false; OUTPUT.println(buf); endShape(); } /////////////////////////////////////////////////////////////////////////////////////////////////////// void pen_down() { String buf = "G1 Z1"; is_pen_down = true; OUTPUT.println(buf); beginShape(); } /////////////////////////////////////////////////////////////////////////////////////////////////////// void move_abs(int x, int y) { String buf = "G1 X" + nf(x,0) + " Y" + nf(y,0); if (x < drawing_min_x) { drawing_min_x = x; } if (x > drawing_max_x) { drawing_max_x = x; } if (y < drawing_min_y) { drawing_min_y = y; } if (y > drawing_max_y) { drawing_max_y = y; } if (is_pen_down) { stroke(0, 100, 0, 100-(squiggle_count * sharpie_dry_out)); vertex(x + center_x,y + center_y); } x_old = x; y_old = y; OUTPUT.println(buf); } /////////////////////////////////////////////////////////////////////////////////////////////////////// void random_darkness_walk() { int x, y; find_darkest(); x = darkest_x; y = darkest_y; squiggle_count++; find_darkest_neighbor(x, y); move_abs(int(darkest_x*drawing_scale+x_offset), int(darkest_y*drawing_scale+y_offset)); pen_down(); for (int s = 0; s < squiggle_length; s++) { find_darkest_neighbor(x, y); lighten(adjustbrightness, darkest_x, darkest_y); move_abs(int(darkest_x*drawing_scale+x_offset), int(darkest_y*drawing_scale+y_offset)); x = darkest_x; y = darkest_y; } pen_up(); } /////////////////////////////////////////////////////////////////////////////////////////////////////// void find_darkest_neighbor(int start_x, int start_y) { float darkest_neighbor = 256; int min_x, max_x, min_y, max_y; min_x = constrain(start_x - half_radius, half_radius, img.width - half_radius); min_y = constrain(start_y - half_radius, half_radius, img.height - half_radius); max_x = constrain(start_x + half_radius, half_radius, img.width - half_radius); max_y = constrain(start_y + half_radius, half_radius, img.height - half_radius); // One day I will test this to see if it does anything close to what I think it does. for (int x = min_x; x <= max_x; x++) { for (int y = min_y; y <= max_y; y++) { // Calculate the 1D location from a 2D grid int loc = x + y*img.width; float d = dist(start_x, start_y, x, y); if (d <= half_radius) { float r = red (img.pixels[loc]) + random(0, 0.01); // random else you get ugly horizontal lines if (r < darkest_neighbor) { darkest_x = x; darkest_y = y; darkest_neighbor = r; } } } } } /////////////////////////////////////////////////////////////////////////////////////////////////////// void find_darkest() { darkest_value = 256; for (int x = half_radius; x < img.width - half_radius; x++) { for (int y = half_radius; y < img.height - half_radius; y++ ) { // Calculate the 1D location from a 2D grid int loc = x + y*img.width; float r = red (img.pixels[loc]); if (r < darkest_value) { darkest_x = x; darkest_y = y; darkest_value = r; } } } } /////////////////////////////////////////////////////////////////////////////////////////////////////// void lighten(int adjustbrightness, int start_x, int start_y) { int min_x, max_x, min_y, max_y; min_x = constrain(start_x - half_radius, half_radius, img.width - half_radius); min_y = constrain(start_y - half_radius, half_radius, img.height - half_radius); max_x = constrain(start_x + half_radius, half_radius, img.width - half_radius); max_y = constrain(start_y + half_radius, half_radius, img.height - half_radius); /* for (int x = min_x; x <= max_x; x++) { for (int y = min_y; y <= max_y; y++) { float d = dist(start_x, start_y, x, y); if (d <= half_radius) { // Calculate the 1D location from a 2D grid int loc = y*img.width + x; float r = red (img.pixels[loc]); r += adjustbrightness / d; r = constrain(r,0,255); color c = color(r); img.pixels[loc] = c; } } } */ // Hey boys and girls its thedailywtf.com time, yeah..... lighten_one_pixel(adjustbrightness * 6, start_x, start_y); lighten_one_pixel(adjustbrightness * 2, start_x + 1, start_y ); lighten_one_pixel(adjustbrightness * 2, start_x - 1, start_y ); lighten_one_pixel(adjustbrightness * 2, start_x , start_y + 1); lighten_one_pixel(adjustbrightness * 2, start_x , start_y - 1); lighten_one_pixel(adjustbrightness * 1, start_x + 1, start_y + 1); lighten_one_pixel(adjustbrightness * 1, start_x - 1, start_y - 1); lighten_one_pixel(adjustbrightness * 1, start_x - 1, start_y + 1); lighten_one_pixel(adjustbrightness * 1, start_x + 1, start_y - 1); } /////////////////////////////////////////////////////////////////////////////////////////////////////// void lighten_one_pixel(int adjustbrightness, int x, int y) { int loc = (y)*img.width + x; float r = red (img.pixels[loc]); r += adjustbrightness; r = constrain(r,0,255); color c = color(r); img.pixels[loc] = c; } /////////////////////////////////////////////////////////////////////////////////////////////////////// ===== Etape 2 : Figuratif vers abstrait =====