Spaces:
Sleeping
Sleeping
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Multi-Model Vision App</title> | |
<style> | |
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; } | |
h1 { color: #3f51b5; text-align: center; } | |
.container { display: flex; flex-direction: column; gap: 20px; } | |
.card { border: 1px solid #ddd; border-radius: 8px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } | |
.model-selection { display: flex; gap: 10px; margin: 20px 0; } | |
.model-btn { padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; background-color: #e0e0e0; } | |
.model-btn.active { background-color: #3f51b5; color: white; } | |
.model-btn:disabled { opacity: 0.5; cursor: not-allowed; } | |
.upload-container { display: flex; flex-direction: column; gap: 10px; } | |
#file-input { margin-bottom: 10px; } | |
#process-btn { padding: 10px 15px; background-color: #3f51b5; color: white; border: none; border-radius: 4px; cursor: pointer; } | |
#process-btn:disabled { background-color: #9e9e9e; cursor: not-allowed; } | |
.preview-container { display: flex; justify-content: center; margin: 20px 0; } | |
.preview-image { max-width: 100%; max-height: 300px; border-radius: 4px; } | |
.result-container { margin-top: 20px; } | |
.result-image { max-width: 100%; max-height: 400px; border-radius: 4px; } | |
.detection-list { list-style-type: none; padding: 0; } | |
.detection-item { padding: 8px; border-bottom: 1px solid #eee; } | |
.detection-item:last-child { border-bottom: none; } | |
.confidence { display: inline-block; padding: 2px 6px; background-color: #3f51b5; color: white; border-radius: 10px; font-size: 0.8em; margin-left: 8px; } | |
.performance { margin-top: 20px; font-size: 0.9em; color: #666; } | |
.error { color: #f44336; font-weight: bold; } | |
.loading { text-align: center; margin: 20px 0; } | |
</style> | |
</head> | |
<body> | |
<h1>Multi-Model Vision App</h1> | |
<div class="container"> | |
<div class="card"> | |
<h2>Select Model</h2> | |
<div class="model-selection"> | |
<button id="yolo-btn" class="model-btn">YOLOv8 (Detection)</button> | |
<button id="detr-btn" class="model-btn">DETR (Detection)</button> | |
<button id="vit-btn" class="model-btn">ViT (Classification)</button> | |
</div> | |
</div> | |
<div class="card upload-container"> | |
<h2>Upload Image</h2> | |
<input type="file" id="file-input" accept="image/*"> | |
<div class="preview-container" id="preview-container"></div> | |
<button id="process-btn" disabled>Process Image</button> | |
</div> | |
<div class="card result-container" id="result-container" style="display: none;"> | |
<h2 id="result-title">Results</h2> | |
<div id="result-content"></div> | |
</div> | |
</div> | |
<script> | |
// Model selection | |
const yoloBtn = document.getElementById('yolo-btn'); | |
const detrBtn = document.getElementById('detr-btn'); | |
const vitBtn = document.getElementById('vit-btn'); | |
const fileInput = document.getElementById('file-input'); | |
const processBtn = document.getElementById('process-btn'); | |
const previewContainer = document.getElementById('preview-container'); | |
const resultContainer = document.getElementById('result-container'); | |
const resultTitle = document.getElementById('result-title'); | |
const resultContent = document.getElementById('result-content'); | |
let selectedModel = null; | |
let selectedFile = null; | |
let modelsStatus = { yolo: false, detr: false, vit: false }; | |
// Check API status | |
async function checkApiStatus() { | |
try { | |
const response = await fetch('/api/status'); | |
const data = await response.json(); | |
modelsStatus = data.models; | |
// Update UI based on model availability | |
yoloBtn.disabled = !modelsStatus.yolo; | |
detrBtn.disabled = !modelsStatus.detr; | |
vitBtn.disabled = !modelsStatus.vit; | |
if (!modelsStatus.yolo) yoloBtn.title = "YOLOv8 model not available"; | |
if (!modelsStatus.detr) detrBtn.title = "DETR model not available"; | |
if (!modelsStatus.vit) vitBtn.title = "ViT model not available"; | |
} catch (error) { | |
console.error('Error checking API status:', error); | |
alert('Error connecting to the API. Please make sure the server is running.'); | |
} | |
} | |
// Format time for display | |
function formatTime(ms) { | |
if (ms < 1000) return `${ms.toFixed(2)} ms`; | |
return `${(ms / 1000).toFixed(2)} s`; | |
} | |
// Select model | |
function selectModel(model) { | |
selectedModel = model; | |
yoloBtn.classList.remove('active'); | |
detrBtn.classList.remove('active'); | |
vitBtn.classList.remove('active'); | |
switch(model) { | |
case 'yolo': | |
yoloBtn.classList.add('active'); | |
break; | |
case 'detr': | |
detrBtn.classList.add('active'); | |
break; | |
case 'vit': | |
vitBtn.classList.add('active'); | |
break; | |
} | |
updateProcessButton(); | |
} | |
// Update process button state | |
function updateProcessButton() { | |
processBtn.disabled = !selectedModel || !selectedFile; | |
} | |
// Handle file selection | |
fileInput.addEventListener('change', (e) => { | |
const file = e.target.files[0]; | |
if (file) { | |
selectedFile = file; | |
const reader = new FileReader(); | |
reader.onload = (e) => { | |
previewContainer.innerHTML = `<img src="${e.target.result}" class="preview-image" alt="Preview">`; | |
}; | |
reader.readAsDataURL(file); | |
updateProcessButton(); | |
} else { | |
selectedFile = null; | |
previewContainer.innerHTML = ''; | |
updateProcessButton(); | |
} | |
}); | |
// Model selection event listeners | |
yoloBtn.addEventListener('click', () => selectModel('yolo')); | |
detrBtn.addEventListener('click', () => selectModel('detr')); | |
vitBtn.addEventListener('click', () => selectModel('vit')); | |
// Process image | |
processBtn.addEventListener('click', async () => { | |
if (!selectedModel || !selectedFile) return; | |
resultContainer.style.display = 'block'; | |
resultTitle.textContent = `Processing with ${selectedModel.toUpperCase()}...`; | |
resultContent.innerHTML = '<div class="loading">Processing image...</div>'; | |
const formData = new FormData(); | |
formData.append('image', selectedFile); | |
let endpoint = ''; | |
switch(selectedModel) { | |
case 'yolo': | |
endpoint = '/api/detect/yolo'; | |
break; | |
case 'detr': | |
endpoint = '/api/detect/detr'; | |
break; | |
case 'vit': | |
endpoint = '/api/classify/vit'; | |
break; | |
} | |
try { | |
const response = await fetch(endpoint, { | |
method: 'POST', | |
body: formData | |
}); | |
if (!response.ok) { | |
throw new Error(`HTTP error! Status: ${response.status}`); | |
} | |
const data = await response.json(); | |
displayResults(selectedModel, data); | |
} catch (error) { | |
console.error('Error processing image:', error); | |
resultTitle.textContent = 'Error'; | |
resultContent.innerHTML = `<div class="error">Error processing image: ${error.message}</div>`; | |
} | |
}); | |
// Display results | |
function displayResults(model, data) { | |
resultTitle.textContent = `${model.toUpperCase()} Results`; | |
let html = ''; | |
if (model === 'yolo' || model === 'detr') { | |
// Detection results | |
html += ` | |
<div style="display: flex; flex-direction: column; gap: 20px;"> | |
<div> | |
<h3>Processed Image</h3> | |
<img src="data:image/jpeg;base64,${data.image_with_boxes}" class="result-image" alt="Detection Result"> | |
</div> | |
<div> | |
<h3>Detected Objects</h3> | |
`; | |
if (data.detections && data.detections.length > 0) { | |
html += '<ul class="detection-list">'; | |
data.detections.forEach(item => { | |
html += ` | |
<li class="detection-item"> | |
${item.label} | |
<span class="confidence">${(item.confidence * 100).toFixed(0)}%</span> | |
</li> | |
`; | |
}); | |
html += '</ul>'; | |
} else { | |
html += '<p>No objects detected</p>'; | |
} | |
html += ` | |
</div> | |
<div class="performance"> | |
<p>Inference Time: ${formatTime(data.inference_time)}</p> | |
<p>Total Processing Time: ${formatTime(data.total_time)}</p> | |
</div> | |
</div> | |
`; | |
} else if (model === 'vit') { | |
// Classification results | |
html += ` | |
<div style="display: flex; flex-direction: column; gap: 20px;"> | |
<div> | |
<h3>Classification Results</h3> | |
`; | |
if (data.classifications && data.classifications.length > 0) { | |
html += '<ul class="detection-list">'; | |
data.classifications.forEach(item => { | |
html += ` | |
<li class="detection-item"> | |
${item.label} | |
<span class="confidence">${(item.confidence * 100).toFixed(1)}%</span> | |
</li> | |
`; | |
}); | |
html += '</ul>'; | |
} else { | |
html += '<p>No classifications found</p>'; | |
} | |
html += ` | |
</div> | |
<div class="performance"> | |
<p>Inference Time: ${formatTime(data.inference_time)}</p> | |
<p>Total Processing Time: ${formatTime(data.total_time)}</p> | |
</div> | |
</div> | |
`; | |
} | |
resultContent.innerHTML = html; | |
} | |
// Initialize | |
checkApiStatus(); | |
</script> | |
</body> | |
</html> | |