Le Processing ARToolkit est un outil puissant qui permet aux développeurs de créer des applications interactives en temps réel. Il permet de remplacer le marker par une vidéo active en live sur un flux vidéo webcam et de détecter le repère 3D du marker. Cet outil est très utile pour les développeurs qui souhaitent créer des applications interactives en temps réel, telles que des jeux vidéo, des applications de réalité augmentée ou des applications de vision par ordinateur. Dans cet article, nous allons examiner en détail comment le Processing ARToolkit peut être utilisé pour remplacer le marker par une vidéo active en live sur un flux vidéo webcam et détecter le repère 3D du marker.
Processing ARToolkit : Remplacement du marker par une vidéo active en live sur un flux vidéo webcam et détection du repère 3D du marker.
Par X. HINAULT – 18 Février 2012

Explication
- Ce programme teste la superposition d’un marker par une vidéo active lue par la librairie GSVideo en utilisant ARToolkit avec Processing à l’aide de la librairie nyar4psg.
- On réalise ici :
- la capture d’un flux vidéo avec la librairie GSVideo
- à chaque nouvelle frame, on réalise la détection détection d’un marker parmi 3 ou 4 prédéfinis (les fichiers doivent être présents sur votre machine)
- on superpose l’image courante de la vidéo sur le marker
- on affiche également le repère 3D du marker détecté
- Le fichier vidéo utilisé est disponible ici : fichier à télécharger ici : http://mirror.bigbuckbunny.de/peach/bigbuckbunny_movies/big_buck_bunny_480p_stereo.ogg
Ce qui est assez génial :
- la détection est très fluide et le marker est véritablement « remplacé » par l’image 2D dans le flux vidéo final
- la superposition du marker par l’image est sensible à la rotation (si on retourne le marker, l’image tourne, permettant d’imaginer de créer une sorte de « volant virtuel » à l’aide d’un simple marker)
- la superposition du marker par l’image est robuste même en cas d’angulation importante par rapport à l’axe de la caméra
- la luminosité ambiante n’intervient pas, les réglages se limitant au minimum.. c’est à dire, rien de spécial au niveau du code !
- on pourrait également superposer des images différentes en fonction du marker détecté, permettant d’envisager une « galerie virtuelle »
- on peut également fixer la taille du recouvrement, l’image pouvant être beaucoup plus grande que le marker, permettant d’envisager une sorte de « mur » virtuel…
Matériel et configuration utilisés
- PC Intel Core Quad 2.33 Ghz
- Webcam(s) USB C270
- Ubuntu 10.04 LTS
- Processing 1-5
- Librairie GSVideo 0.9
- nyar4psg 1.1.7
Ressources utiles
Le programme
Copier/coller le code dans Processing, adaptez les chemins à votre situation, enjoy !
// par X. HINAULT – tous droits réservés
// inspired from : // Augmented Reality Basic Example by Amnon Owed (21/12/11)
// Programme écrit le : 10/01/2012.
// ——- Licence du code de ce programme : GPL v3—–
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License,
// or any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////// Description du programme ////////////
// Utilise la librairie GSVideo de capture et lecture vidéo
// XXXXXXXXXXXXXXXXXXXXXX ENTETE DECLARATIVE XXXXXXXXXXXXXXXXXXXXXX
// inclusion des librairies utilisées
import codeanticode.gsvideo.*; // importe la librairie vidéo GSVideo qui implémente GStreamer pour Processing (compatible Linux)
// librairie comparable à la librairie native vidéo de Processing (qui implémente QuickTime..)- Voir Reference librairie Video Processing
// cette librairie doit être présente dans le répertoire modes/java/libraries du répertoire Processing (1-5)
// voir ici : http://gsvideo.sourceforge.net/
// et ici : http://codeanticode.wordpress.com/2011/05/16/gsvideo-09-release
import java.io.*; // pour le chargement des fichiers descriptifs des « patterns »
import processing.opengl.*; // pour rendu 3D avec la librairie OPenGL
import jp.nyatla.nyar4psg.*; // La librairie NyARToolkit Processing library = ARToolKit pour Processing
// Cette librairie permet de détecter des « pattern » ou « marker » dans une image
// et d’analyser leur transformation de perspective
// nyAR4psg est à télécharger ici : http://sourceforge.jp/projects/nyartoolkit/releases/
// et à mettre dans le répertoire des librairies
//—– chemin absolu fichier de paramètres de distorsion de la camera —-
//String camParaPath = « /home/hinault/Téléchargements/librairies_processing/nyar4psg-1.1.6/data/camera_para.dat »;
String camParaPath = « /home/hinault/Téléchargements/processing-1.5/modes/java/libraries/NyAR4psg/data/camera_para.dat »;
//—– chemin absolu fichiers de description des « patterns » ou « markers » —-
String patternPath = « /home/hinault/Téléchargements/patternMaker/examples/ARToolKit_Patterns »;
// l’archive patternMaker est disponible ici : http://www.cs.utah.edu/gdc/projects/augmentedreality/download.html
// déclaration objets
GSCapture cam1; // déclare un objet GSCapture représentant une webcam
GSMovie movie;
PImage imgSrc, imgAR; // Objets PImages utiles
// déclaration variables globales
//—— déclaration des variables de couleur utiles —-
int jaune=color(255,255,0);
int vert=color(0,255,0);
int rouge=color(255,0,0);
int bleu=color(0,0,255);
int noir=color(0,0,0);
int blanc=color(255,255,255);
int bleuclair=color(0,255,255);
int violet=color(255,0,255);
//— taille de l’image webcam —
int widthCapture= 640;
int heightCapture=480;
//— taille de l’image à utiliser pour la détection = plus petite pour plus rapide —
int widthAR= 640;
int heightAR=480;
int numMarkers = 3; // le nombre de pattern markers à utiliser
String[] nameMarkers= new String[numMarkers]; // pour mémoriser le nom des marker
MultiMarker nya; // déclaration de l’objet principal pour reconnaissance « markers »
float displayScale; // échelle d’affichage
// XXXXXXXXXXXXXXXXXXXXXX Fonction SETUP XXXXXXXXXXXXXXXXXXXXXX
void setup(){ // fonction d’initialisation exécutée 1 fois au démarrage
// —- initialisation paramètres graphiques utilisés
colorMode(RGB, 255,255,255); // fixe format couleur R G B pour fill, stroke, etc…
fill(0,0,255); // couleur remplissage RGB – noFill() si pas de remplissage
stroke (0,0,0); // couleur pourtour RGB – noStroke() si pas de pourtour
rectMode(CORNER); // origine rectangle : CORNER = coin sup gauche | CENTER : centre
imageMode(CORNER); // origine image : CORNER = coin sup gauche | CENTER : centre
ellipseMode(CENTER); // origine cercles / ellipses : CENTER : centre (autres : RADIUS, CORNERS, CORNER
//strokeWeight(0); // largeur pourtour
frameRate(20);// Images par seconde – The default rate is 60 frames per second
// — initialisation fenêtre de base —
size(widthCapture, heightCapture,P3D); // ouvre une fenêtre xpixels x ypixels et active P3D — obligatoire pour 3D !!
//size(widthCapture, heightCapture,OPENGL); // ouvre une fenêtre xpixels x ypixels et active ou OPENGL — obligatoire pour 3D !!
background(bleu); // couleur fond fenetre
//—- initialisation police utilisée —-
textFont(createFont(« Arial », 80)); // police utilisée
// — initialisation des objets et fonctionnalités utilisées —
//======== Initialisation Objets GSVideo (capture et/ou lecture video =========
//GSCapture(this, int requestWidth, int requestHeight, [int frameRate], [String sourceName], [String cameraName])
// cam1 = new GSCapture(this, width, height,20, »v4l2src », »/dev/video0″); // Initialise objet GSCapture désignant webcam – avant GSVideo 1.0
cam1 = new GSCapture(this, widthCapture, heightCapture,« v4l2src »,« /dev/video0 », 20); // Initialise objet GSCapture désignant webcam – depuis GSVideo 1.0
// largeur et hauteur doivent être compatible avec la webcam – typiquement 160×120 ou 320×240 ou 640×480…
// NB : Framerate >=20 évite message erreur
// cam1.play(); // démarre objet GSCapture = la webcam – version GSVideo avant 0.9
cam1.start(); // démarre objet GSCapture = la webcam – version GSVideo après 0.9
//========== initialisation movie ====================
movie = new GSMovie(this, « /home/hinault/Bureau/trans/big_buck_bunny_480p_stereo.ogg »);
// fichier à télécharger ici : http://mirror.bigbuckbunny.de/peach/bigbuckbunny_movies/big_buck_bunny_480p_stereo.ogg
movie.loop();
//=========== initialisation détection des markers =========================
// création d’un objet MultiMarker avec résolution voulue, les paramètres caméra et le système de coordonnées voulu
nya = new MultiMarker(this, widthAR, heightAR, camParaPath, NyAR4PsgConfig.CONFIG_DEFAULT);
// fixe le nombre de fois qu’un marqueur ne doit plus petre détecté pour ne plus l’afficher.
//Par défaut = 10. Mettre à 1 pour visualisation immédiate
nya.setLostDelay(10);
// fixe le niveau de seuil de détection à utiliser. Valeur possible entre 0 et 255. Mettre -1 (=THLESHOLD_AUTO) pour seuil automatique
nya.setThreshold(MultiMarker.THLESHOLD_AUTO);
// fixe le niveau de seuil de confiance (= probabilité de correspondance) à utiliser pour la reconnaissance des markers. Valeur possible entre 0 et 1.
// Valeur par défaut = 0.51 (=.DEFAULT_CF_THRESHOLD). Plus le seuil est élevé et plus la détection est rigoureuse.
nya.setConfidenceThreshold(MultiMarker.DEFAULT_CF_THRESHOLD);
//nya.setConfidenceThreshold(0.8); // sélection exigeante
//– chargement des fichiers de description des patterns
//— pour chargement manuel des fichiers voulus
int widthMarker=135; // taille réelle du marker utilisé en mmm – on aura un correspondance 1 mm = 1 pixel dans le repère 3D du marker
//int sizeMarker=16; // résolution du marker – 16×16 par défaut – utiliser 16×16
//int borderMarker=25; // largeur du bord du marker – 25% par défaut
// nya.addARMarker(patternPath + « / » + patterns[40], 80); // ajoute le fichier de description à l’objet principal de détection AR
//nameMarkers[0]= patterns[40]; // mémorise le nom du marker [i]
nameMarkers[0]= « 4x4_99.patt »; // mémorise le nom du fichier du marker voulu
nya.addARMarker(patternPath + « / » + nameMarkers[0], widthMarker); // ajoute le fichier de description à l’objet principal de détection AR – bordure 25% et 16×16 par défaut
//nya.addARMarker(patternPath + « / » + nameMarkers[0], sizeMarker, widthMarker); // ajoute le fichier de description à l’objet principal de détection AR – 16×16 par défaut
//nya.addARMarker(patternPath + « / » + nameMarkers[0],sizeMarker,borderMarker, widthMarker); // ajoute le fichier de description à l’objet principal de détection AR
println (« Fichier chargé : « + nameMarkers[0]);
//nya.addARMarker(patternPath + « / » + patterns[83], 80); // ajoute le fichier de description à l’objet principal de détection AR
//nameMarkers[1]= patterns[83]; // mémorise le nom du marker [i]
nameMarkers[1]= « 4x4_50.patt »; // mémorise le nom du fichier du marker voulu
nya.addARMarker(patternPath + « / » + nameMarkers[1] , widthMarker); // ajoute le fichier de description à l’objet principal de détection AR
//nya.addARMarker(patternPath + « / » + nameMarkers[1] , sizeMarker,widthMarker); // ajoute le fichier de description à l’objet principal de détection AR
//nya.addARMarker(patternPath + « / » + nameMarkers[1] , sizeMarker,borderMarker,widthMarker); // ajoute le fichier de description à l’objet principal de détection AR
println (« Fichier chargé : « + nameMarkers[1]);
//nya.addARMarker(patternPath + « / » + patterns[99], 80); // ajoute le fichier de description à l’objet principal de détection AR
//nameMarkers[2]= patterns[99]; // mémorise le nom du marker [i]
nameMarkers[2]= « 4x4_83.patt »; // mémorise le nom du fichier du marker voulu
nya.addARMarker(patternPath + « / » + nameMarkers[2], widthMarker); // ajoute le fichier de description à l’objet principal de détection AR
//nya.addARMarker(patternPath + « / » + nameMarkers[2], sizeMarker,widthMarker); // ajoute le fichier de description à l’objet principal de détection AR
//nya.addARMarker(patternPath + « / » + nameMarkers[2], sizeMarker,borderMarker,widthMarker); // ajoute le fichier de description à l’objet principal de détection AR
println (« Fichier chargé : « + nameMarkers[2]);
//noLoop();
} // fin fonction Setup
// XXXXXXXXXXXXXXXXXXXXXX Fonction Draw XXXXXXXXXXXXXXXXXXXX
void draw() { // fonction exécutée en boucle
// Code type capture GSVideo – préférer utilisation de captureEvent()
if (cam1.available() == true) { // si une nouvelle frame est disponible
cam1.read(); // acquisition d’un frame
background(0); // efface le fond
image(cam1, 0, 0); // affiche image
//set(0, 0, cam); // plus rapide
println(« debut= »+millis());
println(« seuil de binarisation actuel = « + nya.getCurrentThreshold());
nya.detect(cam1); // detection des markers dans l’image à la résolution voulue
// l’image passée en paramètre doit avoir la même résolution que ce qui a été défini à l’initialisation du constructeur
drawMarkers(); // dessiner les coordonnées des « markers » détectés
draw3D(); // dessiner en 3D sur les markers détectés
//nya.drawBackground(cam1);
//image(cam1, 0, 0); // affiche image
println(« fin= »+millis());
} // fin if available
/*
if (pipe.available() == true) { // si une nouvelle frame est disponible
pipe.read(); // acquisition d’un frame
image(pipe, 0, 0); // affiche image
//set(0, 0, cam); // plus rapide
} // fin if available
*/
} // fin de la fonction draw()
// XXXXXXXXXXXXXXXXXXXXXX Autres Fonctions XXXXXXXXXXXXXXXXXXXXXX
//— fonction event movie
public void movieEvent(GSMovie movie) { // à chaque fois qu’une frame vidéo est disponible
movie.read();
imgSrc=movie.get();
}
//———- fonction de dessin de tous les markers détectés
void drawMarkers() {
// paramètres affichage texte
textAlign(LEFT, TOP); // paramètre d’affichage du texte
textSize(10); // taille à utiliser pour le texte
// — paramètre graphique
noStroke();
// scale from AR detection size to sketch display size (changes the display of the coordinates, not the values)
//scale(displayScale);
// for all the markers…
for (int i=0; i<numMarkers; i++) { // passe en revue les markers de référence
// if the marker does NOT exist (the ! exlamation mark negates it) continue to the next marker, aka do nothing
if ((!nya.isExistMarker(i))) { continue; }
// passe au marker suivant si le marker(i) n’est pas détecté
// the following code is only reached and run if the marker DOES EXIST
println (« Le marker « + nameMarkers[i] + » est détecté. »);
println(« seuil de confiance = « + nya.getConfidence(i)); // affiche le seuil de confiance de détection
// get the four marker coordinates into an array of 2D PVectors
PVector[] pos2d = nya.getMarkerVertex2D(i);// récupère les 4 coins dans un tableau de PVector. Coordonnées 2D – origine = 0,0 de l’image = coin sup gauche
// draw each vector both textually and with a red dot
for (int j=0; j<pos2d.length; j++) {
String s = « (« + int(pos2d[j].x) + « , » + int(pos2d[j].y) + « ) »;
fill(255);
rect(pos2d[j].x, pos2d[j].y, textWidth(s) + 3, textAscent() + textDescent() + 3);
fill(0);
text(s, pos2d[j].x + 2, pos2d[j].y + 2);
fill(0, 0, 255);
ellipse(pos2d[j].x, pos2d[j].y, 10, 10);
} // fin for pos2d
} // fin if numMarker
} // — fin draw Markers —
void draw3D () {
//——- OPENGL DOIT ETRE ACTIVE ++ cf size() ———–
nya.setARPerspective(); // uniformise la perspective pour tous les markers…
PMatrix3D syst3D; // déclare un système de coordonnées 3D..
// objets qui disparaissent… cf http://processing.org/reference/frustum_.html
// et aussi : http://processing.org/reference/perspective_.html
// — cf pas avec P3D au lieu OPENGPl
for (int i=0; i<numMarkers; i++) { // passe en revue les Markers de référence
if ((!nya.isExistMarker(i))) { continue; } // si le marker n’est pas détecté on passe au suivant
syst3D = nya.getMarkerMatrix(i); // récupère le système de coordonnées 3D…
setMatrix(syst3D); // fixe le système de coordonnées du nouveau système de coordonnées
scale(1, –1); // tourne le système de coordonnées pour travailler intuitivement pour les utilisateurs Processing
scale(1.0); // 1 pour taille x1
//— affichage du repère 3D 0x, 0y, 0z
stroke(255,0,0);
line (0,0,0, 100,0,0); // axe des x
stroke(0,255,0);
line (0,0,0, 0,100,0); // axe des y
stroke(0,0,255);
line (0,0,0, 0,0,100); // axe des z
//— dessin 3D —
/* // forme prédéfinies
lights(); // allume la lampe…
//stroke(0,0,0);
noStroke(); // pas de pourtour
fill(255,0,0, 160); // 4ème valeur = transparence
// sphere(25); // dessine une sphère
box (135,135,30); // plan
noLights(); // stop lumières
*/
//— forme vectorielle —
beginShape(); // début du tracé de la forme vectorielle
texture(imgSrc); // mode texture = utilisation d’une image à l’intérieur de la forme !
// vertex(x, y, z, u, v); avec u : coordonnées x mapping et v : coordonnée y mapping
float coeffX=imgSrc.width/imgSrc.height;
float coeffY=imgSrc.height/imgSrc.width;
vertex(–100, –100, 0, 0, 0);
vertex(100, –100, 0, imgSrc.width, 0);
vertex(100, 100, 0, imgSrc.width, imgSrc.height);
vertex(–100, 100, 0, 0, imgSrc.height);
/*
vertex(-100*coeffX, -100*coeffY, 0, 0, 0);
vertex(100, -100*coeffY, 0, imgSrc.width, 0);
vertex(100*coeffX, 100*coeffY, 0, imgSrc.width, imgSrc.height);
vertex(-100*coeffX, 100*coeffY, 0, 0, imgSrc.height);
*/
endShape(); // fin du tracé de la forme vectorielle
} // fin for numMarkers
// restaure la perspective par défaut – fonction Processing core.PGraphics
perspective();
} // fin draw 3D
//——— fonction loadPatternFilesnames() : charge l’ensemble des fichiers *.patt du répertoire
// this function loads .patt filenames into a list of Strings based on a full path to a directory (relies on java.io)
String[] loadPatternFilenames(String path) {
File folder = new File(path);
FilenameFilter pattFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(« .patt »);
}
}; // fin filenameFilter
return folder.list(pattFilter); // renvoi le tableau de String
} // fin loadPatternFilenames
/*
//— évènement capture vidéo —
void captureEvent(GSCapture cam) { // est appelée lorsqu’une capture survient
// cf doc librairie Video Processing – cf exemple Capture LivePocky
// est appelée à chaque fois qu’une nouvelle frame est disponible, quelque soit la caméra
// utiliser des conditions pour tester la caméra disponible
if (cam1.available() == true) cam1.read(); // acquisition d’une nouvelle frame
*/
//————- Fonction d’arret de Processing —-
public void stop(){ // fonction d’arrêt de Processing
//pipe.delete(); // efface l’objet GScapture
cam1.delete();
super.stop(); // obligatoire
} // fin fonction stop()
//XXXXXXXXXXXXXXXXXX Fin du programme XXXXXXXXXXXXXXXXX
Articles similaires:
- Processing ARToolkit : Test simple de la détection d’un marker à partir d’un flux vidéo webcam.
- Processing ARToolkit : Second essai utilisant ARToolkit et la réalité augmentée
- Processing ARToolkit : Remplacement du marker par une image en live sur un flux vidéo webcam et détection du repère 3D du marker.
- http://web.archive.org/web/20210804223007/http://www.mon-club-elec.fr/pmwiki_mon_club_elec/pmwiki.php?n=MAIN.OutilsProcessingARToolkit
- Processing + ARToolkit + JavacvPro : Détection du centre, du type d’un marker à partir d’un flux vidéo webcam.
Articles Liés
- Javascript : Graphique Dygraphs simple
Le Javascript est un langage de programmation très populaire et puissant qui permet aux développeurs…
- Processing ARToolkit : Remplacement du marker par une image en live sur un flux vidéo webcam et détection du repère 3D du marker.
Le Processing ARToolkit est un outil puissant qui permet aux développeurs de créer des…
- Javascript : Afficher 6 widgets graphiques fournis par une librairie graphique externe.
Le Javascript est un langage de programmation très populaire qui permet aux développeurs de créer…