LoginSignup
2

More than 1 year has passed since last update.

posted at

updated at

Organization

うさみちゃん会議中の顔怖っ! (face-api.jsで表情検知)

はじめに

Ateam Lifestyle Inc. Advent Calendar 2020 4日目は
株式会社エイチームライフスタイルの @engabesi が担当します。

弊社は情勢に合わせ在宅と出社を任意で選択できる勤務体制になっています。(2020/12/03時点)
その際、Web会議中の参加者が皆真顔で怖い時があるという声を聞きました。

画面越しとはいえ、真顔で囲まれると緊張しますよね。

というわけで今回は自分がその囲む側にならないように、
真顔で数秒間いると戒めとして写真と時刻、回数を表示するものを作りました。

成果物

github: https://github.com/engabesi/serious-face-detector
githubPages: https://engabesi.github.io/serious-face-detector/
(試すにはカメラの許可とハードウェアアクセラレーションをオンにする必要があります)

環境 & 使用したもの

やること

  • 土台(カメラと顔矩形取得)は前回のニコ○スを流用
  • 表情取得
  • 撮影処理
  • 実際に使用

土台作成

前回のリポジトリをimportし、不要になるニコ○ス成分を削除
(prettierの設定違いにより他の変更も含まれてます)
https://github.com/engabesi/serious-face-detector/commit/66db596368dd6d1b21c220642dd84d4fb28a70a9

表情

表情の値を取得します。
ロードするモデルを追加と呼び出すメソッドを1つ追加するだけです。

docs\index.js
// ===中略===
const loadModels = async () => {
  await Promise.all([
    faceapi.nets.tinyFaceDetector.loadFromUri(`${gitPagesPath}/js/lib/models`),
+   faceapi.nets.faceExpressionNet.loadFromUri(`${gitPagesPath}/js/lib/models`),
  ]);
};

(async () => {
// ===中略===
    setInterval(async () => {
      const results = await faceapi
        .detectAllFaces(
          video,
          new faceapi.TinyFaceDetectorOptions(tinyFaceDetectorOption)
        )
+       .withFaceExpressions();
// ===中略===

先程の記述を追加すると、resultの中に表情とその割合がobjectで含まれます。
これをasSortedArray()で割合順にソートし、一番%が高い表情によってポイントを増減させます。
このポイントが100になったら撮影等の処理を走らせます。
https://github.com/engabesi/serious-face-detector/commit/e27e58933f90ac03d0688bad4fcae78e9ec48bad

docs\index.js
// ===中略===
+ const calcExpressionPoint = (expression) => {
+   switch (expression) {
+     case "neutral":
+       return 1;
+     case "angry":
+     case "disgusted":
+       return 2;
+     case "happy":
+       return -5;
+     default:
+       return 0;
+   }
+ };

  (async () => {
// ===中略===
+     // 真顔ポイント
+     let seriousPoint = 0;

      setInterval(async () => {
// ===中略===
        const resizedResults = faceapi.resizeResults(results, displaySize);
        resizedResults.forEach((result) => {
+         const expression = result.expressions.asSortedArray()[0].expression;
+         seriousPoint += calcExpressionPoint(expression);
+         if (seriousPoint >= 100) {
+           // 画像出力
+           seriousPoint = 0;
+         }
        });

撮影処理

ポイントが100になった時の撮影処理を実装します。
canvasを隣に設置、100になった際に描画します。
https://github.com/engabesi/serious-face-detector/commit/30ea477895a29edbbef25ff064f62d3143f52899

htmlにcanvasと撮影時間表示用のlabelを設置。
次にjsに撮影処理を書きます。

index.js
+     let seriousCount = 0;

+     const pictureCanvas = document.querySelector("canvas");
+     const timestampLabel = document.querySelector("#timestamp");
      setInterval(async () => {
// ===中略===
          if (seriousPoint >= 100) {
            // 画像出力
+           pictureCanvas
+             .getContext("2d")
+             .drawImage(video, 0, 0, pictureCanvas.width, pictureCanvas.height);
            seriousPoint = 0;
+           seriousCount++;
+           timestampLabel.textContent = `${seriousCount}回目 ${new Date()}`;
          }

効果音追加

これだけじゃスッと自分の真顔が映し出されるだけで味気無いので効果音も足します。
https://github.com/engabesi/serious-face-detector/commit/006fc9d7fef26b0490287b112d33774c07aca873

  • index.htmlにaudiosource追加
  • audio/warning.mp3追加
  • jsの撮影処理時にplay()で音を鳴らす

これで完成です

実際に使ってみた

15分の会議中に自分の顔を映して何回アラートが鳴るか試してみました。
sono1.png

結構意識してるつもりなのですがそれでもこの回数。
大体10秒真顔で1回なので、単純計算で630秒 = 10分30秒ぐらい真顔の時間が。
実際には笑顔でポイントを稼いでいるところがあるので更に長くなります。
頷いたりして負担を和らげるように心がけてはいますが…。気をつけます。

自分が思ってる以上に怖い顔をしている時間が長いということを改めて思い知らされました。
皆さんもWeb会議問わず気をつけましょう。

明日は @dayamaguchi1 さんです。
ECS on EC2の記事を書いてくださるそうなのでお楽しみに!

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
What you can do with signing up
2