Instructions to use xfcghj/AR with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use xfcghj/AR with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("xfcghj/AR", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
| import io | |
| import os | |
| import tarfile | |
| import pickle | |
| import zstandard | |
| import matplotlib.pyplot as plt | |
| import numpy as np | |
| import torch | |
| import trimesh | |
| # 引入刚才写好的 VAE 包装类 | |
| from models.vae import TripoSGVaeWrapper | |
| # 引入 TripoSG 官方提供的层次化几何提取工具 | |
| from triposg.inference_utils import hierarchical_extract_geometry | |
| # 配置路径 | |
| archive_path = "/home/dataset-assist-0/usr/lh/ysh/dw/RL/AR/data/DyMesh_50000v_16f_0001_part_00" | |
| triposg_weights = "/home/dataset-assist-0/usr/lh/ysh/dw/RL/AR/TripoSG/pretrained_weights/TripoSG" | |
| output_image_path = "vae_reconstruction_test.png" | |
| def get_first_mesh_frame(archive_file): | |
| """从数据集中提取第一个有效物体的第0帧""" | |
| with open(archive_file, 'rb') as fh: | |
| dctx = zstandard.ZstdDecompressor() | |
| with dctx.stream_reader(fh) as reader: | |
| with tarfile.open(fileobj=reader, mode='r|') as tar: | |
| for member in tar: | |
| if member.isfile(): | |
| f = tar.extractfile(member) | |
| if f is not None: | |
| data = pickle.load(io.BytesIO(f.read())) | |
| if isinstance(data, dict) and 'vertices' in data and 'faces' in data: | |
| if data['vertices'].shape[0] == 16: | |
| # 提取第0帧 (形状为 [V, 3]) 和 共享的面片 | |
| v_f0 = data['vertices'][0] | |
| faces = data['faces'] | |
| short_name = os.path.basename(member.name).split('.')[0] | |
| return v_f0, faces, short_name | |
| raise RuntimeError("未在压缩包中找到合法的 16帧 动态 Mesh 数据。") | |
| if __name__ == "__main__": | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| dtype = torch.float16 | |
| # 1. 读取原数据并抽取静态帧 | |
| print("📦 正在从数据包中读取测试 Mesh...") | |
| raw_vertices, raw_faces, obj_name = get_first_mesh_frame(archive_path) | |
| print(f"成功提取物体 [{obj_name}] 的第一帧. 顶点数: {raw_vertices.shape[0]}, 面数: {raw_faces.shape[0]}") | |
| # 2. 对原网格在内存中做归一化,以便后续做对齐可视化 | |
| mesh_raw = trimesh.Trimesh(vertices=raw_vertices, faces=raw_faces, process=False) | |
| max_edge = np.max(mesh_raw.bounding_box.extents) | |
| mesh_raw.vertices = (mesh_raw.vertices - mesh_raw.center_mass) * (2.0 / max_edge) | |
| # 3. 初始化 VAE 包装模型 | |
| print("🔮 正在初始化冻结的 TripoSG VAE 模型...") | |
| vae_wrapper = TripoSGVaeWrapper(weights_dir=triposg_weights, device=device, dtype=dtype) | |
| # 4. 编码 (Mesh -> Latent Code) | |
| # 修改 3. 编码部分的调用逻辑: | |
| print("🚀 正在将 Mesh 表面密集点云编码至隐空间...") | |
| # 将 numpy 数组显式转换为 torch.Tensor | |
| v_tensor = torch.from_numpy(raw_vertices) | |
| f_tensor = torch.from_numpy(raw_faces) | |
| latent_code = vae_wrapper.encode_mesh(v_tensor, f_tensor, num_samples=204800) | |
| print(f"编码成功!隐特征张量形状: {latent_code.shape}") | |
| # 5. 解码重建 (Latent Code -> SDF -> Marching Cubes Mesh) | |
| print("📐 正在通过层次化八叉树提取器(Hierarchical Octree)解码并重建 Mesh...") | |
| geometric_func = lambda x: vae_wrapper.decode_latent(latent_code, sampled_points=x) | |
| output_geometry = hierarchical_extract_geometry( | |
| geometric_func, | |
| device=device, | |
| bounds=(-1.005, -1.005, -1.005, 1.005, 1.005, 1.005), | |
| dense_octree_depth=8, # 密度深度 | |
| hierarchical_octree_depth=9, # 恢复细节的八叉树总深度 | |
| ) | |
| # 将提取出的解算数据转换为 trimesh 对象 | |
| recon_v, recon_f = output_geometry[0] | |
| mesh_recon = trimesh.Trimesh(vertices=recon_v.astype(np.float32), faces=recon_f, process=False) | |
| print(f"重建成功!重建后 Mesh 顶点数: {mesh_recon.vertices.shape[0]}, 面数: {mesh_recon.faces.shape[0]}") | |
| # 🌟 5.5 进行几何指标定量评估 (Evaluation Metric) - 使用 Scipy KDTree 修复版本 | |
| print("📊 正在执行 KDTree 算法,评测重建精度指标...") | |
| from scipy.spatial import KDTree | |
| num_eval_samples = 50000 | |
| # 在两个几何表面分别均匀采样 5万 个测试点 | |
| pts_raw, _ = trimesh.sample.sample_surface(mesh_raw, num_eval_samples) | |
| pts_recon, _ = trimesh.sample.sample_surface(mesh_recon, num_eval_samples) | |
| # 建立空间平衡二叉树,用于秒级检索最近邻点 | |
| tree_raw = KDTree(pts_raw) | |
| tree_recon = KDTree(pts_recon) | |
| # 1. 计算重建点到真值点云的最近距离 (Recon -> Raw) -> 代表重建偏离误差 | |
| dists_recon_to_raw, _ = tree_raw.query(pts_recon, k=1) | |
| # 2. 计算真值点到重建点云的最近距离 (Raw -> Recon) -> 代表细节遗漏误差 | |
| dists_raw_to_recon, _ = tree_recon.query(pts_raw, k=1) | |
| # 计算均值 | |
| mae_recon_to_raw = np.mean(dists_recon_to_raw) | |
| mae_raw_to_recon = np.mean(dists_raw_to_recon) | |
| # 模拟双向倒角距离 (Chamfer Distance) | |
| chamfer_dist_approx = mae_recon_to_raw + mae_raw_to_recon | |
| # 计算在 1% 空间尺度阈值下的 F-Score | |
| obj_scale = np.linalg.norm(mesh_raw.bounding_box.extents) | |
| tau = 0.01 * obj_scale # 阈值设置为物体对角线长度的 1% | |
| precision = np.mean(dists_recon_to_raw < tau) | |
| recall = np.mean(dists_raw_to_recon < tau) | |
| f_score = (2 * precision * recall) / (precision + recall) if (precision + recall) > 0 else 0.0 | |
| # 打印评估报告 | |
| print("\n" + "="*50) | |
| print("📈 VAE Mesh 自编码重建评测报告 (KDTree 加速版):") | |
| print("="*50) | |
| print(f"1. 重建偏离误差 (Recon -> Raw MAE): {mae_recon_to_raw:.6f} (越小越好)") | |
| print(f"2. 细节遗漏误差 (Raw -> Recon MAE): {mae_raw_to_recon:.6f} (越小越好)") | |
| print(f"3. 倒角距离指标 (Chamfer Distance): {chamfer_dist_approx:.6f} (越小越好)") | |
| print(f"4. 表面对齐分数 (F-Score @ 1%): {f_score * 100:.2f}% (越大越好)") | |
| print("="*50 + "\n") | |
| # 6. 使用 Matplotlib 绘制同视角对比图 | |
| print("🎨 正在绘制原网格与 VAE 重建网格的对比效果图...") | |
| fig = plt.figure(figsize=(14, 6)) | |
| # 共同视角参数控制,防止发生视差偏离 | |
| all_points = np.concatenate([mesh_raw.vertices, mesh_recon.vertices], axis=0) | |
| mid_x, mid_y, mid_z = (all_points.max(axis=0) + all_points.min(axis=0)) / 2.0 | |
| max_range = (all_points.max(axis=0) - all_points.min(axis=0)).max() / 2.0 | |
| # 子图 1:原始 Mesh(归一化后) | |
| ax1 = fig.add_subplot(1, 2, 1, projection='3d') | |
| ax1.plot_trisurf(mesh_raw.vertices[:, 0], mesh_raw.vertices[:, 1], mesh_raw.vertices[:, 2], | |
| triangles=mesh_raw.faces, cmap='coolwarm', edgecolor='none', alpha=0.7) | |
| ax1.set_title(f"Original Mesh (Normalized)\n({obj_name})", fontsize=12, weight='bold') | |
| ax1.set_xlim(mid_x - max_range, mid_x + max_range) | |
| ax1.set_ylim(mid_y - max_range, mid_y + max_range) | |
| ax1.set_zlim(mid_z - max_range, mid_z + max_range) | |
| ax1.axis('off') | |
| ax1.view_init(elev=20, azim=45) # 设定固定视角 | |
| # 子图 2:VAE 解码重建的 Mesh | |
| ax2 = fig.add_subplot(1, 2, 2, projection='3d') | |
| ax2.plot_trisurf(mesh_recon.vertices[:, 0], mesh_recon.vertices[:, 1], mesh_recon.vertices[:, 2], | |
| triangles=mesh_recon.faces, cmap='viridis', edgecolor='none', alpha=0.7) | |
| # 在标题上动态附带上 F-Score 信息,方便肉眼和指标联动对照 | |
| ax2.set_title(f"Reconstructed Mesh\n(F-Score: {f_score * 100:.2f}%)", fontsize=12, weight='bold') | |
| ax2.set_xlim(mid_x - max_range, mid_x + max_range) | |
| ax2.set_ylim(mid_y - max_range, mid_y + max_range) | |
| ax2.set_zlim(mid_z - max_range, mid_z + max_range) | |
| ax2.axis('off') | |
| ax2.view_init(elev=20, azim=45) # 设定相同视角 | |
| plt.tight_layout() | |
| plt.savefig(output_image_path, dpi=150, bbox_inches='tight') | |
| plt.close() | |
| print(f"💾 对比可视化结果已成功保存至: {output_image_path}") |