1 of 15

メルカリレンズβ

WebAssembly × AIのプロダクト開発

2022/02/15

PWA Night vol.36 ~WebAssembly〜

Tomohiro Kato @tkato

1

2 of 15

メルカリレンズβ

リアルタイムな機械学習や画像処理をWebAssemblyで実装

カメラでかざす�似ている商品の取引価格がわかる

2

3 of 15

Webでもネイティブに近いパフォーマンスを実現

iOS版をWebアプリに移植する形で進めた

Native app (iOS)

Web

3

4 of 15

Agenda

どのようにネイティブアプリをWebアプリに移植したのか?

  • なぜWebアプリとして開発するのか
  • ネイティブアプリをWebアプリに移植する上での苦労
  • WebAssemblyを利用して達成できたこと

4

5 of 15

Software Engineer, Mercari

  • FrontendやIoTでの機械学習応用に興味
  • Rustや音楽が好きです

GitHub: tkat0

Twitter: @_tkato_

Tomohiro Kato / @tkato

5

6 of 15

なぜWebアプリとして提供するのか

  • アプリのインストールを待たずに使える
  • リリースサイクルを早くできる
    • 審査不要
    • マルチプラットフォーム対応がしやすい

6

7 of 15

MediaPipeをWebで動かしたい

iOS版では、MediaPipeのObject Detection & Trackingを利用�emscriptenを使って、これをWasmとしてビルドしたい

MediaPipe

  • モバイル向けARアプリ開発フレームワーク(C++)
  • 機械学習や画像処理のパイプラインを作れる

7

8 of 15

MediaPipeはそのままではWebで動かせない...

  • DetectionとTrackingを並列動作させるためThreadを利用
  • 当時のモバイルブラウザでは、Threadが動かない

update target

Detection

(画像から物体を見つける)

Tracking

見つけた物体を追跡

time

8

9 of 15

どのようにネイティブアプリをWebアプリに移植したか

  • MediaPipe内部の並列処理を、Wasmの外に出した
  • 並列処理はJS側でWebWorkerで実装

TensorFlow.js

Tracking

Detection

Thread 2

Thread 1

Tracking

Detection

WebWorker 2

WebWorker 1

MediaPipe

MediaPipe

9

10 of 15

WebAssemblyを利用して達成できたこと

工事は必要だったがネイティブアプリをWebアプリに移植できた

  • MediaPipeをWebAssemblyとして動かすことができた
    • Thread周りは今後に期待
  • Webアプリのメリットを活かすことができた
    • アプリインストール不要という体験
    • リリースサイクルを早くできる

Detection &

Tracking on Web

10

11 of 15

まとめ

どのようにネイティブアプリをWebアプリに移植したのか?

  • なぜWebアプリとして開発するのか
  • ネイティブアプリをWebアプリに移植する上での苦労
  • WebAssemblyを利用して達成できたこと

11

12 of 15

12

13 of 15

既存のC++ライブラリをWebAssemblyにするには?

emscriptenを利用する

  1. JSからアクセスしたい関数やクラスをwrap
  2. build systemの設定 (cmake, bazel, …)
  3. ビルド/実行時のエラーを修正
  4. (opt) アルゴリズムの調整

13

14 of 15

C++

JS

#include <emscripten/bind.h>

struct Config {

string input;

string output; …

}

class Graph {

MediaPipeGraph(Config config) { ... }

void start() { ... } …

}

EMSCRIPTEN_BINDINGS(graph_binding) {

emscripten::class_<Graph>("Graph")

.constructor<Config>()

.function("start", &Graph::start) …

emscripten::value_object<Config>("Config")

.field("input", &Config::input)

.field("output", &Config::output) …

}

import Module from “./core”

const config = {

input: "...",

output: "..."

};

const graph = new Module.Graph(config});

graph.start();

Minimal example of emscripten

build

$ emcc --bind -o core.js core.cpp

14

15 of 15

Related Articles

15