画面左側に表示されるお手本動画に合わせて踊ろう! mediapipeの画像認識を用いてカメラからキャプチャしたダンスの類似度を算出&NintendoSwitchのJoyconで太鼓を叩く動作を検出し、スコア化! 極上の没入感を提供します
7曲のダンスゲームを収録!
比較するランドマーク座標(お手本とプレイヤーのもの)を相対座標に変換
画面のどの位置にいるかではなく、「頭に対して手がどこにあるか、足がどこにあるか」という各ランドマーク同士の相対的な位置が重要
姿勢推定から得られたランドマークのデータを入力することで12の点(右肩)からの相対座標を出力する
function getRelativeLandmarks(landmarks) {
if (!landmarks || landmarks.length === 0) {
console.error("ランドマークが正しく取得されていません");
return []; // エラー回避のために空の配列を返す
}
const baseLandmark = landmarks[12]; // 基準座標
return landmarks.map(landmark => ({
x: landmark.x - baseLandmark.x,
y: landmark.y - baseLandmark.y,
}));
}
⇩
ダンスの類似度をcos類似度(角度を用いた類似度)で算出
ダンスのポーズ検出において、ランドマーク同士の距離ではなく、角度が重要。同じ距離でも角度が違えば全く違うポーズになってしまう。
コサイン類似度はベクトル同士の向きの類似度であり角度が近いほど1になり遠いほど-1になる。ここでは、各ランドマーク毎にコサイン類似度を算出し、平均をとり、コサイン平均類似度を出している。
function calculateCosineSimilarity(landmarks1, landmarks2) {
let dotProduct = 0;
let norm1 = 0;
let norm2 = 0;
for (let i = 0; i < landmarks1.length; i++) {
const x1 = landmarks1[i].x;
const y1 = landmarks1[i].y;
const x2 = landmarks2[i].x;
const y2 = landmarks2[i].y;
dotProduct += (x1 * x2) + (y1 * y2);
norm1 += (x1 * x1) + (y1 * y1);
norm2 += (x2 * x2) + (y2 * y2);
}
return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
}
以上を毎フレーム繰り返してお手本との類似度をとります
```python import cv2 import mediapipe as mp import json import os import time動画を読み込ませ、fpsを指定するだけで各フレームのランドマーク座標を保存したJSONファイルを作成する。これによりゲームで踊れるダンスを簡単に増やせる汎用的なプログラムになった。
mp_pose = mp.solutions.pose pose = mp_pose.Pose()
def extract_and_save_landmarks(video_path, save_path, target_fps=60): cap = cv2.VideoCapture(video_path) landmarks_data = []
frame_interval = 1.0 / target_fps
prev_time = time.time()
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
current_time = time.time()
if current_time - prev_time >= frame_interval:
# mediapipeで骨格検出
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = pose.process(frame_rgb)
if results.pose_landmarks:
frame_landmarks = []
for landmark in results.pose_landmarks.landmark:
frame_landmarks.append({
'x': landmark.x,
'y': landmark.y,
'z': landmark.z,
'visibility': landmark.visibility
})
landmarks_data.append(frame_landmarks)
prev_time = current_time
cap.release()
# JSONファイルとして保存
with open(save_path, 'w') as f:
json.dump(landmarks_data, f)
extract_and_save_landmarks('app/static/video/sample.mp4', 'app/static/landmarks/sample_landmarks.json')
### リポジトリ
AzureのFunctions用
~~https://github.com/jyogi-web/yanabaru-Functions~~
使うのをやめた
将来的には使いそう
joyconの代わり用
https://github.com/jyogi-web/yanbaru-react-pwa
joycon
https://github.com/jyogi-web/yanbaru-back-joycon
mediapipe
https://github.com/jyogi-web/yanbaru-back-mediapipe
#### yanbaru-back-mediapipe
- mediapipe
- flask
#### yanbaru-back-joycon
- pyjoycon
- flask
#### yanbaru-react-pwa&yanabaru-Functions
- DeviceMotionEvent
- React
- Static web app
- ~~SignalR Service~~←今回は諦めた
#### プレイの様子(開発中)
カメラ映像の上にランドマークが表示されたらスタートボタンを押してゲームスタート!
bluetooth接続して、楽しようとしたらデプロイしたら無理だと気づきました。 他のデバイス(ラズパイ)などを使ってAzure IoT HubやAzure IoT Centralなどのクラウドサービスと連携すれば、JoyconのBluetooth接続でも可能かなと....
使用技術
https://red-grass-08143c200.5.azurestaticapps.net/ ↑スマホで「リクエストを許可」をすると値が取れます。 スコアに関しては取れるのを確認したかっただけなのでめちゃくちゃな値が取れます 加速度は取れましたけど、連携が少し手間取ったので今後連携したいなと思います。
Xcodeと仲良くなれなかったのでWebで実装をしました...
今回の構成図
Azureの提供するリアルタイムプッシュ通信を提供するAzureSignalRを使ってみたかったですけど、時間がなくて断念... 今後挑戦してみてもおもしろいのかなと思ってます
元ネタ https://www.youtube.com/watch?v=DxpzZ96LxjE
毎日部室で部員がこんな激しいダンスを踊っていますw