Fracture_Detection / index.html
Raushan2709's picture
Upload 5 files
f177f7b verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>X-ray Fracture Detection</title>
<!-- Tailwind CSS for styling -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Inter font from Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
}
/* Simple spinner animation */
.loader {
border: 4px solid #f3f3f3;
border-radius: 50%;
border-top: 4px solid #3498db;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body class="bg-gray-100 min-h-screen flex items-center justify-center">
<div class="w-full max-w-lg mx-auto bg-white rounded-xl shadow-lg p-8 md:p-12">
<!-- Header -->
<div class="text-center mb-8">
<h1 class="text-3xl font-bold text-gray-800">Fracture Detection AI</h1>
<p class="text-gray-500 mt-2">Upload an X-ray image to check for fractures.</p>
</div>
<!-- File Upload Section -->
<div>
<!-- Styled file input -->
<label for="file-upload" class="w-full cursor-pointer bg-gray-200 text-gray-700 font-semibold py-3 px-4 rounded-lg inline-flex items-center justify-center hover:bg-gray-300 transition duration-300">
<svg class="w-6 h-6 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path></svg>
<span id="file-label">Select an X-ray Image</span>
</label>
<input id="file-upload" type="file" class="hidden" accept="image/*">
</div>
<!-- Image Preview -->
<div id="image-preview-container" class="mt-6 text-center hidden">
<p class="text-sm font-medium text-gray-600 mb-2">Image Preview:</p>
<img id="image-preview" src="#" alt="Image preview" class="max-w-xs mx-auto rounded-lg shadow-md"/>
</div>
<!-- Predict Button -->
<div class="mt-8">
<button id="predict-button" class="w-full bg-blue-600 text-white font-bold py-3 px-4 rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-4 focus:ring-blue-300 transition duration-300 disabled:bg-gray-400 disabled:cursor-not-allowed" disabled>
Upload & Predict
</button>
</div>
<!-- Results Section -->
<div id="results" class="mt-8 text-center hidden">
<!-- Spinner -->
<div id="loader" class="loader mx-auto hidden"></div>
<!-- Result Text -->
<div id="result-text" class="mt-4 p-4 rounded-lg"></div>
</div>
<!-- Error Message -->
<div id="error-message" class="mt-6 text-center text-red-600 font-semibold hidden"></div>
</div>
<script>
const fileUpload = document.getElementById('file-upload');
const fileLabel = document.getElementById('file-label');
const imagePreviewContainer = document.getElementById('image-preview-container');
const imagePreview = document.getElementById('image-preview');
const predictButton = document.getElementById('predict-button');
const resultsDiv = document.getElementById('results');
const resultText = document.getElementById('result-text');
const loader = document.getElementById('loader');
const errorMessage = document.getElementById('error-message');
let selectedFile = null;
// Listen for file selection
fileUpload.addEventListener('change', (event) => {
selectedFile = event.target.files[0];
if (selectedFile) {
// Update label text
fileLabel.textContent = selectedFile.name;
// Show image preview
const reader = new FileReader();
reader.onload = (e) => {
imagePreview.src = e.target.result;
imagePreviewContainer.classList.remove('hidden');
};
reader.readAsDataURL(selectedFile);
// Enable predict button
predictButton.disabled = false;
// Reset previous results
resultsDiv.classList.add('hidden');
errorMessage.classList.add('hidden');
}
});
// Listen for predict button click
predictButton.addEventListener('click', async () => {
if (!selectedFile) return;
// Prepare for API call
const formData = new FormData();
formData.append('file', selectedFile);
// Show loading spinner and hide previous results
resultsDiv.classList.remove('hidden');
loader.classList.remove('hidden');
resultText.classList.add('hidden');
errorMessage.classList.add('hidden');
predictButton.disabled = true;
try {
// IMPORTANT: This assumes your FastAPI server is running on http://127.0.0.1:8000
const response = await fetch('http://127.0.0.1:8000/predict', {
method: 'POST',
body: formData,
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || `Server error: ${response.statusText}`);
}
const data = await response.json();
// Display the result
displayResult(data);
} catch (error) {
// Display error message
console.error('Error:', error);
errorMessage.textContent = `Error: Could not connect to the API or the file is invalid. Make sure the server is running. Details: ${error.message}`;
errorMessage.classList.remove('hidden');
resultsDiv.classList.add('hidden');
} finally {
// Hide loader and re-enable button
loader.classList.add('hidden');
predictButton.disabled = false;
}
});
function displayResult(data) {
const prediction = data.prediction;
const confidence = parseFloat(data.confidence) * 100;
// Clear previous styles
resultText.classList.remove('bg-green-100', 'text-green-800', 'bg-red-100', 'text-red-800', 'bg-yellow-100', 'text-yellow-800');
let resultHTML = `<p class="text-xl font-bold">Prediction: <span class="capitalize">${prediction}</span></p>
<p class="text-md mt-1">Confidence: ${confidence.toFixed(2)}%</p>`;
// Style based on prediction
if (prediction.toLowerCase().includes('fracture')) {
resultText.classList.add('bg-red-100', 'text-red-800');
} else {
resultText.classList.add('bg-green-100', 'text-green-800');
}
resultText.innerHTML = resultHTML;
resultText.classList.remove('hidden');
}
</script>
</body>
</html>