AgentCanvas / Pages / Developer Guide / Nodesets / Model / Pointmap NodeSet
2026-07-08

The Pointmap NodeSet (PointmapNodeSet, workspace/nodesets/model/model_pointmap.py) wraps Sapiens single-image pointmap estimation via transformers Sapiens2ForPointmapEstimation: one RGB frame in, a dense per-pixel 3D (X, Y, Z) coordinate map out. It is the monocular counterpart to VGGT's multi-view pointmap โ€” where VGGT fuses several views into one 3D field, this lifts a single image into 3D geometry directly, no intrinsics and no second view. Obstacle geometry, free-space extent, "how far is that wall", and back-projecting a 2D detection into a 3D point all want this.

env: ac-fm (shared FM env) ยท backend: transformers Sapiens2ForPointmapEstimation + AutoImageProcessor ยท default: facebook/sapiens2-pointmap-0.4b (ungated)

Primitive Purpose
model_pointmap__estimate_pointmap Per-image 3D pointmap for a list of same-resolution frames โ†’ pointmap (JSON envelope of an (N, H, W, 3) XYZ map + per-image scales).

1. One primitive: RGB โ†’ 3D pointmap

The tool takes a list of same-resolution images and returns, per image, a per-pixel (X, Y, Z) coordinate in canonical camera space. It is the single-view sibling of Surface Normals and Depth Anything โ€” same input contract, same envelope convention โ€” so a monocular geometry trio (depth ยท normals ยท pointmap) composes as drop-in Sapiens/DINO nodes. In a rollout loop N is typically 1 (per-frame geometry).


2. Images in โ€” uniform resolution

The images port accepts a list of {rgb_base64} dicts (or raw base64 strings). All images in one call must share resolution โ€” the envelope is a single (N, H, W, 3) buffer, so mixed sizes degrade to empty with a self-log (split into uniform batches). A malformed frame degrades the whole call rather than guessing.


3. Model & post-processing

The engine loads Sapiens2ForPointmapEstimation + AutoImageProcessor for the configured model_id. A dedicated processor post-process crops the preprocessing padding and resizes each prediction back to its original (H, W), returning one {"pointmap": (3, H, W)} per image in canonical camera space; a per-image scale factor is carried alongside rather than silently applied:

outputs = model(pixel_values=pixel_values)
# dedicated post-process: crop preprocessing padding, resize back to (H, W)
results = processor.post_process_pointmap_estimation(outputs, target_sizes=[(H, W)] * N)
pts = np.stack([r["pointmap"].permute(1, 2, 0) for r in results])  # (N, H, W, 3) canonical
scales = outputs.scales                          # one scalar per image; XYZ / scale = metric

Each model_id is a lazy singleton in a registry, frozen and moved to the device once; inference is single-flight per engine.


4. Pointmap envelope

The output is a TEXT JSON envelope โ€” the pointmap as a raw C-contiguous float32 buffer, base64-encoded (byte-exact across the server-mode HTTP boundary), plus one scale per image:

{"shape": [1, 480, 640, 3], "dtype": "float32", "b64": "<buffer>", "model_id": "facebook/sapiens2-pointmap-0.4b", "scales": [0.247]}

pointmap[n, y, x] is an (X, Y, Z) coordinate in canonical camera space, at the original input resolution. Divide the XYZ by scales[n] to convert to metric coordinates โ€” the raw prediction is scale-canonical, so the scale factor travels with the buffer rather than being baked in.


5. Canvas Node

model_pointmap__estimate_pointmap

FieldDetail
Inputsimages (ANY โ€” list of {rgb_base64} dicts or raw base64 strings, uniform size)
Outputspointmap (TEXT โ€” the ยง4 envelope; "" on degraded)
Configmodel_id (select โ€” sapiens2-pointmap 0.4b/0.8b/1b/5b)
Backend callprocessor โ†’ model(...) under no_grad โ†’ post_process_pointmap_estimation โ†’ (N, H, W, 3) + scales

6. Server Mode

parallelism = "shared" (a stateless pointmap estimator โ€” one server across eval workers); server_python = conda_env_python("ac-fm", "POINTMAP_PYTHON"). Pointmap estimation runs in the shared ac-fm env โ€” Sapiens2ForPointmapEstimation is native to the transformers stack there. The file stays Python-3.8-parseable. Load: POST /api/components/nodesets/model_pointmap/load?mode=server.


7. Environment Variables

VariableDefaultPurpose
POINTMAP_PYTHONac-fm env pythonInterpreter for the server subprocess
POINTMAP_DEVICEauto (โ†’ cuda when available)Torch device for the loaded model

8. Degraded Mode

A weight-load failure latches (_load_failed โ€” no retry storm); that, a missing/malformed frame, or mixed input resolutions yields "" on the node's output. An empty envelope is a clean "no pointmap this step" signal; consumers keep their own fallback and never receive a fabricated 3D map.

AgentCanvas docs