| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| <title>BOM Pattern Detection</title> |
| <link rel="preconnect" href="https://fonts.googleapis.com" /> |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet" /> |
| <link rel="stylesheet" href="/static/css/style.css" /> |
| </head> |
| <body> |
| |
| <aside class="sidebar"> |
| <div class="sidebar-logo"> |
| <div class="logo-icon"> |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none"> |
| <rect x="2" y="2" width="9" height="9" rx="2" fill="#2563EB"/> |
| <rect x="13" y="2" width="9" height="9" rx="2" fill="#2563EB" opacity="0.5"/> |
| <rect x="2" y="13" width="9" height="9" rx="2" fill="#2563EB" opacity="0.5"/> |
| <rect x="13" y="13" width="9" height="9" rx="2" fill="#2563EB"/> |
| </svg> |
| </div> |
| <span class="logo-text">BOM Detector</span> |
| </div> |
|
|
| <nav class="sidebar-nav"> |
| <a href="#" class="nav-item active" data-page="detect"> |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| <circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/> |
| </svg> |
| <span>Detection</span> |
| </a> |
| <a href="#" class="nav-item" data-page="about"> |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| <circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/> |
| </svg> |
| <span>About</span> |
| </a> |
| </nav> |
|
|
| <div class="sidebar-section"> |
| <div class="section-label">Algorithm</div> |
| <div class="setting-row"> |
| <label class="toggle-label"> |
| <span>Auto-tune mode</span> |
| <div class="toggle" id="autoToggle"> |
| <input type="checkbox" id="autoMode" checked /> |
| <span class="toggle-slider"></span> |
| </div> |
| </label> |
| </div> |
| <div class="setting-row"> |
| <label class="toggle-label"> |
| <span>VLM filter <span class="setting-tag">Qwen2-VL</span></span> |
| <div class="toggle" id="vlmToggle"> |
| <input type="checkbox" id="useVlm" /> |
| <span class="toggle-slider"></span> |
| </div> |
| </label> |
| <p class="setting-hint">Local vision-language model rejects false |
| positives on borderline matches. More accurate, slower (first run |
| downloads ~4.5 GB).</p> |
| </div> |
| </div> |
|
|
| <div class="sidebar-section" id="manualSettings" style="display:none;"> |
| <div class="section-label">Thresholds</div> |
|
|
| <div class="slider-group"> |
| <div class="slider-row"> |
| <span class="slider-label">NCC</span> |
| <span class="slider-val" id="nccVal">0.55</span> |
| </div> |
| <input type="range" class="slider" id="nccThreshold" min="0.1" max="0.9" step="0.01" value="0.55" /> |
| </div> |
|
|
| <div class="slider-group"> |
| <div class="slider-row"> |
| <span class="slider-label">DINOv2 Cosine</span> |
| <span class="slider-val" id="dinoVal">0.84</span> |
| </div> |
| <input type="range" class="slider" id="dinoThreshold" min="0.5" max="0.99" step="0.01" value="0.84" /> |
| </div> |
|
|
| <div class="slider-group"> |
| <div class="slider-row"> |
| <span class="slider-label">NMS IoU</span> |
| <span class="slider-val" id="nmsVal">0.40</span> |
| </div> |
| <input type="range" class="slider" id="nmsThreshold" min="0.1" max="0.9" step="0.01" value="0.40" /> |
| </div> |
| </div> |
|
|
| <div class="sidebar-footer"> |
| <div class="tech-badges"> |
| <span class="badge">NCC</span> |
| <span class="badge">DINOv2</span> |
| <span class="badge">Zero-shot</span> |
| </div> |
| </div> |
| </aside> |
|
|
| |
| <main class="main"> |
| <header class="topbar"> |
| <div class="topbar-left"> |
| <h1 class="page-title">Pattern Detection</h1> |
| <span class="page-subtitle">BOM Engineering Drawings · Zero-shot</span> |
| </div> |
| <div class="topbar-right"> |
| <div class="status-dot" id="statusDot"></div> |
| <span class="status-text" id="statusText">Ready</span> |
| </div> |
| </header> |
|
|
| |
| <section class="upload-section"> |
| <div class="upload-grid"> |
| |
| <div class="upload-card" id="patternDropzone"> |
| <div class="upload-inner"> |
| <div class="upload-icon"> |
| <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"> |
| <rect x="3" y="3" width="18" height="18" rx="3"/> |
| <circle cx="8.5" cy="8.5" r="1.5"/> |
| <polyline points="21 15 16 10 5 21"/> |
| </svg> |
| </div> |
| <div class="upload-label">Pattern Template</div> |
| <div class="upload-hint">Drop image or click to browse</div> |
| <input type="file" id="patternFile" accept="image/*" class="file-input" /> |
| </div> |
| <div class="upload-preview" id="patternPreview" style="display:none;"> |
| <img id="patternImg" alt="Pattern" /> |
| <button class="clear-btn" id="clearPattern">×</button> |
| </div> |
| </div> |
|
|
| |
| <div class="upload-card" id="drawingDropzone"> |
| <div class="upload-inner"> |
| <div class="upload-icon"> |
| <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"> |
| <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/> |
| <polyline points="14 2 14 8 20 8"/> |
| <line x1="16" y1="13" x2="8" y2="13"/> |
| <line x1="16" y1="17" x2="8" y2="17"/> |
| <polyline points="10 9 9 9 8 9"/> |
| </svg> |
| </div> |
| <div class="upload-label">Engineering Drawing</div> |
| <div class="upload-hint">Drop image or click to browse</div> |
| <input type="file" id="drawingFile" accept="image/*" class="file-input" /> |
| </div> |
| <div class="upload-preview" id="drawingPreview" style="display:none;"> |
| <img id="drawingImg" alt="Drawing" /> |
| <button class="clear-btn" id="clearDrawing">×</button> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="run-row"> |
| <button class="run-btn" id="runBtn" disabled> |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"> |
| <polygon points="5 3 19 12 5 21 5 3"/> |
| </svg> |
| Run Detection |
| </button> |
| </div> |
| </section> |
|
|
| |
| <section class="stats-row" id="statsRow" style="display:none;"> |
| <div class="stat-card"> |
| <div class="stat-value" id="statDetections">—</div> |
| <div class="stat-label">Detections</div> |
| </div> |
| <div class="stat-card"> |
| <div class="stat-value" id="statAvgConf">—</div> |
| <div class="stat-label">Avg Confidence</div> |
| </div> |
| <div class="stat-card"> |
| <div class="stat-value" id="statBestDino">—</div> |
| <div class="stat-label">Best DINOv2</div> |
| </div> |
| <div class="stat-card"> |
| <div class="stat-value" id="statTime">—</div> |
| <div class="stat-label">Time (s)</div> |
| </div> |
| </section> |
|
|
| |
| <section class="results-section" id="resultsSection" style="display:none;"> |
| <div class="results-grid"> |
| |
| <div class="result-card viz-card"> |
| <div class="card-header"> |
| <span class="card-title">Annotated Output</span> |
| <button class="download-btn" id="downloadBtn"> |
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> |
| <polyline points="7 10 12 15 17 10"/> |
| <line x1="12" y1="15" x2="12" y2="3"/> |
| </svg> |
| Save |
| </button> |
| </div> |
| <div class="viz-container"> |
| <img id="vizImage" alt="Detection result" /> |
| </div> |
| </div> |
|
|
| |
| <div class="result-card detections-card"> |
| <div class="card-header"> |
| <span class="card-title">Detections</span> |
| <span class="card-count" id="detectionCount">0</span> |
| <button class="download-btn" id="csvBtn" style="display:none;" title="Download CSV"> |
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> |
| <polyline points="7 10 12 15 17 10"/> |
| <line x1="12" y1="15" x2="12" y2="3"/> |
| </svg> |
| CSV |
| </button> |
| </div> |
| <div class="detections-list" id="detectionsList"></div> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <div class="loading-overlay" id="loadingOverlay" style="display:none;"> |
| <div class="loading-card"> |
| <div class="spinner"></div> |
| <div class="loading-title">Detecting Patterns</div> |
| <div class="loading-sub" id="loadingStage">Initializing pipeline…</div> |
| </div> |
| </div> |
|
|
| |
| <section class="about-page" id="aboutPage" style="display:none;"> |
| <div class="about-hero"> |
| <div class="about-hero-icon"> |
| <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"> |
| <circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/> |
| </svg> |
| </div> |
| <h2 class="about-title">BOM Pattern Detection</h2> |
| <p class="about-subtitle">Zero-shot symbol detection for engineering drawings · No training data required</p> |
| <div class="about-badges"> |
| <span class="about-badge">NCC</span> |
| <span class="about-badge">DINOv2 ViT-S/14</span> |
| <span class="about-badge">Qwen2-VL-2B</span> |
| <span class="about-badge">Zero-shot</span> |
| </div> |
| </div> |
|
|
| <div class="about-grid"> |
| |
| <div class="about-card about-card-wide"> |
| <div class="about-card-title"> |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg> |
| Pipeline Architecture |
| </div> |
| <div class="pipeline-steps"> |
| <div class="pipe-step"> |
| <div class="pipe-num" style="background:#6366f1;">0</div> |
| <div class="pipe-body"> |
| <div class="pipe-name">Preprocess</div> |
| <div class="pipe-desc">Adaptive binarize · Denoise</div> |
| </div> |
| <div class="pipe-time"><0.5s</div> |
| </div> |
| <div class="pipe-arrow">→</div> |
| <div class="pipe-step"> |
| <div class="pipe-num" style="background:#2563eb;">1</div> |
| <div class="pipe-body"> |
| <div class="pipe-name">NCC Matching</div> |
| <div class="pipe-desc">Multi-scale · Multi-angle · NMS</div> |
| </div> |
| <div class="pipe-time">15–60s CPU</div> |
| </div> |
| <div class="pipe-arrow">→</div> |
| <div class="pipe-step"> |
| <div class="pipe-num" style="background:#0e9f6e;">2</div> |
| <div class="pipe-body"> |
| <div class="pipe-name">DINOv2 Verify</div> |
| <div class="pipe-desc">Cosine similarity · Derotate</div> |
| </div> |
| <div class="pipe-time">2–10s GPU</div> |
| </div> |
| <div class="pipe-arrow">→</div> |
| <div class="pipe-step"> |
| <div class="pipe-num" style="background:#0891b2;">2b</div> |
| <div class="pipe-body"> |
| <div class="pipe-name">Struct Filters</div> |
| <div class="pipe-desc">Chamfer · NMS · Gap filter</div> |
| </div> |
| <div class="pipe-time"><1s</div> |
| </div> |
| <div class="pipe-arrow" style="opacity:.5;">→</div> |
| <div class="pipe-step" style="opacity:.7; border-style:dashed;"> |
| <div class="pipe-num" style="background:#d97706;">3</div> |
| <div class="pipe-body"> |
| <div class="pipe-name">VLM Filter</div> |
| <div class="pipe-desc">Qwen2-VL · Optional</div> |
| </div> |
| <div class="pipe-time">~0.4s/crop</div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="about-card"> |
| <div class="about-card-title"> |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg> |
| Key Features |
| </div> |
| <ul class="about-list"> |
| <li> |
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#16a34a" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg> |
| Fully zero-shot — no fine-tuning or labeled data |
| </li> |
| <li> |
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#16a34a" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg> |
| Multi-scale (0.7×–1.8×) & multi-angle (±10°, 90°) |
| </li> |
| <li> |
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#16a34a" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg> |
| Handles simple and complex symbol templates |
| </li> |
| <li> |
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#16a34a" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg> |
| Optional VLM stage for hard FP rejection |
| </li> |
| <li> |
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#16a34a" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg> |
| CPU + GPU support · Docker ready |
| </li> |
| </ul> |
| </div> |
|
|
| |
| <div class="about-card"> |
| <div class="about-card-title"> |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg> |
| Tech Stack |
| </div> |
| <div class="tech-table"> |
| <div class="tech-row"><span class="tech-layer">Stage 1</span><span class="tech-val">OpenCV NCC · <code>TM_CCOEFF_NORMED</code></span></div> |
| <div class="tech-row"><span class="tech-layer">Stage 2</span><span class="tech-val">DINOv2 ViT-S/14 · PyTorch</span></div> |
| <div class="tech-row"><span class="tech-layer">Stage 3</span><span class="tech-val">Qwen2-VL-2B · HuggingFace</span></div> |
| <div class="tech-row"><span class="tech-layer">Backend</span><span class="tech-val">FastAPI · Python 3.11</span></div> |
| <div class="tech-row"><span class="tech-layer">Deploy</span><span class="tech-val">Docker · HuggingFace Spaces</span></div> |
| </div> |
| </div> |
|
|
| |
| <div class="about-card about-spec-card"> |
| <div class="about-card-title"> |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg> |
| System Specification |
| </div> |
| <p class="about-spec-desc">Đặc tả kỹ thuật chi tiết toàn bộ hệ thống: kiến trúc pipeline, thuật toán từng stage, các quyết định thiết kế, kết quả thực nghiệm và hướng cải thiện.</p> |
| <div class="about-spec-meta"> |
| <span>13 mục · Pipeline · Filters · VLM · Config · Results</span> |
| </div> |
| <a href="/spec" target="_blank" class="spec-link-btn"> |
| <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> |
| Mở đặc tả hệ thống |
| </a> |
| </div> |
| </div> |
|
|
| <div class="about-footer"> |
| Sotatek AI/CV Engineer Assessment · 2026 · |
| <a href="mailto:duydeodai2003@gmail.com">duydeodai2003@gmail.com</a> |
| </div> |
| </section> |
| </main> |
|
|
| <script src="/static/js/app.js"></script> |
| </body> |
| </html> |
|
|