| | document.addEventListener('DOMContentLoaded', function() { |
| | |
| | const radioButtons = document.querySelectorAll('input[name="inputMethod"]'); |
| | radioButtons.forEach(radio => { |
| | radio.addEventListener('change', toggleInputMethod); |
| | }); |
| |
|
| | |
| | initializeSearchAndLabels(); |
| | }); |
| |
|
| | function toggleInputMethod() { |
| | const localInputGroup = document.getElementById('localInputGroup'); |
| | const urlInputGroup = document.getElementById('urlInputGroup'); |
| | const selectedMethod = document.querySelector('input[name="inputMethod"]:checked').value; |
| |
|
| | if (selectedMethod === 'local') { |
| | localInputGroup.style.display = 'block'; |
| | urlInputGroup.style.display = 'none'; |
| | |
| | document.getElementById('urlInput').value = ''; |
| | } else { |
| | localInputGroup.style.display = 'none'; |
| | urlInputGroup.style.display = 'block'; |
| | |
| | document.getElementById('fileInput').value = ''; |
| | } |
| | |
| | |
| | clearPreviewAndResults(); |
| | } |
| |
|
| | function clearPreviewAndResults() { |
| | const imagePreview = document.getElementById('imagePreview'); |
| | const resultsDiv = document.getElementById('results'); |
| | |
| | imagePreview.src = ''; |
| | resultsDiv.innerHTML = ''; |
| | } |
| |
|
| | |
| | document.getElementById('fileInput').addEventListener('change', previewImage); |
| | document.getElementById('urlInput').addEventListener('input', debounce(previewImage, 500)); |
| |
|
| | function debounce(func, wait) { |
| | let timeout; |
| | return function executedFunction(...args) { |
| | const later = () => { |
| | clearTimeout(timeout); |
| | func(...args); |
| | }; |
| | clearTimeout(timeout); |
| | timeout = setTimeout(later, wait); |
| | }; |
| | } |
| |
|
| | function previewImage() { |
| | const fileInput = document.getElementById('fileInput'); |
| | const urlInput = document.getElementById('urlInput'); |
| | const imagePreview = document.getElementById('imagePreview'); |
| | const defaultPreviewMessage = document.getElementById('defaultPreviewMessage'); |
| |
|
| | if (fileInput.files && fileInput.files[0]) { |
| | const reader = new FileReader(); |
| | reader.onload = function(e) { |
| | imagePreview.src = e.target.result; |
| | imagePreview.style.display = 'block'; |
| | defaultPreviewMessage.style.display = 'none'; |
| | }; |
| | reader.readAsDataURL(fileInput.files[0]); |
| | urlInput.value = ''; |
| | } else if (urlInput.value) { |
| | imagePreview.src = urlInput.value; |
| | imagePreview.style.display = 'block'; |
| | defaultPreviewMessage.style.display = 'none'; |
| | fileInput.value = ''; |
| | } else { |
| | |
| | imagePreview.style.display = 'none'; |
| | defaultPreviewMessage.style.display = 'block'; |
| | } |
| | } |
| |
|
| | async function getBase64FromUrl(url) { |
| | const response = await fetch(url); |
| | const blob = await response.blob(); |
| | return new Promise((resolve, reject) => { |
| | const reader = new FileReader(); |
| | reader.onload = () => { |
| | const base64String = reader.result.split(',')[1]; |
| | resolve(base64String); |
| | }; |
| | reader.onerror = reject; |
| | reader.readAsDataURL(blob); |
| | }); |
| | } |
| |
|
| | async function analyzeImage() { |
| | |
| | document.getElementById('results').innerHTML = ''; |
| | |
| | const spinner = document.getElementById('spinner'); |
| | const analyzeBtn = document.getElementById('analyzeBtn'); |
| | const resultsDiv = document.getElementById('results'); |
| | |
| | spinner.style.display = 'block'; |
| | analyzeBtn.disabled = true; |
| | analyzeBtn.textContent = 'Analyzing...'; |
| |
|
| | const fileInput = document.getElementById('fileInput'); |
| | const urlInput = document.getElementById('urlInput'); |
| | let base64Image; |
| |
|
| | try { |
| | |
| | const defaultLabels = Array.from(document.querySelectorAll('.default-labels input[type="checkbox"]:checked')) |
| | .map(checkbox => checkbox.value); |
| | |
| | const selectedLabels = Array.from(document.querySelectorAll('#selectedLabels .selected-label')) |
| | .map(label => label.dataset.label); |
| |
|
| | |
| | const allSelectedLabels = [...defaultLabels, ...selectedLabels]; |
| |
|
| | if (allSelectedLabels.length === 0) { |
| | throw new Error('Please select at least one category'); |
| | } |
| |
|
| | if (fileInput.files && fileInput.files[0]) { |
| | |
| | const reader = new FileReader(); |
| | base64Image = await new Promise((resolve) => { |
| | reader.onload = (e) => { |
| | resolve(e.target.result.split(',')[1]); |
| | }; |
| | reader.readAsDataURL(fileInput.files[0]); |
| | }); |
| | } else if (urlInput.value) { |
| | |
| | base64Image = await getBase64FromUrl(urlInput.value); |
| | } else { |
| | throw new Error('Please select a file or enter a URL.'); |
| | } |
| |
|
| | const payload = { |
| | images: [base64Image], |
| | labels: allSelectedLabels, |
| | multilabel: false |
| | }; |
| |
|
| | const response = await fetch('http://localhost:8000/predict', { |
| | method: 'POST', |
| | headers: { |
| | 'Content-Type': 'application/json' |
| | }, |
| | body: JSON.stringify(payload) |
| | }); |
| |
|
| | const data = await response.json(); |
| | displayResults(data.predictions); |
| | } catch (error) { |
| | console.error('Error:', error); |
| | resultsDiv.innerHTML = `<p class="error-message">Error: ${error.message}</p>`; |
| | } finally { |
| | spinner.style.display = 'none'; |
| | analyzeBtn.disabled = false; |
| | analyzeBtn.textContent = 'Analyze'; |
| | } |
| | } |
| |
|
| | function displayResults(predictions) { |
| | const resultsDiv = document.getElementById('results'); |
| | resultsDiv.innerHTML = ''; |
| |
|
| | if (!predictions || predictions.length === 0) { |
| | resultsDiv.innerHTML = '<p>No predictions available.</p>'; |
| | return; |
| | } |
| |
|
| | predictions.forEach(prediction => { |
| | resultsDiv.innerHTML += '<h2>Results:</h2>'; |
| | for (const [label, probability] of Object.entries(prediction)) { |
| | const percentage = (probability * 100).toFixed(1); |
| | resultsDiv.innerHTML += ` |
| | <div class="prediction-result"> |
| | <strong>${label}:</strong> |
| | <div class="progress-bar"> |
| | <div class="progress" style="width: ${percentage}%"></div> |
| | </div> |
| | <span>${percentage}%</span> |
| | </div>`; |
| | } |
| | }); |
| | } |
| |
|
| | |
| | const medicalLabels = { |
| | 'Others': ['Effusion', 'Edema', 'Scar Tissue', 'Calcification'], |
| | 'Pulmonology': ['Pneumonia', 'Asthma', 'COPD', 'Tuberculosis', 'Pulmonary Fibrosis', 'Pleural Effusion', 'Pulmonary Edema', 'Lung Nodule', 'Atelectasis', 'Pneumothorax'], |
| | 'Oncology': ['Tumor', 'Breast Cancer', 'Lung Cancer', 'Prostate Cancer', 'Leukemia', 'Lymphoma', 'Melanoma', 'Colorectal Cancer', 'Glioma', 'Metastasis'], |
| | 'Orthopedics': ['Fracture', 'Arthritis', 'Osteoporosis', 'Scoliosis', 'Tendonitis', 'Joint Effusion', 'Disc Herniation', 'Osteomyelitis', 'Bursitis', 'Bone Lesion'], |
| | 'Cardiology': ['Myocardial Infarction', 'Arrhythmia', 'Heart Failure', 'Cardiomegaly', 'Aortic Aneurysm', 'Valvular Heart Disease', 'Coronary Artery Disease', 'Pericardial Effusion', 'Pulmonary Embolism'], |
| | 'Dermatology': ['Urtikaria', 'Akne', 'Eczema', 'Psoriasis', 'Melanoma', 'Basal Cell Carcinoma', 'Squamous Cell Carcinoma', 'Skin Ulcer', 'Rash'], |
| | 'Gastroenterology': ['Cirrhosis', 'Hepatitis', 'Ulcer', 'Gastric Cancer', 'Polyp', 'Pancreatitis', 'Cholecystitis', 'Colitis', 'Crohn’s Disease'], |
| | 'Neurology': ['Stroke', 'Multiple Sclerosis', 'Parkinson’s Disease', 'Alzheimer’s Disease', 'Epilepsy', 'Brain Tumor', 'Hydrocephalus', 'Meningitis', 'Intracranial Hemorrhage'], |
| | 'Endocrinology': ['Diabetes', 'Thyroid Nodule', 'Goiter', 'Adrenal Tumor', 'Pituitary Adenoma', 'Hyperthyroidism', 'Hypothyroidism', 'Parathyroid Hyperplasia'], |
| | 'Hematology': ['Anemia', 'Thrombocytopenia', 'Leukemia', 'Lymphoma', 'Hemophilia', 'Polycythemia', 'Sickle Cell Disease'], |
| | 'Urology': ['Kidney Stone', 'Bladder Cancer', 'Prostate Cancer', 'Urinary Tract Infection', 'Renal Cyst', 'Hydronephrosis'], |
| | 'Ophthalmology': ['Glaucoma', 'Cataract', 'Retinal Detachment', 'Macular Degeneration', 'Diabetic Retinopathy', 'Conjunctivitis'], |
| | 'Gynecology': ['Ovarian Cyst', 'Fibroids', 'Endometriosis', 'Breast Cancer', 'Polycystic Ovary Syndrome', 'Cervical Cancer'], |
| | 'Rheumatology': ['Rheumatoid Arthritis', 'Lupus', 'Scleroderma', 'Ankylosing Spondylitis', 'Gout', 'Sjogren’s Syndrome'] |
| | |
| | }; |
| |
|
| | function initializeSearchAndLabels() { |
| | |
| | const existingSearchContainers = document.querySelectorAll('.search-container'); |
| | existingSearchContainers.forEach(container => container.remove()); |
| |
|
| | |
| | const searchContainer = document.createElement('div'); |
| | searchContainer.className = 'search-container'; |
| | searchContainer.innerHTML = ` |
| | <div class="search-wrapper"> |
| | <input type="text" id="labelSearch" class="search-input" placeholder="Search labels..."> |
| | <button id="showAllLabels" class="show-all-btn">Show All</button> |
| | </div> |
| | <div id="labelDropdown" class="label-dropdown" style="display: none;"></div> |
| | `; |
| |
|
| | |
| | const defaultLabels = document.querySelector('.default-labels'); |
| | if (defaultLabels) { |
| | defaultLabels.parentNode.insertBefore(searchContainer, defaultLabels.nextElementSibling); |
| | } |
| |
|
| | |
| | const searchInput = document.getElementById('labelSearch'); |
| | const labelDropdown = document.getElementById('labelDropdown'); |
| | const showAllButton = document.getElementById('showAllLabels'); |
| |
|
| | |
| | searchInput.addEventListener('focus', () => { |
| | const searchTerm = searchInput.value.trim().toLowerCase(); |
| | showFilteredResults(searchTerm); |
| | }); |
| |
|
| | |
| | searchInput.addEventListener('input', (e) => { |
| | const searchTerm = e.target.value.trim().toLowerCase(); |
| | if (searchTerm) { |
| | showFilteredResults(searchTerm); |
| | } else { |
| | labelDropdown.style.display = 'none'; |
| | } |
| | }); |
| |
|
| | |
| | showAllButton.addEventListener('click', function(e) { |
| | e.stopPropagation(); |
| | labelDropdown.style.display = 'block'; |
| | showFilteredResults(''); |
| | searchInput.value = ''; |
| | }); |
| |
|
| | |
| | document.addEventListener('click', (e) => { |
| | if (!searchInput.contains(e.target) && |
| | !labelDropdown.contains(e.target) && |
| | !showAllButton.contains(e.target)) { |
| | labelDropdown.style.display = 'none'; |
| | } |
| | }); |
| | } |
| |
|
| | function showFilteredResults(searchTerm) { |
| | const labelDropdown = document.getElementById('labelDropdown'); |
| | labelDropdown.innerHTML = ''; |
| | let hasResults = false; |
| |
|
| | |
| | const sortedCategories = Object.entries(medicalLabels).sort((a, b) => a[0].localeCompare(b[0])); |
| |
|
| | |
| | sortedCategories.forEach(([category, labels]) => { |
| | |
| | |
| | const matchingLabels = searchTerm === '' ? |
| | labels.sort() : |
| | labels.filter(label => label.toLowerCase().includes(searchTerm.toLowerCase())); |
| |
|
| | if (matchingLabels.length > 0) { |
| | hasResults = true; |
| | |
| | |
| | const categoryHeader = document.createElement('div'); |
| | categoryHeader.className = 'dropdown-category-header'; |
| | categoryHeader.textContent = category; |
| | labelDropdown.appendChild(categoryHeader); |
| |
|
| | |
| | matchingLabels.forEach(label => { |
| | const option = document.createElement('div'); |
| | option.className = 'dropdown-option'; |
| | |
| | const checkbox = document.createElement('input'); |
| | checkbox.type = 'checkbox'; |
| | checkbox.id = `${category}-${label}`; |
| | checkbox.checked = isLabelSelected(category, label); |
| | checkbox.addEventListener('change', () => updateSelectedLabels(category, label)); |
| |
|
| | const labelText = document.createElement('span'); |
| | labelText.className = 'label-text'; |
| | labelText.textContent = label; |
| |
|
| | const categoryBadge = document.createElement('span'); |
| | categoryBadge.className = 'category-badge'; |
| | categoryBadge.textContent = category; |
| |
|
| | option.appendChild(checkbox); |
| | option.appendChild(labelText); |
| | option.appendChild(categoryBadge); |
| | labelDropdown.appendChild(option); |
| | }); |
| | } |
| | }); |
| |
|
| | labelDropdown.style.display = hasResults || searchTerm === '' ? 'block' : 'none'; |
| | } |
| |
|
| | function isLabelSelected(category, label) { |
| | const selectedLabelsContainer = document.getElementById('selectedLabels'); |
| | return !!selectedLabelsContainer.querySelector( |
| | `[data-category="${category}"][data-label="${label}"]` |
| | ); |
| | } |
| |
|
| | function updateSelectedLabels(category, label) { |
| | const selectedLabelsContainer = document.getElementById('selectedLabels'); |
| | const checkbox = document.getElementById(`${category}-${label}`); |
| | |
| | if (checkbox.checked) { |
| | const labelElement = document.createElement('span'); |
| | labelElement.className = 'selected-label'; |
| | labelElement.textContent = `${label}`; |
| | labelElement.dataset.category = category; |
| | labelElement.dataset.label = label; |
| | |
| | const categoryBadge = document.createElement('span'); |
| | categoryBadge.className = 'category-badge'; |
| | categoryBadge.textContent = category; |
| | |
| | const removeButton = document.createElement('span'); |
| | removeButton.className = 'remove-label'; |
| | removeButton.innerHTML = '×'; |
| | removeButton.onclick = () => { |
| | labelElement.remove(); |
| | checkbox.checked = false; |
| | }; |
| | |
| | labelElement.appendChild(categoryBadge); |
| | labelElement.appendChild(removeButton); |
| | selectedLabelsContainer.appendChild(labelElement); |
| | } else { |
| | const existingLabel = selectedLabelsContainer.querySelector( |
| | `[data-category="${category}"][data-label="${label}"]` |
| | ); |
| | if (existingLabel) { |
| | existingLabel.remove(); |
| | } |
| | } |
| | } |
| |
|
| | |
| | document.addEventListener('DOMContentLoaded', initializeSearchAndLabels); |
| |
|
| |
|