Skip to content

Commit

Permalink
ENH: Segment Features Filters are more consistent with memory allocat…
Browse files Browse the repository at this point in the history
…ions (#1030)

* ENH: Segment Features Filters are more consistent with memory allocations

- All will only allocate a single tuple for feature attribute matrix during preflight
- Feature Attribute Matrix is now resized after the segmentation is completed instead of every iteration
- Code is much more consistent in variable naming and code layout between the 3 filters
- Unit test: Check for a specific number of features that were created.
- Update error codes that are all the same. -5551
- Update docs for C++20 mentions and update a pipeline.

---------

Signed-off-by: Michael Jackson <[email protected]>
Co-authored-by: Nathan Young <[email protected]>
  • Loading branch information
imikejackson and nyoungbq authored Aug 2, 2024
1 parent d534b18 commit 06395e5
Show file tree
Hide file tree
Showing 31 changed files with 318 additions and 291 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

DREAM3D-NX is a user-friendly and versatile desktop application that leverages the open-source ‘simplnx’ software library to enable users to manipulate, analyze, and visualize multidimensional, multimodal data with ease. With its advanced reconstruction, quantification, meshing, data organization, and visualization capabilities, DREAM3D-NX has emerged as a go-to tool for the materials science community to reconstruct and quantify 3D microstructures. Its flexibility and adaptability make it suitable for a broad range of multidimensional data analysis applications beyond materials science and engineering domain.

`simplnx` is a rewrite of the [SIMPL](https://www.github.com/bluequartzsoftware/simpl) library upon which [DREAM3D](https://www.github.com/bluequartzsoftware/dream3d) is written. This library aims to be fully C++17 compliant, removes the need for Qt5 at the library level and brings more flexible data organization and grouping options. The library is under active development by BlueQuartz Software at the current time.
`simplnx` is a rewrite of the [SIMPL](https://www.github.com/bluequartzsoftware/simpl) library upon which [DREAM3D](https://www.github.com/bluequartzsoftware/dream3d) is written. This library aims to be fully C++20 compliant, removes the need for Qt5 at the library level and brings more flexible data organization and grouping options. The library is under active development by BlueQuartz Software at the current time.

## Project Vision

Expand Down
4 changes: 2 additions & 2 deletions docs/Build_From_Source.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@

## Prerequisites ##

In order to compile `simplnx` you will need a C++17 compiler suite installed on your computer.
In order to compile `simplnx` you will need a C++20 compiler suite installed on your computer.

+ Compiler
+ Windows Visual Studio 2019 v142 toolset
+ macOS 12.0 and Xcode 14.2 or higher
+ macOS 12.5 and Xcode 14.2 or higher
+ Linux with GCC Version 11.0 or higher or clang 14.

## Install vcpkg ##
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
},
"output_file_path": "Data/Output/Reconstruction/Small_IN100.h5ebsd",
"reference_frame_index": 0,
"stacking_orde_index": 1,
"stacking_order_index": 1,
"z_spacing": 0.25
},
"filter": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,35 @@ Result<> CAxisSegmentFeatures::operator()()
}
}

m_FeatureIdsArray = m_DataStructure.getDataAs<Int32Array>(m_InputValues->FeatureIdsArrayName);
m_FeatureIdsArray = m_DataStructure.getDataAs<Int32Array>(m_InputValues->FeatureIdsArrayPath);
m_FeatureIdsArray->fill(0);
auto* active = m_DataStructure.getDataAs<UInt8Array>(m_InputValues->ActiveArrayName);
auto* active = m_DataStructure.getDataAs<UInt8Array>(m_InputValues->ActiveArrayPath);
active->fill(1);

// Generate the random voxel indices that will be used for the seed points to start a new grain growth/agglomeration
// Run the segmentation algorithm
execute(imageGeometry);

const auto totalFeatures = static_cast<int64>(active->getNumberOfTuples());
if(totalFeatures < 2)
// Sanity check the result.
if(this->m_FoundFeatures < 1)
{
return MakeErrorResult(-8362, "The number of Features was 0 or 1 which means no Features were detected. A threshold value may be set too high");
return {MakeErrorResult(-87000, fmt::format("The number of Features is '{}' which means no Features were detected. A threshold value may be set incorrectly", this->m_FoundFeatures))};
}

// By default we randomize grains
// Resize the Feature Attribute Matrix
std::vector<usize> tDims = {static_cast<usize>(this->m_FoundFeatures + 1)};
auto& cellFeaturesAM = m_DataStructure.getDataRefAs<AttributeMatrix>(m_InputValues->CellFeatureAttributeMatrixPath);
cellFeaturesAM.resizeTuples(tDims); // This will resize the active array

