/*========================================================================= * * Copyright Insight Software Consortium * * 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 * * http://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 itkKernelTransform_hxx #define itkKernelTransform_hxx #include "itkKernelTransform.h" namespace itk { template KernelTransform::KernelTransform() : Superclass(NDimensions) // the second NDimensions is associated is provided as // a tentative number for initializing the Jacobian. // The matrix can be resized at run time so this number // here is irrelevant. The correct size of the Jacobian // will be NDimension X NDimension.NumberOfLandMarks. { this->m_I.set_identity(); this->m_SourceLandmarks = PointSetType::New(); this->m_TargetLandmarks = PointSetType::New(); this->m_Displacements = VectorSetType::New(); this->m_WMatrixComputed = false; this->m_Stiffness = 0.0; } template KernelTransform:: ~KernelTransform() { } template void KernelTransform::SetSourceLandmarks(PointSetType *landmarks) { itkDebugMacro("setting SourceLandmarks to " << landmarks); if( this->m_SourceLandmarks != landmarks ) { this->m_SourceLandmarks = landmarks; this->UpdateParameters(); this->Modified(); } } template void KernelTransform::SetTargetLandmarks(PointSetType *landmarks) { itkDebugMacro("setting TargetLandmarks to " << landmarks); if( this->m_TargetLandmarks != landmarks ) { this->m_TargetLandmarks = landmarks; this->UpdateParameters(); this->Modified(); } } template void KernelTransform::ComputeG( const InputVectorType &, GMatrixType & itkNotUsed(gmatrix) ) const { itkExceptionMacro(<< "ComputeG(vector,gmatrix) must be reimplemented" << " in subclasses of KernelTransform."); } template const typename KernelTransform::GMatrixType & KernelTransform::ComputeReflexiveG(PointsIterator) const { m_GMatrix.fill(NumericTraits::ZeroValue()); m_GMatrix.fill_diagonal(m_Stiffness); return m_GMatrix; } template void KernelTransform::ComputeDeformationContribution(const InputPointType & thisPoint, OutputPointType & result) const { /* * Default implementation of the the method. This can be overloaded * in transforms whose kernel produce diagonal G matrices. */ PointIdentifier numberOfLandmarks = this->m_SourceLandmarks ->GetNumberOfPoints(); PointsIterator sp = this->m_SourceLandmarks->GetPoints()->Begin(); GMatrixType Gmatrix; for( unsigned int lnd = 0; lnd < numberOfLandmarks; lnd++ ) { this->ComputeG(thisPoint - sp->Value(), Gmatrix); for( unsigned int dim = 0; dim < NDimensions; dim++ ) { for( unsigned int odim = 0; odim < NDimensions; odim++ ) { result[odim] += Gmatrix(dim, odim) * m_DMatrix(dim, lnd); } } ++sp; } } template void KernelTransform ::ComputeD() { PointIdentifier numberOfLandmarks = this->m_SourceLandmarks ->GetNumberOfPoints(); PointsIterator sp = this->m_SourceLandmarks->GetPoints()->Begin(); PointsIterator tp = this->m_TargetLandmarks->GetPoints()->Begin(); PointsIterator end = this->m_SourceLandmarks->GetPoints()->End(); this->m_Displacements->Reserve(numberOfLandmarks); typename VectorSetType::Iterator vt = this->m_Displacements->Begin(); while( sp != end ) { vt->Value() = tp->Value() - sp->Value(); ++vt; ++sp; ++tp; } } template void KernelTransform ::ComputeWMatrix() { typedef vnl_svd SVDSolverType; this->ComputeL(); this->ComputeY(); SVDSolverType svd(this->m_LMatrix, 1e-8); this->m_WMatrix = svd.solve(this->m_YMatrix); this->ReorganizeW(); } template void KernelTransform ::ComputeL() { PointIdentifier numberOfLandmarks = this->m_SourceLandmarks ->GetNumberOfPoints(); vnl_matrix O2(NDimensions * ( NDimensions + 1 ), NDimensions * ( NDimensions + 1 ), 0); this->ComputeP(); this->ComputeK(); this->m_LMatrix.set_size( NDimensions * ( numberOfLandmarks + NDimensions + 1 ), NDimensions * ( numberOfLandmarks + NDimensions + 1 ) ); this->m_LMatrix.fill(0.0); this->m_LMatrix.update(this->m_KMatrix, 0, 0); this->m_LMatrix.update( this->m_PMatrix, 0, this->m_KMatrix.columns() ); this->m_LMatrix.update(this->m_PMatrix.transpose(), this->m_KMatrix.rows(), 0); this->m_LMatrix.update( O2, this->m_KMatrix.rows(), this->m_KMatrix.columns() ); } template void KernelTransform ::ComputeK() { PointIdentifier numberOfLandmarks = this->m_SourceLandmarks ->GetNumberOfPoints(); GMatrixType G; this->ComputeD(); this->m_KMatrix.set_size(NDimensions * numberOfLandmarks, NDimensions * numberOfLandmarks); this->m_KMatrix.fill(0.0); PointsIterator p1 = this->m_SourceLandmarks->GetPoints()->Begin(); PointsIterator end = this->m_SourceLandmarks->GetPoints()->End(); // K matrix is symmetric, so only evaluate the upper triangle and // store the values in bot the upper and lower triangle unsigned int i = 0; while( p1 != end ) { PointsIterator p2 = p1; // start at the diagonal element unsigned int j = i; // Compute the block diagonal element, i.e. kernel for pi->pi G = this->ComputeReflexiveG(p1); this->m_KMatrix.update(G, i * NDimensions, i * NDimensions); ++p2; ++j; // Compute the upper (and copy into lower) triangular part of K while( p2 != end ) { const InputVectorType s = p1.Value() - p2.Value(); this->ComputeG(s, G); // write value in upper and lower triangle of matrix this->m_KMatrix.update(G, i * NDimensions, j * NDimensions); this->m_KMatrix.update(G, j * NDimensions, i * NDimensions); ++p2; ++j; } ++p1; ++i; } } template void KernelTransform ::ComputeP() { PointIdentifier numberOfLandmarks = this->m_SourceLandmarks ->GetNumberOfPoints(); IMatrixType I; IMatrixType temp; InputPointType p; p.Fill( NumericTraits::ZeroValue() ); I.set_identity(); this->m_PMatrix.set_size( NDimensions * numberOfLandmarks, NDimensions * ( NDimensions + 1 ) ); this->m_PMatrix.fill(0.0); for( unsigned long i = 0; i < numberOfLandmarks; i++ ) { this->m_SourceLandmarks->GetPoint(i, &p); for( unsigned int j = 0; j < NDimensions; j++ ) { temp = I * p[j]; this->m_PMatrix.update(temp, i * NDimensions, j * NDimensions); } this->m_PMatrix.update(I, i * NDimensions, NDimensions * NDimensions); } } template void KernelTransform ::ComputeY() { PointIdentifier numberOfLandmarks = this->m_SourceLandmarks ->GetNumberOfPoints(); typename VectorSetType::ConstIterator displacement = this->m_Displacements->Begin(); this->m_YMatrix.set_size(NDimensions * ( numberOfLandmarks + NDimensions + 1 ), 1); this->m_YMatrix.fill(0.0); for( unsigned int i = 0; i < numberOfLandmarks; i++ ) { for( unsigned int j = 0; j < NDimensions; j++ ) { this->m_YMatrix.put(i * NDimensions + j, 0, displacement.Value()[j]); } ++displacement; } for( unsigned int i = 0; i < NDimensions * ( NDimensions + 1 ); i++ ) { this->m_YMatrix.put(numberOfLandmarks * NDimensions + i, 0, 0); } } template void KernelTransform ::ReorganizeW() { PointIdentifier numberOfLandmarks = this->m_SourceLandmarks ->GetNumberOfPoints(); // The deformable (non-affine) part of the registration goes here this->m_DMatrix.set_size(NDimensions, numberOfLandmarks); unsigned int ci = 0; for( unsigned int lnd = 0; lnd < numberOfLandmarks; lnd++ ) { for( unsigned int dim = 0; dim < NDimensions; dim++ ) { this->m_DMatrix(dim, lnd) = this->m_WMatrix(ci++, 0); } } // This matrix holds the rotational part of the Affine component for( unsigned int j = 0; j < NDimensions; j++ ) { for( unsigned int i = 0; i < NDimensions; i++ ) { this->m_AMatrix(i, j) = this->m_WMatrix(ci++, 0); } } // This vector holds the translational part of the Affine component for( unsigned int k = 0; k < NDimensions; k++ ) { this->m_BVector(k) = this->m_WMatrix(ci++, 0); } // release WMatrix memory by assigning a small one. this->m_WMatrix = WMatrixType(1, 1); } template typename KernelTransform::OutputPointType KernelTransform ::TransformPoint(const InputPointType & thisPoint) const { OutputPointType result; typedef typename OutputPointType::ValueType ValueType; result.Fill(NumericTraits::ZeroValue()); //TODO: It is unclear if the following line is needed. this->ComputeDeformationContribution(thisPoint, result); // Add the rotational part of the Affine component for( unsigned int j = 0; j < NDimensions; j++ ) { for( unsigned int i = 0; i < NDimensions; i++ ) { result[i] += this->m_AMatrix(i, j) * thisPoint[j]; } } // This vector holds the translational part of the Affine component for( unsigned int k = 0; k < NDimensions; k++ ) { result[k] += this->m_BVector(k) + thisPoint[k]; } return result; } template void KernelTransform ::ComputeJacobianWithRespectToParameters(const InputPointType &, JacobianType & jacobian) const { jacobian.Fill(0.0); // FIXME: TODO // The Jacobian should be computable in terms of the matrices // used to Transform points... itkExceptionMacro(<< "Get[Local]Jacobian must be implemented in subclasses" << " of KernelTransform."); } template void KernelTransform ::SetParameters(const ParametersType & parameters) { // Set the parameters // NOTE that in this transformation both the Source and Target // landmarks could be considered as parameters. It is assumed // here that the Target landmarks are provided by the user and // are not changed during the optimization process required for // registration. // Save parameters. Needed for proper operation of TransformUpdateParameters. if( ¶meters != &(this->m_Parameters) ) { const size_t parameterSize = this->GetParameters().Size(); const NumberOfParametersType offset = NumericTraits< NumberOfParametersType >::ZeroValue(); this->CopyInParameters( &( parameters.data_block() )[offset], &( parameters.data_block() )[offset] + parameterSize ); } typename PointsContainer::Pointer landmarks = PointsContainer::New(); const unsigned int numberOfLandmarks = parameters.Size() / NDimensions; landmarks->Reserve(numberOfLandmarks); PointsIterator itr = landmarks->Begin(); PointsIterator end = landmarks->End(); InputPointType landMark; unsigned int pcounter = 0; while( itr != end ) { for( unsigned int dim = 0; dim < NDimensions; dim++ ) { landMark[dim] = parameters[pcounter]; ++pcounter; } itr.Value() = landMark; ++itr; } this->m_SourceLandmarks->SetPoints(landmarks); // 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 KernelTransform ::SetFixedParameters(const FixedParametersType & parameters) { // Set the fixed parameters // Since the API of the SetParameters() function sets the // source landmarks, this function was added to support the // setting of the target landmarks, and allowing the Transform // I/O mechanism to be supported. typename PointsContainer::Pointer landmarks = PointsContainer::New(); const unsigned int numberOfLandmarks = parameters.Size() / NDimensions; landmarks->Reserve(numberOfLandmarks); PointsIterator itr = landmarks->Begin(); PointsIterator end = landmarks->End(); InputPointType landMark; unsigned int pcounter = 0; while( itr != end ) { for( unsigned int dim = 0; dim < NDimensions; dim++ ) { landMark[dim] = parameters[pcounter]; ++pcounter; } itr.Value() = landMark; ++itr; } this->m_TargetLandmarks->SetPoints(landmarks); } template void KernelTransform ::UpdateParameters() const { this->m_Parameters = ParametersType(this->m_SourceLandmarks ->GetNumberOfPoints() * NDimensions); PointsIterator itr = this->m_SourceLandmarks->GetPoints()->Begin(); PointsIterator end = this->m_SourceLandmarks->GetPoints()->End(); unsigned int pcounter = 0; while( itr != end ) { InputPointType landmark = itr.Value(); for( unsigned int dim = 0; dim < NDimensions; dim++ ) { this->m_Parameters[pcounter] = landmark[dim]; ++pcounter; } ++itr; } } template const typename KernelTransform::ParametersType & KernelTransform ::GetParameters() const { this->UpdateParameters(); return this->m_Parameters; } template const typename KernelTransform::FixedParametersType & KernelTransform ::GetFixedParameters() const { // Get the fixed parameters // This returns the target landmark locations // This was added to support the Transform Reader/Writer mechanism this->m_FixedParameters = ParametersType(this->m_TargetLandmarks ->GetNumberOfPoints() * NDimensions); PointsIterator itr = this->m_TargetLandmarks->GetPoints()->Begin(); PointsIterator end = this->m_TargetLandmarks->GetPoints()->End(); unsigned int pcounter = 0; while( itr != end ) { InputPointType landmark = itr.Value(); for( unsigned int dim = 0; dim < NDimensions; dim++ ) { this->m_FixedParameters[pcounter] = landmark[dim]; ++pcounter; } ++itr; } return this->m_FixedParameters; } template void KernelTransform ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); if( this->m_SourceLandmarks ) { os << indent << "SourceLandmarks: " << std::endl; this->m_SourceLandmarks->Print( os, indent.GetNextIndent() ); } if( this->m_TargetLandmarks ) { os << indent << "TargetLandmarks: " << std::endl; this->m_TargetLandmarks->Print( os, indent.GetNextIndent() ); } if( this->m_Displacements ) { os << indent << "Displacements: " << std::endl; this->m_Displacements->Print( os, indent.GetNextIndent() ); } os << indent << "Stiffness: " << this->m_Stiffness << std::endl; } } // end namespace itk #endif