Skip to content

Commit e689671

Browse files
authored
ENH: Microtexture related filter cleanup (#1438)
* API: Update to latest EbsdLib API changes * BUG: CAxisSegmentFeatures - Ensure all voxels that are segmented have a hexagonal phase type. * BUG: ComputeAvgOrientations fix incorrect computation. * BUG: ConvertOrientations * make sure the quaternion is properly formed before conversion * BUG: Edax,Bruker,Oxford H5 EBSD Readers should check for null PatternData pointer * BUG: FindFeatureReferenceCAxisOrientation - Use doubles to accumulate the StdDev values. Output values now agree with DREAM.3D > 6.5.172 * BUG: Fixes ComputeFeatureNeighborCAxisAlignments crash if "Find Avg Misalignments" was not enabled. * BUG: Fixes incorrect progress message in ComputeNeighborhoods filter * BUG: ReadH5Ebsd: EulerAngles must be read to perform the Euler Ref frame transform * BUG: ReadH5OimData-Fix index calculation when copying data into final arrays * Compute Average C-Axis was updated and documented with comments * DOC: Add Point Group and Rotation Point Group to the Laue class table * DOC: Updates CreateEnsembleInfo documentation to add in the Rotation Point Groups * ENH: Adds function to create a randomized "index" array that is used elsewhere * ENH: Fixes SegmentFeatures algorithm to use a throttled messenger for progress updates * ENH: Fixes small bugs related to running Microtexture pipelines * ENH: Remove warning about hex phases for CAxisSegmentFeatures * REV: Compute Feature Neighbor Code Review * REV: Compute Feature Reference CAxis Misorientations has been reviewed. * REV: ComputeFeatureReferenceCAxisMisorientations: Document and small code refactorings * STY: Use proper include syntax for EbsdLib since it is an external library * TEST: Update ComputeAvgOrientations Unit Test. This is really ONLY testing Cubic High Laue group. * TEST: Update ComputeFZQuaternions Unit Test to allow for a tolerance for results
1 parent 24c7d5f commit e689671

File tree

83 files changed

+769
-661
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+769
-661
lines changed

src/Plugins/OrientationAnalysis/docs/ComputeAvgCAxesFilter.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,20 @@ This **Filter** determines the average C-axis location of each **Feature** by th
1010

1111
1. Gather all **Elements** that belong to the **Feature**
1212
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
13-
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
13+
an orientation matrix (which represents a passive transform matrix), taking the transpose of the matrix to convert it from passive to active, and then multiplying the
1414
transposed matrix by the crystallographic C-Axis direction vector <001>.
15-
3. Average the locations and store as the average for the **Feature**
15+
3. Average the locations and store the average for the **Feature**
1616

17-
*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.
17+
*Note:* This **Filter** will only work properly for *Hexagonal* materials. The **Filter** does not apply any symmetry
18+
operators because there is only one c-axis (<001>) in *Hexagonal* materials and thus all symmetry operators will leave
19+
the c-axis in the same position in the sample *reference frame*. However, in *Cubic* materials, for example, the {100}
20+
family of directions are all equivalent and the <001> direction will change location in the sample *reference frame* when
21+
symmetry operators are applied.
1822

1923
This filter will error out if **ALL** phases are non-hexagonal. Any non-hexagonal phases will have their computed values set to NaN value.
2024

25+
The output is a direction vector for each feature.
26+
2127
% Auto generated parameter table will be inserted here
2228

2329
## Example Pipelines

src/Plugins/OrientationAnalysis/docs/CreateEnsembleInfoFilter.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,22 @@ Processing (Generation)
88

99
This **Filter** allows the user to enter basic crystallographic information about each phase. The Laue class, Phase Type, and Phase Name can all be entered by the user. The information is stored in an EnsembleAttributeMatrix. These values are needed to allow the calculation of certain kinds of crystallographic statistics on the volume, if they have not already been provided by some other means. Each row in the table lists the **Crystal Structure**, **Phase Type**, and **Phase Name**. The proper values for the crystal structure and phase type come from internal constants within DREAM3D-NX and are listed here:
1010

11-
### Crystal Structure
12-
13-
| String Name | Internal Value | Laue Name |
14-
| ------------|----------------|----------|
15-
| Hexagonal_High | 0 | Hexagonal-High 6/mmm |
16-
| Cubic_High | 1 | Cubic Cubic-High m3m |
17-
| Hexagonal_Low | 2 | Hexagonal-Low 6/m |
18-
| Cubic_Low | 3 | Cubic Cubic-Low m3 (Tetrahedral) |
19-
| Triclinic | 4 | Triclinic -1 |
20-
| Monoclinic | 5 | Monoclinic 2/m |
21-
| OrthoRhombic | 6 | Orthorhombic mmm |
22-
| Tetragonal_Low | 7 | Tetragonal-Low 4/m |
23-
| Tetragonal_High | 8 | Tetragonal-High 4/mmm |
24-
| Trigonal_Low | 9 | Trigonal-Low -3 |
25-
| Trigonal_High | 10 | Trigonal-High -3m |
26-
| UnknownCrystalStructure | 999 | Undefined Crystal Structure |
11+
## DREAM3D-NX Laue Group to Point Group Table
12+
13+
| Internal Value | String Name | HM Sym | Point Group | Rotation Point Group |
14+
|----------------|--------------------------|--------|-------------|----------------------|
15+
| 0 | Hexagonal_High | 6/mmm | 27 | 622 |
16+
| 1 | Cubic_High | m-3m | 32 | 432 |
17+
| 2 | Hexagonal_Low | 6/m | 23 | 6 |
18+
| 3 | Cubic_Low | m-3 | 29 | 23 |
19+
| 4 | Triclinic | -1 | 2 | 1 |
20+
| 5 | Monoclinic | 2/m | 5 | 2 |
21+
| 6 | OrthoRhombic | mmm | 8 | 222 |
22+
| 7 | Tetragonal_Low | 4/m | 11 | 4 |
23+
| 8 | Tetragonal_High | 4/mmm | 15 | 422 |
24+
| 9 | Trigonal_Low | -3 | 17 | 3 |
25+
| 10 | Trigonal_High | -3m | 20 | 32 |
26+
| 999 | UnknownCrystalStructure | | | |
2727

2828
### Phase Type
2929

src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/AlignSectionsMisorientation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include "simplnx/Utilities/FilterUtilities.hpp"
77
#include "simplnx/Utilities/MaskCompareUtilities.hpp"
88

9-
#include "EbsdLib/LaueOps/LaueOps.h"
9+
#include <EbsdLib/LaueOps/LaueOps.h>
1010

1111
#include <iostream>
1212

src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/AlignSectionsMutualInformation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "simplnx/Utilities/FilterUtilities.hpp"
99
#include "simplnx/Utilities/StringUtilities.hpp"
1010

11-
#include "EbsdLib/LaueOps/LaueOps.h"
11+
#include <EbsdLib/LaueOps/LaueOps.h>
1212

1313
#include <vector>
1414

src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/BadDataNeighborOrientationCheck.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "simplnx/Utilities/MessageHelper.hpp"
99

1010
#include "EbsdLib/Core/Orientation.hpp"
11-
#include "EbsdLib/LaueOps/LaueOps.h"
11+
#include <EbsdLib/LaueOps/LaueOps.h>
1212

1313
using namespace nx::core;
1414

src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/CAxisSegmentFeatures.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "simplnx/Utilities/ClusteringUtilities.hpp"
99
#include "simplnx/Utilities/Math/MatrixMath.hpp"
1010

11-
#include "EbsdLib/LaueOps/LaueOps.h"
11+
#include <EbsdLib/Core/OrientationTransformation.hpp>
1212

1313
using namespace nx::core;
1414
using namespace nx::core::OrientationUtilities;
@@ -42,10 +42,16 @@ Result<> CAxisSegmentFeatures::operator()()
4242
return MakeErrorResult(-8362, fmt::format("Mask Array DataPath does not exist or is not of the correct type (Bool | UInt8) {}", m_InputValues->MaskArrayPath.toString()));
4343
}
4444
}
45+
46+
// Loop through all the "Phase" cell values and validate that any phase found is
47+
// a hexagonal phase. This guards against there being multiple phases defined in
48+
// and EBSD file but the non-hexagonal phases were actually never found
4549
const auto& crystalStructures = m_DataStructure.getDataRefAs<UInt32Array>(m_InputValues->CrystalStructuresArrayPath);
46-
for(usize i = 1; i < crystalStructures.size(); ++i)
50+
usize numCells = m_CellPhases->getNumberOfTuples();
51+
for(usize cellIdx = 0; cellIdx < numCells; ++cellIdx)
4752
{
48-
const auto crystalStructureType = crystalStructures[i];
53+
int32 currentPhaseIdx = m_CellPhases->getValue(cellIdx);
54+
const auto crystalStructureType = crystalStructures[currentPhaseIdx];
4955
if(crystalStructureType != EbsdLib::CrystalStructure::Hexagonal_High && crystalStructureType != EbsdLib::CrystalStructure::Hexagonal_Low)
5056
{
5157
return MakeErrorResult(-8363, fmt::format("Input data is using {} type crystal structures but segmenting features via c-axis mis orientation requires all phases to be either Hexagonal-Low 6/m "

src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeAvgCAxes.cpp

Lines changed: 80 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "simplnx/DataStructure/DataArray.hpp"
66
#include "simplnx/Utilities/ImageRotationUtilities.hpp"
7+
#include "simplnx/Utilities/Math/GeometryMath.hpp"
78
#include "simplnx/Utilities/Math/MatrixMath.hpp"
89

910
#include "EbsdLib/Core/Orientation.hpp"
@@ -34,6 +35,8 @@ const std::atomic_bool& ComputeAvgCAxes::getCancel()
3435
// -----------------------------------------------------------------------------
3536
Result<> ComputeAvgCAxes::operator()()
3637
{
38+
39+
// Figure out if all phases are either Hexagonal-Low 6/m or Hexagonal-High 6/mmm Laue Phases
3740
const auto& crystalStructures = m_DataStructure.getDataRefAs<UInt32Array>(m_InputValues->CrystalStructuresArrayPath);
3841
bool allPhasesHexagonal = true;
3942
bool noPhasesHexagonal = true;
@@ -45,17 +48,18 @@ Result<> ComputeAvgCAxes::operator()()
4548
noPhasesHexagonal = noPhasesHexagonal && !isHex;
4649
}
4750

51+
// If NONE of the phases are hexagonal then bail out now with an error
4852
if(noPhasesHexagonal)
4953
{
50-
return MakeErrorResult(-6402, "Finding the average c-axes requires at least one phase to be Hexagonal-Low 6/m or Hexagonal-High 6/mmm type crystal structures but none were found.");
54+
return MakeErrorResult(-76402, "No phases that have a crystal symmetry of Hexagonal (6/mmm or 6/m) were found.");
5155
}
5256

5357
Result<> result;
58+
59+
// Throw a warning for any NON-Hex Laue Phases
5460
if(!allPhasesHexagonal)
5561
{
56-
result.warnings().push_back(
57-
{-6403,
58-
"Finding the average c-axes requires Hexagonal-Low 6/m or Hexagonal-High 6/mmm type crystal structures. All calculations for non Hexagonal phases will be skipped and a NaN value inserted."});
62+
result.warnings().push_back({-76403, "Non Hexagonal phases were found. All calculations for non Hexagonal phases will be skipped and a NaN value inserted."});
5963
}
6064

6165
const auto& featureIds = m_DataStructure.getDataRefAs<Int32Array>(m_InputValues->FeatureIdsArrayPath);
@@ -66,77 +70,87 @@ Result<> ComputeAvgCAxes::operator()()
6670
const usize totalPoints = featureIds.getNumberOfTuples();
6771
const usize totalFeatures = avgCAxes.getNumberOfTuples();
6872

69-
Matrix3fR g1T;
73+
Matrix3dR g1T;
7074
g1T.fill(0.0f);
71-
const Eigen::Vector3f cAxis{0.0f, 0.0f, 1.0f};
72-
Eigen::Vector3f c1{0.0f, 0.0f, 0.0f};
75+
const Eigen::Vector3d cAxis{0.0f, 0.0f, 1.0f};
76+
Eigen::Vector3d c1{0.0f, 0.0f, 0.0f};
7377

7478
std::vector<int32> counter(totalFeatures, 0);
7579

76-
Eigen::Vector3f curCAxis{0.0f, 0.0f, 0.0f};
77-
usize cAxesIndex = 0;
78-
float32 w = 0.0f;
79-
80+
// Loop over each cell
8081
for(usize i = 0; i < totalPoints; i++)
8182
{
82-
if(featureIds[i] > 0)
83+
int32 currentFeatureId = featureIds[i];
84+
// If the featureId for a given cell is valid ( > 0) then analyze that value
85+
if(currentFeatureId > 0)
8386
{
84-
cAxesIndex = 3 * featureIds[i];
85-
const auto crystalStructureType = crystalStructures[cellPhases[i]];
86-
if(crystalStructureType == EbsdLib::CrystalStructure::Hexagonal_High || crystalStructureType == EbsdLib::CrystalStructure::Hexagonal_Low)
87-
{
88-
const usize quatIndex = i * 4;
89-
90-
// Create the 3x3 Orientation Matrix from the Quaternion. This represents a passive rotation matrix
91-
OrientationF oMatrix = OrientationTransformation::qu2om<QuatF, OrientationF>({quats[quatIndex], quats[quatIndex + 1], quats[quatIndex + 2], quats[quatIndex + 3]});
92-
93-
// Convert the passive rotation matrix to an active rotation matrix
94-
g1T = OrientationMatrixToGMatrixTranspose(oMatrix);
95-
96-
// Multiply the active transformation matrix by the C-Axis (as Miller Index). This actively rotates
97-
// the crystallographic C-Axis (which is along the <0,0,1> direction) into the physical sample
98-
// reference frame
99-
c1 = g1T * cAxis;
100-
101-
// normalize so that the magnitude is 1
102-
c1.normalize();
103-
104-
// Compute the running average c-axis and normalize the result
105-
curCAxis[0] = avgCAxes[cAxesIndex] / counter[featureIds[i]];
106-
curCAxis[1] = avgCAxes[cAxesIndex + 1] / counter[featureIds[i]];
107-
curCAxis[2] = avgCAxes[cAxesIndex + 2] / counter[featureIds[i]];
108-
curCAxis.normalize();
109-
110-
// Ensure that angle between the current point's sample reference frame C-Axis
111-
// and the running average sample C-Axis is positive
112-
w = ImageRotationUtilities::CosBetweenVectors(c1, curCAxis);
113-
if(w < 0)
114-
{
115-
c1 *= -1.0f;
116-
}
117-
// Continue summing up the rotations
118-
counter[featureIds[i]]++;
119-
avgCAxes[cAxesIndex] += c1[0];
120-
avgCAxes[cAxesIndex + 1] += c1[1];
121-
avgCAxes[cAxesIndex + 2] += c1[2];
122-
}
123-
else
87+
const int32 currentCellPhase = cellPhases[i]; // Get the current cell phase
88+
const auto crystalStructureType = crystalStructures[currentCellPhase]; // Get the CrystalStructure, i.e., Laue class of the cell
89+
const usize cAxesIndex = 3 * currentFeatureId;
90+
91+
// Ensure the Laue class is correct, otherwise mark the values with a NaN and continue
92+
if(crystalStructureType != EbsdLib::CrystalStructure::Hexagonal_High && crystalStructureType != EbsdLib::CrystalStructure::Hexagonal_Low)
12493
{
12594
avgCAxes[cAxesIndex] = NAN;
12695
avgCAxes[cAxesIndex + 1] = NAN;
12796
avgCAxes[cAxesIndex + 2] = NAN;
97+
continue;
98+
}
99+
100+
counter[currentFeatureId]++; // Increment the count
101+
const usize quatIndex = i * 4;
102+
103+
// Create the 3x3 Orientation Matrix from the Quaternion. This represents a passive rotation matrix
104+
OrientationD oMatrix = OrientationTransformation::qu2om<QuatD, OrientationD>({quats[quatIndex], quats[quatIndex + 1], quats[quatIndex + 2], quats[quatIndex + 3]});
105+
106+
// Convert the passive rotation matrix to an active rotation matrix by taking the transpose
107+
g1T = OrientationMatrixToGMatrixTranspose(oMatrix);
108+
109+
// Multiply the active transformation matrix by the C-Axis (as Miller Index). This actively rotates
110+
// the crystallographic C-Axis (which is along the <0,0,1> direction) into the physical sample
111+
// reference frame
112+
c1 = g1T * cAxis;
113+
114+
// normalize so that the magnitude is 1
115+
c1.normalize();
116+
117+
// Compute the running average c-axis and normalize the result
118+
Eigen::Vector3d curCAxis{0.0f, 0.0f, 0.0f};
119+
curCAxis[0] = avgCAxes[cAxesIndex] / static_cast<float32>(counter[currentFeatureId]);
120+
curCAxis[1] = avgCAxes[cAxesIndex + 1] / static_cast<float32>(counter[currentFeatureId]);
121+
curCAxis[2] = avgCAxes[cAxesIndex + 2] / static_cast<float32>(counter[currentFeatureId]);
122+
curCAxis.normalize();
123+
124+
// Ensure that angle between the current point's sample reference frame C-Axis
125+
// and the running average sample C-Axis is positive
126+
float64 w = ImageRotationUtilities::CosBetweenVectors(c1, curCAxis);
127+
if(w < 0)
128+
{
129+
c1 *= -1.0f;
128130
}
131+
132+
// Continue summing up the rotations
133+
float value = avgCAxes[cAxesIndex] + c1[0];
134+
avgCAxes[cAxesIndex] = value;
135+
136+
value = avgCAxes[cAxesIndex + 1] + c1[1];
137+
avgCAxes[cAxesIndex + 1] = value;
138+
139+
value = avgCAxes[cAxesIndex + 2] + c1[2];
140+
avgCAxes[cAxesIndex + 2] = value;
129141
}
130142
}
131143

132144
for(size_t i = 1; i < totalFeatures; i++)
133145
{
134146
const usize tupleIndex = i * 3;
135-
if(std::isnan(avgCAxes[tupleIndex]))
147+
float32 avgCAxesValue = avgCAxes[tupleIndex];
148+
if(std::isnan(avgCAxesValue))
136149
{
137150
continue;
138151
}
139-
152+
// If we got passed the last check this could happen if the cell points were
153+
// masked out? Maybe?
140154
if(counter[i] == 0)
141155
{
142156
avgCAxes[tupleIndex] = 0;
@@ -145,19 +159,18 @@ Result<> ComputeAvgCAxes::operator()()
145159
}
146160
else
147161
{
148-
float32 a = avgCAxes.getValue(tupleIndex);
149-
float32 b = avgCAxes.getValue(tupleIndex + 1);
150-
float32 c = avgCAxes.getValue(tupleIndex + 2);
151-
152-
a /= counter[i];
153-
b /= counter[i];
154-
c /= counter[i];
155-
156-
MatrixMath::Normalize3x1(a, b, c);
157-
158-
avgCAxes.setValue(tupleIndex, a);
159-
avgCAxes.setValue(tupleIndex + 1, b);
160-
avgCAxes.setValue(tupleIndex + 2, c);
162+
// Compute the final average c-axis value
163+
float value = avgCAxes[3 * i];
164+
value /= static_cast<float>(counter[i]);
165+
avgCAxes[3 * i] = value;
166+
167+
value = avgCAxes[3 * i + 1];
168+
value /= static_cast<float>(counter[i]);
169+
avgCAxes[3 * i + 1] = value;
170+
171+
value = avgCAxes[3 * i + 2];
172+
value /= static_cast<float>(counter[i]);
173+
avgCAxes[3 * i + 2] = value;
161174
}
162175
}
163176
return result;

0 commit comments

Comments
 (0)