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
File size: 5,799 Bytes
f0fc238 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | import os
import sys
triposg_root = "/home/dataset-assist-0/usr/lh/ysh/dw/RL/AR/TripoSG"
if triposg_root not in sys.path:
sys.path.append(triposg_root)
import torch
import torch.nn as nn
import numpy as np
import trimesh
# 现在可以完美导入了
from triposg.models.autoencoders import TripoSGVAEModel
class TripoSGVaeWrapper(nn.Module):
def __init__(self, weights_dir: str = "/home/dataset-assist-0/usr/lh/ysh/dw/RL/AR/TripoSG/pretrained_weights/TripoSG", device: str = "cuda", dtype: torch.dtype = torch.float16):
"""
加载预训练且冻结的 TripoSG VAE 模型,集成内置的 Mesh 采样与归一化预处理。
"""
super().__init__()
self.device = device
self.dtype = dtype
# 1. 加载预训练 VAE
assert os.path.exists(os.path.join(weights_dir, "vae")), f"Error: 找不到 VAE 子文件夹,请确认路径: {weights_dir}"
self.vae = TripoSGVAEModel.from_pretrained(
weights_dir,
subfolder="vae",
).to(self.device, dtype=self.dtype)
# 2. 彻底冻结所有 VAE 参数
self.vae.eval()
for param in self.vae.parameters():
param.requires_grad = False
def preprocess_mesh(self, vertices: torch.Tensor, faces: torch.Tensor, num_samples: int = 204800) -> torch.Tensor:
verts_np = vertices.detach().cpu().numpy()
faces_np = faces.detach().cpu().numpy()
mesh = trimesh.Trimesh(vertices=verts_np, faces=faces_np, process=True)
if mesh.is_empty: return None
components = mesh.split(only_watertight=False)
mesh = max(components, key=lambda m: len(m.vertices))
distances = np.linalg.norm(mesh.vertices - mesh.center_mass, axis=1)
if len(distances) > 0:
mean_dist = np.mean(distances)
std_dist = np.std(distances)
mask = (distances < (mean_dist + 3.0 * (std_dist + 1e-6))).astype(bool)
if not np.all(mask):
# 1. 过滤顶点
new_vertices = mesh.vertices[mask]
# 2. 核心步骤:构建索引映射表 (Old Index -> New Index)
# 创建一个数组,存储原索引到新索引的映射
map_old_to_new = np.full(len(mesh.vertices), -1, dtype=int)
map_old_to_new[mask] = np.arange(len(new_vertices))
# 3. 过滤面片:只保留所有顶点都在 mask 里的面片
valid_faces_mask = mask[mesh.faces].all(axis=1)
new_faces = mesh.faces[valid_faces_mask]
# 4. 更新面片的顶点索引:将旧编号替换为新编号
new_faces = map_old_to_new[new_faces]
# 现在重建的 mesh 是合法且连续的
mesh = trimesh.Trimesh(vertices=new_vertices, faces=new_faces, process=True)
if mesh.is_empty: return None
# 后续归一化和采样逻辑保持不变...
q_min, q_max = np.percentile(mesh.vertices, [2.5, 97.5], axis=0)
max_extent = np.max(q_max - q_min)
scale = 2.0 / (max_extent + 1e-6)
normalized_vertices = (mesh.vertices - mesh.center_mass) * scale
norm_mesh = trimesh.Trimesh(vertices=normalized_vertices, faces=mesh.faces, process=False)
surface_points, face_indices = trimesh.sample.sample_surface(norm_mesh, count=num_samples)
surface_normals = norm_mesh.face_normals[face_indices]
surface_tensor = torch.cat([torch.from_numpy(surface_points), torch.from_numpy(surface_normals)], dim=-1).float()
surface_tensor = torch.clamp(surface_tensor, -10.0, 10.0)
return surface_tensor.unsqueeze(0).to(device=self.device, dtype=torch.float16)
@torch.no_grad()
def encode_mesh(self, vertices: np.ndarray, faces: np.ndarray, num_samples: int = 204800) -> torch.Tensor:
# 1. 预处理数据
surface_tensor = self.preprocess_mesh(vertices, faces, num_samples=num_samples)
# --- 增加深度快照 ---
if torch.isnan(surface_tensor).any():
print("[CRITICAL] Input to VAE contains NaN!")
# 检查输入数值范围 (这是最关键的一步)
# 如果 min/max 超过了 (-10, 10),VAE 极易溢出
if surface_tensor.min() < -100 or surface_tensor.max() > 100:
print(f"[CRITICAL] Input range extreme: min={surface_tensor.min()}, max={surface_tensor.max()}")
# 2. VAE 编码
encoder_output = self.vae.encode(surface_tensor)
latent_dist = encoder_output.latent_dist
# 检查分布参数
if torch.isnan(latent_dist.mean).any() or torch.isnan(latent_dist.var).any():
print("[CRITICAL] VAE produced NaN in latent_dist params!")
latent_sample = latent_dist.sample()
# 4. 编码后检查
if torch.isnan(latent_sample).any():
print("[Debug] VAE output NaN!")
# 此时 surface_tensor 已经包含了原始点云信息
# 建议保存 debug 数据以便后续排查
torch.save(surface_tensor, "nan_surface_tensor.pt")
raise ValueError("VAE produced NaN")
return latent_sample
@torch.no_grad()
def decode_latent(self, latent_sample: torch.Tensor, sampled_points: torch.Tensor) -> torch.Tensor:
"""
传入隐特征向量以及需要查询的 3D 空间坐标点,解码返回这些坐标点上的 SDF/Occupancy 预测值
"""
return self.vae.decode(latent_sample, sampled_points=sampled_points).sample |