arshtech's picture
Update templates/index.html
57f57c4 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Face Detection App</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; font-family: Arial, sans-serif; }
body { background: linear-gradient(135deg, #667eea, #764ba2); min-height: 100vh; padding: 20px; }
.container { max-width: 1000px; margin: 0 auto; background: white; border-radius: 15px; padding: 30px; }
header { text-align: center; margin-bottom: 30px; }
h1 { color: #333; margin-bottom: 10px; }
.subtitle { color: #666; }
.main-content { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; margin-bottom: 30px; }
@media (max-width: 768px) { .main-content { grid-template-columns: 1fr; } }
.input-section, .output-section { background: #f8f9fa; padding: 25px; border-radius: 10px; border: 2px dashed #dee2e6; }
.section-title { color: #495057; margin-bottom: 20px; }
.input-options { display: flex; gap: 10px; margin-bottom: 20px; }
.btn { padding: 12px; border: none; border-radius: 8px; background: #6c757d; color: white; cursor: pointer; flex: 1; }
.btn:hover { background: #5a6268; }
.btn.active { background: #007bff; }
.upload-area { border: 2px dashed #adb5bd; border-radius: 10px; padding: 40px 20px; text-align: center; margin-bottom: 20px; cursor: pointer; }
.upload-area:hover { border-color: #007bff; }
.upload-icon { font-size: 3rem; margin-bottom: 15px; color: #6c757d; }
.camera-container { position: relative; width: 100%; height: 300px; background: #000; border-radius: 10px; overflow: hidden; margin-bottom: 20px; display: none; }
#videoElement { width: 100%; height: 100%; object-fit: cover; }
.capture-btn { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); background: #dc3545; width: 60px; height: 60px; border-radius: 50%; border: 4px solid white; cursor: pointer; }
.slider-container { margin: 20px 0; }
.slider-label { display: flex; justify-content: space-between; margin-bottom: 10px; color: #495057; }
.slider { width: 100%; height: 8px; background: #dee2e6; border-radius: 5px; outline: none; }
.slider::-webkit-slider-thumb { width: 20px; height: 20px; border-radius: 50%; background: #007bff; cursor: pointer; }
.detect-btn { width: 100%; padding: 15px; background: #28a745; color: white; border: none; border-radius: 8px; font-size: 1.1rem; cursor: pointer; }
.detect-btn:hover { background: #218838; }
.detect-btn:disabled { background: #6c757d; cursor: not-allowed; }
.image-container { width: 100%; height: 300px; background: #e9ecef; border-radius: 10px; overflow: hidden; margin-bottom: 20px; display: flex; align-items: center; justify-content: center; }
#outputImage { max-width: 100%; max-height: 100%; display: none; }
.placeholder-text { color: #6c757d; text-align: center; }
.results-container { background: white; border-radius: 10px; padding: 20px; border: 2px solid #e9ecef; }
.result-item { display: flex; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid #dee2e6; }
.result-item:last-child { border-bottom: none; }
.result-label { font-weight: bold; color: #495057; }
.result-value { color: #007bff; font-weight: bold; }
.loading { display: none; text-align: center; margin: 20px 0; }
.spinner { border: 4px solid #f3f3f3; border-radius: 50%; border-top: 4px solid #007bff; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 0 auto 15px; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
#fileInput { display: none; }
</style>
</head>
<body>
<div class="container">
<header>
<h1>Face Detection App</h1>
<p class="subtitle">Upload an image or use camera to detect faces</p>
</header>
<div class="main-content">
<div class="input-section">
<h2 class="section-title">Input</h2>
<div class="input-options">
<button id="uploadBtn" class="btn active">Upload Image</button>
<button id="cameraBtn" class="btn">Use Camera</button>
</div>
<div id="uploadArea" class="upload-area">
<div class="upload-icon">📁</div>
<p>Click to upload an image</p>
<input type="file" id="fileInput" accept="image/*">
</div>
<div id="cameraContainer" class="camera-container">
<video id="videoElement" autoplay playsinline></video>
<button id="captureBtn" class="capture-btn"></button>
</div>
<div class="slider-container">
<div class="slider-label">
<span>Detection Scale:</span>
<span id="scaleValue">1.1</span>
</div>
<input type="range" min="1.1" max="2.0" step="0.1" value="1.1" class="slider" id="scaleSlider">
</div>
<button id="detectBtn" class="detect-btn" disabled>Detect Faces</button>
<div id="loading" class="loading">
<div class="spinner"></div>
<p>Processing image...</p>
</div>
</div>
<div class="output-section">
<h2 class="section-title">Output</h2>
<div class="image-container">
<img id="outputImage" alt="Processed Image">
<p id="placeholderText" class="placeholder-text">Processed image will appear here</p>
</div>
<div id="resultsContainer" class="results-container" style="display: none;">
<h3 style="margin-bottom: 15px; color: #495057;">Detection Results</h3>
<div class="result-item">
<span class="result-label">Faces Detected:</span>
<span id="facesCount" class="result-value">0</span>
</div>
<div id="facesDetails"></div>
</div>
</div>
</div>
</div>
<script>
const uploadBtn = document.getElementById('uploadBtn');
const cameraBtn = document.getElementById('cameraBtn');
const uploadArea = document.getElementById('uploadArea');
const fileInput = document.getElementById('fileInput');
const cameraContainer = document.getElementById('cameraContainer');
const videoElement = document.getElementById('videoElement');
const captureBtn = document.getElementById('captureBtn');
const scaleSlider = document.getElementById('scaleSlider');
const scaleValue = document.getElementById('scaleValue');
const detectBtn = document.getElementById('detectBtn');
const outputImage = document.getElementById('outputImage');
const placeholderText = document.getElementById('placeholderText');
const resultsContainer = document.getElementById('resultsContainer');
const facesCount = document.getElementById('facesCount');
const facesDetails = document.getElementById('facesDetails');
const loading = document.getElementById('loading');
let currentImage = null;
let stream = null;
uploadBtn.addEventListener('click', () => {
uploadBtn.classList.add('active');
cameraBtn.classList.remove('active');
uploadArea.style.display = 'block';
cameraContainer.style.display = 'none';
stopCamera();
});
cameraBtn.addEventListener('click', () => {
cameraBtn.classList.add('active');
uploadBtn.classList.remove('active');
uploadArea.style.display = 'none';
cameraContainer.style.display = 'block';
startCamera();
});
uploadArea.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', handleImageUpload);
captureBtn.addEventListener('click', captureImage);
detectBtn.addEventListener('click', detectFaces);
scaleSlider.addEventListener('input', () => scaleValue.textContent = scaleSlider.value);
function handleImageUpload(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
currentImage = e.target.result;
outputImage.src = currentImage;
outputImage.style.display = 'block';
placeholderText.style.display = 'none';
detectBtn.disabled = false;
resultsContainer.style.display = 'none';
};
reader.readAsDataURL(file);
}
}
function startCamera() {
if (navigator.mediaDevices?.getUserMedia) {
navigator.mediaDevices.getUserMedia({ video: true })
.then(mediaStream => {
stream = mediaStream;
videoElement.srcObject = stream;
detectBtn.disabled = false;
})
.catch(error => {
console.error("Camera error: ", error);
alert("Unable to access camera. Please check permissions.");
});
} else {
alert("Your browser doesn't support camera access.");
}
}
function stopCamera() {
if (stream) {
stream.getTracks().forEach(track => track.stop());
stream = null;
}
}
function captureImage() {
const canvas = document.createElement('canvas');
canvas.width = videoElement.videoWidth;
canvas.height = videoElement.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
currentImage = canvas.toDataURL('image/png');
outputImage.src = currentImage;
outputImage.style.display = 'block';
placeholderText.style.display = 'none';
resultsContainer.style.display = 'none';
}
async function detectFaces() {
if (!currentImage) {
alert("Please upload an image or capture from camera first.");
return;
}
loading.style.display = 'block';
detectBtn.disabled = true;
try {
const response = await fetch('/detect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ image: currentImage, scale: scaleSlider.value })
});
const data = await response.json();
if (data.success) {
outputImage.src = data.result_image;
displayResults(data.faces_detected, data.face_data);
} else {
alert('Error detecting faces: ' + data.error);
}
} catch (error) {
alert('Error detecting faces. Please try again.');
} finally {
loading.style.display = 'none';
detectBtn.disabled = false;
}
}
function displayResults(faceCount, faceData) {
facesCount.textContent = faceCount;
facesDetails.innerHTML = '';
faceData.forEach(face => {
const faceElement = document.createElement('div');
faceElement.className = 'result-item';
faceElement.innerHTML = `
<span class="result-label">Face ${face.id}:</span>
<span class="result-value">${face.gender}, ${face.age}</span>
`;
facesDetails.appendChild(faceElement);
});
resultsContainer.style.display = 'block';
}
uploadBtn.click();
</script>
</body>
</html>