Paradise151 commited on
Commit
07d8394
·
verified ·
1 Parent(s): b4a777f

Rename indexRealTime.html to indexWVRealTime.html

Browse files
Files changed (2) hide show
  1. indexRealTime.html +0 -133
  2. indexWVRealTime.html +182 -0
indexRealTime.html DELETED
@@ -1,133 +0,0 @@
1
- <script>
2
- const video = document.getElementById('video');
3
- const resultImg = document.getElementById('resultImg');
4
- const jsonOut = document.getElementById('jsonOut');
5
- const startBtn = document.getElementById('startBtn');
6
- const stopBtn = document.getElementById('stopBtn');
7
- const cameraSelect = document.getElementById('cameraSelect');
8
- const confSlider = document.getElementById('confSlider');
9
- const iouSlider = document.getElementById('iouSlider');
10
- const confVal = document.getElementById('confVal');
11
- const iouVal = document.getElementById('iouVal');
12
- const modeSelect = document.getElementById('modeSelect');
13
-
14
- let stream = null;
15
- let active = false;
16
- let currentDeviceId = null;
17
- let busy = false; // сервер занят
18
- let pendingFrame = null; // последний кадр в буфере
19
-
20
- confSlider.oninput = () => confVal.textContent = confSlider.value;
21
- iouSlider.oninput = () => iouVal.textContent = iouSlider.value;
22
-
23
- async function enumerateCameras() {
24
- const devices = await navigator.mediaDevices.enumerateDevices();
25
- cameraSelect.innerHTML = '';
26
- const cams = devices.filter(d => d.kind === 'videoinput');
27
- cams.forEach((cam, i) => {
28
- const opt = document.createElement('option');
29
- opt.value = cam.deviceId || i;
30
- opt.textContent = cam.label || `Камера ${i+1}`;
31
- cameraSelect.appendChild(opt);
32
- });
33
- if (cams.length > 0) currentDeviceId = cams[0].deviceId;
34
- }
35
-
36
- async function startCamera(deviceId) {
37
- if (stream) stopCamera();
38
- const constraints = {
39
- video: {
40
- deviceId: deviceId ? { exact: deviceId } : undefined,
41
- facingMode: 'environment',
42
- width: { ideal: 720 },
43
- height: { ideal: 720 }
44
- },
45
- audio: false
46
- };
47
- stream = await navigator.mediaDevices.getUserMedia(constraints);
48
- video.srcObject = stream;
49
- await video.play();
50
- requestAnimationFrame(captureLoop);
51
- }
52
-
53
- function stopCamera() {
54
- if (stream) {
55
- stream.getTracks().forEach(t => t.stop());
56
- stream = null;
57
- }
58
- }
59
-
60
- cameraSelect.onchange = async () => {
61
- currentDeviceId = cameraSelect.value;
62
- await startCamera(currentDeviceId);
63
- };
64
-
65
- function captureLoop() {
66
- if (!active || !stream) return;
67
-
68
- const canvas = document.createElement('canvas');
69
- const w = Math.min(720, video.videoWidth || 640);
70
- const h = Math.floor(w * (video.videoHeight / video.videoWidth));
71
- canvas.width = w;
72
- canvas.height = h;
73
- const ctx = canvas.getContext('2d');
74
- ctx.drawImage(video, 0, 0, w, h);
75
-
76
- canvas.toBlob(blob => {
77
- pendingFrame = blob; // сохраняем последний кадр
78
- if (!busy) sendFrame(); // если сервер свободен — отправляем
79
- }, 'image/jpeg', 0.9);
80
-
81
- requestAnimationFrame(captureLoop);
82
- }
83
-
84
- async function sendFrame() {
85
- if (!pendingFrame) return;
86
- busy = true;
87
-
88
- const formData = new FormData();
89
- formData.append('file', pendingFrame, 'frame.jpg');
90
- formData.append('conf', confSlider.value);
91
- formData.append('iou', iouSlider.value);
92
- const returnImage = (modeSelect.value === 'image') ? 1 : 0;
93
- formData.append('return_image', returnImage.toString());
94
-
95
- pendingFrame = null; // очищаем буфер
96
-
97
- try {
98
- const resp = await fetch('/predict', { method: 'POST', body: formData });
99
- if (returnImage === 1) {
100
- const arrBuf = await resp.arrayBuffer();
101
- const blobRes = new Blob([arrBuf], { type: 'image/jpeg' });
102
- resultImg.src = URL.createObjectURL(blobRes);
103
- resultImg.style.display = 'block';
104
- jsonOut.style.display = 'none';
105
- } else {
106
- const data = await resp.json();
107
- jsonOut.textContent = JSON.stringify(data, null, 2);
108
- jsonOut.style.display = 'block';
109
- resultImg.style.display = 'none';
110
- }
111
- } catch (e) {
112
- console.error("Ошибка запроса:", e);
113
- }
114
-
115
- busy = false;
116
- if (pendingFrame) sendFrame(); // если уже есть новый кадр — сразу отправляем
117
- }
118
-
119
- startBtn.onclick = async () => {
120
- await enumerateCameras();
121
- await startCamera(currentDeviceId);
122
- active = true;
123
- startBtn.disabled = true;
124
- stopBtn.disabled = false;
125
- };
126
-
127
- stopBtn.onclick = () => {
128
- active = false;
129
- startBtn.disabled = false;
130
- stopBtn.disabled = true;
131
- stopCamera();
132
- };
133
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
indexWVRealTime.html ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>GrechnikNet — YOLOv8n Detection</title>
6
+ <style>
7
+ body { background:#111; color:#eee; font-family: sans-serif; margin:0; }
8
+ header { padding:16px; text-align:center; }
9
+ main { display:flex; flex-direction:column; align-items:center; gap:16px; padding:16px; }
10
+ video, img { width: min(100vw, 720px); height: auto; border:1px solid #333; border-radius:8px; }
11
+ .controls { display:flex; gap:16px; flex-wrap:wrap; align-items:center; justify-content:center; }
12
+ .control { background:#1b1b1b; padding:10px 12px; border-radius:8px; }
13
+ label { display:block; font-size:14px; margin-bottom:6px; }
14
+ input[type="range"] { width:200px; }
15
+ button { padding:10px 14px; border:none; border-radius:8px; background:#3a6df0; color:#fff; cursor:pointer; }
16
+ button:disabled { background:#555; cursor:not-allowed; }
17
+ .row { display:flex; flex-direction:row; gap:16px; flex-wrap:wrap; justify-content:center; }
18
+ </style>
19
+ </head>
20
+ <body>
21
+ <header>
22
+ <h2>GrechnikNet — Impurity Detection (Phone Camera)</h2>
23
+ <p>Наведи камеру телефона на гречку, получи боксы. Без зеркала, корректный цвет.</p>
24
+ </header>
25
+
26
+ <main>
27
+ <div class="controls">
28
+ <div class="control">
29
+ <label>Камера:</label>
30
+ <select id="cameraSelect"></select>
31
+ </div>
32
+ <div class="control">
33
+ <label>Confidence: <span id="confVal">0.25</span></label>
34
+ <input type="range" id="confSlider" min="0" max="1" step="0.05" value="0.25">
35
+ </div>
36
+ <div class="control">
37
+ <label>IoU: <span id="iouVal">0.45</span></label>
38
+ <input type="range" id="iouSlider" min="0" max="1" step="0.05" value="0.45">
39
+ </div>
40
+ <div class="control">
41
+ <label>Режим ответа:</label>
42
+ <select id="modeSelect">
43
+ <option value="image">Аннотированное изображение</option>
44
+ <option value="json">Только боксы (JSON)</option>
45
+ </select>
46
+ </div>
47
+ <div class="control">
48
+ <button id="startBtn">Старт</button>
49
+ <button id="stopBtn" disabled>Стоп</button>
50
+ </div>
51
+ </div>
52
+
53
+ <div class="row">
54
+ <video id="video" playsinline autoplay muted></video>
55
+ <img id="resultImg" alt="Detections" />
56
+ <pre id="jsonOut" style="display:none; width: min(100vw, 720px); background:#0e0e0e; padding:12px; border-radius:8px;"></pre>
57
+ </div>
58
+ </main>
59
+
60
+ <script>
61
+ const video = document.getElementById('video');
62
+ const resultImg = document.getElementById('resultImg');
63
+ const jsonOut = document.getElementById('jsonOut');
64
+ const startBtn = document.getElementById('startBtn');
65
+ const stopBtn = document.getElementById('stopBtn');
66
+ const cameraSelect = document.getElementById('cameraSelect');
67
+ const confSlider = document.getElementById('confSlider');
68
+ const iouSlider = document.getElementById('iouSlider');
69
+ const confVal = document.getElementById('confVal');
70
+ const iouVal = document.getElementById('iouVal');
71
+ const modeSelect = document.getElementById('modeSelect');
72
+
73
+ let stream = null;
74
+ let captureInterval = null;
75
+ let currentDeviceId = null;
76
+
77
+ confSlider.oninput = () => confVal.textContent = confSlider.value;
78
+ iouSlider.oninput = () => iouVal.textContent = iouSlider.value;
79
+
80
+ async function enumerateCameras() {
81
+ const devices = await navigator.mediaDevices.enumerateDevices();
82
+ cameraSelect.innerHTML = '';
83
+ const cams = devices.filter(d => d.kind === 'videoinput');
84
+ cams.forEach((cam, i) => {
85
+ const opt = document.createElement('option');
86
+ opt.value = cam.deviceId || i;
87
+ opt.textContent = cam.label || `Камера ${i+1}`;
88
+ cameraSelect.appendChild(opt);
89
+ });
90
+ if (cams.length > 0) currentDeviceId = cams[0].deviceId;
91
+ }
92
+
93
+ async function startCamera(deviceId) {
94
+ if (stream) stopCamera();
95
+ const constraints = {
96
+ video: {
97
+ deviceId: deviceId ? { exact: deviceId } : undefined,
98
+ facingMode: 'environment', // на телефоне — основная камера
99
+ width: { ideal: 720 },
100
+ height: { ideal: 720 }
101
+ },
102
+ audio: false
103
+ };
104
+ stream = await navigator.mediaDevices.getUserMedia(constraints);
105
+ video.srcObject = stream;
106
+ await video.play();
107
+ }
108
+
109
+ function stopCamera() {
110
+ if (stream) {
111
+ stream.getTracks().forEach(t => t.stop());
112
+ stream = null;
113
+ }
114
+ }
115
+
116
+ cameraSelect.onchange = async () => {
117
+ currentDeviceId = cameraSelect.value;
118
+ await startCamera(currentDeviceId);
119
+ };
120
+
121
+ async function captureAndSendFrame() {
122
+ if (!stream) return;
123
+
124
+ // Снимаем кадр в canvas
125
+ const canvas = document.createElement('canvas');
126
+ const w = Math.min(720, video.videoWidth || 640);
127
+ const h = Math.floor(w * (video.videoHeight / video.videoWidth));
128
+ canvas.width = w;
129
+ canvas.height = h;
130
+ const ctx = canvas.getContext('2d');
131
+
132
+ // Без зеркала: просто рисуем кадр как есть
133
+ ctx.drawImage(video, 0, 0, w, h);
134
+
135
+ // Конвертим в Blob (JPEG)
136
+ const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/jpeg', 0.85));
137
+ const formData = new FormData();
138
+ formData.append('file', blob, 'frame.jpg');
139
+ formData.append('conf', confSlider.value);
140
+ formData.append('iou', iouSlider.value);
141
+
142
+ const returnImage = (modeSelect.value === 'image') ? 1 : 0;
143
+ formData.append('return_image', returnImage.toString());
144
+
145
+ const resp = await fetch('/predict', { method: 'POST', body: formData });
146
+
147
+ if (returnImage === 1) {
148
+ const arrBuf = await resp.arrayBuffer();
149
+ const blobRes = new Blob([arrBuf], { type: 'image/jpeg' });
150
+ const url = URL.createObjectURL(blobRes);
151
+ resultImg.src = url;
152
+ resultImg.style.display = 'block';
153
+ jsonOut.style.display = 'none';
154
+ } else {
155
+ const data = await resp.json();
156
+ jsonOut.textContent = JSON.stringify(data, null, 2);
157
+ jsonOut.style.display = 'block';
158
+ resultImg.style.display = 'none';
159
+ }
160
+ }
161
+
162
+ startBtn.onclick = async () => {
163
+ await enumerateCameras();
164
+ await startCamera(currentDeviceId);
165
+ startBtn.disabled = true;
166
+ stopBtn.disabled = false;
167
+ // Частота отправки кадров: 5–10 FPS (баланс скорости/качества)
168
+ captureInterval = setInterval(captureAndSendFrame, 120);
169
+ };
170
+
171
+ stopBtn.onclick = () => {
172
+ startBtn.disabled = false;
173
+ stopBtn.disabled = true;
174
+ clearInterval(captureInterval);
175
+ captureInterval = null;
176
+ stopCamera();
177
+ };
178
+
179
+ // Предзагрузка списка камер (нужно permission для labels; поэтому делаем после старта)
180
+ </script>
181
+ </body>
182
+ </html>