Dermatrix / templates /upload.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 - Upload</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;
}
/* Upload Section */
.upload-section {
background-color: #fff;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0,0,0,0.08);
padding: 40px;
margin-bottom: 40px;
text-align: center;
}
.upload-section h1 {
font-size: 2rem;
color: #333;
margin-bottom: 20px;
}
.upload-section p {
font-size: 1.1rem;
color: #666;
margin-bottom: 30px;
max-width: 700px;
margin-left: auto;
margin-right: auto;
}
/* Upload Area */
.upload-area {
border: 2px dashed #aac;
border-radius: 10px;
padding: 50px 20px;
background-color: #f8faff;
cursor: pointer;
transition: all 0.3s ease;
margin-bottom: 30px;
}
.upload-area:hover {
border-color: #007BFF;
background-color: #f0f8ff;
}
.upload-area.active {
border-color: #007BFF;
background-color: #e6f4ff;
}
.upload-icon {
font-size: 3rem;
color: #007BFF;
margin-bottom: 15px;
}
.upload-text {
font-size: 1.2rem;
color: #666;
margin-bottom: 10px;
}
.upload-subtext {
font-size: 0.9rem;
color: #888;
}
/* File Input */
#file-input {
display: none;
}
/* Preview Area */
.preview-area {
display: none;
margin: 30px auto;
max-width: 500px;
}
.preview-area img {
max-width: 100%;
max-height: 400px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.preview-info {
margin-top: 15px;
font-size: 0.9rem;
color: #666;
}
/* Button Styles */
.btn {
background-color: #007BFF;
color: #fff;
border: none;
padding: 12px 24px;
font-size: 1rem;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
display: inline-block;
}
.btn:hover {
background-color: #0056b3;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,123,255,0.4);
}
.btn-secondary {
background-color: #6c757d;
margin-right: 10px;
}
.btn-secondary:hover {
background-color: #5a6268;
}
/* Instructions Section */
.instructions {
background-color: #fff;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0,0,0,0.08);
padding: 30px;
}
.instructions h2 {
font-size: 1.5rem;
color: #333;
margin-bottom: 20px;
}
.instruction-cards {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.instruction-card {
flex: 1 1 300px;
background-color: #f9f9f9;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: all 0.3s ease;
border-left: 3px solid transparent;
}
.instruction-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 15px rgba(0,0,0,0.1);
border-left: 3px solid #007BFF;
}
.instruction-card h3 {
font-size: 1.1rem;
color: #333;
margin-bottom: 10px;
}
.instruction-card p {
font-size: 0.95rem;
color: #666;
line-height: 1.5;
}
/* Footer */
footer {
text-align: center;
padding: 20px;
margin-top: 40px;
color: #777;
font-size: 0.9rem;
}
/* Responsive */
@media (max-width: 768px) {
.container {
padding: 0 15px;
margin: 20px auto;
}
.upload-section {
padding: 30px 15px;
}
.upload-section h1 {
font-size: 1.7rem;
}
.upload-area {
padding: 30px 15px;
}
.instructions {
padding: 20px 15px;
}
}
</style>
</head>
<body>
<!-- Top Navigation -->
<header>
<div class="logo">SkinAI</div>
<nav>
<a href="/">Home</a>
<a href="/upload" style="color: #007BFF;">Upload</a>
<a href="/result">Results</a>
<a href="#">Contact</a>
<button class="help-btn">Help</button>
</nav>
</header>
<div class="container">
<!-- Upload Section -->
<section class="upload-section">
<h1>Upload Your Skin Image</h1>
<p>Our AI will analyze your image and provide insights about potential skin conditions. Please upload a clear, well-lit photo of the affected area.</p>
<!-- Upload Area -->
<div class="upload-area" id="drop-area">
<div class="upload-icon">📤</div>
<div class="upload-text">Drag & Drop your image here</div>
<div class="upload-subtext">or click to browse files</div>
<input type="file" id="file-input" accept="image/*">
</div>
<!-- Preview Area (initially hidden) -->
<div class="preview-area" id="preview-area">
<img id="preview-image" src="#" alt="Preview">
<div class="preview-info" id="file-info">File information will appear here</div>
<div style="margin-top: 20px;">
<button class="btn btn-secondary" id="cancel-btn">Cancel</button>
<button class="btn" id="analyze-btn">Analyze Image</button>
</div>
</div>
</section>
<!-- Instructions Section -->
<section class="instructions">
<h2>Best Practices for Accurate Results</h2>
<div class="instruction-cards">
<!-- Instruction 1 -->
<div class="instruction-card">
<h3>Good Lighting</h3>
<p>Ensure your photo is taken in bright, natural light. Avoid shadows or harsh lighting that might distort colors or details.</p>
</div>
<!-- Instruction 2 -->
<div class="instruction-card">
<h3>Clear Focus</h3>
<p>Take a clear, in-focus image of the affected area. Blurry images may lead to inaccurate results.</p>
</div>
<!-- Instruction 3 -->
<div class="instruction-card">
<h3>Proper Distance</h3>
<p>Capture the image from about 6-12 inches away to show sufficient detail while maintaining context of the affected area.</p>
</div>
</div>
</section>
</div>
<footer>
<p>© 2025 SkinAI. All rights reserved. For educational purposes only. Not a substitute for professional medical advice.</p>
</footer>
<script>
// JavaScript for handling file uploads and preview
// JavaScript for handling file uploads, preview, and analysis
document.addEventListener('DOMContentLoaded', function() {
const dropArea = document.getElementById('drop-area');
const fileInput = document.getElementById('file-input');
const previewArea = document.getElementById('preview-area');
const previewImage = document.getElementById('preview-image');
const fileInfo = document.getElementById('file-info');
const cancelBtn = document.getElementById('cancel-btn');
const analyzeBtn = document.getElementById('analyze-btn');
// Open file browser when clicking the upload area
dropArea.addEventListener('click', () => {
fileInput.click();
});
// Handle file selection
fileInput.addEventListener('change', handleFiles);
// Prevent default drag behaviors
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false);
document.body.addEventListener(eventName, preventDefaults, false);
});
// Highlight drop area when dragging over it
['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false);
});
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false);
// Cancel button functionality
cancelBtn.addEventListener('click', () => {
resetUpload();
});
// Analyze button functionality - send to backend
analyzeBtn.addEventListener('click', () => {
sendImageForAnalysis();
});
// Functions
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
function highlight() {
dropArea.classList.add('active');
}
function unhighlight() {
dropArea.classList.remove('active');
}
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
handleFiles(files);
}
function handleFiles(e) {
const files = this.files || e;
if (files && files[0]) {
const file = files[0];
// Check if it's an image
if (!file.type.match('image.*')) {
alert('Please upload an image file');
return;
}
// Display file info
const size = (file.size / 1024).toFixed(2);
fileInfo.textContent = `${file.name} (${size} KB)`;
// Show preview
const reader = new FileReader();
reader.onload = function(e) {
previewImage.src = e.target.result;
dropArea.style.display = 'none';
previewArea.style.display = 'block';
// Store image data for results page
sessionStorage.setItem('analyzedImage', e.target.result);
}
reader.readAsDataURL(file);
}
}
function resetUpload() {
fileInput.value = '';
previewImage.src = '#';
previewArea.style.display = 'none';
dropArea.style.display = 'block';
// Clear session storage data
sessionStorage.removeItem('analyzedImage');
sessionStorage.removeItem('analysisResult');
sessionStorage.removeItem('detectedCondition');
}
function sendImageForAnalysis() {
// Show loading state
analyzeBtn.disabled = true;
analyzeBtn.textContent = 'Analyzing...';
// Get the file
const file = fileInput.files[0];
if (!file) {
alert('Please select an image first');
analyzeBtn.disabled = false;
analyzeBtn.textContent = 'Analyze Image';
return;
}
// Create FormData object
const formData = new FormData();
formData.append('image', file);
// Send to backend
fetch('/analyze', {
method: 'POST',
body: formData
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(result => {
// Store result in session storage
sessionStorage.setItem('analysisResult', JSON.stringify(result));
sessionStorage.setItem('detectedCondition', result.prediction || '');
// Redirect to results page
window.location.href = 'result';
})
.catch(error => {
console.error('Error:', error);
alert('There was an error analyzing your image. Please try again.');
// Reset button state
analyzeBtn.disabled = false;
analyzeBtn.textContent = 'Analyze Image';
});
}
});
</script>
</body>
</html>