10
10
#include " simplnx/Filter/Actions/CreateImageGeometryAction.hpp"
11
11
#include " simplnx/Filter/Actions/DeleteDataAction.hpp"
12
12
#include " simplnx/Filter/Actions/RenameDataAction.hpp"
13
+ #include " simplnx/Filter/Actions/UpdateImageGeomAction.hpp"
13
14
#include " simplnx/Parameters/BoolParameter.hpp"
14
15
#include " simplnx/Parameters/ChoicesParameter.hpp"
15
16
#include " simplnx/Parameters/DataGroupCreationParameter.hpp"
23
24
#include " simplnx/Utilities/ImageRotationUtilities.hpp"
24
25
#include " simplnx/Utilities/ParallelAlgorithmUtilities.hpp"
25
26
#include " simplnx/Utilities/ParallelTaskAlgorithm.hpp"
27
+ #include " simplnx/Utilities/SIMPLConversion.hpp"
26
28
#include " simplnx/Utilities/StringUtilities.hpp"
27
29
28
30
#include < Eigen/Dense>
29
31
30
32
#include < fmt/core.h>
31
33
32
34
#include < algorithm>
33
- #include < array>
34
-
35
- #include " simplnx/Utilities/SIMPLConversion.hpp"
36
-
37
- #include < stdexcept>
38
35
39
36
using namespace nx ::core;
40
37
using namespace nx ::core::ImageRotationUtilities;
@@ -45,238 +42,6 @@ const std::string k_TempGeometryName = ".rotated_image_geometry";
45
42
46
43
using RotationRepresentationType = RotateSampleRefFrameFilter::RotationRepresentation;
47
44
48
- constexpr float32 k_Threshold = 0 .01f ;
49
-
50
- const Eigen::Vector3f k_XAxis = Eigen::Vector3f::UnitX();
51
- const Eigen::Vector3f k_YAxis = Eigen::Vector3f::UnitY();
52
- const Eigen::Vector3f k_ZAxis = Eigen::Vector3f::UnitZ();
53
- #if 0
54
- void DetermineMinMax(const Matrix3fR& rotationMatrix, const FloatVec3& spacing, usize col, usize row, usize plane, float32& xMin, float32& xMax, float32& yMin, float32& yMax, float32& zMin,
55
- float32& zMax)
56
- {
57
- Eigen::Vector3f coords(static_cast<float32>(col) * spacing[0], static_cast<float32>(row) * spacing[1], static_cast<float32>(plane) * spacing[2]);
58
-
59
- Eigen::Vector3f newCoords = rotationMatrix * coords;
60
-
61
- xMin = std::min(newCoords[0], xMin);
62
- xMax = std::max(newCoords[0], xMax);
63
-
64
- yMin = std::min(newCoords[1], yMin);
65
- yMax = std::max(newCoords[1], yMax);
66
-
67
- zMin = std::min(newCoords[2], zMin);
68
- zMax = std::max(newCoords[2], zMax);
69
- }
70
-
71
- float32 CosBetweenVectors(const Eigen::Vector3f& vectorA, const Eigen::Vector3f& vectorB)
72
- {
73
- float32 normA = vectorA.norm();
74
- float32 normB = vectorB.norm();
75
-
76
- if(normA == 0.0f || normB == 0.0f)
77
- {
78
- return 1.0f;
79
- }
80
-
81
- return vectorA.dot(vectorB) / (normA * normB);
82
- }
83
-
84
- float32 DetermineSpacing(const FloatVec3& spacing, const Eigen::Vector3f& axisNew)
85
- {
86
- float32 xAngle = std::abs(CosBetweenVectors(k_XAxis, axisNew));
87
- float32 yAngle = std::abs(CosBetweenVectors(k_YAxis, axisNew));
88
- float32 zAngle = std::abs(CosBetweenVectors(k_ZAxis, axisNew));
89
-
90
- std::array<float32, 3> axes = {xAngle, yAngle, zAngle};
91
-
92
- auto result = std::max_element(axes.cbegin(), axes.cend());
93
-
94
- usize index = std::distance(axes.cbegin(), result);
95
-
96
- return spacing[index];
97
- }
98
-
99
- RotateArgs CreateRotateArgs(const ImageGeom& imageGeom, const Matrix3fR& rotationMatrix)
100
- {
101
- const SizeVec3 origDims = imageGeom.getDimensions();
102
- const FloatVec3 spacing = imageGeom.getSpacing();
103
-
104
- float32 xMin = std::numeric_limits<float32>::max();
105
- float32 xMax = std::numeric_limits<float32>::min();
106
- float32 yMin = std::numeric_limits<float32>::max();
107
- float32 yMax = std::numeric_limits<float32>::min();
108
- float32 zMin = std::numeric_limits<float32>::max();
109
- float32 zMax = std::numeric_limits<float32>::min();
110
-
111
- const std::vector<std::array<usize, 3>> coords{{0, 0, 0},
112
- {origDims[0] - 1, 0, 0},
113
- {0, origDims[1] - 1, 0},
114
- {origDims[0] - 1, origDims[1] - 1, 0},
115
- {0, 0, origDims[2] - 1},
116
- {origDims[0] - 1, 0, origDims[2] - 1},
117
- {0, origDims[1] - 1, origDims[2] - 1},
118
- {origDims[0] - 1, origDims[1] - 1, origDims[2] - 1}};
119
-
120
- for(const auto& item : coords)
121
- {
122
- DetermineMinMax(rotationMatrix, spacing, item[0], item[1], item[2], xMin, xMax, yMin, yMax, zMin, zMax);
123
- }
124
-
125
- Eigen::Vector3f xAxisNew = rotationMatrix * k_XAxis;
126
- Eigen::Vector3f yAxisNew = rotationMatrix * k_YAxis;
127
- Eigen::Vector3f zAxisNew = rotationMatrix * k_ZAxis;
128
-
129
- float32 xResNew = DetermineSpacing(spacing, xAxisNew);
130
- float32 yResNew = DetermineSpacing(spacing, yAxisNew);
131
- float32 zResNew = DetermineSpacing(spacing, zAxisNew);
132
-
133
- ImageGeom::MeshIndexType xpNew = static_cast<int64>(std::nearbyint((xMax - xMin) / xResNew) + 1);
134
- ImageGeom::MeshIndexType ypNew = static_cast<int64>(std::nearbyint((yMax - yMin) / yResNew) + 1);
135
- ImageGeom::MeshIndexType zpNew = static_cast<int64>(std::nearbyint((zMax - zMin) / zResNew) + 1);
136
-
137
- RotateArgs params;
138
-
139
- params.xp = static_cast<int64>(origDims[0]);
140
- params.xRes = spacing[0];
141
- params.yp = static_cast<int64>(origDims[1]);
142
- params.yRes = spacing[1];
143
- params.zp = static_cast<int64>(origDims[2]);
144
- params.zRes = spacing[2];
145
-
146
- params.xpNew = static_cast<int64>(xpNew);
147
- params.xResNew = xResNew;
148
- params.xMinNew = xMin;
149
- params.ypNew = static_cast<int64>(ypNew);
150
- params.yResNew = yResNew;
151
- params.yMinNew = yMin;
152
- params.zpNew = static_cast<int64>(zpNew);
153
- params.zResNew = zResNew;
154
- params.zMinNew = zMin;
155
-
156
- return params;
157
- }
158
-
159
- template <typename K>
160
- bool closeEnough(const K& a, const K& b, const K& epsilon = std::numeric_limits<K>::epsilon())
161
- {
162
- return (epsilon > fabs(a - b));
163
- }
164
-
165
- constexpr RotationRepresentationType CastIndexToRotationRepresentation(uint64 index)
166
- {
167
- switch(index)
168
- {
169
- case to_underlying(RotationRepresentationType::AxisAngle): {
170
- return RotationRepresentationType::AxisAngle;
171
- }
172
- case to_underlying(RotationRepresentationType::RotationMatrix): {
173
- return RotationRepresentationType::RotationMatrix;
174
- }
175
- default: {
176
- throw std::runtime_error(fmt::format("RotateSampleRefFrameFilter: Failed to cast index {} to RotationRepresentation", index));
177
- }
178
- }
179
- }
180
-
181
- Result<Matrix3fR> ConvertAxisAngleToRotationMatrix(const std::vector<float32>& pRotationValue)
182
- {
183
- Matrix3fR transformationMatrix;
184
-
185
- // Convert Degrees to Radians for the last element
186
- const float rotAngle = pRotationValue[3] * Constants::k_PiOver180F;
187
- // Ensure the axis part is normalized
188
- FloatVec3 normalizedAxis(pRotationValue[0], pRotationValue[1], pRotationValue[2]);
189
- MatrixMath::Normalize3x1<float32>(normalizedAxis.data());
190
-
191
- const float cosTheta = cos(rotAngle);
192
- const float oneMinusCosTheta = 1 - cosTheta;
193
- const float sinTheta = sin(rotAngle);
194
- const float l = normalizedAxis[0];
195
- const float m = normalizedAxis[1];
196
- const float n = normalizedAxis[2];
197
-
198
- // First Row:
199
- transformationMatrix(0, 0) = l * l * (oneMinusCosTheta) + cosTheta;
200
- transformationMatrix(0, 1) = m * l * (oneMinusCosTheta) - (n * sinTheta);
201
- transformationMatrix(0, 2) = n * l * (oneMinusCosTheta) + (m * sinTheta);
202
-
203
- // Second Row:
204
- transformationMatrix(1, 0) = l * m * (oneMinusCosTheta) + (n * sinTheta);
205
- transformationMatrix(1, 1) = m * m * (oneMinusCosTheta) + cosTheta;
206
- transformationMatrix(1, 2) = n * m * (oneMinusCosTheta) - (l * sinTheta);
207
-
208
- // Third Row:
209
- transformationMatrix(2, 0) = l * n * (oneMinusCosTheta) - (m * sinTheta);
210
- transformationMatrix(2, 1) = m * n * (oneMinusCosTheta) + (l * sinTheta);
211
- transformationMatrix(2, 2) = n * n * (oneMinusCosTheta) + cosTheta;
212
-
213
- return {transformationMatrix};
214
- }
215
-
216
- Result<Matrix3fR> ConvertRotationTableToRotationMatrix(const std::vector<std::vector<float64>>& rotationMatrixTable)
217
- {
218
- if(rotationMatrixTable.size() != 3)
219
- {
220
- return MakeErrorResult<Matrix3fR>(-45004, "Rotation Matrix must be 3 x 3");
221
- }
222
-
223
- for(const auto& row : rotationMatrixTable)
224
- {
225
- if(row.size() != 3)
226
- {
227
- return MakeErrorResult<Matrix3fR>(-45005, "Rotation Matrix must be 3 x 3");
228
- }
229
- }
230
- Matrix3fR rotationMatrix;
231
- const usize numTableRows = rotationMatrixTable.size();
232
- const usize numTableCols = rotationMatrixTable[0].size();
233
- for(size_t rowIndex = 0; rowIndex < numTableRows; rowIndex++)
234
- {
235
- std::vector<double> row = rotationMatrixTable[rowIndex];
236
- for(size_t colIndex = 0; colIndex < numTableCols; colIndex++)
237
- {
238
- rotationMatrix(rowIndex, colIndex) = static_cast<float>(row[colIndex]);
239
- }
240
- }
241
-
242
- float32 determinant = rotationMatrix.determinant();
243
-
244
- if(!closeEnough(determinant, 1.0f, k_Threshold))
245
- {
246
- return MakeErrorResult<Matrix3fR>(-45006, fmt::format("Rotation Matrix must have a determinant of 1 (is {})", determinant));
247
- }
248
-
249
- Matrix3fR transpose = rotationMatrix.transpose();
250
- Matrix3fR inverse = rotationMatrix.inverse();
251
-
252
- if(!transpose.isApprox(inverse, k_Threshold))
253
- {
254
- return MakeErrorResult<Matrix3fR>(-45007, "Rotation Matrix's inverse and transpose must be equal");
255
- }
256
-
257
- return {rotationMatrix};
258
- }
259
- #endif
260
-
261
- // Result<Matrix3fR> ComputeRotationMatrix(const Arguments& args)
262
- // {
263
- // auto rotationRepresentationIndex = args.value<uint64>(RotateSampleRefFrameFilter::k_RotationRepresentation_Key);
264
- //
265
- // RotationRepresentationType rotationRepresentation = CastIndexToRotationRepresentation(rotationRepresentationIndex);
266
- //
267
- // switch(rotationRepresentation)
268
- // {
269
- // case RotationRepresentationType::AxisAngle: {
270
- // auto pRotationValue = args.value<std::vector<float32>>(RotateSampleRefFrameFilter::k_RotationAxisAngle_Key);
271
- // return ConvertAxisAngleToRotationMatrix(pRotationValue);
272
- // }
273
- // case RotationRepresentationType::RotationMatrix: {
274
- // auto rotationMatrixTable = args.value<DynamicTableParameter::ValueType>(RotateSampleRefFrameFilter::k_RotationMatrix_Key);
275
- // return ConvertRotationTableToRotationMatrix(rotationMatrixTable);
276
- // }
277
- // }
278
- // }
279
-
280
45
} // namespace
281
46
282
47
namespace nx ::core
@@ -324,6 +89,8 @@ Parameters RotateSampleRefFrameFilter::parameters() const
324
89
params.insert (std::make_unique<VectorFloat32Parameter>(k_RotationAxisAngle_Key, " Rotation Axis-Angle [<ijk>w]" , " Axis-Angle in sample reference frame to rotate about." ,
325
90
VectorFloat32Parameter::ValueType{0 .0f , 0 .0f , 1 .0f , 90 .0F }, std::vector<std::string>{" i" , " j" , " k" , " w (Deg)" }));
326
91
params.insertLinkableParameter (std::make_unique<BoolParameter>(k_RemoveOriginalGeometry_Key, " Perform In-Place Rotation" , " Performs the rotation in-place for the given Image Geometry" , true ));
92
+ params.insertLinkableParameter (
93
+ std::make_unique<BoolParameter>(k_KeepInputGeometryOrigin_Key, " Keep Input Geometry's Origin" , " The input geometry's origin is kept instead of the origin resulting from the transform" , false ));
327
94
328
95
DynamicTableInfo tableInfo;
329
96
tableInfo.setColsInfo (DynamicTableInfo::StaticVectorInfo ({" 1" , " 2" , " 3" , " 4" }));
@@ -362,10 +129,10 @@ IFilter::UniquePointer RotateSampleRefFrameFilter::clone() const
362
129
IFilter::PreflightResult RotateSampleRefFrameFilter::preflightImpl (const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler,
363
130
const std::atomic_bool& shouldCancel, const ExecutionContext& executionContext) const
364
131
{
365
-
366
132
auto srcImagePath = filterArgs.value <DataPath>(k_SelectedImageGeometryPath_Key);
367
133
auto destImagePath = filterArgs.value <DataPath>(k_CreatedImageGeometryPath_Key);
368
134
auto pRemoveOriginalGeometry = filterArgs.value <bool >(k_RemoveOriginalGeometry_Key);
135
+ auto keepInputGeometryOrigin = filterArgs.value <bool >(k_KeepInputGeometryOrigin_Key);
369
136
370
137
nx::core::Result<OutputActions> resultOutputActions;
371
138
@@ -394,9 +161,12 @@ IFilter::PreflightResult RotateSampleRefFrameFilter::preflightImpl(const DataStr
394
161
const std::vector<usize> dims = {static_cast <usize>(rotateArgs.xpNew ), static_cast <usize>(rotateArgs.ypNew ), static_cast <usize>(rotateArgs.zpNew )};
395
162
const std::vector<float32> spacing = {rotateArgs.xResNew , rotateArgs.yResNew , rotateArgs.zResNew };
396
163
auto origin = selectedImageGeom.getOrigin ().toContainer <std::vector<float32>>();
397
- origin[0 ] += rotateArgs.xMinNew ;
398
- origin[1 ] += rotateArgs.yMinNew ;
399
- origin[2 ] += rotateArgs.zMinNew ;
164
+ if (!keepInputGeometryOrigin)
165
+ {
166
+ origin[0 ] += rotateArgs.xMinNew ;
167
+ origin[1 ] += rotateArgs.yMinNew ;
168
+ origin[2 ] += rotateArgs.zMinNew ;
169
+ }
400
170
401
171
std::vector<usize> dataArrayShape = {dims[2 ], dims[1 ], dims[0 ]}; // The DataArray shape goes slowest to fastest (ZYX)
402
172
@@ -487,6 +257,7 @@ IFilter::PreflightResult RotateSampleRefFrameFilter::preflightImpl(const DataStr
487
257
{
488
258
resultOutputActions.value ().appendDeferredAction (std::make_unique<RenameDataAction>(destImagePath, srcImagePath.getTargetName ()));
489
259
}
260
+
490
261
// Return both the resultOutputActions and the preflightUpdatedValues via std::move()
491
262
return {std::move (resultOutputActions), std::move (preflightUpdatedValues)};
492
263
}
@@ -498,9 +269,10 @@ Result<> RotateSampleRefFrameFilter::executeImpl(DataStructure& dataStructure, c
498
269
auto destImagePath = filterArgs.value <DataPath>(k_CreatedImageGeometryPath_Key);
499
270
auto sliceBySlice = filterArgs.value <bool >(k_RotateSliceBySlice_Key);
500
271
auto removeOriginalGeometry = filterArgs.value <bool >(k_RemoveOriginalGeometry_Key);
272
+ auto keepInputGeometryOrigin = filterArgs.value <bool >(k_KeepInputGeometryOrigin_Key);
501
273
502
274
auto & srcImageGeom = dataStructure.getDataRefAs <ImageGeom>(srcImagePath);
503
-
275
+ auto sourceImageGeomorigin = srcImageGeom. getOrigin ();
504
276
if (removeOriginalGeometry)
505
277
{
506
278
auto tempPathVector = srcImagePath.getPathVector ();
@@ -561,6 +333,10 @@ Result<> RotateSampleRefFrameFilter::executeImpl(DataStructure& dataStructure, c
561
333
562
334
taskRunner.wait (); // This will spill over if the number of DataArrays to process does not divide evenly by the number of threads.
563
335
336
+ if (keepInputGeometryOrigin)
337
+ {
338
+ destImageGeom.setOrigin (srcImageGeom.getOrigin ());
339
+ }
564
340
return {};
565
341
}
566
342
0 commit comments