Skip to content

Commit 021a4f7

Browse files
authored
PERF: InterpolatePointCloudToRegularGrid now calculates stats on-the-fly (#1549)
* The filter is still serial, but unnecessary allocations and dynamic_cast have been removed resulting in an increase in speed. * Move parameters around. Adjust output array types. * Compute the voxel index on the fly instead of relying on a previous filter to generate them. * Statistics for each voxel such as min/max/count/mean/stdDev are all now optionally calculated in this filter.
1 parent 575eba6 commit 021a4f7

8 files changed

Lines changed: 792 additions & 468 deletions

src/Plugins/SimplnxCore/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ set(AlgorithmList
254254
InitializeImageGeomCellData
255255
InterpolatePointCloudToRegularGrid
256256
IterativeClosestPoint
257+
InterpolatePointCloudToRegularGrid
257258
LabelTriangleGeometry
258259
LaplacianSmoothing
259260
MapPointCloudToRegularGrid

src/Plugins/SimplnxCore/docs/InterpolatePointCloudToRegularGridFilter.md

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,78 @@ Sampling (Interpolation)
66

77
## Description
88

9-
This **Filter** interpolates the values of arrays stored in a **Vertex Geometry** onto a user-selected **Image Geometry**. The user defines the (x,y,z) radii of a kernel in *real space units*. This kernel can be intialized to either *uniform* or *Gaussian*. The interpolation algorithm proceeds as follows:
9+
This **Filter** interpolates the values of arrays stored in a **Vertex Geometry** (point cloud) onto a user-selected **Image Geometry** (regular grid). For each point in the cloud, the filter applies a kernel centered on the point's voxel and accumulates weighted contributions into every voxel the kernel overlaps. The result is a single interpolated value per voxel per array, computed as a weighted average of all contributions.
1010

11-
1. The kernel radii defined by the user in real space units are converted into voxel units, based on the spacing of the supplied **Image Geometry**.
12-
2. The kernel is centered on each vertex in the **Vertex Geometry**.
13-
3. The values of each **Attribute Array** on the centered vertex are associated to each voxel intersected by the kernel. This stored value is multiplied by the value of the kernel.
11+
### Kernel
1412

15-
The result of the above approach is a list of data at each voxel in the **Image Geometry** for each interpolated **Attribute Array**. These lists may be of different lengths within each voxel, since the kernels from each point may overlap. This duplication may result in significant memory usage if the number of points is large; the user may select a subset of arrays to interpolate to alleviate this issue. Note that all arrays selected for interpolation must be scalar.
13+
The user defines the (x, y, z) radii of a kernel in *real space units*. The kernel can be initialized to one of two modes:
1614

17-
A mask may be supplied to the filter. Points that are not within the mask are ignored during interpolation. Additionally, the distances between each voxel and the source point for the intersecting kernel may be stored; this significantly increases the required memory. Arrays may be passed through to the image geometry without applying any interpolation. This operation is equivalent to used a uniform kernel.
15+
- **Uniform** – Every voxel within the kernel radius receives equal weight (1.0).
16+
- **Gaussian** - Voxel weights fall off with distance from the center according to a Gaussian function controlled by user-specified *sigmas* in each dimension.
17+
18+
The kernel radii are converted to voxel units based on the spacing of the **Image Geometry**. If the kernel radius in a given dimension is smaller than the voxel spacing, the kernel has a zero extent in that dimension (i.e., each point only affects its own voxel along that axis).
19+
20+
### Algorithm
21+
22+
The interpolation proceeds in two passes:
23+
24+
**Pass 1 – Accumulation (one iteration over all vertices):**
25+
26+
1. For each vertex in the **Vertex Geometry**, compute the destination voxel index from the vertex coordinates and the **Image Geometry**'s origin, spacing, and dimensions. Vertices that fall outside the grid are silently skipped.
27+
2. If a mask is enabled and the vertex is masked out, skip it.
28+
3. Center the kernel on that voxel and determine the kernel bounds, clipped to the grid extents.
29+
4. For each voxel within the clipped kernel:
30+
- Look up the kernel weight using the 3D offset from the center voxel.
31+
- For each *interpolated* array: multiply the source value by the kernel weight and accumulate the weighted value, the weight itself and any enabled statistics into the destination voxel.
32+
- For each *copied* array: accumulate the source value with uniform weight (1.0) regardless of the selected kernel type.
33+
34+
**Pass 2 – Finalization (one iteration over all voxels):**
35+
36+
For each voxel that received at least one contribution:
37+
38+
- The interpolated output value is `sum(weight * value) / sum(weight)`.
39+
- Any enabled statistics are written from the accumulated state.
40+
41+
### Arrays to Interpolate vs. Arrays to Copy
42+
43+
Both options transfer data from the **Vertex Geometry** onto the **Image Geometry**, but they differ in how the kernel is applied:
44+
45+
- **Arrays to Interpolate** use the selected kernel (Uniform or Gaussian). The kernel weight multiplies each vertex's value before being accumulated into surrounding voxels. The final output is a kernel-weighted average. Optional statistics (length, min, max, mean, standard deviation, summation) can be computed on the weighted contributions. The output array type is always *float64* because the weighted average produces floating-point values regardless of the input type.
46+
47+
- **Arrays to Copy** always use a uniform kernel (weight = 1.0), even when Gaussian interpolation is selected. The final output is a simple arithmetic average of all values that fell within the kernel radius. No statistics are computed for copied arrays. The output array type matches the source array type.
48+
49+
Use *Arrays to Interpolate* for data values that should be smoothed or blended by the kernel (e.g., measured scalar fields). Use *Arrays to Copy* for categorical or index data where Gaussian weighting would not be meaningful.
50+
51+
All arrays selected for interpolation or copying must be **scalar** (single-component) arrays.
52+
53+
### Inline Statistics
54+
55+
Rather than storing all per-voxel contributions and computing statistics in a separate filter, this filter can compute the following statistics *inline* during the accumulation pass. Each statistic is optional and controlled by a boolean parameter. When enabled, an additional output array is created for each interpolated array with the array name plus a configurable suffix (e.g., `FaceAreas_Length`).
56+
57+
| Statistic | Output Type | Description |
58+
|------------------------|-------------|--------------------------------------------------------------------------------------------------------------|
59+
| **Length** | uint64 | Number of weighted contributions to the voxel |
60+
| **Minimum** | float32 | Smallest weighted value (`weight * source_value`) |
61+
| **Maximum** | float32 | Largest weighted value |
62+
| **Mean** | float32 | Arithmetic mean of weighted values (`sum / count`) |
63+
| **Standard Deviation** | float32 | Population standard deviation of weighted values, computed via Welford's numerically stable online algorithm |
64+
| **Summation** | float32 | Sum of all weighted values |
65+
66+
Statistics are computed on the *weighted* values (`kernel_weight * source_value`), which are the same quantities that contribute to the interpolated average. Voxels with no contributions receive a value of zero for all statistics.
67+
68+
Median is not supported because it requires storing every vertex's contribution, which would negate the memory savings of the flat accumulation approach.
69+
70+
### Mask
71+
72+
An optional boolean mask array may be provided. Vertices where the mask value is *false* are skipped entirely during interpolation – they do not contribute to any voxel's accumulated value or statistics.
73+
74+
### Memory Usage
75+
76+
This filter uses flat arrays rather than variable-length lists, so memory usage is proportional to the number of voxels in the **Image Geometry** (not the number of points times the kernel size). For each interpolated array, the filter temporarily allocates ~56 bytes per voxel for accumulation state. For each copied array, ~16 bytes per voxel is allocated. These temporary variables are freed after the finalization pass.
77+
78+
### Voxel Index Computation
79+
80+
The destination voxel for each vertex is computed on-the-fly from the vertex coordinates and the **Image Geometry**'s origin, spacing, and dimensions. There is no need to pre-compute a voxel indices array (e.g., via the *Map Point Cloud to Regular Grid* filter). Vertices whose coordinates fall outside the **Image Geometry** bounds are silently skipped.
1881

1982
% Auto generated parameter table will be inserted here
2083

0 commit comments

Comments
 (0)