visual_hull — 3D space_carving op

• Data kinds: images → voxel

• Call: import fullseye as fs; fs.ledger.visual_hull(silhouettes: 'Sequence[np.ndarray]', Ks: 'Sequence[np.ndarray]', Rs: 'Sequence[np.ndarray]', ts: 'Sequence[np.ndarray]', bounds: 'Bounds', res: 'int') -> 'np.ndarray' (to call the implementation directly, import visualhull; visualhull.visual_hull(silhouettes: 'Sequence[np.ndarray]', Ks: 'Sequence[np.ndarray]', Rs: 'Sequence[np.ndarray]', ts: 'Sequence[np.ndarray]', bounds: 'Bounds', res: 'int') -> 'np.ndarray'; from the registry, ops3d.get("visual_hull"))

Usage

Return the visual hull of multi-view silhouettes as voxel occupancy (an alias of carve).

Arguments, return value and exceptions are exactly the same as carve (internally it simply calls carve as-is).

• silhouettes: list of M foreground masks (H,W) bool, one per camera (sizes may differ between cameras).

• Ks/Rs/ts: intrinsic matrix (3,3), rotation (3,3) and translation (3,) of each camera. Projection convention is X_cam = R @ X_world + t, pixel = (K @ X_cam)[:2] / Z (OpenCV style). Pixels are rounded to the nearest neighbour.

• bounds: carving region ((xmin,xmax),(ymin,ymax),(zmin,zmax)) (max > min required on each axis).

• res: number of voxel divisions per axis (positive integer). Total voxels res**3.

Returns (res,res,res) bool. With indexing='ij' the axes are (x,y,z), and the centre of vox[i,j,k] is (xmin+(i+.5)dx, ymin+(j+.5)dy, zmin+(k+.5)dz). A voxel survives if, in every camera, it is "in front of the camera (Z>0), inside the image, and that pixel is silhouette foreground" (AND). If even 1 camera fails, it is carved away.

fail-closed: mismatched list lengths, 0 cameras, res <= 0 and degenerate bounds all raise ValueError (with 0 cameras it does not return "all voxels occupied" disguised as a reconstruction result).

Note: a visual hull is a superset of the object, and concavities not visible from any camera remain filled. If the silhouettes are coverage masks dilated by 1 pixel as in synthesize_silhouette, you avoid losing object voxels carved away by discretisation error. The result can be meshed with voxel_to_mesh, and also used as-is as the occupancy input of esdf.

Background guides (the physics and conventions behind this op)

• blender_interop — Using Blender alongside — build the shape, measure it with fullseye (pitfalls: axes, units, ground truth)

References (sample data, literature)

• Sample-data catalog (download URLs / licences) — 2-D uses skimage.data (BSD/public domain) plus synthetic images; 3-D lists download URLs for real data sources (Stanford, PDS, …).

• Operator provenance and references — the sources of the research/methods this op family came from.

Runnable examples (verified samples that actually call this op)

• space_carving — py -3.11 examples_3d/space_carving.py

Ops the type connects to (they accept voxel as input)

voxel_to_mips · voxel_to_mesh · signed_distance_field · to_points · sobel3d · hessian3d · curvature_maps · edt_jfa

Same category (space_carving)

carve · synthesize_silhouette · carve_look_at


*Provenance: visualhull.py — 3D operator registry. This per-op note is generated by tools/opdocs.py md (do not hand-edit).*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.