diff --git a/Realite Virtuelle/Exos/2023-03-29/TD_contours_complete/TD_contours_complete.pde b/Realite Virtuelle/Exos/2023-03-29/TD_contours_complete/TD_contours_complete.pde new file mode 100644 index 0000000000000000000000000000000000000000..4fda6fb029a9bbbf957a0a42f94f08f398495c66 --- /dev/null +++ b/Realite Virtuelle/Exos/2023-03-29/TD_contours_complete/TD_contours_complete.pde @@ -0,0 +1,232 @@ +// Importation des librairies +import processing.video.*; // Bibliotheque de controle camera + +/* + +L'espace des paramètres est quantifié (on discrétise r et theta) ● Pour chaque point de l'image binaire des contours +● Pour chaque possible theta +● Calculer le r correspondant +● Incrémenter la case Hough(r,theta) +● Rechercher les maximums locaux de Hough(r,theta) + +On va rajouter l'implémentation de la transformation de Hough pour la détection de lignes + +*/ + +class Point { + + int x; + int y; + + public Point(int x, int y) { + this.x = x; + this.y = y; + } + +} + +// Parametres de taille de la capture video +final int widthCapture = 640; // largeur capture +final int heightCapture = 480; // hauteur capture +final int numPixels = widthCapture * heightCapture; // nombre de pixels d'une image video +final int fpsCapture = 30; // taux d’images/secondes + +// Quelques couleurs... +final int noir = color(0,0,0); +final int blanc = color(255,255,255); + +// Declaration des variables globales +String[] cameras; // Liste des cameras dispos +Capture webCam; // Declaration de la Capture par Camera + +PImage img_lissee; // image lissée +PImage contours; //image seuillée des contours +PImage gradient; // image de la norme du gradient +PImage gradnms; // image du gradient après nms + +int rMax = (int) Math.sqrt(widthCapture * widthCapture + heightCapture * heightCapture); + +int[][] hough = new int[rMax * 2 + 1][181]; + +ArrayList<Point> maxLocaux = new ArrayList<Point>(); + + +//********************************************************************** +// Fonction d'initialisation de l'application - executee une seule fois +void setup() { + //Initialisation des parametres graphiques utilises + size(640,480); // Ouverture en mode normal 640 * 480 + surface.setTitle("Exemple extraction de contours"); + colorMode(RGB, 255,255,255); + noFill(); // pas de remplissage + background(noir); // couleur fond fenetre + + //Recherche d'une webcam + cameras = Capture.list(); + if (cameras.length == 0) { + println("Pas de Webcam sur cet ordinateur !"); + exit(); + } else { + // Initialisation de la webcam Video par defaut + webCam = new Capture(this, widthCapture, heightCapture, cameras[0], fpsCapture); + webCam.start(); // Mise en marche de la webCam + } + + //Initialisation des images + img_lissee = createImage(webCam.width, webCam.height, RGB); + gradient = createImage(webCam.width, webCam.height, RGB); + contours = createImage(webCam.width, webCam.height, RGB); + gradnms = createImage(webCam.width, webCam.height, RGB); + +} // Finde Setup + + + +//** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// Fonction de re - tracage de la fenetre - executee en boucle +void draw() { + + background(0); + + hough = new int[rMax * 2 + 1][181]; + + if (webCam.available() == true) { // Verification de presence d'une nouvelle frame + webCam.read(); // Lecture du flux sur la camera... lecture d'une frame + image(webCam, 0, 0); // Restitution de l'image captee sur la webCam + + // Calcul des gradients et contours : exemple avec seuillage simple sur gradient nms + compute_contours(webCam, img_lissee, gradient, gradnms, contours, 80); + + if (mousePressed && (mouseButton == RIGHT)) { // Test clic droit de la souris... + // Affichage de l'image des gradients + image(gradnms,0,0); + } + else if (mousePressed && (mouseButton == LEFT)) {// Test clic gauche de la souris... + // Affichage de l'image des contours + image(contours,0,0); + } + else{ + // Affichagede l'image de la Webcam + image(webCam,0,0); + } + } + + // On explore les pixels + for (int x = 0; x < widthCapture; x++) { + + for (int y = 0; y < heightCapture; y++) { + + if (contours.get(x, y) == blanc) { + + for (float theta = 0; theta < Math.PI; theta += 0.1) { + + float r = x * cos(theta) + y * sin(theta); + + hough[(int) (r + rMax)][radianToDegree(theta)]++; + + } + + } + + } + + } + + // // On cherche les maximums locaux + // maxLocaux.clear(); + + // On calcule le maximum de la matrice et la moyenne + int max = 0; + int moyenne = 0; + int n = 0; + + for (int r = 0; r < hough.length; r++) { + for (int theta = 0; theta < hough[0].length; theta++) { + if (hough[r][theta] > 10) { + if (hough[r][theta] > max) { + max = hough[r][theta]; + } + moyenne += hough[r][theta]; + n++; + } + } + } + + moyenne /= n; + + + float seuil = moyenne + (max - moyenne) / 2; + + // println("Seuil : " + seuil, "Moyenne : " + moyenne, "Max : " + max); + + // On cherche les maximums locaux (au dessus du seuil) + maxLocaux.clear(); + + for (int r = 1; r < hough.length - 1; r++) { + for (int theta = 1; theta < hough[0].length - 1; theta++) { + if (hough[r][theta] > seuil) { + + // On regarde dans un rayon de 15x15 si on a un maximum local + boolean maxLocal = true; + + for (int r2 = r - 15; r2 < r + 15; r2++) { + for (int theta2 = theta - 15; theta2 < theta + 15; theta2++) { + if (r2 + rMax < 0 || r2 + rMax >= hough.length || theta2 < 0 || theta2 >= hough[0].length) { + continue; + } + + if (hough[r][theta] > hough[r2][theta2]) { + maxLocal = false; + break; + } + } + + if (!maxLocal) { + break; + } + + } + + if (maxLocal) { + maxLocaux.add(new Point(r, theta)); + } + + } + } + } + + // On recherche s'il y a des teta qui sont proche, si oui, on regarde si les r sont proche, si oui, on les fusionne + for (int i = 0; i < maxLocaux.size(); i++) { + for (int j = i + 1; j < maxLocaux.size(); j++) { + if (abs(maxLocaux.get(i).y - maxLocaux.get(j).y) < 10) { + if (abs(maxLocaux.get(i).x - maxLocaux.get(j).x) < 10) { + maxLocaux.get(i).x = (maxLocaux.get(i).x + maxLocaux.get(j).x) / 2; + maxLocaux.get(i).y = (maxLocaux.get(i).y + maxLocaux.get(j).y) / 2; + maxLocaux.remove(j); + j--; + } + } + } + } + + // On affiche les maximums locaux + for (Point p : maxLocaux) { + stroke(255, 0, 0); + if (p.y == 0) { + // Line vertical, cas d'erreur pour éviter la division par 0 + line(0, p.x - rMax, widthCapture, p.x - rMax); + } else { + line(0, (p.x - rMax) / sin(degreeToRadian(p.y)), widthCapture, (p.x - rMax - widthCapture * cos(degreeToRadian(p.y))) / sin(degreeToRadian(p.y))); + } + } + +} + + +int radianToDegree(float radian) { + return (int) (radian * 180 / Math.PI); +} + +float degreeToRadian(int degree) { + return (float) (degree * Math.PI / 180); +} \ No newline at end of file diff --git a/Realite Virtuelle/Exos/2023-03-29/TD_contours_complete/contours.pde b/Realite Virtuelle/Exos/2023-03-29/TD_contours_complete/contours.pde new file mode 100644 index 0000000000000000000000000000000000000000..c84346bce3fb7091654051cd24a6deb75cd5e398 --- /dev/null +++ b/Realite Virtuelle/Exos/2023-03-29/TD_contours_complete/contours.pde @@ -0,0 +1,167 @@ +//********************************************************************** +// Calcul du gradient de Sobel +// img : PImage image à traiter +// gradient : PImage image du résultat du gradient +//********************************************************************** +void compute_gradient_sobel(PImage img, PImage gradient) { + float[][] filtreSobelH = { { -1, 0, 1 }, + { -2, 0, 2 }, + { -1, 0, 1 } }; + float[][] filtreSobelV = { { 1, 2, 1 }, + { 0, 0, 0 }, + { -1, -2, -1 } }; + float grad = 0; + float gradH = 0; + float gradV = 0; + int loc = 0; + + //Parcours des pixels de l'image + for (int x = 1; x < img.width-1; x++) { + for (int y = 1; y < img.height-1; y++ ) { + gradV = 0; gradH = 0; + //Calcul du résultat de la convolution par les 2 masques : + gradH = apply_kernel_lum(x, y, filtreSobelH, img); + gradV = apply_kernel_lum(x, y, filtreSobelV, img); + //Calcul de la norme du gradient : + grad = sqrt(gradH*gradH+gradV*gradV); + + //Stockage dans une PImage (valeur entre 0 et 255) + loc = x + y*img.width; + gradient.pixels[loc] = color(grad); + } + } + gradient.updatePixels(); +} +//********************************************************************** +// Calcul du gradient + nms +// img : PImage image à traiter +// gradient : PImage image du résultat du gradient +// gradnms : PImage image du résultat de la suppression de nom maxima +//********************************************************************** +void compute_gradient_nms(PImage img, PImage gradient, PImage gradnms) { + // A COMPLETER ! + float[][] filtreSobelH = { { -1, 0, 1 }, + { -2, 0, 2 }, + { -1, 0, 1 } }; + float[][] filtreSobelV = { { 1, 2, 1 }, + { 0, 0, 0 }, + { -1, -2, -1 } }; + float grad = 0; + float angle = 0; + float[] gradmag = new float[img.width*img.height]; + float[] gradangle = new float[img.width*img.height]; + float maxgrad = 0; + int loc = 0; + + //Parcours des pixels de l'image + for (int x = 1; x < img.width-1; x++) { + for (int y = 1; y < img.height-1; y++ ) { + float gradV = 0, gradH=0; + //Calcul du résultat de la convolution par les 2 masques : + gradH = apply_kernel_lum(x, y, filtreSobelH, img); + gradV = apply_kernel_lum(x, y, filtreSobelV, img); + //Calcul de la norme du gradient : + grad = sqrt(gradH*gradH+gradV*gradV); + if (grad>maxgrad) { + maxgrad = grad; + } + angle = atan2(gradV,gradH); + loc = x + y*img.width; + gradmag[loc] = grad; + gradangle[loc] = angle; + } + } + //NMS + //Parcours des pixels de l'image + float pix1 = 0; + float pix2 = 0; + float curr_angle = 0; + for (int x = 1; x < img.width-1; x++) { + for (int y = 1; y < img.height-1; y++ ) { + loc = x + y*img.width; + curr_angle = gradangle[loc]; + grad = gradmag[loc]; + // Seuil bas + if (grad>20){ + gradient.pixels[loc]=color(int(255*grad/maxgrad)); + + //Gradient vertical + if ((-0.393<=curr_angle & curr_angle<0.393)||(-3.1416<=curr_angle&curr_angle<-2.749)||(2.749<=curr_angle&curr_angle<=3.1416)){ + pix1 = gradmag[x+(y+1)*img.width]; + pix2 = gradmag[x+(y-1)*img.width]; + } + //Gradient horizontal + else if((1.178<=curr_angle & curr_angle<1.964)||(-1.964<=curr_angle&curr_angle<-1.178)){ + pix1 = gradmag[x-1+y*img.width]; + pix2 = gradmag[x+1+y*img.width]; + } + //Gradient diagonal + else if((0.393<=curr_angle & curr_angle<1.178)||(-2.749<=curr_angle&curr_angle<-1.964)){ + pix1 = gradmag[x-1+(y+1)*img.width]; + pix2 = gradmag[x+1+(y-1)*img.width]; + } + //Gradient diagonal + else { + pix1 = gradmag[x-1+(y-1)*img.width]; + pix2 = gradmag[x+1+(y+1)*img.width]; + } + // suppression des non max + if ((grad>=pix1)&(grad>=pix2)){ + gradnms.pixels[loc]=color(int(255*grad/maxgrad)); + }else { + gradnms.pixels[loc]=color(0);} + }else{ + gradient.pixels[loc]=color(0); + gradnms.pixels[loc]=color(0); + } + + } + } + gradient.updatePixels(); + gradnms.updatePixels(); + +} + + +//********************************************************************** +// Seuillage simple +// img : PImage image à traiter +// imgSeuilleeimgSeuillee : PImage image du résultat du seuillage +// seuil : seuil utilisé pour le seuillage simple +//********************************************************************** +void seuillage(PImage img, PImage imgSeuillee, int seuil) { + int loc = 0; + for (int x = 0; x < img.width; x++) { + for (int y = 0; y < img.height; y++ ) { + // Pixel courant + loc = x + y*img.width; + color pix = img.pixels[loc]; + if (brightness(pix) > seuil) { + imgSeuillee.pixels[loc] = color(255); + } else { + imgSeuillee.pixels[loc] = color(0); + } + } + } + imgSeuillee.updatePixels(); +} + + +//********************************************************************** +// Calcul des contours : séquence d'opérateurs +// img : PImage image à traiter +// img_lissee : PImage image du résultat du lissage (moyenneur ici) +// gradient : PImage image du résultat du gradient +// gradnms : PImage image du résultat de la suppression de nom maxima +// seuil : seuil utilisé pour le seuillage simple +// contours : PImage image du résultat de l'extraction de contours après seuillage +//********************************************************************** +void compute_contours(PImage img, PImage img_lissee, PImage gradient, PImage gradnms, PImage contours, int seuil) { + float[][] masque_lissage = { { 1./9., 1./9., 1./9. }, + { 1./9., 1./9., 1./9. }, + { 1./9., 1./9., 1/9. } }; + image_convolution(img, masque_lissage, img_lissee); + //compute_gradient_sobel(img_lissee, gradient); + compute_gradient_nms(img_lissee, gradient, gradnms); + seuillage(gradnms, contours, seuil); +} diff --git a/Realite Virtuelle/Exos/2023-03-29/TD_contours_complete/convolution.pde b/Realite Virtuelle/Exos/2023-03-29/TD_contours_complete/convolution.pde new file mode 100644 index 0000000000000000000000000000000000000000..3c2cfebfdbd064d69afa4396ea93f0106bdb6c05 --- /dev/null +++ b/Realite Virtuelle/Exos/2023-03-29/TD_contours_complete/convolution.pde @@ -0,0 +1,40 @@ +//************************************************************************ +//***** Calcul de la combinaison linéaire avec les coefficient du masque * +//***** à la position x y dans l'image, sur la luminance uniquement ****** +float apply_kernel_lum(int x, int y, float[][] kernel, PImage img) +{ + float lum = 0.0; + int kernel_size = kernel.length; + int m = kernel_size / 2; + // Parcours du masque + for (int i = 0; i < kernel_size; i++){ + for (int j= 0; j < kernel_size; j++){ + int xloc = x+i-m; + int yloc = y+j-m; + // Contraint la valeur dans les bornes de l'image + xloc = constrain(xloc,0,img.width-1); + yloc = constrain(yloc,0,img.height-1); + //Calcul de l'indice linéaire du pixel + int loc = xloc + img.width*yloc; + // Calcul de la multiplication avec le masque + lum += (brightness(img.pixels[loc]) * kernel[i][j]); + } + } + // Renvoie la valeur résultante + return lum; +} +//********************************************************************** +// Convolution d'une image par un masque (kernel) +void image_convolution(PImage img, float[][] kernel, PImage resultat) +{ + float lum = 0.0; + // Parcours de l'image + for (int y = 0; y < img.height; y++) { + for (int x = 0; x < img.width; x++) { + // Parcours du masque + lum = apply_kernel_lum(x,y, kernel, img); + resultat.pixels[x + img.width*y]=color(lum); + } + } + resultat.updatePixels(); +} diff --git a/Realite Virtuelle/Exos/2023-03-29/TD_contours_complete/save.pde b/Realite Virtuelle/Exos/2023-03-29/TD_contours_complete/save.pde new file mode 100644 index 0000000000000000000000000000000000000000..2194bb04c38283e69490ae82913b8d9c62cda65f --- /dev/null +++ b/Realite Virtuelle/Exos/2023-03-29/TD_contours_complete/save.pde @@ -0,0 +1,193 @@ +// // Importation des librairies +// import processing.video.*; // Bibliotheque de controle camera + +// /* + +// L'espace des paramètres est quantifié (on discrétise r et theta) ● Pour chaque point de l'image binaire des contours +// ● Pour chaque possible theta +// ● Calculer le r correspondant +// ● Incrémenter la case Hough(r,theta) +// ● Rechercher les maximums locaux de Hough(r,theta) + +// On va rajouter l'implémentation de la transformation de Hough pour la détection de lignes + +// */ + +// class Point { + +// int x; +// int y; + +// public Point(int x, int y) { +// this.x = x; +// this.y = y; +// } + +// } + +// // Parametres de taille de la capture video +// final int widthCapture = 640; // largeur capture +// final int heightCapture = 480; // hauteur capture +// final int numPixels = widthCapture * heightCapture; // nombre de pixels d'une image video +// final int fpsCapture = 30; // taux d’images/secondes + +// // Quelques couleurs... +// final int noir = color(0,0,0); +// final int blanc = color(255,255,255); + +// // Declaration des variables globales +// String[] cameras; // Liste des cameras dispos +// Capture webCam; // Declaration de la Capture par Camera + +// PImage img_lissee; // image lissée +// PImage contours; //image seuillée des contours +// PImage gradient; // image de la norme du gradient +// PImage gradnms; // image du gradient après nms + +// int rMax = (int) Math.sqrt(widthCapture * widthCapture + heightCapture * heightCapture); + +// int[][] hough = new int[rMax * 2 + 1][181]; + +// ArrayList<Point> maxLocaux = new ArrayList<Point>(); + + +// //********************************************************************** +// // Fonction d'initialisation de l'application - executee une seule fois +// void setup() { +// //Initialisation des parametres graphiques utilises +// size(640,480); // Ouverture en mode normal 640 * 480 +// surface.setTitle("Exemple extraction de contours"); +// colorMode(RGB, 255,255,255); +// noFill(); // pas de remplissage +// background(noir); // couleur fond fenetre + +// //Recherche d'une webcam +// cameras = Capture.list(); +// if (cameras.length == 0) { +// println("Pas de Webcam sur cet ordinateur !"); +// exit(); +// } else { +// // Initialisation de la webcam Video par defaut +// webCam = new Capture(this, widthCapture, heightCapture, cameras[0], fpsCapture); +// webCam.start(); // Mise en marche de la webCam +// } + +// //Initialisation des images +// img_lissee = createImage(webCam.width, webCam.height, RGB); +// gradient = createImage(webCam.width, webCam.height, RGB); +// contours = createImage(webCam.width, webCam.height, RGB); +// gradnms = createImage(webCam.width, webCam.height, RGB); + +// } // Finde Setup + + + +// //** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// // Fonction de re - tracage de la fenetre - executee en boucle +// void draw() { + +// background(0); + +// hough = new int[rMax * 2 + 1][181]; + +// if (webCam.available() == true) { // Verification de presence d'une nouvelle frame +// webCam.read(); // Lecture du flux sur la camera... lecture d'une frame +// image(webCam, 0, 0); // Restitution de l'image captee sur la webCam + +// // Calcul des gradients et contours : exemple avec seuillage simple sur gradient nms +// compute_contours(webCam, img_lissee, gradient, gradnms, contours, 80); + +// if (mousePressed && (mouseButton == RIGHT)) { // Test clic droit de la souris... +// // Affichage de l'image des gradients +// image(gradnms,0,0); +// } +// else if (mousePressed && (mouseButton == LEFT)) {// Test clic gauche de la souris... +// // Affichage de l'image des contours +// image(contours,0,0); +// } +// else{ +// // Affichagede l'image de la Webcam +// image(webCam,0,0); +// } +// } + +// // On explore les pixels +// for (int x = 0; x < widthCapture; x++) { + +// for (int y = 0; y < heightCapture; y++) { + +// if (contours.get(x, y) == blanc) { + +// for (float theta = 0; theta < Math.PI; theta += 0.1) { + +// float r = x * cos(theta) + y * sin(theta); + +// hough[(int) (r + rMax)][radianToDegree(theta)]++; + +// } + +// } + +// } + +// } + +// // // On cherche les maximums locaux +// // maxLocaux.clear(); + +// // On calcule le maximum de la matrice et la moyenne +// int max = 0; +// int moyenne = 0; +// int n = 0; + +// for (int r = 0; r < hough.length; r++) { +// for (int theta = 0; theta < hough[0].length; theta++) { +// if (hough[r][theta] > 10) { +// if (hough[r][theta] > max) { +// max = hough[r][theta]; +// } +// moyenne += hough[r][theta]; +// n++; +// } +// } +// } + +// moyenne /= n; + + +// float seuil = max - (max - moyenne) / 2; + +// // println("Seuil : " + seuil, "Moyenne : " + moyenne, "Max : " + max); + +// // On cherche les maximums locaux (au dessus du seuil) +// maxLocaux.clear(); + +// for (int r = 1; r < hough.length - 1; r++) { +// for (int theta = 1; theta < hough[0].length - 1; theta++) { +// if (hough[r][theta] > seuil) { +// maxLocaux.add(new Point(r, theta)); +// } +// } +// } + +// // On affiche les maximums locaux +// for (Point p : maxLocaux) { +// stroke(255, 0, 0); +// if (p.y == 0) { +// // Line vertical, cas d'erreur pour éviter la division par 0 +// line(0, p.x - rMax, widthCapture, p.x - rMax); +// } else { +// line(0, (p.x - rMax) / sin(degreeToRadian(p.y)), widthCapture, (p.x - rMax - widthCapture * cos(degreeToRadian(p.y))) / sin(degreeToRadian(p.y))); +// } +// } + +// } + + +// int radianToDegree(float radian) { +// return (int) (radian * 180 / Math.PI); +// } + +// float degreeToRadian(int degree) { +// return (float) (degree * Math.PI / 180); +// } \ No newline at end of file