前回の記事より、簡単なフォームチェックのロジックを追加してみました。
ボーン座標のズレ
ブラウザのgetusermediaを使って、TensoleJS posenet を適当にVideoタグで表示すると、映し出された映像に描画するボーン座標にズレが生じます。
これは、縦横比の問題であろうと思われ、試行錯誤の結果、iPhone8の場合、横幅340px、縦幅380pxでズレが最小限に抑えられるようです。以下はズレた例です。
フォームチェックのテンプレート
某一流プロゴルファーのボーン座標を写真から取得しました。
ゴルフのアドレス(構え)としては、ちょっとおかしい感じがありますね。
写真を横幅170px、縦380pxにリザイズして、画面上に合うように手作業しました。
どうやら、少し大きめの画像でないと、誤認識するようです。以下、元画像を横幅を340pxに拡大してボーン座標を取得してみました。少しまともになったかな。
フォームチェックのロジック
とりあえず最初は簡単な方法で、と思い、
・テンプレートの目と耳以外の13箇所それぞれの位置と写した映像のそれぞれの位置の距離を測る。
・その距離が10ピクセル以下なら1、でなければ0とする。
・最大13箇所中、何箇所が1になっているか、を百分率する。これを一致率としてみる。
estimateSinglePose()を実行している関数poseDetectionFrame()の中にある、poses.forEach(({score, keypoints}) に、判定のロジックを入れます。
poses.forEach(({score, keypoints}) => {
if (score >= minPoseConfidence) {
if (guiState.output.showPoints) {
drawKeypoints(keypoints, minPartConfidence, ctx);
}
if (guiState.output.showSkeleton) {
drawSkeleton(keypoints, minPartConfidence, ctx);
}
if (guiState.output.showBoundingBox) {
drawBoundingBox(keypoints, ctx);
}
}
make_video_bone(keypoints) // 追加
});
キーポイントを配列に分割します。
function make_video_bone(keypoints) {
for (let i = 0; i < keypoints.length; i++) {
const keypoint = keypoints[i];
video_bone[keypoint.part] = keypoint.position;
}
if (templates_bone.nose && video_bone.nose) {
calc_distance(templates_bone, video_bone);
}
}
テンプレートと比較して一致率を計算します。
function calc_distance(tb, vb) {
var cnt = 0;
var max = 13;
var temp = Math.sqrt(Math.pow((tb.nose.x - vb.nose.x),2) + Math.pow((tb.nose.y - vb.nose.y),2));
if (temp < 10) {
cnt++;
}
var temp = Math.sqrt(Math.pow((tb.leftShoulder.x - vb.leftShoulder.x),2) + Math.pow((tb.leftShoulder.y - vb.leftShoulder.y),2));
if (temp < 10) {
cnt++;
}
・・・・・
var rs = cnt / max * 100;
// 画面上に一致率を表示
$('.pa').html(Math.round(rs));
}
もっといい判定方法は無いかな、と思っています。
検証
スマホでは手ブレが激しいので、13箇所がうまく判定出来ず、常に一致率は0%。
逆に言うと、手ブレがわかるほどスムーズなライブ再生で性能が良い、ということですね。
軸にしてみるとか、もう少しチューニングが必要。
以下、ブルーの線がライブ、赤い線がテンプレート(模範)です。なかなか一致しない。。