advances / try_page.html
OussamaElfila21's picture
Update try_page.html
bf373ba verified
raw
history blame contribute delete
29.5 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTTP Image Upload Demo</title>
<style>
/* Reset and base styles */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: #333;
line-height: 1.6;
}
/* Demo section styling */
.execution-section {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
background-color: #f8f9fa;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.section-title {
font-size: 2rem;
color: #384B70;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid #507687;
}
.demo-container {
display: flex;
flex-wrap: wrap;
gap: 2rem;
margin-top: 1.5rem;
}
.upload-container, .response-container {
flex: 1;
min-width: 300px;
padding: 1.5rem;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.container-title {
font-size: 1.5rem;
margin-bottom: 1rem;
color: #384B70;
}
/* Upload area styling */
.file-input-container {
border: 2px dashed #ccc;
border-radius: 5px;
padding: 2rem;
text-align: center;
margin-bottom: 1rem;
transition: all 0.3s ease;
}
.file-input-container:hover {
border-color: #507687;
background-color: #f8f9fa;
}
#fileInput {
display: none;
}
.file-label {
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.file-icon {
font-size: 2.5rem;
color: #507687;
width: 64px;
height: 64px;
}
.file-placeholder {
max-width: 100%;
height: auto;
margin-top: 1rem;
border-radius: 4px;
display: none;
}
#sendButton {
background-color: #384B70;
color: white;
border: none;
border-radius: 4px;
padding: 0.75rem 1.5rem;
font-size: 1rem;
cursor: pointer;
transition: background-color 0.3s;
width: 100%;
margin-top: 1rem;
}
#sendButton:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
#sendButton:hover:not(:disabled) {
background-color: #507687;
}
/* Response area styling */
.response-output {
height: 300px;
overflow-y: auto;
background-color: #f8f9fa;
border: 1px solid #ddd;
border-radius: 4px;
padding: 1rem;
font-family: monospace;
white-space: pre-wrap;
}
/* Tabs styling */
.tabs {
display: flex;
border-bottom: 1px solid #ddd;
margin-bottom: 1rem;
}
.tab-button {
padding: 0.5rem 1rem;
background-color: #f1f1f1;
border: none;
cursor: pointer;
transition: background-color 0.3s;
font-size: 1rem;
}
.tab-button.active {
background-color: #384B70;
color: white;
}
.tab-content {
display: none;
height: 300px;
}
.tab-content.active {
display: block;
}
/* Visualization area styling */
#visualizationContainer {
position: relative;
height: 100%;
overflow: auto;
background-color: #f8f9fa;
border: 1px solid #ddd;
border-radius: 4px;
}
.detection-canvas {
display: block;
margin: 0 auto;
}
/* Utilities */
#loading {
display: none;
margin-top: 1rem;
color: #384B70;
font-weight: bold;
text-align: center;
}
#message {
margin-top: 1rem;
padding: 0.75rem;
border-radius: 4px;
text-align: center;
display: none;
}
.error {
background-color: #ffebee;
color: #d32f2f;
}
.success {
background-color: #e8f5e9;
color: #388e3c;
}
.info {
font-size: 0.9rem;
color: #666;
margin-top: 0.5rem;
}
.stats {
margin-top: 1rem;
font-size: 0.9rem;
color: #666;
}
/* Debug output */
#debugOutput {
margin-top: 0.5rem;
font-size: 0.8rem;
color: #999;
border-top: 1px dashed #ddd;
padding-top: 0.5rem;
display: none;
}
</style>
</head>
<body>
<!-- Interactive Demo Section -->
<section class="execution-section">
<h2 class="section-title">Try It Yourself</h2>
<p>Upload an image and see the object detection and depth estimation results in real-time.</p>
<div class="demo-container">
<!-- Upload Container -->
<div class="upload-container">
<h3 class="container-title">Upload Image</h3>
<div class="file-input-container">
<label for="fileInput" class="file-label">
<img src="https://upload.wikimedia.org/wikipedia/commons/a/a1/Icons8_flat_folder.svg" class="file-icon"/>
<span>Click to select image</span>
<p class="info">PNG or JPEG, max 2MB</p>
</label>
<input type="file" accept="image/*" id="fileInput" />
<img id="imagePreview" class="file-placeholder" alt="Image preview" />
</div>
<button id="sendButton" disabled>Process Image</button>
<div id="loading">Processing your image...</div>
<div id="message"></div>
<div class="stats">
<div id="imageSize"></div>
<div id="processingTime"></div>
</div>
<div id="debugOutput"></div>
</div>
<!-- Response Container with Tabs -->
<div class="response-container">
<h3 class="container-title">Response</h3>
<div class="tabs">
<button class="tab-button active" data-tab="raw">Raw Output</button>
<button class="tab-button" data-tab="visual">Visual Output</button>
</div>
<!-- Raw Output Tab -->
<div id="rawTab" class="tab-content active">
<pre class="response-output" id="responseOutput">// Response will appear here after processing</pre>
</div>
<!-- Visual Output Tab -->
<div id="visualTab" class="tab-content">
<div id="visualizationContainer">
<canvas id="detectionCanvas" class="detection-canvas"></canvas>
</div>
</div>
</div>
</div>
</section>
<script>
// DOM Elements
const fileInput = document.getElementById('fileInput');
const imagePreview = document.getElementById('imagePreview');
const sendButton = document.getElementById('sendButton');
const loading = document.getElementById('loading');
const message = document.getElementById('message');
const responseOutput = document.getElementById('responseOutput');
const imageSizeInfo = document.getElementById('imageSize');
const processingTimeInfo = document.getElementById('processingTime');
const tabButtons = document.querySelectorAll('.tab-button');
const tabContents = document.querySelectorAll('.tab-content');
const detectionCanvas = document.getElementById('detectionCanvas');
const ctx = detectionCanvas.getContext('2d');
const debugOutput = document.getElementById('debugOutput');
// Enable debug mode (set to false in production)
const DEBUG = true;
// API endpoint URL
const API_URL = '/api/predict';
let imageFile = null;
let startTime = null;
let originalImage = null;
let processingWidth = 0;
let processingHeight = 0;
let responseData = null;
// Tab switching functionality
tabButtons.forEach(button => {
button.addEventListener('click', () => {
const tabName = button.getAttribute('data-tab');
// Update button states
tabButtons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
// Update tab content visibility
tabContents.forEach(content => content.classList.remove('active'));
document.getElementById(tabName + 'Tab').classList.add('active');
// If switching to visual tab and we have data, ensure visualization is rendered
if (tabName === 'visual' && responseData && originalImage) {
visualizeResults(originalImage, responseData);
}
});
});
// Handle file input change
fileInput.addEventListener('change', (event) => {
const file = event.target.files[0];
// Clear previous selections
imageFile = null;
imagePreview.style.display = 'none';
sendButton.disabled = true;
originalImage = null;
responseData = null;
// Validate file
if (!file) return;
if (file.size > 2 * 1024 * 1024) {
showMessage('File size exceeds 2MB limit.', 'error');
return;
}
if (!['image/png', 'image/jpeg'].includes(file.type)) {
showMessage('Only PNG and JPEG formats are supported.', 'error');
return;
}
// Store file for upload
imageFile = file;
// Show image preview
const reader = new FileReader();
reader.onload = (e) => {
const image = new Image();
image.src = e.target.result;
image.onload = () => {
// Store original image for visualization
originalImage = image;
// Set preview
imagePreview.src = e.target.result;
imagePreview.style.display = 'block';
// Update image info
imageSizeInfo.textContent = `Original size: ${image.width}x${image.height} pixels`;
// Resize image for processing
resizeImage(image, file.type);
// Enable send button
sendButton.disabled = false;
showMessage('Image ready to process.', 'info');
};
};
reader.readAsDataURL(file);
});
// Resize image function
function resizeImage(image, fileType) {
const canvas = document.createElement('canvas');
const maxWidth = 640;
const maxHeight = 320;
let width = image.width;
let height = image.height;
// Calculate dimensions
if (width > height) {
if (width > maxWidth) {
height = Math.round((height * maxWidth) / width);
width = maxWidth;
}
} else {
if (height > maxHeight) {
width = Math.round((width * maxHeight) / height);
height = maxHeight;
}
}
// Store processing dimensions for visualization
processingWidth = width;
processingHeight = height;
// Set canvas dimensions and draw image
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, width, height);
// For API calls, we don't need to convert to binary
// but we keep this method to ensure dimensions are correctly calculated
}
// Handle send button click
sendButton.addEventListener('click', async () => {
if (!imageFile) {
showMessage('No image selected.', 'error');
return;
}
// Clear previous response
responseOutput.textContent = "// Processing...";
clearCanvas();
responseData = null;
debugOutput.style.display = 'none';
// Show loading state
loading.style.display = 'block';
message.style.display = 'none';
// Reset processing time
processingTimeInfo.textContent = '';
// Record start time
startTime = performance.now();
// Create form data for HTTP request
const formData = new FormData();
formData.append('file', imageFile);
try {
// Send HTTP request
const response = await fetch(API_URL, {
method: 'POST',
body: formData
});
// Handle response
if (!response.ok) {
const errorText = await response.text();
throw new Error(`HTTP error ${response.status}: ${errorText}`);
}
// Parse JSON response
const data = await response.json();
responseData = data;
// Calculate processing time
const endTime = performance.now();
const timeTaken = endTime - startTime;
// Format and display raw response
responseOutput.textContent = JSON.stringify(data, null, 2);
processingTimeInfo.textContent = `Processing time: ${timeTaken.toFixed(2)} ms`;
// Visualize the results
if (originalImage) {
visualizeResults(originalImage, data);
}
// Show success message
showMessage('Image processed successfully!', 'success');
} catch (error) {
console.error('Error processing image:', error);
showMessage(`Error: ${error.message}`, 'error');
responseOutput.textContent = `// Error: ${error.message}`;
if (DEBUG) {
debugOutput.style.display = 'block';
debugOutput.textContent = `Error: ${error.message}\n${error.stack || ''}`;
}
} finally {
loading.style.display = 'none';
}
});
// Visualize detection results
function visualizeResults(image, data) {
try {
// Set canvas dimensions
detectionCanvas.width = processingWidth;
detectionCanvas.height = processingHeight;
// Draw the resized original image
ctx.drawImage(image, 0, 0, processingWidth, processingHeight);
// Set styles for bounding boxes
ctx.lineWidth = 3;
ctx.font = 'bold 14px Arial';
// Find detections from various possible keys
let detections = [];
if (data.detections && Array.isArray(data.detections)) {
detections = data.detections;
} else if (data.predictions && Array.isArray(data.predictions)) {
detections = data.predictions;
} else if (data.objects && Array.isArray(data.objects)) {
detections = data.objects;
} else if (data.results && Array.isArray(data.results)) {
detections = data.results;
} else {
// Try to look one level deeper if no detections found
for (const key in data) {
if (typeof data[key] === 'object' && data[key] !== null) {
if (Array.isArray(data[key])) {
detections = data[key];
break;
} else {
for (const subKey in data[key]) {
if (Array.isArray(data[key][subKey])) {
detections = data[key][subKey];
break;
}
}
}
}
}
}
// Scaling factors based on original image vs resized processing dimensions
const scaleX = processingWidth / image.width;
const scaleY = processingHeight / image.height;
// Process each detection
detections.forEach((detection, index) => {
let bbox = null;
let label = null;
let confidence = null;
let distance = null;
// Extract label
if (detection.class !== undefined) {
label = detection.class;
} else {
for (const key of ['label', 'name', 'category', 'className']) {
if (detection[key] !== undefined) {
label = detection[key];
break;
}
}
}
if (!label) label = `Object ${index + 1}`;
// Extract confidence score
for (const key of ['confidence', 'score', 'probability', 'conf']) {
if (detection[key] !== undefined) {
confidence = detection[key];
break;
}
}
// Extract distance (using 'distance_estimated' first)
if (detection.distance_estimated !== undefined) {
distance = detection.distance_estimated;
} else {
for (const key of ['distance', 'depth', 'z', 'dist', 'range']) {
if (detection[key] !== undefined) {
distance = detection[key];
break;
}
}
}
// Attempt to get bounding box coordinates
if (detection.features &&
detection.features.xmin !== undefined &&
detection.features.ymin !== undefined &&
detection.features.xmax !== undefined &&
detection.features.ymax !== undefined) {
bbox = {
xmin: detection.features.xmin,
ymin: detection.features.ymin,
xmax: detection.features.xmax,
ymax: detection.features.ymax
};
} else {
// Recursive search for bbox-like properties
function findBBox(obj) {
if (!obj || typeof obj !== 'object') return null;
if ((obj.x !== undefined && obj.y !== undefined &&
(obj.width !== undefined || obj.w !== undefined ||
obj.height !== undefined || obj.h !== undefined)) ||
(obj.xmin !== undefined && obj.ymin !== undefined &&
obj.xmax !== undefined && obj.ymax !== undefined)) {
return obj;
}
if (Array.isArray(obj) && obj.length === 4 &&
obj.every(item => typeof item === 'number')) {
return obj;
}
for (const key of ['bbox', 'box', 'bounding_box', 'boundingBox']) {
if (obj[key] !== undefined) {
return obj[key];
}
}
for (const key in obj) {
const result = findBBox(obj[key]);
if (result) return result;
}
return null;
}
bbox = findBBox(detection);
}
// If bounding box found, process and draw it
if (bbox) {
let x, y, width, height;
if (Array.isArray(bbox)) {
if (bbox.length === 4) {
// Check if values are normalized (all between 0 and 1)
const isNormalized = bbox.every(val => val >= 0 && val <= 1);
if (isNormalized) {
x = bbox[0] * processingWidth;
y = bbox[1] * processingHeight;
width = (bbox[2] - bbox[0]) * processingWidth;
height = (bbox[3] - bbox[1]) * processingHeight;
} else if (bbox[2] > bbox[0] && bbox[3] > bbox[1]) {
// Absolute coordinates
x = bbox[0] * scaleX;
y = bbox[1] * scaleY;
width = (bbox[2] - bbox[0]) * scaleX;
height = (bbox[3] - bbox[1]) * scaleY;
} else {
// Format assumed to be [x, y, width, height]
x = bbox[0] * scaleX;
y = bbox[1] * scaleY;
width = bbox[2] * scaleX;
height = bbox[3] * scaleY;
}
}
} else {
// Object format handling
if (bbox.x !== undefined && bbox.y !== undefined) {
// x,y,width,height format
x = bbox.x;
y = bbox.y;
width = bbox.width || bbox.w || 0;
height = bbox.height || bbox.h || 0;
// Check if normalized
if (x <= 1 && y <= 1 && width <= 1 && height <= 1) {
x *= processingWidth;
y *= processingHeight;
width *= processingWidth;
height *= processingHeight;
} else {
// Assume coordinates are based on the original image
x *= scaleX;
y *= scaleY;
width *= scaleX;
height *= scaleY;
}
} else if (bbox.xmin !== undefined && bbox.ymin !== undefined) {
x = bbox.xmin;
y = bbox.ymin;
width = (bbox.xmax || 0) - bbox.xmin;
height = (bbox.ymax || 0) - bbox.ymin;
if (x <= 1 && y <= 1 && bbox.xmax <= 1 && bbox.ymax <= 1) {
x *= processingWidth;
y *= processingHeight;
width *= processingWidth;
height *= processingHeight;
} else {
x *= scaleX;
y *= scaleY;
width *= scaleX;
height *= scaleY;
}
}
}
// Draw the bounding box if coordinates are valid
if (x !== undefined && y !== undefined &&
width !== undefined && height !== undefined &&
width > 0 && height > 0) {
// Generate a color based on the label
const hue = stringToHue(label);
ctx.strokeStyle = `hsl(${hue}, 100%, 40%)`;
ctx.fillStyle = `hsla(${hue}, 100%, 40%, 0.3)`;
// Draw the bounding box rectangle
ctx.beginPath();
ctx.rect(x, y, width, height);
ctx.stroke();
ctx.fill();
// Format and display confidence value if available
let confidenceText = "";
if (confidence !== null && confidence !== undefined) {
if (confidence <= 1) {
confidenceText = ` ${(confidence * 100).toFixed(0)}%`;
} else {
confidenceText = ` ${confidence.toFixed(0)}%`;
}
}
// Format distance if available
let distanceText = "";
if (distance !== null && distance !== undefined) {
distanceText = ` : ${distance.toFixed(2)} m`;
}
// Prepare the label text
const labelText = `${label}${confidenceText}${distanceText}`;
const textWidth = ctx.measureText(labelText).width + 10;
// Draw label background
ctx.fillStyle = `hsl(${hue}, 100%, 40%)`;
ctx.fillRect(x, y - 20, textWidth, 20);
// Draw label text
ctx.fillStyle = "white";
ctx.fillText(labelText, x + 5, y - 5);
}
}
});
} catch (error) {
console.error('Error visualizing results:', error);
// Optional: Display debug information if needed
debugOutput.style.display = 'block';
debugOutput.textContent += `VISUALIZATION ERROR: ${error.message}\n${error.stack}\n`;
}
}
// Generate consistent hue for string
function stringToHue(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
return hash % 360;
}
// Clear canvas
function clearCanvas() {
if (detectionCanvas.getContext) {
ctx.clearRect(0, 0, detectionCanvas.width, detectionCanvas.height);
}
}
// Show message function
function showMessage(text, type) {
message.textContent = text;
message.className = '';
message.classList.add(type);
message.style.display = 'block';
if (type === 'info') {
setTimeout(() => {
message.style.display = 'none';
}, 3000);
}
}
</script>
</body>
</html>