mistpe commited on
Commit
fcdeb6f
·
verified ·
1 Parent(s): eadf517

Update static/js/holistic.js

Browse files
Files changed (1) hide show
  1. static/js/holistic.js +215 -3
static/js/holistic.js CHANGED
@@ -3,10 +3,158 @@ import DeviceDetector from "https://cdn.skypack.dev/[email protected]";
3
  // Client 和 os 是正则表达式。
4
  // 参见: https://cdn.jsdelivr.net/npm/[email protected]/README.md
5
  // 了解 client 和 os 的合法值
 
 
 
 
 
 
6
  testSupport([
7
  { client: 'Chrome' },
8
  ]);
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  function testSupport(supportedDevices) {
11
  const deviceDetector = new DeviceDetector();
12
  const detectedDevice = deviceDetector.parse(navigator.userAgent);
@@ -89,6 +237,7 @@ function connect(ctx, connectors) {
89
 
90
  let activeEffect = 'mask';
91
 
 
92
  function onResults(results) {
93
  // 隐藏旋转器。
94
  document.body.classList.add('loaded');
@@ -147,6 +296,9 @@ function onResults(results) {
147
  }
148
  }
149
 
 
 
 
150
  // 姿势...
151
  drawingUtils.drawConnectors(canvasCtx, results.poseLandmarks, mpHolistic.POSE_CONNECTIONS, { color: 'white' });
152
  drawingUtils.drawLandmarks(canvasCtx, Object.values(mpHolistic.POSE_LANDMARKS_LEFT)
@@ -185,6 +337,39 @@ function onResults(results) {
185
 
186
  canvasCtx.restore();
187
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
  const holistic = new mpHolistic.Holistic(config);
190
  holistic.onResults(onResults);
@@ -258,19 +443,46 @@ new controls
258
  activeEffect = x['effect'];
259
  holistic.setOptions(options);
260
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
 
262
- // 添加窗口大小调整事件监听器,以确保画布大小随窗口变化而调整
 
 
 
 
 
 
263
  window.addEventListener('resize', () => {
264
  const aspect = videoElement.videoHeight / videoElement.videoWidth;
265
  let width, height;
266
  if (window.innerWidth > window.innerHeight) {
267
  height = window.innerHeight;
268
  width = height / aspect;
269
- }
270
- else {
271
  width = window.innerWidth;
272
  height = width * aspect;
273
  }
274
  canvasElement.width = width;
275
  canvasElement.height = height;
 
 
 
 
276
  });
 
3
  // Client 和 os 是正则表达式。
4
  // 参见: https://cdn.jsdelivr.net/npm/[email protected]/README.md
5
  // 了解 client 和 os 的合法值
6
+ // 导入必要的库
7
+
8
+ // 初始化速度和加速度图表
9
+ let speedChart, accelerationChart;
10
+ let previousPoseData = null;
11
+ let lastTimestamp = 0;
12
  testSupport([
13
  { client: 'Chrome' },
14
  ]);
15
 
16
+ function initCharts() {
17
+ const speedChartCtx = document.getElementById('speedChart').getContext('2d');
18
+ const accelerationChartCtx = document.getElementById('accelerationChart').getContext('2d');
19
+
20
+ speedChart = new Chart(speedChartCtx, {
21
+ type: 'line',
22
+ data: {
23
+ labels: [],
24
+ datasets: [{
25
+ label: 'Movement Speed (px/s)',
26
+ data: [],
27
+ borderColor: 'rgba(75, 192, 192, 1)',
28
+ tension: 0.4,
29
+ borderWidth: 2,
30
+ fill: false
31
+ }]
32
+ },
33
+ options: {
34
+ responsive: true,
35
+ maintainAspectRatio: false,
36
+ animation: {
37
+ duration: 0
38
+ },
39
+ scales: {
40
+ y: {
41
+ beginAtZero: true,
42
+ title: {
43
+ display: true,
44
+ text: 'Speed'
45
+ }
46
+ },
47
+ x: {
48
+ title: {
49
+ display: true,
50
+ text: 'Time'
51
+ }
52
+ }
53
+ }
54
+ }
55
+ });
56
+
57
+ accelerationChart = new Chart(accelerationChartCtx, {
58
+ type: 'line',
59
+ data: {
60
+ labels: [],
61
+ datasets: [{
62
+ label: 'Acceleration (px/s²)',
63
+ data: [],
64
+ borderColor: 'rgba(255, 99, 132, 1)',
65
+ tension: 0.4,
66
+ borderWidth: 2,
67
+ fill: false
68
+ }]
69
+ },
70
+ options: {
71
+ responsive: true,
72
+ maintainAspectRatio: false,
73
+ animation: {
74
+ duration: 0
75
+ },
76
+ scales: {
77
+ y: {
78
+ beginAtZero: true,
79
+ title: {
80
+ display: true,
81
+ text: 'Acceleration'
82
+ }
83
+ },
84
+ x: {
85
+ title: {
86
+ display: true,
87
+ text: 'Time'
88
+ }
89
+ }
90
+ }
91
+ }
92
+ });
93
+ }
94
+
95
+ // 计算姿态变化的速度和加速度
96
+ function calculateMotionMetrics(currentPose, timestamp) {
97
+ if (!previousPoseData || !currentPose.poseLandmarks) {
98
+ previousPoseData = currentPose;
99
+ lastTimestamp = timestamp;
100
+ return { speed: 0, acceleration: 0 };
101
+ }
102
+
103
+ const deltaTime = (timestamp - lastTimestamp) / 1000; // 转换为秒
104
+ if (deltaTime === 0) return { speed: 0, acceleration: 0 };
105
+
106
+ // 计算关键点的平均位移
107
+ let totalDisplacement = 0;
108
+ let validPoints = 0;
109
+
110
+ currentPose.poseLandmarks.forEach((landmark, index) => {
111
+ if (previousPoseData.poseLandmarks[index]) {
112
+ const dx = landmark.x - previousPoseData.poseLandmarks[index].x;
113
+ const dy = landmark.y - previousPoseData.poseLandmarks[index].y;
114
+ const displacement = Math.sqrt(dx * dx + dy * dy);
115
+ totalDisplacement += displacement;
116
+ validPoints++;
117
+ }
118
+ });
119
+
120
+ const averageDisplacement = validPoints > 0 ? totalDisplacement / validPoints : 0;
121
+ const currentSpeed = averageDisplacement / deltaTime;
122
+
123
+ // 计算加速度
124
+ const previousSpeed = speedChart.data.datasets[0].data[speedChart.data.datasets[0].data.length - 1] || 0;
125
+ const acceleration = (currentSpeed - previousSpeed) / deltaTime;
126
+
127
+ // 更新先前数据
128
+ previousPoseData = currentPose;
129
+ lastTimestamp = timestamp;
130
+
131
+ return { speed: currentSpeed, acceleration: acceleration };
132
+ }
133
+
134
+ // 更新图表数据
135
+ function updateCharts(metrics) {
136
+ const timestamp = new Date().toLocaleTimeString();
137
+ const maxDataPoints = 50; // 限制数据点数量
138
+
139
+ // 更新速度图表
140
+ speedChart.data.labels.push(timestamp);
141
+ speedChart.data.datasets[0].data.push(metrics.speed);
142
+ if (speedChart.data.labels.length > maxDataPoints) {
143
+ speedChart.data.labels.shift();
144
+ speedChart.data.datasets[0].data.shift();
145
+ }
146
+ speedChart.update('none');
147
+
148
+ // 更新加速度图表
149
+ accelerationChart.data.labels.push(timestamp);
150
+ accelerationChart.data.datasets[0].data.push(metrics.acceleration);
151
+ if (accelerationChart.data.labels.length > maxDataPoints) {
152
+ accelerationChart.data.labels.shift();
153
+ accelerationChart.data.datasets[0].data.shift();
154
+ }
155
+ accelerationChart.update('none');
156
+ }
157
+
158
  function testSupport(supportedDevices) {
159
  const deviceDetector = new DeviceDetector();
160
  const detectedDevice = deviceDetector.parse(navigator.userAgent);
 
237
 
238
  let activeEffect = 'mask';
239
 
240
+
241
  function onResults(results) {
242
  // 隐藏旋转器。
243
  document.body.classList.add('loaded');
 
296
  }
297
  }
298
 
299
+ // 计算并更新动作指标
300
+ const metrics = calculateMotionMetrics(results, performance.now());
301
+ updateCharts(metrics);
302
  // 姿势...
303
  drawingUtils.drawConnectors(canvasCtx, results.poseLandmarks, mpHolistic.POSE_CONNECTIONS, { color: 'white' });
304
  drawingUtils.drawLandmarks(canvasCtx, Object.values(mpHolistic.POSE_LANDMARKS_LEFT)
 
337
 
338
  canvasCtx.restore();
339
  }
340
+ // 视频上传处理
341
+ function handleVideoUpload(file) {
342
+ const formData = new FormData();
343
+ formData.append('video', file);
344
+
345
+ // 重置图表数据
346
+ speedChart.data.labels = [];
347
+ speedChart.data.datasets[0].data = [];
348
+ accelerationChart.data.labels = [];
349
+ accelerationChart.data.datasets[0].data = [];
350
+ previousPoseData = null;
351
+ lastTimestamp = 0;
352
+
353
+ // 发送视频到服务器
354
+ fetch('/upload_video', {
355
+ method: 'POST',
356
+ body: formData
357
+ })
358
+ .then(response => response.json())
359
+ .then(data => {
360
+ if (data.success) {
361
+ // 更新视频源
362
+ videoElement.src = URL.createObjectURL(file);
363
+ videoElement.play();
364
+ } else {
365
+ console.error('Video upload failed:', data.error);
366
+ }
367
+ })
368
+ .catch(error => {
369
+ console.error('Error uploading video:', error);
370
+ });
371
+ }
372
+
373
 
374
  const holistic = new mpHolistic.Holistic(config);
375
  holistic.onResults(onResults);
 
443
  activeEffect = x['effect'];
444
  holistic.setOptions(options);
445
  });
446
+ // 初始化函数
447
+ function initialize() {
448
+ // 初始化图表
449
+ initCharts();
450
+
451
+ // 设置视频上传处理
452
+ const videoUploadInput = document.querySelector('#video-upload');
453
+ if (videoUploadInput) {
454
+ videoUploadInput.addEventListener('change', (e) => {
455
+ if (e.target.files.length > 0) {
456
+ handleVideoUpload(e.target.files[0]);
457
+ }
458
+ });
459
+ }
460
+
461
+ // 初始化姿态检测
462
+ const holistic = new mpHolistic.Holistic(config);
463
+ holistic.onResults(onResults);
464
 
465
+ // ... 保持其他原有的初始化逻辑 ...
466
+ }
467
+
468
+ // 启动应用
469
+ window.addEventListener('load', initialize);
470
+
471
+ // 保持原有的窗口大小调整逻辑
472
  window.addEventListener('resize', () => {
473
  const aspect = videoElement.videoHeight / videoElement.videoWidth;
474
  let width, height;
475
  if (window.innerWidth > window.innerHeight) {
476
  height = window.innerHeight;
477
  width = height / aspect;
478
+ } else {
 
479
  width = window.innerWidth;
480
  height = width * aspect;
481
  }
482
  canvasElement.width = width;
483
  canvasElement.height = height;
484
+
485
+ // 重新调整图表大小
486
+ speedChart.resize();
487
+ accelerationChart.resize();
488
  });