Skip to content

Commit 9c9c639

Browse files
cookpahjmjohnson
authored andcommitted
BUG: Jacobians not in physical space
The gradient is calculated in a neighborhood defined in index space. DisplacementFieldTransform accounts for this by multiplying rows of the gradient by the direction matrix. DisplacementFieldJacobianDeterminantFilter did not, hence its determinants were different for the same physical transformation under different voxel index ordering. DisplacementFieldJacobianDeterminantFilter is now fixed to use the direction matrix. The test is updated to check that the determinant for a given displacement field is consistent at the same point in physical space, regardless of how the index space is oriented. More details in issue #5358.
1 parent 8675ce7 commit 9c9c639

File tree

3 files changed

+51
-13
lines changed

3 files changed

+51
-13
lines changed

Modules/Filtering/DisplacementField/include/itkDisplacementFieldJacobianDeterminantFilter.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ class ITK_TEMPLATE_EXPORT DisplacementFieldJacobianDeterminantFilter
151151
using RealVectorType = Vector<TRealType, InputPixelType::Dimension>;
152152
using RealVectorImageType = Image<RealVectorType, TInputImage::ImageDimension>;
153153

154+
/** Matrix type used to store the input direction matrix and the gradient matrix from
155+
which the Jacobian determinant is computed. */
156+
using InternalMatrixType = vnl_matrix_fixed<TRealType, ImageDimension, ImageDimension>;
157+
154158
/** Type of the iterator that will be used to move through the image. Also
155159
the type which will be passed to the evaluate function */
156160
using ConstNeighborhoodIteratorType = ConstNeighborhoodIterator<RealVectorImageType>;
@@ -271,6 +275,8 @@ class ITK_TEMPLATE_EXPORT DisplacementFieldJacobianDeterminantFilter
271275
typename ImageBaseType::ConstPointer m_RealValuedInputImage{};
272276

273277
RadiusType m_NeighborhoodRadius{};
278+
279+
InternalMatrixType m_InputDirection{};
274280
};
275281
} // end namespace itk
276282

Modules/Filtering/DisplacementField/include/itkDisplacementFieldJacobianDeterminantFilter.hxx

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ DisplacementFieldJacobianDeterminantFilter<TInputImage, TRealType, TOutputImage>
138138
{
139139
Superclass::BeforeThreadedGenerateData();
140140

141+
// Cache the direction matrix from the input
142+
const auto & dir_d = this->GetInput()->GetDirection().GetVnlMatrix();
143+
for (unsigned int i = 0; i < ImageDimension; ++i)
144+
{
145+
for (unsigned int j = 0; j < ImageDimension; ++j)
146+
{
147+
m_InputDirection(i, j) = static_cast<TRealType>(dir_d(i, j));
148+
}
149+
}
150+
141151
// Set the weights on the derivatives.
142152
// Are we using image spacing in the calculations? If so we must update now
143153
// in case our input image has changed.
@@ -208,18 +218,40 @@ TRealType
208218
DisplacementFieldJacobianDeterminantFilter<TInputImage, TRealType, TOutputImage>::EvaluateAtNeighborhood(
209219
const ConstNeighborhoodIteratorType & it) const
210220
{
211-
vnl_matrix_fixed<TRealType, ImageDimension, VectorDimension> J;
212-
for (unsigned int i = 0; i < ImageDimension; ++i)
221+
InternalMatrixType localGrad;
222+
InternalMatrixType physicalGrad;
223+
224+
for (unsigned int col = 0; col < ImageDimension; ++col)
225+
{
226+
// Take finite difference along index axis col
227+
for (unsigned int row = 0; row < ImageDimension; ++row)
228+
{
229+
TRealType localComponentGrad = m_HalfDerivativeWeights[col] * (it.GetNext(col)[row] - it.GetPrevious(col)[row]);
230+
231+
localGrad[row][col] = localComponentGrad;
232+
}
233+
}
234+
235+
for (unsigned int row = 0; row < ImageDimension; ++row)
213236
{
214-
for (unsigned int j = 0; j < VectorDimension; ++j)
237+
vnl_vector_fixed<TRealType, ImageDimension> localComponentGrad_d;
238+
for (unsigned int col = 0; col < ImageDimension; ++col)
239+
{
240+
localComponentGrad_d[col] = localGrad[row][col];
241+
}
242+
243+
vnl_vector_fixed<TRealType, ImageDimension> physicalComponentGrad = m_InputDirection * localComponentGrad_d;
244+
245+
for (unsigned int col = 0; col < ImageDimension; ++col)
215246
{
216-
J[i][j] = m_HalfDerivativeWeights[i] * (it.GetNext(i)[j] - it.GetPrevious(i)[j]);
247+
physicalGrad[row][col] = physicalComponentGrad[col];
217248
}
218-
// add one on the diagonal to consider the warping and not only the
219-
// deformation field
220-
J[i][i] += 1.0;
249+
250+
physicalGrad[row][row] += itk::NumericTraits<TRealType>::OneValue();
221251
}
222-
return vnl_det(J);
252+
253+
// Return determinant of physical Jacobian:
254+
return vnl_determinant(physicalGrad);
223255
}
224256

225257
template <typename TInputImage, typename TRealType, typename TOutputImage>

Modules/Filtering/DisplacementField/test/itkDisplacementFieldJacobianDeterminantFilterTest.cxx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,13 @@ TestDisplacementJacobianDeterminantValue()
195195
auto dispacementfieldFlip = flip->GetOutput();
196196

197197
// 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;
198+
itk::Matrix<double, 2, 2> flipMat;
199+
flipMat.SetIdentity();
200+
flipMat[0][0] = -1.0;
201+
202+
itk::Matrix<double, 2, 2> newDirFlip = flipMat * dispacementfield->GetDirection();
201203

202204
dispacementfieldFlip->SetDirection(newDirFlip);
203-
dispacementfieldFlip->SetOrigin(dispacementfield->GetOrigin());
204-
dispacementfieldFlip->SetSpacing(dispacementfield->GetSpacing());
205205

206206
const float detFlipped = GetDeterminantAtPoint(dispacementfieldFlip, physPt);
207207

0 commit comments

Comments
 (0)