From c7fb75235f5ac9e3f6dcc7e995c45641df43fc3a Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Tue, 17 Oct 2023 11:04:44 -0400 Subject: [PATCH] DOC: Improve explanations for the Find Average C-Axis algorithm. (#741) Signed-off-by: Michael Jackson --- .../OrientationAnalysis/docs/FindAvgCAxesFilter.md | 6 +++++- .../Filters/Algorithms/FindAvgCAxes.cpp | 13 +++++++++++-- .../Filters/Algorithms/FindAvgOrientations.cpp | 5 +++-- .../Filters/ImportH5EspritDataFilter.cpp | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Plugins/OrientationAnalysis/docs/FindAvgCAxesFilter.md b/src/Plugins/OrientationAnalysis/docs/FindAvgCAxesFilter.md index 56afefbf33..a3ff22a8e4 100644 --- a/src/Plugins/OrientationAnalysis/docs/FindAvgCAxesFilter.md +++ b/src/Plugins/OrientationAnalysis/docs/FindAvgCAxesFilter.md @@ -9,11 +9,15 @@ Statistics (Crystallographic) This **Filter** determines the average C-axis location of each **Feature** by the following algorithm: 1. Gather all **Elements** that belong to the **Feature** -2. Determine the location of the c-axis in the sample *reference frame* for the rotated quaternions for all **Elements** +2. Determine the location of the c-axis in the sample *reference frame* for the rotated quaternions for all **Elements**. This is achieved by converting the input quaternion to +and orientation matrix (which represents a passive transform matrix), taking the transpose of the matrix to convert it from passive to active, and thne multiplying the +transposed matrix by the crystallographic C-Axis direction vector <001>. 3. Average the locations and store as the average for the **Feature** *Note:* This **Filter** will only work properly for *Hexagonal* materials. The **Filter** does not apply any symmetry operators because there is only one c-axis (<001>) in *Hexagonal* materials and thus all symmetry operators will leave the c-axis in the same position in the sample *reference frame*. However, in *Cubic* materials, for example, the {100} family of directions are all equivalent and the <001> direction will change location in the sample *reference frame* when symmetry operators are applied. +This filter will error out if **ALL** phases are non-hexagonal. Any non-hexagonal phases will have their computed values set to NaN value. + % Auto generated parameter table will be inserted here ## Example Pipelines diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/FindAvgCAxes.cpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/FindAvgCAxes.cpp index f433a00188..56bea89f79 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/FindAvgCAxes.cpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/FindAvgCAxes.cpp @@ -85,26 +85,35 @@ Result<> FindAvgCAxes::operator()() if(crystalStructureType == EbsdLib::CrystalStructure::Hexagonal_High || crystalStructureType == EbsdLib::CrystalStructure::Hexagonal_Low) { const usize quatIndex = i * 4; + + // Create the 3x3 Orientation Matrix from the Quaternion. This represents a passive rotation matrix OrientationF oMatrix = OrientationTransformation::qu2om({quats[quatIndex], quats[quatIndex + 1], quats[quatIndex + 2], quats[quatIndex + 3]}); - // Convert the quaternion matrix to a transposed g matrix so when caxis is multiplied by it, it will give the sample direction that the caxis is along + // Convert the passive rotation matrix to an active rotation matrix g1T = OrientationMatrixToGMatrixTranspose(oMatrix); + // Multiply the active transformation matrix by the C-Axis (as Miller Index). This actively rotates + // the crystallographic C-Axis (which is along the <0,0,1> direction) into the physical sample + // reference frame c1 = g1T * cAxis; // normalize so that the magnitude is 1 c1.normalize(); + // Compute the running average c-axis and normalize the result curCAxis[0] = avgCAxes[cAxesIndex] / counter[featureIds[i]]; curCAxis[1] = avgCAxes[cAxesIndex + 1] / counter[featureIds[i]]; curCAxis[2] = avgCAxes[cAxesIndex + 2] / counter[featureIds[i]]; - curCAxis.normalize(); + + // Ensure that angle between the current point's sample reference frame C-Axis + // and the running average sample C-Axis is positive w = ImageRotationUtilities::CosBetweenVectors(c1, curCAxis); if(w < 0) { c1 *= -1.0f; } + // Continue summing up the rotations counter[featureIds[i]]++; avgCAxes[cAxesIndex] += c1[0]; avgCAxes[cAxesIndex + 1] += c1[1]; diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/FindAvgOrientations.cpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/FindAvgOrientations.cpp index 8610591a4d..2a32096e41 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/FindAvgOrientations.cpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/FindAvgOrientations.cpp @@ -75,10 +75,11 @@ Result<> FindAvgOrientations::operator()() float32 count = counts[featureId]; QuatF curAvgQuat(avgQuats[featureIdOffset] / count, avgQuats[featureIdOffset + 1] / count, avgQuats[featureIdOffset + 2] / count, avgQuats[featureIdOffset + 3] / count); - // Get the pointer to the current voxel's Quaternion - QuatF voxQuat(quats[i * 4], quats[i * 4 + 1], quats[i * 4 + 2], quats[i * 4 + 3]); // Makes a copy into voxQuat!!!! + // Make a copy of the current quaternion from the DataArray into a QuatF object + QuatF voxQuat(quats[i * 4], quats[i * 4 + 1], quats[i * 4 + 2], quats[i * 4 + 3]); QuatF nearestQuat = orientationOps[crystalStructures[phase]]->getNearestQuat(curAvgQuat, voxQuat); + // Add the running average quat with the current quat curAvgQuat = curAvgQuat + nearestQuat; // Copy the new curAvgQuat back into the output array avgQuats[featureIdOffset] = curAvgQuat.x(); diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/ImportH5EspritDataFilter.cpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/ImportH5EspritDataFilter.cpp index a18260ed57..2528b10365 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/ImportH5EspritDataFilter.cpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/ImportH5EspritDataFilter.cpp @@ -68,7 +68,7 @@ Parameters ImportH5EspritDataFilter::parameters() const params.insert(std::make_unique(k_SelectedScanNames_Key, "Scan Names", "The name of the scan in the .h5 file. EDAX can store multiple scans in a single file", OEMEbsdScanSelectionParameter::ValueType{}, /* OEMEbsdScanSelectionParameter::AllowedManufacturers{EbsdLib::OEM::Bruker, EbsdLib::OEM::DREAM3D},*/ - OEMEbsdScanSelectionParameter::EbsdReaderType::Esprit, OEMEbsdScanSelectionParameter::ExtensionsType{".h5"})); + OEMEbsdScanSelectionParameter::EbsdReaderType::Esprit, OEMEbsdScanSelectionParameter::ExtensionsType{".h5", ".hdf5"})); params.insert(std::make_unique(k_ZSpacing_Key, "Z Spacing (Microns)", "The spacing in microns between each layer.", 1.0f)); params.insert(std::make_unique(k_Origin_Key, "Origin", "The origin of the volume", std::vector{0.0F, 0.0F, 0.0F}, std::vector{"x", "y", "z"})); params.insert(std::make_unique(k_DegreesToRadians_Key, "Convert Euler Angles to Radians", "Whether or not to convert the euler angles to radians", true));