codeby-hp's picture
Upload 7 files
ae467e7 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Brain Tumor Classification</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}
.glass-effect {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.gradient-accent {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 0.8s linear infinite;
}
.spinner.hidden {
display: none;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
</head>
<body class="bg-gray-50">
<div class="min-h-screen flex items-center justify-center px-4 py-8">
<div class="w-full max-w-2xl">
<!-- Header -->
<div class="mb-8 text-center">
<h1 class="text-4xl font-bold text-gray-900 mb-2">Brain Tumor Classification</h1>
<p class="text-gray-600">Upload an MRI scan for AI-powered analysis</p>
</div>
<!-- Main Card -->
<div id="mainCard" class="glass-effect rounded-2xl shadow-xl p-8 mb-6">
<!-- Upload Section -->
<div id="uploadSection" class="mb-8">
<label for="imageInput" class="block mb-4">
<div class="border-2 border-dashed border-gray-300 rounded-xl p-8 text-center cursor-pointer hover:border-purple-500 transition-colors">
<svg class="w-12 h-12 mx-auto text-gray-400 mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
<p class="text-gray-700 font-semibold mb-1">Click to upload or drag and drop</p>
<p class="text-sm text-gray-500">PNG, JPG, GIF up to 10MB</p>
</div>
</label>
<input type="file" id="imageInput" accept="image/*" class="hidden" />
</div>
<!-- Preview Section -->
<div id="previewSection" class="hidden mb-8">
<div class="relative rounded-xl overflow-hidden bg-gray-100 mb-4">
<img id="previewImage" src="" alt="Preview" class="w-full h-auto max-h-96 object-contain" />
</div>
<button id="removeButton" class="w-full bg-gray-200 hover:bg-gray-300 text-gray-800 font-semibold py-2 rounded-lg transition-colors">
Choose Different Image
</button>
</div>
<!-- Submit Button -->
<button id="classifyButton" class="w-full gradient-accent text-white font-semibold py-3 rounded-lg hover:opacity-90 transition-opacity mb-4 flex items-center justify-center gap-2">
<span id="buttonText">Classify Image</span>
<span id="spinner" class="hidden spinner"></span>
</button>
<!-- Error Message -->
<div id="errorMessage" class="hidden bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg text-sm"></div>
</div>
<!-- Results Section -->
<div id="resultsSection" class="hidden glass-effect rounded-2xl shadow-xl p-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6">Classification Results</h2>
<!-- Main Prediction -->
<div class="mb-8 p-6 gradient-accent text-white rounded-xl">
<p class="text-sm font-semibold opacity-90 mb-2">DIAGNOSIS</p>
<p id="mainPrediction" class="text-3xl font-bold mb-2">-</p>
<p id="mainConfidence" class="text-lg opacity-90">Confidence: -%</p>
</div>
<!-- Detailed Breakdown -->
<div class="mb-8">
<h3 class="text-lg font-semibold text-gray-900 mb-4">Confidence Scores</h3>
<div id="predictionsList" class="space-y-3"></div>
</div>
<!-- Action Buttons -->
<button id="analyzeButton" class="w-full gradient-accent text-white font-semibold py-3 rounded-lg hover:opacity-90 transition-opacity">
Analyze Another Image
</button>
</div>
<!-- Footer -->
<div class="mt-8 text-center text-gray-600 text-sm">
<p>Vision Transformer (ViT) powered classification</p>
</div>
</div>
</div>
<script>
const imageInput = document.getElementById('imageInput');
const uploadSection = document.getElementById('uploadSection');
const previewSection = document.getElementById('previewSection');
const previewImage = document.getElementById('previewImage');
const classifyButton = document.getElementById('classifyButton');
const removeButton = document.getElementById('removeButton');
const mainCard = document.getElementById('mainCard');
const resultsSection = document.getElementById('resultsSection');
const errorMessage = document.getElementById('errorMessage');
const analyzeButton = document.getElementById('analyzeButton');
const mainPrediction = document.getElementById('mainPrediction');
const mainConfidence = document.getElementById('mainConfidence');
const predictionsList = document.getElementById('predictionsList');
const buttonText = document.getElementById('buttonText');
const spinner = document.getElementById('spinner');
imageInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
previewImage.src = event.target.result;
uploadSection.classList.add('hidden');
previewSection.classList.remove('hidden');
resultsSection.classList.add('hidden');
errorMessage.classList.add('hidden');
};
reader.readAsDataURL(file);
}
});
removeButton.addEventListener('click', () => {
imageInput.value = '';
previewSection.classList.add('hidden');
uploadSection.classList.remove('hidden');
resultsSection.classList.add('hidden');
errorMessage.classList.add('hidden');
});
classifyButton.addEventListener('click', async () => {
const file = imageInput.files[0];
if (!file) {
showError('Please select an image');
return;
}
const formData = new FormData();
formData.append('file', file);
classifyButton.disabled = true;
buttonText.textContent = 'Classifying...';
spinner.classList.remove('hidden');
errorMessage.classList.add('hidden');
try {
const response = await fetch('/api/v1/classify', {
method: 'POST',
body: formData
});
if (!response.ok) {
const error = await response.json();
showError(error.detail || 'Classification failed');
return;
}
const data = await response.json();
displayResults(data.prediction);
} catch (error) {
showError('Network error: ' + error.message);
} finally {
classifyButton.disabled = false;
buttonText.textContent = 'Classify Image';
spinner.classList.add('hidden');
}
});
analyzeButton.addEventListener('click', () => {
imageInput.value = '';
previewSection.classList.add('hidden');
uploadSection.classList.remove('hidden');
resultsSection.classList.add('hidden');
mainCard.classList.remove('hidden');
errorMessage.classList.add('hidden');
});
function displayResults(prediction) {
mainPrediction.textContent = prediction.predicted_class;
mainConfidence.textContent = `Confidence: ${prediction.confidence}%`;
predictionsList.innerHTML = '';
Object.entries(prediction.all_predictions).forEach(([className, confidence]) => {
const progressPercent = Math.round(confidence);
const barColor = className === prediction.predicted_class ? 'bg-purple-500' : 'bg-gray-300';
const html = `
<div>
<div class="flex justify-between items-center mb-1">
<span class="text-gray-700 font-medium">${className}</span>
<span class="text-gray-600 text-sm">${progressPercent}%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2">
<div class="${barColor} h-2 rounded-full transition-all" style="width: ${progressPercent}%"></div>
</div>
</div>
`;
predictionsList.innerHTML += html;
});
mainCard.classList.add('hidden');
resultsSection.classList.remove('hidden');
}
function showError(message) {
errorMessage.textContent = message;
errorMessage.classList.remove('hidden');
}
const dropZone = document.querySelector('[for="imageInput"]');
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
dropZone.addEventListener('drop', (e) => {
const dt = e.dataTransfer;
const files = dt.files;
imageInput.files = files;
const event = new Event('change', { bubbles: true });
imageInput.dispatchEvent(event);
});
</script>
</body>
</html>