Download verification_code/render3d.py from acd23/cad-3d-reconstruction: direct link, hf CLI and curl.
- Browser
- Download file 3.21 kB
-
https://huggingface.co/acd23/cad-3d-reconstruction/resolve/main/verification_code/render3d.py
- Command line
-
hf download hf://acd23/cad-3d-reconstruction/verification_code/render3d.py
-
curl -L -o render3d.py https://huggingface.co/acd23/cad-3d-reconstruction/resolve/main/verification_code/render3d.py
3.21 kB
| """Offscreen renderer for point clouds and meshes (Open3D EGL, headless). | |
| Produces clean multi-view sheets so reconstruction results can be judged | |
| visually. Framing uses robust (percentile) bounds so a handful of stray | |
| outlier points cannot shrink the subject to a speck. | |
| """ | |
| import os, argparse | |
| import numpy as np | |
| import open3d as o3d | |
| from open3d.visualization import rendering | |
| from PIL import Image, ImageDraw | |
| W = H = 560 | |
| VIEWS = [("front", 0, 0), ("right", 90, 0), ("back", 180, 0), | |
| ("left", 270, 0), ("top", 0, 88), ("iso", 40, 25)] | |
| BG = (0.12, 0.13, 0.15, 1.0) | |
| def robust_bounds(pts, lo=0.5, hi=99.5): | |
| mn = np.percentile(pts, lo, axis=0) | |
| mx = np.percentile(pts, hi, axis=0) | |
| return mn, mx | |
| def geom_points(geom): | |
| if isinstance(geom, o3d.geometry.PointCloud): | |
| return np.asarray(geom.points) | |
| return np.asarray(geom.vertices) | |
| def render_geometry(geom, out_prefix, point_size=2.5, fill=0.85, label=None): | |
| pts = geom_points(geom) | |
| mn, mx = robust_bounds(pts) | |
| center = (mn + mx) / 2.0 | |
| radius = float(np.linalg.norm(mx - mn)) / 2.0 | |
| dist = radius / (np.tan(np.radians(30.0)) * fill) | |
| rend = rendering.OffscreenRenderer(W, H) | |
| rend.scene.set_background(np.array(BG, dtype=np.float32)) | |
| mat = rendering.MaterialRecord() | |
| if isinstance(geom, o3d.geometry.PointCloud): | |
| mat.shader = "defaultUnlit" | |
| mat.point_size = point_size | |
| else: | |
| mat.shader = "defaultLit" | |
| mat.base_color = [0.78, 0.80, 0.85, 1.0] | |
| rend.scene.add_geometry("g", geom, mat) | |
| rend.scene.scene.enable_sun_light(True) | |
| rend.scene.scene.set_sun_light([-0.3, -0.9, -0.5], [1.0, 1.0, 1.0], 75000) | |
| tiles = [] | |
| for name, az, el in VIEWS: | |
| a, e = np.radians(az), np.radians(el) | |
| eye = center + dist * np.array([np.cos(e) * np.sin(a), np.sin(e), np.cos(e) * np.cos(a)]) | |
| up = [0, 1, 0] if el < 80 else [0, 0, -1] | |
| rend.scene.camera.look_at(center, eye, up) | |
| tiles.append((name, Image.fromarray(np.asarray(rend.render_to_image())))) | |
| del rend | |
| sheet = Image.new("RGB", (W * 3, H * 2), (30, 33, 38)) | |
| d = ImageDraw.Draw(sheet) | |
| for i, (name, im) in enumerate(tiles): | |
| x, y = (i % 3) * W, (i // 3) * H | |
| sheet.paste(im, (x, y)) | |
| d.text((x + 10, y + 8), name, fill=(210, 215, 225)) | |
| if label: | |
| d.text((10, H * 2 - 20), label, fill=(160, 168, 180)) | |
| sheet.save(out_prefix + "_sheet.png") | |
| tiles[5][1].save(out_prefix + "_iso.png") | |
| return out_prefix + "_sheet.png" | |
| def load(path): | |
| if path.endswith((".ply", ".obj")): | |
| m = o3d.io.read_triangle_mesh(path) | |
| if len(m.triangles) > 0: | |
| m.compute_vertex_normals() | |
| return m | |
| if path.endswith(".obj"): | |
| raise ValueError("empty obj: " + path) | |
| return o3d.io.read_point_cloud(path) | |
| if __name__ == "__main__": | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("input"); ap.add_argument("out_prefix") | |
| ap.add_argument("--point-size", type=float, default=2.5) | |
| ap.add_argument("--label", default=None) | |
| a = ap.parse_args() | |
| print(render_geometry(load(a.input), a.out_prefix, | |
| point_size=a.point_size, label=a.label)) | |