diff --git a/Code/Algorithms/Elastix/include/mapElxCLIRegistrationAlgorithmBase.tpp b/Code/Algorithms/Elastix/include/mapElxCLIRegistrationAlgorithmBase.tpp index 37234d0..cdc5ab6 100644 --- a/Code/Algorithms/Elastix/include/mapElxCLIRegistrationAlgorithmBase.tpp +++ b/Code/Algorithms/Elastix/include/mapElxCLIRegistrationAlgorithmBase.tpp @@ -1,742 +1,742 @@ // ----------------------------------------------------------------------- // MatchPoint - DKFZ translational registration framework // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See mapCopyright.txt or // http://www.dkfz.de/en/sidt/projects/MatchPoint/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) // Subversion HeadURL: $HeadURL$ */ #ifndef __MAP_ELX_CLI_REGISTRATION_ALGORITHM_BASE_TPP #define __MAP_ELX_CLI_REGISTRATION_ALGORITHM_BASE_TPP #include "itkSpatialObjectToImageFilter.h" #include "itkImage.h" #include "mapAlgorithmException.h" #include "mapPreCachedRegistrationKernel.h" #include "mapInverseRegistrationKernelGenerator.h" #include "mapRegistrationManipulator.h" #include "mapAlgorithmWrapperEvent.h" #include "mapImageWriter.h" #include "mapProcessExecutor.h" #include "mapConvert.h" #include "mapMetaProperty.h" #include "mapString.h" #include "mapFieldByFileLoadFunctor.h" #include "mapFieldDecomposer.h" namespace map { namespace algorithm { namespace elastix { template typename CLIRegistrationAlgorithmBase::FieldRepRequirement::Type CLIRegistrationAlgorithmBase:: isMovingRepresentationRequired() const { return FieldRepRequirement::No; }; template typename CLIRegistrationAlgorithmBase::FieldRepRequirement::Type CLIRegistrationAlgorithmBase:: isTargetRepresentationRequired() const { return FieldRepRequirement::No; }; template bool CLIRegistrationAlgorithmBase:: isStoppable() const { return false; }; template typename CLIRegistrationAlgorithmBase::IterationCountType CLIRegistrationAlgorithmBase:: doGetCurrentIteration() const { return 0; }; template typename CLIRegistrationAlgorithmBase::IterationCountType CLIRegistrationAlgorithmBase:: doGetMaxIterations() const { return 0; }; template bool CLIRegistrationAlgorithmBase:: hasIterationCount() const { return false; }; template bool CLIRegistrationAlgorithmBase:: hasMaxIterationCount() const { return false; }; template bool CLIRegistrationAlgorithmBase:: hasCurrentOptimizerValue() const { return false; }; template CLIRegistrationAlgorithmBase:: CLIRegistrationAlgorithmBase() : _deleteTempDirectory(true) { _spInternalMovingImage = NULL; _spInternalTargetImage = NULL; }; template CLIRegistrationAlgorithmBase:: ~CLIRegistrationAlgorithmBase() { }; template bool CLIRegistrationAlgorithmBase:: isReusable() const { return true; }; template void CLIRegistrationAlgorithmBase:: configureAlgorithm() { if (this->isFirstConfiguration()) { _elastixDir = ""; core::String envDir = ""; if (itksys::SystemTools::GetEnv("MAP_ELASTIX_PATH", envDir)) { _elastixDir = envDir; } _workingDir = itksys::SystemTools::GetCurrentWorkingDirectory(); _deleteTempDirectory = true; } }; template void CLIRegistrationAlgorithmBase:: compileInfos(MetaPropertyVectorType& infos) const { #ifndef MAP_SEAL_ALGORITHMS infos.push_back(map::algorithm::MetaPropertyInfo::New("WorkingDirectory", typeid(map::core::String), true, true)); infos.push_back(map::algorithm::MetaPropertyInfo::New("ElastixDirectory", typeid(map::core::String), true, true)); infos.push_back(map::algorithm::MetaPropertyInfo::New("DeleteTempDirectory", typeid(bool), true, true)); #endif }; template typename CLIRegistrationAlgorithmBase::MetaPropertyPointer CLIRegistrationAlgorithmBase:: doGetProperty(const MetaPropertyNameType& name) const { MetaPropertyPointer spResult; if (name == "WorkingDirectory") { spResult = map::core::MetaProperty::New(this->_workingDir); } else if (name == "ElastixDirectory") { spResult = map::core::MetaProperty::New(this->_elastixDir); } else if (name == "DeleteTempDirectory") { spResult = map::core::MetaProperty::New(this->_deleteTempDirectory); } else { assert(false); //any other property name should have been excluded by the calling function. } return spResult; }; template void CLIRegistrationAlgorithmBase:: doSetProperty(const MetaPropertyNameType& name, const MetaPropertyType* pProperty) { if (name == "WorkingDirectory") { core::String dir; map::core::unwrapMetaProperty(pProperty, dir); this->_workingDir = dir; } else if (name == "ElastixDirectory") { core::String dir; map::core::unwrapMetaProperty(pProperty, dir); this->_elastixDir = dir; } else if (name == "DeleteTempDirectory") { bool del; map::core::unwrapMetaProperty(pProperty, del); this->_deleteTempDirectory = del; } else { assert(false); //any other property name should have been excluded by the calling function. } }; template typename CLIRegistrationAlgorithmBase::InterimRegistrationPointer CLIRegistrationAlgorithmBase:: determineInterimRegistration(const MovingRepresentationDescriptorType* pMovingRepresentation, const TargetRepresentationDescriptorType* pTargetRepresentation) const { - InterimRegistrationPointer spResult = NULL; + InterimRegistrationPointer spResult = nullptr; return spResult; }; template bool CLIRegistrationAlgorithmBase:: doStopAlgorithm() { assert(false); mapExceptionMacro(AlgorithmException, << "Cannot stop Elastix algorithm. Interim stop feature is not supported. Wrong usage of iterative algorithm interface"); }; template void CLIRegistrationAlgorithmBase:: initializeCurrentTempDir() { srand(time(NULL)); core::OStringStream stream; stream << itksys::SystemTools::GetCurrentDateTime("%Y-%m-%d_%H-%M-%S") << "_" << rand(); core::String currentTempDir = core::FileDispatch::createFullPath(_workingDir, stream.str()); if (!itksys::SystemTools::MakeDirectory(currentTempDir.c_str())) { mapExceptionMacro(AlgorithmException, << "Cannot create temporary working sub dir. Please check validity of given working dir and ensure right privileges for the application. Failed temporary sub dir: " << _currentTempDir); } _currentTempDir = currentTempDir; }; template void CLIRegistrationAlgorithmBase:: prepareAlgorithm() { try { this->prepCheckValidity(); this->_spFinalizedRegistration = NULL; //initialize registration components this->InvokeEvent(events::AlgorithmEvent(this, "Transfer cached MetaProperties.")); this->configureAlgorithmByMetaProperties(); this->InvokeEvent(events::AlgorithmEvent(this, "Initializing registration.")); this->initializeCurrentTempDir(); //preparing data this->InvokeEvent(events::AlgorithmEvent(this, "Initializing/Preparing input data.")); _spInternalMovingImage = this->getMovingImage(); _spInternalTargetImage = this->getTargetImage(); this->prepPerpareInternalInputData(); //storing temporary images this->InvokeEvent(events::AlgorithmEvent(this, "Passing input data to elastix working directory.")); this->prepSaveElastixInputData(); this->InvokeEvent(events::AlgorithmEvent(this, "Generating parameter maps for elastix.")); this->prepParameterMaps(); if (this->_parameterMaps.empty()) { mapExceptionMacro(AlgorithmException, << "Cannot start algorithm; no parameter maps are defined."); } unsigned int stageNR = 0; for (ParameterMapVectorType::const_iterator pos = this->_parameterMaps.begin(); pos != this->_parameterMaps.end(); ++pos, ++stageNR) { this->InvokeEvent(events::AlgorithmEvent(this, "Store parameter map for stage #" + map::core::convert::toStr(stageNR) + ".")); saveParameterMapToFile(*pos, this->getParameterFilePath(stageNR)); } } catch (...) { cleanTempDir(); throw; } }; template void CLIRegistrationAlgorithmBase:: cleanTempDir() const { try { if (itksys::SystemTools::FileExists(_currentTempDir.c_str()) && this->_deleteTempDirectory) { itksys::SystemTools::RemoveADirectory(_currentTempDir.c_str()); } } catch (...) { mapLogWarningObjMacro( << "Cannot clean up. Exception while removing the directory. Directory" << this->_currentTempDir); } }; template void CLIRegistrationAlgorithmBase:: prepCheckValidity() { if (!this->getMovingImage()) { mapExceptionMacro(AlgorithmException, << "Cannot start algorithm; no moving image."); } if (!this->getTargetImage()) { mapExceptionMacro(AlgorithmException, << "Cannot start algorithm; no target image."); } } template void CLIRegistrationAlgorithmBase:: prepPerpareInternalInputData() { //default implementation does nothing } template void saveTempImage(const TImage* image, const core::String& filePath) { typedef typename io::ImageWriter WriterType; typename WriterType::Pointer spWriter = WriterType::New(); spWriter->setInput(image); spWriter->setFilePath(filePath); spWriter->update(); }; template typename ::itk::Image::Pointer generateMaskImage(const ::itk::SpatialObject* so, const core::FieldRepresentationDescriptor* descriptor) { assert(so); assert(descriptor); typedef ::itk::Image MaskImageType; typedef ::itk::SpatialObject ObjectType; typedef ::itk::SpatialObjectToImageFilter FilterType; typename FilterType::Pointer spFilter = FilterType::New(); spFilter->SetInput(so); typename MaskImageType::SizeType size; for (unsigned long i = 0; i < VDimension; ++i) { size[i] = static_cast (descriptor->getSize()[i] / descriptor->getSpacing()[i]); } spFilter->SetSize(size); spFilter->SetSpacing(descriptor->getSpacing()); spFilter->SetOrigin(descriptor->getOrigin()); spFilter->SetDirection(descriptor->getDirection()); spFilter->Update(); spFilter->SetInsideValue(1); spFilter->SetOutsideValue(0); return spFilter->GetOutput(); }; template void CLIRegistrationAlgorithmBase:: prepSaveElastixInputData() { //save the images _movingImageTempPath = core::FileDispatch::createFullPath(_currentTempDir, "moving.mhd"); _targetImageTempPath = core::FileDispatch::createFullPath(_currentTempDir, "target.mhd"); _finalFieldTempPath = core::FileDispatch::createFullPath(_currentTempDir, "deformationField.mhd"); _movingMaskTempPath = ""; _targetMaskTempPath = ""; this->InvokeEvent(events::AlgorithmEvent(this, "Write temporary moving image. Path: " + _movingImageTempPath)); saveTempImage(_spInternalMovingImage.GetPointer(), _movingImageTempPath); this->InvokeEvent(events::AlgorithmEvent(this, "Write temporary target image. Path: " + _targetImageTempPath)); saveTempImage(_spInternalTargetImage.GetPointer(), _targetImageTempPath); //save the masks if (this->getMovingMask().IsNotNull()) { //add moving mask typedef itk::Image MaskImageType; MaskImageType::Pointer spMovingMaskImage = generateMaskImage (this->getMovingMask(), core::createFieldRepresentation(*(_spInternalMovingImage)).GetPointer()); _movingMaskTempPath = core::FileDispatch::createFullPath(_currentTempDir, "movingMask.mhd"); this->InvokeEvent(events::AlgorithmEvent(this, "Write temporary moving mask image. Path: " + _movingMaskTempPath)); saveTempImage(spMovingMaskImage.GetPointer(), _movingMaskTempPath); } if (this->getTargetMask().IsNotNull()) { //add target mask typedef itk::Image MaskImageType; MaskImageType::Pointer spTargetMaskImage = generateMaskImage (this->getTargetMask(), core::createFieldRepresentation(*(_spInternalTargetImage)).GetPointer()); _targetMaskTempPath = core::FileDispatch::createFullPath(_currentTempDir, "targetMask.mhd"); this->InvokeEvent(events::AlgorithmEvent(this, "Write temporary target mask image. Path: " + _targetMaskTempPath)); saveTempImage(spTargetMaskImage.GetPointer(), _targetMaskTempPath); } } template bool CLIRegistrationAlgorithmBase:: runAlgorithm() { map::utilities::ProcessExecutor::Pointer spExec = map::utilities::ProcessExecutor::New(); typename ::itk::MemberCommand::Pointer spCommand = ::itk::MemberCommand::New(); spCommand->SetCallbackFunction(this, &Self::onElxOutputEvent); spExec->AddObserver(map::events::ExternalProcessStdOutEvent(), spCommand); map::utilities::ProcessExecutor::ArgumentListType args; args.push_back("-m"); args.push_back(_movingImageTempPath); args.push_back("-f"); args.push_back(_targetImageTempPath); args.push_back("-out"); args.push_back(_currentTempDir); if (this->getMovingMask().IsNotNull()) { //add moving mask args.push_back("-mMask"); args.push_back(_movingMaskTempPath); } if (this->getTargetMask().IsNotNull()) { //add target mask args.push_back("-tMask"); args.push_back(_targetMaskTempPath); } unsigned int stageNR = 0; for (ParameterMapVectorType::const_iterator pos = this->_parameterMaps.begin(); pos != this->_parameterMaps.end(); ++pos, ++stageNR) { args.push_back("-p"); args.push_back(this->getParameterFilePath(stageNR)); } core::OStringStream ostr; ostr << "Calling elastix (" << _elastixDir << ") with arguments:"; for (map::utilities::ProcessExecutor::ArgumentListType::const_iterator pos = args.begin(); pos != args.end(); ++pos) { ostr << " " << *pos; } this->InvokeEvent(events::AlgorithmEvent(this, ostr.str())); if (!spExec->execute(_elastixDir, core::FileDispatch::createFullPath(_elastixDir, "elastix"), args)) { mapExceptionMacro(AlgorithmException, << "Error when executing elastix to determine the registration."); } return spExec->getExitValue() == 0; }; template typename CLIRegistrationAlgorithmBase::FinalFieldPointer CLIRegistrationAlgorithmBase:: generateField() const { map::utilities::ProcessExecutor::Pointer spExec = map::utilities::ProcessExecutor::New(); map::utilities::ProcessExecutor::ArgumentListType args; args.push_back("-def"); args.push_back("all"); args.push_back("-out"); args.push_back(_currentTempDir); args.push_back("-tp"); args.push_back(this->getFinalTransformFilePath()); core::OStringStream ostr; ostr << "Calling transformix (" << _elastixDir << ") with arguments:"; for (map::utilities::ProcessExecutor::ArgumentListType::const_iterator pos = args.begin(); pos != args.end(); ++pos) { ostr << " " << *pos; } this->InvokeEvent(events::AlgorithmEvent(NULL, ostr.str())); if (!spExec->execute(_elastixDir, core::FileDispatch::createFullPath(_elastixDir, "transformix"), args)) { mapExceptionMacro(AlgorithmException, << "Error when executing transformix to generate the deformation field."); } typedef map::core::functors::FieldByFileLoadFunctor FunctorType; FunctorType::Pointer spFunctor = FunctorType::New(_finalFieldTempPath); FinalFieldPointer spField; ::map::core::FieldDecomposer::decomposeTransform(spFunctor->generateTransform(), spField); if (spField.IsNull()) { mapExceptionMacro(AlgorithmException, << "Error when loading transformix deformation field. File name: " << _finalFieldTempPath); } return spField; }; template core::String map::algorithm::elastix::CLIRegistrationAlgorithmBase:: getParameterFilePath(unsigned int stageNr) const { core::OStringStream ostr; ostr << "parameters_stage_#" << stageNr << ".txt"; core::String result = core::FileDispatch::createFullPath(_currentTempDir, ostr.str()); return result; }; template core::String map::algorithm::elastix::CLIRegistrationAlgorithmBase:: getFinalTransformFilePath() const { if (this->_parameterMaps.empty()) { mapExceptionMacro(AlgorithmException, << "Cannot determine final transform file path; no parameter maps are defined."); } core::OStringStream ostr; ostr << "TransformParameters." << this->_parameterMaps.size() - 1 << ".txt"; core::String result = core::FileDispatch::createFullPath(_currentTempDir, ostr.str()); return result; }; template void CLIRegistrationAlgorithmBase:: finalizeAlgorithm() { - RegistrationPointer spResult = NULL; + RegistrationPointer spResult = nullptr; try { _spFinalizedField = this->generateField(); typedef typename map::core::PreCachedRegistrationKernel InverseKernelType; typename FieldTransformType::Pointer transform = FieldTransformType::New(); transform->SetDisplacementField(_spFinalizedField.GetPointer()); typename InverseKernelType::Pointer spIKernel = InverseKernelType::New(); spIKernel->setTransformModel(transform); //now build the direct kernel via inversion of the inverse kernel typedef core::InverseRegistrationKernelGenerator < RegistrationType::TargetDimensions, RegistrationType::MovingDimensions > GeneratorType; typename GeneratorType::Pointer spGenerator = GeneratorType::New(); typedef typename GeneratorType::InverseKernelBaseType DirectKernelType; typename Superclass::MovingRepresentationDescriptorType::ConstPointer spMovingRep = ::map::core::createFieldRepresentation(*(this->getMovingImage())).GetPointer(); if (this->getMovingRepresentation()) { //user has defined a representation descriptor -> use this one spMovingRep = this->getMovingRepresentation(); } typename DirectKernelType::Pointer spDKernel = spGenerator->generateInverse(* (spIKernel.GetPointer()), spMovingRep); if (spDKernel.IsNull()) { mapExceptionMacro(AlgorithmException, << "Error. Cannot determine direct mapping kernel of final registration. Current inverse kernel: " << spIKernel); } //now create the registration and set the kernels spResult = RegistrationType::New(); ::map::core::RegistrationManipulator manipulator(spResult); manipulator.setDirectMapping(spDKernel); manipulator.setInverseMapping(spIKernel); manipulator.getTagValues()[tags::AlgorithmUID] = this->getUID()->toStr(); _spFinalizedRegistration = spResult; } catch (...) { //delete temp dir even in case of an exception cleanTempDir(); throw; } //delete temp dir cleanTempDir(); _currentTempDir = ""; }; template typename CLIRegistrationAlgorithmBase::RegistrationPointer CLIRegistrationAlgorithmBase:: doGetRegistration() const { return _spFinalizedRegistration; }; template bool CLIRegistrationAlgorithmBase:: registrationIsOutdated() const { bool outdated = _spFinalizedRegistration.IsNull(); if (_spFinalizedRegistration.IsNotNull()) { if (!outdated) { //check if the inputs have been changed outdated = this->_spInternalMovingImage != this->getMovingImage(); } if (!outdated) { //check if the inputs have been changed outdated = this->_spInternalTargetImage != this->getTargetImage(); } } return outdated; }; template typename CLIRegistrationAlgorithmBase::OptimizerMeasureType CLIRegistrationAlgorithmBase:: doGetCurrentOptimizerValue() const { OptimizerMeasureType result; return result; }; template void CLIRegistrationAlgorithmBase:: PrintSelf(std::ostream& os, ::itk::Indent indent) const { Superclass::PrintSelf(os, indent); ImageRegistrationAlgorithmBase::PrintSelf(os, indent); os << indent << "Finalized registration: " << _spFinalizedRegistration << std::endl; }; template void CLIRegistrationAlgorithmBase:: onElxOutputEvent(::itk::Object* caller, const ::itk::EventObject& eventObject) { const events::ExternalProcessStdOutEvent* pStdEvent = dynamic_cast(&eventObject); if (pStdEvent) { this->InvokeEvent(events::AlgorithmIterationEvent(this, pStdEvent->getComment())); } } } // end namespace elastix } // end namespace algorithm } // end namespace map #endif diff --git a/Code/Algorithms/Elastix/source/ParameterFileParser/itkParameterFileParser.cxx b/Code/Algorithms/Elastix/source/ParameterFileParser/itkParameterFileParser.cxx index 737e509..1e89b89 100644 --- a/Code/Algorithms/Elastix/source/ParameterFileParser/itkParameterFileParser.cxx +++ b/Code/Algorithms/Elastix/source/ParameterFileParser/itkParameterFileParser.cxx @@ -1,477 +1,478 @@ /*========================================================================= * * Copyright UMC Utrecht and contributors * * 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 __itkParameterFileParser_cxx #define __itkParameterFileParser_cxx #include "itkParameterFileParser.h" #include #include +#include + namespace itk { /** * **************** Constructor *************** */ ParameterFileParser ::ParameterFileParser() { this->m_ParameterFileName = ""; this->m_ParameterMap.clear(); } // end Constructor() /** * **************** Destructor *************** */ ParameterFileParser ::~ParameterFileParser() { if( this->m_ParameterFile.is_open() ) { this->m_ParameterFile.close(); } } // end Destructor() /** * **************** GetParameterMap *************** */ const ParameterFileParser::ParameterMapType & ParameterFileParser ::GetParameterMap( ) const { return this->m_ParameterMap; } // end GetParameterMap() /** * **************** ReadParameterFile *************** */ void ParameterFileParser ::ReadParameterFile( ) { /** Perform some basic checks. */ this->BasicFileChecking(); /** Open the parameter file for reading. */ if( this->m_ParameterFile.is_open() ) { this->m_ParameterFile.clear(); this->m_ParameterFile.close(); } this->m_ParameterFile.open( this->m_ParameterFileName.c_str(), std::fstream::in ); /** Check if it opened. */ if( !this->m_ParameterFile.is_open() ) { itkExceptionMacro( << "ERROR: could not open " << this->m_ParameterFileName << " for reading." ); } /** Clear the map. */ this->m_ParameterMap.clear(); /** Loop over the parameter file, line by line. */ std::string lineIn; std::string lineOut; while( this->m_ParameterFile.good() ) { /** Extract a line. */ itksys::SystemTools::GetLineFromStream( this->m_ParameterFile, lineIn ); /** Check this line. */ bool validLine = this->CheckLine( lineIn, lineOut ); if( validLine ) { /** Get the parameter name from this line and store it. */ this->GetParameterFromLine( lineIn, lineOut ); } // Otherwise, we simply ignore this line } /** Close the parameter file. */ this->m_ParameterFile.clear(); this->m_ParameterFile.close(); } // end ReadParameterFile() /** * **************** BasicFileChecking *************** */ void ParameterFileParser ::BasicFileChecking( ) const { /** Check if the file name is given. */ if( this->m_ParameterFileName.empty() ) { itkExceptionMacro( << "ERROR: FileName has not been set." ); } /** Basic error checking: existence. */ bool exists = itksys::SystemTools::FileExists( this->m_ParameterFileName.c_str() ); if( !exists ) { itkExceptionMacro( << "ERROR: the file " << this->m_ParameterFileName << " does not exist." ); } /** Basic error checking: file or directory. */ bool isDir = itksys::SystemTools::FileIsDirectory( this->m_ParameterFileName ); if( isDir ) { itkExceptionMacro( << "ERROR: the file " << this->m_ParameterFileName << " is a directory." ); } /** Check the extension. */ std::string ext = itksys::SystemTools::GetFilenameLastExtension( this->m_ParameterFileName ); if( ext != ".txt" ) { itkExceptionMacro( << "ERROR: the file " << this->m_ParameterFileName << " should be a text file (*.txt)." ); } } // end BasicFileChecking() /** * **************** CheckLine *************** */ bool ParameterFileParser ::CheckLine( const std::string & lineIn, std::string & lineOut ) const { /** Preprocessing of lineIn: * 1) Replace tabs with spaces * 2) Remove everything after comment sign // * 3) Remove leading spaces * 4) Remove trailing spaces */ lineOut = lineIn; itksys::SystemTools::ReplaceString( lineOut, "\t", " " ); itksys::RegularExpression commentPart( "//" ); if( commentPart.find( lineOut ) ) { lineOut = lineOut.substr( 0, commentPart.start() ); } itksys::RegularExpression leadingSpaces( "^[ ]*(.*)" ); leadingSpaces.find( lineOut ); lineOut = leadingSpaces.match( 1 ); itksys::RegularExpression trailingSpaces( "[ \t]+$" ); if( trailingSpaces.find( lineOut ) ) { lineOut = lineOut.substr( 0, trailingSpaces.start() ); } /** * Checks: * 1. Empty line -> false * 2. Comment (line starts with "//") -> false * 3. Line is not between brackets (...) -> exception * 4. Line contains less than two words -> exception * * Otherwise return true. */ /** 1. Check for non-empty lines. */ itksys::RegularExpression reNonEmptyLine( "[^ ]+" ); bool match1 = reNonEmptyLine.find( lineOut ); if( !match1 ) { return false; } /** 2. Check for comments. */ itksys::RegularExpression reComment( "^//" ); bool match2 = reComment.find( lineOut ); if( match2 ) { return false; } /** 3. Check if line is between brackets. */ if( !itksys::SystemTools::StringStartsWith( lineOut.c_str(), "(" ) || !itksys::SystemTools::StringEndsWith( lineOut.c_str(), ")" ) ) { std::string hint = "Line is not between brackets: \"(...)\"."; this->ThrowException( lineIn, hint ); } /** Remove brackets. */ lineOut = lineOut.substr( 1, lineOut.size() - 2 ); /** 4. Check: the line should contain at least two words. */ itksys::RegularExpression reTwoWords( "([ ]+)([^ ]+)" ); bool match4 = reTwoWords.find( lineOut ); if( !match4 ) { std::string hint = "Line does not contain a parameter name and value."; this->ThrowException( lineIn, hint ); } /** At this point we know its at least a line containing a parameter. * However, this line can still be invalid, for example: * (string &^%^*) * This will be checked later. */ return true; } // end CheckLine() /** * **************** GetParameterFromLine *************** */ void ParameterFileParser ::GetParameterFromLine( const std::string & fullLine, const std::string & line ) { /** A line has a parameter name followed by one or more parameters. * They are all separated by one or more spaces (all tabs have been * removed previously) or by quotes in case of strings. So, * 1) we split the line at the spaces or quotes * 2) the first one is the parameter name * 3) the other strings that are not a series of spaces, are parameter values */ /** 1) Split the line. */ std::vector< std::string > splittedLine; this->SplitLine( fullLine, line, splittedLine ); /** 2) Get the parameter name. */ std::string parameterName = splittedLine[ 0 ]; itksys::SystemTools::ReplaceString( parameterName, " ", "" ); splittedLine.erase( splittedLine.begin() ); /** 3) Get the parameter values. */ std::vector< std::string > parameterValues; for( unsigned int i = 0; i < splittedLine.size(); ++i ) { if( !splittedLine[ i ].empty() ) { parameterValues.push_back( splittedLine[ i ] ); } } /** 4) Perform some checks on the parameter name. */ itksys::RegularExpression reInvalidCharacters1( "[.,:;!@#$%^&-+|<>?]" ); bool match = reInvalidCharacters1.find( parameterName ); if( match ) { std::string hint = "The parameter \"" + parameterName + "\" contains invalid characters (.,:;!@#$%^&-+|<>?)."; this->ThrowException( fullLine, hint ); } /** 5) Perform checks on the parameter values. */ itksys::RegularExpression reInvalidCharacters2( "[,;!@#$%&|<>?]" ); for(const auto & parameterValue : parameterValues) { /** For all entries some characters are not allowed. */ if( reInvalidCharacters2.find( parameterValue ) ) { std::string hint = "The parameter value \"" + parameterValue + "\" contains invalid characters (,;!@#$%&|<>?)."; this->ThrowException( fullLine, hint ); } } /** 6) Insert this combination in the parameter map. */ if( this->m_ParameterMap.count( parameterName ) != 0u ) { std::string hint = "The parameter \"" + parameterName + "\" is specified more than once."; this->ThrowException( fullLine, hint ); } else { this->m_ParameterMap.insert( make_pair( parameterName, parameterValues ) ); } } // end GetParameterFromLine() /** * **************** SplitLine *************** */ void ParameterFileParser ::SplitLine( const std::string & fullLine, const std::string & line, std::vector< std::string > & splittedLine ) const { splittedLine.clear(); splittedLine.resize( 1 ); - std::vector< itksys::String > splittedLine1; /** Count the number of quotes in the line. If it is an odd value, the * line contains an error; strings should start and end with a quote, so * the total number of quotes is even. */ std::size_t numQuotes = itksys::SystemTools::CountChar( line.c_str(), '"' ); if( numQuotes % 2 == 1 ) { /** An invalid parameter line. */ std::string hint = "This line has an odd number of quotes (\")."; this->ThrowException( fullLine, hint ); } /** Loop over the line. */ std::string::const_iterator it; unsigned int index = 0; numQuotes = 0; for( it = line.begin(); it < line.end(); ++it ) { if( *it == '"' ) { /** Start a new element. */ splittedLine.emplace_back("" ); index++; numQuotes++; } else if( *it == ' ' ) { /** Only start a new element if it is not a quote, otherwise just add * the space to the string. */ if( numQuotes % 2 == 0 ) { splittedLine.emplace_back("" ); index++; } else { splittedLine[ index ].push_back( *it ); } } else { /** Add this character to the element. */ splittedLine[ index ].push_back( *it ); } } } // end SplitLine() /** * **************** ThrowException *************** */ void ParameterFileParser ::ThrowException( const std::string & line, const std::string & hint ) const { /** Construct an error message. */ std::string errorMessage = "ERROR: the following line in your parameter file is invalid: \n\"" + line + "\"\n" + hint + "\nPlease correct you parameter file!"; /** Throw exception. */ itkExceptionMacro( << errorMessage.c_str() ); } // end ThrowException() /** * **************** ReturnParameterFileAsString *************** */ std::string ParameterFileParser ::ReturnParameterFileAsString( ) { /** Perform some basic checks. */ this->BasicFileChecking(); /** Open the parameter file for reading. */ if( this->m_ParameterFile.is_open() ) { this->m_ParameterFile.clear(); this->m_ParameterFile.close(); } this->m_ParameterFile.open( this->m_ParameterFileName.c_str(), std::fstream::in ); /** Check if it opened. */ if( !this->m_ParameterFile.is_open() ) { itkExceptionMacro( << "ERROR: could not open " << this->m_ParameterFileName << " for reading." ); } /** Loop over the parameter file, line by line. */ std::string line; std::string output; while( this->m_ParameterFile.good() ) { /** Extract a line. */ itksys::SystemTools::GetLineFromStream( this->m_ParameterFile, line ); // \todo: returns bool output += line + "\n"; } /** Close the parameter file. */ this->m_ParameterFile.clear(); this->m_ParameterFile.close(); /** Return the string. */ return output; } // end ReturnParameterFileAsString() } // end namespace itk #endif // end __itkParameterFileParser_cxx