こんにちは、皆さん!

今回は僕が2020年の3月末に参加したロサンゼルスのハッカソン、LAHacksでの話を紹介していきたいと思います。この記事は2日目の内容になりますので、初日の内容が見たい方はこちらからお願いします↓


トピック


プロジェクトの進め方を考える

昨日の段階で「顔の表情を認識して、それに応じた絵文字を表示するビデオチャット」を作ることが決定していたので、今日はそれを完成までに持っていく時に必要になる工程を考え、それをみんなで効率がいいように分配するところから入りました。

とりあえず、一番重要となる顔認識の部分を模索し、それで簡単にWebcamを使って、パソコンの前の人物の表情により絵文字を表示できるような簡単なアプリを作ってみるところから入ろうという話になりました。

僕が実際に簡単なアプリを作る役割を担い、他のメンバーはAPIからのリスポンスでどのようなデータが得られ、どのような操作を行うことができるのかを確認することにしました。

使うフレームワークは結構議論になりましたが、開発の比重がそんなに重くないと踏んで、チーム内で僕含めて3人くらいしか使えないReactにしました。他のメンバーはその他の作業にその間フォーカスできるので、これはこれでいいのかなって思いました。


GoogleのVideo Intelligence APIを試す

まず、顔を認識してそこから表情の情報を引き出す必要がありますので、Google CloudのVideo Intelligence APIがうまく適応できるかどうか確認してみました。


しかし、ここでわかったのはこれをうまく使うためにはビデオファイルをAPIに対してアップロードする必要がありました。

でも、ビデオチャットのように使いたいので、リアルタイムでレスポンスを受け取りたいので、ファイルをアップロードするわけには行きませんでした。そこで、目を付けたのが、Live Stream の方のAPIでした。これならビデオをAPIに直接流し込めるのではないかと思いました。


だが、ここでも問題発生しました。ドキュメントを読んでも全くどうすればいいのかわからなかったのです。一つだけわかったのが、GStreamerというストリーミング用の技術を使ってGoogleのAPIと自分のアプリケーションの間にパイプラインを構築しないといけないみたいということでした。ちょっとそれでは難しすぎるなと踏んだ僕は、SlackでスポンサーできていたGoogleのエンジニアにもっと簡単な方法はないかと質問をしてみました。そしたら、下記のような回答が返ってきました。

Googleエンジニアからの返答

これを見たときは衝撃でしたね(笑)。

Googleエンジニア:「あまり詳しくないけど、これ見てみるといいよ。」

僕(心の中):「いや、それ見てわからなかったから質問しているのだが。。。?」


とまあ、このような返答をもらった時点であまりこのAPIでうまくいくような気がしないなってのは気づきましたので、こちらではなく、別の方法を模索することがチームでも決定しました。


Vision APIの導入

動画がダメなら写真で行こうということで、次に目をつけたのは写真からデータを抽出してくれるGoogleのVision APIでした。


これを思いついた時の懸念が写真から情報を引き出すのに一体どれだけの時間がかかるのかという問題でした。動画ですので、毎秒ごとにでも顔の表情をチェックして、ユーザーにその結果を返したいのですが、APIコールとかしていたら相当時間がかかってしまうのではないか心配していました。

試しにReactでWebcamを立てて、ユーザーの写真を撮って、それをAPIコールに使ってみたのですが、本当にすぐにレスポンスが返ってきていろいろと間に合ってしまったのです。

Vision APIには様々な種類の画像分析方法があります。今回は人間の顔の表情を認識して欲しいので、顔認識の機能を使用しました。その他の認識機能も含む下記のようなリクエストボディーを作りました(画像はちょっと処理をしてこうなっていますので、気にしないでください)。

  let body = JSON.stringify({
    requests: [
      {
        features: [
          // { type: "LABEL_DETECTION", maxResults: 10 },
          // { type: "LANDMARK_DETECTION", maxResults: 5 },
          { type: 'FACE_DETECTION', maxResults: 5 },
          // { type: "LOGO_DETECTION", maxResults: 5 },
          // { type: "TEXT_DETECTION", maxResults: 5 },
          // { type: "DOCUMENT_TEXT_DETECTION", maxResults: 5 },
          // { type: "SAFE_SEARCH_DETECTION", maxResults: 5 },
          // { type: "IMAGE_PROPERTIES", maxResults: 5 },
          // { type: "CROP_HINTS", maxResults: 5 },
          // { type: "WEB_DETECTION", maxResults: 5 }
        ],
        image: {
          content: imageSrc ? imageSrc.substring(imageSrc.indexOf(',') + 1) : imageSrc,
        },
      },
    ],
  });

