From abb81f6bfd09573fe53488901c81a5927e7a53d9 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Wed, 11 Nov 2026 15:34:43 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E4=B8=80=E4=BA=9B=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=85=BC=E5=AE=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 27 +++++++++++ fill2sc/generate_fill_mesh_hsc.py | 78 +++++++++++++++++++++++++++---- 2 files changed, 95 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 726066d..6dd1646 100644 --- a/.gitignore +++ b/.gitignore @@ -84,3 +84,30 @@ fill2sc/fill_preview.png fill2sc/fill_report.txt .gitignore fill2sc/index.txt +fill2sc/data/20261102/20261102_glasses_vertices.txt +fill2sc/data/20261102/20261102_glasses.blend +fill2sc/data/20261102/20261102.mtl +fill2sc/data/20261102/20261102.zip +fill2sc/data/20261102/20261102Tex1.jpg +fill2sc/data/20261102/fill_20261102_preview.png +fill2sc/data/20261102/index.txt +fill2sc/data/631230/631230_glasses_vertices.txt +fill2sc/data/631230/631230_glasses.blend +fill2sc/data/631230/631230.jpg +fill2sc/data/631230/631230.mtl +fill2sc/data/631230/631230.zip +fill2sc/data/631230/fill_631230_preview.png +fill2sc/data/631230/index.txt +fill2sc/data/632723/632723.jpg +fill2sc/data/632723/632723.mtl +fill2sc/data/632723/fill_632723_preview.png +fill2sc/data/632723/fill_preview.png +fill2sc/data/632723/fill_report.txt +fill2sc/data/632723/index.txt +fill2sc/data/636394/636394_glasses_vertices.txt +fill2sc/data/636394/636394_glasses.blend +fill2sc/data/636394/636394.jpg +fill2sc/data/636394/636394.mtl +fill2sc/data/636394/636394.zip +fill2sc/data/636394/fill_636394_preview.png +fill2sc/data/636394/index.txt diff --git a/fill2sc/generate_fill_mesh_hsc.py b/fill2sc/generate_fill_mesh_hsc.py index fa0c2d5..8ff6066 100644 --- a/fill2sc/generate_fill_mesh_hsc.py +++ b/fill2sc/generate_fill_mesh_hsc.py @@ -92,9 +92,9 @@ class FillMeshGenerator: OUT_PREVIEW = "fill_preview.png" RES = 0.1 # 栅格分辨率 - X0, X1 = -10.2, 10.2 - Y0, Y1 = 9.2, 19.8 - RY0, RY1 = 9.7, 19.3 # 填充区域 y 裁剪(只处理正面眼镜部分) + X0, X1 = -16.0, 16.0 # 原来 -10.2, 10.2 → 扩大到覆盖 x[-13.61, 13.19] + Y0, Y1 = 0.0, 62.0 + RY0, RY1 = 1.0, 61.0 SEP = 0.25 # 区分前后表面的最小 z 间距 FLUSH = 0.15 # 填充前表面距镜框前面的距离(与镜框平齐, 留嵌入余量) EMBED_BOT = 0.8 # 填充底面嵌入脸部深度 @@ -103,7 +103,7 @@ class FillMeshGenerator: CLAMP_BACK = 0.3 # 距最后表面的安全距离(防穿出后脑) GAP_MIN = 0.25 # 填充最小厚度 SIGMA = 2.0 # 高度场平滑 sigma(格) - ERODE = 2 # 足迹腐蚀(格) 0.2, 与膨胀0.3配合净外扩0.1(补偿边界平滑收缩) + ERODE = 4 # 足迹腐蚀(格) 0.2, 与膨胀0.3配合净外扩0.1(补偿边界平滑收缩) BSTEP = 0.18 # 边界重采样步长 BITER = 25 # 边界 Laplacian 平滑迭代 BLAM = 0.6 # 边界平滑系数 @@ -872,6 +872,22 @@ class FillMeshGenerator: thickm = np.where(R, hf - hb, np.nan) self.log(f" 填充厚度: min {np.nanmin(thickm):.2f}, 中位 {np.nanmedian(thickm):.2f}, " f"mean {np.nanmean(thickm):.2f}, max {np.nanmax(thickm):.2f}") + + # # ===== 新增:高度场平滑(三角化前)===== + # # 只对填充区域 R 内部做平滑,不影响边界 + # from scipy.ndimage import gaussian_filter + # hf_smooth = hf.copy() + # hb_smooth = hb.copy() + + # # 前表面平滑(sigma=1.0 对应 1 格半径,即约 0.1 单位的空间平滑) + # hf_smooth[R] = gaussian_filter(np.where(R, hf, 0), sigma=1.0, mode='reflect')[R] + # # 后表面平滑 + # hb_smooth[R] = gaussian_filter(np.where(R, hb, 0), sigma=1.0, mode='reflect')[R] + + # # 用平滑后的值替换 + # hf = hf_smooth + # hb = hb_smooth + # # ===== 结束新增 ===== return hf, hb, frame_band, wall_zone, gz1f, bzs, real_f, fz @@ -1696,6 +1712,43 @@ class FillMeshGenerator: print("报告已写入 fill_report.txt") +# ====================================================================== +# 诊断工具 +# ====================================================================== +def diagnose_indices(obj_path, idx_path): + """诊断索引文件是否匹配模型""" + gen = FillMeshGenerator() + V, F = gen.parse_obj(obj_path) + idx = np.loadtxt(idx_path, dtype=np.int64) - 1 + + # 检查索引范围 + print(f"顶点总数: {len(V)}") + print(f"索引总数: {len(idx)}") + print(f"索引范围: {idx.min()} ~ {idx.max()}") + print(f"超出范围的索引: {(idx >= len(V)).sum()}") + + # 检查眼镜顶点坐标范围 + glasses_V = V[idx] + print(f"\n眼镜顶点坐标范围:") + print(f" x: [{glasses_V[:,0].min():.2f}, {glasses_V[:,0].max():.2f}]") + print(f" y: [{glasses_V[:,1].min():.2f}, {glasses_V[:,1].max():.2f}]") + print(f" z: [{glasses_V[:,2].min():.2f}, {glasses_V[:,2].max():.2f}]") + + # 检查是否在栅格化窗口内 + gen2 = FillMeshGenerator() + in_window = ( + (glasses_V[:,0] > gen2.X0) & (glasses_V[:,0] < gen2.X1) & + (glasses_V[:,1] > gen2.Y0) & (glasses_V[:,1] < gen2.Y1) + ) + print(f"\n在栅格化窗口内的顶点: {in_window.sum()} / {len(idx)}") + print(f" 窗口范围: x[{gen2.X0}, {gen2.X1}], y[{gen2.Y0}, {gen2.Y1}]") + + # 检查眼镜面数 + ism = np.zeros(len(V), bool) + ism[idx] = True + face_g = ism[F].any(1) + print(f"\n眼镜面数: {face_g.sum()} / {len(F)}") + # ====================================================================== # 入口 # ====================================================================== @@ -1707,14 +1760,19 @@ if __name__ == "__main__": help="眼镜顶点索引文件路径 (默认: index.txt)") parser.add_argument("--out_fill", type=str, default="fill_mesh.obj", help="输出填充网格文件路径 (默认: fill_mesh.obj)") + parser.add_argument("--diagnose", action="store_true", + help="仅运行诊断,不生成填充网格") args = parser.parse_args() - gen = FillMeshGenerator( - in_obj=args.in_obj, - in_idx=args.in_idx, - out_fill=args.out_fill - ) - gen.run() + if args.diagnose: + diagnose_indices(args.in_obj, args.in_idx) + else: + gen = FillMeshGenerator( + in_obj=args.in_obj, + in_idx=args.in_idx, + out_fill=args.out_fill + ) + gen.run() # python generate_fill_mesh_hsc.py --in_obj 632723.obj --in_idx index.txt --out_fill fill_mesh.obj \ No newline at end of file