Dermatrix / templates /result.html
Inquisiter07's picture
Deploy Flask skin disease app with Grad-CAM
07da06b
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>SkinAI - Results</title>
<style>
/* Reset */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background-color: #f5f8fa;
color: #000;
}
/* Top Navigation with Enhanced Hover Effects */
header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 15px 5%;
background-color: #fff;
border-bottom: 1px solid #ccc;
position: relative;
z-index: 10;
}
.logo {
font-size: 1.5rem;
font-weight: bold;
color: #333;
}
nav a {
text-decoration: none;
color: #333;
margin-left: 20px;
font-weight: 500;
padding: 5px 0;
position: relative;
transition: color 0.3s ease;
}
/* Underline hover effect for nav links */
nav a:after {
content: '';
position: absolute;
width: 0;
height: 2px;
bottom: 0;
left: 0;
background-color: #007BFF;
transition: width 0.3s ease;
}
nav a:hover {
color: #007BFF;
text-decoration: none;
}
nav a:hover:after {
width: 100%;
}
.help-btn {
background-color: #007BFF;
color: #fff;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
margin-left: 20px;
transition: all 0.3s ease;
}
.help-btn:hover {
background-color: #0056b3;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,123,255,0.3);
}
/* Main Content Area */
.container {
max-width: 1200px;
margin: 40px auto;
padding: 0 20px;
}
/* Results Section */
.results-section {
background-color: #fff;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0,0,0,0.08);
padding: 40px;
margin-bottom: 40px;
}
.results-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30px;
}
.results-header h1 {
font-size: 2rem;
color: #333;
}
.results-header .new-analysis {
background-color: #f8f9fa;
color: #333;
border: 1px solid #ddd;
padding: 8px 16px;
border-radius: 4px;
text-decoration: none;
transition: all 0.3s ease;
}
.results-header .new-analysis:hover {
background-color: #e9ecef;
transform: translateY(-2px);
}
/* Results Grid */
.results-grid {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 30px;
}
/* Image Section */
.image-section {
background-color: #f8f9fa;
border-radius: 8px;
padding: 20px;
text-align: center;
}
.image-section img {
max-width: 100%;
max-height: 300px;
border-radius: 8px;
margin-bottom: 15px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.image-caption {
font-size: 0.9rem;
color: #666;
}
/* Analysis Section */
.analysis-section {
display: flex;
flex-direction: column;
gap: 20px;
}
.analysis-card {
background-color: #f8f9fa;
border-radius: 8px;
padding: 20px;
border-left: 4px solid #007BFF;
}
.analysis-card h2 {
font-size: 1.4rem;
color: #333;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: space-between;
}
.confidence-badge {
background-color: #e6f4ff;
color: #0066cc;
font-size: 0.9rem;
padding: 4px 12px;
border-radius: 20px;
font-weight: normal;
}
.analysis-card p {
font-size: 1rem;
color: #555;
line-height: 1.6;
margin-bottom: 15px;
}
/* Recommendations List */
.recommendations-list {
margin-top: 10px;
}
.recommendations-list h3 {
font-size: 1.1rem;
color: #333;
margin-bottom: 15px;
}
.recommendations-list ul {
padding-left: 20px;
}
.recommendations-list li {
margin-bottom: 10px;
line-height: 1.5;
font-size: 0.95rem;
color: #444;
}
/* Other Possible Conditions */
.other-conditions {
margin-top: 20px;
}
.other-conditions h3 {
font-size: 1.1rem;
color: #333;
margin-bottom: 15px;
}
.condition-bars {
display: flex;
flex-direction: column;
gap: 10px;
}
.condition-bar {
display: flex;
align-items: center;
gap: 10px;
}
.condition-name {
min-width: 100px;
font-size: 0.9rem;
}
.progress-bar {
flex-grow: 1;
height: 10px;
background-color: #e9ecef;
border-radius: 5px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: #007BFF;
}
.condition-percentage {
font-size: 0.9rem;
color: #666;
min-width: 50px;
text-align: right;
}
/* Q&A Section */
.qa-section {
background-color: #fff;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0,0,0,0.08);
padding: 40px;
}
.qa-section h2 {
font-size: 1.6rem;
color: #333;
margin-bottom: 25px;
}
.qa-form {
display: flex;
gap: 10px;
margin-bottom: 30px;
}
.qa-input {
flex-grow: 1;
padding: 12px 15px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
.qa-btn {
background-color: #007BFF;
color: #fff;
border: none;
padding: 0 20px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
}
.qa-btn:hover {
background-color: #0056b3;
}
.qa-response {
background-color: #f8f9fa;
border-radius: 8px;
padding: 20px;
border-left: 4px solid #28a745;
margin-bottom: 20px;
display: none; /* Initially hidden */
}
.qa-response h3 {
font-size: 1.1rem;
color: #333;
margin-bottom: 10px;
}
.qa-response p {
font-size: 0.95rem;
color: #555;
line-height: 1.6;
}
.disclaimer {
margin-top: 20px;
padding: 15px;
background-color: #fff8e1;
border-left: 4px solid #ffc107;
font-size: 0.9rem;
color: #6c4a00;
line-height: 1.5;
}
/* Footer */
footer {
text-align: center;
padding: 20px;
margin-top: 40px;
color: #777;
font-size: 0.9rem;
}
/* Loader */
.loader-container {
display: flex;
justify-content: center;
align-items: center;
margin: 20px 0;
}
.loader {
border: 4px solid #f3f3f3;
border-top: 4px solid #007BFF;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin-right: 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Responsive */
@media (max-width: 768px) {
.container {
padding: 0 15px;
margin: 20px auto;
}
.results-section, .qa-section {
padding: 30px 15px;
}
.results-grid {
grid-template-columns: 1fr;
}
.results-header {
flex-direction: column;
align-items: flex-start;
gap: 15px;
}
}
</style>
</head>
<body>
<!-- Top Navigation -->
<header>
<div class="logo">SkinAI</div>
<nav>
<a href="/">Home</a>
<a href="/upload">Upload</a>
<a href="/result" style="color: #007BFF;">Results</a>
<a href="#">Contact</a>
<button class="help-btn">Help</button>
</nav>
</header>
<div class="container">
<!-- Results Section -->
<section class="results-section">
<div class="results-header">
<h1>Analysis Results</h1>
<a href="upload" class="new-analysis">Upload New Image</a>
</div>
<div class="results-grid">
<!-- Image Section -->
<div class="image-section">
<img id="analyzed-image" src="#" alt="Analyzed Skin Image">
<div class="image-caption">Uploaded on <span id="upload-date">April 8, 2025</span></div>
</div>
<!-- Analysis Section -->
<div class="analysis-section">
<div class="analysis-card">
<h2>
<span id="condition-name">Loading result...</span>
<span class="confidence-badge" id="confidence-level">--</span>
</h2>
<p id="condition-description">Analyzing your image...</p>
<!-- Loader (initially shown) -->
<div class="loader-container" id="analysis-loader">
<div class="loader"></div>
<span>Processing image with AI...</span>
</div>
<!-- Recommendations (initially hidden) -->
<div class="recommendations-list" id="recommendations-section" style="display: none;">
<h3>Recommendations</h3>
<ul id="recommendations-list">
<!-- Recommendations will be inserted here -->
</ul>
</div>
<!-- Other Possible Conditions -->
<div class="other-conditions" id="other-conditions-section" style="display: none;">
<h3>Other Possible Conditions</h3>
<div class="condition-bars" id="condition-bars">
<!-- Condition bars will be inserted here -->
</div>
</div>
</div>
<div class="disclaimer">
<strong>Disclaimer:</strong> This analysis is for informational purposes only and should not replace professional medical advice. Please consult a healthcare provider for proper diagnosis and treatment.
</div>
</div>
</div>
</section>
<!-- Q&A Section -->
<section class="qa-section">
<h2>Ask About Your Condition</h2>
<div class="qa-form">
<input type="text" class="qa-input" id="question-input" placeholder="Ask a question about your skin condition...">
<button class="qa-btn" id="ask-btn">Ask</button>
</div>
<!-- QA Response (initially hidden) -->
<div class="qa-response" id="qa-response">
<h3>Response</h3>
<p id="qa-answer">The answer will appear here.</p>
</div>
<div class="disclaimer">
<strong>Important:</strong> The AI assistant provides general information about skin conditions. Your specific case may vary, and medical professionals should be consulted for personalized advice and treatment.
</div>
</section>
</div>
<footer>
<p>© 2025 SkinAI. All rights reserved. For educational purposes only. Not a substitute for professional medical advice.</p>
</footer>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Retrieve data from sessionStorage (set during upload)
const imageData = sessionStorage.getItem('analyzedImage');
const analysisResult = JSON.parse(sessionStorage.getItem('analysisResult') || '{}');
const condition = sessionStorage.getItem('detectedCondition') || '';
// Set the image
if (imageData) {
document.getElementById('analyzed-image').src = imageData;
} else {
document.getElementById('analyzed-image').src = "/api/placeholder/400/320";
document.getElementById('analyzed-image').alt = "No image available";
}
// Set the current date
const now = new Date();
document.getElementById('upload-date').textContent = now.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
// Process and display results if available
if (Object.keys(analysisResult).length > 0) {
displayResults(analysisResult);
} else {
// If no results in session storage, check URL params
// This allows direct navigation to this page with the results as parameters
const urlParams = new URLSearchParams(window.location.search);
const resultParam = urlParams.get('result');
if (resultParam) {
try {
const decodedResult = JSON.parse(decodeURIComponent(resultParam));
displayResults(decodedResult);
} catch (e) {
console.error("Failed to parse result from URL parameter", e);
document.getElementById('condition-name').textContent = "No results available";
document.getElementById('condition-description').textContent = "Please upload an image for analysis.";
document.getElementById('analysis-loader').style.display = "none";
}
} else {
// No results available - show message
document.getElementById('condition-name').textContent = "No results available";
document.getElementById('condition-description').textContent = "Please upload an image for analysis.";
document.getElementById('analysis-loader').style.display = "none";
}
}
// Handle Q&A form submission
document.getElementById('ask-btn').addEventListener('click', function() {
askQuestion();
});
document.getElementById('question-input').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
askQuestion();
}
});
// Function to display analysis results
function displayResults(result) {
// Hide loader
document.getElementById('analysis-loader').style.display = "none";
// Display primary condition and confidence
document.getElementById('condition-name').textContent = result.prediction || "Unknown Condition";
const confidencePercent = result.confidence ? Math.round(result.confidence * 100) : 0;
document.getElementById('confidence-level').textContent = `${confidencePercent}% Confidence`;
// Display description
document.getElementById('condition-description').textContent = result.description ||
"No detailed information available for this condition.";
// Display recommendations
if (result.recommendations && result.recommendations.length > 0) {
const recommendationsList = document.getElementById('recommendations-list');
recommendationsList.innerHTML = '';
result.recommendations.forEach(rec => {
const li = document.createElement('li');
li.textContent = rec;
recommendationsList.appendChild(li);
});
document.getElementById('recommendations-section').style.display = "block";
}
// Display other possible conditions
if (result.topConditions && result.topConditions.length > 0) {
const conditionBars = document.getElementById('condition-bars');
conditionBars.innerHTML = '';
// Skip the first condition (already shown as primary) if it's the same as the top result
const startIndex = (result.topConditions[0][0] === result.prediction) ? 1 : 0;
// Display up to 4 additional conditions
for (let i = startIndex; i < Math.min(startIndex + 4, result.topConditions.length); i++) {
const condition = result.topConditions[i];
const conditionName = condition[0];
const probability = condition[1];
const percentage = Math.round(probability * 100);
const conditionBar = document.createElement('div');
conditionBar.className = 'condition-bar';
conditionBar.innerHTML = `
<div class="condition-name">${conditionName}</div>
<div class="progress-bar">
<div class="progress-fill" style="width: ${percentage}%"></div>
</div>
<div class="condition-percentage">${percentage}%</div>
`;
conditionBars.appendChild(conditionBar);
}
document.getElementById('other-conditions-section').style.display = "block";
}
// Store detected condition for Q&A
sessionStorage.setItem('detectedCondition', result.prediction || '');
}
// Function to handle Q&A
function askQuestion() {
const questionInput = document.getElementById('question-input');
const question = questionInput.value.trim();
if (!question) return;
// Get the detected condition
const detectedCondition = sessionStorage.getItem('detectedCondition') || '';
// Show loading indication
document.getElementById('qa-answer').textContent = "Getting answer...";
document.getElementById('qa-response').style.display = "block";
// Call the backend API
fetch('/ask', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
question: question,
condition: detectedCondition
})
})
.then(response => response.json())
.then(data => {
document.getElementById('qa-answer').textContent = data.answer ||
"I couldn't generate an answer for that question.";
})
.catch(error => {
console.error('Error:', error);
document.getElementById('qa-answer').textContent =
"Sorry, there was an error processing your question. Please try again later.";
});
// Clear the input
questionInput.value = '';
}
});
</script>
</body>
</html>