Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
82fc897
ENH: Add helpers that validate Cell and Feature Attribute Matrix loca…
imikejackson Oct 16, 2026
d9c11f1
BUG: ErodeDilateBadData requires Feature Ids in the geometry's Cell A…
imikejackson Oct 16, 2026
0436026
BUG: ErodeDilateCoordinationNumber requires Feature Ids in the geomet…
imikejackson Oct 16, 2026
39cf75a
BUG: FillBadData requires Feature Ids in the geometry's Cell Attribut…
imikejackson Oct 16, 2026
d21f310
BUG: KeepRemoveRankedFeatures requires Feature Ids in the geometry's …
imikejackson Oct 16, 2026
4c53348
BUG: RemoveFlaggedFeatures requires Feature Ids in the geometry's Cel…
imikejackson Oct 16, 2026
75e32bc
BUG: RequireMinNumNeighbors requires Feature Ids in the geometry's Ce…
imikejackson Oct 16, 2026
82906d5
BUG: RequireMinimumSizeFeatures requires Feature Ids in the geometry'…
imikejackson Oct 16, 2026
5c25fe2
BUG: QuickSurfaceMesh checks where Feature Ids comes from
imikejackson Oct 16, 2026
55d9c66
BUG: Keep the Feature Ids tuple-count guards and compare Feature AM o…
imikejackson Oct 19, 2026
786cf5b
STYLE: Normalize Feature Ids once in executeImpl and restore test inc…
imikejackson Oct 19, 2026
b5fe79c
DOC: Record the new preflight paths in the ErodeDilateBadData and Fil…
imikejackson Oct 19, 2026
00c27bf
REV: Share the Cell Attribute Matrix path mapping and fix the V&V report
imikejackson Oct 20, 2026
4b6edda
TEST: Check MapToGeometryCellDataPath directly
imikejackson Oct 21, 2026
00066ec
REV: Bring the RemoveFlaggedFeatures V&V report up to date and use fa…
imikejackson Oct 21, 2026
d7f56e1
REV: Fix review findings in tests, V&V reports, docs and names
joeykleingers Oct 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/Plugins/SimplnxCore/docs/ErodeDilateBadDataFilter.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Processing (Cleanup)

This **Filter** grows or shrinks regions of *bad data* (cells with *Feature Id = 0*) by one cell-layer per iteration, using standard image-morphology operations. It is a building block for cleaning up isolated noise or for compensating for boundaries that were systematically under- or over-measured by the experiment.

**Cell Feature Ids** and every array in **Attribute Arrays to Ignore** must be in the selected geometry's Cell Attribute Matrix.

![Fig. 1: Dilate grows the bad-data (Feature Id 0) region outward by one cell layer per iteration; Erode shrinks it, reassigning each bad cell to its majority neighbor. The Number of Iterations sets the layer count.](Images/ErodeDilateBadData_IterationSequence.png)

### Dilation
Expand Down Expand Up @@ -47,8 +49,11 @@ slice.

### Preflight Errors

The Filter refuses to run in two cases:
The Filter refuses to run in four cases:

- **-14605**: *Cell Feature Ids* is not in the selected **Image Geometry**'s Cell Attribute Matrix, or the geometry has no
Cell Attribute Matrix.
- **-14604**: an array in *Attribute Arrays to Ignore* is not in the selected **Image Geometry**'s Cell Attribute Matrix.
- **-14601**: all three of *X Direction*, *Y Direction*, and *Z Direction* are disabled. At least one direction is
required, otherwise there are no neighbors to erode or dilate across.
- **-14602**: the selected **Image Geometry** has a dimension of *0* **Cells**. All three dimensions must be non-zero.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Processing (Cleanup)

This **Filter** smooths the interface between *good* and *bad* cells (or, more generally, between any two adjacent feature regions) by removing cells whose neighborhood is mostly the opposite class. It targets isolated voxels and small protrusions that are surrounded by cells of a different feature, then fills them in with their neighbors' data via isotropic coarsening.

