GrechnikNet / indexLWVRT.html
Paradise151's picture
Rename index.html to indexLWVRT.html
5105cce verified
<!-- СТАБИЛЬНАЯ БОЛЬШАЯ РАБОЧАЯ ВЕРСИЯ В REAL TIME РЕЖИМЕ!!! -->
<!-- indexLWVRT.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>GrechnikNet — YOLOv8n Detection</title>
<style>
body { background:#111; color:#eee; font-family: sans-serif; margin:0; }
header { padding:16px; text-align:center; }
main { display:flex; flex-direction:column; align-items:center; gap:16px; padding:16px; }
video, img { width: min(100vw, 720px); height: auto; border:1px solid #333; border-radius:8px; }
.controls { display:flex; gap:16px; flex-wrap:wrap; align-items:center; justify-content:center; }
.control { background:#1b1b1b; padding:10px 12px; border-radius:8px; }
label { display:block; font-size:14px; margin-bottom:6px; }
input[type="range"] { width:200px; }
button { padding:10px 14px; border:none; border-radius:8px; background:#3a6df0; color:#fff; cursor:pointer; }
button:disabled { background:#555; cursor:not-allowed; }
.row { display:flex; flex-direction:row; gap:16px; flex-wrap:wrap; justify-content:center; }
</style>
</head>
<body>
<header>
<h2>GrechnikNet — Impurity Detection (Phone Camera)</h2>
<p>Наведи камеру телефона на гречку, получи боксы. Без зеркала, корректный цвет.</p>
</header>
<main>
<div class="controls">
<div class="control">
<label>Камера:</label>
<select id="cameraSelect"></select>
</div>
<div class="control">
<label>Confidence: <span id="confVal">0.25</span></label>
<input type="range" id="confSlider" min="0" max="1" step="0.05" value="0.25">
</div>
<div class="control">
<label>IoU: <span id="iouVal">0.45</span></label>
<input type="range" id="iouSlider" min="0" max="1" step="0.05" value="0.45">
</div>
<div class="control">
<label>Режим ответа:</label>
<select id="modeSelect">
<option value="image">Аннотированное изображение</option>
<option value="json">Только боксы (JSON)</option>
</select>
</div>
<div class="control">
<button id="startBtn">Старт</button>
<button id="stopBtn" disabled>Стоп</button>
</div>
</div>
<div class="row">
<video id="video" playsinline autoplay muted></video>
<img id="resultImg" alt="Detections" />
<pre id="jsonOut" style="display:none; width: min(100vw, 720px); background:#0e0e0e; padding:12px; border-radius:8px;"></pre>
</div>
</main>
<script>
const video = document.getElementById('video');
const resultImg = document.getElementById('resultImg');
const jsonOut = document.getElementById('jsonOut');
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const cameraSelect = document.getElementById('cameraSelect');
const confSlider = document.getElementById('confSlider');
const iouSlider = document.getElementById('iouSlider');
const confVal = document.getElementById('confVal');
const iouVal = document.getElementById('iouVal');
const modeSelect = document.getElementById('modeSelect');
let stream = null;
let captureInterval = null;
let currentDeviceId = null;
confSlider.oninput = () => confVal.textContent = confSlider.value;
iouSlider.oninput = () => iouVal.textContent = iouSlider.value;
async function enumerateCameras() {
const devices = await navigator.mediaDevices.enumerateDevices();
cameraSelect.innerHTML = '';
const cams = devices.filter(d => d.kind === 'videoinput');
cams.forEach((cam, i) => {
const opt = document.createElement('option');
opt.value = cam.deviceId || i;
opt.textContent = cam.label || `Камера ${i+1}`;
cameraSelect.appendChild(opt);
});
if (cams.length > 0) currentDeviceId = cams[0].deviceId;
}
async function startCamera(deviceId) {
if (stream) stopCamera();
const constraints = {
video: {
deviceId: deviceId ? { exact: deviceId } : undefined,
facingMode: 'environment', // на телефоне — основная камера
width: { ideal: 720 },
height: { ideal: 720 }
},
audio: false
};
stream = await navigator.mediaDevices.getUserMedia(constraints);
video.srcObject = stream;
await video.play();
}
function stopCamera() {
if (stream) {
stream.getTracks().forEach(t => t.stop());
stream = null;
}
}
cameraSelect.onchange = async () => {
currentDeviceId = cameraSelect.value;
await startCamera(currentDeviceId);
};
async function captureAndSendFrame() {
if (!stream) return;
// Снимаем кадр в canvas
const canvas = document.createElement('canvas');
const w = Math.min(720, video.videoWidth || 640);
const h = Math.floor(w * (video.videoHeight / video.videoWidth));
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// Без зеркала: просто рисуем кадр как есть
ctx.drawImage(video, 0, 0, w, h);
// Конвертим в Blob (JPEG)
const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/jpeg', 0.85));
const formData = new FormData();
formData.append('file', blob, 'frame.jpg');
formData.append('conf', confSlider.value);
formData.append('iou', iouSlider.value);
const returnImage = (modeSelect.value === 'image') ? 1 : 0;
formData.append('return_image', returnImage.toString());
const resp = await fetch('/predict', { method: 'POST', body: formData });
if (returnImage === 1) {
const arrBuf = await resp.arrayBuffer();
const blobRes = new Blob([arrBuf], { type: 'image/jpeg' });
const url = URL.createObjectURL(blobRes);
resultImg.src = url;
resultImg.style.display = 'block';
jsonOut.style.display = 'none';
} else {
const data = await resp.json();
jsonOut.textContent = JSON.stringify(data, null, 2);
jsonOut.style.display = 'block';
resultImg.style.display = 'none';
}
}
startBtn.onclick = async () => {
await enumerateCameras();
await startCamera(currentDeviceId);
startBtn.disabled = true;
stopBtn.disabled = false;
// Частота отправки кадров: 5–10 FPS (баланс скорости/качества)
captureInterval = setInterval(captureAndSendFrame, 120);
};
stopBtn.onclick = () => {
startBtn.disabled = false;
stopBtn.disabled = true;
clearInterval(captureInterval);
captureInterval = null;
stopCamera();
};
// Предзагрузка списка камер (нужно permission для labels; поэтому делаем после старта)
</script>
</body>
</html>