過去に記載した簡単なフォームチェックにて、TensorflowJS + HTML5 でiOS・Safariを含め、ブラウザでキーポイント(関節点)+フォームを描画することに成功。
どうにかして「タイガー・ウッズと僕の構えがどのくらい違っているか?」を試行錯誤してましたが、モデルとなるキーポイントと描画されているキーポイント同士の距離計算(三平方定理)ではほとんど一致することがなく、計算がなかなか出来ないでいました。
結果、線同士の角度類似でやってみたらどうか、と思い、「2次元コサイン類似度」を使ってみました。
##2次元コサイン類似度
数学はめっぽう弱いので、こちらを参照下さい。
要は、2つの線の傾き(方向)がどのくらい一致しているか、という認識でいます。モデルとなる右肩~左肩がなす線と、カメラに写っている右肩~左肩がなす線の角度・方向が一致するならOK。
コサイン類似度は
・コサイン類似度が 1 に近い ⟺ 2本は同じ向きに近い
・コサイン類似度が 0 ⟺ 90度
・コサイン類似度が −1 に近い ⟺ 2本は逆向きに近い
となるようなので、0.8くらいならOKとしようかなと。
##2次元コサイン類似度の計算
Javascriptでの計算方法が探してもなかったため、他の言語のコーディングを元に関数を自作。一応値は大丈夫そう。
2つの線のエンド座標からスタート座標までのベクトル値(x, y)が必要(だと思う)。
var a = [0, 10]; // 単位はベクトル。[x2 - x1, y2 - y1]だと思う。
var b = [10, 0]; // 単位はベクトル。つまり座標系で、aは真上に、bは右横に駆け抜けた、ということだと思う。
var CS = getCosineSimilarity(a, b);
console.log(CS); // 1 つまり90度
function getCosineSimilarity(a, b) {
var cos = 0;
var i = 0;
var j = 0;
var k = 0;
var keys = Object.keys(a);
keys.forEach(function(key) {
i += a[key] * b[key];
j += Math.pow(a[key], 2);
k += Math.pow(b[key], 2);
});
cos = i / (Math.sqrt(j) * Math.sqrt(k));
return cos;
}
とりあえず両肩の類似度のみ手計算
モデルのキーポイントJSONとカメラに写っているキーポイントJSONの配列は同じにしてあるので、上記関数に実値をかませてみます。
モデルの右肩から左肩の座標線の角度
(67 ,83) -> (110, 82) = -1.33度
カメラの右肩から左肩の座標線の角度(わざと描画位置をずらしてみる)
(66 ,169) -> (106, 167) = -2.86度
角度はatanで計算。合ってる?
var redian = Math.atan2((ey - sy), (ex - sx));
var angle = redian * (180 / Math.PI);
そして
var a = [43, -1];
var b = [40, -2];
となり・・。
##結果
類似度 0.9996。
肩の角度だけはタイガーにそっくりな・・の?
純粋に角度で閾を作って比較した方がいいかもしれませんね。あとモデルの肩の角度もちょっとおかしいので微調整することにします。
他の部位も同じ計算を行っていけば、と思っています。また続報します。
数学詳しい方、助けて下さいませ。。
##タイガーからキーポイントを取得し、タイガーに当ててみる
右上の数字が、両肩の角度の差です。コサイン類似度では、やはり点数が高すぎる感じだったので、角度差としてみました
わざと位置をずらしましたが、角度計算に影響は無いですね。
UIを考えないといけないけど、HTML canvasなので、凝ったことがやりづらいんですよね。
Tensorflow Posenet のラインが単色で分かりづらかったので、OpenPoseみたいな色に改造しました。