**Cell Feature Ids** and every array in **Attribute Arrays to Ignore** must be in the selected geometry's Cell Attribute Matrix.

The *coordination number* is the count of a cell's face-sharing neighbors that belong to a different class. With 6 face neighbors, the coordination number ranges from **0 to 6**:

- **CN = 0** -- all 6 neighbors agree with the cell. Solidly inside a region.
Expand Down
2 changes: 2 additions & 0 deletions src/Plugins/SimplnxCore/docs/FillBadDataFilter.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Processing (Cleanup)

This **Filter** distinguishes small *bad-data noise* from large *real defects* in a segmented dataset and fills only the noise. **Cells** with *Feature Id = 0* are considered *bad*; the filter groups them into connected components, classifies each component by its size, and fills components below the user threshold by copying the most common neighbor's data into them. Components at or above the threshold are left intact, since they likely represent real features (pores, cracks, voids).

**Cell Feature Ids** and every array in **Attribute Arrays to Ignore** must be in the selected geometry's Cell Attribute Matrix. **Cell Phases** must also be in that Attribute Matrix when **Store Defects as New Phase** is enabled.

| Small IN100 Before | Small IN100 After |
|--------------------------------------|-------------------------------------|
| ![](Images/fill_bad_data_before.png) | ![](Images/fill_bad_data_after.png) |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ This **Filter** keeps or removes a count or percentage of **Features** based on
a chosen scalar **Feature** level array. For example, "keep the 10 largest grains by *NumElements*",
or "remove the smallest 10% of grains".

Cell Feature Ids must be in the selected Image Geometry's Cell Attribute Matrix; when Fill-in Removed Features is on, Attribute Arrays to Ignore must also be in that matrix. The parent of Feature Ranking Array must be a different Attribute Matrix directly under the selected Image Geometry.

This is a **rank** threshold, not a **value** threshold. It answers *"which are the 10 biggest?"*
rather than *"which are bigger than 100 voxels?"*. The cut therefore depends on the whole population
rather than on each **Feature** independently. For a value threshold, use **Remove Minimum Size
Expand Down
2 changes: 2 additions & 0 deletions src/Plugins/SimplnxCore/docs/QuickSurfaceMeshFilter.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Surface Meshing (Generation)

This **Filter** generates a **Triangle Geometry** surface mesh from a grid **Geometry** (Image or Rectilinear Grid). For every cell face shared between two different **Features** (i.e., a Feature boundary), the filter emits two triangles, producing the *stair-stepped* surface that defines the boundary of each Feature.

**Cell Feature Ids** must have one tuple per cell of the selected Image or Rectilinear Grid Geometry. When **Attempt to Fix Problem Voxels** is on, **Cell Feature Ids** must also be in that geometry's Cell Attribute Matrix because the filter modifies this array in place. When this option is off, **Cell Feature Ids** may be in any Attribute Matrix.

The resulting mesh is "blocky" because each triangle aligns with a voxel face. To smooth it, apply [Laplacian Smoothing](LaplacianSmoothingFilter.md) afterward -- or, preferably, use [Surface Nets](SurfaceNetsFilter.md), which combines meshing and smoothing in one step.

![Example Quick Mesh Output](Images/QuickSurface_Output.png)
Expand Down
2 changes: 2 additions & 0 deletions src/Plugins/SimplnxCore/docs/RemoveFlaggedFeaturesFilter.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Processing (Cleanup)

This **Filter** removes or extracts **Features** that an earlier filter in the pipeline has flagged. The user supplies a boolean (or unsigned 8-bit) array at the **Feature** level. Features with a value of *true* are removed or extracted. This filter discards features by any criterion that a flag can express: biased features, surface features, minority phases, or user-selected features. For the specific case of removing features smaller than a size threshold, see [Remove Minimum Size Features](RequireMinimumSizeFeaturesFilter.md). For a rank-based selection ("remove the 10 smallest"), see [Keep/Remove Ranked Features](KeepRemoveRankedFeaturesFilter.md).

