• ブログTOP
  • AI・ディープラーニング
  • AR
  • VR
  • WEBサイト
  • WEBマーケティング
  • SEO
  • SNS
  • その他
  • お問い合わせ

ALAKI株式会社

COZIKEEいつまでも使えるTech情報発信ブログ

WebARはじめてみませんか?〜第4回 tracking.jsで完全オリジナルのマーカーを実現 / 画像認識編〜

2019年4月12日

  • このエントリーをはてなブックマークに追加

こんにちは!
デザイナーの伊東(@ALAKIWebVRAR1)です。

※TwitterでもWebARの事を中心に情報発信中!
よかったらフォローお願いしますm(_ _)m
Follow @ALAKIWebVRAR1

アプリをインストールする必要がなく、ブラウザで簡単に体験していただけるWebAR。
これまでARkit2や8th Wall WebによるマーカーレスWebAR、AR.jsを利用したマーカー型WebARをご紹介してきました。
今、話題の「5G」により、世間でも話題の注目の技術になってきた実感があります。

今回チャレンジしたいのは、通常の四角い黒の線に囲まれたマーカーではなく、自由に指定した特定の画像に対して3Dオブジェクトを表示させる、というものです。
ありもののプラットフォームを利用して簡単に開発するというよりは、ARの「認識」と「出力」という仕組みを分離して実現するため、中級〜上級者向けの内容になっているかと思います。

この記事では、

  1. 任意の画像の特徴点と、Webカメラに映った映像の特徴点の抽出
  2. 抽出した類似点同士を比較して、類似点を検出する

という、ARの「認識」の部分の仕組みを構築していきたいと思います。

そこで今回使用するのが、tracking.jsという画像認識に特化したプラグインです。
「認識」にtracking.jsを利用し、最終的には「出力」部分にAR.jsを利用して実現するというところを目標にやっていきたいと思います。

最後までよろしくお願いします!

この記事の目次

  • 1 下準備
  • 2 画像の特徴点の抽出
  • 3 画像とWebカメラの映像の類似点の検出
  • 4 まとめ

下準備

まずはtracking.jsをダウンロードしましょう。
下記サイトにアクセスして、「Download tracking.js」をクリックしてください。
https://trackingjs.com/

スクリーンショット 2019-04-11 22.40.45

ダウンロードしたzipファイルを展開すると、中に「examples」というフォルダがあるので開いてください。
これらのサンプルをベースにして、開発を進めていきたいと思います。

画像の特徴点の抽出

まずは任意の静止画の特徴点を抽出したいと思います。
「examples」フォルダの中の、「fast.html」というベースにカスタマイズしていきたいと思います。

以下が余分なスタイルなどを排除したソースコードになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<!doctype html>
 
<html>
<head>
  <meta charset="utf-8">
  <title>tracking.js - feature detection</title>
  <link rel="stylesheet" href="assets/demo.css">
 
  <script src="../build/tracking-min.js"></script>
  <script src="../node_modules/dat.gui/build/dat.gui.min.js"></script>
 
  <style>
  .demo-container {
    background: #131112;
  }
  #image {
    position: absolute;
    left: -1000px;
    top: -1000px;
  }
  #canvas {
    position: absolute;
    left: 50%;
    top: 50%;
    margin: -200px 0 0 -200px;
  }
  </style>
</head>
<body>
  <div class="demo-title">
    <p><a href="http://trackingjs.com" target="_parent">tracking.js</a> - detect feature points on a image</p>
  </div>
 
  <div class="demo-frame">
    <div class="demo-container">
      <img id="image" src="assets/fast.png" />
      <canvas id="canvas" width="400" height="400"></canvas>
    </div>
  </div>
 
  <script>
    window.onload = function() {
      var width = 400;
      var height = 400;
      var canvas = document.getElementById('canvas');
      var context = canvas.getContext('2d');
 
      var image = document.getElementById('image');
 
      window.fastThreshold = 10;
 
      var doFindFeatures = function() {
        tracking.Fast.THRESHOLD = window.fastThreshold;
        context.drawImage(image, 0, 0, width, height);
 
        var imageData = context.getImageData(0, 0, width, height);
        var gray = tracking.Image.grayscale(imageData.data, width, height);
        var corners = tracking.Fast.findCorners(gray, width, height);
 
        for (var i = 0; i < corners.length; i += 2) {
          context.fillStyle = '#f00';
          context.fillRect(corners[i], corners[i + 1], 3, 3);
        }
      };
 
      doFindFeatures();
 
      var gui = new dat.GUI();
      gui.add(window, 'fastThreshold', 0, 100).onChange(doFindFeatures);
    }
  </script>
