Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@ Statistics (Crystallography)

## Description

This **Filter** determines the Kernel Average Misorientation (KAM) for each **Cell**. The user can select the size of the kernel to be used in the calculation. The kernel size entered by the user is the *radius* of the kernel (i.e., entering values of *1*, *2*, *3* will result in a kernel that is *3*, *5*, and *7* **Cells** in size in the X, Y and Z directions, respectively). The algorithm for determination of KAM is as follows:
This **Filter** determines the Kernel Average Misorientation (KAM) for each **Cell**. This **Filter** requires an **Image Geometry**, and the output KAM values are stored in degrees. The user can select the size of the kernel to be used in the calculation. The kernel size entered by the user is the *radius* of the kernel (i.e., entering values of *1*, *2*, *3* will result in a kernel that is *3*, *5*, and *7* **Cells** in size in the X, Y and Z directions, respectively). The algorithm for determination of KAM is as follows:

1. Calculate the misorientation angle between each **Cell** in a kernel and the central **Cell** of the kernel
2. Average all of the misorientations for the kernel and store at the central **Cell**

The calculation will **not** consider cells that belong to different 'feature Ids', i.e., different grains.
The **Use Feature Ids** option controls which **Cells** within the kernel are included in the average:

+ **Checked (default):** only **Cells** that belong to the same *Feature* (same *Feature Id*) as the central **Cell** are considered — the calculation will **not** cross grain boundaries. This is the traditional per-grain KAM.
+ **Unchecked:** the *Feature Id* grouping is ignored and the average may cross grain boundaries, producing a per-voxel KAM. A kernel **Cell** is still excluded if its *Feature Id* is 0 (invalid/background data) or if its *Phase* differs from the central **Cell**'s *Phase* (averaging is restricted to Cells of the same Phase).

In both modes, **Cells** with a *Feature Id* of 0 or a *Phase* of 0 are considered invalid and receive a KAM value of 0.

*Note:* All **Cells** in the kernel are weighted equally during the averaging, though they are not equidistant from the central **Cell**.

For related per-feature misorientation metrics, see the **Compute Feature Reference Misorientations** and **Compute Misorientation** filters.

% Auto generated parameter table will be inserted here

## Example Pipelines
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -749,10 +749,14 @@
],
"version": 1
},
"parameters_version": 1,
"parameters_version": 2,
"quats_array_path": {
"value": "fw-ar-IF1-aptr12-corr/Cell Data/Quats",
"version": 1
},
"use_feature_ids": {
"value": true,
"version": 1
}
},
"comments": "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -749,10 +749,14 @@
],
"version": 1
},
"parameters_version": 1,
"parameters_version": 2,
"quats_array_path": {
"value": "fw-ar-IF1-avtr12-corr/Cell Data/Quats",
"version": 1
},
"use_feature_ids": {
"value": true,
"version": 1
}
},
"comments": "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,14 @@
],
"version": 1
},
"parameters_version": 1,
"parameters_version": 2,
"quats_array_path": {
"value": "DataContainer/Cell Data/Quats",
"version": 1
},
"use_feature_ids": {
"value": true,
"version": 1
}
},
"comments": "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <EbsdLib/LaueOps/LaueOps.h>

#include <algorithm>
#include <chrono>

using namespace nx::core;
Expand All @@ -25,8 +26,12 @@ class FindKernelAvgMisorientationsImpl
, m_InputValues(inputValues)
, m_ShouldCancel(shouldCancel)
{
m_OrientationOps = ebsdlib::LaueOps::GetAllOrientationOps();
}