そして、このようなリクエストを送ると、すごく長いレスポンスが返ってきます。今回は顔の表情だけにフォーカスしたいので、その一部分であるレスポンスの下記の部分だけに注目します。

{
   "joyLikelihood": "VERY_UNLIKELY",
   "sorrowLikelihood": "VERY_UNLIKELY",
   "angerLikelihood": "VERY_UNLIKELY",
   "surpriseLikelihood": "VERY_UNLIKELY",
   "underExposedLikelihood": "LIKELY",
   "blurredLikelihood": "VERY_LIKELY",
   "headwearLikelihood": "VERY_UNLIKELY"
}

そうなんです。Vision APIはその表情が上記に挙げたそれぞれのものである可能性がどれだけ高いのか、4段階の基準で教えてくれるのです。(VERY_LIKELY > LIKELY > UNLIKELY > VERY_UNLIKELY)

この時のリスポンスがLIKELY以上であれば、その人がその時にその状態にある可能性が高くなります。僕たちはとりあえず、驚きの表情でこの機能をReactのWebcamアプリ内に入れてみることにしました。オンライン上から驚きの絵文字の画像を引っ張ってきて、コンポーネント化しておきました。

これが綺麗にうまくいき、口を大きく開けて、目を見開くと、見事に驚きの表情の絵文字が出てきました。


ビデオチャットの導入

次に僕が取りかかったのは今自分のサイト内のWebcamで立てていたものをビデオチャットに導入する部分です。

スポンサーのTwilioがビデオチャットのAPIを用意してくれているのはわかっていたので、それを使おうと考えていました。


しかし、見る限り結構たくさんコードを打つ必要があり、時間制限内に調整も含めて終えれるかどうかわからなかったので、他の方法を模索していたのですが、TwilioがReact用に出来上がったビデオチャットのアプリをオープンソースとして、Githubに挙げていることに気づきました。


これを使った時点で一瞬でビデオチャットが出来上がりました。

次にそこに先ほどのWebcamでやっていた部分を入れる作業に入りました。ビデオチャットでは既にWebcamが使われていましたが、そこのコンポーネントがこれまで使っていたWebcamのコンポーネントと異なっていたので、自分たちのWebcamを新たなコンポーネントとして付け加えることにしました。

しかし、Webcamが2つあるのはどうもおかしいので、顔認識用のビデオはCSSで opacity: 0; にして見えない状態にしておきました。

という感じで2日目の夜7時ごろには顔の表情に反応できるビデオチャットが完成していました。ここで残り時間16時間です。


問題発生

ここで一つ問題が発覚しました。顔の認識はうまくいってましたが、当初から考えていた、親指をあげてライクをしたら、ライクの絵文字が飛び出すという機能がVision APIの認識機能だけでは作れないということがわかりました。

ウェブサイト自体は2、3人で十分で、人手に余裕はあったので、ここに数人分を投入することにしました。

ここまでGoogle Cloudのサービスを結構使っていたので、なるべくその範囲内で探そうと考えたところ、AutoMLという機械学習サービスを発見しました。


GoogleのAutoMLの採用

AutoMLとはGoogle Cloudの画像を使った機械学習サービスです。時間が限られている中で、自分たちで1から機械学習のモデルを作るのは難しかったので、こちらを活用しました。


僕自身はウェブサイトの方を編集していたので、こちらは友達に任せました。友達がリサーチした結果では、自分たちで写真を用意して、ラベルをつけてあげることで学習とテストをすることができるそうです。

