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

Update templates/video_app.html

Browse files
Files changed (1) hide show
  1. templates/video_app.html +207 -268
templates/video_app.html CHANGED
@@ -335,209 +335,178 @@
335
 
336
 
337
  <script>
338
- // Three.js setup
339
- const scene = new THREE.Scene();
340
- scene.background = new THREE.Color(0xf0f4f8);
341
- const aspect = 4 / 3;
342
- const camera = new THREE.PerspectiveCamera(60, aspect, 0.1, 1000);
343
- const renderer = new THREE.WebGLRenderer({ antialias: true });
344
- const modelContainer = document.getElementById('model-container');
345
- renderer.setSize(modelContainer.clientWidth, modelContainer.clientWidth / aspect);
346
- modelContainer.appendChild(renderer.domElement);
347
-
348
- camera.position.set(0, 0, 1.5);
349
- camera.lookAt(0, 0, 0);
350
-
351
- // Add lighting
352
- const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
353
- scene.add(ambientLight);
354
- const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
355
- directionalLight.position.set(1, 1, 1);
356
- scene.add(directionalLight);
357
-
358
- // Create spheres for keypoints
359
- const spheres = [];
360
- for (let i = 0; i < 33; i++) {
361
- const geometry = new THREE.SphereGeometry(0.015, 32, 32);
362
- const material = new THREE.MeshPhongMaterial({
363
- color: 0x3498db,
364
- shininess: 100,
365
- specular: 0x111111
366
- });
367
- const sphere = new THREE.Mesh(geometry, material);
368
- scene.add(sphere);
369
- spheres.push(sphere);
370
- }
371
-
372
- // Function to create cylinders
373
- const cylinders = [];
374
- function createCylinder(point1, point2) {
375
- const direction = new THREE.Vector3().subVectors(point2, point1);
376
- const cylinder = new THREE.Mesh(
377
- new THREE.CylinderGeometry(0.007, 0.007, direction.length(), 8, 1),
378
- new THREE.MeshPhongMaterial({
379
- color: 0x2c3e50,
380
  shininess: 100,
381
  specular: 0x111111
382
- })
383
- );
384
- cylinder.position.copy(point1);
385
- cylinder.position.addScaledVector(direction, 0.5);
386
- cylinder.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction.normalize());
387
- scene.add(cylinder);
388
- return cylinder;
389
- }
390
-
391
- // Initialize charts
392
- const speedChartCtx = document.getElementById('speedChart').getContext('2d');
393
- const accelerationChartCtx = document.getElementById('accelerationChart').getContext('2d');
394
- const speedChart = new Chart(speedChartCtx, {
395
- type: 'line',
396
- data: {
397
- labels: [],
398
- datasets: [{
399
- label: 'Speed',
400
- data: [],
401
- borderColor: 'rgba(75, 192, 192, 1)',
402
- borderWidth: 1,
403
- fill: false
404
- }]
405
- },
406
- options: {
407
- scales: {
408
- y: { beginAtZero: true }
409
- },
410
- animation: false
411
  }
412
- });
413
- const accelerationChart = new Chart(accelerationChartCtx, {
414
- type: 'line',
415
- data: {
416
- labels: [],
417
- datasets: [{
418
- label: 'Acceleration',
419
- data: [],
420
- borderColor: 'rgba(255, 99, 132, 1)',
421
- borderWidth: 1,
422
- fill: false
423
- }]
424
- },
425
- options: {
426
- scales: {
427
- y: { beginAtZero: true }
428
- },
429
- animation: false
430
  }
431
- });
432
 
