-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
378 lines (356 loc) · 15.3 KB
/
index.html
File metadata and controls
378 lines (356 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
<!DOCTYPE html>
<html>
<head>
<title>迳口麒麟舞AI传承大师</title>
<meta charset="utf-8">
<link rel="stylesheet" href="static/css/index.css" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="Cache-Control" content="no-transform " />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<meta name="renderer" content="webkit">
<script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
<script>LA.init({id:"3LTNMctPRNmiv7MN",ck:"3LTNMctPRNmiv7MN"})</script>
</head>
<div id="background-div"></div>
<body data-debug="false">
<div class="main" id="main">
<div id="info" style="display: none"></div>
<div id="main-container">
<div class="canvas-container flip" id="left-frame"></div>
<video id="localVideo" class="video left-canvas" playsinline></video>
<canvas id="localSkeletonCanvas" class="left-canvas"></canvas>
<canvas id="localPassiveCanvas" class="left-canvas"></canvas>
<canvas id="localNodeCanvas" class="left-canvas"></canvas>
<canvas id="localCanvas" class="left-canvas"></canvas>
<div class="canvas-container" id="right-frame"></div>
<video id="remoteVideo" class="video right-canvas" playsinline></video>
<canvas id="remoteSkeletonCanvas" class="right-canvas"></canvas>
<canvas id="remotePassiveCanvas" class="right-canvas"></canvas>
<canvas id="remoteNodeCanvas" class="right-canvas"></canvas>
<canvas id="remoteCanvas" class="right-canvas"></canvas>
</div>
<div id="tip" class="tip">请站在大约3米远处,确保全身入镜</div>
<div id="score" class="score">得分:0.00</div>
<div class="pgbar">
<div class="pgbar-round">
<progress id="progress" value="0.5" max="1"></progress>
</div>
<div id="progress-icon" style="margin-left: 50%;"></div>
</div>
<div class="footer">
<div class="sub-footer">
<div class="btn" id="start"><div id="start-btn-text">开始</div></div>
<div class="btn" id="pause"><div id="pause-btn-text">暂停</div></div>
<div class="btn" id="stop"><div id="stop-btn-text">停止</div></div>
<div class="btn" id="example"><div id="example-btn-text">示例</div></div>
<div class="btn" id="cache" style="display: none"></div>
<div id="cache-btn-text" style="display: none">缓存</div>
</div>
</div>
</div>
<div id="popup-group" style="display: none">
<div id="popup-mask" onclick="dismissModal(this.parentNode)"></div>
<div id="popup">
<div id="popup-icon"></div>
<div id="popup-title"></div>
<div id="popup-text"></div>
</div>
</div>
<div class="fpsbox" id="fpsbox" style="width: 100px"></div>
<script>
// 模态框展示模块
function showModal(content = "", title = "", noDismiss = false) {
document.getElementById("popup-title").innerHTML = title;
document.getElementById("popup-text").innerHTML = content;
const g = document.getElementById("popup-group");
g.style.display = "flex";
if (noDismiss) {
g.dataset.noDismiss = 1;
}
}
// 切换模态框状态
function hideModal(popupGroup) {
popupGroup.style.display = "none";
delete popupGroup.dataset.noDismiss;
}
function hideAllModals() {
hideModal(document.getElementById("popup-group"));
}
// 用户关闭
function dismissModal(popupGroup) {
if (popupGroup.dataset.noDismiss) {
return;
}
hideModal(popupGroup);
}
</script>
<script src="static/js/tf.min.js"></script>
<script src="static/js/dat.gui.min.js"></script>
<!-- PosNet,国内使用可以修改其中的https://storage.googleapis.com/ 为镜像域名或使用同站资源 -->
<script src="static/js/posenet.min.js"></script>
<!-- MoveNet,国内使用可以修改其中的https://tfhub.dev/ 为镜像域名或使用同站资源 -->
<script src="static/js/pose-detection.min.js"></script>
<!-- Stats.js,可选,如果要使用FPS和性能统计组件功能则需要 -->
<script src="static/js/Stats.min.js"></script>
<!-- 缓存,15fps大约每秒3KB,可以自行穿插删除降低帧率或在计算缓存时使用低帧率视频 -->
<script src="static/js/remoteCache.js"></script>
<script type="module" src="static/mjs/camera.mjs"></script>
<script type="module">
// 动作评分模块使用代码
import { ActionCamera } from "./static/mjs/camera.mjs";
const DEBUG = false; // 是否开启标准姿态比对模式
// ActionCamera类使用方法
// 1.初始化并配置
const remoteVideo = document.getElementById("remoteVideo");
const localCamera = document.getElementById("localVideo");
const remoteCanvas = document.getElementById("remoteSkeletonCanvas");
const localCanvas = document.getElementById("localSkeletonCanvas");
const fpsbox = document.getElementById("fpsbox");
const stats = new Stats();
// 配置设置方法1:
// const config = camera.getConfig(); // 修改config不会影响原配置
// config. ... = ...
// camera.setConfig(config); // 覆盖配置
// 方法2:constructor直接传入
const config = {
// 含有【🌟】的配置修改后会触发模型重新加载
algorithm: "single-pose", // 姿态识别算法,single-pose或multi-pose
input: {
usePoseNet: false, // 【🌟】使用PoseNet(true)还是MoveNet(false),默认使用MoveNet
videoWidth: 720,
videoHeight: 540,
loadedCallback: () => {
},
},
poseNetInput: {
// PosNet的ResNet50/MobileNetV1
architecture: "MobileNetV1", // 【🌟】姿态识别模型架构
outputStride: 8, // 【🌟】姿态识别模型输出步长,MobileNetV1=8/16,ResNet50=16/32,越大越精细,但会增加计算量
inputResolution: 200, // 姿态识别模型精度,200-900,越大越精细,但会增加计算量
multiplier: 0.75, // 【🌟】姿态识别模型缩放比例,0.5/0.75/1.0,默认0.75
quantBytes: 2, // 【🌟】姿态识别模型量化比例,1/2/4,默认2
},
moveNetInput: {
// MoveNet(注意MoveNet不能检测多人)
modelType: poseDetection.movenet.modelType.SINGLEPOSE_THUNDER, // 【🌟】可选SINGLEPOSE_THUNDER或SINGLEPOSE_LIGHTNING
},
singlePoseDetection: {
minPoseConfidence: 0.1, // 最小姿态置信度,越高则越不容易在没人时误判为有人,但也容易无法及时发现人物或产生时有时无的问题
// minPartConfidence: 0.5, // PosNet,最小关节点置信度,越高则越不容易将物体误判为关节,但越容易漏关节
minPartConfidence: 0.05, // MoveNet的参数略低于PosNet,但效果更好
},
multiPoseDetection: {
maxPoseDetections: 5, // 最多识别人数
minPoseConfidence: 0.15, // 同上,越高则越不容易将物体误判为人,但也越容易漏掉人物
minPartConfidence: 0.05, // 同上
nmsRadius: 30.0, // 非极大值抑制半径,越大则越不容易将相似的姿态合并
},
output: {
// 辅助图形配置
showSkeletons: true, // 显示骨架(黑色折线)
showPoints: true, // 显示关节点(动态粉色实心圆点)
stats: stats, // 性能统计组件
flipPoseHorizontal: true, // 手动水平翻转(因为是前置摄像头,所以要手动翻转)
posesQueueLength: 5, // 姿态队列长度,越长则越不容易漏掉姿态,但也越容易卡顿
displayCacheSkeleton: true, // 缓存过程中是否渲染骨架和关节(同普通骨架和关节颜色)
},
net: null, // 姿态识别模型对象,null表示尚未加载完毕
};
const camera = new ActionCamera(
remoteVideo,
localCamera,
remoteCanvas,
localCanvas,
config
); // 初始化,载入配置并开始下载模型
const example_video = "static/std64_fixed.mp4";
const COMP_CATEGORY = {
0: null, // 摄像头
1: "static/std64_fixed.mp4", // 标准64s视频
2: "static/std_fixed.mp4", // 6s标准视频
3: "static/lazy_fixed.mp4", // 懒人示范视频
4: "static/crazy_fixed.mp4", // 努力模仿视频
};
function setupFPS() {
// 性能统计(FPS、渲染间隔、内存占用)
stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
fpsbox.appendChild(stats.domElement);
}
function closeFPS() {
stats.domElement.remove();
}
window.onload = function () {
// 首先根据屏幕宽度设置body的zoom
function resizeBody() {
const body = document.body;
const bgDiv = document.getElementById("background-div");
const width = window.innerWidth;
const height = window.innerHeight;
const targetRatio = 1920 / 1080;
const currentRatio = width / height;
// 如果是高度太高,根据宽度调整
if (currentRatio < targetRatio) {
const zoom = width / 1920;
body.style.zoom = zoom;
bgDiv.style.backgroundSize = "100%";
} else {
// 如果是宽度太宽,根据高度调整
const zoom = height / 1080;
body.style.zoom = zoom;
// 还需要更改背景模式为高度适应
bgDiv.style.backgroundSize = "auto 100%";
}
}
// 注册窗口大小改变事件
window.addEventListener("resize", resizeBody);
// 首次调用
resizeBody();
document
.getElementById("start")
.addEventListener("click", async function () {
// 点击开始按钮后的事件处理
try {
setupFPS();
selectAndPlay();
} catch (e) {
// 将错误信息通过模块显示
showError(e);
}
});
document.getElementById("pause").addEventListener("click", function () {
// 点击暂停按钮后的事件处理
try {
const remoteVideo = document.getElementById("remoteVideo");
if (remoteVideo.paused) {
remoteVideo.play();
} else {
remoteVideo.pause();
}
} catch (e) {
showError(e);
}
});
document.getElementById("stop").addEventListener("click", function () {
// 点击停止按钮后的事件处理
try {
closeFPS();
camera.stopPlaying();
} catch (e) {
showError(e);
}
});
document.getElementById("cache").addEventListener("click", function () {
// 点击缓存按钮后的事件处理
try {
// 缓存数据,用于比对
camera.startCaching(example_video);
} catch (e) {
showError(e);
}
});
document
.getElementById("example")
.addEventListener("click", function () {
// 点击示例视频按钮后的事件处理
try {
setupFPS();
selectAndPlay(1);
} catch (e) {
showError(e);
}
});
const progressBar = document.getElementById("progress"); // 进度条
const progessIcon = document.getElementById("progress-icon"); // 进度条图标
const scoreText = document.getElementById("score"); // 评分显示
const tipText = document.getElementById("tip"); // 提示信息
setInterval(() => {
updateStatus(progressBar, progessIcon, scoreText, tipText);
}, 1000); // 1s更新一次
};
async function showError(error) {
let info = document.getElementById("info");
info.innerHTML = `出现错误:${error.message} <br> 详细信息:${error.stack}`;
info.style.display = "block";
throw error;
}
function selectAndPlay(index) {
if (index) {
camera.startPlaying(example_video, COMP_CATEGORY[index], remoteCache);
} else if (document.body.dataset.debug === "true") {
let index = prompt(
"请输入标准视频序号:\n" + Object.entries(COMP_CATEGORY).join("\n")
);
while (!(index in COMP_CATEGORY)) {
index = prompt(
"请输入正确的序号!\n" + Object.entries(COMP_CATEGORY).join("\n")
);
}
// 3. 开始播放
camera.startPlaying(example_video, COMP_CATEGORY[index], remoteCache);
} else {
camera.startPlaying(example_video, null, remoteCache);
}
}
const TIP_TEXT = {
0: "", // 清除提示
1: "🔥请远离摄像头,确保全身入镜🔥",
2: "❄️请靠近摄像头❄️",
3: "🌟请全身入镜🌟",
4: "",
};
let waitTextShowed = false;
// 4. 定时获取分数
function updateStatus(progressBar, progressIcon, scoreText, tipText) {
const status = camera.getStatus();
if (status.waiting && !waitTextShowed) {
showModal("模型正在加载,请稍候...", "提示", true);
waitTextShowed = true;
return;
} else if (!status.waiting && waitTextShowed) {
hideAllModals();
waitTextShowed = false;
}
if (status.paused) {
if (status.transScore)
scoreText.textContent = ` 得分:${status.transScore.toFixed(2)}(暂停)`; // 显示暂停状态
}
else if (status.score) {
scoreText.textContent = `得分:${status.score.toFixed(2)}`;
tipText.textContent = TIP_TEXT[status.tip]; // 显示提示信息
}
if (status.finalScore) {
showFinalScore(status.finalScore); // 显示最终得分
tipText.textContent = TIP_TEXT[status.tip]; // 显示提示信息
} else {
const progress = status.currentTime / status.duration;
if (!isNaN(progress)) {
progressBar.value = progress; // 进度条
progressIcon.style.marginLeft = `${(progress * 100).toFixed(2)}%`; // 进度条图标
}
}
if (status.finalRemoteCache) {
console.log("Final remote cache:", status.finalRemoteCache);
alert("缓存完成!请前往控制台查看");
}
}
function showFinalScore(finalScore) {
let remark = "";
if (finalScore > 90) remark = "太强辣!";
else if (finalScore > 80) remark = "相当不错!";
else remark = "再接再厉!";
showModal(
remark +
"<br>点击任意处继续",
"评分结束<br>您的分数是:" + finalScore.toFixed(2)
);
}
</script>
<script>
// if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
// setTimeout(() => {
// debugger;
// }, 60000);
// }
</script>
</body>
</html>