TD3 - Suivi d'un marqueur coloré

1. Color tracking

L'objectif de cette première partie est de réaliser le suivi d'une marqueur coloré (un post-it) dans une flux vidéo. Pour ce faire, vous allez mettre en œuvre une technique standard de vision par ordinateur. Pour vous simplifier la tâche, vous allez utiliser la la bibliothèque OpenCV (Open Computer Vision) via le wrapper JavaScript opencv4nodejs pour Node.js.

PostIt
Flux vidéo et cercle englobant du marqueur (à gauche) et image binaire segmentant le marqueur (à droite)

Téléchargez le squelette de code rvra_td3.zip et installez les modules nécessaire en exécutant la commande npm install depuis le répertoire décompressé. Sur les machines du CREMI, pour accéder à la version correcte de Node.js et d'OpenCV, exécutez au préalable les commandes :

export PATH=/opt/users/Node.js/bin:$PATH
export LD_LIBRARY_PATH=/opt/local/lib:$LD_LIBRARY_PATH
Branchez la caméra et lancez l'exécution du serveur Express avec la commande npm start. Dans un navigateur web, ouvrez l'URL http://localhost:3000/config : le flux vidéo de la caméra devrait s'y afficher. Lisez le code fourni dans 'main.js' et 'public/config.html' ; en vous aidant de la documentation des différents modules, analysez comment les images du flux vidéo sont récupérées sur le serveur et envoyées au client via socket.io.

Vous pouvez sélectionner par une clic souris la couleur du marqueur à suivre. Cette couleur est alors mise à jour dans le menu (pour éventuellement la modifier à la main) et transmise au serveur à l'aide d'une requête asynchrone.

Le pipeline de base que vous devez mettre en place sur le serveur est le suivant :

  1. Seuillez l'image couleur à l'aide la fonction inRange (doc.).
  2. Lors du seuillage, une certaine tolérance sera nécessaire autour de la couleur sélectionnée ; permettez à l'utilisateur de la contrôler (ajoutez ce paramètre au menu).
  3. Pour diminuer le bruit dans l'image seuillée, appliquez une ouverture morphologique à l'aide des focntion erode et dilate.
  4. Détectez les contours dans l'image binaire obtenue à l'aide de la fonction findContours.
  5. Parmi les N polygones détectés, conservez le plus grand ; calculez et afficher sur l'image son cercle englobant.

Vous constaterez que l'espace de couleur RGB n'est pas très robuste aux variations des conditions d'éclairage. Pour améliorer les résultats, effectuez le seuillage dans l'espace HSV sur le canal de teinte H. Vous pouvez arbitrairement fixer les bornes pour les autres canaux S et V (à [100,255] par exemple).

Pour travailler sans caméra, vous pouvez utiliser la vidéo suivante : PostIt.mov. Utilisez alors le code suivant pour ouvrir la capture vidéo OpenCV :

const wCap = new cv.VideoCapture(path.join(__dirname, 'PostIt.mov' ));

2. Stéréo et tracking

Vous allez utiliser le code de tracking précédent pour suivre la tête de l'observateur et en estimer la distance à l'écran (profondeur), en supposant que la direction de vue reste orthogonale à l'écran. Le calcul de la profondeur ne peut néanmoins pas se faire en se basant uniquement sur l'image 2D : une information supplémentaire est nécessaire, le rapport entre la taille réelle du marqueur et sa taille après projection dans l'image. Pour cela, nous avons besoin des paramètres intrinsèques de la caméra.

2.1. Calibration de la caméra

La calibration de la caméra consiste à retrouver les transformations qu'un point dans l'espace subit pour être projeté sur l'image. Dans ce TD, nous nous intéresserons seulement au cas particulier du modèle de caméra tête d'épingle (pinhole camera).

Dans cette configuration trois transformations sont à estimer :

  • la transformation liée aux paramètres intrinsèques de la caméra (distance focale et coordonnées du centre optique)
  • les distorsions dues à la lentille.
  • la transformation liée à ses paramètres extrinsèques (position et orientation)

Les deux premières sont constantes pour une caméra donnée et peuvent donc être pré-calculées. Plusieurs solutions existent pour calibrer une caméra, notamment avec OpenCV. Lisez la documentation et expliquez dans votre rapport le fonctionnement général d'une calibration de caméra.

Compilez et utilisez le code fournit correspondant à ce tutoriel OpenCV pour déterminer la matrice des paramètres intrinsèques et les coefficients de distorsion de votre caméra.

Pour travailler sans caméra, vous pouvez utiliser la vidéo suivante : calibration.mov. Modifiez alors la ligne 19 du fichier de configuration param.xml comme suit :

<Input>"calibration.mov"</Input>

2.2. Calcul de la profondeur

Copiez dans votre code de suivi d'un marqueur coloré les paramètres intrinsèques précédemment estimés. En utilisant cette information, proposez un schéma représentant la scène et les différents éléments nécessaires au calcul de la profondeur. Modifiez ensuite le code de votre tracker pour convertir la position du centre du cercle englobante dans l'image (en pixels) en positions 3D (en mm) dans le repère de la caméra.

Intégrez enfin le code du rendu stéréo du TD précédent (code fourni dans le dossier public). L'objectif est de placer la caméra virtuelle à la position précédemment calculée. Pour transmettre cette information du serveur au client, utilisez à nouveau socket.io (utilisez le nom de votre machine à la place de localhost). Testez en collant le marqueur coloré sur un Google Cardboard.

2.3. Bonus : fiducial marker

La robustesse et la précision du suivi avec un marqueur coloré est faible. En outre, un tel marqueur ne permettent pas d'obtenir l'orientation 3D du marqueur par rapport à la caméra (paramètres extrinsèques). Une façon plus robuste d'y parvenir est d'utiliser un (voire plusieurs) marqueur(s) possédant un motif binaire distinctif.

ARToolKit a été l'une des premières bibliothèques logicielles implémentant une telle approche. De nombreuses alternatives ont été ensuite proposées, améliorant aussi bien les motifs des marqueurs (cf. figure ci-jointe) que les algorithmes de tracking. Pour ce TD, vous utiliserez la bibliothèque ArUco via le portage JavaScript js-aruco.

marqueurs

En vous aidant de la (succincte) documentation, remplacer votre color tracking par l'estimation de la pose de cette bibliothèque. Comme celle-ci n'est pas basée sur opencv4nodejs, vous aurez besoin de convertir une cv.Mat en ImageData ; utilisez pour cela la bibliothèque pixel-util et le code suivant :

const imgData = pixelUtil.createImageData(frame.cols, frame.rows);
const matRGBA = frame.channels === 1
		? frame.cvtColor(cv.COLOR_GRAY2RGBA)
		: frame.cvtColor(cv.COLOR_BGR2RGBA);
imgData.data = matRGBA.getData();

Pour travailler sans caméra, vous pouvez utiliser la vidéo suivante : ArUco.mov.