Commit e3659387 authored by Niels Dekker's avatar Niels Dekker
Browse files

Merge branch 'develop' into SELX-126-Make-compilation-logs-public

parents a31cbe7e 08bd6edd
......@@ -23,12 +23,8 @@
#include "selxLogger.h"
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#include <boost/program_options.hpp>
namespace po = boost::program_options;
#include <iostream>
#include <algorithm>
......@@ -44,23 +40,47 @@ operator<<( std::ostream & os, const std::vector< T > & v )
return os;
}
namespace selx
{
// helper function to parse command line arguments for log level
std::istream& operator>>(std::istream& in, selx::LogLevel& loglevel)
{
std::string token;
in >> token;
if (token == "off")
loglevel = selx::LogLevel::OFF;
else if (token == "critical")
loglevel = selx::LogLevel::CRT;
else if (token == "error")
loglevel = selx::LogLevel::ERR;
else if (token == "warning")
loglevel = selx::LogLevel::WRN;
else if (token == "info")
loglevel = selx::LogLevel::INF;
else if (token == "debug")
loglevel = selx::LogLevel::DBG;
else if (token == "trace")
loglevel = selx::LogLevel::TRC;
else
in.setstate(std::ios_base::failbit);
return in;
}
}
int
main( int ac, char * av[] )
{
selx::Logger::Pointer logger = selx::Logger::New();
try
{
typedef std::vector< std::string > VectorOfStringsType;
typedef std::vector< boost::filesystem::path > VectorOfPathsType;
// instantiate a SuperElastixFilter that is loaded with default components
selx::SuperElastixFilter::Pointer superElastixFilter = selx::SuperElastixFilter::New();
// add logger
logger->AddStream("cout", std::cout);
logger->SetLogLevel(selx::LogLevel::TRC);
superElastixFilter->SetLogger(logger);
boost::filesystem::path logPath;
// default log level
selx::LogLevel logLevel = selx::LogLevel::WRN;
boost::filesystem::path configurationPath;
VectorOfPathsType configurationPaths;
......@@ -68,24 +88,20 @@ main( int ac, char * av[] )
VectorOfStringsType inputPairs;
VectorOfStringsType outputPairs;
// Store the reader so that they will not be destroyed before the pipeline is executed.
std::vector< selx::AnyFileReader::Pointer > fileReaders;
// Store the writers for the update call
//vector<ImageWriter2DType::Pointer> fileWriters;
std::vector< selx::AnyFileWriter::Pointer > fileWriters;
boost::program_options::options_description desc("Allowed options");
desc.add_options()
( "help", "produce help message" )
("conf", boost::program_options::value< VectorOfPathsType >(&configurationPaths)->required()->multitoken(), "Configuration file")
("in", boost::program_options::value< VectorOfStringsType >(&inputPairs)->multitoken(), "Input data: images, labels, meshes, etc. Usage <name>=<path>")
( "out", po::value< VectorOfStringsType >( &outputPairs )->multitoken(), "Output data: images, labels, meshes, etc. Usage <name>=<path>" )
( "graphout", po::value< fs::path >(), "Output Graphviz dot file" )
;
("out", boost::program_options::value< VectorOfStringsType >(&outputPairs)->multitoken(), "Output data: images, labels, meshes, etc. Usage <name>=<path>")
("graphout", boost::program_options::value< boost::filesystem::path >(), "Output Graphviz dot file")
("logfile", boost::program_options::value< boost::filesystem::path >(&logPath), "Log output file")
("loglevel", boost::program_options::value< selx::LogLevel >(&logLevel), "Log level")
;
po::variables_map vm;
po::store( po::parse_command_line( ac, av, desc ), vm );
po::notify( vm );
boost::program_options::variables_map vm;
boost::program_options::store(boost::program_options::parse_command_line(ac, av, desc), vm);
boost::program_options::notify(vm);
if( vm.count( "help" ) )
{
......@@ -93,8 +109,25 @@ main( int ac, char * av[] )
return 0;
}
// optionally, stream to log file
std::ofstream outfile;
if ( vm.count("logfile") )
{
outfile = std::ofstream(logPath.c_str());
logger->AddStream("logfile", outfile);
}
logger->AddStream("SELXcout", std::cout);
logger->SetLogLevel(logLevel);
// instantiate a SuperElastixFilter that is loaded with default components
selx::SuperElastixFilter::Pointer superElastixFilter = selx::SuperElastixFilter::New();
superElastixFilter->SetLogger(logger);
// create empty blueprint
selx::Blueprint::Pointer blueprint = selx::Blueprint::New();
blueprint->SetLogger(logger);
for (const auto & configurationPath : configurationPaths)
{
blueprint->MergeFromFile(configurationPath.string());
......@@ -102,13 +135,17 @@ main( int ac, char * av[] )
if( vm.count( "graphout" ) )
{
blueprint->Write(vm["graphout"].as< fs::path >().string());
blueprint->Write(vm["graphout"].as< boost::filesystem::path >().string());
}
//turn the blueprint into an itkObject to connect to the superElastix itkFilter
// The Blueprint needs to be set to superElastixFilter before GetInputFileReader and GetOutputFileWriter should be called.
superElastixFilter->SetBlueprint(blueprint);
// Store the readers so that they will not be destroyed before the pipeline is executed.
std::vector< selx::AnyFileReader::Pointer > fileReaders;
// Store the writers for the update call
std::vector< selx::AnyFileWriter::Pointer > fileWriters;
if( vm.count( "in" ) )
{
logger->Log( selx::LogLevel::INF, "Preparing input data ... ");
......@@ -120,8 +157,6 @@ main( int ac, char * av[] )
const std::string & name = nameAndPath[ 0 ];
const std::string & path = nameAndPath[ 1 ];
// since we do not know which reader type we should instantiate for input "name",
// we ask SuperElastix for a reader that matches the type of the source component "name"
logger->Log( selx::LogLevel::INF, "Preparing input " + name + " ..." );
......
## Coding conventions ##
* Adhere to style in the same file
* Separate STYLE commits from functional changes. STYLE commits don't need much attention from others, functional changes do.
* Some IDEs tend to apply their own coding style. This introduces unwanted STYLE changes if code-blocks are copy-pasted. Please turn off this feature.
* Preferably, use commit messages tags: ENH, BUG, COMP, PERF, STYLE.
### Suggested ###
* In general we adhere to a style similar to [elastix style](https://github.com/SuperElastix/elastix/blob/develop/CONTRIBUTING.md) and [itk style](https://www.vtk.org/Wiki/ITK/Coding_Style_Guide)
Thanks,
The `elastix` team
......@@ -22,6 +22,7 @@
#include "itkDataObject.h"
#include "itkObjectFactory.h"
#include "selxLogger.h"
#include <string>
#include <vector>
......@@ -101,9 +102,11 @@ public:
void MergeFromFile(const std::string& filename);
void SetLogger( Logger::Pointer logger );
private:
BlueprintImplPointer m_Blueprint;
Logger::Pointer m_Logger;
};
}
......
......@@ -26,7 +26,15 @@ namespace selx
Blueprint
::Blueprint()
{
this->m_Blueprint = BlueprintImplPointer( new BlueprintImpl() );
// Create default logger which redirects to std::cout
this->m_Logger = Logger::New();
//TODO: cannot have independent loggers redirecting to cout.
//this->m_Logger->AddStream("cout", std::cout);
//TODO: this seems to affect other instantiated loggers too.
//this->m_Logger->SetLogLevel(selx::LogLevel::INF);
this->m_Blueprint = BlueprintImplPointer( new BlueprintImpl( this->m_Logger->GetLoggerImpl() ) );
}
const BlueprintImpl &
......@@ -144,20 +152,20 @@ Blueprint
this->m_Blueprint->Write( filename );
}
/*
void
Blueprint
::FromFile( const std::string& filename )
::MergeFromFile( const std::string& filename )
{
this->m_Blueprint->FromFile( filename );
this->m_Blueprint->MergeFromFile( filename );
}
*/
void
Blueprint
::MergeFromFile( const std::string& filename )
::SetLogger( Logger::Pointer logger )
{
this->m_Blueprint->MergeFromFile( filename );
this->m_Logger = logger;
this->m_Blueprint->SetLoggerImpl( logger->GetLoggerImpl() );
}
} // namespace selx
......@@ -94,6 +94,15 @@ make_edge_label_writer( ParameterMapType p )
return edge_label_writer< ParameterMapType >( p );
}
// TODO: remove this argumentless constructor
BlueprintImpl::BlueprintImpl( void ) : m_LoggerImpl(&(Logger::New()->GetLoggerImpl()))
{
}
BlueprintImpl::BlueprintImpl( LoggerImpl & loggerImpl ) : m_LoggerImpl(&loggerImpl)
{
}
bool
BlueprintImpl
......@@ -427,28 +436,33 @@ void
BlueprintImpl::MergeFromFile(const std::string & fileNameString)
{
PathType fileName(fileNameString);
LoggerImpl logger;
logger.Log(LogLevel::INF, "Loading {0} ... ", fileName);
this->m_LoggerImpl->Log(LogLevel::INF, "Loading {0} ... ", fileName);
auto propertyTree = ReadPropertyTree(fileName);
logger.Log(LogLevel::INF, "Loading {0} ... Done", fileName);
this->m_LoggerImpl->Log(LogLevel::INF, "Loading {0} ... Done", fileName);
logger.Log(LogLevel::INF, "Checking {0} for include files ... ", fileName);
this->m_LoggerImpl->Log(LogLevel::INF, "Checking {0} for include files ... ", fileName);
auto includesList = FindIncludes(propertyTree);
if (includesList.size() > 0)
{
for (auto const & includePath : includesList)
{
logger.Log(LogLevel::INF, "Including file {0} ... ", includePath);
this->m_LoggerImpl->Log(LogLevel::INF, "Including file {0} ... ", includePath);
this->MergeFromFile(includePath.string());
}
}
logger.Log(LogLevel::INF, "Checking {0} for include files ... done", fileName);
this->m_LoggerImpl->Log(LogLevel::INF, "Checking {0} for include files ... done", fileName);
this->MergeProperties(propertyTree);
return;
}
void
BlueprintImpl::SetLoggerImpl( LoggerImpl & loggerImpl )
{
this->m_LoggerImpl = &loggerImpl;
}
BlueprintImpl::PropertyTreeType
BlueprintImpl::ReadPropertyTree(const PathType & filename)
{
......@@ -496,7 +510,6 @@ BlueprintImpl::FindIncludes(const PropertyTreeType & propertyTree)
Blueprint::Pointer
BlueprintImpl::FromPropertyTree(const PropertyTreeType & pt)
{
LoggerImpl logger;
Blueprint::Pointer blueprint = Blueprint::New();
BOOST_FOREACH(const PropertyTreeType::value_type & v, pt.equal_range("Component"))
......@@ -525,8 +538,7 @@ BlueprintImpl::FromPropertyTree(const PropertyTreeType & pt)
std::string connectionName = v.second.data();
if (connectionName != "")
{
// TODO: Why is it ignored?
logger.Log(LogLevel::WRN, "Connection {0} is ignored.", connectionName);
this->m_LoggerImpl->Log(LogLevel::INF, "Found {0}, but connection names are ignored.", connectionName);
}
std::string outName;
std::string inName;
......@@ -548,7 +560,7 @@ BlueprintImpl::FromPropertyTree(const PropertyTreeType & pt)
}
else if (connectionKey == "Name")
{
logger.Log(LogLevel::WRN, "Connections with key 'Name' are ignored.");
this->m_LoggerImpl->Log(LogLevel::WRN, "Connections with key 'Name' are ignored.");
continue;
}
else
......@@ -567,7 +579,6 @@ BlueprintImpl::FromPropertyTree(const PropertyTreeType & pt)
void
BlueprintImpl::MergeProperties(const PropertyTreeType & pt)
{
LoggerImpl logger;
BOOST_FOREACH(const PropertyTreeType::value_type & v, pt.equal_range("Component"))
{
std::string componentName;
......@@ -604,6 +615,7 @@ BlueprintImpl::MergeProperties(const PropertyTreeType & pt)
if (ownValues.size() != otherValues.size())
{
// No, based on the number of values we see that it is different. Blueprints cannot be Composed
this->m_LoggerImpl->Log(LogLevel::ERR, "Merging blueprints failed : Component properties cannot be redefined");
throw std::invalid_argument("Merging blueprints failed : Component properties cannot be redefined");
}
else
......@@ -615,6 +627,7 @@ BlueprintImpl::MergeProperties(const PropertyTreeType & pt)
if (*otherValue != *ownValue)
{
// No, at least one value is different. Blueprints cannot be Composed
this->m_LoggerImpl->Log(LogLevel::ERR, "Merging blueprints failed : Component properties cannot be redefined");
throw std::invalid_argument("Merging blueprints failed: Component properties cannot be redefined");
}
}
......@@ -641,7 +654,7 @@ BlueprintImpl::MergeProperties(const PropertyTreeType & pt)
std::string connectionName = v.second.data();
if (connectionName != "")
{
logger.Log(LogLevel::WRN, "Connection {0} is ignored.", connectionName);
this->m_LoggerImpl->Log(LogLevel::INF, "Found {0}, but connection names are ignored.", connectionName);
}
std::string outName;
std::string inName;
......@@ -663,7 +676,7 @@ BlueprintImpl::MergeProperties(const PropertyTreeType & pt)
}
else if (connectionKey == "Name")
{
logger.Log(LogLevel::WRN, "Connections with key 'Name' are ignored.");
this->m_LoggerImpl->Log(LogLevel::WRN, "Connections with key 'Name' are ignored.");
continue;
}
else
......@@ -691,6 +704,7 @@ BlueprintImpl::MergeProperties(const PropertyTreeType & pt)
if (ownValues.size() != otherValues.size())
{
// No, based on the number of values we see that it is different. Blueprints cannot be Composed
this->m_LoggerImpl->Log(LogLevel::ERR, "Merging blueprints failed : Component properties cannot be redefined");
throw std::invalid_argument("Merging blueprints failed: Component properties cannot be redefined");
}
else
......@@ -702,6 +716,7 @@ BlueprintImpl::MergeProperties(const PropertyTreeType & pt)
if (*otherValue != *ownValue)
{
// No, at least one value is different. Blueprints cannot be Composed
this->m_LoggerImpl->Log(LogLevel::ERR, "Merging blueprints failed : Component properties cannot be redefined");
throw std::invalid_argument("Merging blueprints failed: Component properties cannot be redefined");
}
}
......
......@@ -42,6 +42,7 @@
#include "selxBlueprint.h"
#include "selxLoggerImpl.h"
namespace selx
{
......@@ -98,6 +99,12 @@ public:
typedef boost::graph_traits< GraphType >::out_edge_iterator OutputIteratorType;
typedef std::pair< OutputIteratorType, OutputIteratorType > OutputIteratorPairType;
// TODO: remove this argumentless constructor
BlueprintImpl();
BlueprintImpl( LoggerImpl & loggerImpl);
bool SetComponent( ComponentNameType, ParameterMapType parameterMap );
ParameterMapType GetComponent( ComponentNameType componentName ) const;
......@@ -129,6 +136,8 @@ public:
void MergeFromFile(const std::string & filename);
void SetLoggerImpl( LoggerImpl & loggerImpl );
private:
typedef boost::property_tree::ptree PropertyTreeType;
......@@ -148,6 +157,8 @@ private:
void MergeProperties(const PropertyTreeType &);
GraphType m_Graph;
LoggerImpl * m_LoggerImpl;
};
} // namespace selx
......
/*=========================================================================
*
* Copyright Leiden University Medical Center, Erasmus University Medical
* Center 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 selxItkImageSourceFixedComponent_h
#define selxItkImageSourceFixedComponent_h
#include "selxSuperElastixComponent.h"
#include "selxSinksAndSourcesInterfaces.h"
#include "selxItkObjectInterfaces.h"
#include <string.h>
#include "itkImageFileReader.h"
#include "selxAnyFileReader.h"
#include "selxFileReaderDecorator.h"
namespace selx
{
template< int Dimensionality, class TPixel >
class ItkImageSourceFixedComponent :
public SuperElastixComponent<
Accepting< >,
Providing< SourceInterface,
itkImageFixedInterface< Dimensionality, TPixel >,
itkImageDomainFixedInterface< Dimensionality >>
>
{
public:
/** Standard ITK typedefs. */
typedef ItkImageSourceFixedComponent<
Dimensionality, TPixel
> Self;
typedef SuperElastixComponent<
Accepting< >,
Providing< SourceInterface,
itkImageFixedInterface< Dimensionality, TPixel >,
itkImageDomainFixedInterface< Dimensionality >>
> Superclass;
typedef std::shared_ptr< Self > Pointer;
typedef std::shared_ptr< const Self > ConstPointer;
ItkImageSourceFixedComponent( const std::string & name, LoggerImpl & logger );
virtual ~ItkImageSourceFixedComponent();
typedef typename itkImageFixedInterface< Dimensionality, TPixel >::ItkImageType ItkImageType;
typedef typename itkImageDomainFixedInterface< Dimensionality >::ItkImageDomainType ItkImageDomainType;
typedef typename itk::ImageFileReader< ItkImageType > ItkImageReaderType;
typedef FileReaderDecorator< ItkImageReaderType > DecoratedReaderType;
// providing interfaces
virtual typename ItkImageType::Pointer GetItkImageFixed() override;
virtual void SetMiniPipelineInput( itk::DataObject::Pointer ) override;
virtual AnyFileReader::Pointer GetInputFileReader( void ) override;
virtual typename ItkImageDomainType::Pointer GetItkImageDomainFixed() override;
virtual bool MeetsCriterion( const ComponentBase::CriterionType & criterion ) override;
static const char * GetDescription() { return "ItkImageSourceFixed Component"; }
private:
typename ItkImageType::Pointer m_Image;
protected:
// return the class name and the template arguments to uniquely identify this component.
static inline const std::map< std::string, std::string > TemplateProperties()
{
return { { keys::NameOfClass, "ItkImageSourceFixedComponent" }, { keys::PixelType, PodString< TPixel >::Get() }, { keys::Dimensionality, std::to_string( Dimensionality ) } };
}
};
} //end namespace selx
#ifndef ITK_MANUAL_INSTANTIATION
#include "selxItkImageSourceFixedComponent.hxx"
#endif
#endif // #define selxItkImageSourceFixedComponent_h
/*=========================================================================
*
* Copyright Leiden University Medical Center, Erasmus University Medical
* Center 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.
*
*=========================================================================*/
#include "selxItkImageSourceFixedComponent.h"
#include "selxCheckTemplateProperties.h"
namespace selx
{
template< int Dimensionality, class TPixel >
ItkImageSourceFixedComponent< Dimensionality, TPixel >
::ItkImageSourceFixedComponent( const std::string & name, LoggerImpl & logger ) : Superclass( name, logger ), m_Image( nullptr )
{
}
template< int Dimensionality, class TPixel >
ItkImageSourceFixedComponent< Dimensionality, TPixel >
::~ItkImageSourceFixedComponent()
{
}
template< int Dimensionality, class TPixel >
typename ItkImageSourceFixedComponent< Dimensionality, TPixel >::ItkImageType::Pointer
ItkImageSourceFixedComponent< Dimensionality, TPixel >
::GetItkImageFixed()
{
if( this->m_Image == nullptr )
{
throw std::runtime_error( "SourceComponent needs to be initialized by SetMiniPipelineInput()" );
}
return this->m_Image;
}
template< int Dimensionality, class TPixel >
void
ItkImageSourceFixedComponent< Dimensionality, TPixel >
::SetMiniPipelineInput( itk::DataObject::Pointer object )
{
this->m_Image = dynamic_cast< ItkImageType * >( object.GetPointer() );
if( this->m_Image == nullptr )
{
throw std::runtime_error( "DataObject passed by the NetworkBuilder is not of the right ImageType or not at all an ImageType" );
}
return;
}
template< int Dimensionality, class TPixel >
typename AnyFileReader::Pointer
ItkImageSourceFixedComponent< Dimensionality, TPixel >::GetInputFileReader()
{
// Instanstiate an image file reader, decorated such that it can be implicitly cast to an AnyFileReaderType
return DecoratedReaderType::New().GetPointer();
}
template< int Dimensionality, class TPixel >
typename ItkImageSourceFixedComponent< Dimensionality, TPixel >::ItkImageDomainType::Pointer
ItkImageSourceFixedComponent< Dimensionality, TPixel >
::GetItkImageDomainFixed()
{
if( this->m_Image == nullptr )
{
throw std::runtime_error( "SourceComponent needs to be initialized by SetMiniPipelineInput()" );
}
return this->m_Image.GetPointer();
}
template< int Dimensionality, class TPixel >
bool
ItkImageSourceFixedComponent< Dimensionality, TPixel >
::MeetsCriterion( const ComponentBase::CriterionType & criterion )
{