For all operations, Cell Feature Ids must be in the selected Image Geometry's Cell Attribute Matrix; when removing with Fill-in Removed Features on, Attribute Arrays to Ignore must also be in that matrix. The parent of Flagged Features must be a different Attribute Matrix directly under the selected Image Geometry.

### Selected Operation

The *Selected Operation* parameter provides the following choices:
Expand Down
5 changes: 5 additions & 0 deletions src/Plugins/SimplnxCore/docs/RequireMinNumNeighborsFilter.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Processing (Cleanup)

This **Filter** removes **Features** that have fewer contiguous neighboring **Features** than the selected minimum. It uses a precomputed *Number of Neighbors* array, which is normally created by the [Compute Feature Neighbors](./ComputeFeatureNeighborsFilter.md) **Filter**. **Feature** tuple 0 is retained as the background tuple.

Cell Feature Ids and Cell Arrays to Ignore must be in the selected Image Geometry's Cell Attribute Matrix. The parent of Number of Neighbors must be a different Attribute Matrix directly under the selected Image Geometry.

The most common use case is cleaning up isolated single-feature islands left over from segmentation -- features whose surroundings turned out to be too sparsely connected to support a meaningful grain. After flagged features are removed, the remaining features grow outward via [isotropic coarsening](RequireMinimumSizeFeaturesFilter.md) until every cell is reassigned.

The threshold is a **count of contiguous neighbors** (a non-negative integer). Setting the threshold to 0 removes nothing. Setting it larger than the maximum number of neighbors any feature has produces an error (since all features would be removed). Inspect the *Number of Neighbors* output of [Compute Feature Neighbors](ComputeFeatureNeighborsFilter.md) before choosing a threshold.
Expand Down Expand Up @@ -44,7 +46,10 @@ This **Filter** operates in place and does not create a new geometry or output a

## Validation and Failure Conditions

- Preflight fails with `-55575` when *Cell Feature Ids* is not in the selected **Image Geometry**'s Cell Attribute Matrix.
- Preflight fails with `-55571` when *Cell Feature Ids* does not contain exactly one tuple per **Image Geometry** cell.
- Preflight fails with `-55573` when an array in *Cell Arrays to Ignore* is not in that Cell Attribute Matrix.
- Preflight fails with `-55574` when the parent of *Number of Neighbors* is not an Attribute Matrix directly under the selected **Image Geometry**, or is its Cell Attribute Matrix.
- Preflight fails with `-252` when *Number of Neighbors* and the enabled *Feature Phases* array do not have matching tuple counts.
- Execution fails with `-5555` when the selected *Phase Index* is not present in *Feature Phases*.
- Execution fails with `-55569` when the selected minimum would reject every eligible non-background **Feature**.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Processing (Cleanup)

This **Filter** removes small **Features** from a segmented dataset and fills in the space they leave behind by expanding their neighbors. Features whose cell count is below the user-specified *Minimum Allowed Features Size* are erased; the remaining features grow outward into the erased cells until every cell is reassigned to a surviving feature.

Cell Feature Ids must be in the selected Image Geometry's Cell Attribute Matrix. The parent of Feature Num. Cells must be a different Attribute Matrix directly under the selected Image Geometry.

This is typically used immediately after segmentation ([Segment Features (Misorientation)](../OrientationAnalysis/EBSDSegmentFeaturesFilter.md) or similar) to discard spurious single-cell or few-cell "grains" that are almost always noise rather than real features.

### How This Filter Works
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "simplnx/Parameters/DataGroupSelectionParameter.hpp"
#include "simplnx/Parameters/GeometrySelectionParameter.hpp"
#include "simplnx/Parameters/MultiArraySelectionParameter.hpp"
#include "simplnx/Utilities/DataArrayUtilities.hpp"

#include "simplnx/Utilities/SIMPLConversion.hpp"

