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ın WebRTC getUserMedia API'sı ile birlikte kullanılmasıyla ilgilidir. Oluşturma için, sunduğu yüksek performans nedeniyle WebGL'yi kullanıyorum. Bu makalenin nihai sonucu, web kamerası videosundaki bir artırılmış gerçeklik işaretçisinin üzerine 3D model yerleştiren bir demo uygulamadır.

JSARToolKit, bir JavaScript artırılmış gerçeklik kitaplığıdır. Bu, GPL altında kullanıma sunulan açık kaynak bir kitaplıktır ve Mozilla Remixing Reality demosu için yaptığım Flash FLARToolKit'in doğrudan bağlantı noktasıdır. FLARToolKit, C ARToolKit'in bağlantı noktası olan Java NyARToolKit bağlantı noktasıdır. Çok yol katettiniz, ama şimdi devam ediyoruz.

JSARToolKit, tuval öğeleri üzerinde çalışır. Resmin kanvastan okunması gerektiğinden, resmin sayfayla aynı kaynaktan gelmesi veya aynı kaynak politikasını uygulamak için CORS'yi kullanması gerekir. Özetle, doku olarak kullanmak istediğiniz resim veya video öğesinde crossOrigin özelliğini '' veya 'anonymous' değerine ayarlayın.

Analiz için JSARToolKit'e bir kanvas ilettiğinizde, JSARToolKit resimde bulunan AR işaretçilerinin ve ilgili dönüştürme matrislerinin bir listesini döndürür. İşaretçinin üzerine 3D bir nesne çizmek için, dönüştürme matrisini kullandığınız 3D oluşturma kitaplığına geçirirsiniz. Böylece, nesneniz matris kullanılarak dönüştürülür. Ardından, WebGL sahnenizde video çerçevesini çizin ve nesneyi bunun üzerine çizin. Hepsi bu kadar.

JSARToolKit'i kullanarak video analiz etmek için, videoyu bir tuvale çizin ve ardından kanvası JSARToolKit'e iletin. Bu işlemi her kare için yaptığınızda video AR izlemesine sahip olursunuz. JSARToolKit, modern JavaScript motorlarında bunu 640x480 boyutlu video karelerinde bile gerçek zamanlı olarak yapacak kadar hızlıdır. Ancak video karesi ne kadar büyükse işlenmesi de o kadar uzun sürer. İyi bir video karesinin boyutu 320x240'tır, ancak küçük veya birden çok işaretçi kullanmayı bekliyorsanız 640x480 boyutu tercih edilir.

Demografi

Web kamerası demosunu görüntülemek için tarayıcınızda WebRTC'yi etkinleştirmeniz gerekir (Chrome'da about:flags bölümüne gidip MediaStream'i etkinleştirin). Ayrıca, aşağıdaki AR işaretçisini de yazdırmanız gerekir. İşaretçi resmini telefonunuzda veya tabletinizde açmayı ve web kamerasına göstermeyi de deneyebilirsiniz.

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

JSARToolKit kurulumu

JSARToolKit API, oldukça Java benzeridir, bu nedenle onu kullanmak için bazı bozulmalar yapmanız gerekecektir. Temel fikir, kafes nesne üzerinde çalışan bir algılayıcı nesnenizin olmasıdır. Algılayıcı ile kafes arasında, kafes koordinatlarını kamera koordinatlarına dönüştüren bir kamera parametresi nesnesi bulunur. Algılanan işaretçileri algılayıcıdan almak için bunları yineleyin ve dönüşüm matrislerini kodunuza kopyalayın.

İlk adım kafes nesnesini, kamera parametresi nesnesini ve algılayıcı nesnesini oluşturmaktır.

// 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

Şimdi, WebRTC API'larından web kamerası videosu alan bir video öğesi oluşturacağım. Önceden kaydedilmiş videolar için videonun kaynak özelliğini video URL'sine ayarlamanız yeterlidir. Hareketsiz resimlerden işaretçi algılama yapıyorsanız, resim öğesini de hemen hemen aynı şekilde kullanabilirsiniz.

WebRTC ve getUserMedia hâlâ yeni ortaya çıkan teknolojiler olduğundan bunları algılamanız gerekir. Daha ayrıntılı bilgi için Eric Bidelman'ın HTML5'te Ses ve Video Yakalama hakkındaki 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ının sorunsuz çalışmasını sağladıktan sonra, AR matrislerini algılamak için cihaza görüntüler yüklemeye başlayabiliriz. İlk olarak görüntüyü kafes nesnenin kanvasına çizin, ardından algılayıcıyı kafes nesnesi üzerinde çalıştırın. Algılayıcı, resimde bulunan işaretçi 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 tekrarlamak ve dönüşüm matrislerini elde etmektir. Dönüşüm matrislerini, 3D nesneleri işaretçilerin üzerine yerleştirmek için 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şlemesi

JSARToolKit matrislerini, glMatrix matrislerine (son dört öğedeki çeviri sütunuyla birlikte 16 öğeli FloatArrays'e) kopyalamak için gereken kodu burada bulabilirsiniz. Sihirli bir şekilde çalışıyor (oku: ARToolKit matrislerinin nasıl ayarlandığını bilmiyorum. Ters Y eksenini tahmin ediyorum.) Yine de, bu işaretleri tersine çeviren bu voodoo, 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 fonksiyon yazmanız gerekir. Ayrıca, FLARParam.copycameraMatrix yöntemini kullanmanız gerekir. CopyKameraMatrix 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. Three.js’de JSARToolKit çıktısının nasıl kullanılacağını anlatacağım. Üç şeye ihtiyacınız olacak: video görüntüsünün üzerine çizildiği tam ekran bir dörtlü, FLARParam perspektif matrisi olan bir kamera ve dönüşümü olarak işaretçi matrisine sahip bir nesne. Aşağıdaki kodda yer alan entegrasyon adımlarında 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 ile ilgili temel bilgileri inceledik. Artık JavaScript ile artırılmış gerçeklik uygulamalarını kullanarak kendi web kameranızı oluşturmaya hazırsınız.

JSARToolKit'i Three.js ile entegre etmek biraz zor bir işlemdir, ancak bunu yapmak kesinlikle mümkündür. Demoda bunu doğru yaptığımdan% 100 emin değilim. Bu nedenle entegrasyonu sağlamanın daha iyi bir yolunu biliyorsanız lütfen bize bildirin. yama kullanabilirsiniz :)

Referanslar