</body>
</html>

ポイントとなる点を解説していきます。

.demo-frame内に、使用する画像とその画像と同じ大きさのcanvasを用意します。

1
2
3
4
5
6
  <div class="demo-frame">
    <div class="demo-container">
      <img id="image" src="assets/fast.png" />
      <canvas id="canvas" width="400" height="400"></canvas>
    </div>
  </div>

jsの記述を見てみると、画像をcanvasに描き込んでいるのがわかります。

1
2
3
4
5
6
7
8
      var canvas = document.getElementById('canvas');
      var context = canvas.getContext('2d');
 
      var image = document.getElementById('image');
 
      〜
 
      context.drawImage(image, 0, 0, width, height); /* キャンバスに画像を描き込む */

tracking.jsで画像や動画を扱う際は、必ずcanvasにデータを描き込んでおく必要があります。
canvasにすると、特徴点を抽出したり、比較しやすいデータの形になるようです。

下のコードでは、特徴点を抽出する際の「しきい値」のデフォルト値を設定しています。

1
window.fastThreshold = 10;

しきい値を少なくすれば抽出される点は増え、しきい値を大きくすれば抽出される点は減ります。
画像によってこの値を調節して、適切に抽出されるようにチューニングしましょう。

肝心の特徴点を抽出する部分のコードを見てみましょう。

1
2
3
4
5
6
7
8
        var imageData = context.getImageData(0, 0, width, height);
        var gray = tracking.Image.grayscale(imageData.data, width, height);
        var corners = tracking.Fast.findCorners(gray, width, height);
 
        for (var i = 0; i < corners.length; i += 2) {
          context.fillStyle = '#f00';
          context.fillRect(corners[i], corners[i + 1], 3, 3);
        }

変数imageDataにはcanvasに描き込んだ画像をデータとして代入しています。
次に、変数imageDataを一度白黒画像に変換して、変数grayに代入しています。
tracking.jsでは、特徴点を抽出する前に、より抽出の精度を高めるために最適な形に画像処理を行います。
特徴点の抽出には色情報は不要なので、白黒加工を施しているようです。

画像処理の次に特徴点を抽出して、変数cornersに代入します。
tracking.Fast.findCornersが抽出する関数です。
引数には画像データとその幅と高さを渡してあげます。

最後に、先ほど抽出した特徴点を、for関数で一つずつ描画してあげます。

以上が基本的な静止画像の特徴点の抽出の仕組みです。

スクリーンショット 2019-04-11 23.44.15

この仕組みはWebカメラの映像の特徴点の抽出にも応用が効きます。

画像とWebカメラの映像の類似点の検出

画像と映像の特徴点が抽出できれば、その二つを照らし合わせて類似点を検出しましょう。

ソースの全体像は割愛して、htmlとjs部分を分けてみていきます。

1
2
3
4
5
6
7
  <div class="demo-frame">
    <div class="demo-container">
      <img id="image1" src="assets/logo1.png" />
      <video id="video" width="800" height="600" preload autoplay loop muted controls></video>
      <canvas id="canvas" width="1000" height="600"></canvas>
    </div>
  </div>

先ほどの静止画のパターンと同様、.demo-frameに素材を入れておきます。
今回は画像と、Webカメラの映像を出力するためのvideoタグを設置します。
そして画像とvideoタグの映像は、両方ともcanvasに描き込みます。
そのため、canvasの幅はvideoタグとimgタグの幅の合計値にしておきましょう。

