[MF]修改game骨骼显示错误,完善模型识别
This commit is contained in:
parent
193fe94053
commit
24f9ff2428
@ -939,8 +939,8 @@
|
|||||||
for (const [startIdx, endIdx] of HAND_CONNECTIONS) {
|
for (const [startIdx, endIdx] of HAND_CONNECTIONS) {
|
||||||
const start = keypoints[startIdx];
|
const start = keypoints[startIdx];
|
||||||
const end = keypoints[endIdx];
|
const end = keypoints[endIdx];
|
||||||
// 只有当连接的两个关键点都存在且置信度较高时才绘制
|
// 只有当连接的两个关键点都存在
|
||||||
if (start && end && start.score > 0.3 && end.score > 0.3) {
|
if (start && end) {
|
||||||
canvasCtx.beginPath();
|
canvasCtx.beginPath();
|
||||||
canvasCtx.moveTo(start.x, start.y);
|
canvasCtx.moveTo(start.x, start.y);
|
||||||
canvasCtx.lineTo(end.x, end.y);
|
canvasCtx.lineTo(end.x, end.y);
|
||||||
@ -954,7 +954,7 @@
|
|||||||
canvasCtx.shadowBlur = 10; // 关键点发光更强
|
canvasCtx.shadowBlur = 10; // 关键点发光更强
|
||||||
|
|
||||||
for (const keypoint of keypoints) {
|
for (const keypoint of keypoints) {
|
||||||
if (keypoint.score > 0.3) { // 同样只绘制置信度高的关键点
|
if (keypoint) { // 同样只绘制置信度高的关键点
|
||||||
canvasCtx.beginPath();
|
canvasCtx.beginPath();
|
||||||
canvasCtx.arc(keypoint.x, keypoint.y, 6, 0, 2 * Math.PI); // 绘制半径为6的圆
|
canvasCtx.arc(keypoint.x, keypoint.y, 6, 0, 2 * Math.PI); // 绘制半径为6的圆
|
||||||
canvasCtx.fill();
|
canvasCtx.fill();
|
@ -1,38 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Hand Pose Detection</title>
|
|
||||||
<style>
|
|
||||||
body { margin: 0; overflow: hidden; display: flex; justify-content: center; align-items: center; min-height: 100vh; background-color: #f0f0f0; }
|
|
||||||
#container { position: relative; width: auto; height: auto; max-width: 100%; max-height: 100vh; }
|
|
||||||
video, canvas {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
canvas { z-index: 10; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="container">
|
|
||||||
<video id="webcam" autoplay playsinline muted></video>
|
|
||||||
<canvas id="output"></canvas>
|
|
||||||
</div>
|
|
||||||
<!-- 引入 TensorFlow.js 核心库 -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgl"></script>
|
|
||||||
|
|
||||||
<!-- 重点:引入 @mediapipe/hands 库 -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands"></script>
|
|
||||||
|
|
||||||
<!-- 引入 TensorFlow Models - Hand Pose Detection 库 -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/hand-pose-detection"></script>
|
|
||||||
|
|
||||||
<script src="script.js"></script> <!-- 你的主 JavaScript 文件 -->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
122
new/script.js
122
new/script.js
@ -1,122 +0,0 @@
|
|||||||
const video = document.getElementById('webcam');
|
|
||||||
const canvas = document.getElementById('output');
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
let detector, rafId;
|
|
||||||
|
|
||||||
async function setupCamera() {
|
|
||||||
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
|
||||||
try {
|
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
||||||
video.srcObject = stream;
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
video.onloadedmetadata = () => {
|
|
||||||
video.play(); // 确保视频播放才能获取到正确的宽度和高度
|
|
||||||
video.onplaying = () => { // 视频真正开始播放时
|
|
||||||
canvas.width = video.videoWidth;
|
|
||||||
canvas.height = video.videoHeight;
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error accessing webcam:', error);
|
|
||||||
alert('Cannot access webcam. Please check permissions.');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
alert('Your browser does not support webcam access.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadModel() {
|
|
||||||
const model = handPoseDetection.SupportedModels.MediaPipeHands;
|
|
||||||
const detectorConfig = {
|
|
||||||
runtime: 'mediapipe', // 或者 'tfjs'
|
|
||||||
solutionPath: 'https://cdn.jsdelivr.net/npm/@mediapipe/hands' // MediaPipe solution files path
|
|
||||||
};
|
|
||||||
detector = await handPoseDetection.createDetector(model, detectorConfig);
|
|
||||||
console.log('Hand Pose Detector loaded.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const handSkeleton = {
|
|
||||||
'thumb': [0, 1, 2, 3, 4],
|
|
||||||
'indexFinger': [0, 5, 6, 7, 8],
|
|
||||||
'middleFinger': [0, 9, 10, 11, 12],
|
|
||||||
'ringFinger': [0, 13, 14, 15, 16],
|
|
||||||
'pinky': [0, 17, 18, 19, 20],
|
|
||||||
// 腕部和手掌基部的连接,确保0号点连接到所有手指的起始关节
|
|
||||||
'palmBase': [0, 1, 5, 9, 13, 17, 0] // 形成手掌的近似轮廓
|
|
||||||
};
|
|
||||||
|
|
||||||
async function detectHands() {
|
|
||||||
if (detector && video.readyState === 4) {
|
|
||||||
const hands = await detector.estimateHands(video, {
|
|
||||||
flipHorizontal: false
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空画布
|
|
||||||
|
|
||||||
if (hands.length > 0) {
|
|
||||||
for (const hand of hands) {
|
|
||||||
// 绘制手部骨架连接线
|
|
||||||
// MediaPipe Hands 模型通常有21个关键点,这些关键点有固定的索引
|
|
||||||
// 这些连接关系是标准的,可以通过硬编码定义或查找库的定义
|
|
||||||
const keypoints = hand.keypoints; // 简化访问
|
|
||||||
|
|
||||||
// 直接根据索引绘制连接线
|
|
||||||
const connectionsToDraw = [
|
|
||||||
// Thumb (拇指)
|
|
||||||
[0, 1], [1, 2], [2, 3], [3, 4],
|
|
||||||
// Index finger (食指)
|
|
||||||
[0, 5], [5, 6], [6, 7], [7, 8],
|
|
||||||
// Middle finger (中指)
|
|
||||||
[0, 9], [9, 10], [10, 11], [11, 12],
|
|
||||||
// Ring finger (无名指)
|
|
||||||
[0, 13], [13, 14], [14, 15], [15, 16],
|
|
||||||
// Pinky finger (小指)
|
|
||||||
[0, 17], [17, 18], [18, 19], [19, 20],
|
|
||||||
// Palm base connections (手掌基部连接)
|
|
||||||
[0, 5], [5, 9], [9, 13], [13, 17], [17, 0] // Connect wrist to finger bases and form a loop
|
|
||||||
];
|
|
||||||
|
|
||||||
ctx.strokeStyle = '#00FFFF'; // 青色
|
|
||||||
ctx.lineWidth = 2; // 线宽
|
|
||||||
|
|
||||||
for (const connection of connectionsToDraw) {
|
|
||||||
const start = keypoints[connection[0]];
|
|
||||||
const end = keypoints[connection[1]];
|
|
||||||
if (start && end) { // 确保关键点存在
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(start.x, start.y);
|
|
||||||
ctx.lineTo(end.x, end.y);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绘制关键点
|
|
||||||
ctx.fillStyle = '#FF0000'; // 红色
|
|
||||||
for (const keypoint of keypoints) {
|
|
||||||
// keypoint.x, keypoint.y 是像素坐标
|
|
||||||
// keypoint.z 是深度信息 (相对坐标),通常不用于2D绘制
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(keypoint.x, keypoint.y, 4, 0, 2 * Math.PI); // 绘制半径为4的圆
|
|
||||||
ctx.fill();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绘制手部关键点标记(如果仍想使用util函数绘制)
|
|
||||||
// handPoseDetection.util.drawLandmarks(ctx, hand.keypoints, {color: '#FF0000', radius: 4});
|
|
||||||
// 由于我们已经手动绘制了,这行可以注释掉或移除,避免重复绘制。
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rafId = requestAnimationFrame(detectHands);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function app() {
|
|
||||||
await setupCamera();
|
|
||||||
await loadModel();
|
|
||||||
detectHands();
|
|
||||||
}
|
|
||||||
|
|
||||||
app();
|
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* =============================================================================
|
* =============================================================================
|
||||||
* 动态版 - 手部姿态识别与模型管理脚本 (v3.0)
|
* 动态版 - 手部姿态识别与模型管理脚本 (v3.0)
|
||||||
* 由人体姿态识别修改为手部姿态识别,并确保非镜像显示
|
* 由人体姿态识别修改为手部姿态识别
|
||||||
* =============================================================================
|
* =============================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -180,14 +180,14 @@ async function addExample(classId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 模型与预测逻辑 (小修改) ---
|
// --- 模型与预测逻辑 ---
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始或停止姿态预测 (少量文案修改)
|
* 开始或停止姿态预测
|
||||||
*/
|
*/
|
||||||
function togglePrediction() {
|
function togglePrediction() {
|
||||||
if (classifier.getNumClasses() === 0) {
|
if (classifier.getNumClasses() === 0) {
|
||||||
alert("请先为至少一个手势采集样本后再开始预测!"); // 文案修改
|
alert("请先为至少一个手势采集样本后再开始预测!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isPredicting = !isPredicting;
|
isPredicting = !isPredicting;
|
||||||
@ -344,8 +344,8 @@ function drawHand(hand) {
|
|||||||
for (const connection of HAND_CONNECTIONS) {
|
for (const connection of HAND_CONNECTIONS) {
|
||||||
const start = keypoints[connection[0]];
|
const start = keypoints[connection[0]];
|
||||||
const end = keypoints[connection[1]];
|
const end = keypoints[connection[1]];
|
||||||
// 检查关键点的 score,确保是可靠的
|
// 检查关键点是否存在
|
||||||
if (start && end && start.score > 0.3 && end.score > 0.3) {
|
if (start && end) {
|
||||||
canvasCtx.beginPath();
|
canvasCtx.beginPath();
|
||||||
canvasCtx.moveTo(start.x, start.y);
|
canvasCtx.moveTo(start.x, start.y);
|
||||||
canvasCtx.lineTo(end.x, end.y);
|
canvasCtx.lineTo(end.x, end.y);
|
||||||
@ -356,7 +356,7 @@ function drawHand(hand) {
|
|||||||
// 绘制关键点
|
// 绘制关键点
|
||||||
canvasCtx.fillStyle = '#FF0000'; // 红色
|
canvasCtx.fillStyle = '#FF0000'; // 红色
|
||||||
for (const keypoint of keypoints) {
|
for (const keypoint of keypoints) {
|
||||||
if (keypoint.score > 0.3) { // 同样检查 score
|
if (keypoint) { // 同样检查
|
||||||
canvasCtx.beginPath();
|
canvasCtx.beginPath();
|
||||||
// 关键点半径设置小一点,因为手部关键点比人体姿态更密集
|
// 关键点半径设置小一点,因为手部关键点比人体姿态更密集
|
||||||
canvasCtx.arc(keypoint.x, keypoint.y, 4, 0, 2 * Math.PI);
|
canvasCtx.arc(keypoint.x, keypoint.y, 4, 0, 2 * Math.PI);
|
@ -92,7 +92,7 @@ h3 {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
/* 核心修改:移除以下两行,实现非镜像显示 */
|
/* 核心修改:移除以下两行,实现非镜像显示 */
|
||||||
/* transform: scaleX(-1); */
|
transform: scaleX(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#video {
|
#video {
|
Loading…
x
Reference in New Issue
Block a user