/*========================================================================= * * 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 itkBSplineTransformParametersAdaptor_hxx #define itkBSplineTransformParametersAdaptor_hxx #include "itkBSplineDecompositionImageFilter.h" #include "itkBSplineResampleImageFunction.h" #include "itkResampleImageFilter.h" namespace itk { template BSplineTransformParametersAdaptor::BSplineTransformParametersAdaptor() { this->m_RequiredFixedParameters.SetSize(SpaceDimension * (SpaceDimension + 3)); this->m_RequiredTransformDomainOrigin.Fill(0.0); this->m_RequiredTransformDomainDirection.SetIdentity(); this->m_RequiredTransformDomainPhysicalDimensions.Fill(1.0); this->m_RequiredTransformDomainMeshSize.Fill(1); this->UpdateRequiredFixedParameters(); } template void BSplineTransformParametersAdaptor::SetRequiredTransformDomainOrigin(const OriginType & origin) { if (origin != this->m_RequiredTransformDomainOrigin) { itkDebugMacro("Setting m_RequiredTransformDomainOrigin to " << origin); this->m_RequiredTransformDomainOrigin = origin; this->UpdateRequiredFixedParameters(); this->Modified(); } } template void BSplineTransformParametersAdaptor::SetRequiredTransformDomainPhysicalDimensions( const PhysicalDimensionsType & dimensions) { if (dimensions != this->m_RequiredTransformDomainPhysicalDimensions) { itkDebugMacro("Setting m_RequiredTransformDomainPhysicalDimensions to " << dimensions); this->m_RequiredTransformDomainPhysicalDimensions = dimensions; this->UpdateRequiredFixedParameters(); this->Modified(); } } template void BSplineTransformParametersAdaptor::SetRequiredTransformDomainDirection(const DirectionType & direction) { if (direction != this->m_RequiredTransformDomainDirection) { itkDebugMacro("Setting m_RequiredTransformDomainDirection to " << direction); this->m_RequiredTransformDomainDirection = direction; this->UpdateRequiredFixedParameters(); this->Modified(); } } template void BSplineTransformParametersAdaptor::SetRequiredTransformDomainMeshSize(const MeshSizeType & meshSize) { if (meshSize != this->m_RequiredTransformDomainMeshSize) { itkDebugMacro("Setting m_RequiredTransformDomainMeshSize to " << meshSize); this->m_RequiredTransformDomainMeshSize = meshSize; this->UpdateRequiredFixedParameters(); this->Modified(); } } template void BSplineTransformParametersAdaptor::SetRequiredFixedParameters(const FixedParametersType fixedParameters) { Superclass::SetRequiredFixedParameters(fixedParameters); // Set the direction parameters for (SizeValueType di = 0; di < SpaceDimension; ++di) { for (SizeValueType dj = 0; dj < SpaceDimension; ++dj) { this->m_RequiredTransformDomainDirection[di][dj] = this->m_RequiredFixedParameters[3 * SpaceDimension + (di * SpaceDimension + dj)]; } } // set the mesh size parameters for (SizeValueType i = 0; i < SpaceDimension; ++i) { this->m_RequiredTransformDomainMeshSize[i] = static_cast(this->m_RequiredFixedParameters[i]) - TransformType::SplineOrder; } // Set the physical dimensions parameters for (SizeValueType i = 0; i < SpaceDimension; ++i) { FixedParametersValueType gridSpacing = this->m_RequiredFixedParameters[2 * SpaceDimension + i]; this->m_RequiredTransformDomainPhysicalDimensions[i] = gridSpacing * static_cast(this->m_RequiredTransformDomainMeshSize[i]); } // Set the origin parameters OriginType origin; for (SizeValueType i = 0; i < SpaceDimension; ++i) { FixedParametersValueType gridSpacing = this->m_RequiredFixedParameters[2 * SpaceDimension + i]; origin[i] = 0.5 * gridSpacing * (TransformType::SplineOrder - 1); } origin = this->m_RequiredTransformDomainDirection * origin; for (SizeValueType i = 0; i < SpaceDimension; ++i) { this->m_RequiredTransformDomainOrigin[i] = origin[i] + this->m_RequiredFixedParameters[SpaceDimension + i]; } } template void BSplineTransformParametersAdaptor::UpdateRequiredFixedParameters() { // Fixed parameters store the following information: // grid size // grid origin // grid spacing // grid direction // The size of these is equal to the SpaceDimension // set the grid size parameters for (SizeValueType i = 0; i < SpaceDimension; ++i) { this->m_RequiredFixedParameters[i] = this->m_RequiredTransformDomainMeshSize[i] + TransformType::SplineOrder; } // Set the origin parameters OriginType origin; for (SizeValueType i = 0; i < SpaceDimension; ++i) { FixedParametersValueType gridSpacing = this->m_RequiredTransformDomainPhysicalDimensions[i] / static_cast(this->m_RequiredTransformDomainMeshSize[i]); origin[i] = -0.5 * gridSpacing * (TransformType::SplineOrder - 1); } origin = this->m_RequiredTransformDomainDirection * origin; for (SizeValueType i = 0; i < SpaceDimension; ++i) { this->m_RequiredFixedParameters[SpaceDimension + i] = static_cast(origin[i] + this->m_RequiredTransformDomainOrigin[i]); } // Set the spacing parameters for (SizeValueType i = 0; i < SpaceDimension; ++i) { FixedParametersValueType gridSpacing = this->m_RequiredTransformDomainPhysicalDimensions[i] / static_cast(this->m_RequiredTransformDomainMeshSize[i]); this->m_RequiredFixedParameters[2 * SpaceDimension + i] = static_cast(gridSpacing); } // Set the direction parameters for (SizeValueType di = 0; di < SpaceDimension; ++di) { for (SizeValueType dj = 0; dj < SpaceDimension; ++dj) { this->m_RequiredFixedParameters[3 * SpaceDimension + (di * SpaceDimension + dj)] = static_cast(this->m_RequiredTransformDomainDirection[di][dj]); } } } template void BSplineTransformParametersAdaptor::AdaptTransformParameters() { if (!this->m_Transform) { itkExceptionMacro("Transform has not been set."); } if (this->m_RequiredFixedParameters == this->m_Transform->GetFixedParameters()) { return; } SizeType newGridSize; OriginType newGridOrigin; SpacingType newGridSpacing; DirectionType newGridDirection; for (SizeValueType i = 0; i < SpaceDimension; ++i) { newGridSize[i] = static_cast(this->m_RequiredFixedParameters[i]); newGridOrigin[i] = this->m_RequiredFixedParameters[SpaceDimension + i]; newGridSpacing[i] = this->m_RequiredFixedParameters[2 * SpaceDimension + i]; for (SizeValueType j = 0; j < SpaceDimension; ++j) { newGridDirection[i][j] = this->m_RequiredFixedParameters[3 * SpaceDimension + (i * SpaceDimension + j)]; } } const RegionType & coefficientImageRegion = this->m_Transform->GetCoefficientImages()[0]->GetLargestPossibleRegion(); IndexType newGridIndex = coefficientImageRegion.GetIndex(); // Resample the coefficient images using CoefficientUpsampleFunctionType = BSplineResampleImageFunction; using UpsampleFilterType = ResampleImageFilter; using DecompositionFilterType = BSplineDecompositionImageFilter; CoefficientImageArray newCoefficientImages; // Loop over dimension: each direction is upsampled separately. for (SizeValueType j = 0; j < SpaceDimension; ++j) { /* Set the coefficient image as the input of the upsampler filter. * The upsampler samples the deformation field at the locations * of the new control points, given the current coefficients * (note: it does not just interpolate the coefficient image, * which would be wrong). The B-spline coefficients that * describe the resulting image are computed by the * decomposition filter. * * This code is copied from the itk-example * DeformableRegistration6.cxx . */ auto upsampler = UpsampleFilterType::New(); auto coeffUpsampleFunction = CoefficientUpsampleFunctionType::New(); upsampler->SetInterpolator(coeffUpsampleFunction); upsampler->SetSize(newGridSize); upsampler->SetOutputStartIndex(newGridIndex); upsampler->SetOutputSpacing(newGridSpacing); upsampler->SetOutputOrigin(newGridOrigin); upsampler->SetOutputDirection(newGridDirection); upsampler->SetInput(this->m_Transform->GetCoefficientImages()[j]); auto decompositionFilter = DecompositionFilterType::New(); decompositionFilter->SetSplineOrder(TransformType::SplineOrder); decompositionFilter->SetInput(upsampler->GetOutput()); newCoefficientImages[j] = decompositionFilter->GetOutput(); newCoefficientImages[j]->Update(); newCoefficientImages[j]->DisconnectPipeline(); } this->m_Transform->SetCoefficientImages(newCoefficientImages); } template void BSplineTransformParametersAdaptor::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Required transform domain origin: " << this->m_RequiredTransformDomainOrigin << std::endl; os << indent << "Required transform domain direction: " << this->m_RequiredTransformDomainDirection << std::endl; os << indent << "Required transform domain physical dimensions: " << this->m_RequiredTransformDomainPhysicalDimensions << std::endl; os << indent << "Required transform domain mesh size: " << this->m_RequiredTransformDomainMeshSize << std::endl; } } // namespace itk #endif