/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkMatrixOffsetTransformBase_hxx #define itkMatrixOffsetTransformBase_hxx #include "itkNumericTraits.h" #include "vnl/algo/vnl_matrix_inverse.h" #include "itkMath.h" #include "itkCrossHelper.h" namespace itk { template MatrixOffsetTransformBase::MatrixOffsetTransformBase( unsigned int paramDims) : Superclass(paramDims) { m_MatrixMTime.Modified(); m_InverseMatrixMTime = m_MatrixMTime; this->m_FixedParameters.SetSize(VInputDimension); this->m_FixedParameters.Fill(0.0); } template MatrixOffsetTransformBase::MatrixOffsetTransformBase( const MatrixType & matrix, const OutputVectorType & offset) : m_Matrix(matrix) , m_Offset(offset) { m_MatrixMTime.Modified(); std::copy_n(offset.begin(), VOutputDimension, m_Translation.begin()); this->ComputeMatrixParameters(); } template void MatrixOffsetTransformBase::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); unsigned int i, j; os << indent << "Matrix: " << std::endl; for (i = 0; i < VInputDimension; ++i) { os << indent.GetNextIndent(); for (j = 0; j < VOutputDimension; ++j) { os << m_Matrix[i][j] << ' '; } os << std::endl; } os << indent << "Offset: " << m_Offset << std::endl; os << indent << "Center: " << m_Center << std::endl; os << indent << "Translation: " << m_Translation << std::endl; os << indent << "Inverse: " << std::endl; const auto & inverseMatrix = this->GetInverseMatrix(); for (i = 0; i < VInputDimension; ++i) { os << indent.GetNextIndent(); for (j = 0; j < VOutputDimension; ++j) { os << inverseMatrix[i][j] << ' '; } os << std::endl; } os << indent << "Singular: " << m_Singular << std::endl; } template void MatrixOffsetTransformBase::SetIdentity() { m_Matrix.SetIdentity(); m_MatrixMTime.Modified(); m_Offset.Fill(OutputVectorValueType{}); m_Translation.Fill(OutputVectorValueType{}); // Fixed parameters must be preserved when setting the transform to identity // the center is part of the stationary fixed parameters // and should not be modified by SetIdentity. m_Center.Fill(InputPointValueType{}); m_Singular = false; m_InverseMatrix.SetIdentity(); m_InverseMatrixMTime = m_MatrixMTime; this->Modified(); } template void MatrixOffsetTransformBase::Compose(const Self * other, bool pre) { if (pre) { m_Offset = m_Matrix * other->m_Offset + m_Offset; m_Matrix = m_Matrix * other->m_Matrix; } else { m_Offset = other->m_Matrix * m_Offset + other->m_Offset; m_Matrix = other->m_Matrix * m_Matrix; } this->ComputeTranslation(); this->ComputeMatrixParameters(); m_MatrixMTime.Modified(); this->Modified(); } template auto MatrixOffsetTransformBase::TransformPoint( const InputPointType & point) const -> OutputPointType { return m_Matrix * point + m_Offset; } template auto MatrixOffsetTransformBase::TransformVector( const InputVectorType & vect) const -> OutputVectorType { return m_Matrix * vect; } template auto MatrixOffsetTransformBase::TransformVector( const InputVnlVectorType & vect) const -> OutputVnlVectorType { return m_Matrix.GetVnlMatrix() * vect; } template auto MatrixOffsetTransformBase::TransformVector( const InputVectorPixelType & vect) const -> OutputVectorPixelType { const unsigned int vectorDim = vect.Size(); vnl_vector vnl_vect(vectorDim); vnl_matrix vnl_mat(vectorDim, vect.Size(), 0.0); for (unsigned int i = 0; i < vectorDim; ++i) { vnl_vect[i] = vect[i]; for (unsigned int j = 0; j < vectorDim; ++j) { if ((i < VInputDimension) && (j < VInputDimension)) { vnl_mat(i, j) = m_Matrix(i, j); } else if (i == j) { vnl_mat(i, j) = 1.0; } } } vnl_vector tvect = vnl_mat * vnl_vect; OutputVectorPixelType outVect; outVect.SetSize(vectorDim); for (unsigned int i = 0; i < vectorDim; ++i) { outVect[i] = tvect(i); } return outVect; } template auto MatrixOffsetTransformBase::TransformCovariantVector( const InputCovariantVectorType & vec) const -> OutputCovariantVectorType { OutputCovariantVectorType result; // Converted vector const auto & inverseMatrix = this->GetInverseMatrix(); for (unsigned int i = 0; i < VOutputDimension; ++i) { result[i] = ScalarType{}; for (unsigned int j = 0; j < VInputDimension; ++j) { result[i] += inverseMatrix[j][i] * vec[j]; // Inverse transposed } } return result; } template auto MatrixOffsetTransformBase::TransformCovariantVector( const InputVectorPixelType & vect) const -> OutputVectorPixelType { const unsigned int vectorDim = vect.Size(); vnl_vector vnl_vect(vectorDim); vnl_matrix vnl_mat(vectorDim, vect.Size(), 0.0); const auto & inverseMatrix = this->GetInverseMatrix(); for (unsigned int i = 0; i < vectorDim; ++i) { vnl_vect[i] = vect[i]; for (unsigned int j = 0; j < vectorDim; ++j) { if ((i < VInputDimension) && (j < VInputDimension)) { vnl_mat(i, j) = inverseMatrix(j, i); } else if (i == j) { vnl_mat(i, j) = 1.0; } } } vnl_vector tvect = vnl_mat * vnl_vect; OutputVectorPixelType outVect; outVect.SetSize(vectorDim); for (unsigned int i = 0; i < vectorDim; ++i) { outVect[i] = tvect(i); } return outVect; } template auto MatrixOffsetTransformBase::TransformDiffusionTensor3D( const InputDiffusionTensor3DType & tensor) const -> OutputDiffusionTensor3DType { JacobianType jacobian; jacobian.SetSize(InverseMatrixType::RowDimensions, InverseMatrixType::ColumnDimensions); const auto & inverseMatrix = this->GetInverseMatrix(); for (unsigned int i = 0; i < InverseMatrixType::RowDimensions; ++i) { for (unsigned int j = 0; j < InverseMatrixType::ColumnDimensions; ++j) { jacobian(i, j) = inverseMatrix(i, j); } } OutputDiffusionTensor3DType result = this->PreservationOfPrincipalDirectionDiffusionTensor3DReorientation(tensor, jacobian); return result; } template auto MatrixOffsetTransformBase::TransformDiffusionTensor3D( const InputVectorPixelType & tensor) const -> OutputVectorPixelType { OutputVectorPixelType result(InputDiffusionTensor3DType::InternalDimension); // Converted tensor result.Fill(0.0); InputDiffusionTensor3DType dt(0.0); const unsigned int tDim = tensor.Size(); for (unsigned int i = 0; i < tDim; ++i) { dt[i] = tensor[i]; } OutputDiffusionTensor3DType outDT = this->TransformDiffusionTensor3D(dt); for (unsigned int i = 0; i < InputDiffusionTensor3DType::InternalDimension; ++i) { result[i] = outDT[i]; } return result; } template typename MatrixOffsetTransformBase::OutputSymmetricSecondRankTensorType MatrixOffsetTransformBase::TransformSymmetricSecondRankTensor( const InputSymmetricSecondRankTensorType & inputTensor) const { JacobianType jacobian; jacobian.SetSize(VOutputDimension, VInputDimension); JacobianType invJacobian; invJacobian.SetSize(VInputDimension, VOutputDimension); JacobianType tensor; tensor.SetSize(VInputDimension, VInputDimension); for (unsigned int i = 0; i < VInputDimension; ++i) { for (unsigned int j = 0; j < VInputDimension; ++j) { tensor(i, j) = inputTensor(i, j); } } const auto & inverseMatrix = this->GetInverseMatrix(); for (unsigned int i = 0; i < VInputDimension; ++i) { for (unsigned int j = 0; j < VOutputDimension; ++j) { jacobian(j, i) = this->GetMatrix()(j, i); invJacobian(i, j) = inverseMatrix(i, j); } } JacobianType outTensor = jacobian * tensor * invJacobian; OutputSymmetricSecondRankTensorType outputTensor; for (unsigned int i = 0; i < VOutputDimension; ++i) { for (unsigned int j = 0; j < VOutputDimension; ++j) { outputTensor(i, j) = outTensor(i, j); } } return outputTensor; } template auto MatrixOffsetTransformBase::TransformSymmetricSecondRankTensor( const InputVectorPixelType & inputTensor) const -> OutputVectorPixelType { JacobianType jacobian; jacobian.SetSize(VOutputDimension, VInputDimension); JacobianType invJacobian; invJacobian.SetSize(VInputDimension, VOutputDimension); JacobianType tensor; tensor.SetSize(VInputDimension, VInputDimension); for (unsigned int i = 0; i < VInputDimension; ++i) { for (unsigned int j = 0; j < VInputDimension; ++j) { tensor(i, j) = inputTensor[j + VInputDimension * i]; } } const auto & inverseMatrix = this->GetInverseMatrix(); for (unsigned int i = 0; i < VInputDimension; ++i) { for (unsigned int j = 0; j < VOutputDimension; ++j) { jacobian(j, i) = this->GetMatrix()(j, i); invJacobian(i, j) = inverseMatrix(i, j); } } JacobianType outTensor = jacobian * tensor * invJacobian; OutputVectorPixelType outputTensor; for (unsigned int i = 0; i < VOutputDimension; ++i) { for (unsigned int j = 0; j < VOutputDimension; ++j) { outputTensor[j + VOutputDimension * i] = outTensor(i, j); } } return outputTensor; } template auto MatrixOffsetTransformBase::GetInverseMatrix() const -> const InverseMatrixType & { // If the transform has been modified we recompute the inverse if (m_InverseMatrixMTime != m_MatrixMTime) { m_Singular = false; try { m_InverseMatrix = m_Matrix.GetInverse(); } catch (...) { m_Singular = true; } m_InverseMatrixMTime = m_MatrixMTime; } return m_InverseMatrix; } template bool MatrixOffsetTransformBase::GetInverse( InverseTransformType * inverse) const { if (!inverse) { return false; } inverse->SetFixedParameters(this->GetFixedParameters()); const auto & inverseMatrix = this->GetInverseMatrix(); if (m_Singular) { return false; } inverse->m_Matrix = inverseMatrix; inverse->m_InverseMatrix = m_Matrix; inverse->m_Offset = -(inverseMatrix * m_Offset); inverse->ComputeTranslation(); inverse->ComputeMatrixParameters(); return true; } template auto MatrixOffsetTransformBase::GetInverseTransform() const -> InverseTransformBasePointer { return Superclass::InvertTransform(*this); } template void MatrixOffsetTransformBase::SetFixedParameters( const FixedParametersType & fp) { if (fp.size() < VInputDimension) { itkExceptionMacro("Error setting fixed parameters: parameters array size (" << fp.size() << ") is less than expected (VInputDimension = " << VInputDimension << ')'); } this->m_FixedParameters = fp; InputPointType c; for (unsigned int i = 0; i < VInputDimension; ++i) { c[i] = this->m_FixedParameters[i]; } this->SetCenter(c); } template const typename MatrixOffsetTransformBase::FixedParametersType & MatrixOffsetTransformBase::GetFixedParameters() const { for (unsigned int i = 0; i < VInputDimension; ++i) { this->m_FixedParameters[i] = this->m_Center[i]; } return this->m_FixedParameters; } template auto MatrixOffsetTransformBase::GetParameters() const -> const ParametersType & { // Transfer the linear part unsigned int par = 0; for (unsigned int row = 0; row < VOutputDimension; ++row) { for (unsigned int col = 0; col < VInputDimension; ++col) { this->m_Parameters[par] = m_Matrix[row][col]; ++par; } } // Transfer the constant part for (unsigned int i = 0; i < VOutputDimension; ++i) { this->m_Parameters[par] = m_Translation[i]; ++par; } return this->m_Parameters; } template void MatrixOffsetTransformBase::SetParameters( const ParametersType & parameters) { if (parameters.Size() < (VOutputDimension * VInputDimension + VOutputDimension)) { itkExceptionMacro("Error setting parameters: parameters array size (" << parameters.Size() << ") is less than expected " << " (VInputDimension * VOutputDimension + VOutputDimension) " << " (" << VInputDimension << " * " << VOutputDimension << " + " << VOutputDimension << " = " << VInputDimension * VOutputDimension + VOutputDimension << ')'); } unsigned int par = 0; // Save parameters. Needed for proper operation of TransformUpdateParameters. if (¶meters != &(this->m_Parameters)) { this->m_Parameters = parameters; } for (unsigned int row = 0; row < VOutputDimension; ++row) { for (unsigned int col = 0; col < VInputDimension; ++col) { m_Matrix[row][col] = this->m_Parameters[par]; ++par; } } // Transfer the constant part for (unsigned int i = 0; i < VOutputDimension; ++i) { m_Translation[i] = this->m_Parameters[par]; ++par; } m_MatrixMTime.Modified(); this->ComputeMatrix(); // Not necessary since parameters explicitly define // the matrix this->ComputeOffset(); // Modified is always called since we just have a pointer to the // parameters and cannot know if the parameters have changed. this->Modified(); } template void MatrixOffsetTransformBase:: ComputeJacobianWithRespectToParameters(const InputPointType & p, JacobianType & jacobian) const { // This will not reallocate memory if the dimensions are equal // to the matrix's current dimensions. jacobian.SetSize(VOutputDimension, this->GetNumberOfLocalParameters()); jacobian.Fill(0.0); // The Jacobian of the affine transform is composed of // subblocks of diagonal matrices, each one of them having // a constant value in the diagonal. const InputVectorType v = p - this->GetCenter(); unsigned int blockOffset = 0; for (unsigned int block = 0; block < VInputDimension; ++block) { for (unsigned int dim = 0; dim < VOutputDimension; ++dim) { jacobian(block, blockOffset + dim) = v[dim]; } blockOffset += VInputDimension; } for (unsigned int dim = 0; dim < VOutputDimension; ++dim) { jacobian(dim, blockOffset + dim) = 1.0; } } template void MatrixOffsetTransformBase:: ComputeJacobianWithRespectToPosition(const InputPointType &, JacobianPositionType & jac) const { jac = this->GetMatrix().GetVnlMatrix(); } template void MatrixOffsetTransformBase:: ComputeInverseJacobianWithRespectToPosition(const InputPointType &, InverseJacobianPositionType & jac) const { jac = this->GetInverseMatrix().GetVnlMatrix(); } template void MatrixOffsetTransformBase::ComputeOffset() { const MatrixType & matrix = this->GetMatrix(); OffsetType offset; for (unsigned int i = 0; i < VOutputDimension; ++i) { offset[i] = m_Translation[i] + m_Center[i]; for (unsigned int j = 0; j < VInputDimension; ++j) { offset[i] -= matrix[i][j] * m_Center[j]; } } m_Offset = offset; } template void MatrixOffsetTransformBase::ComputeTranslation() { const MatrixType & matrix = this->GetMatrix(); OffsetType translation; for (unsigned int i = 0; i < VOutputDimension; ++i) { translation[i] = m_Offset[i] - m_Center[i]; for (unsigned int j = 0; j < VInputDimension; ++j) { translation[i] += matrix[i][j] * m_Center[j]; } } m_Translation = translation; } template void MatrixOffsetTransformBase::ComputeMatrix() { // Since parameters explicitly define the matrix in this base class, this // function does nothing. Normally used to compute a matrix when // its parameterization (e.g., the class' versor) is modified. } template void MatrixOffsetTransformBase::ComputeMatrixParameters() { // Since parameters explicitly define the matrix in this base class, this // function does nothing. Normally used to update the parameterization // of the matrix (e.g., the class' versor) when the matrix is explicitly // set. } } // end namespace itk #endif