次にjs部分をみていきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
    (function() {
      // logoTracker ======================================================
      var logoTracker = function() {
        logoTracker.base(this, 'constructor');
      };
      tracking.inherits(logoTracker, tracking.Tracker);
 
      logoTracker.prototype.templateDescriptors_ = null;
      logoTracker.prototype.templateKeypoints_ = null;
      logoTracker.prototype.fastThreshold = 60;
      logoTracker.prototype.blur = 3;
 
      logoTracker.prototype.setTemplate = function(pixels, width, height) {
        var blur = tracking.Image.blur(pixels, width, height, 3);
        var grayscale = tracking.Image.grayscale(pixels, width, height);
        this.templateKeypoints_ = tracking.Fast.findCorners(grayscale, width, height);
        this.templateDescriptors_ = tracking.Brief.getDescriptors(grayscale, width, this.templateKeypoints_);
      };
 
      logoTracker.prototype.track = function(pixels, width, height) {
        var blur = tracking.Image.blur(pixels, width, height, this.blur);
        var grayscale = tracking.Image.grayscale(pixels, width, height);
        var keypoints = tracking.Fast.findCorners(grayscale, width, height, this.fastThreshold);
        var descriptors = tracking.Brief.getDescriptors(grayscale, width, keypoints);
        this.emit('track', {
          data: tracking.Brief.reciprocalMatch(this.templateKeypoints_, this.templateDescriptors_, keypoints, descriptors)
        });
      };
 
      // Track ===================================================================
      var boxLeft = 800;
      var video = document.getElementById('video');
      var canvas = document.getElementById('canvas');
      var image1 = document.getElementById('image1');
      var canvasRect = canvas.getBoundingClientRect();
      var context = canvas.getContext('2d');
      var templateImageData;
      var capturing = false;
      var videoHeight = 600;
      var videoWidth = 800;
 
      var tracker = new logoTracker();
 
      tracker.on('track', function(event) {
        stats.end();
 
        if (capturing) {
          return;
        }
        // Sorts best matches by confidence.
        event.data.sort(function(a, b) {
          return b.confidence - a.confidence;
        });
        // Re-draws template on canvas.
        context.putImageData(templateImageData, boxLeft, 0);
 
        // Plots lines connecting matches.
        for (var i = 0; i < Math.min(10, event.data.length); i++) {
          var template = event.data[i].keypoint1;
          var frame = event.data[i].keypoint2;
          if(event.data[i].confidence > 0.9) {
            context.beginPath();
            context.strokeStyle = 'magenta';
            context.moveTo(frame[0], frame[1]);
            context.lineTo(boxLeft + template[0], template[1]);
            context.stroke();
          }
        }
      });
 
      var trackerTask = tracking.track(video, tracker, { camera: true });
      // Waits for the user to accept the camera.
      trackerTask.stop();
 
      // Sync video ============================================================
      function requestFrame() {
        window.requestAnimationFrame(function() {
          context.clearRect(0, 0, canvas.width, canvas.height);
          if (video.readyState === video.HAVE_ENOUGH_DATA) {
            try {
              context.drawImage(video, 0, 0, videoWidth, videoHeight);
            } catch (err) {}
          }
          requestFrame();
        });
      }
      requestFrame();
 
      context.drawImage(image1, 800, 0, 200, 200);
      templateImageData = context.getImageData(800, 0, 200, 200);
      tracker.setTemplate(templateImageData.data, 200, 200);
      trackerTask.run();
 
      // GUI Controllers
      var gui = new dat.GUI();
      gui.add(tracker, 'fastThreshold', 20, 100).step(5);
      gui.add(tracker, 'blur', 1.1, 5.0).step(0.1);
    }());

まずは静止画像と映像の特徴点の抽出のための関数を作っておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
      logoTracker.prototype.setTemplate = function(pixels, width, height) {
        var blur = tracking.Image.blur(pixels, width, height, 3);
        var grayscale = tracking.Image.grayscale(pixels, width, height);
        this.templateKeypoints_ = tracking.Fast.findCorners(grayscale, width, height);
        this.templateDescriptors_ = tracking.Brief.getDescriptors(grayscale, width, this.templateKeypoints_);
      };
 
      logoTracker.prototype.track = function(pixels, width, height) {
        var blur = tracking.Image.blur(pixels, width, height, this.blur);
        var grayscale = tracking.Image.grayscale(pixels, width, height);
        var keypoints = tracking.Fast.findCorners(grayscale, width, height, this.fastThreshold);
        var descriptors = tracking.Brief.getDescriptors(grayscale, width, keypoints);
        this.emit('track', {
          data: tracking.Brief.reciprocalMatch(this.templateKeypoints_, this.templateDescriptors_, keypoints, descriptors)
        });
      };