433
- // Global variables
434
- let allResults = [];
435
- let currentFrame = 0;
436
- let fps = 30;
437
- let isPlaying = false;
438
- let videoElement;
439
-
440
- // Function to update 3D model
441
- function update3DModel(landmarks) {
442
- landmarks.forEach((coord, index) => {
443
- spheres[index].position.set((coord[0] - 0.5) * 2, -(coord[1] - 0.5) * 2, -coord[2] * 0.5);
 
 
 
 
 
 
 
 
 
444
  });
445
-
446
- // Clear existing cylinders
447
- cylinders.forEach(cylinder => scene.remove(cylinder));
448
- cylinders.length = 0;
449
-
450
- // Create new cylinders
451
- const connections = [
452
- // Face
453
- [0, 1], [1, 4], [4, 7], [7, 8], [8, 5], [5, 2], [2, 0],
454
- [0, 9], [9, 10], [0, 10],
455
- // Arms
456
- [11, 13], [13, 15], [15, 17], [15, 19], [15, 21],
457
- [12, 14], [14, 16], [16, 18], [16, 20], [16, 22],
458
- // Body
459
- [11, 12], [11, 23], [12, 24], [23, 24],
460
- // Legs
461
- [23, 25], [25, 27], [27, 29],
462
- [24, 26], [26, 28], [28, 30], [28, 32]
463
- ];
464
-
465
- connections.forEach(([i, j]) => {
466
- cylinders.push(createCylinder(spheres[i].position, spheres[j].position));
467
  });
468
- }
469
 
470
- // Function to update charts
471
- function updateCharts(frame) {
472
- const frameData = allResults[frame];
473
- if (frameData) {
474
- speedChart.data.labels.push(frame);
475
- speedChart.data.datasets[0].data.push(frameData.velocities.reduce((a, b) => a + b, 0) / frameData.velocities.length);
476
-
477
- accelerationChart.data.labels.push(frame);
478
- accelerationChart.data.datasets[0].data.push(frameData.accelerations.reduce((a, b) => a + b, 0) / frameData.accelerations.length);
479
-
480
- // Keep only the last 100 data points
481
- if (speedChart.data.labels.length > 100) {
482
- speedChart.data.labels.shift();
483
- speedChart.data.datasets[0].data.shift();
484
- }
485
- if (accelerationChart.data.labels.length > 100) {
486
- accelerationChart.data.labels.shift();
487
- accelerationChart.data.datasets[0].data.shift();
488
- }
489
-
490
- speedChart.update();
491
- accelerationChart.update();
492
  }
493
- }
494
 
