JSARToolKit kullanarak artırılmış gerçeklik uygulamaları yazma

Ilmari Heikkinen

Giriş

Bu makale, web'de artırılmış gerçeklik uygulamaları yapmak için JSARToolKit kitaplığını WebRTC getUserMedia API ile kullanma hakkındadır. Oluşturma işlemi için sunduğu yüksek performans nedeniyle WebGL kullanıyorum. Bu makalenin sonunda, web kamerası videosunda bir artırılmış gerçeklik işaretçisinin üzerine 3D model yerleştiren bir demo uygulama elde edersiniz.

JSARToolKit, JavaScript için bir artırılmış gerçeklik kitaplığıdır. GPL kapsamında yayınlanan açık kaynak bir kütüphanedir ve Mozilla Remixing Reality demo için oluşturduğum Flash FLARToolKit'in doğrudan bağlantısıdır. FLARToolKit, C ARToolKit'in bağlantı noktası olan Java NyARToolKit'in bağlantı noktasıdır. Uzun bir yoldu ama sonunda başardık.

JSARToolKit, kanvas öğeleriyle çalışır. Resmi kanvastan okuması gerektiğinden, resmin sayfayla aynı kaynaktan gelmesi veya aynı kaynak politikasını atlatmak için CORS kullanılması gerekir. Özetlemek gerekirse, doku olarak kullanmak istediğiniz resim veya video öğesindeki crossOrigin mülkünü '' veya 'anonymous' olarak ayarlayın.

JSARToolKit'e analiz için bir tuval gönderdiğinizde JSARToolKit, resimde bulunan AR işaretçilerinin listesini ve ilgili dönüşüm matrislerini döndürür. Bir işaretçinin üzerine 3D nesne çizmek için dönüşüm matrisini kullandığınız 3D oluşturma kitaplığına iletirsiniz. Böylece nesnenizin matris kullanılarak dönüştürülmesi sağlanır. Ardından, WebGL sahnenizde video çerçevesini ve nesneyi bunun üzerine çizin. Artık hazırsınız.

JSARToolKit'i kullanarak videoyu analiz etmek için videoyu bir kanvas üzerine çizin ve ardından kanvası JSARToolKit'e aktarın. Bunu her kare için yaptığınızda video AR izlemeyi etkinleştirmiş olursunuz. JSARToolKit, modern JavaScript motorlarında 640x480 video karelerinde bile bunu gerçek zamanlı olarak yapacak kadar hızlıdır. Ancak video çerçevesi ne kadar büyükse işlenmesi de o kadar uzun sürer. 320x240 iyi bir video çerçevesi boyutudur ancak küçük işaretçiler veya birden fazla işaretçi kullanmayı planlıyorsanız 640x480 tercih edilir.

Demo

Web kamerası demosunu görüntülemek için tarayıcınızda WebRTC'nin etkinleştirilmiş olması gerekir (Chrome'da about:flags adresine gidip MediaStream'i etkinleştirin). Aşağıdaki AR işaretçisini de yazdırmanız gerekir. İşaretçi resmini telefonunuzda veya tabletinizde açıp web kamerasına göstermeyi de deneyebilirsiniz.

AR işaretçisi.
AR işaretçisi.

JSARToolKit'i ayarlama

JSARToolKit API, Java'ya oldukça benzer. Bu nedenle, kullanmak için bazı değişiklikler yapmanız gerekir. Temel fikir, bir raster nesnesinde çalışan bir algılayıcı nesnenizin olmasıdır. Algılayıcı ile raster arasında, raster koordinatlarını kamera koordinatlarına dönüştüren bir kamera parametresi nesnesi bulunur. Algılanan işaretçileri dedektörden almak için bunları iterasyonla gezer ve dönüşüm matrislerini kodunuza kopyalarsınız.

İlk adımda raster nesnesi, kamera parametresi nesnesi ve algılayıcı nesnesi oluşturulur.

// Create a RGB raster object for the 2D canvas.
// JSARToolKit uses raster objects to read image data.
// Note that you need to set canvas.changed = true on every frame.
var raster = new NyARRgbRaster_Canvas2D(canvas);

// FLARParam is the thing used by FLARToolKit to set camera parameters.
// Here we create a FLARParam for images with 320x240 pixel dimensions.
var param = new FLARParam(320, 240);

// The FLARMultiIdMarkerDetector is the actual detection engine for marker detection.
// It detects multiple ID markers. ID markers are special markers that encode a number.
var detector = new FLARMultiIdMarkerDetector(param, 120);

// For tracking video set continue mode to true. In continue mode, the detector
// tracks markers across multiple frames.
detector.setContinueMode(true);

// Copy the camera perspective matrix from the FLARParam to the WebGL library camera matrix.
// The second and third parameters determine the zNear and zFar planes for the perspective matrix.
param.copyCameraMatrix(display.camera.perspectiveMatrix, 10, 10000);