logoTracker.prototype.setTemplateに静止画像の特徴点を、logoTracker.prototype.trackに映像の特徴点を戻り値として代入されるようにしておきました。
抽出前の画像処理として、白黒加工とぼかし処理をしてあります。
ちなみになぜぼかし処理をかけているかというと、Webカメラの映像にはノイズが多いため、そのノイズが特徴点として検索されないようぼかしているようです。

また、logoTracker.prototype.trackには、”track”という類似点が検出された際のイベントを受け取れるようにしておきます。

trackイベントを受け取った際の処理はこちらです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
      tracker.on('track', function(event) {
        stats.end();
 
        if (capturing) {
          return;
        }
        // Sorts best matches by confidence.
        event.data.sort(function(a, b) {
          return b.confidence - a.confidence;
        });
        // Re-draws template on canvas.
        context.putImageData(templateImageData, boxLeft, 0);
 
        // Plots lines connecting matches.
        for (var i = 0; i < Math.min(10, event.data.length); i++) {
          var template = event.data[i].keypoint1;
          var frame = event.data[i].keypoint2;
          if(event.data[i].confidence > 0.9) {
            context.beginPath();
            context.strokeStyle = 'magenta';
            context.moveTo(frame[0], frame[1]);
            context.lineTo(boxLeft + template[0], template[1]);
            context.stroke();
          }
        }
      });

// Plots lines connecting matches.というコメント以下が、検出された類似点を元に、特徴点と特徴点の間に線を引くという処理をしています。
ここでポイントなのが、不要な類似点をいかに消すかというところです。
普通に類似点を検出すると、静止画像と全く違う形のものがWebカメラに映っても類似点として検出されることがあります。

不要な類似点を消すためには、受け取ったtrackイベントが持っている”confidence”という値を利用します。このconfidenceは、マッチした部分の信頼度を数値(0.5〜1)として持っています。
信頼度が0.9以上の場合のみ線を描画するという条件分岐を記述してあげれば、信頼度の低いマッチに関してはシャットアウトできるというわけです。

注意が必要なのは、Webカメラを起動している環境によって、同じ物どうしを照らし合わせても信頼度が変わってくるという点です。

明るい部屋では検出された類似点が、暗い部屋では検出されない、といったことがおきます。
明るさに応じてconfidenceの許容値を操作できるような配慮をしてあげると良いでしょう。

まとめ

いかがだったでしょうか。
今回は任意の画像を認識させて3Dオブジェクトを表示させるための、「認識」の部分にフォーカスを当てて解説しました。画像認識の際に必要な難しい数学の知識などを、tracking.jsは関数として簡単に利用できるようにしてくれています。

類似点が検出できれば、特異点の検出にも応用が効き、WebAR以外にも様々な用途が考えられます。
tracking.jsには他にも複数のサンプルが用意されているので、興味がある方はぜひ遊んでみてください。

「5G」が話題になっていることや、「大阪万博」も近くにつれて、さらにWebARは盛り上がりを見せてくるかと思います。
弊社でも実際にお問い合わせをいただく件数が増えてきたのを実感しています。

WebARのご相談はいつでもお待ちしておりますので、下記URLからお問い合わせください!
https://alaki.co.jp/contact/

 

  • ツイート
  • このエントリーをはてなブックマークに追加
ALAKI ロゴ
さよなら過去のAR。
WebARがイベント集客に
革命を起こします。
特設サイトはこちら >

You may also like...

  • スクリーンショット 2021-07-10 9.04.44

    AR(WebAR)の導入事例をご紹介!〜EC × AR〜

    10 7月, 2021

  • スクリーンショット 2018-03-16 0.37.47

    WebARはじめてみませんか?〜第2回 A-frameで3Dオブジェクトを自在に操作する〜

    2 2月, 2019

  • スクリーンショット 2018-11-29 23.56.53

    blender2.8を使って3Dオブジェクトにアニメーションをつける方法〜part.1〜

    30 11月, 2018

  • Next story WebARはじめてみませんか?〜第5回 8th Wall Webの画像認識機能の使い方〜
  • Previous story LaravelのクエリビルダでFROM句にサブクエリを利用する方法

弊社サービス

MONJI+ MUGHEN ALAKI クラウド型受発注システム さよなら過去のAR。WebARがイベント集客に革命を起こします。

運営会社

ALAKI株式会社 ALAKI株式会社採用サイト