最後にそのようにトレーニングさせて完成したモデルをGoogle Cloudで直接デプロイでき、RESTのエンドポイントが作られるので、そこにあとはAPIリクエストを送ればいいみたいです。

そこで友達からみんなに適当に親指を立てた写真とそれ以外の写真を送って欲しいと言われ、みんなでバカバカしく、あれやこれやと自撮りを取りました(これも大学生活の良い思い出笑)。

トレーニングには最低でも8時間くらいかかるらしく、夜7時くらいに稼働させたので、夜中の3時くらいにやっと完成という感じでした。

後から気付いたのですが、この機械学習サービスめっちゃ高いんですよ。1ノードしか使わなかったが、1時間で3.15ドル(350円ほど)。おかげさまで、ハッカソンでもらったクレジット50ドルの半分をこれで使い切りました(笑)。

AutoMLのトレーニングにかかる費用

時系列的には最後にこれのデプロイした結果をテストしたのですが、結果的に人間と手の両方を写してしまったので、機械的には人間がいて、手をあげれば良いという認識をされてしまいました(手の形が親指を上げた状態かどうかではなく)。

これに気付いた時にはもう時間がなかったので、最終的にライク機能ではなく、ウェーブ機能(手を上げた)に変えました(笑)。次回はもっと学習データを注意深く選定しようと思います。


リアルタイムデータベースの採用

ここまででほとんどウェブサイトとしては完了していたのですが、最後に自分の方で表示された絵文字がみんなにも見えるように、情報を他の人たちのウェブサイトにも伝えなければなりませんでした。

これは下記のような手順で発生します。

表情変化が他のユーザーまで行き渡る道筋

このように最初の変化をサーバーに教える部分はクライアントのウェブサイトからできるのですが、それを同じチャットルーム内にいる他のユーザーに伝えるにはサーバー側から通知をしてあげなくてはなりません。

こういうのは通常のAPIリクエストとは逆向きのリクエストなので、ソケットを開かないといけません。しかし、時間がないので、ここの部分を短縮するために急遽、Firebaseを採用しました。

FirebaseはGoogleが提供しているリアルタイムデータベースと言われるものです。そのライブラリを使用することで、ソケットが自動的に作られて、データベース側に新しい更新が入ってくるたびにその通知をソケットを開いた先のクライアントに教えてくれます。

これのおかげで30分ほどでサーバー側からの通知を完成させることができました。自分が一番つまづいたのはFirebaseのドキュメントを読んだ時でした。最初はそのままFirebaseを使おうとしていたのですが、なかなかうまくいきませんでした。その後、Firebaseのコンポーネントのうちの一つである、Firestoreを使ったら、あっという間に出来上がりました。


2日目終了

夜中の4時に差し掛かったへんで、とりあえず、2日目を終了することにしました。AutoMLで得たものをウェブサイトに入れることもできましたし、リアルタイムデータベースもつなげることができました。

夜が開けたあとの11時までに必要なものを全てを提出すれば良いので、ここでひとまず朝8時まで休みを取ることにしました。他のメンバーのうち、2人は当に寝ていたので、残りの僕たちも休むことにしました。

残りとしては先に寝た友達たちとウェブサイトの機能確認、デモ動画を取って、Devpostというサイトに自分たちが作ったアプリケーションの説明を書き上げることくらいでした。


まとめ

2日目はアプリケーションを1から作り、90%完成まで持っていかなければならなかったので、かなり疲れるような1日でした。

まだ使ったこともないようなサービスやAPIのドキュメントを読んで、それを自分のアプリケーションに合うように使いこなすのは結構エネルギーが必要です。Vision API、TwilioのAPI、AutoML、Firestoreもそれぞれこれまで使ったことなかっただけに時間はかかりましたが、とても大きな学習が得られた1日でした。

友達との一日中の会話はとても楽しかったし、大学生活で参加してよかったなと思った1日でした。

最終日の記事についてはこちらからお願いします↓


皆さんの役に立つことを願います!
では、また次回まで✌
記事更新はツイッターで告知するので、ツイッターの方でもフォローお願いします!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です