نوشتن برنامه های واقعیت افزوده با استفاده از JSARToolKit

Ilmari Heikkinen

معرفی

این مقاله در مورد استفاده از کتابخانه JSARToolKit با WebRTC getUserMedia API برای انجام برنامه های واقعیت افزوده در وب است. برای رندر، من از WebGL به دلیل افزایش عملکرد آن استفاده می کنم. نتیجه نهایی این مقاله یک برنامه آزمایشی است که یک مدل سه بعدی را در بالای یک نشانگر واقعیت افزوده در ویدیوی وب کم قرار می دهد.

JSARToolKit یک کتابخانه واقعیت افزوده برای جاوا اسکریپت است. این یک کتابخانه منبع باز است که تحت GPL منتشر شده است و یک پورت مستقیم از Flash FLARToolKit است که من برای نسخه ی نمایشی Remixing Reality Mozilla ساخته ام. FLARToolKit خود پورت Java NyARToolKit است که پورت C ARToolKit است. راه طولانی است، اما ما اینجا هستیم.

JSARToolKit بر روی عناصر بوم عمل می کند. همانطور که باید تصویر را از روی بوم بخواند، تصویر باید از همان مبدأ صفحه باشد یا از CORS برای دور زدن خط مشی مبدا یکسان استفاده کند . به طور خلاصه، ویژگی crossOrigin را روی عنصر تصویر یا ویدیویی که می‌خواهید به‌عنوان بافت استفاده کنید، روی '' یا 'anonymous' تنظیم کنید.

هنگامی که یک بوم را برای تجزیه و تحلیل به JSARToolKit ارسال می کنید، JSARToolKit لیستی از نشانگرهای AR موجود در تصویر و ماتریس های تبدیل مربوطه را برمی گرداند. برای ترسیم یک شی 3 بعدی در بالای نشانگر، ماتریس تبدیل را به هر کتابخانه رندر سه بعدی که استفاده می کنید منتقل می کنید تا شی شما با استفاده از ماتریس تبدیل شود. سپس، فریم ویدیو را در صحنه WebGL خود بکشید و شی را روی آن بکشید و شما آماده هستید.

برای تجزیه و تحلیل ویدیو با استفاده از JSARToolKit، ویدیو را روی بوم بکشید، سپس بوم را به JSARToolKit منتقل کنید. این کار را برای هر فریم انجام دهید و ردیابی AR ویدیویی خواهید داشت. JSARToolKit در موتورهای جاوا اسکریپت مدرن به اندازه کافی سریع است که این کار را در زمان واقعی حتی در فریم های ویدیویی 640x480 انجام می دهد. با این حال، هرچه فریم ویدیو بزرگتر باشد، پردازش آن بیشتر طول می کشد. اندازه فریم ویدیویی خوب 320x240 است، اما اگر انتظار دارید از نشانگرهای کوچک یا چندین نشانگر استفاده کنید، 640x480 ترجیح داده می شود.

نسخه ی نمایشی

برای مشاهده نسخه نمایشی وب کم، باید WebRTC را در مرورگر خود فعال کنید (در کروم، به about:flags بروید و MediaStream را فعال کنید). همچنین باید نشانگر AR را در زیر چاپ کنید. همچنین می‌توانید تصویر نشانگر را روی تلفن یا رایانه لوحی خود باز کرده و به وب‌کم نشان دهید.

نشانگر AR.
نشانگر AR.

راه اندازی JSARToolKit

JSARToolKit API کاملاً جاوا مانند است، بنابراین برای استفاده از آن باید تغییراتی را انجام دهید. ایده اصلی این است که شما یک شی آشکارساز دارید که روی یک شی شطرنجی عمل می کند. بین آشکارساز و شطرنجی یک شی پارامتر دوربین است که مختصات شطرنجی را به مختصات دوربین تبدیل می کند. برای دریافت نشانگرهای شناسایی شده از آشکارساز، روی آنها تکرار می کنید و ماتریس های تبدیل آنها را در کد خود کپی می کنید.

