Spaces:
Runtime error
Runtime error
File size: 3,219 Bytes
c9a178c 48b693a c9a178c 48b693a e11ff65 3615639 c9a178c 48b693a 3615639 c9a178c 48b693a 3615639 c9a178c 48b693a 3615639 c9a178c 3615639 c9a178c 48b693a 3615639 48b693a 3615639 48b693a 907bcd6 c2dff49 5be4ca8 48b693a 5be4ca8 c2dff49 48b693a 5be4ca8 48b693a 5be4ca8 48b693a c9a178c 48b693a 5be4ca8 48b693a 5be4ca8 c9a178c 8718a23 48b693a 8718a23 48b693a c9a178c 8718a23 | 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 | import tempfile
import os
# --- DA3 SETTINGS ---
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
CHECKPOINT = "depth-anything/da3-small"
processor = AutoImageProcessor.from_pretrained(CHECKPOINT)
model = AutoModelForDepthEstimation.from_pretrained(CHECKPOINT).to(DEVICE)
def process_textured_mesh(input_image):
if input_image is None:
return None, None
# 1. GENERATE DEPTH
inputs = processor(images=input_image, return_tensors="pt").to(DEVICE)
with torch.no_grad():
outputs = model(**inputs)
depth = torch.nn.functional.interpolate(
outputs.predicted_depth.unsqueeze(1),
size=input_image.size[::-1],
mode="bicubic",
).squeeze().cpu().numpy()
# 2. CREATE TEXTURED GRID
# We use a step of 2 to keep the mesh lightweight for the browser
width, height = input_image.size
step = 2
x, y = np.meshgrid(np.arange(0, width, step), np.arange(0, height, step))
# Normalize Z (depth) and center X, Y in a unit-10 space
z = (depth[::step, ::step] / (depth.max() + 1e-5)) * 3.0
x_centered = ((x / width) - 0.5) * 10.0 * (width / height)
y_centered = (0.5 - (y / height)) * 10.0
points = np.stack((x_centered, y_centered, z), axis=-1)
rows, cols, _ = points.shape
# 3. VERTICES & UV MAPPING
vertices = points.reshape(-1, 3)
# UVs map the image (0-1 range) to the vertices
uvs = np.stack((x / width, 1.0 - (y / height)), axis=-1).reshape(-1, 2)
# Build Triangles
faces = []
for i in range(rows - 1):
for j in range(cols - 1):
v0 = i * cols + j
v1 = v0 + 1
v2 = (i + 1) * cols + j
v3 = v2 + 1
faces.append([v0, v2, v1])
faces.append([v1, v2, v3])
# 4. CONSTRUCT MESH
mesh = o3d.geometry.TriangleMesh()
mesh.vertices = o3d.utility.Vector3dVector(vertices)
mesh.triangles = o3d.utility.Vector3iVector(np.array(faces))
# Assign UVs (Open3D expects UVs per triangle vertex, so we tile them)
mesh.triangle_uvs = o3d.utility.Vector2dVector(np.tile(uvs, (3, 1)))
# 5. EXPORT
temp_dir = tempfile.gettempdir()
mesh_path = os.path.join(temp_dir, "model.obj")
texture_path = os.path.join(temp_dir, "texture.png")
# Save image as texture
input_image.save(texture_path)
# Save OBJ
o3d.io.write_triangle_mesh(mesh_path, mesh)
# To see textures in some viewers, we return the OBJ.
# In Blender, you'll simply load this texture.png onto the model.
return mesh_path, mesh_path
# --- UI ---
with gr.Blocks() as demo:
gr.Markdown("# 🎭 DA3 Textured 3D Mesh")
with gr.Row():
with gr.Column():
img_in = gr.Image(type="pil", label="Input")
btn = gr.Button("🔨 Generate Mesh", variant="primary")
with gr.Column():
# Gradio 5.0+ focuses on the center (0,0,0) automatically
v3d = gr.Model3D(label="3D Preview", camera_position=(0, 90, 15))
dl = gr.DownloadButton("💾 Download OBJ + PNG")
btn.click(fn=process_textured_mesh, inputs=[img_in], outputs=[v3d, dl])
demo.launch() |