// make sure all values are initialized and "re-reserve" index 0
auto* activeArray = m_DataStructure.getDataAs<UInt8Array>(m_InputValues->ActiveArrayPath);
activeArray->getDataStore()->fill(1);
(*activeArray)[0] = 0;

// Randomize the feature Ids for purely visual clarify. Having random Feature Ids
// allows users visualizing the data to better discern each grain otherwise the coloring
// would look like a smooth gradient. This is a user input parameter
if(m_InputValues->RandomizeFeatureIds)
{
randomizeFeatureIds(m_FeatureIdsArray, totalFeatures);
randomizeFeatureIds(m_FeatureIdsArray, this->m_FoundFeatures + 1);
}

return {};
Expand Down Expand Up @@ -111,7 +122,7 @@ int64 CAxisSegmentFeatures::getSeed(int32 gnum, int64 nextSeed) const
}
if(seed >= 0)
{
auto& cellFeatureAM = m_DataStructure.getDataRefAs<AttributeMatrix>(m_InputValues->CellFeatureAttributeMatrixName);
auto& cellFeatureAM = m_DataStructure.getDataRefAs<AttributeMatrix>(m_InputValues->CellFeatureAttributeMatrixPath);
featureIds[static_cast<usize>(seed)] = gnum;
const std::vector<usize> tDims = {static_cast<usize>(gnum) + 1};
cellFeatureAM.resizeTuples(tDims); // This will resize the active array
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ struct ORIENTATIONANALYSIS_EXPORT CAxisSegmentFeaturesInputValues
DataPath CellPhasesArrayPath;
DataPath MaskArrayPath;
DataPath CrystalStructuresArrayPath;
DataPath FeatureIdsArrayName;
DataPath CellFeatureAttributeMatrixName;
DataPath ActiveArrayName;
DataPath FeatureIdsArrayPath;
DataPath CellFeatureAttributeMatrixPath;
DataPath ActiveArrayPath;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,43 +21,52 @@ EBSDSegmentFeatures::~EBSDSegmentFeatures() noexcept = default;
// -----------------------------------------------------------------------------
Result<> EBSDSegmentFeatures::operator()()
{
auto* gridGeom = m_DataStructure.getDataAs<IGridGeometry>(m_InputValues->gridGeomPath);
auto* gridGeom = m_DataStructure.getDataAs<IGridGeometry>(m_InputValues->ImageGeometryPath);

m_QuatsArray = m_DataStructure.getDataAs<Float32Array>(m_InputValues->quatsArrayPath);
m_CellPhases = m_DataStructure.getDataAs<Int32Array>(m_InputValues->cellPhasesArrayPath);
if(m_InputValues->useGoodVoxels)
m_QuatsArray = m_DataStructure.getDataAs<Float32Array>(m_InputValues->QuatsArrayPath);
m_CellPhases = m_DataStructure.getDataAs<Int32Array>(m_InputValues->CellPhasesArrayPath);
if(m_InputValues->UseMask)
{
try
{
m_GoodVoxelsArray = std::move(InstantiateMaskCompare(m_DataStructure, m_InputValues->goodVoxelsArrayPath));
m_GoodVoxelsArray = std::move(InstantiateMaskCompare(m_DataStructure, m_InputValues->MaskArrayPath));
} catch(const std::out_of_range& exception)
{
// This really should NOT be happening as the path was verified during preflight BUT we may be calling this from
// somewhere else that is NOT going through the normal nx::core::IFilter API of Preflight and Execute
std::string message = fmt::format("Mask Array DataPath does not exist or is not of the correct type (Bool | UInt8) {}", m_InputValues->goodVoxelsArrayPath.toString());
std::string message = fmt::format("Mask Array DataPath does not exist or is not of the correct type (Bool | UInt8) {}", m_InputValues->MaskArrayPath.toString());
return MakeErrorResult(-485090, message);
}
}
m_CrystalStructures = m_DataStructure.getDataAs<UInt32Array>(m_InputValues->crystalStructuresArrayPath);
m_CrystalStructures = m_DataStructure.getDataAs<UInt32Array>(m_InputValues->CrystalStructuresArrayPath);

m_FeatureIdsArray = m_DataStructure.getDataAs<Int32Array>(m_InputValues->featureIdsArrayPath);
m_FeatureIdsArray = m_DataStructure.getDataAs<Int32Array>(m_InputValues->FeatureIdsArrayPath);
m_FeatureIdsArray->fill(0); // initialize the output array with zeros

// Run the segmentation algorithm
execute(gridGeom);

auto* activeArray = m_DataStructure.getDataAs<IDataArray>(m_InputValues->activeArrayPath);
auto totalFeatures = activeArray->getNumberOfTuples();
if(totalFeatures < 2)
// Sanity check the result.
if(this->m_FoundFeatures < 1)
{
return {nonstd::make_unexpected(std::vector<Error>{Error{-87000, "The number of Features was 0 or 1 which means no Features were detected. A threshold value may be set too high"}})};
return {MakeErrorResult(-87000, fmt::format("The number of Features is '{}' which means no Features were detected. A threshold value may be set incorrectly", this->m_FoundFeatures))};
}

// Resize the Feature Attribute Matrix
std::vector<usize> tDims = {static_cast<usize>(this->m_FoundFeatures + 1)};
auto& cellFeaturesAM = m_DataStructure.getDataRefAs<AttributeMatrix>(m_InputValues->CellFeatureAttributeMatrixPath);
cellFeaturesAM.resizeTuples(tDims); // This will resize the active array

// make sure all values are initialized and "re-reserve" index 0
auto* activeArray = m_DataStructure.getDataAs<UInt8Array>(m_InputValues->ActiveArrayPath);
activeArray->getDataStore()->fill(1);
(*activeArray)[0] = 0;

// Randomize the feature Ids for purely visual clarify. Having random Feature Ids
// allows users visualizing the data to better discern each grain otherwise the coloring
// would look like a smooth gradient. This is a user input parameter
if(m_InputValues->shouldRandomizeFeatureIds)
if(m_InputValues->RandomizeFeatureIds)
{
randomizeFeatureIds(m_FeatureIdsArray, totalFeatures);
randomizeFeatureIds(m_FeatureIdsArray, this->m_FoundFeatures + 1);
}

return {};
Expand All @@ -78,7 +87,7 @@ int64_t EBSDSegmentFeatures::getSeed(int32 gnum, int64 nextSeed) const
{
if(featureIds->getValue(randPoint) == 0) // If the GrainId of the voxel is ZERO then we can use this as a seed point
{
if((!m_InputValues->useGoodVoxels || m_GoodVoxelsArray->isTrue(randPoint)) && cellPhases->getValue(randPoint) > 0)
if((!m_InputValues->UseMask || m_GoodVoxelsArray->isTrue(randPoint)) && cellPhases->getValue(randPoint) > 0)
{
seed = static_cast<int64>(randPoint);
}
Expand All @@ -94,12 +103,7 @@ int64_t EBSDSegmentFeatures::getSeed(int32 gnum, int64 nextSeed) const
}
if(seed >= 0)
{
auto& activeArray = m_DataStructure.getDataAs<UInt8Array>(m_InputValues->activeArrayPath)->getDataStoreRef();
auto& cellFeatureAM = m_DataStructure.getDataRefAs<AttributeMatrix>(m_InputValues->cellFeatureAttributeMatrixPath);
featureIds->setValue(static_cast<usize>(seed), gnum);
std::vector<usize> tDims = {static_cast<usize>(gnum) + 1};
cellFeatureAM.resizeTuples(tDims); // This will resize the actives array
activeArray[gnum] = 1;
}
return seed;
}
Expand Down Expand Up @@ -139,7 +143,7 @@ bool EBSDSegmentFeatures::determineGrouping(int64 referencePoint, int64 neighbor
OrientationF axisAngle = m_OrientationOps[phase1]->calculateMisorientation(q1, q2);
w = axisAngle[3];
}
if(w < m_InputValues->misorientationTolerance)
if(w < m_InputValues->MisorientationTolerance)
{
group = true;
featureIds[neighborPoint] = gnum;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ namespace nx::core
*/
struct ORIENTATIONANALYSIS_EXPORT EBSDSegmentFeaturesInputValues
{
float32 misorientationTolerance;
bool useGoodVoxels;
bool shouldRandomizeFeatureIds;
DataPath gridGeomPath;
DataPath quatsArrayPath;
DataPath cellPhasesArrayPath;
DataPath goodVoxelsArrayPath;
DataPath crystalStructuresArrayPath;
DataPath featureIdsArrayPath;
DataPath cellFeatureAttributeMatrixPath;
DataPath activeArrayPath;
float32 MisorientationTolerance;
bool UseMask;
bool RandomizeFeatureIds;
DataPath ImageGeometryPath;
DataPath QuatsArrayPath;
DataPath CellPhasesArrayPath;
DataPath MaskArrayPath;
DataPath CrystalStructuresArrayPath;
DataPath FeatureIdsArrayPath;
DataPath CellFeatureAttributeMatrixPath;
DataPath ActiveArrayPath;
};

/**
Expand Down
Loading

0 comments on commit 06395e5

Please sign in to comment.