// For each valid focal cell in the chunk: walk the (2rx+1)x(2ry+1)x(2rz+1) kernel, admit
// neighbors per the Use Feature Ids mode, accumulate symmetry-reduced misorientation via
// LaueOps, and store the average (degrees) at the focal cell. Invalid focal cells get 0.
void convert(size_t zStart, size_t zEnd, size_t yStart, size_t yEnd, size_t xStart, size_t xEnd) const
{
// Input Arrays / Parameter Data
Expand All @@ -39,22 +44,21 @@ class FindKernelAvgMisorientationsImpl
const auto& crystalStructuresArray = m_DataStructure.getDataRefAs<UInt32Array>(m_InputValues->CrystalStructuresArrayPath);
const auto& crystalStructures = crystalStructuresArray.getDataStoreRef();
const auto kernelSize = m_InputValues->KernelSize;
const bool useFeatureIds = m_InputValues->UseFeatureIds;

// Output Arrays
auto& kernelAvgMisorientationsArray = m_DataStructure.getDataRefAs<Float32Array>(m_InputValues->KernelAverageMisorientationsArrayName);
auto& kernelAvgMisorientations = kernelAvgMisorientationsArray.getDataStoreRef();

std::vector<ebsdlib::LaueOps::Pointer> m_OrientationOps = ebsdlib::LaueOps::GetAllOrientationOps();

auto* gridGeom = m_DataStructure.getDataAs<ImageGeom>(m_InputValues->InputImageGeometry);
SizeVec3 udims = gridGeom->getDimensions();
const auto& imageGeom = m_DataStructure.getDataRefAs<ImageGeom>(m_InputValues->InputImageGeometry);
SizeVec3 udims = imageGeom.getDimensions();

ebsdlib::QuatD q1;
ebsdlib::QuatD q2;

// messenger values
usize counter = 0;
usize increment = (zEnd - zStart) / 100;
usize increment = std::max(static_cast<usize>(1), (zEnd - zStart) / 100);

ProgressMessenger progressMessenger = m_ProgressMessageHelper.createProgressMessenger();

Expand All @@ -63,11 +67,6 @@ class FindKernelAvgMisorientationsImpl
auto zPoints = static_cast<int64_t>(udims[2]);
for(size_t plane = zStart; plane < zEnd; plane++)
{
if(m_ShouldCancel)
{
break;
}

if(counter > increment)
{
progressMessenger.sendProgressMessage(counter);
Expand All @@ -76,6 +75,11 @@ class FindKernelAvgMisorientationsImpl

for(size_t row = yStart; row < yEnd; row++)
{
if(m_ShouldCancel)
{
return;
}

for(size_t col = xStart; col < xEnd; col++)
{
size_t point = (plane * xPoints * yPoints) + (row * xPoints) + col;
Expand All @@ -92,29 +96,35 @@ class FindKernelAvgMisorientationsImpl

for(int32_t j = -kernelSize[2]; j < kernelSize[2] + 1; j++)
{

if(plane + j < 0 || plane + j > zPoints - 1)
const int64_t zIdx = static_cast<int64_t>(plane) + j;
if(zIdx < 0 || zIdx > zPoints - 1)
{
continue;
}
const int64_t jStride = j * xPoints * yPoints;
for(int32_t k = -kernelSize[1]; k < kernelSize[1] + 1; k++)
{
if(row + k < 0 || row + k > yPoints - 1)
const int64_t yIdx = static_cast<int64_t>(row) + k;
if(yIdx < 0 || yIdx > yPoints - 1)
{
continue;
}
const int64_t kStride = k * xPoints;
for(int32_t l = -kernelSize[0]; l < kernelSize[0] + 1; l++)
{
if(col + l < 0 || col + l > xPoints - 1)
const int64_t xIdx = static_cast<int64_t>(col) + l;
if(xIdx < 0 || xIdx > xPoints - 1)
{
continue;
}
const int64_t neighbor = static_cast<int64_t>(point) + jStride + kStride + l;
if(neighbor >= 0 && featureIds[point] == featureIds[static_cast<size_t>(neighbor)])
// All three indices are clamped in-bounds, so the flattened neighbor index is
// always valid; no separate negative-index guard is needed.
const auto neighborIdx = static_cast<size_t>((zIdx * xPoints * yPoints) + (yIdx * xPoints) + xIdx);
// Per-grain mode: neighbor must belong to the same feature as the central cell.
// Per-voxel mode (use_feature_ids == false): neighbor must be a valid cell
// (featureId > 0) of the same phase as the central cell.
const bool neighborContributes = useFeatureIds ? (featureIds[point] == featureIds[neighborIdx]) : (featureIds[neighborIdx] > 0 && cellPhases[neighborIdx] == cellPhases[point]);
if(neighborContributes)
{
quatIndex = neighbor * 4;
quatIndex = neighborIdx * 4;
q2[0] = quats[quatIndex];
q2[1] = quats[quatIndex + 1];
q2[2] = quats[quatIndex + 2];
Expand All @@ -127,13 +137,11 @@ class FindKernelAvgMisorientationsImpl
}
}
}
// numVoxel is always >= 1 here: the focal cell passes both neighbor gates (j=k=l=0)
// and contributes a self-misorientation of 0 degrees.
kernelAvgMisorientations[point] = totalMisorientation / static_cast<float>(numVoxel);
if(numVoxel == 0)
{
kernelAvgMisorientations[point] = 0.0f;
}
}
if(featureIds[point] == 0 || cellPhases[point] == 0)
else
{
kernelAvgMisorientations[point] = 0.0f;
}
Expand All @@ -155,6 +163,7 @@ class FindKernelAvgMisorientationsImpl
DataStructure& m_DataStructure;
const ComputeKernelAvgMisorientationsInputValues* m_InputValues = nullptr;
const std::atomic_bool& m_ShouldCancel;
std::vector<ebsdlib::LaueOps::Pointer> m_OrientationOps;
};

} // namespace
Expand All @@ -175,8 +184,8 @@ ComputeKernelAvgMisorientations::~ComputeKernelAvgMisorientations() noexcept = d
// -----------------------------------------------------------------------------
Result<> ComputeKernelAvgMisorientations::operator()()
{
auto* gridGeom = m_DataStructure.getDataAs<ImageGeom>(m_InputValues->InputImageGeometry);
SizeVec3 udims = gridGeom->getDimensions();
const auto& imageGeom = m_DataStructure.getDataRefAs<ImageGeom>(m_InputValues->InputImageGeometry);
SizeVec3 udims = imageGeom.getDimensions();

MessageHelper messageHelper(m_MessageHandler);
ProgressMessageHelper progressMessageHelper = messageHelper.createProgressMessageHelper();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace nx::core
struct ORIENTATIONANALYSIS_EXPORT ComputeKernelAvgMisorientationsInputValues
{
VectorInt32Parameter::ValueType KernelSize;
bool UseFeatureIds = true;
DataPath FeatureIdsArrayPath;
DataPath CellPhasesArrayPath;
DataPath QuatsArrayPath;
Expand All @@ -24,7 +25,12 @@ struct ORIENTATIONANALYSIS_EXPORT ComputeKernelAvgMisorientationsInputValues
};

/**
* @class
* @class ComputeKernelAvgMisorientations
* @brief Computes the Kernel Average Misorientation (KAM) for each cell of an Image Geometry.
* For each valid cell (featureId > 0 and phase > 0), the misorientation between the cell and
* every admitted neighbor in a user-sized kernel is averaged and stored in degrees. Neighbors
* are admitted per-grain (same feature id, the default) or per-voxel (featureId > 0 and same
* phase) depending on the UseFeatureIds input.
*/
class ORIENTATIONANALYSIS_EXPORT ComputeKernelAvgMisorientations
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "simplnx/DataStructure/DataPath.hpp"
#include "simplnx/Filter/Actions/CreateArrayAction.hpp"
#include "simplnx/Parameters/ArraySelectionParameter.hpp"
#include "simplnx/Parameters/BoolParameter.hpp"
#include "simplnx/Parameters/DataGroupSelectionParameter.hpp"
#include "simplnx/Parameters/DataObjectNameParameter.hpp"
#include "simplnx/Parameters/GeometrySelectionParameter.hpp"
Expand Down Expand Up @@ -56,6 +57,11 @@ Parameters ComputeKernelAvgMisorientationsFilter::parameters() const
params.insertSeparator(Parameters::Separator{"Input Parameter(s)"});
params.insert(std::make_unique<VectorInt32Parameter>(k_KernelSize_Key, "Kernel Radius", "Size of the kernel in the X, Y and Z directions (in number of Cells)", std::vector<int32>{1, 1, 1},
std::vector<std::string>{"X", "Y", "Z"}));
params.insert(std::make_unique<BoolParameter>(
k_UseFeatureIds_Key, "Use Feature Ids",
"When checked (default), only kernel Cells belonging to the same Feature as the central Cell are included in the average (per-grain KAM). When unchecked, every in-bounds kernel Cell with a "
"Feature Id greater than 0 and the same phase as the central Cell is included, allowing the average to cross Feature boundaries (per-voxel KAM).",
true));
params.insertSeparator(Parameters::Separator{"Input Cell Data"});
params.insert(std::make_unique<GeometrySelectionParameter>(k_SelectedImageGeometryPath_Key, "Selected Image Geometry", "Path to the target geometry", DataPath({"Data Container"}),
GeometrySelectionParameter::AllowedTypes{IGeometry::Type::Image}));
Expand All @@ -81,7 +87,11 @@ Parameters ComputeKernelAvgMisorientationsFilter::parameters() const
//------------------------------------------------------------------------------
IFilter::VersionType ComputeKernelAvgMisorientationsFilter::parametersVersion() const
{
return 1;
return 2;
// Version 1 -> 2
// Change 1:
// Added k_UseFeatureIds_Key = "use_feature_ids" (default true preserves the
// original per-grain behavior; false enables per-voxel KAM per issue #1613)
}

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -127,6 +137,7 @@ Result<> ComputeKernelAvgMisorientationsFilter::executeImpl(DataStructure& dataS
ComputeKernelAvgMisorientationsInputValues inputValues;

inputValues.KernelSize = filterArgs.value<VectorInt32Parameter::ValueType>(k_KernelSize_Key);
inputValues.UseFeatureIds = filterArgs.value<bool>(k_UseFeatureIds_Key);
inputValues.FeatureIdsArrayPath = filterArgs.value<DataPath>(k_CellFeatureIdsArrayPath_Key);
inputValues.CellPhasesArrayPath = filterArgs.value<DataPath>(k_CellPhasesArrayPath_Key);
inputValues.QuatsArrayPath = filterArgs.value<DataPath>(k_QuatsArrayPath_Key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ namespace nx::core
{
/**
* @class ComputeKernelAvgMisorientationsFilter
* @brief This filter will ....
* @brief Computes the Kernel Average Misorientation (KAM) for each cell, averaging the
* misorientation between the cell and its kernel neighbors either within the cell's feature
* (per-grain, default) or across feature boundaries (per-voxel) per the Use Feature Ids option.
*/
class ORIENTATIONANALYSIS_EXPORT ComputeKernelAvgMisorientationsFilter : public IFilter
{
Expand All @@ -25,6 +27,7 @@ class ORIENTATIONANALYSIS_EXPORT ComputeKernelAvgMisorientationsFilter : public

// Parameter Keys
static constexpr StringLiteral k_KernelSize_Key = "kernel_size";
static constexpr StringLiteral k_UseFeatureIds_Key = "use_feature_ids";
static constexpr StringLiteral k_CellFeatureIdsArrayPath_Key = "feature_ids_path";
static constexpr StringLiteral k_CellPhasesArrayPath_Key = "cell_phases_array_path";
static constexpr StringLiteral k_QuatsArrayPath_Key = "quats_array_path";
Expand Down
Loading
Loading