人気の記事

  • alaki1462_A_businessman_who_works_logically_1da80a2e-c297-4911-bdd6-feba0ebf1357

    仕事術

    【書籍要約】数学的な仕事術大全

    4 11月, 2024

  • fv-main-img-min

    空間コンピューティング

    世界初!「空間コンピューティング」を専用端末不要、スマホのWebブラウザのみで実現する『Web XR Site』を公開しました

    22 6月, 2023

  • Virtual reality players isometric vector illustration. VR UI and navigation. Futuristic digital technology. Virtual screen. Mixed reality 3d concept. Person in VR headset. Web banner idea

    AR

    Appleから新世代MRグラス「Vision Pro」の登場 – デジタルと現実世界が融合する新時代へ

    6 6月, 2023

  • AWE

    未分類

    ARアプリ『MUGHEN』が世界最大のAR/VRアワード「AWE」のファイナリストに選出されました!

    26 5月, 2023

  • python-logo-master-v3-TM

    pyhotn / 業務効率化

    「SwinIR」で高画質化した画像を pythonを使用して一括で元のサイズに戻す方法

    2 2月, 2023

カテゴリー

  • AI・ディープラーニング
  • AR
  • BtoB
  • CMS
  • Git
  • Google AdWords
  • gulp
  • Javascript
  • Laravel
  • Photoshop
  • Pug
  • pyhotn
  • Sass
  • SEO
  • SNS
  • Twitter広告
  • VR
  • Web3.0
  • WEBサイト
  • WEBマーケティング
  • YouTube
  • コーディング
  • システム開発
  • スマートフォンサイト
  • セキュリティ
  • セミナー
  • その他
  • ツール
  • デザイン
  • ベトナムレポート
  • 仕事術
  • 未分類
  • 業務効率化
  • 空間コンピューティング
  • 色

アーカイブ

  • 2024年11月
  • 2023年6月
  • 2023年5月
  • 2023年2月
  • 2023年1月
  • 2022年12月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年2月
  • 2021年12月
  • 2021年9月
  • 2021年7月
  • 2021年6月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年6月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月
  • 2018年1月
  • 2017年12月
  • 2017年11月
  • 2017年10月
  • 2017年9月
  • 2017年8月
  • 2017年7月
  • 2017年6月
  • 2017年4月
  • 2017年3月
  • 2017年2月
  • 2017年1月
  • 2016年12月
  • 2016年11月
  • 2016年10月
  • 2016年9月
  • 2016年8月
  • 2016年7月
  • 2016年6月
  • 2016年5月
  • 2016年4月
  • 2016年3月
  • 2016年2月
  • 2016年1月
  • 2015年12月
  • 2015年11月
  • 2015年10月
  • 2015年9月
  • 2015年7月
  • 2015年3月
  • 2015年2月
  • 2015年1月
  • 2014年12月
  • 2014年11月
  • 2014年10月
  • 2014年9月
  • 2014年8月
  • 2014年7月
  • 2014年6月
  • 2013年10月
  • 2013年9月

Facebook

お問い合わせ

お問い合わせ

人気の記事

  • 【書籍要約】数学的な仕事術大全
  • 世界初!「空間コンピューティング」を専用端末不要、スマホのWebブラウザのみで実現する『Web XR Site』を公開しました
  • Appleから新世代MRグラス「Vision Pro」の登場 – デジタルと現実世界が融合する新時代へ
  • ARアプリ『MUGHEN』が世界最大のAR/VRアワード「AWE」のファイナリストに選出されました!
  • 「SwinIR」で高画質化した画像を pythonを使用して一括で元のサイズに戻す方法

カテゴリー

  • AI・ディープラーニング
  • AR
  • BtoB
  • CMS
  • Git
  • Google AdWords
  • gulp
  • Javascript
  • Laravel
  • Photoshop
  • Pug
  • pyhotn
  • Sass
  • SEO
  • SNS
  • Twitter広告
  • VR
  • Web3.0
  • WEBサイト
  • WEBマーケティング
  • YouTube
  • コーディング
  • システム開発
  • スマートフォンサイト
  • セキュリティ
  • セミナー
  • その他
  • ツール
  • デザイン
  • ベトナムレポート
  • 仕事術
  • 未分類
  • 業務効率化
  • 空間コンピューティング
  • 色

検索

ALAKI株式会社 © 2025. All Rights Reserved.