Paradise151 commited on
Commit
1aa1637
·
verified ·
1 Parent(s): b0b0757

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +47 -219
index.html CHANGED
@@ -4,62 +4,55 @@
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');
@@ -71,7 +64,8 @@
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;
@@ -90,128 +84,6 @@
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
- <script>
182
- const video = document.getElementById('video');
183
- const resultImg = document.getElementById('resultImg');
184
- const jsonOut = document.getElementById('jsonOut');
185
- const startBtn = document.getElementById('startBtn');
186
- const stopBtn = document.getElementById('stopBtn');
187
- const cameraSelect = document.getElementById('cameraSelect');
188
- const confSlider = document.getElementById('confSlider');
189
- const iouSlider = document.getElementById('iouSlider');
190
- const confVal = document.getElementById('confVal');
191
- const iouVal = document.getElementById('iouVal');
192
- const modeSelect = document.getElementById('modeSelect');
193
-
194
- let stream = null;
195
- let running = false;
196
- let active = false;
197
- let currentDeviceId = null;
198
-
199
- confSlider.oninput = () => confVal.textContent = confSlider.value;
200
- iouSlider.oninput = () => iouVal.textContent = iouSlider.value;
201
-
202
- async function enumerateCameras() {
203
- const devices = await navigator.mediaDevices.enumerateDevices();
204
- cameraSelect.innerHTML = '';
205
- const cams = devices.filter(d => d.kind === 'videoinput');
206
- cams.forEach((cam, i) => {
207
- const opt = document.createElement('option');
208
- opt.value = cam.deviceId || i;
209
- opt.textContent = cam.label || `Камера ${i+1}`;
210
- cameraSelect.appendChild(opt);
211
- });
212
- if (cams.length > 0) currentDeviceId = cams[0].deviceId;
213
- }
214
-
215
  async function startCamera(deviceId) {
216
  if (stream) stopCamera();
217
  const constraints = {
@@ -227,23 +99,23 @@
227
  video.srcObject = stream;
228
  await video.play();
229
  }
230
-
231
  function stopCamera() {
232
  if (stream) {
233
  stream.getTracks().forEach(t => t.stop());
234
  stream = null;
235
  }
236
  }
237
-
238
  cameraSelect.onchange = async () => {
239
  currentDeviceId = cameraSelect.value;
240
  await startCamera(currentDeviceId);
241
  };
242
-
243
  async function captureAndSendFrame() {
244
  if (!stream || running || !active) return;
245
  running = true;
246
-
247
  const canvas = document.createElement('canvas');
248
  const w = 480;
249
  const h = Math.floor(w * (video.videoHeight / video.videoWidth));
@@ -251,52 +123,8 @@
251
  canvas.height = h;
252
  const ctx = canvas.getContext('2d');
253
  ctx.drawImage(video, 0, 0, w, h);
254
-
255
  const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/jpeg', 0.85));
256
  const formData = new FormData();
257
  formData.append('file', blob, 'frame.jpg');
258
- formData.append('conf', confSlider.value);
259
- formData.append('iou', iouSlider.value);
260
- formData.append('return_image', modeSelect.value === 'image' ? '1' : '0');
261
-
262
- try {
263
- const resp = await fetch('/predict', { method: 'POST', body: formData });
264
- if (modeSelect.value === 'image') {
265
- const arrBuf = await resp.arrayBuffer();
266
- const blobRes = new Blob([arrBuf], { type: 'image/jpeg' });
267
- resultImg.src = URL.createObjectURL(blobRes);
268
- resultImg.style.display = 'block';
269
- jsonOut.style.display = 'none';
270
- } else {
271
- const data = await resp.json();
272
- jsonOut.textContent = JSON.stringify(data, null, 2);
273
- jsonOut.style.display = 'block';
274
- resultImg.style.display = 'none';
275
- }
276
- } catch (e) {
277
- console.error(e);
278
- }
279
-
280
- running = false;
281
- if (active) requestAnimationFrame(captureAndSendFrame);
282
- }
283
-
284
- startBtn.onclick = async () => {
285
- await enumerateCameras();
286
- await startCamera(currentDeviceId);
287
- active = true;
288
- startBtn.disabled = true;
289
- stopBtn.disabled = false;
290
- captureAndSendFrame(); // запускаем цикл
291
- };
292
-
293
- stopBtn.onclick = () => {
294
- active = false;
295
- startBtn.disabled = false;
296
- stopBtn.disabled = true;
297
- stopCamera();
298
- };
299
- </script>
300
-
301
- </body>
302
- </html>
 
4
  <meta charset="utf-8">
5
  <title>GrechnikNet — YOLOv8n Detection</title>
6
  <style>
7
+ body { background:#111; color:#eee; font-family:sans-serif; text-align:center; }
8
+ .controls { margin:16px; display:flex; flex-wrap:wrap; gap:16px; justify-content:center; }
9
+ .control { background:#1b1b1b; padding:10px; border-radius:8px; }
10
+ label { display:block; margin-bottom:6px; }
 
 
 
11
  input[type="range"] { width:200px; }
12
+ button { padding:8px 14px; border:none; border-radius:6px; background:#3a6df0; color:#fff; cursor:pointer; }
13
  button:disabled { background:#555; cursor:not-allowed; }
14
+ canvas { border:1px solid #333; border-radius:8px; margin-top:12px; }
15
+ pre { text-align:left; background:#0e0e0e; padding:12px; border-radius:8px; }
16
  </style>
17
  </head>
18
  <body>
19
+ <h2>GrechnikNet — Impurity Detection</h2>
20
+ <p>Наведи камеру телефона на гречку и получи боксы</p>
 
 
21
 
22
+ <div class="controls">
23
+ <div class="control">
24
+ <label>Камера:</label>
25
+ <select id="cameraSelect"></select>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  </div>
27
+ <div class="control">
28
+ <label>Confidence: <span id="confVal">0.25</span></label>
29
+ <input type="range" id="confSlider" min="0" max="1" step="0.05" value="0.25">
30
+ </div>
31
+ <div class="control">
32
+ <label>IoU: <span id="iouVal">0.45</span></label>
33
+ <input type="range" id="iouSlider" min="0" max="1" step="0.05" value="0.45">
34
+ </div>
35
+ <div class="control">
36
+ <label>Режим:</label>
37
+ <select id="modeSelect">
38
+ <option value="image">Аннотированное изображение</option>
39
+ <option value="json">JSON боксы</option>
40
+ </select>
41
+ </div>
42
+ <div class="control">
43
+ <button id="startBtn">Старт</button>
44
+ <button id="stopBtn" disabled>Стоп</button>
45
  </div>
46
+ </div>
47
 
48
+ <video id="video" playsinline autoplay muted style="display:none;"></video>
49
+ <canvas id="resultCanvas" width="480" height="480"></canvas>
50
+ <pre id="jsonOut" style="display:none;"></pre>
51
+
52
+ <script>
53
  const video = document.getElementById('video');
54
+ const resultCanvas = document.getElementById('resultCanvas');
55
+ const ctxResult = resultCanvas.getContext('2d');
56
  const jsonOut = document.getElementById('jsonOut');
57
  const startBtn = document.getElementById('startBtn');
58
  const stopBtn = document.getElementById('stopBtn');
 
64
  const modeSelect = document.getElementById('modeSelect');
65
 
66
  let stream = null;
67
+ let active = false;
68
+ let running = false;
69
  let currentDeviceId = null;
70
 
71
  confSlider.oninput = () => confVal.textContent = confSlider.value;
 
84
  if (cams.length > 0) currentDeviceId = cams[0].deviceId;
85
  }
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  async function startCamera(deviceId) {
88
  if (stream) stopCamera();
89
  const constraints = {
 
99
  video.srcObject = stream;
100
  await video.play();
101
  }
102
+
103
  function stopCamera() {
104
  if (stream) {
105
  stream.getTracks().forEach(t => t.stop());
106
  stream = null;
107
  }
108
  }
109
+
110
  cameraSelect.onchange = async () => {
111
  currentDeviceId = cameraSelect.value;
112
  await startCamera(currentDeviceId);
113
  };
114
+
115
  async function captureAndSendFrame() {
116
  if (!stream || running || !active) return;
117
  running = true;
118
+
119
  const canvas = document.createElement('canvas');
120
  const w = 480;
121
  const h = Math.floor(w * (video.videoHeight / video.videoWidth));
 
123
  canvas.height = h;
124
  const ctx = canvas.getContext('2d');
125
  ctx.drawImage(video, 0, 0, w, h);
126
+
127
  const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/jpeg', 0.85));
128
  const formData = new FormData();
129
  formData.append('file', blob, 'frame.jpg');
130
+ formData.append