Panda Noir

JavaScript の限界を究めるブログでした。最近は幅広めに書いてます。

アキラ100%が安心しておぼんを外せるサービスをつくった

夏休みが終わってしまい悲しみを隠しきれないクロパンダです。

f:id:panda_noir:20180930221255p:plain

さて、アキラ100%さんを皆さんはご存知ですか?おぼんで恥部を隠して、見えるか見えないか絶妙な動かし方をして視聴者をハラハラさせる裸芸を披露する芸人さんです。

そんな彼はおぼんを落としたら恥部をさらしてしまうリスクをいつも抱えて芸をしています。こんな生活では心休まるときは訪れません。

そこで今回ユーザーが見ていない時、アキラ100%の画像からおぼんが外れるwebサービスを作ろうと決意しました(?)。

…まあ肖像権の問題から、実際に作ったのは「ユーザーが見ていない時に女の子が迫ってきて、ユーザーが見ている時は止まる」、つまりだるまさんが転んだになりました。

技術紹介

今回使うのは以下の技術です。

  • インカメから画像を取得
  • ChromeのFace Detection(顔認識)API

全体の流れ

  1. インカメから画像を取得
  2. 顔を認識する
  3. 顔が検知された場合『ユーザーが見ている』と判断

ちなみに、今回は使いませんが顔認識APIでは「顔のサイズ・位置」「目や口の位置」といった詳細な情報を取得できます。

インカメから画像を得る

今回はめんどうだったのでjQueryを使ってサービスを作っていきます。

(async () => {
    const $video = $('#video')[0]; // video#videoという要素

    const stream = await navigator.mediaDevices
        .getUserMedia({ video: true })
        .catch(console.error); // インカメを起動
    $video.srcObject = stream; // インカメの画像をvideo要素にアタッチする
    await $video.play().catch(console.error);
    const screenshot = await new ImageCapture($video.srcObject.getVideoTracks()[0]).grabFrame(); // インカメのスクショを取得
})();

これでインカメから画像を取得できます。

顔を認識する

const faceDetector = new FaceDetector();

const detect = async img => await faceDetector.detect(img).catch(console.error); // 顔を認識して、結果を返す

// こんな感じで使います
const screenshot = await getScreenShot($video); // インカメのスクショを取得
const faces = await detect(screenshot); // スクショ内の顔を認識

取得部分自体はなんとたったの1行です!すごい時代ですよね…

FaceDetetorインスタンスのdetect()メソッドに渡せるのは以下のような要素です。

  • ImageBitmap
  • HTMLImageElement
  • HTMLCavasElement
  • HTMLVideoElement

実際のサンプル

これが実際のサンプルです。Akira 100%

動かすには以下の条件があります。

  • Chrome最新版
  • chrome://flags/#enable-experimental-web-platform-features を有効化する
  • OSが顔認証をサポートしている

検証した範囲で、Chrome69.0.3497.100、Windows10にて動作確認しました。Ubuntuでは動きませんでした。

問題点

さて、このサービスですが、いくつか問題点があります。

まず、「若干のタイムラグが存在する」点です。これは「顔が認識されなくなってから200ms経ったら外す」という方法でなんとか回避しています。

次に片目を隠すだけでも顔認識されなくなることです。つまり「片目はバッチリみているのに、ユーザーは見ていない」という判定になってしまいます。ガバガバすぎますね。もちろんアキラ100%さんはわいせつ物陳列罪になってしまいます。

というわけで僕の技術力ではアキラ100%さんが安心しておぼんを外せる世界は実現できませんでした…