From aa9a6858c60b0c3d721457f8b46e81d3e3ec0f23 Mon Sep 17 00:00:00 2001 From: Niels Dekker Date: Tue, 25 Aug 2020 08:57:01 +0200 Subject: [PATCH] WIP: ENH: Executable linked to elastix.lib, subset of elastix.exe functions --- Core/CMakeLists.txt | 6 + Core/Main/ElastixLibBasedExe.cxx | 392 +++++++++++++++++++++++++++++++ 2 files changed, 398 insertions(+) create mode 100644 Core/Main/ElastixLibBasedExe.cxx diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 818d378dd..7ba61c0e9 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -158,6 +158,12 @@ set_target_properties( elastix_lib PROPERTIES OUTPUT_NAME elastix ) target_compile_definitions( elastix_lib PRIVATE ELX_CMAKE_VERSION="${CMAKE_VERSION}" ) target_link_libraries( elastix_lib ${ELASTIX_TARGET_LINK_LIBRARIES} ) +add_executable( ElastixLibBasedExe + Main/ElastixLibBasedExe.cxx +) +target_link_libraries( ElastixLibBasedExe elastix_lib ) +target_compile_definitions(ElastixLibBasedExe PRIVATE ELX_CMAKE_VERSION="${CMAKE_VERSION}") + #--------------------------------------------------------------------- # Create the transformix executable. diff --git a/Core/Main/ElastixLibBasedExe.cxx b/Core/Main/ElastixLibBasedExe.cxx new file mode 100644 index 000000000..ed5b3ba4e --- /dev/null +++ b/Core/Main/ElastixLibBasedExe.cxx @@ -0,0 +1,392 @@ +/*========================================================================= + * + * 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. + * + *=========================================================================*/ + +// Elastix header files: +#include "elastix.h" +#include "elastixlib.h" +#include "elxElastixMain.h" +#include "itkUseMevisDicomTiff.h" + +// ITK header files: +#include +#include +#include + +// Standard C++ header files: +#include +#include // For UINT_MAX. +#include // For size_t. +#include +#include +#include +#include + +namespace +{ + using ImageType = itk::Image; + + // TODO: Check GenerateImageContainer calls in ElastixMain::Run() for + // more extensive reading. + ImageType::Pointer ReadImage(const std::string& filename) + { + /** Setup reader. */ + const auto imageFileReader = itk::ImageFileReader::New(); + imageFileReader->SetFileName(filename); + imageFileReader->Update(); + return imageFileReader->GetOutput(); + } +} + +int +Main( const int argc, const char* const* const argv ) +{ + + /** Check if "--help" or "--version" was asked for. */ + if( argc == 1 ) + { + std::cout << "Use \"elastix --help\" for information about elastix-usage." << std::endl; + return 0; + } + else if( argc == 2 ) + { + std::string argument( argv[ 1 ] ); + if( argument == "-help" || argument == "--help" || argument == "-h" ) + { + PrintHelp(); + return 0; + } + else if( argument == "--version" ) + { + std::cout << std::fixed; + std::cout << std::showpoint; + std::cout << std::setprecision( 3 ); + std::cout << "elastix version: " << __ELASTIX_VERSION << std::endl; + return 0; + } + else if (argument == "--extended-version") + { + std::cout + << std::fixed + << std::showpoint + << std::setprecision(3) + << "elastix version: " + << __ELASTIX_VERSION + << "\nITK version: " + << ITK_VERSION_MAJOR << '.' + << ITK_VERSION_MINOR << '.' + << ITK_VERSION_PATCH + << "\nBuild date: " + << __DATE__ + << ' ' + << __TIME__ +#ifdef _MSC_FULL_VER + << "\nCompiler: Visual C++ version " + << _MSC_FULL_VER << '.' + << _MSC_BUILD +#endif +#ifdef __clang__ + << "\nCompiler: Clang" +#ifdef __VERSION__ + << " version " + << __VERSION__ +#endif +#endif +#if defined(__GNUC__) + << "\nCompiler: GCC" +#ifdef __VERSION__ + << " version " + << __VERSION__ +#endif +#endif + << "\nMemory address size: " + << std::numeric_limits::digits + << "-bit" + << "\nCMake version: " + << ELX_CMAKE_VERSION + << std::endl; + return 0; + } + else + { + std::cout << "Use \"elastix --help\" for information about elastix-usage." << std::endl; + return 0; + } + } + + /** Some typedef's. */ + typedef elx::ElastixMain ElastixMainType; + typedef ElastixMainType::ObjectPointer ObjectPointer; + typedef ElastixMainType::DataObjectContainerPointer DataObjectContainerPointer; + typedef ElastixMainType::FlatDirectionCosinesType FlatDirectionCosinesType; + + typedef ElastixMainType::ArgumentMapType ArgumentMapType; + typedef ArgumentMapType::value_type ArgumentMapEntryType; + + /** Support Mevis Dicom Tiff (if selected in cmake) */ + RegisterMevisDicomTiff(); + + ArgumentMapType argMap; + std::deque< std::string > parameterFileList; + std::string outFolder; + + /** Put command line parameters into parameterFileList. */ + for( unsigned int i = 1; static_cast< long >( i ) < ( argc - 1 ); i += 2 ) + { + std::string key( argv[ i ] ); + std::string value( argv[ i + 1 ] ); + + if( key == "-p" ) + { + /** Queue the ParameterFileNames. */ + parameterFileList.push_back( value ); + /** The different '-p' are stored in the argMap, with + * keys p(1), p(2), etc. */ + std::ostringstream tempPname; + tempPname << "-p(" << parameterFileList.size() << ")"; + std::string tempPName = tempPname.str(); + argMap.insert( ArgumentMapEntryType( tempPName, value ) ); + } + else + { + if( key == "-out" ) + { + /** Make sure that last character of the output folder equals a '/' or '\'. */ + const char last = value[ value.size() - 1 ]; + if( last != '/' && last != '\\' ) { value.append( "/" ); } + value = itksys::SystemTools::ConvertToOutputPath( value ); + + /** Note that on Windows, in case the output folder contains a space, + * the path name is double quoted by ConvertToOutputPath, which is undesirable. + * So, we remove these quotes again. + */ + if( itksys::SystemTools::StringStartsWith( value, "\"" ) + && itksys::SystemTools::StringEndsWith( value, "\"" ) ) + { + value = value.substr( 1, value.length() - 2 ); + } + + /** Save this information. */ + outFolder = value; + + } // end if key == "-out" + + /** Attempt to save the arguments in the ArgumentMap. */ + if( argMap.count( key ) == 0 ) + { + argMap.insert( ArgumentMapEntryType( key, value ) ); + } + else + { + /** Duplicate arguments. */ + std::cerr << "WARNING!" << std::endl; + std::cerr << "Argument " << key << "is only required once." << std::endl; + std::cerr << "Arguments " << key << " " << value << "are ignored" << std::endl; + } + + } // end else (so, if key does not equal "-p") + + } // end for loop + + /** The argv0 argument, required for finding the component.dll/so's. */ + argMap.insert( ArgumentMapEntryType( "-argv0", argv[ 0 ] ) ); + + int returndummy{}; + + /** Check if at least once the option "-p" is given. */ + if( parameterFileList.empty() ) + { + std::cerr << "ERROR: No CommandLine option \"-p\" given!" << std::endl; + returndummy |= -1; + } + + /** Check if the -out option is given. */ + if( ! outFolder.empty() ) + { + /** Check if the output directory exists. */ + if( ! itksys::SystemTools::FileIsDirectory( outFolder ) ) + { + std::cerr << "ERROR: the output directory \"" << outFolder << "\" does not exist." << std::endl; + std::cerr << "You are responsible for creating it." << std::endl; + returndummy |= -2; + } + else + { + /** Setup xout. */ + const std::string logFileName = outFolder + "elastix.log"; + const int returndummy2{ elx::xoutSetup(logFileName.c_str(), true, true) }; + if( returndummy2 != 0 ) + { + std::cerr << "ERROR while setting up xout." << std::endl; + } + returndummy |= returndummy2; + } + } + else + { + returndummy = -2; + std::cerr << "ERROR: No CommandLine option \"-out\" given!" << std::endl; + } + + /** Stop if some fatal errors occurred. */ + if( returndummy != 0 ) + { + return returndummy; + } + + elxout << std::endl; + + /** Declare a timer, start it and print the start time. */ + itk::TimeProbe totaltimer; + totaltimer.Start(); + elxout << "elastix is started at " << GetCurrentDateAndTime() << ".\n" << std::endl; + + /** Print where elastix was run. */ + elxout << "which elastix: " << argv[ 0 ] << std::endl; + itksys::SystemInformation info; + info.RunCPUCheck(); + info.RunOSCheck(); + info.RunMemoryCheck(); + elxout << "elastix runs at: " << info.GetHostname() << std::endl; + elxout << " " << info.GetOSName() << " " + << info.GetOSRelease() << ( info.Is64Bits() ? " (x64), " : ", " ) + << info.GetOSVersion() << std::endl; + elxout << " with " << info.GetTotalPhysicalMemory() << " MB memory, and " + << info.GetNumberOfPhysicalCPU() << " cores @ " + << static_cast< unsigned int >( info.GetProcessorClockFrequency() ) + << " MHz." << std::endl; + + + /** + * ********************* START REGISTRATION ********************* + * + * Do the (possibly multiple) registration(s). + */ + + + const int result = [parameterFileList, outFolder, argMap] + { + const auto nrOfParameterFiles = parameterFileList.size(); + + const auto fixedImageFilename = argMap.at("-f"); + const auto movingImageFilename = argMap.at("-m"); + + using elastix::ELASTIX; + ELASTIX elastixObject; + const auto fixedImage = ReadImage(fixedImageFilename); + const auto movingImage = ReadImage(movingImageFilename); + + std::vector parameterMaps(nrOfParameterFiles); + + for (std::size_t i{}; i < nrOfParameterFiles; ++i) + { + const auto parameterFileParser = itk::ParameterFileParser::New(); + parameterFileParser->SetParameterFileName(parameterFileList[i]); + parameterFileParser->ReadParameterFile(); + auto parameterMap = parameterFileParser->GetParameterMap(); + + parameterMap["FixedImageDimension"] = { std::to_string(fixedImage->GetImageDimension()) }; + parameterMap["MovingImageDimension"] = { std::to_string(movingImage->GetImageDimension()) }; + parameterMaps[i] = parameterMap; + } + + const bool performLogging{true}; + const bool performCout{true}; + ELASTIX::ImagePointer fixedMask{}; + ELASTIX::ImagePointer movingMask{}; + + return elastixObject.RegisterImages(fixedImage, movingImage, parameterMaps, outFolder, performLogging, performCout, fixedMask, movingMask); + }(); + + + elxout << "-------------------------------------------------------------------------" << "\n" << std::endl; + + /** Stop totaltimer and print it. */ + totaltimer.Stop(); + elxout << "Total time elapsed: " + << ConvertSecondsToDHMS( totaltimer.GetMean(), 1 ) << ".\n" << std::endl; + + /** Exit and return the error code. */ + return result; + +} // end Main + + +int +main( int argc, char ** argv ) +{ + try + { + return Main(argc, argv); + } + catch (const std::exception& stdException) + { + std::cerr + << "An error occurred! Exception: " << stdException.what() << '\n'; + return EXIT_FAILURE; + } +} + +/** + * *********************** PrintHelp **************************** + */ + +void +PrintHelp( void ) +{ + /** Print the version. */ + std::cout << std::fixed; + std::cout << std::showpoint; + std::cout << std::setprecision( 3 ); + std::cout << "elastix version: " << __ELASTIX_VERSION << "\n" << std::endl; + + /** What is elastix? */ + std::cout << "elastix registers a moving image to a fixed image.\n"; + std::cout << "The registration-process is specified in the parameter file.\n"; + std::cout << " --help, -h displays this message and exit\n"; + std::cout << " --version output version information and exit\n" + << " --extended-version output extended version information and exit\n" << std::endl; + + /** Mandatory arguments.*/ + std::cout << "Call elastix from the command line with mandatory arguments:\n"; + std::cout << " -f fixed image\n"; + std::cout << " -m moving image\n"; + std::cout << " -out output directory\n"; + std::cout << " -p parameter file, elastix handles 1 or more \"-p\"\n" + << std::endl; + + /** Optional arguments.*/ + std::cout << "Optional extra commands:\n"; + std::cout << " -fMask mask for fixed image\n"; + std::cout << " -mMask mask for moving image\n"; + std::cout << " -t0 parameter file for initial transform\n"; + std::cout << " -priority set the process priority to high, abovenormal, normal (default),\n" + << " belownormal, or idle (Windows only option)\n"; + std::cout << " -threads set the maximum number of threads of elastix\n" + << std::endl; + + /** The parameter file.*/ + std::cout << "The parameter-file must contain all the information " + "necessary for elastix to run properly. That includes which metric to " + "use, which optimizer, which transform, etc. It must also contain " + "information specific for the metric, optimizer, transform, etc. " + "For a usable parameter-file, see the website.\n" << std::endl; + + std::cout << "Need further help?\nCheck the website http://elastix.isi.uu.nl, " + "or mail elastix@bigr.nl." << std::endl; + +} // end PrintHelp()