Vision
Introduction to image processing.
Vision provides a rich information about the immediate environment around the robot. However it requires to process the images to extract pertinent information...
The goal here is to detect a specific object in the scene. The first proposed approach is to detect the object regarding its main color before to move toward approaches handle textures.
Setup our programming environment
This tutorial focus on OpenCV
librairy, addressed in Python
development language on a Linux machine.
First we want to install Python3 and the libraries: Numpy, Matplot, Sklearn, Scipy and OpenCV.
The command whereis
permit to localize a command (like python3
interpreter). If python3 is correctly installed, its execution would not be empty:
Python uses its own package managers pip
to install libraries. So just to be sure you can:
Then we can use pip to install modules:
Now normally you can load the different modules, for instance:
Image from simulation
Gazebo is capable of simulating robot vision (classical and 3D sensor).
Launch a simulation :
'''bash roslaunch larm chalenge-2.launch '''
And observe the topic and mainly the one published the images (camera/rgb/image_raw
) with rostopic list
, rostopic info
and rostopic hz
.
Image are published as 'sensor_msgs/Image' in camera/rgb/image_raw
So the pixel value is stored in img.data
array but several tool to convert ROS image to OpenCV images already exist (for instance cv_bridge)
Segmentation d'images couleur par seuillage des composantes et Gestion de la souris
Voici quelques lignes de codes pour extraire une région d'intérêt à la souris. Grâce à ces quelques lignes il vous sera possible de calculer la valeur moyenne et la variance de chaque composante de l'image, utile pour procéder ensuite à une étape de segmentation. Dans cet exemple, nous procédons tout d'abord à l'acquisition d'une image de la webcam du portable. Puis, nous définissons un crop de l'image acquise grâce à la souris. Il vous est alors facile de calculer les métriques statistiques que vous souhaitez sur ce crop. La moyenne et la variance définissent un modèle gaussien sur chaque composante du crop. Dans la suite, vous pourrez soit utiliser le flux d'images provenant de votre webcam ou celui provenant de la caméra Realsense (cf. tuto précédent)
Dans cet exemple, il s'agit de produire un masque des pixels dont les composantes HSV sont comprises entre les variables lo et hi. Dans cet exemple, en agissant sur le click gauche ou droit de la souris vous diminuez ou augmentez la teinte h des deux variables lo et hi. Par ailleurs, dans notre exemple, vous constaterez que lo et hi se différentient non seulement par leur teinte mais également par leur saturation. Vous pourrez tester ce script sur une image de votre visage. Ces quelques lignes de codes illustrent également comment gérer des actions sur la souris. Elles gères les événements souris tels que le mouvement de la souris (cv2.EVENT_MOUSEMOVE), le double click milieu (EVENT_MBUTTONDBLCLK), le click droit (EVENT_RBUTTONDOWN) et le click gauche (EVENT_LBUTTONDOWN).
Généralement il est très intéressante de changer d'espace colorimétrique afin de mieux cibler l'espace dans lequel l'objet d'intérêt est discriminable : image=cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
Après avoir produite le mask avec mask=cv2.inRange(image, lo, hi)
il est parfois pertinant de débruiter l'image résultats en la flouttant ou par quelques opérations motrphologiques. Cela permet de fermer et remplir les formes :
Dans le code de segmentation d'une image couleur fourni précédemment, vous jouerez avec la taille du kernel (3x3 dans notre exemple), vous ajouterez une étape de flouttage de chaque canal en jouant sur la taille du voisinage (7 x 7 dans notre exemple). Pour finir vous jouerez avec les étapes d'érosion et de dilatation en modifiant le nombre de fois où chaque opérateur morphologique est appliqué (4 fois dans notre exemple).
Le code de segmentation d'une image couleur fourni précédemment permet de définir un mask binaire des pixels dont les composantes HSV sont l'intervalle [lo,hi]. Il est alors possible de détecter les éléments connexes dans le mask afin d'en extraire certaines informations telles qu'ici le minEnclosingCircle. D'autres features peuvent être utiles. Vous les trouverez ici : https://docs.opencv.org/3.4/dd/d49/tutorial_py_contour_features.html Sous l'hypothèse qu'un objet d'interêt est représenté par un ensemble de pixels connexes dont la couleur est contenu dans l'intervalle [lo,hi], il est alors possible de définir des contraintes sur un ensemble de features qui permettent de classer les objets ainsi détectés. Vous ajouterez les lignes suivantes dans le code de segmentation précédent.
Reste ensuite à visualiser les images.
Extraction de régions dans une image binarisée
Voici quelques lignes en python pour extraire des région de pixels connexes dans une image binarisée label()
. De ces régions sont extraites quelques propriétés regionprops()
Ce code agit stratégiquement de la même manière que le script de segmentation précédent mais en utilisant la librairie skimage.
Détection d'objets par template matching
Il est possible de détecter un ou plusieurs objets dans une image en appliquant une procédure de matching d'un template de chaque objet à détecter. Un template est une image du ou des objets en question. La fonction à utiliser est cv.matchTemplate(img_gray,template,parametre)
. Plusieurs parametre de matching sont possibles correspondant chacun à une métrique de corrélation. Voici les lignes de codes que vous testerez. Vous testerez les parametres suivants afin de définir celui qui fournit les meilleurs résultats. Par ailleurs, vous adapterez le code afin de prendre un charge le flux des images de la Realsense et une image template de l'objet que vous voulez détecté.
Segmentation des images par la méthodes des k-moyennes (kmeans)
Kmeans est un algorithme de clustering, dont l'objectif est de partitionner n points de données en k grappes. Chacun des n points de données sera assigné à un cluster avec la moyenne la plus proche. La moyenne de chaque groupe s'appelle «centroïde» ou «centre». Globalement, l'application de k-means donne k grappes distinctes des n points de données d'origine. Les points de données à l'intérieur d'un cluster particulier sont considérés comme «plus similaires» les uns aux autres que les points de données appartenant à d'autres groupes. Cet algorithme peut être appliquer sur des points d’origine géométrique, colorimétriques et autres.
Nous allons appliquer cette méthode afin d'assurer une segmentation couleur d'une image i.e. cela revient à trouver les couleur domainantes dans l'image.
Afin de traiter l’image en tant que point de données, il faut la convertir d’une forme matricielle à une forme vectorielle (liste de couleur rgb) avant d'appliquer la fonction de clustering :
Pour afficher les couleurs les plus dominantes dans l'image, il faut définir deux fonctions : centroid_histogram() pour récupérer le nombre de clusters différents et créer un histogramme basé sur le nombre de pixels affectés à chaque cluster ; et plot_colors() pour initialiser le graphique à barres représentant la fréquence relative de chacune des couleurs
Il suffit maintenant de construire un histogramme de clusters puis créer une figure représentant le nombre de pixels étiquetés pour chaque couleur.
Classification d'images par la mathode des K plus proches voisins (k-NN ou KNN)
Cet exercice permettra d'apprendre un modèle à partir des images de la bases CIFAR-10 téléchargeable ici: http://www.cs.toronto.edu/~kriz/cifar.html Décompresser les fichier dans un dossier que vous utiliserez dans le script suivant. Ici, le dossier est ./data
afin de vérifier que tout s'est bien passé utilisé :
Vous devriez trouver un tableau numpy de 10000x3072 d'uint8s (le 3072 vient du 3 x 1024). Chaque ligne du tableau stocke une image couleur 32x32 en RGB. L'image est stockée dans l'ordre des lignes principales, de sorte que les 32 premières entrées du tableau correspondent aux valeurs des canaux rouges de la première ligne de l'image. Pour vérifier les labels :
Nous avons les étiquettes comme dane matrice 10000 x 1
Pour charger les données de test, utiliser la même procédure que précédement car la forme des données de test est identique à la forme des données d’apprentissage:
Attention, les composantes RGB des images sont arrangées sous la forme d'une vecteur à 1 dimension. Pour afficher chaque image, il faut donc remettre sous la forme d'une image 2D RGB. Pour cela, nous opérons de la manière suivante en considérant que les images sont de résolution 32x32
Désormais, nous allons appliquer l'algorithmes des k-NN sur toutes les images de la base de training img_data et leurs labels img_label_orig
Détection d'objets par ondelettes de Haar
La détection d'objets à l'aide de classificateurs en cascade basés sur la décomposition en ondelettes de Haar est une méthode efficace de détection d'objets proposée par Paul Viola et Michael Jones dans leur article, "Rapid Object Detection using a Boosted Cascade of Simple Features" en 2001. Il s'agit d'une approche basée sur l'apprentissage automatique où un la fonction en cascade est formée à partir d'un grand nombre d'images positives et négatives. Cette méthode a été initialement mise en au point pour détecter des visages et a été étendu à d'autres objets tels quels les voitures. En python, vous pouvez faire appel à cette méthode via object_cascade=cv2.CascadeClassifier()
. Cette classe est instanciée en lui passant un paramètre qui représente le "modèle" adapté à l'objet à détecter. Vous pouvez télécharger les modèles relatifs à des humains ici : https://github.com/opencv/opencv/tree/master/data/haarcascades Pour tester le détecteur sur des véhicules, le modèle proposé par Andrews Sobral est téléchrgeable ici : https://github.com/andrewssobral/vehicle_detection_haarcascades/blob/master/cars.xml
Pour appliquer le détecteur à une image il suffit d'appeler la méthode object=object_cascade.detectMultiScale(gray, scaleFactor=1.10, minNeighbors=3)
en passant en paramètre le nom de la variable image (gray) qu'il faut préalablement transformée en niveau de gris. Il fauat également renseigner le facteur d'échelle (scaleFactor) utilisé pour réduire l'image à chaque étage et le nombre de voisins (minNeighbors) que chaque objet détecté doit avoir pour le valider comme "effectivement" l'objet recherché.
Cette méthode fournit une liste de boites englobantes (x, y, w et h) que vous afficherez sur chaque image couleur traitée afin de visualiser les résultats de la détection.
Ecrire un script permettant de mettre en musique cette classe et cette méthode sur la vidéo cars.mp4 fournies. Vous validerez votre script en utilisant les modèles relatifs au corps humains et en utilisant le flux d'une caméra.
Model training
Cette méthode pourrait être très intéressante pour détecter des objets lors du "challenge". Pour cela, je vous invite à lire et utiliser ce qui est proposé sur les 2 liens suivants. Ces liens décrivent comment il est possible d'apprendre un modèle spécifique à un objet donné.
http://coding-robin.de/2013/07/22/train-your-own-opencv-haar-classifier.html
https://github.com/mrnugget/opencv-haar-classifier-training
Model training for an other feature
Vous trouverez dans le lien suivant, l'apprentissage d'un modèle sur la base d'un autre type de caractéreristique : les Local Binary Pattern (LBP).
https://medium.com/@rithikachowta/object-detection-lbp-cascade-classifier-generation-a1d1a1c2d0b
Gestion de la depth map et estimation de la distance
Ce code permet de calculer la distance des parties de la scène qui se projette en chaque pixel de la caméra. Il utilise les deux nouvelles parties de code suivantes. La première permet de recaler les deux flux (depth map et rgb image) afin de garantir une correspondance pixel à pixel. Ces deux parties de codes sont à replacées dans le code complet présenté ensuite.
LA seconde permet de calculer la distance en un pixel (x,y) donné.
Le code complet integrant la partie acquisition et visualisation est la suivante. Un cercle est positionné dans les deux flux autour du pixel en lequel la distance est calculée.
Last updated