Expand Down Expand Up @@ -100,6 +101,22 @@ IFilter::PreflightResult ErodeDilateBadDataFilter::preflightImpl(const DataStruc
auto zDirOn = filterArgs.value<bool>(k_ZDirOn_Key);
auto imageGeometryPath = filterArgs.value<DataPath>(k_SelectedImageGeometryPath_Key);

auto featureIdsResult = ValidateArrayInGeometryCellData(dataStructure, imageGeometryPath, pFeatureIdsArrayPathValue, -14605, "Cell Feature Ids");
if(featureIdsResult.invalid())
{
return {ConvertResultTo<OutputActions>(std::move(featureIdsResult), {})};
}

auto ignoredArraysResult = ValidateArraysInGeometryCellData(dataStructure, imageGeometryPath, pIgnoredDataArrayPathsValue, -14604, "Attribute Arrays to Ignore");
if(ignoredArraysResult.invalid())
{
return {ConvertResultTo<OutputActions>(std::move(ignoredArraysResult), {})};
}

// The Cell Attribute Matrix can have more than one DataPath. Use the geometry's Cell Attribute Matrix path so that the ignored paths match the modified paths.
const auto cellDataPath = dataStructure.getDataRefAs<ImageGeom>(imageGeometryPath).getCellDataPath();
pIgnoredDataArrayPathsValue = MapToGeometryCellDataPath(cellDataPath, pIgnoredDataArrayPathsValue);

PreflightResult preflightResult;

nx::core::Result<OutputActions> resultOutputActions;
Expand All @@ -125,7 +142,7 @@ IFilter::PreflightResult ErodeDilateBadDataFilter::preflightImpl(const DataStruc

// Report arrays that the filter modifies in place.
// Cell Data arrays are modified.
nx::core::AppendDataObjectModifications(dataStructure, resultOutputActions.value().modifiedActions, pFeatureIdsArrayPathValue.getParent(), pIgnoredDataArrayPathsValue);
nx::core::AppendDataObjectModifications(dataStructure, resultOutputActions.value().modifiedActions, cellDataPath, pIgnoredDataArrayPathsValue);

// Return actions and updated preflight values.
return {std::move(resultOutputActions), std::move(preflightUpdatedValues)};
Expand All @@ -145,6 +162,11 @@ Result<> ErodeDilateBadDataFilter::executeImpl(DataStructure& dataStructure, con
inputValues.IgnoredDataArrayPaths = filterArgs.value<MultiArraySelectionParameter::ValueType>(k_IgnoredDataArrayPaths_Key);
inputValues.InputImageGeometry = filterArgs.value<DataPath>(k_SelectedImageGeometryPath_Key);

// The algorithm compares DataPaths and finds the Cell arrays through the parent of Feature Ids. Use the geometry's Cell Attribute Matrix path for Feature Ids and each ignored array.
const auto cellDataPath = dataStructure.getDataRefAs<ImageGeom>(inputValues.InputImageGeometry).getCellDataPath();
inputValues.FeatureIdsArrayPath = MapToGeometryCellDataPath(cellDataPath, inputValues.FeatureIdsArrayPath);
inputValues.IgnoredDataArrayPaths = MapToGeometryCellDataPath(cellDataPath, inputValues.IgnoredDataArrayPaths);

return ErodeDilateBadData(dataStructure, messageHandler, shouldCancel, &inputValues)();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
#include "SimplnxCore/Filters/Algorithms/ErodeDilateCoordinationNumber.hpp"

#include "simplnx/DataStructure/DataPath.hpp"
#include "simplnx/DataStructure/Geometry/ImageGeom.hpp"
#include "simplnx/Filter/Actions/EmptyAction.hpp"
#include "simplnx/Parameters/ArraySelectionParameter.hpp"
#include "simplnx/Parameters/BoolParameter.hpp"
#include "simplnx/Parameters/DataGroupSelectionParameter.hpp"
#include "simplnx/Parameters/GeometrySelectionParameter.hpp"
#include "simplnx/Parameters/MultiArraySelectionParameter.hpp"
#include "simplnx/Parameters/NumberParameter.hpp"
#include "simplnx/Utilities/DataArrayUtilities.hpp"
#include "simplnx/Utilities/FilterUtilities.hpp"
#include "simplnx/Utilities/SIMPLConversion.hpp"

Expand Down Expand Up @@ -89,6 +91,23 @@ IFilter::PreflightResult ErodeDilateCoordinationNumberFilter::preflightImpl(cons
auto pCoordinationNumberValue = filterArgs.value<int32>(k_CoordinationNumber_Key);
auto pFeatureIdsArrayPathValue = filterArgs.value<DataPath>(k_CellFeatureIdsArrayPath_Key);
auto pIgnoredDataArrayPathsValue = filterArgs.value<MultiArraySelectionParameter::ValueType>(k_IgnoredDataArrayPaths_Key);
auto imageGeometryPath = filterArgs.value<DataPath>(k_SelectedImageGeometryPath_Key);

auto featureIdsResult = ValidateArrayInGeometryCellData(dataStructure, imageGeometryPath, pFeatureIdsArrayPathValue, -16801, "Cell Feature Ids");
if(featureIdsResult.invalid())
{
return {ConvertResultTo<OutputActions>(std::move(featureIdsResult), {})};
}

auto ignoredArraysResult = ValidateArraysInGeometryCellData(dataStructure, imageGeometryPath, pIgnoredDataArrayPathsValue, -16802, "Attribute Arrays to Ignore");
if(ignoredArraysResult.invalid())
{
return {ConvertResultTo<OutputActions>(std::move(ignoredArraysResult), {})};
}

// The Cell Attribute Matrix can have more than one DataPath. Use the geometry's Cell Attribute Matrix path so that the ignored paths match the modified paths.
const auto cellDataPath = dataStructure.getDataRefAs<ImageGeom>(imageGeometryPath).getCellDataPath();
pIgnoredDataArrayPathsValue = MapToGeometryCellDataPath(cellDataPath, pIgnoredDataArrayPathsValue);

if(pCoordinationNumberValue < 0 || pCoordinationNumberValue > 6)
{
Expand All @@ -100,7 +119,7 @@ IFilter::PreflightResult ErodeDilateCoordinationNumberFilter::preflightImpl(cons

// Inform users that the following arrays are going to be modified in place
// Cell Data is going to be modified
nx::core::AppendDataObjectModifications(dataStructure, resultOutputActions.value().modifiedActions, pFeatureIdsArrayPathValue.getParent(), pIgnoredDataArrayPathsValue);
nx::core::AppendDataObjectModifications(dataStructure, resultOutputActions.value().modifiedActions, cellDataPath, pIgnoredDataArrayPathsValue);

// Return both the resultOutputActions and the preflightUpdatedValues via std::move()
return {std::move(resultOutputActions), std::move(preflightUpdatedValues)};
Expand All @@ -118,6 +137,11 @@ Result<> ErodeDilateCoordinationNumberFilter::executeImpl(DataStructure& dataStr
inputValues.IgnoredDataArrayPaths = filterArgs.value<MultiArraySelectionParameter::ValueType>(k_IgnoredDataArrayPaths_Key);
inputValues.InputImageGeometry = filterArgs.value<DataPath>(k_SelectedImageGeometryPath_Key);

// The algorithm compares DataPaths and finds the Cell arrays through the parent of Feature Ids. Use the geometry's Cell Attribute Matrix path for Feature Ids and each ignored array.
const auto cellDataPath = dataStructure.getDataRefAs<ImageGeom>(inputValues.InputImageGeometry).getCellDataPath();
inputValues.FeatureIdsArrayPath = MapToGeometryCellDataPath(cellDataPath, inputValues.FeatureIdsArrayPath);
inputValues.IgnoredDataArrayPaths = MapToGeometryCellDataPath(cellDataPath, inputValues.IgnoredDataArrayPaths);

return ErodeDilateCoordinationNumber(dataStructure, messageHandler, shouldCancel, &inputValues)();
}

Expand Down
Loading
Loading