episode-visualizer / index.html
ghosthcp516's picture
Upload index.html (#4)
05b7713 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Motion Capture Visualization</title>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.0/papaparse.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap" rel="stylesheet">
<!--'Orbitron', sans-serif;Arial, Helvetica, sans-serif-->
<style>
body {
margin: 0;
padding: 20px;
font-family: 'Orbitron', 'Arial', sans-serif;
background-color: #1a1a1a;
color: #ffffff;
min-height: 90vh;
}
h1 {
width: 1280px;
color: #ffffff;
margin: 0 auto 30px;
font-size: 30px;
text-shadow: 2px 2px 4px rgba(255, 255, 255, 0.5)
}
.main-container {
display: grid;
grid-template-columns: 250px 1fr;
gap: 20px;
max-width: 1280px;
margin: 0 auto;
height: calc(100vh - 80px);
}
/* Panel A: Episodes List */
#episodes-container {
background-color: #2a2a2a;
border-radius: 10px;
padding: 15px;
height: 90%;
overflow-y: auto;
}
#episodes-title {
color: #ffffff;
margin-top: 0;
padding-bottom: 10px;
border-bottom: 1px solid #3a3a3a;
}
#episodes-grid {
display: flex;
flex-direction: column;
gap: 4px;
margin-top: 15px;
}
.episode-radio {
padding: 8px;
border-radius: 6px;
transition: background-color 0.2s;
}
.episode-radio:hover {
background-color: #3a3a3a;
}
.episode-radio input[type="radio"] {
margin-right: 10px;
}
.episode-radio label {
cursor: pointer;
}
/* Right Content Container */
.content-container {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
gap: 20px;
height: 100%;
}
/* Video Container */
.video-container {
display: flex;
flex-direction: column;
align-items: center;
background-color: #2a2a2a;
border-radius: 10px;
padding: 22px;
height: fit-content;
}
video {
width: 720px;
border-radius: 6px;
margin-bottom: 15px;
background-color: #000;
}
.controls {
display: flex;
gap: 10px;
justify-content: center;
padding: 10px 0;
}
.controls button {
background-color: #3a3a3a;
border: none;
border-radius: 6px;
padding: 4px 8px;
cursor: pointer;
transition: background-color 0.2s;
font-size: 18px;
}
.controls button:hover {
background-color: #4a4a4a;
}
/* Plot Container */
#plotDiv {
background-color: #2a2a2a;
border-radius: 10px;
padding: 15px;
width: 720px;
height: 550px;
}
#loadingIndicator {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.8);
padding: 20px 40px;
border-radius: 10px;
display: none;
}
/* Scrollbar Styling */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #2a2a2a;
}
::-webkit-scrollbar-thumb {
background: #4a4a4a;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #5a5a5a;
}
</style>
</head>
<body>
<h1>Motion Capture Visualization</h1>
<div class="main-container">
<!-- Panel A: Episodes List -->
<div id="episodes-container">
<h3 id="episodes-title">Episodes</h3>
<div id="episodes-grid"></div>
</div>
<div class="content-container">
<!-- Panel B: Video Player -->
<div class="video-container">
<video id="laptopVideo">
<source src="https://huggingface.co/datasets/cyberorigin/fold_towels/resolve/main/Video/video.mp4"
type="video/mp4">
Your browser does not support the video tag.
</video>
<div class="controls">
<button id="playPauseBtn">▶️</button>
<button id="rewindBtn"></button>
<button id="forwardBtn"></button>
<button id="restartBtn">↩️</button>
</div>
<!-- Panel C: Plot Visualization -->
<div id="plotDiv"></div>
</div>
</div>
</div>
<div id="loadingIndicator">Loading...</div>
<script>
let csvFilePath = 'https://huggingface.co/datasets/cyberorigin/fold_towels/resolve/main/MoCap/mocap.csv';
const body_part_names = [
'Left Shoulder', 'Right Upper Arm', 'Left Lower Leg', 'Spine1', 'Right Upper Leg',
'Spine3', 'Right Lower Arm', 'Left Foot', 'Right Lower Leg', 'Right Shoulder',
'Left Hand', 'Left Upper Leg', 'Right Foot', 'Spine', 'Spine2', 'Left Lower Arm',
'Left Toe', 'Neck', 'Right Hand', 'Right Toe', 'Head', 'Left Upper Arm', 'Hips'
];
const laptopVideo = document.getElementById('laptopVideo');
const playPauseBtn = document.getElementById('playPauseBtn');
const rewindBtn = document.getElementById('rewindBtn');
const forwardBtn = document.getElementById('forwardBtn');
const restartBtn = document.getElementById('restartBtn');
const radioButtons = document.querySelectorAll('input[name="videoOption"]');
const episodeContainer = document.getElementById('episodes-container');
const episodesGrid = document.getElementById('episodes-grid');
document.addEventListener('DOMContentLoaded', loadEpisodesCsv);
function loadEpisodesCsv() {
fetch("https://huggingface.co/datasets/cyberorigin/test/resolve/main/episodes.csv")
.then(response => response.text())
.then(data => {
const lines = data.split('\n');
processEpisodes(lines);
})
.catch(error => console.error('Error loading CSV file:', error));
}
function processEpisodes(lines) {
episodesGrid.innerHTML = '';
lines.forEach((line, index) => {
if (line.trim() !== '' && index != 0) {
const id = line.split(',')[0];
const episodeNumber = index;
const radioDiv = document.createElement('div');
radioDiv.className = 'episode-radio';
const radio = document.createElement('input');
radio.type = 'radio';
radio.id = `episode${episodeNumber}`;
radio.name = 'episodeGroup';
radio.addEventListener('change', () => updateVideoAndCSVSource(id));
const label = document.createElement('label');
label.htmlFor = `episode${episodeNumber}`;
label.textContent = `Episode ${episodeNumber}`;
radioDiv.appendChild(radio);
radioDiv.appendChild(label);
episodesGrid.appendChild(radioDiv);
}
});
}
let totalEpisodes = 100;
// const container = document.getElementById('episodes-container')
let animationFrameId;
let isPlaying = false;
function togglePlayPause() {
if (!isPlaying) {
laptopVideo.play();
playPauseBtn.textContent = '⏸️';
isPlaying = true;
animate3DVisualization();
} else {
laptopVideo.pause();
playPauseBtn.textContent = '▶️';
isPlaying = false;
cancelAnimationFrame(animationFrameId);
}
}
function rewind() {
laptopVideo.currentTime -= 5;
update3DVisualization();
}
function forward() {
laptopVideo.currentTime += 5;
update3DVisualization();
}
function restart() {
laptopVideo.currentTime = 0;
update3DVisualization();
}
playPauseBtn.addEventListener('click', togglePlayPause);
rewindBtn.addEventListener('click', rewind);
forwardBtn.addEventListener('click', forward);
restartBtn.addEventListener('click', restart);
function getCoordinates(data, coordinate) {
return body_part_names.map(part => parseFloat(data[`${part}_${coordinate}`]));
}
let frames;
function processData(results) {
console.log("Processing data:", results);
const motion_capture_data = results.data.filter((_, index) => index % 3 === 0);
frames = motion_capture_data.map((row, index) => ({
name: index.toString(),
data: [{
x: getCoordinates(row, 'x'),
y: getCoordinates(row, 'y'),
z: getCoordinates(row, 'z'),
mode: 'markers',
type: 'scatter3d',
marker: { size: 4.8, color: "blue" }
}]
}));
if (frames.length === 0) {
console.error("No frames were created from the data");
return;
}
const initialFrame = frames[0].data[0]
// Config canvas layout styles
const layout = {
title: {
text: '3D Motion Capture',
font: {
color: 'white',
size: 20
},
x: 0.5,
y: 1.2
},
paper_bgcolor: 'black',
plot_bgcolor: 'black',
scene: {
xaxis: {
title: 'X',
color: 'white',
gridcolor: 'gray'
},
yaxis: {
title: 'Y',
color: 'white',
gridcolor: 'gray'
},
zaxis: {
title: 'Z',
color: 'white',
gridcolor: 'gray'
},
bgcolor: 'black'
},
font: {color: 'white'},
margin: {
l: 2,
r: 2,
b: 2,
t: 50,
pad: 4,
},
}
Plotly.newPlot('plotDiv', [initialFrame], layout)
}
function update3DVisualization() {
if (!frames) return
const currentTime = laptopVideo.currentTime
const totalDuration = laptopVideo.duration
const frameIndex = Math.floor((currentTime / totalDuration) * frames.length)
const frame = frames[Math.min(frameIndex, frames.length - 1)]
Plotly.animate('plotDiv', frame, {
transition: {duration: 0},
frame: {duration: 0, redraw: true},
})
}
function animate3DVisualization() {
update3DVisualization()
if (isPlaying) {
animationFrameId = requestAnimationFrame(animate3DVisualization)
}
}
//function updateVideoAndCSVSource() {
//const selectedOption = document.querySelector('input[name="videoOption"]:checked').value;
//const videoUrl = `https://huggingface.co/datasets/cyberorigin/${selectedOption}/resolve/main/Video/video.mp4`;
//if (selectedOption != "twist_the_tube") {
// csvFilePath = `https://huggingface.co/datasets/cyberorigin/${selectedOption}/resolve/main/MoCap/mocap.csv`;
//}
//else {
// csvFilePath = `https://huggingface.co/datasets/cyberorigin/${selectedOption}/resolve/main/MoCap/MoCap.csv`;
//}
function updateVideoAndCSVSource(id) {
const selectedOption = document.querySelector('input[name="episodeGroup"]:checked').value;
const videoUrl = `https://huggingface.co/datasets/cyberorigin/pick_and_place/resolve/main/color/${id}.mp4`;
csvFilePath = `https://huggingface.co/datasets/cyberorigin/pick_and_place/resolve/main/motion_capture/${id}.csv`;
laptopVideo.pause();
laptopVideo.querySelector('source').src = videoUrl;
laptopVideo.load();
isPlaying = false;
playPauseBtn.textContent = '▶️';
// Fetch and process the new CSV data
fetchAndProcessActionCSV();
}
function fetchAndProcessActionCSV() {
fetch(csvFilePath)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.text();
})
.then(csvString => {
console.log("CSV data loaded successfully");
Papa.parse(csvString, {
header: true,
dynamicTyping: true,
complete: processData
});
})
.catch(error => console.error('Error loading the CSV file:', error));
}
//radioButtons.forEach(radio => {
// radio.addEventListener('change', updateVideoAndCSVSource);
//});
// Initial CSV fetch and processing
fetchAndProcessActionCSV();
</script>
</body>
</html>