Skip to content

Commit b908f01

Browse files
nyoungbqimikejackson
authored andcommitted
Move RotateSampleRefFrame to algorithm class
1 parent 24922d3 commit b908f01

File tree

6 files changed

+408
-323
lines changed

6 files changed

+408
-323
lines changed

src/Plugins/SimplnxCore/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ set(AlgorithmList
231231
ResampleImageGeom
232232
ResampleRectGridToImageGeom
233233
ReshapeDataArray
234+
RotateSampleRefFrame
234235
ScalarSegmentFeatures
235236
SharedFeatureFace
236237
Silhouette
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
#include "RotateSampleRefFrame.hpp"
2+
3+
#include "simplnx/Utilities/DataGroupUtilities.hpp"
4+
#include "simplnx/Utilities/ImageRotationUtilities.hpp"
5+
#include "simplnx/Utilities/ParallelAlgorithmUtilities.hpp"
6+
#include "simplnx/Utilities/ParallelTaskAlgorithm.hpp"
7+
8+
#include <Eigen/Dense>
9+
10+
using namespace nx::core;
11+
12+
namespace
13+
{
14+
using RotationRepresentationType = RotateSampleRefFrame::RotationRepresentation;
15+
16+
constexpr float32 k_Threshold = 0.01f;
17+
18+
const Eigen::Vector3f k_XAxis = Eigen::Vector3f::UnitX();
19+
const Eigen::Vector3f k_YAxis = Eigen::Vector3f::UnitY();
20+
const Eigen::Vector3f k_ZAxis = Eigen::Vector3f::UnitZ();
21+
22+
#if 0
23+
void DetermineMinMax(const Matrix3fR& rotationMatrix, const FloatVec3& spacing, usize col, usize row, usize plane, float32& xMin, float32& xMax, float32& yMin, float32& yMax, float32& zMin,
24+
float32& zMax)
25+
{
26+
Eigen::Vector3f coords(static_cast<float32>(col) * spacing[0], static_cast<float32>(row) * spacing[1], static_cast<float32>(plane) * spacing[2]);
27+
28+
Eigen::Vector3f newCoords = rotationMatrix * coords;
29+
30+
xMin = std::min(newCoords[0], xMin);
31+
xMax = std::max(newCoords[0], xMax);
32+
33+
yMin = std::min(newCoords[1], yMin);
34+
yMax = std::max(newCoords[1], yMax);
35+
36+
zMin = std::min(newCoords[2], zMin);
37+
zMax = std::max(newCoords[2], zMax);
38+
}
39+
40+
float32 CosBetweenVectors(const Eigen::Vector3f& vectorA, const Eigen::Vector3f& vectorB)
41+
{
42+
float32 normA = vectorA.norm();
43+
float32 normB = vectorB.norm();
44+
45+
if(normA == 0.0f || normB == 0.0f)
46+
{
47+
return 1.0f;
48+
}
49+
50+
return vectorA.dot(vectorB) / (normA * normB);
51+
}
52+
53+
float32 DetermineSpacing(const FloatVec3& spacing, const Eigen::Vector3f& axisNew)
54+
{
55+
float32 xAngle = std::abs(CosBetweenVectors(k_XAxis, axisNew));
56+
float32 yAngle = std::abs(CosBetweenVectors(k_YAxis, axisNew));
57+
float32 zAngle = std::abs(CosBetweenVectors(k_ZAxis, axisNew));
58+
59+
std::array<float32, 3> axes = {xAngle, yAngle, zAngle};
60+
61+
auto result = std::max_element(axes.cbegin(), axes.cend());
62+
63+
usize index = std::distance(axes.cbegin(), result);
64+
65+
return spacing[index];
66+
}
67+
68+
RotateArgs CreateRotateArgs(const ImageGeom& imageGeom, const Matrix3fR& rotationMatrix)
69+
{
70+
const SizeVec3 origDims = imageGeom.getDimensions();
71+
const FloatVec3 spacing = imageGeom.getSpacing();
72+
73+
float32 xMin = std::numeric_limits<float32>::max();
74+
float32 xMax = std::numeric_limits<float32>::min();
75+
float32 yMin = std::numeric_limits<float32>::max();
76+
float32 yMax = std::numeric_limits<float32>::min();
77+
float32 zMin = std::numeric_limits<float32>::max();
78+
float32 zMax = std::numeric_limits<float32>::min();
79+
80+
const std::vector<std::array<usize, 3>> coords{{0, 0, 0},
81+
{origDims[0] - 1, 0, 0},
82+
{0, origDims[1] - 1, 0},
83+
{origDims[0] - 1, origDims[1] - 1, 0},
84+
{0, 0, origDims[2] - 1},
85+
{origDims[0] - 1, 0, origDims[2] - 1},
86+
{0, origDims[1] - 1, origDims[2] - 1},
87+
{origDims[0] - 1, origDims[1] - 1, origDims[2] - 1}};
88+
89+
for(const auto& item : coords)
90+
{
91+
DetermineMinMax(rotationMatrix, spacing, item[0], item[1], item[2], xMin, xMax, yMin, yMax, zMin, zMax);
92+
}
93+
94+
Eigen::Vector3f xAxisNew = rotationMatrix * k_XAxis;
95+
Eigen::Vector3f yAxisNew = rotationMatrix * k_YAxis;
96+
Eigen::Vector3f zAxisNew = rotationMatrix * k_ZAxis;
97+
98+
float32 xResNew = DetermineSpacing(spacing, xAxisNew);
99+
float32 yResNew = DetermineSpacing(spacing, yAxisNew);
100+
float32 zResNew = DetermineSpacing(spacing, zAxisNew);
101+
102+
ImageGeom::MeshIndexType xpNew = static_cast<int64>(std::nearbyint((xMax - xMin) / xResNew) + 1);
103+
ImageGeom::MeshIndexType ypNew = static_cast<int64>(std::nearbyint((yMax - yMin) / yResNew) + 1);
104+
ImageGeom::MeshIndexType zpNew = static_cast<int64>(std::nearbyint((zMax - zMin) / zResNew) + 1);
105+
106+
RotateArgs params;
107+
108+
params.xp = static_cast<int64>(origDims[0]);
109+
params.xRes = spacing[0];
110+
params.yp = static_cast<int64>(origDims[1]);
111+
params.yRes = spacing[1];
112+
params.zp = static_cast<int64>(origDims[2]);
113+
params.zRes = spacing[2];
114+
115+
params.xpNew = static_cast<int64>(xpNew);
116+
params.xResNew = xResNew;
117+
params.xMinNew = xMin;
118+
params.ypNew = static_cast<int64>(ypNew);
119+
params.yResNew = yResNew;
120+
params.yMinNew = yMin;
121+
params.zpNew = static_cast<int64>(zpNew);
122+
params.zResNew = zResNew;
123+
params.zMinNew = zMin;
124+
125+
return params;
126+
}
127+
128+
template <typename K>
129+
bool closeEnough(const K& a, const K& b, const K& epsilon = std::numeric_limits<K>::epsilon())
130+
{
131+
return (epsilon > fabs(a - b));
132+
}
133+
134+
constexpr RotationRepresentationType CastIndexToRotationRepresentation(uint64 index)
135+
{
136+
switch(index)
137+
{
138+
case to_underlying(RotationRepresentationType::AxisAngle): {
139+
return RotationRepresentationType::AxisAngle;
140+
}
141+
case to_underlying(RotationRepresentationType::RotationMatrix): {
142+
return RotationRepresentationType::RotationMatrix;
143+
}
144+
default: {
145+
throw std::runtime_error(fmt::format("RotateSampleRefFrameFilter: Failed to cast index {} to RotationRepresentation", index));
146+
}
147+
}
148+
}
149+
150+
Result<Matrix3fR> ConvertAxisAngleToRotationMatrix(const std::vector<float32>& pRotationValue)
151+
{
152+
Matrix3fR transformationMatrix;
153+
154+
// Convert Degrees to Radians for the last element
155+
const float rotAngle = pRotationValue[3] * Constants::k_PiOver180F;
156+
// Ensure the axis part is normalized
157+
FloatVec3 normalizedAxis(pRotationValue[0], pRotationValue[1], pRotationValue[2]);
158+
MatrixMath::Normalize3x1<float32>(normalizedAxis.data());
159+
160+
const float cosTheta = cos(rotAngle);
161+
const float oneMinusCosTheta = 1 - cosTheta;
162+
const float sinTheta = sin(rotAngle);
163+
const float l = normalizedAxis[0];
164+
const float m = normalizedAxis[1];
165+
const float n = normalizedAxis[2];
166+
167+
// First Row:
168+
transformationMatrix(0, 0) = l * l * (oneMinusCosTheta) + cosTheta;
169+
transformationMatrix(0, 1) = m * l * (oneMinusCosTheta) - (n * sinTheta);
170+
transformationMatrix(0, 2) = n * l * (oneMinusCosTheta) + (m * sinTheta);
171+
172+
// Second Row:
173+
transformationMatrix(1, 0) = l * m * (oneMinusCosTheta) + (n * sinTheta);
174+
transformationMatrix(1, 1) = m * m * (oneMinusCosTheta) + cosTheta;
175+
transformationMatrix(1, 2) = n * m * (oneMinusCosTheta) - (l * sinTheta);
176+
177+
// Third Row:
178+
transformationMatrix(2, 0) = l * n * (oneMinusCosTheta) - (m * sinTheta);
179+
transformationMatrix(2, 1) = m * n * (oneMinusCosTheta) + (l * sinTheta);
180+
transformationMatrix(2, 2) = n * n * (oneMinusCosTheta) + cosTheta;
181+
182+
return {transformationMatrix};
183+
}
184+
185+
Result<Matrix3fR> ConvertRotationTableToRotationMatrix(const std::vector<std::vector<float64>>& rotationMatrixTable)
186+
{
187+
if(rotationMatrixTable.size() != 3)
188+
{
189+
return MakeErrorResult<Matrix3fR>(-45004, "Rotation Matrix must be 3 x 3");
190+
}
191+
192+
for(const auto& row : rotationMatrixTable)
193+
{
194+
if(row.size() != 3)
195+
{
196+
return MakeErrorResult<Matrix3fR>(-45005, "Rotation Matrix must be 3 x 3");
197+
}
198+
}
199+
Matrix3fR rotationMatrix;
200+
const usize numTableRows = rotationMatrixTable.size();
201+
const usize numTableCols = rotationMatrixTable[0].size();
202+
for(size_t rowIndex = 0; rowIndex < numTableRows; rowIndex++)
203+
{
204+
std::vector<double> row = rotationMatrixTable[rowIndex];
205+
for(size_t colIndex = 0; colIndex < numTableCols; colIndex++)
206+
{
207+
rotationMatrix(rowIndex, colIndex) = static_cast<float>(row[colIndex]);
208+
}
209+
}
210+
211+
float32 determinant = rotationMatrix.determinant();
212+
213+
if(!closeEnough(determinant, 1.0f, k_Threshold))
214+
{
215+
return MakeErrorResult<Matrix3fR>(-45006, fmt::format("Rotation Matrix must have a determinant of 1 (is {})", determinant));
216+
}
217+
218+
Matrix3fR transpose = rotationMatrix.transpose();
219+
Matrix3fR inverse = rotationMatrix.inverse();
220+
221+
if(!transpose.isApprox(inverse, k_Threshold))
222+
{
223+
return MakeErrorResult<Matrix3fR>(-45007, "Rotation Matrix's inverse and transpose must be equal");
224+
}
225+
226+
return {rotationMatrix};
227+
}
228+
#endif
229+
230+
// Result<Matrix3fR> ComputeRotationMatrix(const Arguments& args)
231+
//{
232+
// auto rotationRepresentationIndex = args.value<uint64>(RotateSampleRefFrameFilter::k_RotationRepresentation_Key);
233+
//
234+
// RotationRepresentationType rotationRepresentation = CastIndexToRotationRepresentation(rotationRepresentationIndex);
235+
//
236+
// switch(rotationRepresentation)
237+
// {
238+
// case RotationRepresentationType::AxisAngle: {
239+
// auto pRotationValue = args.value<std::vector<float32>>(RotateSampleRefFrameFilter::k_RotationAxisAngle_Key);
240+
// return ConvertAxisAngleToRotationMatrix(pRotationValue);
241+
// }
242+
// case RotationRepresentationType::RotationMatrix: {
243+
// auto rotationMatrixTable = args.value<DynamicTableParameter::ValueType>(RotateSampleRefFrameFilter::k_RotationMatrix_Key);
244+
// return ConvertRotationTableToRotationMatrix(rotationMatrixTable);
245+
// }
246+
// }
247+
// }
248+
249+
} // namespace
250+
251+
// -----------------------------------------------------------------------------
252+
RotateSampleRefFrame::RotateSampleRefFrame(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, RotateSampleRefFrameInputValues* inputValues)
253+
: m_DataStructure(dataStructure)
254+
, m_InputValues(inputValues)
255+
, m_ShouldCancel(shouldCancel)
256+
, m_MessageHandler(mesgHandler)
257+
{
258+
}
259+
260+
// -----------------------------------------------------------------------------
261+
RotateSampleRefFrame::~RotateSampleRefFrame() noexcept = default;
262+
263+
// -----------------------------------------------------------------------------
264+
void RotateSampleRefFrame::updateProgress(const std::string& message)
265+
{
266+
m_MessageHandler(IFilter::Message::Type::Info, message);
267+
}
268+
269+
// -----------------------------------------------------------------------------
270+
const std::atomic_bool& RotateSampleRefFrame::getCancel()
271+
{
272+
return m_ShouldCancel;
273+
}
274+
275+
// -----------------------------------------------------------------------------
276+
Result<> RotateSampleRefFrame::operator()()
277+
{
278+
auto& srcImageGeom = m_DataStructure.getDataRefAs<ImageGeom>(m_InputValues->SourceGeometryPath);
279+
auto& destImageGeom = m_DataStructure.getDataRefAs<ImageGeom>(m_InputValues->DestGeometryPath);
280+
281+
ImageRotationUtilities::Matrix4fR rotationMatrix;
282+
283+
switch(static_cast<RotationRepresentationType>(m_InputValues->RotationRepresentationIndex))
284+
{
285+
case RotationRepresentationType::AxisAngle: {
286+
rotationMatrix = ImageRotationUtilities::GenerateRotationTransformationMatrix(m_InputValues->RotationAxisAngle);
287+
break;
288+
}
289+
case RotationRepresentationType::RotationMatrix: {
290+
rotationMatrix = ImageRotationUtilities::GenerateManualTransformationMatrix(m_InputValues->RotationMatrixTable);
291+
break;
292+
}
293+
}
294+
295+
ImageRotationUtilities::RotateArgs rotateArgs = ImageRotationUtilities::CreateRotationArgs(srcImageGeom, rotationMatrix);
296+
297+
auto selectedCellDataChildren = GetAllChildArrayDataPaths(m_DataStructure, srcImageGeom.getCellDataPath());
298+
auto selectedCellArrays = selectedCellDataChildren.has_value() ? selectedCellDataChildren.value() : std::vector<DataPath>{};
299+
300+
ImageRotationUtilities::FilterProgressCallback filterProgressCallback(m_MessageHandler, m_ShouldCancel);
301+
302+
// The actual rotating of the dataStructure arrays is done in parallel where parallel here
303+
// refers to the cropping of each DataArray being done on a separate thread.
304+
ParallelTaskAlgorithm taskRunner;
305+
taskRunner.setParallelizationEnabled(true);
306+
const DataPath srcCelLDataAMPath = srcImageGeom.getCellDataPath();
307+
const auto& srcCellDataAM = srcImageGeom.getCellDataRef();
308+
309+
const DataPath destCellDataAMPath = destImageGeom.getCellDataPath();
310+
311+
for(const auto& [dataId, srcDataObject] : srcCellDataAM)
312+
{
313+
if(m_ShouldCancel)
314+
{
315+
return {};
316+
}
317+
318+
const auto* srcDataArray = m_DataStructure.getDataAs<IDataArray>(srcCelLDataAMPath.createChildPath(srcDataObject->getName()));
319+
auto* destDataArray = m_DataStructure.getDataAs<IDataArray>(destCellDataAMPath.createChildPath(srcDataObject->getName()));
320+
m_MessageHandler(fmt::format("Rotating Volume || Copying Data Array {}", srcDataObject->getName()));
321+
322+
ExecuteParallelFunction<ImageRotationUtilities::RotateImageGeometryWithNearestNeighbor>(srcDataArray->getDataType(), taskRunner, srcDataArray, destDataArray, rotateArgs, rotationMatrix,
323+
m_InputValues->SliceBySlice, &filterProgressCallback);
324+
}
325+
326+
taskRunner.wait(); // This will spill over if the number of DataArrays to process does not divide evenly by the number of threads.
327+
328+
return {};
329+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#pragma once
2+
3+
#include "SimplnxCore/SimplnxCore_export.hpp"
4+
5+
#include "simplnx/DataStructure/DataPath.hpp"
6+
#include "simplnx/DataStructure/DataStructure.hpp"
7+
#include "simplnx/Filter/IFilter.hpp"
8+
#include "simplnx/Parameters/ChoicesParameter.hpp"
9+
#include "simplnx/Parameters/DynamicTableParameter.hpp"
10+
#include "simplnx/Parameters/VectorParameter.hpp"
11+
12+
namespace nx::core
13+
{
14+
15+
struct SIMPLNXCORE_EXPORT RotateSampleRefFrameInputValues
16+
{
17+
bool SliceBySlice;
18+
ChoicesParameter::ValueType RotationRepresentationIndex;
19+
DataPath SourceGeometryPath;
20+
DataPath DestGeometryPath;
21+
VectorFloat32Parameter::ValueType RotationAxisAngle;
22+
DynamicTableParameter::ValueType RotationMatrixTable;
23+
};
24+
25+
/**
26+
* @class
27+
*/
28+
class SIMPLNXCORE_EXPORT RotateSampleRefFrame
29+
{
30+
public:
31+
RotateSampleRefFrame(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, RotateSampleRefFrameInputValues* inputValues);
32+
~RotateSampleRefFrame() noexcept;
33+
34+
RotateSampleRefFrame(const RotateSampleRefFrame&) = delete;
35+
RotateSampleRefFrame(RotateSampleRefFrame&&) noexcept = delete;
36+
RotateSampleRefFrame& operator=(const RotateSampleRefFrame&) = delete;
37+
RotateSampleRefFrame& operator=(RotateSampleRefFrame&&) noexcept = delete;
38+
39+
enum class RotationRepresentation : uint64
40+
{
41+
AxisAngle = 0,
42+
RotationMatrix = 1
43+
};
44+
45+
Result<> operator()();
46+
void updateProgress(const std::string& message);
47+
const std::atomic_bool& getCancel();
48+
49+
private:
50+
DataStructure& m_DataStructure;
51+
const RotateSampleRefFrameInputValues* m_InputValues = nullptr;
52+
const std::atomic_bool& m_ShouldCancel;
53+
const IFilter::MessageHandler& m_MessageHandler;
54+
};
55+
56+
} // namespace nx::core

0 commit comments

Comments
 (0)