Skip to content

Commit 0f64fd5

Browse files
cookpahjmjohnson
authored andcommitted
ENH: Test showing Jacobian differs under different voxel ordering
1 parent 0907661 commit 0f64fd5

File tree

1 file changed

+109
-1
lines changed

1 file changed

+109
-1
lines changed

Modules/Filtering/DisplacementField/test/itkDisplacementFieldJacobianDeterminantFilterTest.cxx

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
#include <iostream>
2020
#include "itkDisplacementFieldJacobianDeterminantFilter.h"
21+
#include "itkFlipImageFilter.h"
2122
#include "itkNullImageToImageFilterDriver.hxx"
23+
#include "itkPermuteAxesImageFilter.h"
2224
#include "itkStdStreamStateSave.h"
2325
#include "itkTestingMacros.h"
2426

@@ -118,7 +120,113 @@ TestDisplacementJacobianDeterminantValue()
118120
}
119121
else
120122
{
121-
std::cout << "Test passed." << std::endl;
123+
std::cout << "Determinant value test passed" << std::endl;
124+
}
125+
126+
// Now test that the determinant is consistent if the voxel space is different, but the physical space is the same.
127+
128+
// Define physical point to test
129+
itk::Point<double, 2> physPt;
130+
dispacementfield->TransformIndexToPhysicalPoint(index, physPt);
131+
132+
// Use this function to get the determinant at a specified physical point (it will be a different index between tests)
133+
auto GetDeterminantAtPoint = [](FieldType::Pointer image, const itk::Point<double, 2> & pt) -> float {
134+
auto filter = FilterType::New();
135+
filter->SetInput(image);
136+
filter->SetUseImageSpacing(true);
137+
filter->Update();
138+
139+
itk::Index<2> mappedIdx = image->TransformPhysicalPointToIndex(pt);
140+
141+
return filter->GetOutput()->GetPixel(mappedIdx);
142+
};
143+
144+
const float detOriginal = GetDeterminantAtPoint(dispacementfield, physPt);
145+
146+
// Check that this is the same as above, just to be sure the function above works
147+
if (itk::Math::abs(detOriginal - expectedJacobianDeterminant) > epsilon)
148+
{
149+
std::cerr.precision(static_cast<int>(itk::Math::abs(std::log10(epsilon))));
150+
std::cerr << "Test failed " << std::endl;
151+
std::cerr << "Error in pixel value at physical point " << physPt << std::endl;
152+
std::cerr << "Expected value " << detOriginal << std::endl;
153+
std::cerr << " differs from " << expectedJacobianDeterminant;
154+
std::cerr << " by more than " << epsilon << std::endl;
155+
testPassed = false;
156+
}
157+
158+
using PermuteFilterType = itk::PermuteAxesImageFilter<FieldType>;
159+
auto permute = PermuteFilterType::New();
160+
161+
PermuteFilterType::PermuteOrderArrayType order;
162+
order[0] = 1;
163+
order[1] = 0; // swap X <-> Y
164+
permute->SetOrder(order);
165+
permute->SetInput(dispacementfield);
166+
permute->Update();
167+
168+
auto dispacementfieldPermuted = permute->GetOutput();
169+
170+
// Adjust direction to match permutation
171+
itk::Matrix<double, 2, 2> origDir = dispacementfield->GetDirection();
172+
itk::Matrix<double, 2, 2> newDirPermute;
173+
newDirPermute[0][0] = origDir[1][0];
174+
newDirPermute[0][1] = origDir[1][1];
175+
newDirPermute[1][0] = origDir[0][0];
176+
newDirPermute[1][1] = origDir[0][1];
177+
178+
dispacementfieldPermuted->SetDirection(newDirPermute);
179+
dispacementfieldPermuted->SetOrigin(dispacementfield->GetOrigin());
180+
dispacementfieldPermuted->SetSpacing(dispacementfield->GetSpacing());
181+
182+
const float detPermuted = GetDeterminantAtPoint(dispacementfieldPermuted, physPt);
183+
184+
using FlipFilterType = itk::FlipImageFilter<FieldType>;
185+
auto flip = FlipFilterType::New();
186+
187+
FlipFilterType::FlipAxesArrayType flipAxes;
188+
flipAxes[0] = true; // Flip X
189+
flipAxes[1] = false; // Keep Y
190+
flip->SetFlipAxes(flipAxes);
191+
flip->SetInput(dispacementfield);
192+
flip->FlipAboutOriginOff();
193+
flip->Update();
194+
195+
auto dispacementfieldFlip = flip->GetOutput();
196+
197+
// Adjust direction to compensate flip
198+
itk::Matrix<double, 2, 2> newDirFlip = dispacementfield->GetDirection();
199+
newDirFlip[0][0] *= -1.0;
200+
newDirFlip[0][1] *= -1.0;
201+
202+
dispacementfieldFlip->SetDirection(newDirFlip);
203+
dispacementfieldFlip->SetOrigin(dispacementfield->GetOrigin());
204+
dispacementfieldFlip->SetSpacing(dispacementfield->GetSpacing());
205+
206+
const float detFlipped = GetDeterminantAtPoint(dispacementfieldFlip, physPt);
207+
208+
std::cout << "Determinant at point " << physPt << ":" << std::endl;
209+
std::cout << " Original: " << detOriginal << std::endl;
210+
std::cout << " Permuted: " << detPermuted << std::endl;
211+
std::cout << " Flipped: " << detFlipped << std::endl;
212+
213+
constexpr double delta = 1e-13;
214+
215+
if (itk::Math::abs(detPermuted - detOriginal) > delta)
216+
{
217+
std::cerr << "Test failed: determinant differs after Permute." << std::endl;
218+
testPassed = false;
219+
}
220+
221+
if (itk::Math::abs(detFlipped - detOriginal) > delta)
222+
{
223+
std::cerr << "Test failed: determinant differs after Flip." << std::endl;
224+
testPassed = false;
225+
}
226+
227+
if (testPassed)
228+
{
229+
std::cout << "Test passed: determinant consistent after Permute and Flip." << std::endl;
122230
}
123231

124232
return testPassed;

0 commit comments

Comments
 (0)