Web kamerasına erişmek için getUserMedia'yı kullanma

Ardından, WebRTC API'leri aracılığıyla web kamerası videosu alan bir video öğesi oluşturacağım. Önceden kaydedilmiş videolar için videonun kaynak özelliğini video URL'sine ayarlayın. Hareketsiz resimlerden işaretçi algılama işlemi yapıyorsanız resim öğesini neredeyse aynı şekilde kullanabilirsiniz.

WebRTC ve getUserMedia henüz yeni ortaya çıkan teknolojiler olduğundan bunları özellik algılamanız gerekir. Daha fazla bilgi için Eric Bidelman'ın HTML5'te Ses ve Video Kaydetme başlıklı makalesine göz atın.

var video = document.createElement('video');
video.width = 320;
video.height = 240;

var getUserMedia = function(t, onsuccess, onerror) {
  if (navigator.getUserMedia) {
    return navigator.getUserMedia(t, onsuccess, onerror);
  } else if (navigator.webkitGetUserMedia) {
    return navigator.webkitGetUserMedia(t, onsuccess, onerror);
  } else if (navigator.mozGetUserMedia) {
    return navigator.mozGetUserMedia(t, onsuccess, onerror);
  } else if (navigator.msGetUserMedia) {
    return navigator.msGetUserMedia(t, onsuccess, onerror);
  } else {
    onerror(new Error("No getUserMedia implementation found."));
  }
};

var URL = window.URL || window.webkitURL;
var createObjectURL = URL.createObjectURL || webkitURL.createObjectURL;
if (!createObjectURL) {
  throw new Error("URL.createObjectURL not found.");
}

getUserMedia({'video': true},
  function(stream) {
    var url = createObjectURL(stream);
    video.src = url;
  },
  function(error) {
    alert("Couldn't access webcam.");
  }
);

İşaretçileri algılama

Algılayıcıyı sorunsuz bir şekilde çalıştırdıktan sonra, AR matrislerini algılaması için ona resim beslemeye başlayabiliriz. Önce resmi raster nesne kanvasına çizin, ardından raster nesnede dedektörü çalıştırın. Algılayıcı, resimde bulunan işaretçilerin sayısını döndürür.

// Draw the video frame to the raster canvas, scaled to 320x240.
canvas.getContext('2d').drawImage(video, 0, 0, 320, 240);

// Tell the raster object that the underlying canvas has changed.
canvas.changed = true;

// Do marker detection by using the detector object on the raster object.
// The threshold parameter determines the threshold value
// for turning the video frame into a 1-bit black-and-white image.
//
var markerCount = detector.detectMarkerLite(raster, threshold);

Son adım, algılanan işaretçileri iteratif olarak incelemek ve dönüşüm matrislerini elde etmektir. İşaretçilerin üzerine 3D nesneler yerleştirmek için dönüşüm matrislerini kullanırsınız.

// Create a NyARTransMatResult object for getting the marker translation matrices.
var resultMat = new NyARTransMatResult();

var markers = {};

// Go through the detected markers and get their IDs and transformation matrices.
for (var idx = 0; idx < markerCount; idx++) {
  // Get the ID marker data for the current marker.
  // ID markers are special kind of markers that encode a number.
  // The bytes for the number are in the ID marker data.
  var id = detector.getIdMarkerData(idx);

  // Read bytes from the id packet.
  var currId = -1;
  // This code handles only 32-bit numbers or shorter.
  if (id.packetLength <= 4) {
    currId = 0;
    for (var i = 0; i &lt; id.packetLength; i++) {
      currId = (currId << 8) | id.getPacketData(i);
    }
  }

  // If this is a new id, let's start tracking it.
  if (markers[currId] == null) {
    markers[currId] = {};
  }
  // Get the transformation matrix for the detected marker.
  detector.getTransformMatrix(idx, resultMat);

  // Copy the result matrix into our marker tracker object.
  markers[currId].transform = Object.asCopy(resultMat);
}

Matris eşleme

JSARToolKit matrislerini glMatrix matrislerine (son dört öğede çeviri sütunu bulunan 16 öğeli FloatArrays) kopyalayan kod aşağıda verilmiştir. Bu işlem sihirli bir şekilde çalışır (yani ARToolKit matrislerinin nasıl ayarlandığını bilmiyorum. Tahminim ters çevrilmiş Y ekseni.) Her neyse, işareti tersine çeviren bu sihir, JSARToolKit matrisinin glMatrix ile aynı şekilde çalışmasını sağlar.

Kitaplığı Three.js gibi başka bir kitaplıkla kullanmak için ARToolKit matrislerini kitaplığın matris biçimine dönüştüren bir işlev yazmanız gerekir. Ayrıca FLARParam.copyCameraMatrix yöntemini de bağlamanız gerekir. copyCameraMatrix yöntemi, FLARParam perspektif matrisini glMatrix tarzı bir matrise yazar.

