0
Help us understand the problem. What are the problem?

posted at

updated at

Organization

ml5.jsでカメラ画像から姿勢推定を行う時にハマったこと

はじめに

この記事ではJavaScriptのライブラリの1つ,ml5.jsを利用した際のハマったことについて紹介したいと思います.

ml5.jsとは

ml5.jsを簡単に説明すると,簡単に機械学習に触れることが出来るライブラリと捉えています.
基本的な画像分類・文章生成から姿勢推定等といった様々な機械学習に触れることが出来ます.

ml5.js aims to make machine learning approachable for a broad audience of artists, creative coders, and students. The library provides access to machine learning algorithms and models in the browser, building on top of TensorFlow.js with no other external dependencies.

今回はこのml5.jsを使って姿勢推定を利用した際のことについて記載しています.

(もっと詳細にml5.jsについて知りたい方に関しては公式ドキュメントやQiita等の記事を見ることをお勧めします)

前提

そもそも姿勢推定とは

姿勢推定とは人の画像から関節点を識別し,その情報からどのような姿勢・状態になっているかを推定する技術です.

pose_estimation.gif

画像引用先:TensorFlow 「ポーズ推定 | TensorFlow Lite」
https://www.tensorflow.org/lite/examples/pose_estimation/overview

行いたいこと

今回Webカメラを通じリアルタイムに姿勢を推定したく思っています.
またどのようなカメラ形式(カメラ解像度)でも正常に姿勢を推定することも必要だと考えています.

問題

どのようなカメラ形式(カメラ解像度)でも下記のようなコードにて姿勢推定を行いました.
今回のコードの特徴として,このような感じとなっています.
1. カメラの解像度に合わせてvideoタグの縦横を調整したいためwidth・heightの値を100%にしている.
2. videoタグの縦横に合わせてcanvasタグの縦横を指定したいためにresizeForAll関数にてリサイズを行っている.

react
let classifier;
let poses;

const App = () => {
    // useRef
    const videoRef = useRef();
    const canvasRef = useRef();

    // poseNet起動関数
    const poseNetPlay = () => {
      classifier = ml5.poseNet(videoRef.current, () => {
          console.log("PoseNet Loaded!");
          classifier.on('pose', result => {
              poses = result;
          });
      });

      resizeForAll();
    };

    // camara&canvasリサイズ関数
    const resizeForAll = () => {
        // videoの大きさにcanvasを合わせる
        canvasRef.current.width = videoRef.current.width;
        canvasRef.current.height = videoRef.current.height;
    };

    // 描画関数
    const CanvasOutput = (poses) => {
        const context = canvasRef.current.getContext('2d');

        // 初期化
        context.clearRect(0, 0, context.canvas.clientWidth, context.canvas.clientHeight);

        // この後に描画する処理を書いていますが略します
    };


    useEffect(() => {
        navigator.mediaDevices
            .getUserMedia({video: true, audio: false})
            .then((stream) => {
              videoRef.current.srcObject = stream;
              videoRef.current.play();
            });
    }, []);

    useInterval(() => {
        // canvasに描画する関数
        CanvasOutput(poses);
    }, 20);

    return(
        <div className="App">
              <div className="wrapper">
                <video id="video" ref={videoRef} height="100%" width="100%" />
                <canvas className="canvas" ref={canvasRef}  />
              </div>
              <button onClick={poseNetPlay} >aaa</button>
        </div>
    )
}

export default App;

出力結果として下記のように出力されました.

qiita_pose_base.png

フリー素材ぱくたそ(www.pakutaso.com)

姿勢推定が出来ていない…というよりはなぜか上に小さく姿勢推定が行われているように見えます.
ただこれだと本来の目的を満たしていないため,この問題を解決しなければいけません.

原因

原因究明

今回canvasの大きさをvideoに合わせるために下記のようにリサイズを行っています.

    // camara&canvasリサイズ関数
    const resizeForAll = () => {
        // videoの大きさにcanvasを合わせる
        canvasRef.current.height = videoRef.current.height;
        canvasRef.current.width = videoRef.current.width;
    };

しかし,videoタグのwidth・heightにカメラの解像度は入っているのでしょうか。。。
console.log("videoRef.current.width, videoRef.current.height")
を見てみます.

2021-10-10.png

なんとvideoタグのwidth・heightを100%指定にしているのにもかかわらず,カメラの解像度とは全く別の値が入っていることが分かります.
またwidth・heightの値を利用して姿勢推定の座標を推定しているため(ここに関してはもしかしたら違うかもしれません)width・heightの値がかなり重要となっています.
それではなぜwidth・heightにカメラの解像度が入っていないのでしょうか?

原因特定

要素には縦横(width・height)がありますが,複数のwidth・heightがhtmlには存在することが理由です.

そしてカメラの解像度はwidth・heightには入っておらず,全く別の値に入っています.そのためその値をwidth・heightに代入する必要があります.

解決策

様々なアプローチを試した結果,videoタグのClientWidth・ClientHeightにカメラの解像度が入ってることが分かりました.
そのためClientWidth・ClientHeightの値をwidth・heightに代入することで正常に表示することが出来ます!

    // camara&canvasリサイズ関数
    const resizeForAll = () => {
        // 追加した部分
        videoRef.current.width = videoRef.current.clientWidth;
        videoRef.current.height = videoRef.current.clientHeight;

        // videoの大きさにcanvasを合わせる
        canvasRef.current.width = videoRef.current.width;
        canvasRef.current.height = videoRef.current.height;
    }

書き換えた後の出力結果として下記のようになります.

qiita_pose_success.png

フリー素材ぱくたそ(www.pakutaso.com)

きちんと体の姿勢(輪郭)に合わせて推定されていることが分かります!

まとめ

今回はml5.jsでカメラ画像から姿勢推定を行う時にハマったこと,Webカメラを取り扱った際に起こったことについて記載しました!

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?