==== Utilisation d'opencv et d'une webcam pour estimer la position et l'orientation de la tête de l'utilisateur ====
A cause des erreurs s'accumulant lors de l'intégration, il est très difficile d'obtenir un résultat correct pour la position de l'utilisateur en utilisant uniquement une centrale inertielle. En s'inspirant de l'approche utilisée par l'oculus rift dk2 expliquée [[http://doc-ok.org/?p=1138|ici]], nous expérimentons l'utilisation de la reconnaissance d'image pour suivre le déplacement de la tête de l'utilisateur.
=== Téléchargement d'OpenCV ===
Pour faciliter le traitement et l'acquisition d'images, nous utilisons la bibliothèque [[http://opencv.org/downloads.html|OpenCV]]. Cette bibliothèque est disponible pour toute les plateformes (windows, mac, linux, raspberrypi, ...) et est implémentée dans plusieurs langage dont c++ et java. Nous utiliserons java car nous disposons d'un simulateur déjà fonctionnel dans ce langage.
=== Programme de reconnaissance de points ===
Ce programme de test, cherche des points de repère sur l'image et fait la correspondance avec un modèle en 3D afin de déterminer l'orientation et la position en 3D.
CVHeadTracking.java : (Point d'entrée du programme)
package fr.pmclab.vrlab.virtualreality;
import java.util.ArrayList;
import java.util.List;
import org.opencv.calib3d.Calib3d;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.KeyPoint;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDouble;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.MatOfPoint3f;
import org.opencv.core.Point;
import org.opencv.core.Point3;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.features2d.FeatureDetector;
import org.opencv.features2d.Features2d;
import org.opencv.imgproc.Imgproc;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.Videoio;
import fr.pmclab.vrlab.cvutils.CVPreviewFrame;
import fr.pmclab.vrlab.cvutils.Mat2Image;
public class CVHeadTracking extends Thread {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
private final VideoCapture cam;
private final Mat2Image mat2Img = new Mat2Image();
private final CVPreviewFrame frame;
private final FeatureDetector simpleBlobDetector;
private final Mat cameraMatrix;
private final MatOfPoint3f objectPoints;
private final MatOfDouble distCoeffs;
public CVHeadTracking(CVPreviewFrame frame) {
this.frame = frame;
// Initialise la webcam
cam = new VideoCapture();
cam.open(0);
cam.set(Videoio.CAP_PROP_EXPOSURE, 15);
cam.set(Videoio.CAP_PROP_BRIGHTNESS, 0);
cam.set(Videoio.CAP_PROP_GAMMA, 100);
// Initialise le detecteur
simpleBlobDetector = FeatureDetector.create(FeatureDetector.SIMPLEBLOB);
// Créé les paramètres de la caméra (il faudrait calibrer la caméra pour plus de précision)
cameraMatrix = Mat.eye(3, 3, CvType.CV_64F);
distCoeffs = new MatOfDouble(Mat.zeros(8, 1, CvType.CV_64F));
// Créé le modèle d'objet 3D
objectPoints = new MatOfPoint3f();
List points = new ArrayList();
points.add(new Point3(0, 0, 0));
points.add(new Point3(0, 13, 0));
points.add(new Point3(13, 0, 0));
objectPoints.fromList(points);;
}
public MatOfPoint2f convertPoints(Mat img, MatOfKeyPoint mat) {
List out = new ArrayList();
for (KeyPoint p : mat.toList()) {
out.add(p.pt);
}
MatOfPoint2f outmat = new MatOfPoint2f();
outmat.fromList(out);
return outmat;
}
public void run() {
while (true) {
// 1) Récupère l'image depuis la webcam
cam.read(mat2Img.getMat());
Mat mat = mat2Img.getMat();
// 2) Applique un flou pour filtrer les faux positifs
Imgproc.blur(mat, mat, new Size(20, 20));
// 3) Détecte les points d'interets
MatOfKeyPoint keypoints = new MatOfKeyPoint();
simpleBlobDetector.detect(mat, keypoints);
// 4) Affiche les points d'interets sur l'image (DEBUG)
Features2d.drawKeypoints(mat, keypoints, mat, new Scalar(2,254,255),Features2d.DRAW_RICH_KEYPOINTS);
// 5) Si on trouve 3 points, on cherche la correspondance entre les points objets 3D et les points image 2D
if (keypoints.rows() == 3) {
Mat rvec = new Mat();
Mat tvec = new Mat();
Calib3d.solvePnP(objectPoints, convertPoints(mat, keypoints), cameraMatrix, distCoeffs, rvec, tvec);
// Affiche le résultat dans la console
double rx = tvec.get(0, 0)[0];
double ry = tvec.get(1, 0)[0];
double rz = tvec.get(2, 0)[0];
System.out.println("Rotation : "+new Point3(rx, ry, rz));
double px = tvec.get(0, 0)[0];
double py = tvec.get(1, 0)[0];
double pz = tvec.get(2, 0)[0];
System.out.println("Position : "+new Point3(px, py, pz));
}
// 6) Affiche l'image dans l'aperçu
frame.setFrame(mat2Img.getImage());
frame.repaint();
try {
Thread.sleep(30);
} catch (Exception e) { }
}
}
public static void main(String[] args) {
System.out.println("Welcome to OpenCV " + Core.VERSION);
CVPreviewFrame frame = new CVPreviewFrame();
frame.setVisible(true);
CVHeadTracking tracker = new CVHeadTracking(frame);
tracker.start();
}
}
CVPreviewFrame.java : (Fenêtre d'affichage pour voir ce qui se passe dans notre algorithme)
package fr.pmclab.vrlab.cvutils;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
public class CVPreviewFrame extends JFrame {
private static final long serialVersionUID = 1863937910467243551L;
private JPanel contentPane;
private BufferedImage img;
public CVPreviewFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 650, 490);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
}
public void paint(Graphics g){
g = contentPane.getGraphics();
g.drawImage(img, 0, 0, this);
}
public void setFrame(BufferedImage img) {
this.img = img;
}
}
Mat2Image.java : (Utilitaire de conversion d'image OpenCV en image java)
package fr.pmclab.vrlab.cvutils;
import java.awt.image.BufferedImage;
import org.opencv.core.Core;
import org.opencv.core.Mat;
public class Mat2Image {
private Mat mat;
private BufferedImage img;
private byte[] dat;
static{
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public Mat2Image() {
mat = new Mat();
}
public Mat2Image(Mat mat) {
getSpace(mat);
}
public void getSpace(Mat mat) {
this.mat = mat;
int w = mat.cols(), h = mat.rows();
if (dat == null || dat.length != w * h * 3) {
dat = new byte[w * h * 3];
}
if (img == null || img.getWidth() != w || img.getHeight() != h || img.getType() != BufferedImage.TYPE_3BYTE_BGR) {
img = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
}
}
public BufferedImage getImage() {
getSpace(mat);
mat.get(0, 0, dat);
img.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), dat);
return img;
}
public Mat getMat() {
return mat;
}
}
=== Modification d'une webcam ===
Les résultats obtenus avec une webcam classique sont meilleurs qu'avec l'imu mais restent médiocre. L'oculus rift utilise une webcam dotée d'un filtre qui ne laisse passer que les infrarouge. Nous pouvons nous procurer ce genre de dispositif en modifiant une webcam bon marché.
A suivre ...
[[:wiki:projet:lunettes_vr|Retour]]