495
- // Function to animate the scene
496
- function animate() {
497
- requestAnimationFrame(animate);
498
- renderer.render(scene, camera);
499
-
500
- if (isPlaying && allResults.length > 0) {
501
- const frameData = allResults[currentFrame];
502
- if (frameData) {
503
- update3DModel(frameData.landmarks);
504
- updateCharts(currentFrame);
505
- currentFrame = (currentFrame + 1) % allResults.length;
506
- if (videoElement) {
507
- videoElement.currentTime = currentFrame / fps;
508
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
509
  }
510
- }
511
- }
 
 
512
 
513
- // Start animation
514
- animate();
 
 
 
515
 
516
- // Function to show messages
517
- function showMessage(message) {
518
  const messageBox = document.getElementById('message-box');
519
  const messageText = document.getElementById('message-text');
520
  messageText.textContent = message;
521
  messageBox.style.display = 'block';
522
 
 
523
  setTimeout(() => {
524
  messageBox.style.display = 'none';
525
  }, 2000);
526
  }
527
 
528
- // Function to show/hide loading indicator
529
- function showLoadingIndicator(isLoading) {
530
- const loadingIndicator = document.getElementById('loading-indicator');
531
- loadingIndicator.style.display = isLoading ? 'block' : 'none';
532
- }
533
-
534
- // Video upload handling
535
  document.getElementById('video-upload').addEventListener('change', function(e) {
536
  const file = e.target.files[0];
537
  if (file) {
538
  const formData = new FormData();
539
  formData.append('video', file);
540
 
 
541
  showLoadingIndicator(true);
542
 
543
  fetch('/upload_video', {
@@ -545,119 +514,89 @@ document.getElementById('video-upload').addEventListener('change', function(e) {
545
  body: formData
546
  }).then(response => response.json())
547
  .then(data => {
548
- showLoadingIndicator(false);
549
  if (data.success) {
550
  showMessage(data.message);
551
- loadResults();
552
  } else {
553
  showMessage('Error uploading video');
554
  }
555
  }).catch(error => {
556
- showLoadingIndicator(false);
557
  showMessage('Upload failed: ' + error.message);
558
  });
559
  }
560
  });
561
 
562
- // Function to load results
563
- function loadResults() {
564
- fetch('/get_results')
565
- .then(response => response.json())
566
- .then(data => {
567
- allResults = data.results;
568
- fps = data.fps;
569
- currentFrame = 0;
570
-
571
- // Reset charts
572
- speedChart.data.labels = [];
573
- speedChart.data.datasets[0].data = [];
574
- accelerationChart.data.labels = [];
575
- accelerationChart.data.datasets[0].data = [];
576
-
577
- // Load video
578
- videoElement = document.createElement('video');
579
- videoElement.src = '/uploads/temp_video.mp4';
580
- videoElement.load();
581
-
582
- const videoContainer = document.getElementById('video-container');
583
- videoContainer.innerHTML = '';
584
- videoContainer.appendChild(videoElement);
585
-
586
- // Add play/pause button
587
- const playPauseBtn = document.createElement('button');
588
- playPauseBtn.textContent = 'Play';
589
- playPauseBtn.onclick = togglePlayPause;
590
- videoContainer.appendChild(playPauseBtn);
591
-
592
- showMessage('Video and results loaded successfully');
593
- })
594
- .catch(error => {
595
- showMessage('Error loading results: ' + error.message);
596
- });
597
  }
598
 
599
- // Function to toggle play/pause
600
- function togglePlayPause() {
601
- isPlaying = !isPlaying;
602
- const playPauseBtn = document.querySelector('#video-container button');
603
- playPauseBtn.textContent = isPlaying ? 'Pause' : 'Play';
604
- if (isPlaying) {
605
- videoElement.play();
606
- } else {
607
- videoElement.pause();
608
- }
609
- }
610
 
611
- // Sync video with 3D model and charts
612
- videoElement.addEventListener('timeupdate', function() {
613
- currentFrame = Math.floor(videoElement.currentTime * fps);
614
- const frameData = allResults[currentFrame];
615
- if (frameData) {
616
- update3DModel(frameData.landmarks);
617
- updateCharts(currentFrame);
618
- }
619
- });
620
 
621
- // Slider for video and 3D model scale
622
- document.getElementById('video-scale').addEventListener('input', function(e) {
623
- videoElement.style.transform = `scale(${e.target.value})`;
624
- });
 
625
 
626
- document.getElementById('model-scale').addEventListener('input', function(e) {
627
- const newWidth = modelContainer.clientWidth * e.target.value;
628
- const newHeight = newWidth / aspect;
629
- renderer.setSize(newWidth, newHeight);
630
- });
 
 
 
631
 
632
- // Window resize handling
633
- window.addEventListener('resize', function() {
634
- const width = modelContainer.clientWidth;
635
- const height = width / aspect;
636
- camera.aspect = aspect;
637
- camera.updateProjectionMatrix();
638
- renderer.setSize(width, height);
 
 
 
 
 
 
 
 
 
 
 
639
  });
640
 
641
- // Initialize the application
642
- document.addEventListener('DOMContentLoaded', function() {
643
- // Check if there's a previously uploaded video
644
- fetch('/get_results')
645
- .then(response => {
646
- if (response.ok) {
647
- return response.json();
648
- } else {
649
- throw new Error('No previous results found');
650
- }
651
- })
652
- .then(data => {
653
- allResults = data.results;
654
- fps = data.fps;
655
- loadResults();
656
- })
657
- .catch(error => {
658
- console.log('No previous video found. Please upload a new video.');
659
- });
660
- });
661
  </script>
 
662
  </body>
663
  </html>
 
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', {
 
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>