Skip to content

Instantly share code, notes, and snippets.

@N-Dekker
Created January 23, 2025 10:17
Show Gist options
  • Save N-Dekker/6679b024939f49827686bce10235b26f to your computer and use it in GitHub Desktop.
Save N-Dekker/6679b024939f49827686bce10235b26f to your computer and use it in GitHub Desktop.
elastix_txt2yml - converts elastix parameter files from txt to yml
// elastix_txt2yml
// Converts elastix parameter files from txt to yml
//
// Usage: elastix_txt2yml <dir> [--just-copy]
//
// Created by Niels Dekker, LKEB, Leiden University Medical Center, 2025.
#include <cassert>
#include <cctype>
#include <cstring>
#include <deque>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
namespace fs = std::filesystem;
namespace
{
using Lines = std::deque<std::string>;
auto
ReadFile(const fs::path & filePath)
{
Lines result;
std::ifstream inputFileStream{ filePath };
std::string line;
while (std::getline(inputFileStream, line))
{
result.push_back(line);
}
return result;
}
void
WriteFile(const fs::path & filePath, const Lines & lines)
{
std::ofstream outputFileStream{ filePath };
for (const auto & line : lines)
{
outputFileStream << line << '\n';
}
}
void
ProcessFile(fs::path filePath, const bool justCopyFile)
{
std::cout << filePath << std::endl;
const auto inputLines = ReadFile(filePath);
auto outputLines = inputLines;
filePath.replace_extension(".yml");
unsigned numberOfParameters{};
for (auto & line : outputLines)
{
if (!line.empty())
{
if (const auto pos = line.find("//"); pos != std::string::npos)
{
line.replace(pos, 2, "#");
}
std::match_results<std::string::const_iterator> results;
if (std::regex_match(line, results, std::regex{ R"delimiter(\s*\(\s*(\w+)\s+([^\)]+)\s*\)(.*))delimiter" }) &&
results.size() == 4)
{
++numberOfParameters;
auto values = results[2].str();
assert(!values.empty());
const auto removeDoubleQuotes = [&values](const std::string_view value) {
for (;;)
{
if (const auto foundPos = values.find(value); foundPos == std::string::npos)
{
break;
}
else
{
values.replace(foundPos, value.size(), std::string_view(value.data() + 1, value.size() - 2));
}
}
};
removeDoubleQuotes("\"false\"");
removeDoubleQuotes("\"true\"");
// Replace tabs with spaces.
std::replace(values.begin(), values.end(), '\t', ' ');
// Replace double spaces with single spaces.
for (;;)
{
if (const auto foundPos = values.find(" "); foundPos == std::string::npos)
{
break;
}
else
{
values.replace(foundPos, 2, " ");
}
}
unsigned numberOfAddedCommas{};
if (const auto numberOfChars = values.size(); numberOfChars > 1)
{
bool evenNumberOfDoubleQuotes = values.back() != '"';
// Prepend a comma to each space.
for (auto i = numberOfChars - 2; i > 0; --i)
{
if (values[i] == '"')
{
evenNumberOfDoubleQuotes = !evenNumberOfDoubleQuotes;
}
else
{
if (evenNumberOfDoubleQuotes && (values[i] == ' '))
{
values.replace(i, 1, ", ");
++numberOfAddedCommas;
}
}
}
}
if (numberOfAddedCommas > 0)
{
values = '[' + values + ']';
}
line = results[1].str() + ": " +values + results[3].str();
}
}
}
const auto containsParameter = [](const auto & str) {
return str.find("parameter") != std::string::npos || str.find("Parameter") != std::string::npos;
};
if ((numberOfParameters > 0) || containsParameter(filePath.stem().string()))
{
WriteFile(filePath, justCopyFile ? inputLines : outputLines);
}
else
{
std::cerr << "Skipped file without elastix parameters: " << filePath << '\n';
}
}
void
ProcessFiles(const std::deque<fs::path> & filePaths, const bool justCopyFiles)
{
for (const auto & filePath : filePaths)
{
try
{
ProcessFile(filePath, justCopyFiles);
}
catch (const std::exception & stdException)
{
std::cerr << "Exception on processing " << filePath << ":\n " << stdException.what();
}
}
}
auto
RetrieveFilePaths(const fs::path & directoryPath)
{
std::deque<fs::path> filePaths;
for (const auto & entry : fs::recursive_directory_iterator{ directoryPath })
{
const auto & path = entry.path();
if (const auto & extension = path.extension();
(!extension.empty() && extension.string() == ".txt") && is_regular_file(path))
{
filePaths.push_back(path);
}
}
return filePaths;
}
} // namespace
int
main(int argc, char ** argv)
{
if (argc < 2)
{
std::cout << "Please specify the source directory path as command-line argument."
"\nNote: This program will modify the source files in-place!!!"
<< std::endl;
}
else
{
if (argv == nullptr)
{
return EXIT_FAILURE;
}
const char * const arg = argv[1];
if (arg == nullptr)
{
return EXIT_FAILURE;
}
try
{
ProcessFiles(RetrieveFilePaths(arg), argc == 3 && argv[2] == std::string_view("--just-copy"));
}
catch (const std::exception& stdException)
{
std::cerr << "Exception: " << stdException.what();
return EXIT_FAILURE;
}
}
std::cout << "Press anything to continue" << std::endl;
std::cin.get();
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment