Cours:Classif : Différence entre versions

De troyesGEII
Aller à : navigation, rechercher
 
(37 révisions intermédiaires par le même utilisateur non affichées)
Ligne 14 : Ligne 14 :
 
* Une Rpi 4 que vous programmerez depuis vos postes linux (en cross-compilation)
 
* Une Rpi 4 que vous programmerez depuis vos postes linux (en cross-compilation)
 
* Une camera PiCam Wide (grand angle)
 
* Une camera PiCam Wide (grand angle)
* la libraire CImg pour des traitements bas niveaux et affichages : https://cimg.eu/
+
* la librairie CImg pour des traitements bas niveaux et affichages : https://cimg.eu/
* la libraire Dlib pour des traitements simples et la reconnaissance : http://dlib.net/
+
* la librairie Dlib pour des traitements simples et la reconnaissance : http://dlib.net/
  
==== Prise d'images ====
+
Vous travaillerez dans un dossier dit "DE TRAVAIL", qui contiendra
 +
* les dossiers de vos projets QtCreator
 +
* le code des librairies Dlib et CImg téléchargées
 +
(dans mes exemples, c'est <code>/home/nicoli01/tests</code>)
 +
 
 +
==== Prise d'images en terminal ====
  
 
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4
 
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4
Ligne 24 : Ligne 29 :
 
* Explorer les options de cette application (<code>libcamera-still -h</code>), en particulier <code>-n</code>, <code>--immediate</code>, <code>--width</code>, <code>--height</code> et <code>-o</code>
 
* Explorer les options de cette application (<code>libcamera-still -h</code>), en particulier <code>-n</code>, <code>--immediate</code>, <code>--width</code>, <code>--height</code> et <code>-o</code>
  
==== Prétraitements et mesure de descripteurs ====
+
Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html
 +
 
 +
==== Mise en place de la librairie Dlib ====
 +
 
 +
Attention, l'affichage des images avec Dlib et CImg sera déporté sur votre PC en ''X11 forwarding'' (option "Forward to local display" dans QtCreator ou "ssh -X" pour la connection depuis un terminal)
 +
 
 +
Cette fonction exploite la librairie Dlib :
 +
* Créer un projet dans QtCreator de type Non-Qt C++
 +
* Télécharger la librairie depuis http://dlib.net, la décompresser et placer son dossier <code>dlib</code> au même niveau que votre dossier de projet
 +
* Modifier votre fichier <code>.pro</code> de la façon suivante :
 +
<source>
 +
QMAKE_CXXFLAGS += -DDLIB_JPEG_SUPPORT
 +
INCLUDEPATH += /home/nicoli01/tests # A REMPLACER PAR LE CHEMIN VERS VOTRE DOSSIER DE TRAVAIL
 +
SOURCES += \
 +
        /home/nicoli01/tests/dlib/all/source.cpp ## A REMPLACER DANS VOTRE CAS
 +
LIBS += -ljpeg -lm -lX11 -lpthread
 +
</source> Ne pas oublier les deux lignes
 +
<source>
 +
target.path = /root
 +
INSTALLS = target
 +
</source>
 +
 
 +
* Pour le contenu de votre <code>main.cpp</code>, vous pourrez vous inspirer de l'exemple suivant http://dlib.net/image_ex.cpp.html (en remplaçant éventuellement la ligne <code>load_image(img, argv[1]);</code> par <code>load_image(img, "image.png");</code> pour lire une image que vous aurez acquis avec <code>libcamera</code>). Par exemple :
 +
<source lang="c++">
 +
#include <dlib/gui_widgets.h>
 +
#include <dlib/image_io.h>
 +
#include <dlib/image_transforms.h>
 +
#include <fstream>
 +
 
 +
 
 +
using namespace std;
 +
using namespace dlib;
 +
 
 +
//  ----------------------------------------------------------------------------
 +
 
 +
int main()
 +
{
 +
    try
 +
    {
 +
        array2d<rgb_pixel> img;
 +
 
 +
        load_image(img, "/Users/frederic/work/docu/enseignements/GEIIBUT3Classif/tests/im.jpg");
 +
 
 +
        image_window win;
 +
        win.set_image(img);
 +
        win.wait_until_closed();
 +
    }
 +
    catch (exception& e)
 +
    {
 +
        cout << "exception thrown: " << e.what() << endl;
 +
    }
 +
}
 +
</source>
 +
 
 +
==== Mise en place de la librairie CImg ====
 +
 
 +
La librairie CImg est constituée d'un unique ficher <code>CImg.h</code>
 +
 
 +
* Télécharger la librairie depuis https://cimg.eu/download.html, décompresser et copier le fichier <code>CImg.h</code> dans votre dossier de travail
 +
* Faire un nouveau projet non Qt, avec le <code>main.cpp</code> suivant pour tester
 +
<source lang="c++">
 +
#include <iostream>
 +
 
 +
#include "/home/nicoli01/tests/testcimg/CImg.h" // A REMPLACER DANS VOTRE CAS
 +
 
 +
using namespace cimg_library;
 +
 
 +
using namespace std;
 +
 
 +
int main()
 +
{
 +
    cout << "Hello World!" << endl;
 +
 
 +
    CImg<unsigned char> image("/root/im.png"); // A REMPLACER DANS VOTRE CAS
 +
    image.display("hello");
 +
    return 0;
 +
}
 +
</source>
 +
 
 +
* Votre fichier <code>.pro</code> est plus simple que pour Dlib, vous n'aurez qu'à rajouter la ligne suivante :
 +
<source>
 +
LIBS += -ljpeg -lm -lX11 -lpthread -pedantic -Dcimg_use_vt100 -Dcimg_display=1
 +
</source>
 +
 
 +
==== Prise d'images en c++ ====
  
==== Identification par KNN ====
+
Voici une fonction <code>imcapture</code>, qui renvoit une image RGB Dlib, que vous pourrez utiliser pour réaliser une acquisition d'image en c++
 +
<source lang="c++">
 +
array2d<rgb_pixel> imcapture() {
 +
array2d<rgb_pixel> image;
 +
    int pb = system("rm im.png 2>/dev/null; libcamera-still -n 1 --immediate --width 640 --height 480 -o im.png 2> /dev/null ");
 +
if (pb) {
 +
cout << "ERROR: no image acquisition" << endl;
 +
exit(1);
 +
} else {
 +
load_image(image, "im.png");
 +
}
 +
return image;
 +
}
 +
</source>
  
* A coder
+
* Utiliser cette fonction <code>imcapture</code> pour écrire un programme qui capture une image et l'affiche (avec Dlib)
  
==== Identification par SVM ====
+
==== Prétraitements et mesure de descripteurs ====
  
* En utilisant Dlib
+
'''Cette partie est moins guidée, elle devra être incluse dans votre compte-rendu.'''
  
Données bruitées à lire sur le topic <code>data/s1</code>.
+
* Modification éventuelle de la zone de capture de la camera (option <code>--roi</code> de <code>libcamera-still</code>, voir https://www.raspberrypi.com/documentation/computers/camera_software.html)
 +
# Conversion en image niv. de gris (cf <code>assign_image()</code> dans Dlib)
 +
# Binarisation (cf <code>threshold_image</code> dans Dlib)
 +
# (si plusieurs objets) Segmentation (cf <code>segment_image()</code> dans Dlib)
  
{{q| a)}} Lire et afficher le graphe des données <code>s1</code>.
+
Si besoin, conversion d'une image Dlib en une image CImg :
 +
<source lang="C++">
 +
CImg<unsigned char> image(img_gray.nc(), img_gray.nr());
 +
cimg_forXY(image,x,y) {
 +
  image(x,y) = (unsigned char)bin_img[y][x];
 +
}
 +
</source>
  
{{q| b)}} En exploitant l'équation aux différence du filtre, appliquer le filtre numérique L<sub>1</sub>(z) = (1 + z<sup>-1</sup>). Afficher le graphe de la sortie (par ex. avec nodered ou bien avec MQTT explorer). Comparer le signal avant et après filtrage (amplitude et quantité de bruit)
+
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur/largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments
  
{{q| c)}} En exploitant le signal s1 non bruité (disponible sur le topic <code>data/s1nonoise</code>) proposer une mesure de la qualité de débruitage du filtre de lissage.
+
* Fonction de calcul des descripteurs géométriques (longueur l, largeur w) d'un objet dans une image :
 +
<source lang="c++">
 +
void compute_desc(CImg<> image) {
 +
    long M00, M10, M01, M11, M20, M02;
 +
    M00 = 0; M10 = 0; M01 = 0; M11 = 0; M20 = 0; M02 = 0;
 +
    cimg_forXY(image,x,y) {
 +
        M00 += image(x,y);
 +
        M10 += x*image(x,y);
 +
        M01 += y*image(x,y);
 +
        M11 += x*y*image(x,y);
 +
        M20 += x*x*image(x,y);
 +
        M02 += y*y*image(x,y);
 +
    }
 +
    long xm = M10/M00;
 +
    long ym = M01/M00;
 +
    long mu20 = M20/M00 - xm*xm;
 +
    long mu02 = M02/M00 - ym*ym;
 +
    long mu11 = M11/M00 - xm*ym;
 +
    long l = sqrt(8 * (mu20 + mu02 + sqrt(4*mu11*mu11 + (mu20-mu02)*(mu20-mu02))));
 +
    long w = sqrt(8 * (mu20 + mu02 - sqrt(4*mu11*mu11 + (mu20-mu02)*(mu20-mu02))));
 +
    cout << "l = " << l << endl;
 +
    cout << "w = " << w << endl;
 +
}
 +
</source>
  
{{q| d)}} Faire de même avec des lissage d'ordre plus élevés. Vous pourrez, soit coder directement L<sub>2</sub>, L<sub>3</sub>, ... ou bien faire des filtrages multiples avec L<sub>1</sub> (en vérifiant ainsi expérimentalement que L<sub>2</sub> = L<sub>1</sub> * L<sub>1</sub>). Mesurer à chaque fois les quantités de bruits obtenus afin de valider quantitativement la qualité des différents filtres.
+
==== Identification par KNN ====
  
===== Dérivation (détection changements) =====
+
'''Cette partie est moins guidée, elle devra être incluse dans votre compte-rendu.'''
  
Données à lire sur les topics <code>data/s2</code> et <code>data/s2b</code> (version avec bruit)
+
{{q| a)}} Dans un premier temps
Le but est de détecter les sauts de valeurs à l'aide de filtre dérivateurs.
+
* un seul plus proche voisin
 +
* un seul échantillon de référence par classe
 +
* viser une décision binaire (ex. "L'objet est-il de la classe ... ?").
  
{{q| a)}} Appliquer le filtre dérivateur D<sub>1</sub>(z) = (1 - z<sup>-1</sup>)/2 sur <code>data/s2</code>. Mettre en place un détecteur de saut de valeurs à partir du résultat obtenu. Vous pourrez valider votre détection en exploitant la vérité terrain (saut/pas de saut) disponible sur le topic <code>s2vt</code>.
+
Fonctions éventuellement utiles dans Dlib :
 +
* <code>serialize</code> et <code>deserialize</code> pour sauvegarder et charger des données depuis des fichiers : http://dlib.net/other.html#serialize
 +
* <code>index_of_min</code> pour la recherche du min
 +
* <code>remove_col</code> ou <code>remove_row</code> pour extraire un élément d'un tableau Dlib.
 +
* <code>squared_euclidean_distance</code> pour le calcul de la distance entre deux vecteurs.
  
{{q| b)}} De même avec la version bruitée (<code>s2b</code>). Il sera probablement nécessaire d'opérer un lissage avant la détection, afin de réduire le bruit.
+
{{q| b)}} Puis progressivement
 +
* viser une décision multiclasse (ex. "Quelle est la classe de l'objet ?").
 +
* augmenter le nb d'échantillons de référence par classe (réfléchir à la façon dont vous souhaitez stocker et manipuler cette information). Si vous avez du temps, il serait intéressant de tracer graphiquement la position de vos échantillons de références dans l'espace des descripteurs, par exemple en exportant vos coordonnées (dans cet espace) vers matlab.
 +
* passer à 3 plus proches voisins
  
===== Convolution (détection de motifs) =====
+
{{q| c)}} Ensuite - et seulement, ou si vous n'arrivez pas à coder a) et b) - vous pourrez comparer avec l'implémentation de Dlib :
 +
* <code>find_k_nearest_neighbors</code> : http://dlib.net/graph_tools.html#find_k_nearest_neighbors
  
