今回、とみーさんと一緒にピクトグラムさんになれるアプリを開発したので、そのことについて書きます。
頑張って作ったのでぜひぜひ利用してみてください!一緒にオリンピックを盛り上げましょう
東京五輪のピクトグラムさんになれるアプリをAIを使って作りました🎉
— Toshiki Tomihira (@tommy19970714) August 2, 2021
↓のURLですぐに遊べます!https://t.co/U3rdsKRaRp
ぜひピクトグラムさんになって東京五輪を応援しましょう!頑張って作ったのでいいねやRTで広めてください😂
Everyone can be Pictogram-san!#Tokyo2020#pictogram_challenge pic.twitter.com/4fhb0StjaB
アプリはこちら→https://pictogram-san.com/
github→https://github.com/tommy19970714/pictogram-san
面白いなと思ったらLGTMしてもらえると励みになります
機能紹介
今回、実装した機能は以下です。
- webcamで取得した映像をピクトグラム化する
- 画面の縦幅を1/2にして、それぞれ上をピクトグラム、下を映像にして流す
- 再生ボタンを押すと音楽を流す
- 音楽が終了したタイミングでその画面のスクリーンショットを撮る
ピクトグラム化する部分でTensorFlow.jsのpose-detectionというモデルを使っています。
全体の部分はNext.js + TypeScriptで組んでいます。
技術的なことはすべて書ききれないので、ポイントとなる部分だけ書くことにします。
Next.js + TypeScriptでTensorFlow.jsを使って姿勢検出する
TensorFlowを使って画像にマスクをかけるアプリやバーチャル背景をつけるアプリを作ったときにも書いたのですが、
$ yarn add @tensorflow/tfjs
とするとTypeScriptでは型エラーが出てしまうので、以下のように必要なものだけ読み込む必要があります。
$ yarn add @tensorflow-models/pose-detection @tensorflow/tfjs-core @tensorflow/tfjs-converter @tensorflow/tfjs-backend-webgl
使うモデルがwasmベースの場合は、@tensorflow/tfjs-backend-webgl
の部分が@tensorflow/tfjs-backend-wasm
になります。
そして今回使うモデル(PoseNet)を読み込みます。
必要に応じて、アーキテクチャなどを指定することができます。
const modelName = SupportedModels.PoseNet
const net = await createDetector(modelName, {
quantBytes: 2,
architecture: 'MobileNetV1'
outputStride: 16,
inputResolution: resolution,
})
アーキテクチャはMobileNetV1とResNet50の2種類があるのですが、ResNet50にすると精度は高くなるものの、かなり重たてスマホでは特に重たくなるので、今回はMobileNetV1を使っています。
モデルが読み込めたら、estimatePosesの引数に画像か動画データを入れます。
今回だとwebcamのデータをそのまま入れています。
const predictions = await net.estimatePoses(webcam, {
maxPoses: 1,
flipHorizontal: false,
})
後は検出したpredictionsを利用して、好きなように描画すればOKです。
画面を起動すると同時にカメラを起動して姿勢検出を開始する
webcamの情報を使うためにはvideoタグが読み込まれるまで待つ必要があります。
そのため、以下のように書く必要があります。
if (webcamRef.current.video.readyState === 4) {
// ここにTensorFlow.jsを使う処理
}
ただ、ページが読み込まれる際にはreadyStateが4でないため、この条件文を付けただけだと、この処理部分がパスされてしまい、永遠に姿勢検出ができません。
そのため、以下のコードでreadyStateが4になるまで待つようにしました。
const handleLoadWaiting = async () => {
return new Promise((resolve) => {
const timer = setInterval(() => {
if (webcamRef.current?.video?.readyState === 4) {
resolve(true)
clearInterval(timer)
}
}, 500)
})
}
const handleStartDrawing = async () => {
await handleLoadWaiting()
// ここに必要な処理
}
ちなみにこの部分は前に私が作ったマスクをつけるアプリの場合はuseEffectで対応してました。(ローディングがないので最初マスクが表示されなくて戸惑うかと思いますが、待ってれば数秒後にマスクがつきます。)
useEffect(() => {
runFaceDetect();
}, [webcamRef.current?.video?.readyState])
今回はhandleLoadWaitingで読み込みが完了するまで待って次に進むという書き方の方が使いやすかったのでそうしてます。
また、この際に、入力のサイズと出力のサイズを指定する必要があるので、その部分だけ注意が必要です。
const webcam = webcamRef.current.video as HTMLVideoElement
const canvas = canvasRef.current
webcam.width = webcam.videoWidth
webcam.height = webcam.videoHeight
canvas.width = webcam.videoWidth
// 今回は描画用canvasはビデオ部分の2倍の高さにする必要があるので2倍にしてる
canvas.height = webcam.videoHeight * 2
最初、入力値の以下部分の幅を指定してなかった関係で、入力値の幅が0だと認識されてしまい、roiが0だと言われるエラーに悩まされました。笑
webcam.width = webcam.videoWidth
webcam.height = webcam.videoHeight
カメラの切り替えを行う
インカメラとリアカメラの切り替えはwebcamに渡している情報のfacingModeを'user'と'environment'で切り替えるだけです。
const [facingMode, setFacingMode] = useState<'user' | 'environment'>('user')
const videoConstraints = {
width: // 幅を指定,
height: // 高さを指定,
facingMode: facingMode,
}
// webcamでは以下のように渡す
<Webcam
audio={false}
mirrored={true}
videoConstraints={videoConstraints}
ref={webcamRef}
/>
後はボタンクリックなどでfacingModeの値を切り替えたらOKです。
参考
こちらは高橋さんのアイデアをもとにしています。
あとがき
今回のアプリはとみーさんを中心として、わせりんさん、西川さん、mikkameさんと一緒に作り上げました。
2日間ずっとdiscordつなぎながらハッカソンのように開発しましたが、なかなか楽しかったので、またやりたいなと思ってます。
===
これで11週目の週イチ発信となりました。
今回ちょうどとみーさんからお誘いをもらって一緒に作ることになりました。
仕事以外で誰かと一緒に開発するというのがなかったこの2ヶ月半だったので、非常に楽しかったです。
良ければこれまでの週イチ発信も見て下さい!
ではでは〜。
- 【React + Typescriptで顔認識】tensorflowを使って画像にマスクをかけるアプリを作った
- 【React + Typescript】ボタン一つでコンポーネントのscssをコピーできるサイトを作った
- 【アップデート】ui-componentsに18個のコンポーネントを追加した
- 【Nuxt.js × Tailwind CSS】ボタン一つで有名絵画風の画像にできるサービスをリリースした!
- 【GASでLINE Bot作成】現在地の近くのおすすめのごはん屋さんを教えてくれるLINE Botを作った
- 【動的OGP】Next.js + TypeScript + Vercelデプロイで動的OGPを実現する
- 【LambdaでOpenCVを利用】AWSとOpenCVを利用してポケモン画像でアスキーアート風に変換するAPIを作った
- ポケモン画像でアスキーアート風に変換するwebアプリを作った
- ボタン一つで漫画風の画像にできるサービスを作った
- 【Next.js + TensorFlowでweb cameraにバーチャル背景をつける】バーチャル旅行を体験できるアプリを作った