mistpe commited on
Commit
349821d
·
verified ·
1 Parent(s): 7155301

Delete templates/video_app.html

Browse files
Files changed (1) hide show
  1. templates/video_app.html +0 -602
templates/video_app.html DELETED
@@ -1,602 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>3D Pose Estimation App</title>
7
- <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
8
- <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
9
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">
11
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css">
12
- <style>
13
- :root {
14
- --primary-color: #4a90e2;
15
- --secondary-color: #f5a623;
16
- --background-color: #f0f4f8;
17
- --card-background: #ffffff;
18
- --text-color: #333333;
19
- --border-radius: 12px;
20
- --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08);
21
- }
22
-
23
- * {
24
- box-sizing: border-box;
25
- margin: 0;
26
- padding: 0;
27
- }
28
-
29
- body {
30
- font-family: 'Roboto', sans-serif;
31
- background: var(--background-color);
32
- color: var(--text-color);
33
- line-height: 1.6;
34
- }
35
-
36
- .container {
37
- max-width: 1400px;
38
- margin: 0 auto;
39
- padding: 20px;
40
- }
41
-
42
- .app-header {
43
- text-align: center;
44
- margin-bottom: 30px;
45
- }
46
-
47
- .app-title {
48
- font-size: 2.5em;
49
- color: var(--primary-color);
50
- margin-bottom: 10px;
51
- }
52
-
53
- .app-description {
54
- font-size: 1.1em;
55
- color: var(--text-color);
56
- opacity: 0.8;
57
- }
58
-
59
- .card-grid {
60
- display: grid;
61
- grid-template-columns: repeat(2, 1fr);
62
- gap: 20px;
63
- }
64
-
65
- .card {
66
- background: var(--card-background);
67
- border-radius: var(--border-radius);
68
- box-shadow: var(--box-shadow);
69
- overflow: hidden;
70
- transition: transform 0.3s ease;
71
- }
72
-
73
- .card:hover {
74
- transform: translateY(-5px);
75
- }
76
-
77
- .card-header {
78
- background: var(--primary-color);
79
- color: white;
80
- padding: 15px;
81
- font-size: 1.2em;
82
- font-weight: bold;
83
- display: flex;
84
- align-items: center;
85
- }
86
-
87
- .card-header i {
88
- margin-right: 10px;
89
- }
90
-
91
- .card-body {
92
- padding: 20px;
93
- }
94
-
95
- .view {
96
- width: 100%;
97
- padding-bottom: 56.25%; /* 16:9 aspect ratio */
98
- position: relative;
99
- border-radius: var(--border-radius);
100
- overflow: hidden;
101
- }
102
-
103
- .view img, .view canvas {
104
- position: absolute;
105
- top: 0;
106
- left: 0;
107
- width: 100%;
108
- height: 100%;
109
- object-fit: contain;
110
- }
111
-
112
- .controls {
113
- margin-top: 20px;
114
- }
115
-
116
- .slider-container {
117
- display: flex;
118
- align-items: center;
119
- margin-bottom: 15px;
120
- }
121
-
122
- .slider-container label {
123
- width: 80px;
124
- margin-right: 10px;
125
- }
126
-
127
- .slider {
128
- flex-grow: 1;
129
- -webkit-appearance: none;
130
- height: 5px;
131
- background: #d7dcdf;
132
- outline: none;
133
- opacity: 0.7;
134
- transition: opacity .2s;
135
- border-radius: 5px;
136
- }
137
-
138
- .slider:hover {
139
- opacity: 1;
140
- }
141
-
142
- .slider::-webkit-slider-thumb {
143
- -webkit-appearance: none;
144
- appearance: none;
145
- width: 18px;
146
- height: 18px;
147
- background: var(--secondary-color);
148
- cursor: pointer;
149
- border-radius: 50%;
150
- }
151
-
152
- .btn {
153
- padding: 12px 20px;
154
- border: none;
155
- border-radius: var(--border-radius);
156
- background-color: var(--primary-color);
157
- color: white;
158
- cursor: pointer;
159
- transition: background-color 0.3s ease, transform 0.1s ease;
160
- margin-top: 10px;
161
- width: 100%;
162
- display: flex;
163
- align-items: center;
164
- justify-content: center;
165
- font-size: 1em;
166
- font-weight: bold;
167
- }
168
-
169
- .btn:hover {
170
- background-color: #3a7bd5;
171
- transform: translateY(-2px);
172
- }
173
-
174
- .btn:active {
175
- transform: translateY(0);
176
- }
177
-
178
- .btn i {
179
- margin-right: 10px;
180
- }
181
-
182
- .chart-container {
183
- display: flex;
184
- flex-wrap: wrap;
185
- justify-content: space-around;
186
- gap: 20px;
187
- }
188
-
189
- .chart-container canvas {
190
- max-width: 100%;
191
- height: auto !important;
192
- }
193
-
194
- #message-box {
195
- position: fixed;
196
- top: 20px;
197
- left: 50%;
198
- transform: translateX(-50%);
199
- background: var(--primary-color);
200
- color: white;
201
- padding: 15px 20px;
202
- border-radius: var(--border-radius);
203
- z-index: 1000;
204
- display: none;
205
- box-shadow: var(--box-shadow);
206
- }
207
-
208
- #loading-indicator {
209
- position: fixed;
210
- top: 50%;
211
- left: 50%;
212
- transform: translate(-50%, -50%);
213
- z-index: 1000;
214
- display: none;
215
- background: rgba(0, 0, 0, 0.8);
216
- color: white;
217
- padding: 20px;
218
- border-radius: var(--border-radius);
219
- box-shadow: var(--box-shadow);
220
- }
221
-
222
- #loading-indicator i {
223
- margin-right: 10px;
224
- }
225
-
226
- @media (max-width: 1024px) {
227
- .card-grid {
228
- grid-template-columns: 1fr;
229
- }
230
-
231
- .app-title {
232
- font-size: 2em;
233
- }
234
-
235
- .app-description {
236
- font-size: 1em;
237
- }
238
- }
239
-
240
- @media (max-width: 768px) {
241
- .container {
242
- padding: 10px;
243
- }
244
-
245
- .card-header {
246
- font-size: 1.1em;
247
- }
248
-
249
- .slider-container {
250
- flex-direction: column;
251
- align-items: flex-start;
252
- }
253
-
254
- .slider-container label {
255
- margin-bottom: 5px;
256
- }
257
-
258
- .btn {
259
- padding: 10px 15px;
260
- }
261
- }
262
- </style>
263
- </head>
264
- <body>
265
- <div id="message-box">
266
- <span id="message-text"></span>
267
- </div>
268
-
269
- <div id="loading-indicator">
270
- <i class="fas fa-spinner fa-spin"></i> Processing...
271
- </div>
272
-
273
- <div class="container">
274
- <header class="app-header">
275
- <h1 class="app-title">3D Pose Estimation App</h1>
276
- <p class="app-description">Analyze and visualize human pose in real-time</p>
277
- </header>
278
-
279
- <div class="card-grid">
280
- <div class="card">
281
- <div class="card-header">
282
- <i class="fas fa-video"></i> Video Input
283
- </div>
284
- <div class="card-body">
285
- <div class="view" id="video-container">
286
- <img id="video-feed" src="{{ url_for('video_feed') }}" alt="Video Feed">
287
- </div>
288
- <div class="controls">
289
- <div class="slider-container">
290
- <label for="video-scale"><i class="fas fa-search"></i> Scale:</label>
291
- <input type="range" min="0.5" max="2" step="0.1" value="1" class="slider" id="video-scale" aria-label="Video Scale Slider">
292
- </div>
293
- <label for="video-upload" class="btn">
294
- <i class="fas fa-upload"></i> Upload Video
295
- <input type="file" id="video-upload" accept="video/*" style="display: none;">
296
- </label>
297
- </div>
298
- </div>
299
- </div>
300
-
301
- <div class="card">
302
- <div class="card-header">
303
- <i class="fas fa-cube"></i> 3D Model Visualization
304
- </div>
305
- <div class="card-body">
306
- <div class="view" id="model-container"></div>
307
- <div class="controls">
308
- <div class="slider-container">
309
- <label for="model-scale"><i class="fas fa-search"></i> Scale:</label>
310
- <input type="range" min="0.5" max="2" step="0.1" value="1" class="slider" id="model-scale" aria-label="3D Model Scale Slider">
311
- </div>
312
- </div>
313
- </div>
314
- </div>
315
-
316
- <div class="card">
317
- <div class="card-header">
318
- <i class="fas fa-tachometer-alt"></i> Speed Analysis
319
- </div>
320
- <div class="card-body">
321
- <canvas id="speedChart" aria-label="Speed Chart" role="img"></canvas>
322
- </div>
323
- </div>
324
-
325
- <div class="card">
326
- <div class="card-header">
327
- <i class="fas fa-bolt"></i> Acceleration Analysis
328
- </div>
329
- <div class="card-body">
330
- <canvas id="accelerationChart" aria-label="Acceleration Chart" role="img"></canvas>
331
- </div>
332
- </div>
333
- </div>
334
- </div>
335
-
336
-
337
- <script>
338
- // 连接到Socket.IO服务器
339
- const socket = io();
340
-
341
- // Three.js设置
342
- const scene = new THREE.Scene();
343
- scene.background = new THREE.Color(0xf0f4f8);
344
- const aspect = 4 / 3;
345
- const camera = new THREE.PerspectiveCamera(60, aspect, 0.1, 1000);
346
- const renderer = new THREE.WebGLRenderer({ antialias: true });
347
- const modelContainer = document.getElementById('model-container');
348
- renderer.setSize(modelContainer.clientWidth, modelContainer.clientWidth / aspect);
349
- modelContainer.appendChild(renderer.domElement);
350
-
351
- camera.position.set(0, 0, 1.5);
352
- camera.lookAt(0, 0, 0);
353
-
354
- // 添加灯光
355
- const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
356
- scene.add(ambientLight);
357
- const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
358
- directionalLight.position.set(1, 1, 1);
359
- scene.add(directionalLight);
360
-
361
- // 创建用于标记关键点的球体
362
- const spheres = [];
363
- for (let i = 0; i < 33; i++) {
364
- const geometry = new THREE.SphereGeometry(0.015, 32, 32);
365
- const material = new THREE.MeshPhongMaterial({
366
- color: 0x3498db,
367
- shininess: 100,
368
- specular: 0x111111
369
- });
370
- const sphere = new THREE.Mesh(geometry, material);
371
- scene.add(sphere);
372
- spheres.push(sphere);
373
- }
374
-
375
- // 创建圆柱体的函数
376
- const cylinders = [];
377
- function createCylinder(point1, point2) {
378
- const direction = new THREE.Vector3().subVectors(point2, point1);
379
- const cylinder = new THREE.Mesh(
380
- new THREE.CylinderGeometry(0.007, 0.007, direction.length(), 8, 1),
381
- new THREE.MeshPhongMaterial({
382
- color: 0x2c3e50,
383
- shininess: 100,
384
- specular: 0x111111
385
- })
386
- );
387
- cylinder.position.copy(point1);
388
- cylinder.position.addScaledVector(direction, 0.5);
389
- cylinder.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction.normalize());
390
- scene.add(cylinder);
391
- return cylinder;
392
- }
393
-
394
- // 初始化图表
395
- const speedChartCtx = document.getElementById('speedChart').getContext('2d');
396
- const accelerationChartCtx = document.getElementById('accelerationChart').getContext('2d');
397
- const speedChart = new Chart(speedChartCtx, {
398
- type: 'line',
399
- data: {
400
- labels: [],
401
- datasets: [{
402
- label: 'Speed',
403
- data: [],
404
- borderColor: 'rgba(75, 192, 192, 1)',
405
- borderWidth: 1,
406
- fill: false
407
- }]
408
- },
409
- options: {
410
- scales: {
411
- y: { beginAtZero: true }
412
- }
413
- }
414
- });
415
- const accelerationChart = new Chart(accelerationChartCtx, {
416
- type: 'line',
417
- data: {
418
- labels: [],
419
- datasets: [{
420
- label: 'Acceleration',
421
- data: [],
422
- borderColor: 'rgba(255, 99, 132, 1)',
423
- borderWidth: 1,
424
- fill: false
425
- }]
426
- },
427
- options: {
428
- scales: {
429
- y: { beginAtZero: true }
430
- }
431
- }
432
- });
433
-
434
- // 更新图表的函数
435
- function updateChart(chart, newData) {
436
- const currentTime = new Date().toLocaleTimeString();
437
- chart.data.labels.push(currentTime);
438
- chart.data.datasets.forEach((dataset) => {
439
- dataset.data.push(newData.reduce((a, b) => a + b, 0) / newData.length);
440
- });
441
- chart.update();
442
- }
443
-
444
- // 监听从服务器发送的pose_data事件
445
- socket.on('pose_data', function(data) {
446
- // 更新3D模型
447
- const landmarks = data.landmarks;
448
- landmarks.forEach((coord, index) => {
449
- spheres[index].position.set((coord[0] - 0.5) * 2, -(coord[1] - 0.5) * 2, -coord[2] * 0.5);
450
- });
451
-
452
- // 清除现有的圆柱体
453
- cylinders.forEach(cylinder => scene.remove(cylinder));
454
- cylinders.length = 0;
455
-
456
- // 创建新的圆柱体
457
- const connections = [
458
- // Face
459
- [0, 1], [1, 4], [4, 7], [7, 8], [8, 5], [5, 2], [2, 0],
460
- [0, 9], [9, 10], [0, 10],
461
- // Arms
462
- [11, 13], [13, 15], [15, 17], [15, 19], [15, 21],
463
- [12, 14], [14, 16], [16, 18], [16, 20], [16, 22],
464
- // Body
465
- [11, 12], [11, 23], [12, 24], [23, 24],
466
- // Legs
467
- [23, 25], [25, 27], [27, 29],
468
- [24, 26], [26, 28], [28, 30], [28, 32]
469
- ];
470
-
471
- connections.forEach(([i, j]) => {
472
- cylinders.push(createCylinder(spheres[i].position, spheres[j].position));
473
- });
474
-
475
- // 更新速度和加速度图表
476
- if (data.velocities) {
477
- updateChart(speedChart, data.velocities);
478
- }
479
- if (data.accelerations) {
480
- updateChart(accelerationChart, data.accelerations);
481
- }
482
- });
483
-
484
- function animate() {
485
- requestAnimationFrame(animate);
486
- renderer.render(scene, camera);
487
- }
488
- animate();
489
-
490
- function showMessage(message) {
491
- const messageBox = document.getElementById('message-box');
492
- const messageText = document.getElementById('message-text');
493
- messageText.textContent = message;
494
- messageBox.style.display = 'block';
495
-
496
- // 2秒后自动隐藏
497
- setTimeout(() => {
498
- messageBox.style.display = 'none';
499
- }, 2000);
500
- }
501
-
502
- // 视频上传处理
503
- document.getElementById('video-upload').addEventListener('change', function(e) {
504
- const file = e.target.files[0];
505
- if (file) {
506
- const formData = new FormData();
507
- formData.append('video', file);
508
-
509
- // 显示加载指示器
510
- showLoadingIndicator(true);
511
-
512
- fetch('/upload_video', {
513
- method: 'POST',
514
- body: formData
515
- }).then(response => response.json())
516
- .then(data => {
517
- showLoadingIndicator(false); // 隐藏加载指示器
518
- if (data.success) {
519
- showMessage(data.message);
520
- document.getElementById('video-feed').src = "{{ url_for('video_feed') }}?" + new Date().getTime();
521
- } else {
522
- showMessage('Error uploading video');
523
- }
524
- }).catch(error => {
525
- showLoadingIndicator(false); // 隐藏加载指示器
526
- showMessage('Upload failed: ' + error.message);
527
- });
528
- }
529
- });
530
-
531
- // 切换到相机模式
532
- document.getElementById('camera-switch').addEventListener('click', function() {
533
- showLoadingIndicator(true); // 显示加载指示器
534
-
535
- fetch('/switch_to_camera', {
536
- method: 'POST'
537
- }).then(response => response.json())
538
- .then(data => {
539
- showLoadingIndicator(false); // 隐藏加载指示器
540
- if (data.success) {
541
- showMessage(data.message);
542
- document.getElementById('video-feed').src = "{{ url_for('video_feed') }}?" + new Date().getTime();
543
- } else {
544
- showMessage('Error switching to camera');
545
- }
546
- }).catch(error => {
547
- showLoadingIndicator(false); // 隐藏加载指示器
548
- showMessage('Switch failed: ' + error.message);
549
- });
550
- });
551
- function showLoadingIndicator(isLoading) {
552
- const loadingIndicator = document.getElementById('loading-indicator');
553
- loadingIndicator.style.display = isLoading ? 'block' : 'none';
554
- }
555
-
556
-
557
- // 缩放处理
558
- document.getElementById('video-scale').addEventListener('input', function(e) {
559
- document.getElementById('video-feed').style.transform = `scale(${e.target.value})`;
560
- });
561
-
562
- document.getElementById('model-scale').addEventListener('input', function(e) {
563
- const newWidth = modelContainer.clientWidth * e.target.value;
564
- const newHeight = newWidth / aspect;
565
- renderer.setSize(newWidth, newHeight);
566
- });
567
-
568
- // 窗口大小调整处理
569
- window.addEventListener('resize', function() {
570
- const width = modelContainer.clientWidth;
571
- const height = width / aspect;
572
- camera.aspect = aspect;
573
- camera.updateProjectionMatrix();
574
- renderer.setSize(width, height);
575
- });
576
-
577
- navigator.mediaDevices.getUserMedia({
578
- video: {
579
- facingMode: 'user',
580
- width: { ideal: 640 },
581
- height: { ideal: 480 },
582
- frameRate: { ideal: 15 }
583
- }
584
- })
585
- .then(stream => {
586
- const video = document.createElement('video');
587
- video.srcObject = stream;
588
- video.play();
589
- const videoFeed = document.getElementById('video-feed');
590
- videoFeed.srcObject = stream;
591
- videoFeed.play();
592
- })
593
- .catch(error => {
594
- console.error('Error accessing camera: ', error);
595
- });
596
-
597
-
598
-
599
- </script>
600
-
601
- </body>
602
- </html>