82
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

JavaScript 2Advent Calendar 2019

Day 7

TensorFlow.js と obniz で作る、笑顔あふれる職場

Posted at
1 / 16

笑顔の効果

心理学者ジェレミー・ディーンは、笑顔は洞察力を高め、信頼を勝ちとると言及し、
また、マザーテレサは、単なる笑顔であっても、私たちには想像できないほどの可能性がある、という言葉を残した。

職場をよりよくすることをミッションとしている私は、今回このテーマに向き合うことにした。


笑顔になれる環境づくりの必要はなく、強制的に笑顔にさせればよい

オット・フォン・ゲーリケ・マグデブルグ大学のミュンテ博士は、笑顔を作るから楽しいという逆因果を見いだしている。
なので、近年はティール組織が話題であるが、真逆の組織レベルである、レッド組織的アプローチ、つまり圧倒的な力で支配する形で笑顔を強制していこうと思う。


笑顔でなければ痛みをともなうソリューションの設計

当初笑わなければ電撃を食らわす方法を検討していたが、識者に確認したところ死ぬ可能性があり、レッド組織どころではなくなるので見送り。
最終的に以下のアーキテクチャでおさめた。

smilelint-arch.png

次は実装をみていく。


ハリセンデバイスを作る


使うもの


obniz を動かしてみる

公式のクイックスタートを参考に動作確認。RPiと比べたら一瞬で終わって驚き。

次に、部品を使う:モーターを動かしてみようを見ながら、シングルギヤボックスをつなげてモーターを回してみる。

obniz.gif

もうできた。


そして obniz はこのブロックプログラム形式だけでなく、ブラウザや Node.js の JavaScript で実装する形式があり、上記はこんなコードになる

(async function(){
  var obniz, button, dcmotor;


  obniz = new Obniz('OBNIZ_ID_HERE');
  await obniz.connectWait();
  button = new ObnizUI.Button('text');
  dcmotor = obniz.wired("DCMotor",{"forward":0, "back":1});
  while (true) {
  await ObnizUI.Util.wait(0);
    if (button.isTouching()) {
      dcmotor.move(true);
    } else {
      dcmotor.stop();
    }
  }

})();

笑顔認識クライアントを作る


使うもの

  • Chrome

実装する

TensorFlow.js搭載chrome拡張で誰でも笑顔体験にやりたいことが書いてあったので参考にさせていただき、そのままだとできなかった部分を変更。

  • tracking.jsが動かなかったので、Face Detection APIを使用
    • Face Detection API を使えるようにするために Experimental Web Platform features を Enabled にする
    • なぜか new FaceDetector({ fastMode: true }) としないと顔検出しなかった
  • TensorFlow.js v1.0 の Breaking changes で動かなかくなったところを変更
      (async function() {
        const width =
          innerHeight / innerWidth > 0.75
            ? innerWidth
            : Math.floor((innerHeight / 3) * 4);
        const height =
          innerHeight / innerWidth > 0.75
            ? Math.floor((innerWidth / 4) * 3)
            : innerHeight;

        const faceCanvas = document.createElement("canvas");
        const faceContext = faceCanvas.getContext("2d");
        const frameCanvas = document.getElementById("frame");
        frameCanvas.setAttribute("width", width);
        frameCanvas.setAttribute("height", height);
        const frameContext = frameCanvas.getContext("2d");

        const camera = document.getElementById("camera");
        camera.setAttribute("width", width);
        camera.setAttribute("height", height);
        camera.srcObject = await navigator.mediaDevices.getUserMedia({
          video: { width, height },
          audio: false
        });

        const faceDetector = new FaceDetector({ fastMode: true });

        const model = await tf.loadLayersModel("output/model.json");

        setInterval(async () => {
          const face = (await faceDetector.detect(camera))[0];

          if (!face) {
            return;
          }

          faceContext.drawImage(
            camera,
            face.boundingBox.x,
            face.boundingBox.y,
            face.boundingBox.width,
            face.boundingBox.height,
            0,
            0,
            face.boundingBox.width,
            face.boundingBox.height
          );

          const tensor = tf.browser
            .fromPixels(faceCanvas, 1)
            .resizeNearestNeighbor([64, 64])
            .toFloat()
            .div(tf.scalar(255))
            .expandDims();

          const score = (await model.predict(tensor).data())[3];

          document.getElementById("score").innerText =
            "Smile Score: " + Math.round(score * 100);

          frameContext.clearRect(0, 0, frameCanvas.width, frameCanvas.height);
          if (score > 0.5) {
            frameContext.strokeStyle = "green";
          } else {
            frameContext.strokeStyle = "red";
          }
          frameContext.lineWidth = 4;
          frameContext.strokeRect(
            face.boundingBox.x,
            face.boundingBox.y,
            face.boundingBox.width,
            face.boundingBox.height
          );
        }, 1000);
      })();

こんな感じ
tensor.gif


結合

笑顔でない時にモーターを動かすようにするだけ

          if (score > 0.5) {
            frameContext.strokeStyle = "green";
+           motor.stop();
          } else {
            frameContext.strokeStyle = "red";
+           motor.move(true);
          }

Demo


うーん、パワーが足りん・・

smilelint.gif

私には少々物足りなかったので、今後より強い刺激を探究していきたいと思う。


おしまい

82
34
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
82
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?