Kaynağa Gözat

chore: 完善.gitignore大文件忽略规则,将超2MB的zip/备份图移出跟踪,同步报价页面当前进度

缪子怡 3 hafta önce
ebeveyn
işleme
668b522ef5
6 değiştirilmiş dosya ile 69 ekleme ve 13 silme
  1. 55 0
      .gitignore
  2. 14 13
      AI快速报价_Demo2.optimizing.html
  3. BIN
      _ref_quote.zip
  4. BIN
      _xz1.zip
  5. BIN
      drawing2.png
  6. BIN
      user_drawing_backup.png

+ 55 - 0
.gitignore

@@ -11,3 +11,58 @@ __pycache__/
 
 # Node
 node_modules/
+
+# ===== 拉迷房型图应用 专属忽略(二进制/大文件不入库)=====
+# 原始户型图/洗图产物
+*.dwg
+*.dxf
+*.psd
+*.tif
+*.tiff
+*.ai
+*.pdf
+*.skp
+*.3ds
+*.max
+*.rvt
+*.rfa
+
+# 图片原图(导出的结果图如需入库请压缩为webp/png且<500KB放 assets/optimized/)
+uploads/
+original-images/
+!assets/optimized/*.webp
+
+# 模型与AI权重
+*.onnx
+*.pt
+*.pth
+*.weights
+*.bin
+models/
+
+# 用户数据与运行时
+*.sqlite
+*.db
+*.log
+logs/
+tmp/
+.cache/
+
+# 依赖与构建
+dist/
+build/
+.next/
+coverage/
+
+# 环境与密钥(严禁提交)
+.env
+.env.*
+*.key
+*secret*
+
+# 超过2MB的临时/备份大文件(铁律:单文件>2MB禁止入库)
+*.zip
+_ref_quote.zip
+_xz1.zip
+drawing2.png
+user_drawing_backup.png

+ 14 - 13
AI快速报价_Demo2.optimizing.html

@@ -933,7 +933,7 @@ function chrTemplates(){if(CHR_T)return CHR_T;
   return CHR_T;}
 /* 读取红框旁的红字柜体名称标注:高分裁剪→红掩码→列投影分字→数字/短横前缀跳过→汉字模板匹配;返回名称串,失败返回 null */
 function readRedLabel(b0,AW,AH,k){
-  const OF=2,OM=6;
+  const OF=Math.max(2,Math.min(4,Math.round(1/k))),OM=6;   /* 尽量按原图分辨率裁剪,避免降采样糊化笔画 */
   const ox0=Math.max(0,b0.x-OM),oy0=Math.max(0,b0.y-OM);
   const ow=Math.min(AW-ox0,b0.w+2*OM),oh=Math.min(AH-oy0,b0.h+2*OM);
   const oc=document.createElement("canvas");oc.width=ow*OF;oc.height=oh*OF;
@@ -957,7 +957,7 @@ function readRedLabel(b0,AW,AH,k){
   for(const p of parts){
     const bmp={w:p.w,h:p.h,a:new Uint8Array(p.w*p.h)};
     for(let y=0;y<p.h;y++)for(let x=0;x<p.w;x++)bmp.a[y*p.w+x]=rm2[(p.y+y)*W+(p.x+x)];
-    if(p.h<=0.45*maxH){dbg.parts.push({w:p.w,h:p.h,cls:"dash"});continue;}   /* 短横分隔符(如"2-电视柜"的"-") */
+    if(p.w<=0.45*maxH){dbg.parts.push({w:p.w,h:p.h,cls:"narrow"});continue;}   /* 窄部件(短横/数字前缀/断裂笔画):柜名汉字均为全宽字,一律跳过 */
     const dn=bmNorm(bmp,9,12);let bD=-1;
     for(let dg=0;dg<10;dg++){const v=bmIou(dn,DT[dg]);if(v>bD)bD=v;}
     const cn2=bmNorm(bmp,20,20);let bC=-1,bCh="";
@@ -1086,9 +1086,9 @@ async function runAI(){
       else logAI("· OCR 未能置信读出蓝色标注值 → 请在下方输入框手动填写标注数值(默认按 mm)","e");}
   }
   /* 图纸已用红字标注柜体名称(如"2-电视柜")→ 识别之,对应红框柜体直接采用图纸名称,系统不再二次命名 */
-  if(S.redbox){const LM=dilateMask(redm,AW,AH,2);
+  if(S.redbox){const LM=dilateMask(redm,AW,AH,3);
     for(const b0 of cc(LM)){
-      if(b0.w<18||b0.w>0.4*AW||b0.h<7||b0.h>30||b0.area<40)continue;
+      if(b0.w<18||b0.w>0.4*AW||b0.h<7||b0.h>40||b0.area<40)continue;
       const txt=readRedLabel(b0,AW,AH,k);if(txt)S.redLabels.push({x:b0.x,y:b0.y,w:b0.w,h:b0.h,text:txt});}
     if(S.redLabels.length)logAI("· 图纸红字柜体名称识别:"+S.redLabels.length+" 处("+S.redLabels.map(l=>l.text).join("、")+")→ 对应红框柜体直接按图纸标注命名,系统不二次命名","b");}
   setProg(0,"done");setProg(1,"running");
@@ -1405,17 +1405,18 @@ async function runAI(){
     if(S.scale||S.ft==="cad"){const wM=c.w/k*dimScale(),hM=c.h/k*dimScale();
       logAI("· "+nm+"["+src+"] "+tag+":实测 "+Math.round(c.w/k)+"×"+Math.round(c.h/k)+"px × "+(dimScale()*1000).toFixed(2)+"mm/px → 最长边 "+fmt2(Math.max(wM,hM))+"m × 高 "+fmt2(fh())+"m → 投影面积 = <b>"+fmt2(Math.max(wM,hM)*fh())+"㎡</b>");}
     else logAI("· "+nm+"["+src+"] "+tag+":"+Math.round(c.w/k)+"px × "+Math.round(c.h/k)+"px(比例未建立,补充已知尺寸后自动换算)","w");};
-  const usedLb=new Set();
-  for(const c of S.redRects){if(cn>=8)break;
+  /* 红字标签↔红框全局最近配对:bbox 间隙距离 ≤60px,按距离升序贪心,一框一标签 */
+  const rcName=new Map();
+  if(S.redLabels&&S.redLabels.length){const pairs=[];
+    S.redRects.forEach((c,ri)=>{for(let li=0;li<S.redLabels.length;li++){const lb=S.redLabels[li];
+      const dx=Math.max(c.x-(lb.x+lb.w),lb.x-(c.x+c.w),0),dy=Math.max(c.y-(lb.y+lb.h),lb.y-(c.y+c.h),0);
+      const dd2=Math.hypot(dx,dy);if(dd2<=60)pairs.push({d:dd2,ri,li});}});
+    pairs.sort((a,b)=>a.d-b.d);const usedR=new Set(),usedL=new Set();
+    for(const p of pairs){if(usedR.has(p.ri)||usedL.has(p.li))continue;usedR.add(p.ri);usedL.add(p.li);rcName.set(p.ri,S.redLabels[p.li].text);}}
+  for(let ri=0;ri<S.redRects.length;ri++){if(cn>=8)break;const c=S.redRects[ri];
     if(kitchen){const ov=Math.max(0,Math.min(c.x+c.w,kitchen.x+kitchen.w)-Math.max(c.x,kitchen.x))*Math.max(0,Math.min(c.y+c.h,kitchen.y+kitchen.h)-Math.max(c.y,kitchen.y));
       if(ov>=0.9*kitchen.w*kitchen.h)continue;}   // 红框整包厨房台面带=校准框,非柜体圈注
-    /* 红字标签关联:标签与红框的 bbox 间隙距离 ≤60px 且最近者 → 该红框名称直接采用图纸标注 */
-    let nameOv=null,bd2=1e9,nameOvLi=-1;
-    if(S.redLabels)for(let li=0;li<S.redLabels.length;li++){if(usedLb.has(li))continue;const lb=S.redLabels[li];
-      const dx=Math.max(c.x-(lb.x+lb.w),lb.x-(c.x+c.w),0),dy=Math.max(c.y-(lb.y+lb.h),lb.y-(c.y+c.h),0);
-      const dd2=Math.hypot(dx,dy);if(dd2<=60&&dd2<bd2){bd2=dd2;nameOv=lb.text;nameOvLi=li;}}
-    if(nameOv!==null)usedLb.add(nameOvLi);
-    pushCab(c,"红框",nameOv);}
+    pushCab(c,"红框",rcName.get(ri)||null);}
   if(S.redbox)logAI("· 场景B:柜体边界直接取图纸红框,系统不二次抓取(不合并 AI 特征抓取、不二次红框标注)","w");
   else for(const c of wards){if(cn>=8)break;pushCab(c,"AI");}   /* 场景A 无红框,无需红框去重 */
   logAI(ph("· 柜体识别:"+cn+" 个(最长边×层高=投影面积,均为到顶高柜)"),"b");await S2(200);

BIN
_ref_quote.zip


BIN
_xz1.zip


BIN
drawing2.png


BIN
user_drawing_backup.png