estimate_normals — 3D curvature op

• Data kinds: points → normals

• Call: import fullseye as fs; fs.ledger.estimate_normals(points, k=25) (to call the implementation directly, import curvature3d; curvature3d.estimate_normals(points, k=25); from the registry, ops3d.get("estimate_normals"))

Usage

Point-cloud normals made consistently outward (pointing away from the neighbourhood centroid). → (N,3).

Procedure (per point, Python loop): take the k+1 neighbours including the point itself with cKDTree (k truncated to N-1), and use the smallest eigenvector of the scatter matrix local.T @ local of the neighbour coordinates, with the query point as origin, as the normal (unit length). Orientation: "flip if the dot product with the neighbourhood centroid is positive" = aligned to the side away from the neighbourhood centroid.

• Points with fewer than 5 neighbours return the fixed value (0, 0, 1) (not estimated).

• The orientation is a local heuristic: for a closed convex shape it points outward, but on open surfaces, thin plates and concave parts the orientation of adjacent points can disagree (global consistency is not guaranteed). To align globally, pass the result through orient_normals or use estimate_oriented_normals from the start. For an organized depth image, normals_from_depth is faster and oriented toward the viewpoint.

• Returns float64 (N,3). k defaults to 25. Deterministic.

• Internally it goes through the same computation as principal_curvatures (normal estimation also runs the quadric surface fit), so it is slow for many points.

Background guides (the physics and conventions behind this op)

• blas_threads_and_memory — Why matrix decompositions are slow — BLAS threads, caches, memory layout

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)

• cylinder_axis_metrology — py -3.11 examples_3d/cylinder_axis_metrology.py

• feature_register — py -3.11 examples_3d/feature_register.py

• oriented_normals — py -3.11 examples_3d/oriented_normals.py

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

icp_point2plane · compute_fpfh · shot_descriptor · fuse_to_voxel · reflect · refract · normal_consistency · ransac_cylinder

Same category (curvature)

principal_curvatures · mean_curvature · gaussian_curvature · shape_index


*Provenance: curvature3d.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.