اولین مرحله ایجاد شی شطرنجی، شی پارامتر دوربین و شی آشکارساز است.

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

استفاده از getUserMedia برای دسترسی به وب کم

در مرحله بعد، من یک عنصر ویدیویی ایجاد می کنم که ویدیوی وب کم را از طریق WebRTC API دریافت می کند. برای ویدیوهای از پیش ضبط شده، فقط ویژگی منبع ویدیو را روی URL ویدیو تنظیم کنید. اگر تشخیص نشانگر را از تصاویر ثابت انجام می دهید، می توانید از یک عنصر تصویر تقریباً به همان روش استفاده کنید.

از آنجایی که WebRTC و getUserMedia هنوز فناوری‌های نوظهور جدیدی هستند، شما باید آنها را شناسایی کنید. برای جزئیات بیشتر، مقاله اریک بیدلمن در مورد ضبط صدا و تصویر در HTML5 را بررسی کنید.

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.");
  }
);

تشخیص نشانگرها

هنگامی که آشکارساز را a-ok اجرا می کنیم، می توانیم شروع به تغذیه تصاویر برای تشخیص ماتریس های AR کنیم. ابتدا تصویر را روی بوم شی شطرنجی بکشید، سپس آشکارساز را روی شی شطرنجی اجرا کنید. آشکارساز تعداد نشانگرهای موجود در تصویر را برمی گرداند.

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

آخرین مرحله تکرار از طریق نشانگرهای شناسایی شده و دریافت ماتریس های تبدیل آنهاست. شما از ماتریس های تبدیل برای قرار دادن اشیاء سه بعدی در بالای نشانگرها استفاده می کنید.

// 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);
}

نقشه برداری ماتریسی

در اینجا کد کپی ماتریس های JSARToolKit به ماتریس های glMatrix (که FloatArray های 16 عنصری با ستون ترجمه در چهار عنصر آخر هستند) آمده است. با جادو کار می‌کند (بخوانید: من نمی‌دانم ماتریس‌های ARToolKit چگونه تنظیم می‌شوند. محور Y معکوس حدس من است.) به هر حال، این بیت معکوس کننده علامت باعث می‌شود ماتریس JSARToolKit مانند glMatrix کار کند.

برای استفاده از کتابخانه با کتابخانه دیگری مانند Three.js، باید تابعی بنویسید که ماتریس های ARToolKit را به فرمت ماتریس کتابخانه تبدیل کند. همچنین باید به روش FLAParam.copyCameraMatrix متصل شوید. روش copyCameraMatrix ماتریس پرسپکتیو FLAParam را در یک ماتریس به سبک glMatrix می نویسد.

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

Three.js یک موتور محبوب جاوا اسکریپت سه بعدی است. من قصد دارم نحوه استفاده از خروجی JSARToolKit را در Three.js توضیح دهم. شما به سه چیز نیاز دارید: یک چهارچوب تمام صفحه با تصویر ویدئویی کشیده شده روی آن، یک دوربین با ماتریس پرسپکتیو FLAParam و یک شی با ماتریس نشانگر به عنوان تبدیل آن. من شما را از طریق یکپارچه سازی در کد زیر راهنمایی می کنم.

// 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);
}

خلاصه

در این مقاله اصول اولیه JSARToolKit را مرور کردیم. اکنون آماده ساختن برنامه های کاربردی واقعیت افزوده وب کم خود با جاوا اسکریپت هستید.

ادغام JSARToolKit با Three.js کمی دردسرساز است، اما مطمئناً ممکن است. من 100% مطمئن نیستم که آیا این کار را در نسخه ی نمایشی خود به درستی انجام می دهم، بنابراین اگر راه بهتری برای دستیابی به ادغام می دانید، لطفاً به من اطلاع دهید. پچ ها خوش آمدید :)

منابع