PatriciaWening commited on
Commit
cb28fad
·
verified ·
1 Parent(s): f6abaad

Upload 4 files

Browse files
Files changed (4) hide show
  1. CameraPage.html +43 -0
  2. ReportPage.html +65 -0
  3. cameraPage.js +173 -0
  4. reportPage.js +112 -0
CameraPage.html ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="id">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Pendeteksi Tingkat Stres</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ </head>
9
+ <body class="bg-gray-100 font-sans">
10
+
11
+ <div class="max-w-2xl mx-auto p-6">
12
+
13
+ <h1 class="text-3xl font-bold text-center text-gray-800 mb-8">Pendeteksi Tingkat Stres</h1>
14
+
15
+ <div class="bg-white p-6 rounded-lg shadow-md">
16
+ <label for="time-input" class="block text-lg font-semibold text-gray-700 mb-2">Masukkan Waktu Pengamatan (detik):</label>
17
+ <input id="time-input" type="number" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Contoh: 10" min="1">
18
+ <button id="start-btn" class="mt-4 w-full py-2 px-4 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition">Mulai Deteksi</button>
19
+ </div>
20
+
21
+ <div class="mt-6 text-center">
22
+ <p id="countdown" class="text-2xl font-bold text-gray-800">00:00</p>
23
+ </div>
24
+
25
+ <div class="mt-8">
26
+ <video id="video" width="100%" height="auto" class="border-2 border-gray-300 rounded-lg" autoplay></video>
27
+ </div>
28
+
29
+ <div class="mt-6 text-center">
30
+ <p id="status" class="text-lg text-gray-700">Tunggu untuk memulai...</p>
31
+ <p id="stress-level" class="text-xl font-bold text-red-600"></p>
32
+ </div>
33
+
34
+ <div class="mt-6 text-center hidden" id="show-result-btn">
35
+ <a href="ReportPage.html" class="w-full py-2 px-4 bg-green-600 text-white rounded-lg hover:bg-green-700 transition">Show Result</a>
36
+ </div>
37
+
38
+ </div>
39
+
40
+ <script src="cameraPage.js"></script>
41
+
42
+ </body>
43
+ </html>
ReportPage.html ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="id">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Laporan Deteksi Stres</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
+ </head>
10
+ <body class="bg-gray-100 font-sans">
11
+
12
+ <div class="max-w-4xl mx-auto p-6">
13
+
14
+ <h1 class="text-3xl font-bold text-center text-gray-800 mb-8">Laporan Analisis Tingkat Stres</h1>
15
+
16
+ <div class="bg-white p-6 rounded-lg shadow-md mb-6">
17
+ <h2 class="text-xl font-semibold text-gray-700 mb-4">Ringkasan Analisis</h2>
18
+
19
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
20
+ <div>
21
+ <p class="text-gray-600">Emosi Dominan:</p>
22
+ <p id="dominant-emotion" class="text-2xl font-bold text-blue-600"></p>
23
+
24
+ <p class="text-gray-600 mt-4">Rata-rata Tingkat Stres:</p>
25
+ <p id="avg-stress" class="text-2xl font-bold"></p>
26
+
27
+ <p class="text-gray-600 mt-4">Total Frame Dianalisis:</p>
28
+ <p id="total-frames" class="text-2xl font-bold text-gray-800"></p>
29
+ </div>
30
+
31
+ <div>
32
+ <p class="text-gray-600">Tingkat Stres Tertinggi:</p>
33
+ <p id="max-stress" class="text-2xl font-bold text-red-600"></p>
34
+
35
+ <p class="text-gray-600 mt-4">Tingkat Stres Terendah:</p>
36
+ <p id="min-stress" class="text-2xl font-bold text-green-600"></p>
37
+
38
+ <p class="text-gray-600 mt-4">Durasi Analisis:</p>
39
+ <p id="duration" class="text-2xl font-bold text-gray-800"></p>
40
+ </div>
41
+ </div>
42
+ </div>
43
+
44
+ <div class="bg-white p-6 rounded-lg shadow-md mb-6">
45
+ <h2 class="text-xl font-semibold text-gray-700 mb-4">Distribusi Emosi</h2>
46
+ <div class="w-full h-64">
47
+ <canvas id="emotion-chart"></canvas>
48
+ </div>
49
+ </div>
50
+
51
+ <div class="bg-white p-6 rounded-lg shadow-md mb-6">
52
+ <h2 class="text-xl font-semibold text-gray-700 mb-4">Interpretasi Tingkat Stres</h2>
53
+ <div id="stress-interpretation" class="p-4 rounded-lg"></div>
54
+ </div>
55
+
56
+ <div class="mt-6 text-center">
57
+ <a href="CameraPage.html" class="py-2 px-4 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition">Kembali ke Kamera</a>
58
+ </div>
59
+
60
+ </div>
61
+
62
+ <script src="reportPage.js"></script>
63
+
64
+ </body>
65
+ </html>
cameraPage.js ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const timeInput = document.getElementById('time-input');
2
+ const startBtn = document.getElementById('start-btn');
3
+ const videoElement = document.getElementById('video');
4
+ const statusElement = document.getElementById('status');
5
+ const stressLevelElement = document.getElementById('stress-level');
6
+ const countdownElement = document.getElementById('countdown');
7
+ const showResultBtn = document.getElementById('show-result-btn');
8
+
9
+ let stream;
10
+ let interval;
11
+ let countdownInterval;
12
+ let totalTime;
13
+ let frameCount = 0;
14
+ let countdownTime;
15
+ let sessionId = Date.now().toString();
16
+
17
+ async function startCamera() {
18
+ try {
19
+ stream = await navigator.mediaDevices.getUserMedia({
20
+ video: true,
21
+ });
22
+ videoElement.srcObject = stream;
23
+ } catch (err) {
24
+ console.error("Error accessing the camera", err);
25
+ statusElement.textContent = "Gagal mengakses kamera.";
26
+ }
27
+ }
28
+
29
+ function stopCamera() {
30
+ if (stream) {
31
+ let tracks = stream.getTracks();
32
+ tracks.forEach(track => track.stop());
33
+ videoElement.srcObject = null;
34
+ }
35
+ }
36
+
37
+ async function sendFrameToBackend(dataUrl) {
38
+ try {
39
+ console.log("Preparing to send frame to backend...");
40
+
41
+ const formData = new FormData();
42
+ formData.append('image', dataUrl);
43
+ formData.append('sessionId', sessionId);
44
+
45
+ console.log("Sending request to backend...");
46
+
47
+ const controller = new AbortController();
48
+ const timeoutId = setTimeout(() => controller.abort(), 10000);
49
+
50
+ const apiResponse = await fetch('https://PatriciaWening-emoapi.hf.space/api/deteksi-emosi', {
51
+ method: 'POST',
52
+ body: formData,
53
+ signal: controller.signal
54
+ });
55
+
56
+ clearTimeout(timeoutId);
57
+
58
+ if (!apiResponse.ok) {
59
+ const errorText = await apiResponse.text();
60
+ console.error(`Server returned error ${apiResponse.status}: ${errorText}`);
61
+ throw new Error(`HTTP error! Status: ${apiResponse.status}`);
62
+ }
63
+
64
+ const data = await apiResponse.json();
65
+ console.log("API response:", data);
66
+
67
+ if (data.faceDetected) {
68
+ drawFaceRectangle(data.faceRegion);
69
+ updateStressLevel(data.stressLevel, data.emotion);
70
+ statusElement.textContent = `Emosi terdeteksi: ${data.emotion}, Tingkat stres: ${data.stressLevel}%`;
71
+ } else {
72
+ statusElement.textContent = data.error || "Wajah tidak terdeteksi, mohon cek posisi kamera.";
73
+ }
74
+ } catch (error) {
75
+ console.error('Error sending frame to backend:', error);
76
+ statusElement.textContent = "Kesalahan saat memproses data: " + error.message;
77
+ }
78
+ }
79
+
80
+ function drawFaceRectangle(region) {
81
+ const canvas = document.createElement('canvas');
82
+ canvas.width = videoElement.videoWidth;
83
+ canvas.height = videoElement.videoHeight;
84
+ const context = canvas.getContext('2d');
85
+
86
+ context.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
87
+
88
+ context.strokeStyle = '#00FF00';
89
+ context.lineWidth = 3;
90
+ context.strokeRect(region.x, region.y, region.width, region.height);
91
+
92
+ const dataURL = canvas.toDataURL('image/png');
93
+ const tempImg = document.createElement('img');
94
+ tempImg.src = dataURL;
95
+ tempImg.style.width = '100%';
96
+ tempImg.style.height = 'auto';
97
+ tempImg.style.border = '2px solid #ccc';
98
+ tempImg.style.borderRadius = '8px';
99
+
100
+ const videoParent = videoElement.parentNode;
101
+ videoParent.insertBefore(tempImg, videoElement);
102
+ videoElement.style.display = 'none';
103
+
104
+ setTimeout(() => {
105
+ tempImg.remove();
106
+ videoElement.style.display = 'block';
107
+ }, 200);
108
+ }
109
+
110
+ function updateStressLevel(stressLevel, emotion) {
111
+ stressLevelElement.textContent = `Tingkat Stres: ${stressLevel} (${emotion})`;
112
+
113
+ if (stressLevel >= 80) {
114
+ stressLevelElement.classList.remove('text-green-600', 'text-yellow-600');
115
+ stressLevelElement.classList.add('text-red-600');
116
+ } else if (stressLevel >= 50) {
117
+ stressLevelElement.classList.remove('text-green-600', 'text-red-600');
118
+ stressLevelElement.classList.add('text-yellow-600');
119
+ } else {
120
+ stressLevelElement.classList.remove('text-yellow-600', 'text-red-600');
121
+ stressLevelElement.classList.add('text-green-600');
122
+ }
123
+
124
+ localStorage.setItem('currentSessionId', sessionId);
125
+ }
126
+
127
+ async function captureAndAnalyze() {
128
+ const canvas = document.createElement('canvas');
129
+ const context = canvas.getContext('2d');
130
+ canvas.width = videoElement.videoWidth;
131
+ canvas.height = videoElement.videoHeight;
132
+ context.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
133
+
134
+ const frame = canvas.toDataURL('image/jpeg');
135
+ sendFrameToBackend(frame);
136
+
137
+ frameCount++;
138
+ }
139
+
140
+ function updateCountdown() {
141
+ const minutes = Math.floor(countdownTime / 60);
142
+ const seconds = countdownTime % 60;
143
+ countdownElement.textContent = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
144
+ countdownTime--;
145
+
146
+ if (countdownTime < 0) {
147
+ clearInterval(countdownInterval);
148
+ }
149
+ }
150
+
151
+ function startDetection() {
152
+ totalTime = parseInt(timeInput.value) * 1000;
153
+ if (isNaN(totalTime) || totalTime <= 0) {
154
+ statusElement.textContent = "Waktu input tidak valid.";
155
+ return;
156
+ }
157
+
158
+ countdownTime = totalTime / 1000;
159
+ countdownInterval = setInterval(updateCountdown, 1000);
160
+
161
+ statusElement.textContent = `Kamera aktif selama ${totalTime / 1000} detik.`;
162
+ startCamera();
163
+ interval = setInterval(captureAndAnalyze, 1000);
164
+
165
+ setTimeout(() => {
166
+ clearInterval(interval);
167
+ stopCamera();
168
+ statusElement.textContent = "Proses selesai!";
169
+ showResultBtn.classList.remove('hidden');
170
+ }, totalTime);
171
+ }
172
+
173
+ startBtn.addEventListener('click', startDetection);
reportPage.js ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const sessionId = localStorage.getItem('currentSessionId');
2
+
3
+ async function fetchReportData() {
4
+ try {
5
+ if (!sessionId) {
6
+ document.getElementById('dominant-emotion').textContent = "Tidak ada data";
7
+ return;
8
+ }
9
+
10
+ const response = await fetch(`http://localhost:8080/api/session-report/${sessionId}`);
11
+ const data = await response.json();
12
+
13
+ if (data.error) {
14
+ document.getElementById('dominant-emotion').textContent = data.error;
15
+ return;
16
+ }
17
+
18
+ document.getElementById('dominant-emotion').textContent = capitalizeFirstLetter(data.dominantEmotion);
19
+ document.getElementById('avg-stress').textContent = data.averageStressLevel;
20
+ setStressLevelColor('avg-stress', data.averageStressLevel);
21
+ document.getElementById('total-frames').textContent = data.totalFrames;
22
+ document.getElementById('max-stress').textContent = data.maxStressLevel;
23
+ document.getElementById('min-stress').textContent = data.minStressLevel;
24
+ document.getElementById('duration').textContent = `${Math.round(data.totalFrames / 1)} detik`;
25
+
26
+ setStressInterpretation(data.averageStressLevel);
27
+
28
+ createEmotionChart(data.emotionCounts);
29
+ } catch (error) {
30
+ console.error('Error fetching report data:', error);
31
+ document.getElementById('dominant-emotion').textContent = "Error mengambil data laporan";
32
+ }
33
+ }
34
+
35
+ function capitalizeFirstLetter(string) {
36
+ return string.charAt(0).toUpperCase() + string.slice(1);
37
+ }
38
+
39
+ function setStressLevelColor(elementId, stressLevel) {
40
+ const element = document.getElementById(elementId);
41
+
42
+ if (stressLevel >= 80) {
43
+ element.classList.add('text-red-600');
44
+ } else if (stressLevel >= 50) {
45
+ element.classList.add('text-yellow-600');
46
+ } else {
47
+ element.classList.add('text-green-600');
48
+ }
49
+ }
50
+
51
+ function setStressInterpretation(stressLevel) {
52
+ const interpretationElement = document.getElementById('stress-interpretation');
53
+ let message, bgColor, textColor;
54
+
55
+ if (stressLevel >= 80) {
56
+ message = "Tingkat stres tinggi. Rekomendasi: Istirahat dan konsultasi dengan profesional kesehatan mental.";
57
+ bgColor = "bg-red-100";
58
+ textColor = "text-red-800";
59
+ } else if (stressLevel >= 50) {
60
+ message = "Tingkat stres sedang. Rekomendasi: Pertimbangkan teknik relaksasi dan manajemen stres.";
61
+ bgColor = "bg-yellow-100";
62
+ textColor = "text-yellow-800";
63
+ } else {
64
+ message = "Tingkat stres rendah. Tetap jaga kesehatan fisik dan mental.";
65
+ bgColor = "bg-green-100";
66
+ textColor = "text-green-800";
67
+ }
68
+
69
+ interpretationElement.classList.add(bgColor, textColor);
70
+ interpretationElement.textContent = message;
71
+ }
72
+
73
+ function createEmotionChart(emotionCounts) {
74
+ const ctx = document.getElementById('emotion-chart').getContext('2d');
75
+
76
+ const labels = Object.keys(emotionCounts).map(emotion => capitalizeFirstLetter(emotion));
77
+ const data = Object.values(emotionCounts);
78
+
79
+ const backgroundColors = [
80
+ 'rgba(255, 99, 132, 0.7)',
81
+ 'rgba(75, 192, 192, 0.7)',
82
+ 'rgba(255, 205, 86, 0.7)',
83
+ 'rgba(54, 162, 235, 0.7)',
84
+ 'rgba(153, 102, 255, 0.7)',
85
+ 'rgba(201, 203, 207, 0.7)',
86
+ 'rgba(255, 159, 64, 0.7)'
87
+ ];
88
+
89
+ new Chart(ctx, {
90
+ type: 'doughnut',
91
+ data: {
92
+ labels: labels,
93
+ datasets: [{
94
+ data: data,
95
+ backgroundColor: backgroundColors.slice(0, labels.length),
96
+ borderColor: 'white',
97
+ borderWidth: 2
98
+ }]
99
+ },
100
+ options: {
101
+ responsive: true,
102
+ maintainAspectRatio: false,
103
+ plugins: {
104
+ legend: {
105
+ position: 'right'
106
+ }
107
+ }
108
+ }
109
+ });
110
+ }
111
+
112
+ window.addEventListener('DOMContentLoaded', fetchReportData);