function copyMarkerMatrix(arMat, glMat) {
  glMat[0] = arMat.m00;
  glMat[1] = -arMat.m10;
  glMat[2] = arMat.m20;
  glMat[3] = 0;
  glMat[4] = arMat.m01;
  glMat[5] = -arMat.m11;
  glMat[6] = arMat.m21;
  glMat[7] = 0;
  glMat[8] = -arMat.m02;
  glMat[9] = arMat.m12;
  glMat[10] = -arMat.m22;
  glMat[11] = 0;
  glMat[12] = arMat.m03;
  glMat[13] = -arMat.m13;
  glMat[14] = arMat.m23;
  glMat[15] = 1;
}

Three.js entegrasyonu

Three.js, popüler bir JavaScript 3D motorudur. JSARToolKit çıkışının Three.js'de nasıl kullanılacağını anlatacağım. Üç şeye ihtiyacınız vardır: Üzerine video resminin çizildiği tam ekran bir dörtgen, FLARParam perspektif matrisine sahip bir kamera ve dönüştürme işlemi olarak işaretçi matrisine sahip bir nesne. Aşağıdaki kodda entegrasyon işleminde size yol göstereceğim.

// I'm going to use a glMatrix-style matrix as an intermediary.
// So the first step is to create a function to convert a glMatrix matrix into a Three.js Matrix4.
THREE.Matrix4.prototype.setFromArray = function(m) {
  return this.set(
    m[0], m[4], m[8], m[12],
    m[1], m[5], m[9], m[13],
    m[2], m[6], m[10], m[14],
    m[3], m[7], m[11], m[15]
  );
};

// glMatrix matrices are flat arrays.
var tmp = new Float32Array(16);

// Create a camera and a marker root object for your Three.js scene.
var camera = new THREE.Camera();
scene.add(camera);

var markerRoot = new THREE.Object3D();
markerRoot.matrixAutoUpdate = false;

// Add the marker models and suchlike into your marker root object.
var cube = new THREE.Mesh(
  new THREE.CubeGeometry(100,100,100),
  new THREE.MeshBasicMaterial({color: 0xff00ff})
);
cube.position.z = -50;
markerRoot.add(cube);

// Add the marker root to your scene.
scene.add(markerRoot);

// Next we need to make the Three.js camera use the FLARParam matrix.
param.copyCameraMatrix(tmp, 10, 10000);
camera.projectionMatrix.setFromArray(tmp);


// To display the video, first create a texture from it.
var videoTex = new THREE.Texture(videoCanvas);

// Then create a plane textured with the video.
var plane = new THREE.Mesh(
  new THREE.PlaneGeometry(2, 2, 0),
  new THREE.MeshBasicMaterial({map: videoTex})
);

// The video plane shouldn't care about the z-buffer.
plane.material.depthTest = false;
plane.material.depthWrite = false;

// Create a camera and a scene for the video plane and
// add the camera and the video plane to the scene.
var videoCam = new THREE.Camera();
var videoScene = new THREE.Scene();
videoScene.add(plane);
videoScene.add(videoCam);

...

// On every frame do the following:
function tick() {
  // Draw the video frame to the canvas.
  videoCanvas.getContext('2d').drawImage(video, 0, 0);
  canvas.getContext('2d').drawImage(videoCanvas, 0, 0, canvas.width, canvas.height);

  // Tell JSARToolKit that the canvas has changed.
  canvas.changed = true;

  // Update the video texture.
  videoTex.needsUpdate = true;

  // Detect the markers in the video frame.
  var markerCount = detector.detectMarkerLite(raster, threshold);
  for (var i=0; i&lt;markerCount; i++) {
    // Get the marker matrix into the result matrix.
    detector.getTransformMatrix(i, resultMat);

    // Copy the marker matrix to the tmp matrix.
    copyMarkerMatrix(resultMat, tmp);

    // Copy the marker matrix over to your marker root object.
    markerRoot.matrix.setFromArray(tmp);
  }

  // Render the scene.
  renderer.autoClear = false;
  renderer.clear();
  renderer.render(videoScene, videoCam);
  renderer.render(scene, camera);
}

Özet

Bu makalede, JSARToolKit'in temel özelliklerini ele aldık. Artık JavaScript ile web kamerası kullanan kendi artırılmış gerçeklik uygulamalarınızı oluşturmaya hazırsınız.

JSARToolKit'i Three.js ile entegre etmek biraz zahmetli olsa da kesinlikle mümkündür. Demoda doğru şekilde yapıp yapmadığımdan emin değilim. Entegrasyonu gerçekleştirmenin daha iyi bir yolu olduğunu düşünüyorsanız lütfen bana bildirin. Yamalar memnuniyetle karşılanır :)

Referanslar