/*========================================================================= * * 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. * *=========================================================================*/ #define ITK_TEMPLATE_EXPLICIT_TransformFileWriter #include "itkTransformFileWriter.h" #include "itkTransformFactoryBase.h" #include "itkTransformIOFactory.h" #include "itkCompositeTransformIOHelper.h" #include namespace itk { template TransformFileWriterTemplate::TransformFileWriterTemplate() : m_FileName{ "" } { TransformFactoryBase::RegisterDefaultTransforms(); } template TransformFileWriterTemplate::~TransformFileWriterTemplate() = default; #if !defined(ITK_FUTURE_LEGACY_REMOVE) /** Set the writer to append to the specified file */ template void TransformFileWriterTemplate::SetAppendOn() { this->SetAppendMode(true); } /** Set the writer to overwrite the specified file - This is the * default mode. */ template void TransformFileWriterTemplate::SetAppendOff() { this->SetAppendMode(false); } /** Get the writer mode. */ template bool TransformFileWriterTemplate::GetAppendMode() { return (this->m_AppendMode); } #endif template <> void TransformFileWriterTemplate::PushBackTransformList(const Object * transObj); template <> void TransformFileWriterTemplate::PushBackTransformList(const Object * transObj); /** Set the input transform and reinitialize the list of transforms */ template void TransformFileWriterTemplate::SetInput(const Object * transform) { m_TransformList.clear(); this->PushBackTransformList(transform); } template const typename TransformFileWriterTemplate::TransformType * TransformFileWriterTemplate::GetInput() { ConstTransformPointer res = m_TransformList.front(); return res.GetPointer(); } /** Add a transform to be written */ template void TransformFileWriterTemplate::AddTransform(const Object * transform) { /* Check for a CompositeTransform. * The convention is that there should be one, and it should * be the first transform in the file */ const std::string transformName = transform->GetNameOfClass(); if (transformName.find("CompositeTransform") != std::string::npos) { if (!this->m_TransformList.empty()) { itkExceptionMacro("Can only write a transform of type CompositeTransform " "as the first transform in the file."); } } this->PushBackTransformList(transform); } template void TransformFileWriterTemplate::Update() { if (m_FileName.empty()) { itkExceptionMacro("No file name given"); } if (m_TransformIO.IsNull()) { using TransformFactoryIOType = TransformIOFactoryTemplate; m_TransformIO = TransformFactoryIOType::CreateTransformIO(m_FileName.c_str(), IOFileModeEnum::WriteMode); if (m_TransformIO.IsNull()) { std::ostringstream msg; msg << "Could not create Transform IO object for writing file " << this->GetFileName() << std::endl; std::list allobjects = ObjectFactoryBase::CreateAllInstance("itkTransformIOBaseTemplate"); if (!allobjects.empty()) { msg << " Tried to create one of the following:" << std::endl; for (auto & allobject : allobjects) { const Object * obj = dynamic_cast(allobject.GetPointer()); msg << " " << obj->GetNameOfClass() << std::endl; } msg << " You probably failed to set a file suffix, or" << std::endl << " set the suffix to an unsupported type." << std::endl; } else { msg << " There are no registered Transform IO factories." << std::endl << " Please visit https://www.itk.org/Wiki/ITK/FAQ#NoFactoryException to diagnose the problem." << std::endl; } itkExceptionMacro(<< msg.str().c_str()); } } m_TransformIO->SetAppendMode(this->m_AppendMode); m_TransformIO->SetUseCompression(this->m_UseCompression); m_TransformIO->SetFileName(this->m_FileName); m_TransformIO->SetTransformList(this->m_TransformList); m_TransformIO->Write(); } template void TransformFileWriterTemplate::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "FileName: " << m_FileName << std::endl; } namespace { /* This helper is used to: Create and set a new type transform that have requested output precision type. */ template struct TransformIOHelper { using OutputTransformPointer = typename TOutputTransformType::Pointer; using InputParameterValueType = typename TInputTransformType::ParametersValueType; using InputFixedParameterValueType = typename TInputTransformType::FixedParametersValueType; using OutputParameterValueType = typename TOutputTransformType::ParametersValueType; using OutputFixedParameterValueType = typename TOutputTransformType::FixedParametersValueType; /* This function gets the type name of the input transform and creates a new transform object based on the requested precision type. */ static OutputTransformPointer CreateNewTypeTransform(std::string transformName) { // Transform name is modified to have the output precision type. TransformIOBaseTemplate::CorrectTransformPrecisionType(transformName); // Instantiate the transform LightObject::Pointer i = ObjectFactoryBase::CreateInstance(transformName.c_str()); OutputTransformPointer convertedTransform = dynamic_cast(i.GetPointer()); if (convertedTransform.IsNull()) { itkGenericExceptionMacro("Could not create an instance of " << transformName); } convertedTransform->UnRegister(); return convertedTransform; } /* Converts the value type of transform parameters to the output precision type */ static OptimizerParameters ConvertParametersType(const OptimizerParameters & sourceParams) { OptimizerParameters outputParams; outputParams.SetSize(sourceParams.GetSize()); for (SizeValueType i = 0; i < sourceParams.GetSize(); ++i) { outputParams[i] = static_cast(sourceParams[i]); } return outputParams; } /* Converts the value type of transform parameters to the output precision type */ static OptimizerParameters ConvertFixedParametersType(const OptimizerParameters & sourceParams) { OptimizerParameters outputParams; outputParams.SetSize(sourceParams.GetSize()); for (SizeValueType i = 0; i < sourceParams.GetSize(); ++i) { outputParams[i] = static_cast(sourceParams[i]); } return outputParams; } /* Set fixed parameters and parameters of the new type created transform */ static void SetAllParameters(const TInputTransformType * transform, OutputTransformPointer & convertedTransform) { // The precision type of the input transform parameters should be converted to the requested output precision convertedTransform->SetFixedParameters(ConvertFixedParametersType(transform->GetFixedParameters())); convertedTransform->SetParameters(ConvertParametersType(transform->GetParameters())); } }; } // namespace namespace { /* Changes the precision type of input transform to the requested precision type */ template inline void AddToTransformList(typename TInputTransformType::ConstPointer & transform, std::list & transformList) { using InputParameterValueType = typename TInputTransformType::ParametersValueType; using OutputParameterValueType = typename TOutputTransformType::ParametersValueType; /* Pushes the converted transform to the input transform list */ using InputTransformType = TransformBaseTemplate; using InputTransformConstPointer = typename InputTransformType::ConstPointer; using InputConstTransformListType = std::list; using OutputTransformType = TransformBaseTemplate; using OutputTransformPointer = typename OutputTransformType::Pointer; using OutputTransformConstPointer = typename OutputTransformType::ConstPointer; using OutputTransformListType = std::list; const std::string transformName = transform->GetTransformTypeAsString(); OutputTransformPointer convertedTransform; using IOhelper = TransformIOHelper; // Composite and DisplacementFieldTransform transforms should be treated differently. if (transformName.find("CompositeTransform") == std::string::npos) { convertedTransform = IOhelper::CreateNewTypeTransform(transformName); IOhelper::SetAllParameters(transform, convertedTransform); } else { /* Following steps are needed to process a composite transform: 1) Use the compositeTransformIOHelper to get the input transforms list. 2) Iterate through the input transform list, convert each sub transform and put them in the output transform list. 3) Use a composite IO Helper agian to set the output transform list into the converted composite transform. */ CompositeTransformIOHelperTemplate inputHelper; InputConstTransformListType inputTransformList = inputHelper.GetTransformList(transform); // create output transform list OutputTransformListType compositeTransformList; /* The first transform of the output transform list should be a composite transform we push back just an empty composite transform as it will be skipped by the outputHelper. */ OutputTransformPointer outputComposite = IOhelper::CreateNewTypeTransform(transformName); compositeTransformList.push_back(outputComposite); // Now we iterate through input list and convert each sub transform to a new transform with requested precision // type. auto it = inputTransformList.begin(); // composite transform is the first transform of the input transform list ++it; // skip the composite transform for (; it != inputTransformList.end(); ++it) { // get the input sub transform const InputTransformType * const inSub = it->GetPointer(); // convert each sub transform and push them to the output transform list std::string inSubName = inSub->GetTransformTypeAsString(); OutputTransformPointer convertedSub = IOhelper::CreateNewTypeTransform(inSubName); IOhelper::SetAllParameters(inSub, convertedSub); // push back the converted sub transform to the composite transform list compositeTransformList.push_back(convertedSub); } convertedTransform = IOhelper::CreateNewTypeTransform(transformName); // new composite transform CompositeTransformIOHelperTemplate outputHelper; // set the output transform list into the new composite transform outputHelper.SetTransformList(convertedTransform, compositeTransformList); } transformList.push_back(OutputTransformConstPointer(convertedTransform)); } template <> // If types match, no conversion void AddToTransformList, TransformBaseTemplate>( TransformBaseTemplate::ConstPointer & transform, std::list::ConstPointer> & transformList) { transformList.push_back(transform); } template <> // If types match, no conversion void AddToTransformList, TransformBaseTemplate>( TransformBaseTemplate::ConstPointer & transform, std::list::ConstPointer> & transformList) { transformList.push_back(transform); } } // namespace template <> void TransformFileWriterTemplate::PushBackTransformList(const Object * transObj) { TransformBaseTemplate::ConstPointer dblptr = dynamic_cast *>(transObj); if (dblptr.IsNotNull()) { AddToTransformList, TransformBaseTemplate>(dblptr, m_TransformList); } else { TransformBaseTemplate::ConstPointer fltptr = dynamic_cast *>(transObj); if (fltptr.IsNotNull()) { AddToTransformList, TransformBaseTemplate>(fltptr, m_TransformList); } else { itkExceptionMacro("The input of writer should be either a double precision " "or a single precision transform type. Called from " "TransformFileWriterTemplate::PushBackTransformList(...) "); } } } template <> void TransformFileWriterTemplate::PushBackTransformList(const Object * transObj) { TransformBaseTemplate::ConstPointer dblptr = dynamic_cast *>(transObj); if (dblptr.IsNotNull()) { AddToTransformList, TransformBaseTemplate>(dblptr, m_TransformList); } else { TransformBaseTemplate::ConstPointer fltptr = dynamic_cast *>(transObj); if (fltptr.IsNotNull()) { AddToTransformList, TransformBaseTemplate>(fltptr, m_TransformList); } else { itkExceptionMacro("The input of writer should be whether a double precision " "or a single precision transform type. Called from " "TransformFileWriterTemplate::PushBackTransformList(...) "); } } } ITK_GCC_PRAGMA_DIAG_PUSH() ITK_GCC_PRAGMA_DIAG(ignored "-Wattributes") template class ITKIOTransformBase_EXPORT TransformFileWriterTemplate; template class ITKIOTransformBase_EXPORT TransformFileWriterTemplate; ITK_GCC_PRAGMA_DIAG_POP() } // namespace itk