Files
JDClone/example_client.html

364 lines
13 KiB
HTML
Raw Permalink Normal View History

2025-05-04 03:56:07 -03:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pose Detection Visualization</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
color: #333;
}
.page-container {
max-width: 1200px;
margin: 0 auto;
}
h1, h2, h3 {
color: #2c3e50;
}
.container {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin-bottom: 30px;
}
.panel {
background-color: white;
border-radius: 8px;
padding: 15px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.video-container {
flex: 1;
min-width: 300px;
}
.video-feed {
width: 100%;
border-radius: 4px;
margin-bottom: 10px;
}
.canvas-container {
position: relative;
width: 100%;
min-height: 480px;
}
#skeletonCanvas {
border: 1px solid #ddd;
background-color: #f8f9fa;
border-radius: 4px;
}
.data-container {
flex: 1;
min-width: 300px;
max-width: 500px;
}
.raw-data {
background-color: #282c34;
color: #abb2bf;
padding: 15px;
border-radius: 4px;
font-family: monospace;
overflow: auto;
max-height: 400px;
font-size: 14px;
}
.controls {
margin-bottom: 20px;
padding: 15px;
background-color: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
button {
background-color: #3498db;
color: white;
border: none;
padding: 8px 15px;
border-radius: 4px;
cursor: pointer;
margin-right: 10px;
font-size: 14px;
}
button:hover {
background-color: #2980b9;
}
.connection-status {
display: inline-block;
margin-left: 15px;
font-weight: bold;
}
.connected {
color: #27ae60;
}
.disconnected {
color: #e74c3c;
}
select {
padding: 8px;
border-radius: 4px;
border: 1px solid #ddd;
margin-right: 10px;
}
footer {
margin-top: 30px;
text-align: center;
color: #7f8c8d;
font-size: 14px;
}
</style>
</head>
<body>
<div class="page-container">
<h1>Pose Detection Visualization</h1>
<div class="controls">
<button id="connectBtn">Connect to Server</button>
<select id="colorTheme">
<option value="default">Default Colors</option>
<option value="neon">Neon</option>
<option value="pastel">Pastel</option>
<option value="grayscale">Grayscale</option>
</select>
<span id="connectionStatus" class="connection-status disconnected">Disconnected</span>
</div>
<div class="container">
<div class="panel video-container">
<h2>Server Video Feeds</h2>
<h3>Raw Video Feed</h3>
<img id="rawVideoFeed" class="video-feed" src="http://localhost:5000/video_feed" alt="Raw Video Feed" />
<h3>Annotated Video Feed</h3>
<img id="annotatedVideoFeed" class="video-feed" src="http://localhost:5000/video_feed/annotated" alt="Annotated Video Feed" />
</div>
<div class="panel data-container">
<h2>Raw Landmark Data</h2>
<pre id="rawData" class="raw-data">Waiting for data...</pre>
</div>
</div>
<div class="panel">
<h2>Custom Skeleton Visualization</h2>
<div class="canvas-container">
<canvas id="skeletonCanvas" width="640" height="480"></canvas>
</div>
</div>
<footer>
<p>Powered by MediaPipe, OpenCV, and Socket.IO</p>
</footer>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<script>
// DOM Elements
const rawDataElement = document.getElementById('rawData');
const skeletonCanvas = document.getElementById('skeletonCanvas');
const ctx = skeletonCanvas.getContext('2d');
const connectBtn = document.getElementById('connectBtn');
const connectionStatus = document.getElementById('connectionStatus');
const colorThemeSelect = document.getElementById('colorTheme');
// Set canvas dimensions
const canvasWidth = skeletonCanvas.width;
const canvasHeight = skeletonCanvas.height;
// Socket connection
let socket;
let isConnected = false;
let landmarksData = null;
// Color themes
const colorThemes = {
default: {
background: '#f8f9fa',
joints: '#e74c3c',
torso: '#e74c3c',
arms: '#3498db',
legs: '#2ecc71',
shoulders: '#f39c12',
head: '#9b59b6'
},
neon: {
background: '#121212',
joints: '#ff00ff',
torso: '#ff0099',
arms: '#00ffff',
legs: '#00ff00',
shoulders: '#ffff00',
head: '#ff9900'
},
pastel: {
background: '#f8f9fa',
joints: '#f08080',
torso: '#f08080',
arms: '#98d8c8',
legs: '#b5ead7',
shoulders: '#ffdac1',
head: '#c7ceea'
},
grayscale: {
background: '#ffffff',
joints: '#333333',
torso: '#444444',
arms: '#666666',
legs: '#888888',
shoulders: '#aaaaaa',
head: '#555555'
}
};
let currentColorTheme = colorThemes.default;
// Connect to the Socket.IO server
function connectToServer() {
if (isConnected) {
socket.disconnect();
isConnected = false;
connectBtn.textContent = 'Connect to Server';
connectionStatus.textContent = 'Disconnected';
connectionStatus.className = 'connection-status disconnected';
return;
}
socket = io('http://localhost:5000');
socket.on('connect', function() {
isConnected = true;
connectBtn.textContent = 'Disconnect';
connectionStatus.textContent = 'Connected';
connectionStatus.className = 'connection-status connected';
console.log('Connected to server');
});
socket.on('disconnect', function() {
isConnected = false;
connectBtn.textContent = 'Connect to Server';
connectionStatus.textContent = 'Disconnected';
connectionStatus.className = 'connection-status disconnected';
console.log('Disconnected from server');
});
socket.on('landmarks', function(data) {
landmarksData = JSON.parse(data);
// Update raw data display
rawDataElement.textContent = JSON.stringify(landmarksData, null, 2);
// Draw skeleton
drawSkeleton(landmarksData);
});
}
// Draw skeleton on canvas
function drawSkeleton(data) {
// Clear canvas
ctx.fillStyle = currentColorTheme.background;
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
if (!data || !data.landmarks || data.landmarks.length === 0) {
// No landmarks to draw
ctx.font = '20px Arial';
ctx.fillStyle = '#999';
ctx.textAlign = 'center';
ctx.fillText('No pose detected', canvasWidth/2, canvasHeight/2);
return;
}
const imageWidth = data.image_width;
const imageHeight = data.image_height;
// Scale factors to map coordinates to canvas
const scaleX = canvasWidth / imageWidth;
const scaleY = canvasHeight / imageHeight;
// Draw connections
if (data.connections) {
data.connections.forEach(connection => {
const startLandmark = data.landmarks.find(l => l.idx === connection.start);
const endLandmark = data.landmarks.find(l => l.idx === connection.end);
if (startLandmark && endLandmark) {
// Get connection color based on body part
let color = currentColorTheme.joints;
// Determine which body part this connection belongs to
// Shoulders (connection between shoulder landmarks)
if ((connection.start === 11 && connection.end === 12) ||
(connection.start === 12 && connection.end === 11)) {
color = currentColorTheme.shoulders;
}
// Torso (connections between shoulders and hips)
else if ((connection.start >= 11 && connection.start <= 12 && connection.end >= 23 && connection.end <= 24) ||
(connection.start >= 23 && connection.start <= 24 && connection.end >= 11 && connection.end <= 12) ||
(connection.start === 23 && connection.end === 24) ||
(connection.start === 24 && connection.end === 23)) {
color = currentColorTheme.torso;
}
// Arms (connections involving elbows and wrists)
else if ((connection.start >= 11 && connection.start <= 16) ||
(connection.end >= 11 && connection.end <= 16)) {
color = currentColorTheme.arms;
}
// Legs (connections involving knees and ankles)
else if ((connection.start >= 23 && connection.start <= 32) ||
(connection.end >= 23 && connection.end <= 32)) {
color = currentColorTheme.legs;
}
// Draw line
ctx.beginPath();
ctx.moveTo(startLandmark.x * scaleX, startLandmark.y * scaleY);
ctx.lineTo(endLandmark.x * scaleX, endLandmark.y * scaleY);
ctx.strokeStyle = color;
ctx.lineWidth = 3;
ctx.stroke();
}
});
}
// Draw landmarks
data.landmarks.forEach(landmark => {
const x = landmark.x * scaleX;
const y = landmark.y * scaleY;
// Skip drawing low visibility landmarks
if (landmark.visibility < 0.5) return;
ctx.beginPath();
ctx.arc(x, y, 5, 0, 2 * Math.PI);
ctx.fillStyle = currentColorTheme.joints;
ctx.fill();
});
}
// Event listeners
connectBtn.addEventListener('click', connectToServer);
colorThemeSelect.addEventListener('change', function() {
const theme = this.value;
currentColorTheme = colorThemes[theme];
// Redraw if we have data
if (landmarksData) {
drawSkeleton(landmarksData);
}
});
// Auto-connect on page load
window.addEventListener('load', function() {
// Draw empty skeleton
ctx.fillStyle = currentColorTheme.background;
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
ctx.font = '20px Arial';
ctx.fillStyle = '#999';
ctx.textAlign = 'center';
ctx.fillText('Connect to see skeleton visualization', canvasWidth/2, canvasHeight/2);
});
</script>
</body>
</html>