Un façon de détecter des motifs consiste à convoluer le signal d'entrée avec un signal similaire avec le motif recherché.
+
==== Identification par SVM ====
  
{{q| a)}} Mettre en place un détecteur de saut (avec les signaux <code>s2</code> puis <code>s2b</code>).
+
* En utilisant Dlib

Version actuelle datée du 2 février 2024 à 11:22

TP Classification : détection d'objet en temps réel

Le travail de cette étape va consister à

  • analyser des images acquise en "temps réel" afin de détecter et identifier des objets
  • les objets seront
    • dans un premier temps des jetons de nain jaune
    • dans un second temps des briques lego.

Technos matérielles et logicielles

Vous utiliserez :

  • Une Rpi 4 que vous programmerez depuis vos postes linux (en cross-compilation)
  • Une camera PiCam Wide (grand angle)
  • la librairie CImg pour des traitements bas niveaux et affichages : https://cimg.eu/
  • la librairie Dlib pour des traitements simples et la reconnaissance : http://dlib.net/

Vous travaillerez dans un dossier dit "DE TRAVAIL", qui contiendra

  • les dossiers de vos projets QtCreator
  • le code des librairies Dlib et CImg téléchargées

(dans mes exemples, c'est /home/nicoli01/tests)

Prise d'images en terminal

  • Connecter (si cela n'est pas fait) la PiCam à la Rpi4
  • Tester la PiCam avec libcamera-hello (la capture doit s'afficher sur l'écran de la Rpi)
  • Tester l'acquisition d'image avec l'éxecutable libcamera-still
  • Explorer les options de cette application (libcamera-still -h), en particulier -n, --immediate, --width, --height et -o

Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html

Mise en place de la librairie Dlib

Attention, l'affichage des images avec Dlib et CImg sera déporté sur votre PC en X11 forwarding (option "Forward to local display" dans QtCreator ou "ssh -X" pour la connection depuis un terminal)

Cette fonction exploite la librairie Dlib :

  • Créer un projet dans QtCreator de type Non-Qt C++
  • Télécharger la librairie depuis http://dlib.net, la décompresser et placer son dossier dlib au même niveau que votre dossier de projet
  • Modifier votre fichier .pro de la façon suivante :
QMAKE_CXXFLAGS += -DDLIB_JPEG_SUPPORT
INCLUDEPATH += /home/nicoli01/tests # A REMPLACER PAR LE CHEMIN VERS VOTRE DOSSIER DE TRAVAIL
SOURCES += \
        /home/nicoli01/tests/dlib/all/source.cpp ## A REMPLACER DANS VOTRE CAS
LIBS += -ljpeg -lm -lX11 -lpthread
Ne pas oublier les deux lignes
target.path = /root
INSTALLS = target
  • Pour le contenu de votre main.cpp, vous pourrez vous inspirer de l'exemple suivant http://dlib.net/image_ex.cpp.html (en remplaçant éventuellement la ligne load_image(img, argv[1]); par load_image(img, "image.png"); pour lire une image que vous aurez acquis avec libcamera). Par exemple :
#include <dlib/gui_widgets.h>
#include <dlib/image_io.h>
#include <dlib/image_transforms.h>
#include <fstream>


using namespace std;
using namespace dlib;

//  ----------------------------------------------------------------------------

int main()
{
    try
    {
        array2d<rgb_pixel> img;

        load_image(img, "/Users/frederic/work/docu/enseignements/GEIIBUT3Classif/tests/im.jpg");

        image_window win;
        win.set_image(img);
        win.wait_until_closed();
    }
    catch (exception& e)
    {
        cout << "exception thrown: " << e.what() << endl;
    }
}

Mise en place de la librairie CImg

La librairie CImg est constituée d'un unique ficher CImg.h

  • Télécharger la librairie depuis https://cimg.eu/download.html, décompresser et copier le fichier CImg.h dans votre dossier de travail
  • Faire un nouveau projet non Qt, avec le main.cpp suivant pour tester
#include <iostream>

#include "/home/nicoli01/tests/testcimg/CImg.h" // A REMPLACER DANS VOTRE CAS

using namespace cimg_library;

using namespace std;

int main()
{
    cout << "Hello World!" << endl;

    CImg<unsigned char> image("/root/im.png"); // A REMPLACER DANS VOTRE CAS
    image.display("hello");
    return 0;
}
  • Votre fichier .pro est plus simple que pour Dlib, vous n'aurez qu'à rajouter la ligne suivante :
LIBS += -ljpeg -lm -lX11 -lpthread -pedantic -Dcimg_use_vt100 -Dcimg_display=1

Prise d'images en c++

Voici une fonction imcapture, qui renvoit une image RGB Dlib, que vous pourrez utiliser pour réaliser une acquisition d'image en c++

array2d<rgb_pixel> imcapture() {
	array2d<rgb_pixel> image;
    int pb = system("rm im.png 2>/dev/null; libcamera-still -n 1 --immediate --width 640 --height 480 -o im.png 2> /dev/null ");
	if (pb) {
		cout << "ERROR: no image acquisition" << endl;
		exit(1);
	} else {
		load_image(image, "im.png");
	}
	return image;		
}
  • Utiliser cette fonction imcapture pour écrire un programme qui capture une image et l'affiche (avec Dlib)

Prétraitements et mesure de descripteurs

Cette partie est moins guidée, elle devra être incluse dans votre compte-rendu.

  1. Conversion en image niv. de gris (cf assign_image() dans Dlib)
  2. Binarisation (cf threshold_image dans Dlib)
  3. (si plusieurs objets) Segmentation (cf segment_image() dans Dlib)

Si besoin, conversion d'une image Dlib en une image CImg :

CImg<unsigned char> image(img_gray.nc(), img_gray.nr());
cimg_forXY(image,x,y) {
  image(x,y) = (unsigned char)bin_img[y][x];
}
  • Fonction de calcul des descripteurs géométriques (longueur l, largeur w) d'un objet dans une image :
void compute_desc(CImg<> image) {
    long M00, M10, M01, M11, M20, M02;
    M00 = 0; M10 = 0; M01 = 0; M11 = 0; M20 = 0; M02 = 0;
    cimg_forXY(image,x,y) {
        M00 += image(x,y);
        M10 += x*image(x,y);
        M01 += y*image(x,y);
        M11 += x*y*image(x,y);
        M20 += x*x*image(x,y);
        M02 += y*y*image(x,y);
    }
    long xm = M10/M00;
    long ym = M01/M00;
    long mu20 = M20/M00 - xm*xm;
    long mu02 = M02/M00 - ym*ym;
    long mu11 = M11/M00 - xm*ym;
    long l = sqrt(8 * (mu20 + mu02 + sqrt(4*mu11*mu11 + (mu20-mu02)*(mu20-mu02))));
    long w = sqrt(8 * (mu20 + mu02 - sqrt(4*mu11*mu11 + (mu20-mu02)*(mu20-mu02))));
    cout << "l = " << l << endl;
    cout << "w = " << w << endl;
}

Identification par KNN

Cette partie est moins guidée, elle devra être incluse dans votre compte-rendu.

a) Dans un premier temps

  • un seul plus proche voisin
  • un seul échantillon de référence par classe
  • viser une décision binaire (ex. "L'objet est-il de la classe ... ?").

Fonctions éventuellement utiles dans Dlib :

  • serialize et deserialize pour sauvegarder et charger des données depuis des fichiers : http://dlib.net/other.html#serialize
  • index_of_min pour la recherche du min
  • remove_col ou remove_row pour extraire un élément d'un tableau Dlib.
  • squared_euclidean_distance pour le calcul de la distance entre deux vecteurs.

b) Puis progressivement

  • viser une décision multiclasse (ex. "Quelle est la classe de l'objet ?").
  • augmenter le nb d'échantillons de référence par classe (réfléchir à la façon dont vous souhaitez stocker et manipuler cette information). Si vous avez du temps, il serait intéressant de tracer graphiquement la position de vos échantillons de références dans l'espace des descripteurs, par exemple en exportant vos coordonnées (dans cet espace) vers matlab.
  • passer à 3 plus proches voisins

c) Ensuite - et seulement, ou si vous n'arrivez pas à coder a) et b) - vous pourrez comparer avec l'implémentation de Dlib :

Identification par SVM

  • En utilisant Dlib