Синхронизация с server/: нормализация видео, овал-подсказка, хосты с путём, DNR-стенд

- app.py: probe_video(), HDR→bt709, fps=30, -t 20, логирование progress/face_ok,
  split_host_path() для хостов с путём (domain.com/v1/giga), FPG_WS_PORT
- templates/index.html: овал-подсказка, canvas 1080×1080, запас 10%
- templates/result.html: стилевые правки (цвета)
- gigadoc_ws.py: поддержка пути в --sdk-host
- test_fpg.py: переведён на DNR-стенд (bearer subprotocol, Origin)
- README: хосты с путём, FPG_WS_PORT, нормализация
- .gitignore: server/, wordpress/, wordpress-plugin/ (отдельные репозитории)
This commit is contained in:
apuc committed 2026-09-17 15:35:10 +03:00
1 parent 421183b7e3
commit 7375618b1d
9 files changed
+423 -108

No files matched your search

+31 -18
View File
@@ -47,7 +47,7 @@
margin: 0 auto;
}
.card {
background: #fff;
background: #faf6ec;
border-radius: 16px;
padding: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
@@ -190,6 +190,16 @@
object-fit: cover;
transform: scaleX(-1);
}
/* Овал — безопасная зона: лицо должно помещаться в него целиком */
.scan-oval {
position: absolute;
inset: 12%;
border: 3px solid rgba(255, 255, 255, 0.9);
border-radius: 50%;
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.4);
pointer-events: none;
z-index: 2;
}
.scan-progress { color: #fff; margin-top: 20px; font-size: 16px; text-align: center; }
.scan-progress-bar {
width: 280px;
@@ -249,8 +259,8 @@
/* Результат */
.result { display: none; }
.summary-box {
background: #f0f4ff;
border-left: 4px solid #0014FC;
background: linear-gradient(135deg, #061A40, #0B4F9C, #2D9CDB);
color: #fff;
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
@@ -281,20 +291,20 @@
font-weight: 600;
}
.status-ok { background: #d4edda; color: #155724; }
.status-warning { background: #fff3cd; color: #856404; }
.status-warning { background: #ffe0d6; color: #a04a2f; }
.status-danger { background: #f8d7da; color: #721c24; }
.status-info { background: #d1ecf1; color: #0c5460; }
.status-info { background: #dbe9ff; color: #1e4a8a; }
.warnings-box {
background: #fff3cd;
border-left: 4px solid #ffc107;
background: #ffe0d6;
border-left: 4px solid #ff7f50;
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.warnings-box h3 { color: #856404; margin-bottom: 10px; font-size: 16px; }
.warnings-box h3 { color: #a04a2f; margin-bottom: 10px; font-size: 16px; }
.warnings-box ul { padding-left: 20px; }
.warnings-box li { margin: 5px 0; color: #856404; }
.warnings-box li { margin: 5px 0; color: #a04a2f; }
.advice-box {
background: #d4edda;
@@ -414,6 +424,7 @@
<div class="scan-modal" id="scanModal">
<div class="scan-video-wrap">
<video class="scan-video" id="scanVideo" autoplay playsinline muted></video>
<div class="scan-oval"></div>
</div>
<div class="scan-progress">
<div id="scanProgressText">Кадров: 0 / 300 (0%)</div>
@@ -422,7 +433,7 @@
</div>
</div>
<button class="scan-cancel" id="scanCancelBtn">✖ Отмена</button>
<div class="scan-hint">Держите лицо в круге, смотрите прямо в камеру, не двигайтесь</div>
<div class="scan-hint">Поместите лицо в овал целиком: лоб, глаза и подбородок — с запасом. Если голова не помещается — отодвиньте телефон</div>
</div>
<script>
@@ -656,7 +667,7 @@
video: {
facingMode: 'user',
width: { ideal: 1280 },
height: { ideal: 720 }
height: { ideal: 1280 }
},
audio: false
});
@@ -676,11 +687,11 @@
scanLastFrameTime = 0;
updateScanProgress(0);
// Canvas 720×720 — квадрат, лицо целиком влезает无论是 portrait или landscape.
// Canvas 1080×1080 — квадрат, лицо целиком влезает в portrait и landscape.
// Серверное extract_frames масштабирует до 1280×720 для SDK.
scanCanvas = document.createElement('canvas');
scanCanvas.width = 720;
scanCanvas.height = 720;
scanCanvas.width = 1080;
scanCanvas.height = 1080;
scanCtx = scanCanvas.getContext('2d');
let captureStream;
@@ -719,16 +730,18 @@
return;
}
if (ts - scanLastFrameTime >= FRAME_INTERVAL) {
// Квадратный center-crop: берём центральную квадратную область из видео.
// Квадратный center-crop с запасом 10%: берём чуть меньший
// квадрат, чтобы голова гарантированно помещалась целиком
// (иначе SDK не находит лицо из-за обрезанной головы).
const vw = scanVideo.videoWidth;
const vh = scanVideo.videoHeight;
if (vw && vh) {
const side = Math.min(vw, vh);
const side = Math.min(vw, vh) * 0.9;
const sx = (vw - side) / 2;
const sy = (vh - side) / 2;
scanCtx.drawImage(scanVideo, sx, sy, side, side, 0, 0, 720, 720);
scanCtx.drawImage(scanVideo, sx, sy, side, side, 0, 0, 1080, 1080);
} else {
scanCtx.drawImage(scanVideo, 0, 0, 720, 720);
scanCtx.drawImage(scanVideo, 0, 0, 1080, 1080);
}
scanFrameCount++;
scanLastFrameTime = ts;
+9 -9
View File
@@ -42,7 +42,7 @@
}
.container { max-width: 900px; margin: 0 auto; }
.card {
background: #fff;
background: #faf6ec;
border-radius: 16px;
padding: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
@@ -95,8 +95,8 @@
.btn-download:active { transform: scale(0.98); }
.summary-box {
background: #f0f4ff;
border-left: 4px solid #0014FC;
background: linear-gradient(135deg, #061A40, #0B4F9C, #2D9CDB);
color: #fff;
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
@@ -109,20 +109,20 @@
.metrics-table tr:hover { background: #fafafa; }
.status-badge { display: inline-block; padding: 3px 10px; border-radius: 12px; font-size: 12px; font-weight: 600; }
.status-ok { background: #d4edda; color: #155724; }
.status-warning { background: #fff3cd; color: #856404; }
.status-warning { background: #ffe0d6; color: #a04a2f; }
.status-danger { background: #f8d7da; color: #721c24; }
.status-info { background: #d1ecf1; color: #0c5460; }
.status-info { background: #dbe9ff; color: #1e4a8a; }
.warnings-box {
background: #fff3cd;
border-left: 4px solid #ffc107;
background: #ffe0d6;
border-left: 4px solid #ff7f50;
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.warnings-box h3 { color: #856404; margin-bottom: 10px; font-size: 16px; }
.warnings-box h3 { color: #a04a2f; margin-bottom: 10px; font-size: 16px; }
.warnings-box ul { padding-left: 20px; }
.warnings-box li { margin: 5px 0; color: #856404; }
.warnings-box li { margin: 5px 0; color: #a04a2f; }
.advice-box {
background: #d4edda;