From 0ec645d1e03562a8cac821d5d5d68c5a428fd0ca Mon Sep 17 00:00:00 2001 From: JianMiau Date: Tue, 28 Jul 2026 11:58:49 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E3=80=8C=E6=A1=86=E9=81=B8?= =?UTF-8?q?=E8=BE=A8=E8=AD=98=E4=B8=BB=E9=AB=94=E3=80=8D=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E9=99=8D=E4=BD=8E=E7=9B=B8=E4=BC=BC=E7=85=A7=E7=89=87?= =?UTF-8?q?=E4=BA=92=E7=9B=B8=E8=AA=8D=E9=8C=AF=EF=BC=88=E6=8E=83=E6=8F=8F?= =?UTF-8?q?=E9=A0=81=20v9=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根本原因: MindAR 以整張照片的特徵點做比對,同場地、同構圖的照片 (例如活動現場連拍)大部分特徵點落在共同背景(場地線條、 天花板、浮水印),會穩定地互相認錯、播出別張的影片。 調高 warmupTolerance 實測無效:錯誤目標是持續穩定匹配, 嚴格化只是更慢地確認錯的答案。 影響: 背景相似的多組照片無法在同一個特徵檔中正確區分。 修法: 1. 管理頁每組配對新增「框主體」:在照片上拖曳框選最有辨識度 的區域(相對座標存於 pairs.json,照片檔不動),編譯時只用 框內影像產生特徵。框的長寬下限 15%,實測框太小(僅臉部 特寫)會特徵點不足而完全偵測不到。 2. server 新增 PUT /api/pairs/:id/crop 儲存裁切框;更動後 自動標記「有變更尚未編譯」。 3. 掃描頁依 mapping 中的 aspect 與 crop 將影片平面放大平移, 辨識目標雖只是照片的一塊,影片仍精確覆蓋整張照片 (已用假相機截圖驗證幾何)。無裁切的舊資料行為不變。 已知限制:兩張照片若互相拍到彼此的主體(同兩人、同場地的 連拍),框選也無法完全區分——框內容本來就存在於另一張裡。 這種情況仍建議更換其中一張照片。 Co-Authored-By: Claude Fable 5 --- public/admin.html | 163 ++++++++++++++++++++++++++++++++++++++++++++-- public/index.html | 22 +++++-- server.js | 22 +++++++ 3 files changed, 196 insertions(+), 11 deletions(-) diff --git a/public/admin.html b/public/admin.html index 1aae1d5..da786ea 100644 --- a/public/admin.html +++ b/public/admin.html @@ -34,6 +34,7 @@ } button:disabled { opacity: .5; cursor: default; } button.danger { background: transparent; color: #d33; padding: 4px 8px; font-weight: 400; } + button.plain { background: transparent; color: #2b6cf5; padding: 4px 8px; font-weight: 400; } button.big { padding: 12px 28px; } table { width: 100%; border-collapse: collapse; font-size: 14px; } th { text-align: left; font-size: 12px; color: #6b7590; font-weight: 500; padding: 6px 8px; } @@ -50,6 +51,30 @@ #msg.err { color: #d33; } .muted { color: #6b7590; font-size: 13px; } progress { width: 240px; } + #cropModal { + position: fixed; inset: 0; z-index: 100; display: flex; + align-items: center; justify-content: center; padding: 20px; + background: rgba(0, 0, 0, .55); + } + #cropModal[hidden] { display: none; } + #cropPanel { + background: #fff; border-radius: 12px; padding: 20px; + max-width: 560px; max-height: 92vh; overflow: auto; + } + @media (prefers-color-scheme: dark) { #cropPanel { background: #1a1f2b; } } + #cropPanel h2 { font-size: 16px; margin: 0 0 8px; } + #cropStage { + position: relative; display: inline-block; margin-top: 10px; + touch-action: none; user-select: none; overflow: hidden; border-radius: 8px; + } + #cropImg { display: block; max-width: 100%; max-height: 58vh; } + #cropRect { + position: absolute; border: 2px solid #2b6cf5; pointer-events: none; + box-shadow: 0 0 0 9999px rgba(0, 0, 0, .5); + } + #cropMsg { min-height: 1.4em; margin-top: 8px; } + #cropMsg.err { color: #d33; } + .cropBtns { display: flex; gap: 10px; justify-content: flex-end; margin-top: 12px; } @@ -82,6 +107,24 @@ + +