Comment transformer une image figurative (.jpg) en image abstraite (.svg)

Nom de l'étudiant: Bonnet Jean-Alexis

Date: 2020-2021

Lien vers le karma: 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) && (x<sourceImg.width) && (y>00) && (y<sourceImg.height)) {
// Obtenez la couleur et la luminosité du pixel échantillonné
c = sourceImg.get (int(x), int(y));
b = brightness(c);
b = map (b, 0, 255, dist*ampScale, 0);
// Montez en fonction de la luminosité échantillonnée
aradius = radius+(b/dist);
xa = aradius*cos(radians(alpha))+sourceImg.width/2;
ya = -aradius*sin(radians(alpha))+sourceImg.height/2;
// Descendre en fonction de la luminosité échantillonnée
k = (density/2)/radius ;
alpha += k;
radius += dist/(360/k);
bradius = radius-(b/dist);
xb = bradius*cos(radians(alpha))+sourceImg.width/2;
yb = -bradius*sin(radians(alpha))+sourceImg.height/2;
// Si la couleur échantillonnée est égale à la couleur du masque, n'écrivez pas sur la forme
if (mask == c) {
if (shapeOn) {
closePolyline ();
output.println("<!-- Mask -->");
}
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("<!-- Out of bounds -->");
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("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
output.println("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">");
output.println("<svg width=\"1200 px\" height=\"1200 px\" viewBox=\"0 0 1200 1200\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">");
}
void openPolyline () {
output.println(" <polyline fill=\"none\" stroke=\"#000000\" points=\"");
}
void vertexPolyline (float x, float y) {
// If the shape has gotten too long close it and open a new one
if (shapeCount%shapeLen == 0 && shapeOn) {
endShape ();
closePolyline ();
output.println("<!-- Maximum Shape Length -->");
beginShape ();
openPolyline ();
}
output.print(" ");
output.print(x);
output.print(",");
output.println(y);
shapeCount++;
}
void closePolyline () {
output.println(" \" />");
shapeCount = 1;
}
void closeSVG () {
output.println("</svg>");
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