File size: 12,493 Bytes
93fbf04
 
 
 
 
 
 
57f57c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93fbf04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57f57c4
93fbf04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57f57c4
93fbf04
 
 
57f57c4
93fbf04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57f57c4
93fbf04
57f57c4
93fbf04
 
 
 
57f57c4
93fbf04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57f57c4
 
93fbf04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57f57c4
93fbf04
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
<!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>