[
  {
    "path": ".gitignore",
    "content": "# IDE files\n.idea/*\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Fortran module files\n*.mod\n*.smod\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n*.lib\n\n# Executables\n*.exe\n*.out\n*.app\n\n# Ignored by Rui\nCMakeLists.txt.user*\nbuild/\n*/build/\n*/bin/\n*/lib/\n*~\ncmake-build-*/\n"
  },
  {
    "path": ".gitlab-ci.yml",
    "content": "# defaults\nimage: nikolausdemmel/ubuntu-dev-cv:16.04\nvariables:\n  BUILD_TYPE: Release\n\n\n# template definition\n.compile_template: &compile_definition\n  stage: build\n  before_script:\n    - mkdir -p ccache\n    - export CCACHE_BASEDIR=${PWD}\n    - export CCACHE_DIR=${PWD}/ccache\n  cache:\n    paths:\n    - ccache/\n  script:\n    - mkdir build\n    - cd build\n    - cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE}\n    - make -j3\n\n\nrelease-16.04:\n  <<: *compile_definition\n  \ndebug-16.04:\n  variables:\n    BUILD_TYPE: Debug\n  <<: *compile_definition\n\nclang-16.04:\n  variables:\n    CC: /usr/bin/clang-5.0\n    CXX: /usr/bin/clang++-5.0\n  <<: *compile_definition\n\nrelease-14.04:\n  image: nikolausdemmel/ubuntu-dev-cv:14.04\n  variables:\n    CC: /usr/lib/ccache/gcc\n    CXX: /usr/lib/ccache/g++\n  <<: *compile_definition\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: required\n\nlanguage: cpp\n\nservices:\n  - docker\n\ncache: ccache\n\nenv:\n  global:\n    - DOCKER_CONTAINER_NAME=nikolausdemmel/ubuntu-dev-cv-gui\n    - CC=/usr/bin/gcc\n    - CXX=/usr/bin/g++\n    - BUILD_TYPE=Release\nmatrix:\n  include:\n  - os: linux\n    env: DIST_VERS=16.04 \n  - os: linux\n    env: DIST_VERS=16.04 CC=/usr/bin/clang-5.0 CXX=/usr/bin/clang++-5.0\n  - os: linux\n    env: DIST_VERS=16.04 BUILD_TYPE=Debug\n  - os: linux\n    env: DIST_VERS=14.04\n  - os: osx\n\nbefore_install:\n  - if [[ \"$TRAVIS_OS_NAME\" == \"osx\" ]]; then brew update; fi\n  \ninstall: |\n  if [[ \"$TRAVIS_OS_NAME\" == \"linux\" ]]; then\n    docker pull $DOCKER_CONTAINER_NAME:$DIST_VERS\n  fi\n  if [[ \"$TRAVIS_OS_NAME\" == \"osx\" ]]; then\n    # upgrade python first, else linking python@2 fails (dependency of opencv@2)\n    brew upgrade python\n    # removing pip numpy, else installing brew numpy fails (dependency of opencv@2)\n    brew install python@2\n    /usr/bin/yes | pip uninstall numpy\n\n    # upgrade already installed\n    brew upgrade cmake\n    # install additional dependencies\n    brew install opencv@2 ccache\n  fi\n\n\n\nscript: |\n  if [[ \"$TRAVIS_OS_NAME\" == \"linux\" ]]; then\n    docker run --name ci -it \\\n        -v ${TRAVIS_BUILD_DIR}:/repo \\\n        -v $HOME/.ccache:/root/.ccache \\\n        -e BUILD_TYPE -e CC -e CXX \\\n        $DOCKER_CONTAINER_NAME:$DIST_VERS \\\n        /bin/bash -c '\n    export PS4='\\''+ \\e[33;1m($0 @ line $LINENO) \\$\\e[0m '\\'' # quotes must be escaped\n    set -e # exit on failure\n    set -x # trace for debug\n    mkdir build && cd build\n    cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE /repo\n    cmake --build .'\n  fi\n  if [[ \"$TRAVIS_OS_NAME\" == \"osx\" ]]; then\n    mkdir build && cd build\n    cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..\n    cmake --build .\n  fi\n\n\n# TODO: try w/o docker, just installing from apt; compare speed.\n#jobs:\n#  foo:\n\n"
  },
  {
    "path": "AUTHORS.txt",
    "content": "Authors ordered by first contribution.\n\nPaul Bergmann <bergmann@in.tum.de>\nRui Wang <wangr@in.tum.de>\nNikolaus Demmel <demmeln@in.tum.de>\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## [0.1.0] - 2018-05-22\n### Added\n- Initial release\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.2)\nset(PROJECT_NAME online_photometric_calibration)\n\nproject(${PROJECT_NAME})\n\n# Set default build type if not specified otherwise.\n# See https://cmake.org/pipermail/cmake/2012-May/050243.html\nif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n  set(CMAKE_BUILD_TYPE Release CACHE STRING \"Choose the type of build.\" FORCE)\n  message(STATUS \"Setting build type to '${CMAKE_BUILD_TYPE}' as none was specified.\")\n  # Set the possible values of build type for cmake-gui\n  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS \"Debug\" \"Release\"\n    \"MinSizeRel\" \"RelWithDebInfo\")\nendif()\n\n# We need at least C++11\nset(CMAKE_CXX_STANDARD 11)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_EXTENSIONS OFF)\n\n# set march=native unless specified\nif(NOT CXX_MARCH)\n  set(CXX_MARCH native)\nendif()\nSET(CMAKE_CXX_FLAGS_RELEASE \"-march=${CXX_MARCH} ${CMAKE_CXX_FLAGS_RELEASE}\")\nSET(CMAKE_CXX_FLAGS_RELWITHDEBINFO  \"-march=${CXX_MARCH} ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}\")\n\n# warnings\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-sign-compare\")\n\n# Required libraries\nif(APPLE)\n  # give the homebrew path as hint, since the formula is keg-only\n  find_package(OpenCV 2.4 REQUIRED HINTS /usr/local/opt/opencv@2)\nelse()\n  find_package(OpenCV 2.4 REQUIRED)\nendif()\nfind_package(Threads REQUIRED)\n\n# Configure CCache if available (requires cmake 3.4 or newer)\nfind_program(CCACHE_PROGRAM ccache)\nif(CCACHE_PROGRAM)\n  message(STATUS \"Found ccache: ${CCACHE_PROGRAM}\")\n  set(CMAKE_C_COMPILER_LAUNCHER   ${CCACHE_PROGRAM})\n  set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PROGRAM})\nendif()\n\n# output paths in the build directory\nset(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)\nset(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)\nset(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)\n\n# include path\ninclude_directories(${PROJECT_SOURCE_DIR}/src)\n\n# Source files\nset(ONLINE_CALIB_SOURCE_FILES\n  src/Database.cpp\n  src/GainRobustTracker.cpp\n  src/ImageReader.cpp\n  src/JacobianGenerator.cpp\n  src/NonlinearOptimizer.cpp\n  src/OptimizationBlock.cpp\n  src/RapidExposureTimeEstimator.cpp\n  src/Tracker.cpp\n  src/VignetteModel.cpp\n)\n\n# add include files to custom target so they show up in IDEs like\n# QtCreator in the project view\nfile(GLOB_RECURSE _INCLUDE_FILES \"src/*.h\" \"src/*.hpp\")\nadd_custom_target(_include_files_fix_target SOURCES ${_INCLUDE_FILES})\n\n# main library\nadd_library(online_pcalib SHARED ${ONLINE_CALIB_SOURCE_FILES})\ntarget_link_libraries(online_pcalib\n  ${OpenCV_LIBS}\n  ${CMAKE_THREAD_LIBS_INIT})\n\n# demo executable for online calibration\nadd_executable(online_pcalib_demo src/main.cpp)\ntarget_link_libraries(online_pcalib_demo\n  online_pcalib)\n\n# install rules\ninstall (TARGETS online_pcalib online_pcalib_demo\n  ARCHIVE DESTINATION lib\n  LIBRARY DESTINATION lib\n  RUNTIME DESTINATION bin)\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "3-Clause BSD License\n\nCopyright 2017-2018 Paul Bergmann and co-authors (see AUTHORS.txt)\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n1. Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright\nnotice, this list of conditions and the following disclaimer in the\ndocumentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "# Online Photometric Calibration\n\nRecent direct visual odometry and SLAM algorithms have demonstrated\nimpressive levels of precision. However, they require a photometric\ncamera calibration in order to achieve competitive results. Hence, the\nrespective algorithm cannot be directly applied to an\noff-the-shelf-camera or to a video sequence acquired with an unknown\ncamera. In this work we propose a method for online photometric\ncalibration which enables to process auto exposure videos with visual\nodometry precisions that are on par with those of photometrically\ncalibrated videos. Our algorithm recovers the exposure times of\nconsecutive frames, the camera response function, and the attenuation\nfactors of the sensor irradiance due to vignetting. Gain robust KLT\nfeature tracks are used to obtain scene point correspondences as input\nto a nonlinear optimization framework. We show that our approach can\nreliably calibrate arbitrary video sequences by evaluating it on\ndatasets for which full photometric ground truth is available. We\nfurther show that our calibration can improve the performance of a\nstate-of-the-art direct visual odometry method that works solely on\npixel intensities, calibrating for photometric parameters in an online\nfashion in realtime. For more details please refer to our paper.\n\nIf you use this code in your research, we would appreciate if you cite\nthe respective publication.\n\n**Online Photometric Calibration of Auto Exposure Video for Realtime Visual Odometry and SLAM**\n(P. Bergmann, R. Wang, D. Cremers),\n*In IEEE Robotics and Automation Letters (RA-L)*, volume 3, 2018.\n[[pdf](https://vision.in.tum.de/_media/spezial/bib/bergmann17calibration.pdf)]\n[[video](https://youtu.be/nQHMG0c6Iew)]\n\nFor more information on photometric calibration, see\nhttps://vision.in.tum.de/research/vslam/photometric-calibration.\n\n**Note:** *This is a preliminary release. You should consider this\nresearch code in beta. All interfaces are subject to change.*\n\n## Install\n\nWe support Ubuntu 14.04 and 16.04, and macOS, but it might work on a\nvariety of other platforms that meet the dependency requirements.\n\n### Dependencies\n\nThe main dependencies are cmake 3.2 or later, a C++11 compiler, and\nOpenCV 2.4.\n\n#### Ubuntu 14.04\n\nOn Ubuntu 14.04 you need to get a more recent version of cmake.\n\n```\nsudo add-apt-repository ppa:george-edison55/cmake-3.x\n```\n\nNow continue to install dependencies like for Ubuntu 16.04.\n\n#### Ubuntu 16.04\n\n**Required:**\n\n```\nsudo apt-get update\nsudo apt-get install \\\n    build-essential \\\n    g++ \\\n    cmake \\\n    libopencv-dev\n```\n\n**Optional:**\n\nCCache can help you speed up repeated builds.\n\n*Note:* You need at least cmake version 3.4 for ccache to work\n automatically.\n\n```\nsudo apt-get install ccache\n```\n\n### macOS\n\nWe assume you have installed [Homebrew](https://brew.sh).\n\n\n**Required:**\n\n```\nbrew install cmake opencv@2\n```\n\n**Optional:**\n\n```\nbrew install ccache\n```\n\n### Compile\n\nStart in the package directory.\n\n```\nmkdir build\ncd build\ncmake ..\nmake -j4\n```\n\nOptionally you can install the built libraries and executables.\n\n```\nsudo make install\n```\n\n\n## Usage\n\n### Online calibration\n\n**Example usage:**\n\nDownload sequence of the TUMmono VO dataset.\n\n```\nSEQ=30\nwget http://vision.in.tum.de/mono/dataset/sequence_$SEQ.zip\nunzip sequence_$SEQ.zip\nunzip -d sequence_$SEQ/images sequence_$SEQ/images.zip\n```\n\nRun online calibration.\n\n```\nbuild/bin/online_pcalib_demo -i sequence_$SEQ/images --exposure-gt-file sequence_$SEQ/times.txt\n```\n\n*Note:* Currently the implementation is not suitable for\n fisheye-lenses with black borders around the image, which includes\n some of the TUMmono VO dataset sequences as well as the TUM VI\n dataset sequences.\n\n### Batch calibration\n\nOnline calibration runs the code in a multithreaded way in parallel on the CPU.\nIf tracking and backend optimization should be performed sequentially and real time \nperformance is not required, the system can be run in batch calibration mode.\nFor running in batch mode, simply add the command line option\n\n```\n  --calibration-mode batch\n```\n\nFor batch calibration you might want to use the exposure times from the optimization\nbackend rather than the rapidly estimated exposure times from the frontend. In order \nto extract more keyframes to the backend optimizer, the run_settings parameters have to\nbe adjusted.\n\nThese parameters can be changed by manually setting:\n\n```\n  --nr-active-frames INT      Maximum number of frames to be stored in the database.\n  --keyframe-spacing INT      Number of frames that keyframes are apart in the backend optimizer.\n  --min-keyframes-valid INT   Minimum number of frames a feature has to be tracked to be considered for optimization.\n```\n\n### Command line options\n\n```\nPhotometric Calibration\nUsage: online_pcalib_demo [OPTIONS]\n\nOptions:\n  -h,--help                   Print this help message and exit\n  -i,--image-folder TEXT=images\n                              Folder with image files to read.\n  --start-image-index INT=0   Start reading from this image index.\n  --end-image-index INT=-1    Stop reading at this image index.\n  --image-width INT=640       Resize image to this width.\n  --image-height INT=480      Resize image to this height.\n  --exposure-gt-file TEXT=times.txt\n                              Textfile containing ground truth exposure times for each frame for visualization.\n  --calibration-mode TEXT=online\n                              Choose 'online' or 'batch'\n  --nr-active-frames INT=200  Maximum number of frames to be stored in the database.\n  --keyframe-spacing INT=15   Number of frames that keyframes are apart in the backend optimizer.\n  --min-keyframes-valid INT=3 Minimum number of frames a feature has to be tracked to be considered for optimization.\n```\n\n\n## License\n\nThis project was originally developed at the [TUM computer vision\ngroup](https://vision.in.tum.de) in 2017 by Paul Bergmann.\n\nIt is currently maintained by Paul Bermann,\n[Rui Wang](https://vision.in.tum.de/members/wangr) and\n[Nikolaus Demmel](https://vision.in.tum.de/members/demmeln). See\n[AUTHROS.txt](AUTHORS.txt) for a list of contributors.\n\nThis project is available under a BSD 3-Clause license.\nSee [LICENSE.txt](LICENSE.txt)\n\nAmong others, we make use of the following libraries:\n\n * OpenCV ([BSD](https://opencv.org/license.html))\n * CLI11 ([BSD](https://github.com/CLIUtils/CLI11/blob/master/LICENSE))\n\n\n"
  },
  {
    "path": "src/CLI11.hpp",
    "content": "#pragma once\n\n// CLI11: Version 1.5.3\n// Originally designed by Henry Schreiner\n// https://github.com/CLIUtils/CLI11\n//\n// This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts\n// from: v1.5.3\n//\n// From LICENSE:\n//\n// CLI11 1.5 Copyright (c) 2017-2018 University of Cincinnati, developed by Henry\n// Schreiner under NSF AWARD 1414736. All rights reserved.\n// \n// Redistribution and use in source and binary forms of CLI11, with or without\n// modification, are permitted provided that the following conditions are met:\n// \n// 1. Redistributions of source code must retain the above copyright notice, this\n//    list of conditions and the following disclaimer. \n// 2. Redistributions in binary form must reproduce the above copyright notice,\n//    this list of conditions and the following disclaimer in the documentation\n//    and/or other materials provided with the distribution.\n// 3. Neither the name of the copyright holder nor the names of its contributors\n//    may be used to endorse or promote products derived from this software without\n//    specific prior written permission.\n// \n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR\n// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\n// Standard combined includes:\n\n#include <algorithm>\n#include <deque>\n#include <exception>\n#include <fstream>\n#include <functional>\n#include <iomanip>\n#include <iostream>\n#include <istream>\n#include <iterator>\n#include <locale>\n#include <memory>\n#include <numeric>\n#include <set>\n#include <sstream>\n#include <stdexcept>\n#include <string>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n\n// Verbatim copy from CLI/Optional.hpp:\n\n#ifdef __has_include\n\n#if defined(CLI11_CPP17) && __has_include(<optional>) && \\\n    defined(__cpp_lib_optional) && !defined(CLI11_STD_OPTIONAL)\n#define CLI11_STD_OPTIONAL\n#endif\n\n#if defined(CLI11_CPP14) && __has_include(<experimental/optional>) && \\\n    !defined(CLI11_EXPERIMENTAL_OPTIONAL)\n#define CLI11_EXPERIMENTAL_OPTIONAL\n#endif\n\n#if __has_include(<boost/optional.hpp>) && !defined(CLI11_BOOST_OPTIONAL)\n#include <boost/version.hpp>\n#if BOOST_VERSION >= 105800\n#define CLI11_BOOST_OPTIONAL\n#endif\n#endif\n\n#endif\n\n#ifdef CLI11_STD_OPTIONAL\n#include <optional>\n#endif\n#ifdef CLI11_EXPERIMENTAL_OPTIONAL\n#include <experimental/optional>\n#endif\n#ifdef CLI11_BOOST_OPTIONAL\n#include <boost/optional.hpp>\n#endif\n\n\n// From CLI/Version.hpp:\n\nnamespace CLI {\n\n// Note that all code in CLI11 must be in a namespace, even if it just a define.\n\n#define CLI11_VERSION_MAJOR 1\n#define CLI11_VERSION_MINOR 5\n#define CLI11_VERSION_PATCH 3\n#define CLI11_VERSION \"1.5.3\"\n\n} // namespace CLI\n\n// From CLI/Macros.hpp:\n\nnamespace CLI {\n\n// Note that all code in CLI11 must be in a namespace, even if it just a define.\n\n// The following version macro is very similar to the one in PyBind11\n\n#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER)\n#if __cplusplus >= 201402L\n#define CLI11_CPP14\n#if __cplusplus >= 201703L\n#define CLI11_CPP17\n#if __cplusplus > 201703L\n#define CLI11_CPP20\n#endif\n#endif\n#endif\n#elif defined(_MSC_VER) && __cplusplus == 199711L\n// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented)\n// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer\n#if _MSVC_LANG >= 201402L\n#define CLI11_CPP14\n#if _MSVC_LANG > 201402L && _MSC_VER >= 1910\n#define CLI11_CPP17\n#if __MSVC_LANG > 201703L && _MSC_VER >= 1910\n#define CLI11_CPP20\n#endif\n#endif\n#endif\n#endif\n\n#if defined(PYBIND11_CPP14)\n#define CLI11_DEPRECATED(reason) [[deprecated(reason)]]\n#elif defined(_MSC_VER)\n#define CLI11_DEPRECATED(reason) __declspec(deprecated(reason))\n#else\n#define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason)))\n#endif\n\n} // namespace CLI\n\n// From CLI/Optional.hpp:\n\nnamespace CLI {\n\n#ifdef CLI11_STD_OPTIONAL\ntemplate <typename T> std::istream &operator>>(std::istream &in, std::optional<T> &val) {\n    T v;\n    in >> v;\n    val = v;\n    return in;\n}\n#endif\n\n#ifdef CLI11_EXPERIMENTAL_OPTIONAL\ntemplate <typename T> std::istream &operator>>(std::istream &in, std::experimental::optional<T> &val) {\n    T v;\n    in >> v;\n    val = v;\n    return in;\n}\n#endif\n\n#ifdef CLI11_BOOST_OPTIONAL\ntemplate <typename T> std::istream &operator>>(std::istream &in, boost::optional<T> &val) {\n    T v;\n    in >> v;\n    val = v;\n    return in;\n}\n#endif\n\n// Export the best optional to the CLI namespace\n#if defined(CLI11_STD_OPTIONAL)\nusing std::optional;\n#elif defined(CLI11_EXPERIMENTAL_OPTIONAL)\nusing std::experimental::optional;\n#elif defined(CLI11_BOOST_OPTIONAL)\nusing boost::optional;\n#endif\n\n// This is true if any optional is found\n#if defined(CLI11_STD_OPTIONAL) || defined(CLI11_EXPERIMENTAL_OPTIONAL) || defined(CLI11_BOOST_OPTIONAL)\n#define CLI11_OPTIONAL\n#endif\n\n} // namespace CLI\n\n// From CLI/StringTools.hpp:\n\nnamespace CLI {\nnamespace detail {\n\n// Based on http://stackoverflow.com/questions/236129/split-a-string-in-c\n/// Split a string by a delim\ninline std::vector<std::string> split(const std::string &s, char delim) {\n    std::vector<std::string> elems;\n    // Check to see if empty string, give consistent result\n    if(s.empty())\n        elems.emplace_back(\"\");\n    else {\n        std::stringstream ss;\n        ss.str(s);\n        std::string item;\n        while(std::getline(ss, item, delim)) {\n            elems.push_back(item);\n        }\n    }\n    return elems;\n}\n\n/// Simple function to join a string\ntemplate <typename T> std::string join(const T &v, std::string delim = \",\") {\n    std::ostringstream s;\n    size_t start = 0;\n    for(const auto &i : v) {\n        if(start++ > 0)\n            s << delim;\n        s << i;\n    }\n    return s.str();\n}\n\n/// Join a string in reverse order\ntemplate <typename T> std::string rjoin(const T &v, std::string delim = \",\") {\n    std::ostringstream s;\n    for(size_t start = 0; start < v.size(); start++) {\n        if(start > 0)\n            s << delim;\n        s << v[v.size() - start - 1];\n    }\n    return s.str();\n}\n\n// Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string\n\n/// Trim whitespace from left of string\ninline std::string &ltrim(std::string &str) {\n    auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });\n    str.erase(str.begin(), it);\n    return str;\n}\n\n/// Trim anything from left of string\ninline std::string &ltrim(std::string &str, const std::string &filter) {\n    auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });\n    str.erase(str.begin(), it);\n    return str;\n}\n\n/// Trim whitespace from right of string\ninline std::string &rtrim(std::string &str) {\n    auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });\n    str.erase(it.base(), str.end());\n    return str;\n}\n\n/// Trim anything from right of string\ninline std::string &rtrim(std::string &str, const std::string &filter) {\n    auto it =\n        std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });\n    str.erase(it.base(), str.end());\n    return str;\n}\n\n/// Trim whitespace from string\ninline std::string &trim(std::string &str) { return ltrim(rtrim(str)); }\n\n/// Trim anything from string\ninline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); }\n\n/// Make a copy of the string and then trim it\ninline std::string trim_copy(const std::string &str) {\n    std::string s = str;\n    return trim(s);\n}\n\n/// Make a copy of the string and then trim it, any filter string can be used (any char in string is filtered)\ninline std::string trim_copy(const std::string &str, const std::string &filter) {\n    std::string s = str;\n    return trim(s, filter);\n}\n/// Print a two part \"help\" string\ninline void format_help(std::stringstream &out, std::string name, std::string description, size_t wid) {\n    name = \"  \" + name;\n    out << std::setw(static_cast<int>(wid)) << std::left << name;\n    if(!description.empty()) {\n        if(name.length() >= wid)\n            out << std::endl << std::setw(static_cast<int>(wid)) << \"\";\n        out << description;\n    }\n    out << std::endl;\n}\n\n/// Verify the first character of an option\ntemplate <typename T> bool valid_first_char(T c) { return std::isalpha(c, std::locale()) || c == '_'; }\n\n/// Verify following characters of an option\ntemplate <typename T> bool valid_later_char(T c) {\n    return std::isalnum(c, std::locale()) || c == '_' || c == '.' || c == '-';\n}\n\n/// Verify an option name\ninline bool valid_name_string(const std::string &str) {\n    if(str.empty() || !valid_first_char(str[0]))\n        return false;\n    for(auto c : str.substr(1))\n        if(!valid_later_char(c))\n            return false;\n    return true;\n}\n\n/// Return a lower case version of a string\ninline std::string to_lower(std::string str) {\n    std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) {\n        return std::tolower(x, std::locale());\n    });\n    return str;\n}\n\n/// Split a string '\"one two\" \"three\"' into 'one two', 'three'\ninline std::vector<std::string> split_up(std::string str) {\n\n    std::vector<char> delims = {'\\'', '\\\"'};\n    auto find_ws = [](char ch) { return std::isspace<char>(ch, std::locale()); };\n    trim(str);\n\n    std::vector<std::string> output;\n\n    while(!str.empty()) {\n        if(str[0] == '\\'') {\n            auto end = str.find('\\'', 1);\n            if(end != std::string::npos) {\n                output.push_back(str.substr(1, end - 1));\n                str = str.substr(end + 1);\n            } else {\n                output.push_back(str.substr(1));\n                str = \"\";\n            }\n        } else if(str[0] == '\\\"') {\n            auto end = str.find('\\\"', 1);\n            if(end != std::string::npos) {\n                output.push_back(str.substr(1, end - 1));\n                str = str.substr(end + 1);\n            } else {\n                output.push_back(str.substr(1));\n                str = \"\";\n            }\n\n        } else {\n            auto it = std::find_if(std::begin(str), std::end(str), find_ws);\n            if(it != std::end(str)) {\n                std::string value = std::string(str.begin(), it);\n                output.push_back(value);\n                str = std::string(it, str.end());\n            } else {\n                output.push_back(str);\n                str = \"\";\n            }\n        }\n        trim(str);\n    }\n\n    return output;\n}\n\n/// Add a leader to the beginning of all new lines (nothing is added\n/// at the start of the first line). `\"; \"` would be for ini files\n///\n/// Can't use Regex, or this would be a subs.\ninline std::string fix_newlines(std::string leader, std::string input) {\n    std::string::size_type n = 0;\n    while(n != std::string::npos && n < input.size()) {\n        n = input.find('\\n', n);\n        if(n != std::string::npos) {\n            input = input.substr(0, n + 1) + leader + input.substr(n + 1);\n            n += leader.size();\n        }\n    }\n    return input;\n}\n\n} // namespace detail\n} // namespace CLI\n\n// From CLI/Error.hpp:\n\nnamespace CLI {\n\n// Use one of these on all error classes\n#define CLI11_ERROR_DEF(parent, name)                                                                                  \\\n  protected:                                                                                                           \\\n    name(std::string name, std::string msg, int exit_code) : parent(std::move(name), std::move(msg), exit_code) {}     \\\n    name(std::string name, std::string msg, ExitCodes exit_code)                                                       \\\n        : parent(std::move(name), std::move(msg), exit_code) {}                                                        \\\n                                                                                                                       \\\n  public:                                                                                                              \\\n    name(std::string msg, ExitCodes exit_code) : parent(#name, std::move(msg), exit_code) {}                           \\\n    name(std::string msg, int exit_code) : parent(#name, std::move(msg), exit_code) {}\n\n// This is added after the one above if a class is used directly and builds its own message\n#define CLI11_ERROR_SIMPLE(name)                                                                                       \\\n    explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {}\n\n/// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut,\n/// int values from e.get_error_code().\nenum class ExitCodes {\n    Success = 0,\n    IncorrectConstruction = 100,\n    BadNameString,\n    OptionAlreadyAdded,\n    FileError,\n    ConversionError,\n    ValidationError,\n    RequiredError,\n    RequiresError,\n    ExcludesError,\n    ExtrasError,\n    INIError,\n    InvalidError,\n    HorribleError,\n    OptionNotFound,\n    ArgumentMismatch,\n    BaseClass = 127\n};\n\n// Error definitions\n\n/// @defgroup error_group Errors\n/// @brief Errors thrown by CLI11\n///\n/// These are the errors that can be thrown. Some of them, like CLI::Success, are not really errors.\n/// @{\n\n/// All errors derive from this one\nclass Error : public std::runtime_error {\n    int exit_code;\n    std::string name{\"Error\"};\n\n  public:\n    int get_exit_code() const { return exit_code; }\n\n    std::string get_name() const { return name; }\n\n    Error(std::string name, std::string msg, int exit_code = static_cast<int>(ExitCodes::BaseClass))\n        : runtime_error(msg), exit_code(exit_code), name(std::move(name)) {}\n\n    Error(std::string name, std::string msg, ExitCodes exit_code) : Error(name, msg, static_cast<int>(exit_code)) {}\n};\n\n// Note: Using Error::Error constructors does not work on GCC 4.7\n\n/// Construction errors (not in parsing)\nclass ConstructionError : public Error {\n    CLI11_ERROR_DEF(Error, ConstructionError)\n};\n\n/// Thrown when an option is set to conflicting values (non-vector and multi args, for example)\nclass IncorrectConstruction : public ConstructionError {\n    CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction)\n    CLI11_ERROR_SIMPLE(IncorrectConstruction)\n    static IncorrectConstruction PositionalFlag(std::string name) {\n        return IncorrectConstruction(name + \": Flags cannot be positional\");\n    }\n    static IncorrectConstruction Set0Opt(std::string name) {\n        return IncorrectConstruction(name + \": Cannot set 0 expected, use a flag instead\");\n    }\n    static IncorrectConstruction SetFlag(std::string name) {\n        return IncorrectConstruction(name + \": Cannot set an expected number for flags\");\n    }\n    static IncorrectConstruction ChangeNotVector(std::string name) {\n        return IncorrectConstruction(name + \": You can only change the expected arguments for vectors\");\n    }\n    static IncorrectConstruction AfterMultiOpt(std::string name) {\n        return IncorrectConstruction(\n            name + \": You can't change expected arguments after you've changed the multi option policy!\");\n    }\n    static IncorrectConstruction MissingOption(std::string name) {\n        return IncorrectConstruction(\"Option \" + name + \" is not defined\");\n    }\n    static IncorrectConstruction MultiOptionPolicy(std::string name) {\n        return IncorrectConstruction(name + \": multi_option_policy only works for flags and exact value options\");\n    }\n};\n\n/// Thrown on construction of a bad name\nclass BadNameString : public ConstructionError {\n    CLI11_ERROR_DEF(ConstructionError, BadNameString)\n    CLI11_ERROR_SIMPLE(BadNameString)\n    static BadNameString OneCharName(std::string name) { return BadNameString(\"Invalid one char name: \" + name); }\n    static BadNameString BadLongName(std::string name) { return BadNameString(\"Bad long name: \" + name); }\n    static BadNameString DashesOnly(std::string name) {\n        return BadNameString(\"Must have a name, not just dashes: \" + name);\n    }\n    static BadNameString MultiPositionalNames(std::string name) {\n        return BadNameString(\"Only one positional name allowed, remove: \" + name);\n    }\n};\n\n/// Thrown when an option already exists\nclass OptionAlreadyAdded : public ConstructionError {\n    CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded)\n    explicit OptionAlreadyAdded(std::string name)\n        : OptionAlreadyAdded(name + \" is already added\", ExitCodes::OptionAlreadyAdded) {}\n    static OptionAlreadyAdded Requires(std::string name, std::string other) {\n        return OptionAlreadyAdded(name + \" requires \" + other, ExitCodes::OptionAlreadyAdded);\n    }\n    static OptionAlreadyAdded Excludes(std::string name, std::string other) {\n        return OptionAlreadyAdded(name + \" excludes \" + other, ExitCodes::OptionAlreadyAdded);\n    }\n};\n\n// Parsing errors\n\n/// Anything that can error in Parse\nclass ParseError : public Error {\n    CLI11_ERROR_DEF(Error, ParseError)\n};\n\n// Not really \"errors\"\n\n/// This is a successful completion on parsing, supposed to exit\nclass Success : public ParseError {\n    CLI11_ERROR_DEF(ParseError, Success)\n    Success() : Success(\"Successfully completed, should be caught and quit\", ExitCodes::Success) {}\n};\n\n/// -h or --help on command line\nclass CallForHelp : public ParseError {\n    CLI11_ERROR_DEF(ParseError, CallForHelp)\n    CallForHelp() : CallForHelp(\"This should be caught in your main function, see examples\", ExitCodes::Success) {}\n};\n\n/// Does not output a diagnostic in CLI11_PARSE, but allows to return from main() with a specific error code.\nclass RuntimeError : public ParseError {\n    CLI11_ERROR_DEF(ParseError, RuntimeError)\n    explicit RuntimeError(int exit_code = 1) : RuntimeError(\"Runtime error\", exit_code) {}\n};\n\n/// Thrown when parsing an INI file and it is missing\nclass FileError : public ParseError {\n    CLI11_ERROR_DEF(ParseError, FileError)\n    CLI11_ERROR_SIMPLE(FileError)\n    static FileError Missing(std::string name) { return FileError(name + \" was not readable (missing?)\"); }\n};\n\n/// Thrown when conversion call back fails, such as when an int fails to coerce to a string\nclass ConversionError : public ParseError {\n    CLI11_ERROR_DEF(ParseError, ConversionError)\n    CLI11_ERROR_SIMPLE(ConversionError)\n    ConversionError(std::string member, std::string name)\n        : ConversionError(\"The value \" + member + \" is not an allowed value for \" + name) {}\n    ConversionError(std::string name, std::vector<std::string> results)\n        : ConversionError(\"Could not convert: \" + name + \" = \" + detail::join(results)) {}\n    static ConversionError TooManyInputsFlag(std::string name) {\n        return ConversionError(name + \": too many inputs for a flag\");\n    }\n    static ConversionError TrueFalse(std::string name) {\n        return ConversionError(name + \": Should be true/false or a number\");\n    }\n};\n\n/// Thrown when validation of results fails\nclass ValidationError : public ParseError {\n    CLI11_ERROR_DEF(ParseError, ValidationError)\n    CLI11_ERROR_SIMPLE(ValidationError)\n    explicit ValidationError(std::string name, std::string msg) : ValidationError(name + \": \" + msg) {}\n};\n\n/// Thrown when a required option is missing\nclass RequiredError : public ParseError {\n    CLI11_ERROR_DEF(ParseError, RequiredError)\n    explicit RequiredError(std::string name) : RequiredError(name + \" is required\", ExitCodes::RequiredError) {}\n    static RequiredError Subcommand(size_t min_subcom) {\n        if(min_subcom == 1)\n            return RequiredError(\"A subcommand\");\n        else\n            return RequiredError(\"Requires at least \" + std::to_string(min_subcom) + \" subcommands\",\n                                 ExitCodes::RequiredError);\n    }\n};\n\n/// Thrown when the wrong number of arguments has been received\nclass ArgumentMismatch : public ParseError {\n    CLI11_ERROR_DEF(ParseError, ArgumentMismatch)\n    CLI11_ERROR_SIMPLE(ArgumentMismatch)\n    ArgumentMismatch(std::string name, int expected, size_t recieved)\n        : ArgumentMismatch(expected > 0 ? (\"Expected exactly \" + std::to_string(expected) + \" arguments to \" + name +\n                                           \", got \" + std::to_string(recieved))\n                                        : (\"Expected at least \" + std::to_string(-expected) + \" arguments to \" + name +\n                                           \", got \" + std::to_string(recieved)),\n                           ExitCodes::ArgumentMismatch) {}\n\n    static ArgumentMismatch AtLeast(std::string name, int num) {\n        return ArgumentMismatch(name + \": At least \" + std::to_string(num) + \" required\");\n    }\n    static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) {\n        return ArgumentMismatch(name + \": \" + std::to_string(num) + \" required \" + type + \" missing\");\n    }\n};\n\n/// Thrown when a requires option is missing\nclass RequiresError : public ParseError {\n    CLI11_ERROR_DEF(ParseError, RequiresError)\n    RequiresError(std::string curname, std::string subname)\n        : RequiresError(curname + \" requires \" + subname, ExitCodes::RequiresError) {}\n};\n\n/// Thrown when an excludes option is present\nclass ExcludesError : public ParseError {\n    CLI11_ERROR_DEF(ParseError, ExcludesError)\n    ExcludesError(std::string curname, std::string subname)\n        : ExcludesError(curname + \" excludes \" + subname, ExitCodes::ExcludesError) {}\n};\n\n/// Thrown when too many positionals or options are found\nclass ExtrasError : public ParseError {\n    CLI11_ERROR_DEF(ParseError, ExtrasError)\n    explicit ExtrasError(std::vector<std::string> args)\n        : ExtrasError((args.size() > 1 ? \"The following arguments were not expected: \"\n                                       : \"The following argument was not expected: \") +\n                          detail::rjoin(args, \" \"),\n                      ExitCodes::ExtrasError) {}\n};\n\n/// Thrown when extra values are found in an INI file\nclass INIError : public ParseError {\n    CLI11_ERROR_DEF(ParseError, INIError)\n    CLI11_ERROR_SIMPLE(INIError)\n    static INIError Extras(std::string item) { return INIError(\"INI was not able to parse \" + item); }\n    static INIError NotConfigurable(std::string item) {\n        return INIError(item + \": This option is not allowed in a configuration file\");\n    }\n};\n\n/// Thrown when validation fails before parsing\nclass InvalidError : public ParseError {\n    CLI11_ERROR_DEF(ParseError, InvalidError)\n    explicit InvalidError(std::string name)\n        : InvalidError(name + \": Too many positional arguments with unlimited expected args\", ExitCodes::InvalidError) {\n    }\n};\n\n/// This is just a safety check to verify selection and parsing match - you should not ever see it\n/// Strings are directly added to this error, but again, it should never be seen.\nclass HorribleError : public ParseError {\n    CLI11_ERROR_DEF(ParseError, HorribleError)\n    CLI11_ERROR_SIMPLE(HorribleError)\n};\n\n// After parsing\n\n/// Thrown when counting a non-existent option\nclass OptionNotFound : public Error {\n    CLI11_ERROR_DEF(Error, OptionNotFound)\n    explicit OptionNotFound(std::string name) : OptionNotFound(name + \" not found\", ExitCodes::OptionNotFound) {}\n};\n\n/// @}\n\n} // namespace CLI\n\n// From CLI/TypeTools.hpp:\n\nnamespace CLI {\n\n// Type tools\n\n// We could check to see if C++14 is being used, but it does not hurt to redefine this\n// (even Google does this: https://github.com/google/skia/blob/master/include/private/SkTLogic.h)\n// It is not in the std namespace anyway, so no harm done.\n\ntemplate <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;\n\ntemplate <typename T> struct is_vector { static const bool value = false; };\n\ntemplate <class T, class A> struct is_vector<std::vector<T, A>> { static bool const value = true; };\n\ntemplate <typename T> struct is_bool { static const bool value = false; };\n\ntemplate <> struct is_bool<bool> { static bool const value = true; };\n\nnamespace detail {\n// Based generally on https://rmf.io/cxx11/almost-static-if\n/// Simple empty scoped class\nenum class enabler {};\n\n/// An instance to use in EnableIf\nconstexpr enabler dummy = {};\n\n// Type name print\n\n/// Was going to be based on\n///  http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template\n/// But this is cleaner and works better in this case\n\ntemplate <typename T,\n          enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>\nconstexpr const char *type_name() {\n    return \"INT\";\n}\n\ntemplate <typename T,\n          enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>\nconstexpr const char *type_name() {\n    return \"UINT\";\n}\n\ntemplate <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>\nconstexpr const char *type_name() {\n    return \"FLOAT\";\n}\n\n/// This one should not be used, since vector types print the internal type\ntemplate <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>\nconstexpr const char *type_name() {\n    return \"VECTOR\";\n}\n\ntemplate <typename T,\n          enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value,\n                      detail::enabler> = detail::dummy>\nconstexpr const char *type_name() {\n    return \"TEXT\";\n}\n\n// Lexical cast\n\n/// Signed integers / enums\ntemplate <typename T,\n          enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), detail::enabler> = detail::dummy>\nbool lexical_cast(std::string input, T &output) {\n    try {\n        size_t n = 0;\n        long long output_ll = std::stoll(input, &n, 0);\n        output = static_cast<T>(output_ll);\n        return n == input.size() && static_cast<long long>(output) == output_ll;\n    } catch(const std::invalid_argument &) {\n        return false;\n    } catch(const std::out_of_range &) {\n        return false;\n    }\n}\n\n/// Unsigned integers\ntemplate <typename T,\n          enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>\nbool lexical_cast(std::string input, T &output) {\n    if(!input.empty() && input.front() == '-')\n        return false; // std::stoull happily converts negative values to junk without any errors.\n\n    try {\n        size_t n = 0;\n        unsigned long long output_ll = std::stoull(input, &n, 0);\n        output = static_cast<T>(output_ll);\n        return n == input.size() && static_cast<unsigned long long>(output) == output_ll;\n    } catch(const std::invalid_argument &) {\n        return false;\n    } catch(const std::out_of_range &) {\n        return false;\n    }\n}\n\n/// Floats\ntemplate <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>\nbool lexical_cast(std::string input, T &output) {\n    try {\n        size_t n = 0;\n        output = static_cast<T>(std::stold(input, &n));\n        return n == input.size();\n    } catch(const std::invalid_argument &) {\n        return false;\n    } catch(const std::out_of_range &) {\n        return false;\n    }\n}\n\n/// String and similar\ntemplate <typename T,\n          enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&\n                          std::is_assignable<T &, std::string>::value,\n                      detail::enabler> = detail::dummy>\nbool lexical_cast(std::string input, T &output) {\n    output = input;\n    return true;\n}\n\n/// Non-string parsable\ntemplate <typename T,\n          enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&\n                          !std::is_assignable<T &, std::string>::value,\n                      detail::enabler> = detail::dummy>\nbool lexical_cast(std::string input, T &output) {\n    std::istringstream is;\n\n    is.str(input);\n    is >> output;\n    return !is.fail() && !is.rdbuf()->in_avail();\n}\n\n} // namespace detail\n} // namespace CLI\n\n// From CLI/Split.hpp:\n\nnamespace CLI {\nnamespace detail {\n\n// Returns false if not a short option. Otherwise, sets opt name and rest and returns true\ninline bool split_short(const std::string &current, std::string &name, std::string &rest) {\n    if(current.size() > 1 && current[0] == '-' && valid_first_char(current[1])) {\n        name = current.substr(1, 1);\n        rest = current.substr(2);\n        return true;\n    } else\n        return false;\n}\n\n// Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true\ninline bool split_long(const std::string &current, std::string &name, std::string &value) {\n    if(current.size() > 2 && current.substr(0, 2) == \"--\" && valid_first_char(current[2])) {\n        auto loc = current.find(\"=\");\n        if(loc != std::string::npos) {\n            name = current.substr(2, loc - 2);\n            value = current.substr(loc + 1);\n        } else {\n            name = current.substr(2);\n            value = \"\";\n        }\n        return true;\n    } else\n        return false;\n}\n\n// Splits a string into multiple long and short names\ninline std::vector<std::string> split_names(std::string current) {\n    std::vector<std::string> output;\n    size_t val;\n    while((val = current.find(\",\")) != std::string::npos) {\n        output.push_back(trim_copy(current.substr(0, val)));\n        current = current.substr(val + 1);\n    }\n    output.push_back(trim_copy(current));\n    return output;\n}\n\n/// Get a vector of short names, one of long names, and a single name\ninline std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>\nget_names(const std::vector<std::string> &input) {\n\n    std::vector<std::string> short_names;\n    std::vector<std::string> long_names;\n    std::string pos_name;\n\n    for(std::string name : input) {\n        if(name.length() == 0)\n            continue;\n        else if(name.length() > 1 && name[0] == '-' && name[1] != '-') {\n            if(name.length() == 2 && valid_first_char(name[1]))\n                short_names.emplace_back(1, name[1]);\n            else\n                throw BadNameString::OneCharName(name);\n        } else if(name.length() > 2 && name.substr(0, 2) == \"--\") {\n            name = name.substr(2);\n            if(valid_name_string(name))\n                long_names.push_back(name);\n            else\n                throw BadNameString::BadLongName(name);\n        } else if(name == \"-\" || name == \"--\") {\n            throw BadNameString::DashesOnly(name);\n        } else {\n            if(pos_name.length() > 0)\n                throw BadNameString::MultiPositionalNames(name);\n            pos_name = name;\n        }\n    }\n\n    return std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>(\n        short_names, long_names, pos_name);\n}\n\n} // namespace detail\n} // namespace CLI\n\n// From CLI/Ini.hpp:\n\nnamespace CLI {\nnamespace detail {\n\ninline std::string inijoin(std::vector<std::string> args) {\n    std::ostringstream s;\n    size_t start = 0;\n    for(const auto &arg : args) {\n        if(start++ > 0)\n            s << \" \";\n\n        auto it = std::find_if(arg.begin(), arg.end(), [](char ch) { return std::isspace<char>(ch, std::locale()); });\n        if(it == arg.end())\n            s << arg;\n        else if(arg.find(R\"(\")\") == std::string::npos)\n            s << R\"(\")\" << arg << R\"(\")\";\n        else\n            s << R\"(')\" << arg << R\"(')\";\n    }\n\n    return s.str();\n}\n\nstruct ini_ret_t {\n    /// This is the full name with dots\n    std::string fullname;\n\n    /// Listing of inputs\n    std::vector<std::string> inputs;\n\n    /// Current parent level\n    size_t level = 0;\n\n    /// Return parent or empty string, based on level\n    ///\n    /// Level 0, a.b.c would return a\n    /// Level 1, a.b.c could return b\n    std::string parent() const {\n        std::vector<std::string> plist = detail::split(fullname, '.');\n        if(plist.size() > (level + 1))\n            return plist[level];\n        else\n            return \"\";\n    }\n\n    /// Return name\n    std::string name() const {\n        std::vector<std::string> plist = detail::split(fullname, '.');\n        return plist.at(plist.size() - 1);\n    }\n};\n\n/// Internal parsing function\ninline std::vector<ini_ret_t> parse_ini(std::istream &input) {\n    std::string name, line;\n    std::string section = \"default\";\n\n    std::vector<ini_ret_t> output;\n\n    while(getline(input, line)) {\n        std::vector<std::string> items;\n\n        detail::trim(line);\n        size_t len = line.length();\n        if(len > 1 && line[0] == '[' && line[len - 1] == ']') {\n            section = line.substr(1, len - 2);\n        } else if(len > 0 && line[0] != ';') {\n            output.emplace_back();\n            ini_ret_t &out = output.back();\n\n            // Find = in string, split and recombine\n            auto pos = line.find(\"=\");\n            if(pos != std::string::npos) {\n                name = detail::trim_copy(line.substr(0, pos));\n                std::string item = detail::trim_copy(line.substr(pos + 1));\n                items = detail::split_up(item);\n            } else {\n                name = detail::trim_copy(line);\n                items = {\"ON\"};\n            }\n\n            if(detail::to_lower(section) == \"default\")\n                out.fullname = name;\n            else\n                out.fullname = section + \".\" + name;\n\n            out.inputs.insert(std::end(out.inputs), std::begin(items), std::end(items));\n        }\n    }\n    return output;\n}\n\n/// Parse an INI file, throw an error (ParseError:INIParseError or FileError) on failure\ninline std::vector<ini_ret_t> parse_ini(const std::string &name) {\n\n    std::ifstream input{name};\n    if(!input.good())\n        throw FileError::Missing(name);\n\n    return parse_ini(input);\n}\n\n} // namespace detail\n} // namespace CLI\n\n// From CLI/Validators.hpp:\n\nnamespace CLI {\n\n/// @defgroup validator_group Validators\n/// @brief Some validators that are provided\n///\n/// These are simple `void(std::string&)` validators that are useful. They throw\n/// a ValidationError if they fail (or the normally expected error if the cast fails)\n/// @{\n\n/// Check for an existing file\ninline std::string ExistingFile(const std::string &filename) {\n    struct stat buffer;\n    bool exist = stat(filename.c_str(), &buffer) == 0;\n    bool is_dir = (buffer.st_mode & S_IFDIR) != 0;\n    if(!exist) {\n        return \"File does not exist: \" + filename;\n    } else if(is_dir) {\n        return \"File is actually a directory: \" + filename;\n    }\n    return std::string();\n}\n\n/// Check for an existing directory\ninline std::string ExistingDirectory(const std::string &filename) {\n    struct stat buffer;\n    bool exist = stat(filename.c_str(), &buffer) == 0;\n    bool is_dir = (buffer.st_mode & S_IFDIR) != 0;\n    if(!exist) {\n        return \"Directory does not exist: \" + filename;\n    } else if(!is_dir) {\n        return \"Directory is actually a file: \" + filename;\n    }\n    return std::string();\n}\n\n/// Check for an existing path\ninline std::string ExistingPath(const std::string &filename) {\n    struct stat buffer;\n    bool const exist = stat(filename.c_str(), &buffer) == 0;\n    if(!exist) {\n        return \"Path does not exist: \" + filename;\n    }\n    return std::string();\n}\n\n/// Check for a non-existing path\ninline std::string NonexistentPath(const std::string &filename) {\n    struct stat buffer;\n    bool exist = stat(filename.c_str(), &buffer) == 0;\n    if(exist) {\n        return \"Path already exists: \" + filename;\n    }\n    return std::string();\n}\n\n/// Produce a range validator function\ntemplate <typename T> std::function<std::string(const std::string &)> Range(T min, T max) {\n    return [min, max](std::string input) {\n        T val;\n        detail::lexical_cast(input, val);\n        if(val < min || val > max)\n            return \"Value \" + input + \" not in range \" + std::to_string(min) + \" to \" + std::to_string(max);\n\n        return std::string();\n    };\n}\n\n/// Range of one value is 0 to value\ntemplate <typename T> std::function<std::string(const std::string &)> Range(T max) {\n    return Range(static_cast<T>(0), max);\n}\n\n/// @}\n\n} // namespace CLI\n\n// From CLI/Option.hpp:\n\nnamespace CLI {\n\nusing results_t = std::vector<std::string>;\nusing callback_t = std::function<bool(results_t)>;\n\nclass Option;\nclass App;\n\nusing Option_p = std::unique_ptr<Option>;\n\nenum class MultiOptionPolicy { Throw, TakeLast, TakeFirst, Join };\n\ntemplate <typename CRTP> class OptionBase {\n    friend App;\n\n  protected:\n    /// The group membership\n    std::string group_ = std::string(\"Options\");\n\n    /// True if this is a required option\n    bool required_{false};\n\n    /// Ignore the case when matching (option, not value)\n    bool ignore_case_{false};\n\n    /// Allow this option to be given in a configuration file\n    bool configurable_{true};\n\n    /// Policy for multiple arguments when `expected_ == 1`  (can be set on bool flags, too)\n    MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw};\n\n    template <typename T> void copy_to(T *other) const {\n        other->group(group_);\n        other->required(required_);\n        other->ignore_case(ignore_case_);\n        other->configurable(configurable_);\n        other->multi_option_policy(multi_option_policy_);\n    }\n\n  public:\n    // setters\n\n    /// Changes the group membership\n    CRTP *group(std::string name) {\n        group_ = name;\n        return static_cast<CRTP *>(this);\n        ;\n    }\n\n    /// Set the option as required\n    CRTP *required(bool value = true) {\n        required_ = value;\n        return static_cast<CRTP *>(this);\n    }\n\n    /// Support Plumbum term\n    CRTP *mandatory(bool value = true) { return required(value); }\n\n    // Getters\n\n    /// Get the group of this option\n    const std::string &get_group() const { return group_; }\n\n    /// True if this is a required option\n    bool get_required() const { return required_; }\n\n    /// The status of ignore case\n    bool get_ignore_case() const { return ignore_case_; }\n\n    /// The status of configurable\n    bool get_configurable() const { return configurable_; }\n\n    /// The status of the multi option policy\n    MultiOptionPolicy get_multi_option_policy() const { return multi_option_policy_; }\n\n    // Shortcuts for multi option policy\n\n    /// Set the multi option policy to take last\n    CRTP *take_last() {\n        auto self = static_cast<CRTP *>(this);\n        self->multi_option_policy(MultiOptionPolicy::TakeLast);\n        return self;\n    }\n\n    /// Set the multi option policy to take last\n    CRTP *take_first() {\n        auto self = static_cast<CRTP *>(this);\n        self->multi_option_policy(MultiOptionPolicy::TakeFirst);\n        return self;\n    }\n\n    /// Set the multi option policy to take last\n    CRTP *join() {\n        auto self = static_cast<CRTP *>(this);\n        self->multi_option_policy(MultiOptionPolicy::Join);\n        return self;\n    }\n\n    /// Allow in a configuration file\n    CRTP *configurable(bool value = true) {\n        configurable_ = value;\n        return static_cast<CRTP *>(this);\n    }\n};\n\nclass OptionDefaults : public OptionBase<OptionDefaults> {\n  public:\n    OptionDefaults() = default;\n\n    // Methods here need a different implementation if they are Option vs. OptionDefault\n\n    /// Take the last argument if given multiple times\n    OptionDefaults *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {\n        multi_option_policy_ = value;\n        return this;\n    }\n\n    /// Ignore the case of the option name\n    OptionDefaults *ignore_case(bool value = true) {\n        ignore_case_ = value;\n        return this;\n    }\n};\n\nclass Option : public OptionBase<Option> {\n    friend App;\n\n  protected:\n    /// @name Names\n    ///@{\n\n    /// A list of the short names (`-a`) without the leading dashes\n    std::vector<std::string> snames_;\n\n    /// A list of the long names (`--a`) without the leading dashes\n    std::vector<std::string> lnames_;\n\n    /// A positional name\n    std::string pname_;\n\n    /// If given, check the environment for this option\n    std::string envname_;\n\n    ///@}\n    /// @name Help\n    ///@{\n\n    /// The description for help strings\n    std::string description_;\n\n    /// A human readable default value, usually only set if default is true in creation\n    std::string defaultval_;\n\n    /// A human readable type value, set when App creates this\n    std::string typeval_;\n\n    /// True if this option has a default\n    bool default_{false};\n\n    ///@}\n    /// @name Configuration\n    ///@{\n\n    /// The number of arguments that make up one option. -1=unlimited (vector-like), 0=flag, 1=normal option,\n    /// 2=complex/pair, etc. Set only when the option is created; this is intrinsic to the type. Eventually, -2 may mean\n    /// vector of pairs.\n    int type_size_{1};\n\n    /// The number of expected values, type_size_ must be < 0. Ignored for flag. N < 0 means at least -N values.\n    int expected_{1};\n\n    /// A list of validators to run on each value parsed\n    std::vector<std::function<std::string(std::string &)>> validators_;\n\n    /// A list of options that are required with this option\n    std::set<Option *> requires_;\n\n    /// A list of options that are excluded with this option\n    std::set<Option *> excludes_;\n\n    ///@}\n    /// @name Other\n    ///@{\n\n    /// Remember the parent app\n    App *parent_;\n\n    /// Options store a callback to do all the work\n    callback_t callback_;\n\n    ///@}\n    /// @name Parsing results\n    ///@{\n\n    /// Results of parsing\n    results_t results_;\n\n    /// Whether the callback has run (needed for INI parsing)\n    bool callback_run_{false};\n\n    ///@}\n\n    /// Making an option by hand is not defined, it must be made by the App class\n    Option(std::string name,\n           std::string description = \"\",\n           std::function<bool(results_t)> callback = [](results_t) { return true; },\n           bool default_ = true,\n           App *parent = nullptr)\n        : description_(std::move(description)), default_(default_), parent_(parent), callback_(std::move(callback)) {\n        std::tie(snames_, lnames_, pname_) = detail::get_names(detail::split_names(name));\n    }\n\n  public:\n    /// @name Basic\n    ///@{\n\n    /// Count the total number of times an option was passed\n    size_t count() const { return results_.size(); }\n\n    /// This class is true if option is passed.\n    operator bool() const { return count() > 0; }\n\n    /// Clear the parsed results (mostly for testing)\n    void clear() { results_.clear(); }\n\n    ///@}\n    /// @name Setting options\n    ///@{\n\n    /// Set the number of expected arguments (Flags don't use this)\n    Option *expected(int value) {\n        // Break if this is a flag\n        if(type_size_ == 0)\n            throw IncorrectConstruction::SetFlag(single_name());\n\n        // Setting 0 is not allowed\n        else if(value == 0)\n            throw IncorrectConstruction::Set0Opt(single_name());\n\n        // No change is okay, quit now\n        else if(expected_ == value)\n            return this;\n\n        // Type must be a vector\n        else if(type_size_ >= 0)\n            throw IncorrectConstruction::ChangeNotVector(single_name());\n\n        // TODO: Can support multioption for non-1 values (except for join)\n        else if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw)\n            throw IncorrectConstruction::AfterMultiOpt(single_name());\n\n        expected_ = value;\n        return this;\n    }\n\n    /// Adds a validator\n    Option *check(std::function<std::string(const std::string &)> validator) {\n        validators_.emplace_back(validator);\n        return this;\n    }\n\n    /// Adds a validator-like function that can change result\n    Option *transform(std::function<std::string(std::string)> func) {\n        validators_.emplace_back([func](std::string &inout) {\n            try {\n                inout = func(inout);\n            } catch(const ValidationError &e) {\n                return std::string(e.what());\n            }\n            return std::string();\n        });\n        return this;\n    }\n\n    /// Sets required options\n    Option *needs(Option *opt) {\n        auto tup = requires_.insert(opt);\n        if(!tup.second)\n            throw OptionAlreadyAdded::Requires(single_name(), opt->single_name());\n        return this;\n    }\n\n    /// Can find a string if needed\n    template <typename T = App> Option *needs(std::string opt_name) {\n        for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)\n            if(opt.get() != this && opt->check_name(opt_name))\n                return needs(opt.get());\n        throw IncorrectConstruction::MissingOption(opt_name);\n    }\n\n    /// Any number supported, any mix of string and Opt\n    template <typename A, typename B, typename... ARG> Option *needs(A opt, B opt1, ARG... args) {\n        needs(opt);\n        return needs(opt1, args...);\n    }\n\n#ifndef CLI11_CPP20\n    /// Sets required options \\deprecated\n    CLI11_DEPRECATED(\"Use needs instead of requires (eventual keyword clash)\")\n    Option *requires(Option *opt) { return needs(opt); }\n\n    /// Can find a string if needed \\deprecated\n    template <typename T = App> Option *requires(std::string opt_name) {\n        for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)\n            if(opt.get() != this && opt->check_name(opt_name))\n                return needs(opt.get());\n        throw IncorrectConstruction::MissingOption(opt_name);\n    }\n\n    /// Any number supported, any mix of string and Opt \\deprecated\n    template <typename A, typename B, typename... ARG> Option *requires(A opt, B opt1, ARG... args) {\n        requires(opt);\n        return requires(opt1, args...);\n    }\n#endif\n\n    /// Sets excluded options\n    Option *excludes(Option *opt) {\n        excludes_.insert(opt);\n\n        // Help text should be symmetric - excluding a should exclude b\n        opt->excludes_.insert(this);\n\n        // Ignoring the insert return value, excluding twice is now allowed.\n        // (Mostly to allow both directions to be excluded by user, even though the library does it for you.)\n\n        return this;\n    }\n\n    /// Can find a string if needed\n    template <typename T = App> Option *excludes(std::string opt_name) {\n        for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)\n            if(opt.get() != this && opt->check_name(opt_name))\n                return excludes(opt.get());\n        throw IncorrectConstruction::MissingOption(opt_name);\n    }\n\n    /// Any number supported, any mix of string and Opt\n    template <typename A, typename B, typename... ARG> Option *excludes(A opt, B opt1, ARG... args) {\n        excludes(opt);\n        return excludes(opt1, args...);\n    }\n\n    /// Sets environment variable to read if no option given\n    Option *envname(std::string name) {\n        envname_ = name;\n        return this;\n    }\n\n    /// Ignore case\n    ///\n    /// The template hides the fact that we don't have the definition of App yet.\n    /// You are never expected to add an argument to the template here.\n    template <typename T = App> Option *ignore_case(bool value = true) {\n        ignore_case_ = value;\n        auto *parent = dynamic_cast<T *>(parent_);\n\n        for(const Option_p &opt : parent->options_)\n            if(opt.get() != this && *opt == *this)\n                throw OptionAlreadyAdded(opt->get_name());\n\n        return this;\n    }\n\n    /// Take the last argument if given multiple times (or another policy)\n    Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {\n\n        if(get_items_expected() < 0)\n            throw IncorrectConstruction::MultiOptionPolicy(single_name());\n        multi_option_policy_ = value;\n        return this;\n    }\n\n    ///@}\n    /// @name Accessors\n    ///@{\n\n    /// The number of arguments the option expects\n    int get_type_size() const { return type_size_; }\n\n    /// The number of times the option expects to be included\n    int get_expected() const { return expected_; }\n\n    /// \\breif The total number of expected values (including the type)\n    /// This is positive if exactly this number is expected, and negitive for at least N values\n    ///\n    /// v = fabs(size_type*expected)\n    /// !MultiOptionPolicy::Throw\n    ///           | Expected < 0  | Expected == 0 | Expected > 0\n    /// Size < 0  |      -v       |       0       |     -v\n    /// Size == 0 |       0       |       0       |      0\n    /// Size > 0  |      -v       |       0       |     -v       // Expected must be 1\n    ///\n    /// MultiOptionPolicy::Throw\n    ///           | Expected < 0  | Expected == 0 | Expected > 0\n    /// Size < 0  |      -v       |       0       |      v\n    /// Size == 0 |       0       |       0       |      0\n    /// Size > 0  |       v       |       0       |      v      // Expected must be 1\n    ///\n    int get_items_expected() const {\n        return std::abs(type_size_ * expected_) *\n               ((multi_option_policy_ != MultiOptionPolicy::Throw || (expected_ < 0 && type_size_ < 0) ? -1 : 1));\n    }\n\n    /// True if this has a default value\n    int get_default() const { return default_; }\n\n    /// True if the argument can be given directly\n    bool get_positional() const { return pname_.length() > 0; }\n\n    /// True if option has at least one non-positional name\n    bool nonpositional() const { return (snames_.size() + lnames_.size()) > 0; }\n\n    /// True if option has description\n    bool has_description() const { return description_.length() > 0; }\n\n    /// Get the description\n    const std::string &get_description() const { return description_; }\n\n    // Just the pname\n    std::string get_pname() const { return pname_; }\n\n    ///@}\n    /// @name Help tools\n    ///@{\n\n    /// Gets a , sep list of names. Does not include the positional name if opt_only=true.\n    std::string get_name(bool opt_only = false) const {\n        std::vector<std::string> name_list;\n        if(!opt_only && pname_.length() > 0)\n            name_list.push_back(pname_);\n        for(const std::string &sname : snames_)\n            name_list.push_back(\"-\" + sname);\n        for(const std::string &lname : lnames_)\n            name_list.push_back(\"--\" + lname);\n        return detail::join(name_list);\n    }\n\n    /// The name and any extras needed for positionals\n    std::string help_positional() const {\n        std::string out = pname_;\n        if(get_expected() > 1)\n            out = out + \"(\" + std::to_string(get_expected()) + \"x)\";\n        else if(get_expected() == -1)\n            out = out + \"...\";\n        out = get_required() ? out : \"[\" + out + \"]\";\n        return out;\n    }\n\n    /// The most descriptive name available\n    std::string single_name() const {\n        if(!lnames_.empty())\n            return std::string(\"--\") + lnames_[0];\n        else if(!snames_.empty())\n            return std::string(\"-\") + snames_[0];\n        else\n            return pname_;\n    }\n\n    /// The first half of the help print, name plus default, etc. Setting opt_only to true avoids the positional name.\n    std::string help_name(bool opt_only = false) const {\n        std::stringstream out;\n        out << get_name(opt_only) << help_aftername();\n        return out.str();\n    }\n\n    /// pname with type info\n    std::string help_pname() const {\n        std::stringstream out;\n        out << get_pname() << help_aftername();\n        return out.str();\n    }\n\n    /// This is the part after the name is printed but before the description\n    std::string help_aftername() const {\n        std::stringstream out;\n\n        if(get_type_size() != 0) {\n            if(!typeval_.empty())\n                out << \" \" << typeval_;\n            if(!defaultval_.empty())\n                out << \"=\" << defaultval_;\n            if(get_expected() > 1)\n                out << \" x \" << get_expected();\n            if(get_expected() == -1)\n                out << \" ...\";\n            if(get_required())\n                out << \" (REQUIRED)\";\n        }\n        if(!envname_.empty())\n            out << \" (env:\" << envname_ << \")\";\n        if(!requires_.empty()) {\n            out << \" Needs:\";\n            for(const Option *opt : requires_)\n                out << \" \" << opt->single_name();\n        }\n        if(!excludes_.empty()) {\n            out << \" Excludes:\";\n            for(const Option *opt : excludes_)\n                out << \" \" << opt->single_name();\n        }\n        return out.str();\n    }\n\n    ///@}\n    /// @name Parser tools\n    ///@{\n\n    /// Process the callback\n    void run_callback() {\n\n        // Run the validators (can change the string)\n        if(!validators_.empty()) {\n            for(std::string &result : results_)\n                for(const std::function<std::string(std::string &)> &vali : validators_) {\n                    std::string err_msg = vali(result);\n                    if(!err_msg.empty())\n                        throw ValidationError(single_name(), err_msg);\n                }\n        }\n\n        bool local_result;\n\n        // Num items expected or length of vector, always at least 1\n        // Only valid for a trimming policy\n        int trim_size = std::min(std::max(std::abs(get_items_expected()), 1), static_cast<int>(results_.size()));\n\n        // Operation depends on the policy setting\n        if(multi_option_policy_ == MultiOptionPolicy::TakeLast) {\n            // Allow multi-option sizes (including 0)\n            results_t partial_result{results_.end() - trim_size, results_.end()};\n            local_result = !callback_(partial_result);\n\n        } else if(multi_option_policy_ == MultiOptionPolicy::TakeFirst) {\n            results_t partial_result{results_.begin(), results_.begin() + trim_size};\n            local_result = !callback_(partial_result);\n\n        } else if(multi_option_policy_ == MultiOptionPolicy::Join) {\n            results_t partial_result = {detail::join(results_, \"\\n\")};\n            local_result = !callback_(partial_result);\n\n        } else {\n            // For now, vector of non size 1 types are not supported but possibility included here\n            if((get_items_expected() > 0 && results_.size() != static_cast<size_t>(get_items_expected())) ||\n               (get_items_expected() < 0 && results_.size() < static_cast<size_t>(-get_items_expected())))\n                throw ArgumentMismatch(single_name(), get_items_expected(), results_.size());\n            else\n                local_result = !callback_(results_);\n        }\n\n        if(local_result)\n            throw ConversionError(get_name(), results_);\n    }\n\n    /// If options share any of the same names, they are equal (not counting positional)\n    bool operator==(const Option &other) const {\n        for(const std::string &sname : snames_)\n            if(other.check_sname(sname))\n                return true;\n        for(const std::string &lname : lnames_)\n            if(other.check_lname(lname))\n                return true;\n        // We need to do the inverse, just in case we are ignore_case\n        for(const std::string &sname : other.snames_)\n            if(check_sname(sname))\n                return true;\n        for(const std::string &lname : other.lnames_)\n            if(check_lname(lname))\n                return true;\n        return false;\n    }\n\n    /// Check a name. Requires \"-\" or \"--\" for short / long, supports positional name\n    bool check_name(std::string name) const {\n\n        if(name.length() > 2 && name.substr(0, 2) == \"--\")\n            return check_lname(name.substr(2));\n        else if(name.length() > 1 && name.substr(0, 1) == \"-\")\n            return check_sname(name.substr(1));\n        else {\n            std::string local_pname = pname_;\n            if(ignore_case_) {\n                local_pname = detail::to_lower(local_pname);\n                name = detail::to_lower(name);\n            }\n            return name == local_pname;\n        }\n    }\n\n    /// Requires \"-\" to be removed from string\n    bool check_sname(std::string name) const {\n        if(ignore_case_) {\n            name = detail::to_lower(name);\n            return std::find_if(std::begin(snames_), std::end(snames_), [&name](std::string local_sname) {\n                       return detail::to_lower(local_sname) == name;\n                   }) != std::end(snames_);\n        } else\n            return std::find(std::begin(snames_), std::end(snames_), name) != std::end(snames_);\n    }\n\n    /// Requires \"--\" to be removed from string\n    bool check_lname(std::string name) const {\n        if(ignore_case_) {\n            name = detail::to_lower(name);\n            return std::find_if(std::begin(lnames_), std::end(lnames_), [&name](std::string local_sname) {\n                       return detail::to_lower(local_sname) == name;\n                   }) != std::end(lnames_);\n        } else\n            return std::find(std::begin(lnames_), std::end(lnames_), name) != std::end(lnames_);\n    }\n\n    /// Puts a result at the end, unless last_ is set, in which case it just keeps the last one\n    void add_result(std::string s) {\n        results_.push_back(s);\n        callback_run_ = false;\n    }\n\n    /// Get a copy of the results\n    std::vector<std::string> results() const { return results_; }\n\n    /// See if the callback has been run already\n    bool get_callback_run() const { return callback_run_; }\n\n    ///@}\n    /// @name Custom options\n    ///@{\n\n    /// Set a custom option, typestring, type_size\n    void set_custom_option(std::string typeval, int type_size = 1) {\n        typeval_ = typeval;\n        type_size_ = type_size;\n        if(type_size_ == 0)\n            required_ = false;\n        if(type_size < 0)\n            expected_ = -1;\n    }\n\n    /// Set the default value string representation\n    void set_default_str(std::string val) { defaultval_ = val; }\n\n    /// Set the default value string representation and evaluate\n    void set_default_val(std::string val) {\n        set_default_str(val);\n        auto old_results = results_;\n        results_ = {val};\n        run_callback();\n        results_ = std::move(old_results);\n    }\n\n    /// Set the type name displayed on this option\n    void set_type_name(std::string val) { typeval_ = val; }\n\n    /// Get the typename for this option\n    std::string get_type_name() const { return typeval_; }\n\n    ///@}\n\n  protected:\n    /// @name App Helpers\n    ///@{\n    /// Can print positional name detailed option if true\n    bool _has_help_positional() const {\n        return get_positional() && (has_description() || !requires_.empty() || !excludes_.empty());\n    }\n    ///@}\n};\n\n} // namespace CLI\n\n// From CLI/App.hpp:\n\nnamespace CLI {\n\n#ifndef CLI11_PARSE\n#define CLI11_PARSE(app, argc, argv)                                                                                   \\\n    try {                                                                                                              \\\n        (app).parse((argc), (argv));                                                                                   \\\n    } catch(const CLI::ParseError &e) {                                                                                \\\n        return (app).exit(e);                                                                                          \\\n    }\n#endif\n\nnamespace detail {\nenum class Classifer { NONE, POSITIONAL_MARK, SHORT, LONG, SUBCOMMAND };\nstruct AppFriend;\n} // namespace detail\n\nnamespace FailureMessage {\nstd::string simple(const App *app, const Error &e);\nstd::string help(const App *app, const Error &e);\n} // namespace FailureMessage\n\nclass App;\n\nusing App_p = std::unique_ptr<App>;\n\n/// Creates a command line program, with very few defaults.\n/** To use, create a new `Program()` instance with `argc`, `argv`, and a help description. The templated\n *  add_option methods make it easy to prepare options. Remember to call `.start` before starting your\n * program, so that the options can be evaluated and the help option doesn't accidentally run your program. */\nclass App {\n    friend Option;\n    friend detail::AppFriend;\n\n  protected:\n    // This library follows the Google style guide for member names ending in underscores\n\n    /// @name Basics\n    ///@{\n\n    /// Subcommand name or program name (from parser if name is empty)\n    std::string name_;\n\n    /// Description of the current program/subcommand\n    std::string description_;\n\n    /// If true, allow extra arguments (ie, don't throw an error). INHERITABLE\n    bool allow_extras_{false};\n\n    /// If true, allow extra arguments in the ini file (ie, don't throw an error). INHERITABLE\n    bool allow_ini_extras_{false};\n\n    ///  If true, return immediately on an unrecognised option (implies allow_extras) INHERITABLE\n    bool prefix_command_{false};\n\n    /// This is a function that runs when complete. Great for subcommands. Can throw.\n    std::function<void()> callback_;\n\n    ///@}\n    /// @name Options\n    ///@{\n\n    /// The default values for options, customizable and changeable INHERITABLE\n    OptionDefaults option_defaults_;\n\n    /// The list of options, stored locally\n    std::vector<Option_p> options_;\n\n    ///@}\n    /// @name Help\n    ///@{\n\n    /// Footer to put after all options in the help output INHERITABLE\n    std::string footer_;\n\n    /// A pointer to the help flag if there is one INHERITABLE\n    Option *help_ptr_{nullptr};\n\n    /// The error message printing function INHERITABLE\n    std::function<std::string(const App *, const Error &e)> failure_message_ = FailureMessage::simple;\n\n    ///@}\n    /// @name Parsing\n    ///@{\n\n    using missing_t = std::vector<std::pair<detail::Classifer, std::string>>;\n\n    /// Pair of classifier, string for missing options. (extra detail is removed on returning from parse)\n    ///\n    /// This is faster and cleaner than storing just a list of strings and reparsing. This may contain the -- separator.\n    missing_t missing_;\n\n    /// This is a list of pointers to options with the original parse order\n    std::vector<Option *> parse_order_;\n\n    /// This is a list of the subcommands collected, in order\n    std::vector<App *> parsed_subcommands_;\n\n    ///@}\n    /// @name Subcommands\n    ///@{\n\n    /// Storage for subcommand list\n    std::vector<App_p> subcommands_;\n\n    /// If true, the program name is not case sensitive INHERITABLE\n    bool ignore_case_{false};\n\n    /// Allow subcommand fallthrough, so that parent commands can collect commands after subcommand.  INHERITABLE\n    bool fallthrough_{false};\n\n    /// A pointer to the parent if this is a subcommand\n    App *parent_{nullptr};\n\n    /// True if this command/subcommand was parsed\n    bool parsed_{false};\n\n    /// Minimum required subcommands\n    size_t require_subcommand_min_ = 0;\n\n    /// Max number of subcommands allowed (parsing stops after this number). 0 is unlimited INHERITABLE\n    size_t require_subcommand_max_ = 0;\n\n    /// The group membership INHERITABLE\n    std::string group_{\"Subcommands\"};\n\n    ///@}\n    /// @name Config\n    ///@{\n\n    /// The name of the connected config file\n    std::string config_name_;\n\n    /// True if ini is required (throws if not present), if false simply keep going.\n    bool config_required_{false};\n\n    /// Pointer to the config option\n    Option *config_ptr_{nullptr};\n\n    ///@}\n\n    /// Special private constructor for subcommand\n    App(std::string description_, std::string name, App *parent)\n        : name_(std::move(name)), description_(std::move(description_)), parent_(parent) {\n        // Inherit if not from a nullptr\n        if(parent_ != nullptr) {\n            if(parent_->help_ptr_ != nullptr)\n                set_help_flag(parent_->help_ptr_->get_name(), parent_->help_ptr_->get_description());\n\n            /// OptionDefaults\n            option_defaults_ = parent_->option_defaults_;\n\n            // INHERITABLE\n            failure_message_ = parent_->failure_message_;\n            allow_extras_ = parent_->allow_extras_;\n            allow_ini_extras_ = parent_->allow_ini_extras_;\n            prefix_command_ = parent_->prefix_command_;\n            ignore_case_ = parent_->ignore_case_;\n            fallthrough_ = parent_->fallthrough_;\n            group_ = parent_->group_;\n            footer_ = parent_->footer_;\n            require_subcommand_max_ = parent_->require_subcommand_max_;\n        }\n    }\n\n  public:\n    /// @name Basic\n    ///@{\n\n    /// Create a new program. Pass in the same arguments as main(), along with a help string.\n    explicit App(std::string description_ = \"\", std::string name = \"\") : App(description_, name, nullptr) {\n        set_help_flag(\"-h,--help\", \"Print this help message and exit\");\n    }\n\n    /// virtual destructor\n    virtual ~App() = default;\n\n    /// Set a callback for the end of parsing.\n    ///\n    /// Due to a bug in c++11,\n    /// it is not possible to overload on std::function (fixed in c++14\n    /// and backported to c++11 on newer compilers). Use capture by reference\n    /// to get a pointer to App if needed.\n    App *set_callback(std::function<void()> callback) {\n        callback_ = callback;\n        return this;\n    }\n\n    /// Set a name for the app (empty will use parser to set the name)\n    App *set_name(std::string name = \"\") {\n        name_ = name;\n        return this;\n    }\n\n    /// Remove the error when extras are left over on the command line.\n    App *allow_extras(bool allow = true) {\n        allow_extras_ = allow;\n        return this;\n    }\n\n    /// Remove the error when extras are left over on the command line.\n    /// Will also call App::allow_extras().\n    App *allow_ini_extras(bool allow = true) {\n        allow_extras(allow);\n        allow_ini_extras_ = allow;\n        return this;\n    }\n\n    /// Do not parse anything after the first unrecognised option and return\n    App *prefix_command(bool allow = true) {\n        prefix_command_ = allow;\n        return this;\n    }\n\n    /// Ignore case. Subcommand inherit value.\n    App *ignore_case(bool value = true) {\n        ignore_case_ = value;\n        if(parent_ != nullptr) {\n            for(const auto &subc : parent_->subcommands_) {\n                if(subc.get() != this && (this->check_name(subc->name_) || subc->check_name(this->name_)))\n                    throw OptionAlreadyAdded(subc->name_);\n            }\n        }\n        return this;\n    }\n\n    /// Check to see if this subcommand was parsed, true only if received on command line.\n    bool parsed() const { return parsed_; }\n\n    /// Get the OptionDefault object, to set option defaults\n    OptionDefaults *option_defaults() { return &option_defaults_; }\n\n    ///@}\n    /// @name Adding options\n    ///@{\n\n    /// Add an option, will automatically understand the type for common types.\n    ///\n    /// To use, create a variable with the expected type, and pass it in after the name.\n    /// After start is called, you can use count to see if the value was passed, and\n    /// the value will be initialized properly. Numbers, vectors, and strings are supported.\n    ///\n    /// ->required(), ->default, and the validators are options,\n    /// The positional options take an optional number of arguments.\n    ///\n    /// For example,\n    ///\n    ///     std::string filename;\n    ///     program.add_option(\"filename\", filename, \"description of filename\");\n    ///\n    Option *add_option(std::string name, callback_t callback, std::string description = \"\", bool defaulted = false) {\n        Option myopt{name, description, callback, defaulted, this};\n\n        if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) {\n               return *v == myopt;\n           }) == std::end(options_)) {\n            options_.emplace_back();\n            Option_p &option = options_.back();\n            option.reset(new Option(name, description, callback, defaulted, this));\n            option_defaults_.copy_to(option.get());\n            return option.get();\n        } else\n            throw OptionAlreadyAdded(myopt.get_name());\n    }\n\n    /// Add option for non-vectors (duplicate copy needed without defaulted to avoid `iostream << value`)\n    template <typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>\n    Option *add_option(std::string name,\n                       T &variable, ///< The variable to set\n                       std::string description = \"\") {\n\n        std::string simple_name = CLI::detail::split(name, ',').at(0);\n        CLI::callback_t fun = [&variable, simple_name](CLI::results_t res) {\n            return detail::lexical_cast(res[0], variable);\n        };\n\n        Option *opt = add_option(name, fun, description, false);\n        opt->set_custom_option(detail::type_name<T>());\n        return opt;\n    }\n\n    /// Add option for non-vectors with a default print\n    template <typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>\n    Option *add_option(std::string name,\n                       T &variable, ///< The variable to set\n                       std::string description,\n                       bool defaulted) {\n\n        std::string simple_name = CLI::detail::split(name, ',').at(0);\n        CLI::callback_t fun = [&variable, simple_name](CLI::results_t res) {\n            return detail::lexical_cast(res[0], variable);\n        };\n\n        Option *opt = add_option(name, fun, description, defaulted);\n        opt->set_custom_option(detail::type_name<T>());\n        if(defaulted) {\n            std::stringstream out;\n            out << variable;\n            opt->set_default_str(out.str());\n        }\n        return opt;\n    }\n\n    /// Add option for vectors (no default)\n    template <typename T>\n    Option *add_option(std::string name,\n                       std::vector<T> &variable, ///< The variable vector to set\n                       std::string description = \"\") {\n\n        CLI::callback_t fun = [&variable](CLI::results_t res) {\n            bool retval = true;\n            variable.clear();\n            for(const auto &a : res) {\n                variable.emplace_back();\n                retval &= detail::lexical_cast(a, variable.back());\n            }\n            return (!variable.empty()) && retval;\n        };\n\n        Option *opt = add_option(name, fun, description, false);\n        opt->set_custom_option(detail::type_name<T>(), -1);\n        return opt;\n    }\n\n    /// Add option for vectors\n    template <typename T>\n    Option *add_option(std::string name,\n                       std::vector<T> &variable, ///< The variable vector to set\n                       std::string description,\n                       bool defaulted) {\n\n        CLI::callback_t fun = [&variable](CLI::results_t res) {\n            bool retval = true;\n            variable.clear();\n            for(const auto &a : res) {\n                variable.emplace_back();\n                retval &= detail::lexical_cast(a, variable.back());\n            }\n            return (!variable.empty()) && retval;\n        };\n\n        Option *opt = add_option(name, fun, description, defaulted);\n        opt->set_custom_option(detail::type_name<T>(), -1);\n        if(defaulted)\n            opt->set_default_str(\"[\" + detail::join(variable) + \"]\");\n        return opt;\n    }\n\n    /// Set a help flag, replaced the existing one if present\n    Option *set_help_flag(std::string name = \"\", std::string description = \"\") {\n        if(help_ptr_ != nullptr) {\n            remove_option(help_ptr_);\n            help_ptr_ = nullptr;\n        }\n\n        // Empty name will simply remove the help flag\n        if(!name.empty()) {\n            help_ptr_ = add_flag(name, description);\n            help_ptr_->configurable(false);\n        }\n\n        return help_ptr_;\n    }\n\n    /// Add option for flag\n    Option *add_flag(std::string name, std::string description = \"\") {\n        CLI::callback_t fun = [](CLI::results_t) { return true; };\n\n        Option *opt = add_option(name, fun, description, false);\n        if(opt->get_positional())\n            throw IncorrectConstruction::PositionalFlag(name);\n        opt->set_custom_option(\"\", 0);\n        return opt;\n    }\n\n    /// Add option for flag integer\n    template <typename T,\n              enable_if_t<std::is_integral<T>::value && !is_bool<T>::value, detail::enabler> = detail::dummy>\n    Option *add_flag(std::string name,\n                     T &count, ///< A variable holding the count\n                     std::string description = \"\") {\n\n        count = 0;\n        CLI::callback_t fun = [&count](CLI::results_t res) {\n            count = static_cast<T>(res.size());\n            return true;\n        };\n\n        Option *opt = add_option(name, fun, description, false);\n        if(opt->get_positional())\n            throw IncorrectConstruction::PositionalFlag(name);\n        opt->set_custom_option(\"\", 0);\n        return opt;\n    }\n\n    /// Bool version - defaults to allowing multiple passings, but can be forced to one if\n    /// `multi_option_policy(CLI::MultiOptionPolicy::Throw)` is used.\n    template <typename T, enable_if_t<is_bool<T>::value, detail::enabler> = detail::dummy>\n    Option *add_flag(std::string name,\n                     T &count, ///< A variable holding true if passed\n                     std::string description = \"\") {\n\n        count = false;\n        CLI::callback_t fun = [&count](CLI::results_t res) {\n            count = true;\n            return res.size() == 1;\n        };\n\n        Option *opt = add_option(name, fun, description, false);\n        if(opt->get_positional())\n            throw IncorrectConstruction::PositionalFlag(name);\n        opt->set_custom_option(\"\", 0);\n        opt->multi_option_policy(CLI::MultiOptionPolicy::TakeLast);\n        return opt;\n    }\n\n    /// Add option for callback\n    Option *add_flag_function(std::string name,\n                              std::function<void(size_t)> function, ///< A function to call, void(size_t)\n                              std::string description = \"\") {\n\n        CLI::callback_t fun = [function](CLI::results_t res) {\n            auto count = static_cast<size_t>(res.size());\n            function(count);\n            return true;\n        };\n\n        Option *opt = add_option(name, fun, description, false);\n        if(opt->get_positional())\n            throw IncorrectConstruction::PositionalFlag(name);\n        opt->set_custom_option(\"\", 0);\n        return opt;\n    }\n\n#ifdef CLI11_CPP14\n    /// Add option for callback (C++14 or better only)\n    Option *add_flag(std::string name,\n                     std::function<void(size_t)> function, ///< A function to call, void(size_t)\n                     std::string description = \"\") {\n        return add_flag_function(name, function, description);\n    }\n#endif\n\n    /// Add set of options (No default)\n    template <typename T>\n    Option *add_set(std::string name,\n                    T &member,           ///< The selected member of the set\n                    std::set<T> options, ///< The set of possibilities\n                    std::string description = \"\") {\n\n        std::string simple_name = CLI::detail::split(name, ',').at(0);\n        CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {\n            bool retval = detail::lexical_cast(res[0], member);\n            if(!retval)\n                throw ConversionError(res[0], simple_name);\n            return std::find(std::begin(options), std::end(options), member) != std::end(options);\n        };\n\n        Option *opt = add_option(name, fun, description, false);\n        std::string typeval = detail::type_name<T>();\n        typeval += \" in {\" + detail::join(options) + \"}\";\n        opt->set_custom_option(typeval);\n        return opt;\n    }\n\n    /// Add set of options\n    template <typename T>\n    Option *add_set(std::string name,\n                    T &member,           ///< The selected member of the set\n                    std::set<T> options, ///< The set of posibilities\n                    std::string description,\n                    bool defaulted) {\n\n        std::string simple_name = CLI::detail::split(name, ',').at(0);\n        CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {\n            bool retval = detail::lexical_cast(res[0], member);\n            if(!retval)\n                throw ConversionError(res[0], simple_name);\n            return std::find(std::begin(options), std::end(options), member) != std::end(options);\n        };\n\n        Option *opt = add_option(name, fun, description, defaulted);\n        std::string typeval = detail::type_name<T>();\n        typeval += \" in {\" + detail::join(options) + \"}\";\n        opt->set_custom_option(typeval);\n        if(defaulted) {\n            std::stringstream out;\n            out << member;\n            opt->set_default_str(out.str());\n        }\n        return opt;\n    }\n\n    /// Add set of options, string only, ignore case (no default)\n    Option *add_set_ignore_case(std::string name,\n                                std::string &member,           ///< The selected member of the set\n                                std::set<std::string> options, ///< The set of possibilities\n                                std::string description = \"\") {\n\n        std::string simple_name = CLI::detail::split(name, ',').at(0);\n        CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {\n            member = detail::to_lower(res[0]);\n            auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {\n                return detail::to_lower(val) == member;\n            });\n            if(iter == std::end(options))\n                throw ConversionError(member, simple_name);\n            else {\n                member = *iter;\n                return true;\n            }\n        };\n\n        Option *opt = add_option(name, fun, description, false);\n        std::string typeval = detail::type_name<std::string>();\n        typeval += \" in {\" + detail::join(options) + \"}\";\n        opt->set_custom_option(typeval);\n\n        return opt;\n    }\n\n    /// Add set of options, string only, ignore case\n    Option *add_set_ignore_case(std::string name,\n                                std::string &member,           ///< The selected member of the set\n                                std::set<std::string> options, ///< The set of posibilities\n                                std::string description,\n                                bool defaulted) {\n\n        std::string simple_name = CLI::detail::split(name, ',').at(0);\n        CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {\n            member = detail::to_lower(res[0]);\n            auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {\n                return detail::to_lower(val) == member;\n            });\n            if(iter == std::end(options))\n                throw ConversionError(member, simple_name);\n            else {\n                member = *iter;\n                return true;\n            }\n        };\n\n        Option *opt = add_option(name, fun, description, defaulted);\n        std::string typeval = detail::type_name<std::string>();\n        typeval += \" in {\" + detail::join(options) + \"}\";\n        opt->set_custom_option(typeval);\n        if(defaulted) {\n            opt->set_default_str(member);\n        }\n        return opt;\n    }\n\n    /// Add a complex number\n    template <typename T>\n    Option *add_complex(std::string name,\n                        T &variable,\n                        std::string description = \"\",\n                        bool defaulted = false,\n                        std::string label = \"COMPLEX\") {\n\n        std::string simple_name = CLI::detail::split(name, ',').at(0);\n        CLI::callback_t fun = [&variable, simple_name, label](results_t res) {\n            if(res[1].back() == 'i')\n                res[1].pop_back();\n            double x, y;\n            bool worked = detail::lexical_cast(res[0], x) && detail::lexical_cast(res[1], y);\n            if(worked)\n                variable = T(x, y);\n            return worked;\n        };\n\n        CLI::Option *opt = add_option(name, fun, description, defaulted);\n        opt->set_custom_option(label, 2);\n        if(defaulted) {\n            std::stringstream out;\n            out << variable;\n            opt->set_default_str(out.str());\n        }\n        return opt;\n    }\n\n    /// Set a configuration ini file option, or clear it if no name passed\n    Option *set_config(std::string name = \"\",\n                       std::string default_filename = \"\",\n                       std::string help = \"Read an ini file\",\n                       bool required = false) {\n\n        // Remove existing config if present\n        if(config_ptr_ != nullptr)\n            remove_option(config_ptr_);\n\n        // Only add config if option passed\n        if(!name.empty()) {\n            config_name_ = default_filename;\n            config_required_ = required;\n            config_ptr_ = add_option(name, config_name_, help, !default_filename.empty());\n            config_ptr_->configurable(false);\n        }\n\n        return config_ptr_;\n    }\n\n    /// Removes an option from the App. Takes an option pointer. Returns true if found and removed.\n    bool remove_option(Option *opt) {\n        auto iterator =\n            std::find_if(std::begin(options_), std::end(options_), [opt](const Option_p &v) { return v.get() == opt; });\n        if(iterator != std::end(options_)) {\n            options_.erase(iterator);\n            return true;\n        }\n        return false;\n    }\n\n    ///@}\n    /// @name Subcommmands\n    ///@{\n\n    /// Add a subcommand. Inherits INHERITABLE and OptionDefaults, and help flag\n    App *add_subcommand(std::string name, std::string description = \"\") {\n        subcommands_.emplace_back(new App(description, name, this));\n        for(const auto &subc : subcommands_)\n            if(subc.get() != subcommands_.back().get())\n                if(subc->check_name(subcommands_.back()->name_) || subcommands_.back()->check_name(subc->name_))\n                    throw OptionAlreadyAdded(subc->name_);\n        return subcommands_.back().get();\n    }\n\n    /// Check to see if a subcommand is part of this command (doesn't have to be in command line)\n    App *get_subcommand(App *subcom) const {\n        for(const App_p &subcomptr : subcommands_)\n            if(subcomptr.get() == subcom)\n                return subcom;\n        throw OptionNotFound(subcom->get_name());\n    }\n\n    /// Check to see if a subcommand is part of this command (text version)\n    App *get_subcommand(std::string subcom) const {\n        for(const App_p &subcomptr : subcommands_)\n            if(subcomptr->check_name(subcom))\n                return subcomptr.get();\n        throw OptionNotFound(subcom);\n    }\n\n    /// Changes the group membership\n    App *group(std::string name) {\n        group_ = name;\n        return this;\n    }\n\n    /// The argumentless form of require subcommand requires 1 or more subcommands\n    App *require_subcommand() {\n        require_subcommand_min_ = 1;\n        require_subcommand_max_ = 0;\n        return this;\n    }\n\n    /// Require a subcommand to be given (does not affect help call)\n    /// The number required can be given. Negative values indicate maximum\n    /// number allowed (0 for any number). Max number inheritable.\n    App *require_subcommand(int value) {\n        if(value < 0) {\n            require_subcommand_min_ = 0;\n            require_subcommand_max_ = static_cast<size_t>(-value);\n        } else {\n            require_subcommand_min_ = static_cast<size_t>(value);\n            require_subcommand_max_ = static_cast<size_t>(value);\n        }\n        return this;\n    }\n\n    /// Explicitly control the number of subcommands required. Setting 0\n    /// for the max means unlimited number allowed. Max number inheritable.\n    App *require_subcommand(size_t min, size_t max) {\n        require_subcommand_min_ = min;\n        require_subcommand_max_ = max;\n        return this;\n    }\n\n    /// Stop subcommand fallthrough, so that parent commands cannot collect commands after subcommand.\n    /// Default from parent, usually set on parent.\n    App *fallthrough(bool value = true) {\n        fallthrough_ = value;\n        return this;\n    }\n\n    /// Check to see if this subcommand was parsed, true only if received on command line.\n    /// This allows the subcommand to be directly checked.\n    operator bool() const { return parsed_; }\n\n    ///@}\n    /// @name Extras for subclassing\n    ///@{\n\n    /// This allows subclasses to inject code before callbacks but after parse.\n    ///\n    /// This does not run if any errors or help is thrown.\n    virtual void pre_callback() {}\n\n    ///@}\n    /// @name Parsing\n    ///@{\n\n    /// Parses the command line - throws errors\n    /// This must be called after the options are in but before the rest of the program.\n    void parse(int argc, char **argv) {\n        // If the name is not set, read from command line\n        if(name_.empty())\n            name_ = argv[0];\n\n        std::vector<std::string> args;\n        for(int i = argc - 1; i > 0; i--)\n            args.emplace_back(argv[i]);\n        parse(args);\n    }\n\n    /// The real work is done here. Expects a reversed vector.\n    /// Changes the vector to the remaining options.\n    void parse(std::vector<std::string> &args) {\n        _validate();\n        _parse(args);\n        run_callback();\n    }\n\n    /// Provide a function to print a help message. The function gets access to the App pointer and error.\n    void set_failure_message(std::function<std::string(const App *, const Error &e)> function) {\n        failure_message_ = function;\n    }\n\n    /// Print a nice error message and return the exit code\n    int exit(const Error &e, std::ostream &out = std::cout, std::ostream &err = std::cerr) const {\n\n        /// Avoid printing anything if this is a CLI::RuntimeError\n        if(dynamic_cast<const CLI::RuntimeError *>(&e) != nullptr)\n            return e.get_exit_code();\n\n        if(dynamic_cast<const CLI::CallForHelp *>(&e) != nullptr) {\n            out << help();\n            return e.get_exit_code();\n        }\n\n        if(e.get_exit_code() != static_cast<int>(ExitCodes::Success)) {\n            if(failure_message_)\n                err << failure_message_(this, e) << std::flush;\n        }\n\n        return e.get_exit_code();\n    }\n\n    /// Reset the parsed data\n    void reset() {\n\n        parsed_ = false;\n        missing_.clear();\n        parsed_subcommands_.clear();\n\n        for(const Option_p &opt : options_) {\n            opt->clear();\n        }\n        for(const App_p &app : subcommands_) {\n            app->reset();\n        }\n    }\n\n    ///@}\n    /// @name Post parsing\n    ///@{\n\n    /// Counts the number of times the given option was passed.\n    size_t count(std::string name) const {\n        for(const Option_p &opt : options_) {\n            if(opt->check_name(name)) {\n                return opt->count();\n            }\n        }\n        throw OptionNotFound(name);\n    }\n\n    /// Get a subcommand pointer list to the currently selected subcommands (after parsing by default, in command line\n    /// order)\n    std::vector<App *> get_subcommands(bool parsed = true) const {\n        if(parsed) {\n            return parsed_subcommands_;\n        } else {\n            std::vector<App *> subcomms(subcommands_.size());\n            std::transform(std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) {\n                return v.get();\n            });\n            return subcomms;\n        }\n    }\n\n    /// Check to see if given subcommand was selected\n    bool got_subcommand(App *subcom) const {\n        // get subcom needed to verify that this was a real subcommand\n        return get_subcommand(subcom)->parsed_;\n    }\n\n    /// Check with name instead of pointer to see if subcommand was selected\n    bool got_subcommand(std::string name) const { return get_subcommand(name)->parsed_; }\n\n    ///@}\n    /// @name Help\n    ///@{\n\n    /// Set footer.\n    App *set_footer(std::string footer) {\n        footer_ = footer;\n        return this;\n    }\n\n    /// Produce a string that could be read in as a config of the current values of the App. Set default_also to include\n    /// default arguments. Prefix will add a string to the beginning of each option.\n    std::string\n    config_to_str(bool default_also = false, std::string prefix = \"\", bool write_description = false) const {\n        std::stringstream out;\n        for(const Option_p &opt : options_) {\n\n            // Only process option with a long-name and configurable\n            if(!opt->lnames_.empty() && opt->get_configurable()) {\n                std::string name = prefix + opt->lnames_[0];\n                std::string value;\n\n                // Non-flags\n                if(opt->get_type_size() != 0) {\n\n                    // If the option was found on command line\n                    if(opt->count() > 0)\n                        value = detail::inijoin(opt->results());\n\n                    // If the option has a default and is requested by optional argument\n                    else if(default_also && !opt->defaultval_.empty())\n                        value = opt->defaultval_;\n                    // Flag, one passed\n                } else if(opt->count() == 1) {\n                    value = \"true\";\n\n                    // Flag, multiple passed\n                } else if(opt->count() > 1) {\n                    value = std::to_string(opt->count());\n\n                    // Flag, not present\n                } else if(opt->count() == 0 && default_also) {\n                    value = \"false\";\n                }\n\n                if(!value.empty()) {\n                    if(write_description && opt->has_description()) {\n                        if(static_cast<int>(out.tellp()) != 0) {\n                            out << std::endl;\n                        }\n                        out << \"; \" << detail::fix_newlines(\"; \", opt->get_description()) << std::endl;\n                    }\n                    out << name << \"=\" << value << std::endl;\n                }\n            }\n        }\n        for(const App_p &subcom : subcommands_)\n            out << subcom->config_to_str(default_also, prefix + subcom->name_ + \".\");\n        return out.str();\n    }\n\n    /// Makes a help message, with a column wid for column 1\n    std::string help(size_t wid = 30, std::string prev = \"\") const {\n        // Delegate to subcommand if needed\n        if(prev.empty())\n            prev = name_;\n        else\n            prev += \" \" + name_;\n\n        auto selected_subcommands = get_subcommands();\n        if(!selected_subcommands.empty())\n            return selected_subcommands.at(0)->help(wid, prev);\n\n        std::stringstream out;\n        out << description_ << std::endl;\n        out << \"Usage:\" << (prev.empty() ? \"\" : \" \") << prev;\n\n        // Check for options_\n        bool npos = false;\n        std::vector<std::string> groups;\n        for(const Option_p &opt : options_) {\n            if(opt->nonpositional()) {\n                npos = true;\n\n                // Add group if it is not already in there\n                if(std::find(groups.begin(), groups.end(), opt->get_group()) == groups.end()) {\n                    groups.push_back(opt->get_group());\n                }\n            }\n        }\n\n        if(npos)\n            out << \" [OPTIONS]\";\n\n        // Positionals\n        bool pos = false;\n        for(const Option_p &opt : options_)\n            if(opt->get_positional()) {\n                // A hidden positional should still show up in the usage statement\n                // if(detail::to_lower(opt->get_group()).empty())\n                //    continue;\n                out << \" \" << opt->help_positional();\n                if(opt->_has_help_positional())\n                    pos = true;\n            }\n\n        if(!subcommands_.empty()) {\n            if(require_subcommand_min_ > 0)\n                out << \" SUBCOMMAND\";\n            else\n                out << \" [SUBCOMMAND]\";\n        }\n\n        out << std::endl;\n\n        // Positional descriptions\n        if(pos) {\n            out << std::endl << \"Positionals:\" << std::endl;\n            for(const Option_p &opt : options_) {\n                if(detail::to_lower(opt->get_group()).empty())\n                    continue; // Hidden\n                if(opt->_has_help_positional())\n                    detail::format_help(out, opt->help_pname(), opt->get_description(), wid);\n            }\n        }\n\n        // Options\n        if(npos) {\n            for(const std::string &group : groups) {\n                if(detail::to_lower(group).empty())\n                    continue; // Hidden\n                out << std::endl << group << \":\" << std::endl;\n                for(const Option_p &opt : options_) {\n                    if(opt->nonpositional() && opt->get_group() == group)\n                        detail::format_help(out, opt->help_name(true), opt->get_description(), wid);\n                }\n            }\n        }\n\n        // Subcommands\n        if(!subcommands_.empty()) {\n            std::set<std::string> subcmd_groups_seen;\n            for(const App_p &com : subcommands_) {\n                const std::string &group_key = detail::to_lower(com->get_group());\n                if(group_key.empty() || subcmd_groups_seen.count(group_key) != 0)\n                    continue; // Hidden or not in a group\n\n                subcmd_groups_seen.insert(group_key);\n                out << std::endl << com->get_group() << \":\" << std::endl;\n                for(const App_p &new_com : subcommands_)\n                    if(detail::to_lower(new_com->get_group()) == group_key)\n                        detail::format_help(out, new_com->get_name(), new_com->description_, wid);\n            }\n        }\n\n        if(!footer_.empty()) {\n            out << std::endl << footer_ << std::endl;\n        }\n\n        return out.str();\n    }\n\n    ///@}\n    /// @name Getters\n    ///@{\n\n    /// Get the app or subcommand description\n    std::string get_description() const { return description_; }\n\n    /// Get the list of options (user facing function, so returns raw pointers)\n    std::vector<Option *> get_options() const {\n        std::vector<Option *> options(options_.size());\n        std::transform(std::begin(options_), std::end(options_), std::begin(options), [](const Option_p &val) {\n            return val.get();\n        });\n        return options;\n    }\n\n    /// Check the status of ignore_case\n    bool get_ignore_case() const { return ignore_case_; }\n\n    /// Check the status of fallthrough\n    bool get_fallthrough() const { return fallthrough_; }\n\n    /// Get the group of this subcommand\n    const std::string &get_group() const { return group_; }\n\n    /// Get footer.\n    std::string get_footer() const { return footer_; }\n\n    /// Get the required min subcommand value\n    size_t get_require_subcommand_min() const { return require_subcommand_min_; }\n\n    /// Get the required max subcommand value\n    size_t get_require_subcommand_max() const { return require_subcommand_max_; }\n\n    /// Get the prefix command status\n    bool get_prefix_command() const { return prefix_command_; }\n\n    /// Get the status of allow extras\n    bool get_allow_extras() const { return allow_extras_; }\n\n    /// Get the status of allow extras\n    bool get_allow_ini_extras() const { return allow_ini_extras_; }\n\n    /// Get a pointer to the help flag.\n    Option *get_help_ptr() { return help_ptr_; }\n\n    /// Get a pointer to the help flag. (const)\n    const Option *get_help_ptr() const { return help_ptr_; }\n\n    /// Get a pointer to the config option.\n    Option *get_config_ptr() { return config_ptr_; }\n\n    /// Get the parent of this subcommand (or nullptr if master app)\n    App *get_parent() { return parent_; }\n\n    /// Get a pointer to the config option. (const)\n    const Option *get_config_ptr() const { return config_ptr_; }\n\n    /// Get the name of the current app\n    std::string get_name() const { return name_; }\n\n    /// Check the name, case insensitive if set\n    bool check_name(std::string name_to_check) const {\n        std::string local_name = name_;\n        if(ignore_case_) {\n            local_name = detail::to_lower(name_);\n            name_to_check = detail::to_lower(name_to_check);\n        }\n\n        return local_name == name_to_check;\n    }\n\n    /// This gets a vector of pointers with the original parse order\n    const std::vector<Option *> &parse_order() const { return parse_order_; }\n\n    /// This returns the missing options from the current subcommand\n    std::vector<std::string> remaining(bool recurse = false) const {\n        std::vector<std::string> miss_list;\n        for(const std::pair<detail::Classifer, std::string> &miss : missing_) {\n            miss_list.push_back(std::get<1>(miss));\n        }\n\n        // Recurse into subcommands\n        if(recurse) {\n            for(const App *sub : parsed_subcommands_) {\n                std::vector<std::string> output = sub->remaining(recurse);\n                std::copy(std::begin(output), std::end(output), std::back_inserter(miss_list));\n            }\n        }\n        return miss_list;\n    }\n\n    /// This returns the number of remaining options, minus the -- seperator\n    size_t remaining_size(bool recurse = false) const {\n        size_t count = static_cast<size_t>(std::count_if(\n            std::begin(missing_), std::end(missing_), [](const std::pair<detail::Classifer, std::string> &val) {\n                return val.first != detail::Classifer::POSITIONAL_MARK;\n            }));\n        if(recurse) {\n            for(const App_p &sub : subcommands_) {\n                count += sub->remaining_size(recurse);\n            }\n        }\n        return count;\n    }\n\n    ///@}\n\n  protected:\n    /// Check the options to make sure there are no conflicts.\n    ///\n    /// Currently checks to see if multiple positionals exist with -1 args\n    void _validate() const {\n        auto count = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {\n            return opt->get_items_expected() < 0 && opt->get_positional();\n        });\n        if(count > 1)\n            throw InvalidError(name_);\n        for(const App_p &app : subcommands_)\n            app->_validate();\n    }\n\n    /// Internal function to run (App) callback, top down\n    void run_callback() {\n        pre_callback();\n        if(callback_)\n            callback_();\n        for(App *subc : get_subcommands()) {\n            subc->run_callback();\n        }\n    }\n\n    /// Check to see if a subcommand is valid. Give up immediately if subcommand max has been reached.\n    bool _valid_subcommand(const std::string &current) const {\n        // Don't match if max has been reached - but still check parents\n        if(require_subcommand_max_ != 0 && parsed_subcommands_.size() >= require_subcommand_max_) {\n            return parent_ != nullptr && parent_->_valid_subcommand(current);\n        }\n\n        for(const App_p &com : subcommands_)\n            if(com->check_name(current) && !*com)\n                return true;\n\n        // Check parent if exists, else return false\n        return parent_ != nullptr && parent_->_valid_subcommand(current);\n    }\n\n    /// Selects a Classifier enum based on the type of the current argument\n    detail::Classifer _recognize(const std::string &current) const {\n        std::string dummy1, dummy2;\n\n        if(current == \"--\")\n            return detail::Classifer::POSITIONAL_MARK;\n        if(_valid_subcommand(current))\n            return detail::Classifer::SUBCOMMAND;\n        if(detail::split_long(current, dummy1, dummy2))\n            return detail::Classifer::LONG;\n        if(detail::split_short(current, dummy1, dummy2))\n            return detail::Classifer::SHORT;\n        return detail::Classifer::NONE;\n    }\n\n    /// Internal parse function\n    void _parse(std::vector<std::string> &args) {\n        parsed_ = true;\n        bool positional_only = false;\n\n        while(!args.empty()) {\n            _parse_single(args, positional_only);\n        }\n\n        if(help_ptr_ != nullptr && help_ptr_->count() > 0) {\n            throw CallForHelp();\n        }\n\n        // Process an INI file\n        if(config_ptr_ != nullptr) {\n            if(*config_ptr_) {\n                config_ptr_->run_callback();\n                config_required_ = true;\n            }\n            if(!config_name_.empty()) {\n                try {\n                    std::vector<detail::ini_ret_t> values = detail::parse_ini(config_name_);\n                    while(!values.empty()) {\n                        if(!_parse_ini(values)) {\n                            throw INIError::Extras(values.back().fullname);\n                        }\n                    }\n                } catch(const FileError &) {\n                    if(config_required_)\n                        throw;\n                }\n            }\n        }\n\n        // Get envname options if not yet passed\n        for(const Option_p &opt : options_) {\n            if(opt->count() == 0 && !opt->envname_.empty()) {\n                char *buffer = nullptr;\n                std::string ename_string;\n\n#ifdef _MSC_VER\n                // Windows version\n                size_t sz = 0;\n                if(_dupenv_s(&buffer, &sz, opt->envname_.c_str()) == 0 && buffer != nullptr) {\n                    ename_string = std::string(buffer);\n                    free(buffer);\n                }\n#else\n                // This also works on Windows, but gives a warning\n                buffer = std::getenv(opt->envname_.c_str());\n                if(buffer != nullptr)\n                    ename_string = std::string(buffer);\n#endif\n\n                if(!ename_string.empty()) {\n                    opt->add_result(ename_string);\n                }\n            }\n        }\n\n        // Process callbacks\n        for(const Option_p &opt : options_) {\n            if(opt->count() > 0 && !opt->get_callback_run()) {\n                opt->run_callback();\n            }\n        }\n\n        // Verify required options\n        for(const Option_p &opt : options_) {\n            // Required or partially filled\n            if(opt->get_required() || opt->count() != 0) {\n                // Make sure enough -N arguments parsed (+N is already handled in parsing function)\n                if(opt->get_items_expected() < 0 && opt->count() < static_cast<size_t>(-opt->get_items_expected()))\n                    throw ArgumentMismatch::AtLeast(opt->single_name(), -opt->get_items_expected());\n\n                // Required but empty\n                if(opt->get_required() && opt->count() == 0)\n                    throw RequiredError(opt->single_name());\n            }\n            // Requires\n            for(const Option *opt_req : opt->requires_)\n                if(opt->count() > 0 && opt_req->count() == 0)\n                    throw RequiresError(opt->single_name(), opt_req->single_name());\n            // Excludes\n            for(const Option *opt_ex : opt->excludes_)\n                if(opt->count() > 0 && opt_ex->count() != 0)\n                    throw ExcludesError(opt->single_name(), opt_ex->single_name());\n        }\n\n        auto selected_subcommands = get_subcommands();\n        if(require_subcommand_min_ > selected_subcommands.size())\n            throw RequiredError::Subcommand(require_subcommand_min_);\n\n        // Convert missing (pairs) to extras (string only)\n        if(!(allow_extras_ || prefix_command_)) {\n            size_t num_left_over = remaining_size();\n            if(num_left_over > 0) {\n                args = remaining(false);\n                std::reverse(std::begin(args), std::end(args));\n                throw ExtrasError(args);\n            }\n        }\n    }\n\n    /// Parse one ini param, return false if not found in any subcommand, remove if it is\n    ///\n    /// If this has more than one dot.separated.name, go into the subcommand matching it\n    /// Returns true if it managed to find the option, if false you'll need to remove the arg manually.\n    bool _parse_ini(std::vector<detail::ini_ret_t> &args) {\n        detail::ini_ret_t &current = args.back();\n        std::string parent = current.parent(); // respects current.level\n        std::string name = current.name();\n\n        // If a parent is listed, go to a subcommand\n        if(!parent.empty()) {\n            current.level++;\n            for(const App_p &com : subcommands_)\n                if(com->check_name(parent))\n                    return com->_parse_ini(args);\n            return false;\n        }\n\n        auto op_ptr = std::find_if(\n            std::begin(options_), std::end(options_), [name](const Option_p &v) { return v->check_lname(name); });\n\n        if(op_ptr == std::end(options_)) {\n            if(allow_ini_extras_) {\n                // Should we worry about classifying the extras properly?\n                missing_.emplace_back(detail::Classifer::NONE, current.fullname);\n                args.pop_back();\n                return true;\n            }\n            return false;\n        }\n\n        // Let's not go crazy with pointer syntax\n        Option_p &op = *op_ptr;\n\n        if(!op->get_configurable())\n            throw INIError::NotConfigurable(current.fullname);\n\n        if(op->results_.empty()) {\n            // Flag parsing\n            if(op->get_type_size() == 0) {\n                if(current.inputs.size() == 1) {\n                    std::string val = current.inputs.at(0);\n                    val = detail::to_lower(val);\n                    if(val == \"true\" || val == \"on\" || val == \"yes\")\n                        op->results_ = {\"\"};\n                    else if(val == \"false\" || val == \"off\" || val == \"no\")\n                        ;\n                    else\n                        try {\n                            size_t ui = std::stoul(val);\n                            for(size_t i = 0; i < ui; i++)\n                                op->results_.emplace_back(\"\");\n                        } catch(const std::invalid_argument &) {\n                            throw ConversionError::TrueFalse(current.fullname);\n                        }\n                } else\n                    throw ConversionError::TooManyInputsFlag(current.fullname);\n            } else {\n                op->results_ = current.inputs;\n                op->run_callback();\n            }\n        }\n\n        args.pop_back();\n        return true;\n    }\n\n    /// Parse \"one\" argument (some may eat more than one), delegate to parent if fails, add to missing if missing from\n    /// master\n    void _parse_single(std::vector<std::string> &args, bool &positional_only) {\n\n        detail::Classifer classifer = positional_only ? detail::Classifer::NONE : _recognize(args.back());\n        switch(classifer) {\n        case detail::Classifer::POSITIONAL_MARK:\n            missing_.emplace_back(classifer, args.back());\n            args.pop_back();\n            positional_only = true;\n            break;\n        case detail::Classifer::SUBCOMMAND:\n            _parse_subcommand(args);\n            break;\n        case detail::Classifer::LONG:\n            // If already parsed a subcommand, don't accept options_\n            _parse_arg(args, true);\n            break;\n        case detail::Classifer::SHORT:\n            // If already parsed a subcommand, don't accept options_\n            _parse_arg(args, false);\n            break;\n        case detail::Classifer::NONE:\n            // Probably a positional or something for a parent (sub)command\n            _parse_positional(args);\n        }\n    }\n\n    /// Count the required remaining positional arguments\n    size_t _count_remaining_positionals(bool required = false) const {\n        size_t retval = 0;\n        for(const Option_p &opt : options_)\n            if(opt->get_positional() && (!required || opt->get_required()) && opt->get_items_expected() > 0 &&\n               static_cast<int>(opt->count()) < opt->get_items_expected())\n                retval = static_cast<size_t>(opt->get_items_expected()) - opt->count();\n\n        return retval;\n    }\n\n    /// Parse a positional, go up the tree to check\n    void _parse_positional(std::vector<std::string> &args) {\n\n        std::string positional = args.back();\n        for(const Option_p &opt : options_) {\n            // Eat options, one by one, until done\n            if(opt->get_positional() &&\n               (static_cast<int>(opt->count()) < opt->get_items_expected() || opt->get_items_expected() < 0)) {\n\n                opt->add_result(positional);\n                parse_order_.push_back(opt.get());\n                args.pop_back();\n                return;\n            }\n        }\n\n        if(parent_ != nullptr && fallthrough_)\n            return parent_->_parse_positional(args);\n        else {\n            args.pop_back();\n            missing_.emplace_back(detail::Classifer::NONE, positional);\n\n            if(prefix_command_) {\n                while(!args.empty()) {\n                    missing_.emplace_back(detail::Classifer::NONE, args.back());\n                    args.pop_back();\n                }\n            }\n        }\n    }\n\n    /// Parse a subcommand, modify args and continue\n    ///\n    /// Unlike the others, this one will always allow fallthrough\n    void _parse_subcommand(std::vector<std::string> &args) {\n        if(_count_remaining_positionals(/* required */ true) > 0)\n            return _parse_positional(args);\n        for(const App_p &com : subcommands_) {\n            if(com->check_name(args.back())) {\n                args.pop_back();\n                if(std::find(std::begin(parsed_subcommands_), std::end(parsed_subcommands_), com.get()) ==\n                   std::end(parsed_subcommands_))\n                    parsed_subcommands_.push_back(com.get());\n                com->_parse(args);\n                return;\n            }\n        }\n        if(parent_ != nullptr)\n            return parent_->_parse_subcommand(args);\n        else\n            throw HorribleError(\"Subcommand \" + args.back() + \" missing\");\n    }\n\n    /// Parse a short (false) or long (true) argument, must be at the top of the list\n    void _parse_arg(std::vector<std::string> &args, bool second_dash) {\n\n        detail::Classifer current_type = second_dash ? detail::Classifer::LONG : detail::Classifer::SHORT;\n\n        std::string current = args.back();\n\n        std::string name;\n        std::string value;\n        std::string rest;\n\n        if(second_dash) {\n            if(!detail::split_long(current, name, value))\n                throw HorribleError(\"Long parsed but missing (you should not see this):\" + args.back());\n        } else {\n            if(!detail::split_short(current, name, rest))\n                throw HorribleError(\"Short parsed but missing! You should not see this\");\n        }\n\n        auto op_ptr = std::find_if(std::begin(options_), std::end(options_), [name, second_dash](const Option_p &opt) {\n            return second_dash ? opt->check_lname(name) : opt->check_sname(name);\n        });\n\n        // Option not found\n        if(op_ptr == std::end(options_)) {\n            // If a subcommand, try the master command\n            if(parent_ != nullptr && fallthrough_)\n                return parent_->_parse_arg(args, second_dash);\n            // Otherwise, add to missing\n            else {\n                args.pop_back();\n                missing_.emplace_back(current_type, current);\n                return;\n            }\n        }\n\n        args.pop_back();\n\n        // Get a reference to the pointer to make syntax bearable\n        Option_p &op = *op_ptr;\n\n        int num = op->get_items_expected();\n\n        // Make sure we always eat the minimum for unlimited vectors\n        int collected = 0;\n\n        // --this=value\n        if(!value.empty()) {\n            // If exact number expected\n            if(num > 0)\n                num--;\n            op->add_result(value);\n            parse_order_.push_back(op.get());\n            collected += 1;\n        } else if(num == 0) {\n            op->add_result(\"\");\n            parse_order_.push_back(op.get());\n            // -Trest\n        } else if(!rest.empty()) {\n            if(num > 0)\n                num--;\n            op->add_result(rest);\n            parse_order_.push_back(op.get());\n            rest = \"\";\n            collected += 1;\n        }\n\n        // Unlimited vector parser\n        if(num < 0) {\n            while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) {\n                if(collected >= -num) {\n                    // We could break here for allow extras, but we don't\n\n                    // If any positionals remain, don't keep eating\n                    if(_count_remaining_positionals() > 0)\n                        break;\n                }\n                op->add_result(args.back());\n                parse_order_.push_back(op.get());\n                args.pop_back();\n                collected++;\n            }\n\n            // Allow -- to end an unlimited list and \"eat\" it\n            if(!args.empty() && _recognize(args.back()) == detail::Classifer::POSITIONAL_MARK)\n                args.pop_back();\n\n        } else {\n            while(num > 0 && !args.empty()) {\n                num--;\n                std::string current_ = args.back();\n                args.pop_back();\n                op->add_result(current_);\n                parse_order_.push_back(op.get());\n            }\n\n            if(num > 0) {\n                throw ArgumentMismatch::TypedAtLeast(op->single_name(), num, op->get_type_name());\n            }\n        }\n\n        if(!rest.empty()) {\n            rest = \"-\" + rest;\n            args.push_back(rest);\n        }\n    }\n};\n\nnamespace FailureMessage {\n\ninline std::string simple(const App *app, const Error &e) {\n    std::string header = std::string(e.what()) + \"\\n\";\n    if(app->get_help_ptr() != nullptr)\n        header += \"Run with \" + app->get_help_ptr()->single_name() + \" for more information.\\n\";\n    return header;\n}\n\ninline std::string help(const App *app, const Error &e) {\n    std::string header = std::string(\"ERROR: \") + e.get_name() + \": \" + e.what() + \"\\n\";\n    header += app->help();\n    return header;\n}\n\n} // namespace FailureMessage\n\nnamespace detail {\n/// This class is simply to allow tests access to App's protected functions\nstruct AppFriend {\n\n    /// Wrap _parse_short, perfectly forward arguments and return\n    template <typename... Args>\n    static auto parse_arg(App *app, Args &&... args) ->\n        typename std::result_of<decltype (&App::_parse_arg)(App, Args...)>::type {\n        return app->_parse_arg(std::forward<Args>(args)...);\n    }\n\n    /// Wrap _parse_subcommand, perfectly forward arguments and return\n    template <typename... Args>\n    static auto parse_subcommand(App *app, Args &&... args) ->\n        typename std::result_of<decltype (&App::_parse_subcommand)(App, Args...)>::type {\n        return app->_parse_subcommand(std::forward<Args>(args)...);\n    }\n};\n} // namespace detail\n\n} // namespace CLI\n\n"
  },
  {
    "path": "src/Database.cpp",
    "content": "//\n//  Database.cpp\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#include \"Database.h\"\n\n/**\n * Initialize vignette model with some vignetting\n */\nDatabase::Database(int image_width,int image_height) :\n    m_vignette_estimate(-0.3,0,0,image_width,image_height),\n    m_response_estimate()\n{\n    m_image_width  = image_width;\n    m_image_height = image_height;\n}\n\nvoid Database::visualizeTracking()\n{\n    // Fetch tracking information and create a canvas\n    Frame last_frame = m_tracked_frames.at(m_tracked_frames.size()-1);\n    // Todo: change to class member\n    cv::Mat draw_image = last_frame.m_image.clone();\n    \n    // Draw features on the canvas\n    for(int i = 0;i < last_frame.m_features.size();i++)\n    {\n        cv::circle(draw_image, last_frame.m_features.at(i)->m_xy_location, 3, cv::Scalar(255,0,0));\n    }\n    \n    // Correct the frame based on the current estimate\n    // Todo: change to class member\n    cv::Mat corrected_frame = last_frame.m_image.clone();\n    \n    for(int r = 0; r < corrected_frame.rows;r++)\n    {\n        for(int c = 0;c < corrected_frame.cols;c++)\n        {\n            // Apply rsponse and vignette for each pixel\n            int o_value = corrected_frame.at<uchar>(r,c);\n            double new_o_value = m_response_estimate.removeResponse(o_value);\n            \n            double v_factor = m_vignette_estimate.getVignetteFactor(cv::Point2f(c,r));\n            new_o_value /= v_factor;\n            \n            // Correct by exposure time\n            new_o_value /= last_frame.m_exp_time;\n            if(new_o_value > 255)new_o_value = 255; \n            corrected_frame.at<uchar>(r,c) = (uchar)new_o_value;\n        }\n    }\n    \n    //resize drawing images to an acceptable size\n    int image_width  = 640;\n    int image_height = 480;\n    \n    cv::resize(draw_image, draw_image, cv::Size(image_width,image_height));\n    cv::resize(corrected_frame, corrected_frame, cv::Size(image_width,image_height));\n    \n    // Display\n    cv::imshow(\"Tracked frame\", draw_image);\n    cv::imshow(\"Corrected frame\", corrected_frame);\n    cv::waitKey(1);\n}\n\nvoid Database::visualizeRapidExposureTimeEstimates(double exponent)\n{\n    // Visualize exposure times \n    // If GT data is available, the estimated exposure times will be aligned \n    // to the GT by computing an optimal alignment factor alignment_alpha\n    // If no GT data is available, the estimated exposure is simply scaled between [0,1]\n    int nr_frames_to_vis = int(fmin(m_tracked_frames.size(),40));\n    int exp_image_height = 150;\n    int draw_spacing = 8;\n    std::vector<double> estimated_exp_times;\n    std::vector<double> gt_exp_times;\n    double alignment_alpha = 1.0;\n    double max_exp = -10000.0;\n    double min_exp = 10000.0;\n    double top = 0;\n    double bot = 0;\n    for(int i = 0;i < nr_frames_to_vis;i++)\n    {\n        // Fetch estimated and GT exposure time data, pow estimates with alignment exponent\n        Frame current_frame = m_tracked_frames.at(m_tracked_frames.size()-nr_frames_to_vis+i);\n        double frame_exp_time = pow(current_frame.m_exp_time,exponent);\n        double frame_time_gt  = current_frame.m_gt_exp_time;\n\n        // Keep track of max and min exposure to scale between [0,1]\n        if(frame_exp_time > max_exp)\n            max_exp = frame_exp_time;\n        if(frame_exp_time < min_exp)\n            min_exp = frame_exp_time;\n\n        // Accumulate information for least square fit between GT and estimated exposure\n        top += frame_exp_time*frame_time_gt;\n        bot += frame_exp_time*frame_exp_time;\n        \n        // Push back estimated exposure values\n        estimated_exp_times.push_back(frame_exp_time);\n\n        // Push gt exposure time if available\n        if(!(frame_time_gt < 0))\n            gt_exp_times.push_back(frame_time_gt);\n    }\n\n    // Set alignment factor only if GT exposure is available\n    if(gt_exp_times.size() == estimated_exp_times.size())\n        alignment_alpha = top/bot;\n    else\n    {\n        // Normalize estimated exposures between [0,1]\n        for(int k = 0;k < estimated_exp_times.size();k++)\n        {\n            estimated_exp_times.at(k) = (estimated_exp_times.at(k)-min_exp)/(max_exp-min_exp);\n        }\n    }\n\n    // Create exposure time canvas\n    cv::Mat exposure_vis_image(exp_image_height,draw_spacing*nr_frames_to_vis,CV_8UC3,cv::Scalar(0,0,0));\n\n    // Draw estimated exposure times as lines to graph\n    for(int i = 0;i < nr_frames_to_vis-1;i++)\n    {\n        int drawing_y_exp_1 = exp_image_height - exp_image_height*(alignment_alpha * estimated_exp_times.at(i));\n        drawing_y_exp_1 = int(fmax(0,drawing_y_exp_1));\n        drawing_y_exp_1 = int(fmin(exp_image_height-1,drawing_y_exp_1));\n\n        int drawing_y_exp_2 = exp_image_height - exp_image_height*(alignment_alpha * estimated_exp_times.at(i+1));\n        drawing_y_exp_2 = int(fmax(0,drawing_y_exp_2));\n        drawing_y_exp_2 = int(fmin(exp_image_height-1,drawing_y_exp_2));\n\n        // Draw exposure lines\n        cv::line(exposure_vis_image, cv::Point(draw_spacing*i,drawing_y_exp_1), cv::Point(draw_spacing*(i+1),drawing_y_exp_2), cv::Scalar(0,0,255));\n     }\n\n    // Draw GT exposure line only if GT exposure data is available\n    if(gt_exp_times.size() == estimated_exp_times.size())\n    {\n        for(int i = 0;i < nr_frames_to_vis-1;i++)\n        {\n            int drawing_y_gt_exp_1 = exp_image_height - exp_image_height * gt_exp_times.at(i);\n            drawing_y_gt_exp_1 = int(fmax(0,drawing_y_gt_exp_1));\n            drawing_y_gt_exp_1 = int(fmin(exp_image_height-1,drawing_y_gt_exp_1));\n\n            int drawing_y_gt_exp_2 = exp_image_height - exp_image_height * gt_exp_times.at(i+1);\n            drawing_y_gt_exp_2 = int(fmax(0,drawing_y_gt_exp_2));\n            drawing_y_gt_exp_2 = int(fmin(exp_image_height-1,drawing_y_gt_exp_2));\n\n            cv::line(exposure_vis_image, cv::Point(draw_spacing*i,drawing_y_gt_exp_1), cv::Point(draw_spacing*(i+1),drawing_y_gt_exp_2), cv::Scalar(255,255,0));\n        }\n    }   \n\n    cv::imshow(\"Estimated Exposure (Rapid)\", exposure_vis_image);\n    cv::moveWindow(\"Estimated Exposure (Rapid)\", 20+20+256,20);\n\n}\n\n// Todo: change the return to parameter passed by reference\nstd::vector<cv::Point2f> Database::fetchActiveFeatureLocations()\n{\n    std::vector<cv::Point2f> point_locations;\n    \n    Frame last_frame = m_tracked_frames.at(m_tracked_frames.size()-1);\n    \n    for(int i = 0;i < last_frame.m_features.size();i++)\n    {\n        point_locations.push_back(last_frame.m_features.at(i)->m_xy_location);\n    }\n    \n    return point_locations;\n}\n\nvoid Database::removeLastFrame()\n{\n    // Erase the information about the first frame\n    m_tracked_frames.erase(m_tracked_frames.begin());\n    \n    // Let all pointers of the new first frame point to NULL\n    for(int i = 0;i < m_tracked_frames.at(0).m_features.size();i++)\n    {\n        m_tracked_frames.at(0).m_features.at(i)->m_prev_feature = NULL;\n    }\n}\n\n"
  },
  {
    "path": "src/Database.h",
    "content": "//\n//  Database.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#ifndef OnlinePhotometricCalibration_Database_h_\n#define OnlinePhotometricCalibration_Database_h_\n\n#include \"StandardIncludes.h\"\n#include \"Frame.h\"\n#include \"VignetteModel.h\"\n#include \"ResponseModel.h\"\n\nclass Database\n{\n    \npublic:\n    \n    /**\n     * Initialize database\n     *\n     * @param image_width  Width of input images in pixels\n     * @param image_height Height of input images in pixels\n     */\n    Database(int image_width,int image_height);\n    \n    /**\n     * Tracked frames, exposure time estimates + radiance estimates of tracked points\n     * Frames also include the tracking information + output intensities\n     */\n    std::vector<Frame> m_tracked_frames;\n    \n    /**\n     * Vignette and response estimates\n     */\n    VignetteModel m_vignette_estimate;\n    ResponseModel m_response_estimate;\n    \n    /**\n     * Information about stored image size\n     */\n    int m_image_width;\n    int m_image_height;\n    \n    /**\n     * Fetch current active feature locations\n     *\n     * @returns Vector of feature locations active in the most current frame\n     */\n    std::vector<cv::Point2f> fetchActiveFeatureLocations();\n    \n    /**\n     * Fetch most current image\n     *\n     * @returns Most current image in the databse\n     */\n     // Todo: change return type to reference\n    cv::Mat fetchActiveImage()\n    {\n        return m_tracked_frames.at(m_tracked_frames.size()-1).m_image;\n    }\n    \n    /**\n     * Remove last frame and tracking information from the database\n     */\n    void removeLastFrame();\n    \n    /**\n     * Visualize the current tracking state\n     * Shows current active image with feature locations \n     * And corrected image based on current photometric estimates\n     * Calling this method slows down performance of the system significantly\n     */\n    void visualizeTracking();\n\n    /**\n    * Visualize the rapid exposure time estimates\n    */\n    void visualizeRapidExposureTimeEstimates(double exponent);\n\n};\n\n#endif // include guard\n"
  },
  {
    "path": "src/Feature.h",
    "content": "//\n//  Feature.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#ifndef OnlinePhotometricCalibration_Feature_h_\n#define OnlinePhotometricCalibration_Feature_h_\n\n#include <stdio.h>\n\n#include \"StandardIncludes.h\"\n\n/**\n * Stores tracking information of one feature in one particular image\n */\n\nclass Feature\n{\n    \npublic:\n    \n    /**\n     * Location of the feature in the image\n     */\n    cv::Point2f m_xy_location;\n    \n    /**\n     * Output values of the image patch centered around the feature location\n     */\n    std::vector<double> m_output_values;\n    \n    /**\n     * Radiance estimates of the image patch centered around the feature location\n     */\n    std::vector<double> m_radiance_estimates;\n    \n    /**\n     * Gradient values of the image patch centered around the feature location\n     */\n    std::vector<double> m_gradient_values;\n    \n    /*\n     * Link to the feature in the previous image corresponding to the same scene point\n     * (Obtained from tracking)\n     */\n    Feature* m_prev_feature;\n    \n    /*\n     * Link to the feature in the previous image corresponding to the same scene point\n     * (Obtained from tracking)\n     */\n    Feature* m_next_feature;\n    \n};\n\n#endif // include guard\n"
  },
  {
    "path": "src/Frame.h",
    "content": "//\n//  Frame.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#ifndef OnlinePhotometricCalibration_Frame_h_\n#define OnlinePhotometricCalibration_Frame_h_\n\n#include \"StandardIncludes.h\"\n#include \"Feature.h\"\n\n/**\n * Stores tracking information for one entire image\n */\nclass Frame\n{\n    \npublic:\n    \n    /**\n     * Original input image\n     */\n    cv::Mat m_image;\n    \n    /**\n     * Photometrically corrected image\n     */\n    cv::Mat m_image_corrected;\n    \n    /**\n     * Gradient information\n     */\n    cv::Mat m_gradient_image;\n    \n    /**\n     * List of features present in this frame\n     */\n    std::vector<Feature*> m_features;\n    \n    /**\n     * Exposure time estimate (from rapid exposure time estimation)\n     */\n    double m_exp_time;\n\n    /**\n    * Ground truth exposure time if available\n    */\n    double m_gt_exp_time;\n};\n\n#endif // include guard\n"
  },
  {
    "path": "src/GainRobustTracker.cpp",
    "content": "//\n//  GainRobustTracker.cpp\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 17.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#include \"GainRobustTracker.h\"\n\nGainRobustTracker::GainRobustTracker(int patch_size,int pyramid_levels)\n{\n    // Initialize patch size and pyramid levels\n    m_patch_size = patch_size;\n    m_pyramid_levels = pyramid_levels;\n}\n\n// Todo: change frame_1 frame 2 to ref (or const ref), pts_1 to ref\ndouble GainRobustTracker::trackImagePyramids(cv::Mat frame_1,\n                                             cv::Mat frame_2,\n                                             std::vector<cv::Point2f> pts_1,\n                                             std::vector<cv::Point2f>& pts_2,\n                                             std::vector<int>& point_status)\n{\n    // All points valid in the beginning of tracking\n    std::vector<int> point_validity;\n    for(int i = 0;i < pts_1.size();i++)\n    {\n        point_validity.push_back(1);\n    }\n    \n    // Calculate image pyramid of frame 1 and frame 2\n    std::vector<cv::Mat> new_pyramid;\n    cv::buildPyramid(frame_2, new_pyramid, m_pyramid_levels);\n    \n    std::vector<cv::Mat> old_pyramid;\n    cv::buildPyramid(frame_1, old_pyramid, m_pyramid_levels);\n    \n    // Temporary vector to update tracking estiamtes over time\n    std::vector<cv::Point2f> tracking_estimates = pts_1;\n    \n    double all_exp_estimates = 0.0;\n    int nr_estimates = 0;\n    \n    // Iterate all pyramid levels and perform gain robust KLT on each level (coarse to fine)\n    for(int level = (int)new_pyramid.size()-1;level >= 0;level--)\n    {\n        // Scale the input points and tracking estimates to the current pyramid level\n        std::vector<cv::Point2f> scaled_tracked_points;\n        std::vector<cv::Point2f> scaled_tracking_estimates;\n        for(int i = 0;i < pts_1.size();i++)\n        {\n            cv::Point2f scaled_point;\n            scaled_point.x = (float)(pts_1.at(i).x/pow(2,level));\n            scaled_point.y = (float)(pts_1.at(i).y/pow(2,level));\n            scaled_tracked_points.push_back(scaled_point);\n            \n            cv::Point2f scaled_estimate;\n            scaled_estimate.x = (float)(tracking_estimates.at(i).x/pow(2,level));\n            scaled_estimate.y = (float)(tracking_estimates.at(i).y/pow(2,level));\n            scaled_tracking_estimates.push_back(scaled_estimate);\n        }\n        \n        // Perform tracking on current level\n        double exp_estimate = trackImageExposurePyr(old_pyramid.at(level),\n                                                    new_pyramid.at(level),\n                                                    scaled_tracked_points,\n                                                    scaled_tracking_estimates,\n                                                    point_validity);\n        \n        // Optional: Do something with the estimated exposure ratio\n        // std::cout << \"Estimated exposure ratio of current level: \" << exp_estimate << std::endl;\n        \n        // Average estimates of each level later\n        all_exp_estimates += exp_estimate;\n        nr_estimates++;\n        \n        // Update the current tracking result by scaling down to pyramid level 0\n        for(int i = 0;i < scaled_tracking_estimates.size();i++)\n        {\n            if(point_validity.at(i) == 0)\n                continue;\n            \n            cv::Point2f scaled_point;\n            scaled_point.x = (float)(scaled_tracking_estimates.at(i).x*pow(2,level));\n            scaled_point.y = (float)(scaled_tracking_estimates.at(i).y*pow(2,level));\n            \n            tracking_estimates.at(i) = scaled_point;\n        }\n    }\n    \n    // Write result to output vectors passed by reference\n    pts_2 = tracking_estimates;\n    point_status = point_validity;\n    \n    // Average exposure ratio estimate\n    double overall_exp_estimate = all_exp_estimates / nr_estimates;\n    return overall_exp_estimate;\n}\n\n/**\n * For a reference on the meaning of the optimization variables and the overall concept of this function\n * refer to the photometric calibration paper \n * introducing gain robust KLT tracking by Kim et al.\n */\n // Todo: change Mat and vector to ref\ndouble GainRobustTracker::trackImageExposurePyr(cv::Mat old_image,\n                                                cv::Mat new_image,\n                                                std::vector<cv::Point2f> input_points,\n                                                std::vector<cv::Point2f>& output_points,\n                                                std::vector<int>& point_validity)\n{\n    // Number of points to track\n    int nr_points = static_cast<int>(input_points.size());\n    \n    // Updated point locations which are updated throughout the iterations\n    if(output_points.size() == 0)\n    {\n        output_points = input_points;\n    }\n    else if(output_points.size() != input_points.size())\n    {\n        std::cout << \"ERROR - OUTPUT POINT SIZE != INPUT POINT SIZE!\" << std::endl;\n        return -1;\n    }\n    \n    // Input image dimensions\n    int image_rows = new_image.rows;\n    int image_cols = new_image.cols;\n    \n    // Final exposure time estimate\n    double K_total = 0.0;\n    \n    for(int round = 0;round < 1;round++)\n    {\n        // Get the currently valid points\n        int nr_valid_points = getNrValidPoints(point_validity);\n        \n        // Allocate space for W,V matrices\n        cv::Mat W(2*nr_valid_points,1,CV_64F,0.0);\n        cv::Mat V(2*nr_valid_points,1,CV_64F,0.0);\n        \n        // Allocate space for U_INV and the original Us\n        cv::Mat U_INV(2*nr_valid_points,2*nr_valid_points,CV_64F,0.0);\n        std::vector<cv::Mat> Us;\n        \n        double lambda = 0;\n        double m = 0;\n\n        int absolute_point_index = -1;\n        \n        for(int p = 0;p < input_points.size();p++)\n        {\n            if(point_validity.at(p) == 0)\n            {\n                continue;\n            }\n            \n            absolute_point_index++;\n            \n            // Build U matrix\n            cv::Mat U(2,2, CV_64F, 0.0);\n            \n            // Bilinear image interpolation\n            cv::Mat patch_intensities_1;\n            cv::Mat patch_intensities_2;\n            int absolute_patch_size = ((m_patch_size+1)*2+1);  // Todo: why m_patch_size+1?\n            cv::getRectSubPix(new_image, cv::Size(absolute_patch_size,absolute_patch_size), output_points.at(p), patch_intensities_2,CV_32F);\n            cv::getRectSubPix(old_image, cv::Size(absolute_patch_size,absolute_patch_size), input_points.at(p), patch_intensities_1,CV_32F);\n            \n            // Go through image patch around this point\n            for(int r = 0; r < 2*m_patch_size+1;r++)\n            {\n                for(int c = 0; c < 2*m_patch_size+1;c++)\n                {\n                    // Fetch patch intensity values\n                    double i_frame_1 = patch_intensities_1.at<float>(1+r,1+c);\n                    double i_frame_2 = patch_intensities_2.at<float>(1+r,1+c);\n                    \n                    if(i_frame_1 < 1)\n                        i_frame_1 = 1;\n                    if(i_frame_2 < 1)\n                        i_frame_2 = 1;\n                    \n                    // Estimate patch gradient values\n                    double grad_1_x = (patch_intensities_1.at<float>(1+r,1+c+1) - patch_intensities_1.at<float>(1+r,1+c-1))/2;\n                    double grad_1_y = (patch_intensities_1.at<float>(1+r+1,1+c) - patch_intensities_1.at<float>(1+r-1,1+c))/2;\n                    \n                    double grad_2_x = (patch_intensities_2.at<float>(1+r,1+c+1) - patch_intensities_2.at<float>(1+r,1+c-1))/2;\n                    double grad_2_y = (patch_intensities_2.at<float>(1+r+1,1+c) - patch_intensities_2.at<float>(1+r-1,1+c))/2;\n                    \n                    double a = (1.0/i_frame_2)*grad_2_x + (1.0/i_frame_1)*grad_1_x;\n                    double b = (1.0/i_frame_2)*grad_2_y + (1.0/i_frame_1)*grad_1_y;\n                    double beta = log(i_frame_2/255.0) - log(i_frame_1/255.0);\n                    \n                    U.at<double>(0,0) += 0.5*a*a;\n                    U.at<double>(1,0) += 0.5*a*b;\n                    U.at<double>(0,1) += 0.5*a*b;\n                    U.at<double>(1,1) += 0.5*b*b;\n                    \n                    W.at<double>(2*absolute_point_index,0)   -= a;\n                    W.at<double>(2*absolute_point_index+1,0) -= b;\n                    \n                    V.at<double>(2*absolute_point_index,0)   -= beta*a;\n                    V.at<double>(2*absolute_point_index+1,0) -= beta*b;\n                    \n                    lambda += 2;\n                    m += 2*beta;\n                }\n            }\n            \n            //Back up U for re-substitution\n            Us.push_back(U);\n            \n            //Invert matrix U for this point and write it to diagonal of overall U_INV matrix\n            cv::Mat U_INV_p = U.inv();\n            //std::cout << cv::determinant(U_INV_p) << std::endl;\n            //std::cout << U_INV_p << std::endl;\n            //std::cout << U << std::endl;\n            \n            U_INV.at<double>(2*absolute_point_index,2*absolute_point_index) = U_INV_p.at<double>(0,0);\n            U_INV.at<double>(2*absolute_point_index+1,2*absolute_point_index) = U_INV_p.at<double>(1,0);\n            U_INV.at<double>(2*absolute_point_index,2*absolute_point_index+1) = U_INV_p.at<double>(0,1);\n            U_INV.at<double>(2*absolute_point_index+1,2*absolute_point_index+1) = U_INV_p.at<double>(1,1);\n        }\n\n        // Todo: check if opencv utilizes the sparsity of U\n        //solve for the exposure\n        cv::Mat K_MAT;\n        cv::solve(-W.t()*U_INV*W+lambda, -W.t()*U_INV*V+m, K_MAT);\n        double K = K_MAT.at<double>(0,0);\n        \n        //std::cout << -W.t()*U_INV*W+lambda << std::endl;\n        //std::cout << -W.t()*U_INV*V+m << std::endl;\n        //std::cout << K_MAT << std::endl;\n        \n        // Solve for the displacements\n        absolute_point_index = -1;\n        for(int p = 0;p < nr_points;p++)\n        {\n            if(point_validity.at(p) == 0)\n                continue;\n            \n            absolute_point_index++;\n            \n            cv::Mat U_p = Us.at(absolute_point_index);\n            cv::Mat V_p = V(cv::Rect(0,2*absolute_point_index,1,2));\n            cv::Mat W_p = W(cv::Rect(0,2*absolute_point_index,1,2));\n            \n            cv::Mat displacement;\n            cv::solve(U_p, V_p - K*W_p, displacement);\n            \n            //std::cout << displacement << std::endl;\n            \n            output_points.at(p).x += displacement.at<double>(0,0);\n            output_points.at(p).y += displacement.at<double>(1,0);\n            \n            // Filter out this point if too close at the boundaries\n            int filter_margin = 2;\n            double x = output_points.at(p).x;\n            double y = output_points.at(p).y;\n            // Todo: the latter two should be \">=\" ?\n            if(x < filter_margin || y < filter_margin || x > image_cols-filter_margin || y > image_rows-filter_margin)\n            {\n                point_validity.at(p) = 0;\n            }\n        }\n        \n        K_total += K;\n    }\n    \n    return exp(K_total);\n}\n\nint GainRobustTracker::getNrValidPoints(std::vector<int> validity_vector)\n{\n    // Simply sum up the validity vector\n    int result = 0;\n    for(int i = 0;i < validity_vector.size();i++)\n    {\n        result += validity_vector.at(i);\n    }\n    return result;\n}\n"
  },
  {
    "path": "src/GainRobustTracker.h",
    "content": "//\n//  GainRobustTracker.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 17.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#ifndef OnlinePhotometricCalibration_GainRobustTracker_h_\n#define OnlinePhotometricCalibration_GainRobustTracker_h_\n\n#include \"StandardIncludes.h\"\n\n/**\n * This class implements gain robust KLT tracking\n * optimizing jointly for displacements of features and an exposure ratio between input frames\n */\n\nclass GainRobustTracker\n{\n    \npublic:\n    \n    /**\n     * Constructor \n     * \n     * @param patch_size Size of tracking patches\n     * @param pyramid_levels Number of pyramid levels used for KLT tracking\n     */\n    GainRobustTracker(int patch_size,int pyramid_levels);\n    \n    /*\n     * Track a new image using exposure estimation + image pyramids\n     *\n     * @param frame_1 Frame to track points from\n     * @param frame_2 Frame to track points to\n     * @param pts_1 Given point locations in frame_1\n     * @param pts_2 Output point locations in frame_2 (tracked from frame_1 to frame_2)\n     * @param point_status Vector indicating point validity (set to 0 by tracker if e.g. tracked patches leave input images)\n     * @returns Exposure ratio estimate between frame_1 and frame_2 based on KLT optimization\n     */\n    double trackImagePyramids(cv::Mat frame_1,\n                              cv::Mat frame_2,\n                              std::vector<cv::Point2f> pts_1,\n                              std::vector<cv::Point2f>& pts_2,\n                              std::vector<int>& point_status);\n    \nprivate:\n    \n    /*\n     * Patch size used for tracking of image patches\n     */\n    int m_patch_size;\n    \n    /*\n     * Number of pyramid levels used for tracking\n     */\n    int m_pyramid_levels;\n    \n    /**\n     * Get number of valid points inside the specified validity vector\n     * \n     * @param validity_vector Vector of validity flags corresponding to tracking points\n     * @returns Number of valid flags inside the input vector\n     */\n    int getNrValidPoints(std::vector<int> validity_vector);\n    \n    /**\n     * Track points on a specific pyramid layer\n     *\n     * @param old_image First input image\n     * @param new_image Second input image, track new features to this image\n     * @param input_points Original points in first input image\n     * @param output_points Tracked point locations in second input image\n     * @returns Exposure ratio estimate between first and second input image\n     */\n    double trackImageExposurePyr(cv::Mat old_image,\n                                 cv::Mat new_image,\n                                 std::vector<cv::Point2f> input_points,\n                                 std::vector<cv::Point2f>& output_points,\n                                 std::vector<int>& point_validity);\n};\n\n#endif // include guard\n"
  },
  {
    "path": "src/ImageReader.cpp",
    "content": "//\n//  ImageReader.cpp\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#include \"ImageReader.h\"\n\nImageReader::ImageReader(std::string image_folder,\n                         cv::Size new_img_size)\n{\n    m_img_new_size = new_img_size;\n    getDir(image_folder, m_files);\n    printf(\"ImageReader: got %d files in %s!\\n\", (int)m_files.size(), image_folder.c_str());\n}\n\ncv::Mat ImageReader::readImage(int image_index)\n{\n    // Read image from disk\n    cv::Mat image = cv::imread(m_files.at(image_index), CV_LOAD_IMAGE_GRAYSCALE);\n        \n    if(!image.data)\n    {\n        std::cout << \"ERROR READING IMAGE \" << m_files.at(image_index) << std::endl;\n        return cv::Mat();\n    }\n    \n    // Resize input image\n    cv::resize(image, image, m_img_new_size);\n    \n    return image;\n}\n\nint ImageReader::getDir(std::string dir, std::vector<std::string> &files)\n{\n    DIR *dp;\n    struct dirent *dirp;\n    if((dp = opendir(dir.c_str())) == NULL)\n    {\n        return -1;\n    }\n\n    while ((dirp = readdir(dp)) != NULL)\n    {\n        std::string name = std::string(dirp->d_name);\n\n        if(name != \".\" && name != \"..\")\n            files.push_back(name);\n    }\n\n    closedir(dp);\n    std::sort(files.begin(), files.end());\n\n    if(dir.at(dir.length() - 1) != '/')\n        dir = dir+\"/\";\n\n    for(unsigned int i = 0; i < files.size(); i++)\n    {\n        if(files[i].at(0) != '/')\n            files[i] = dir + files[i];\n    }\n\n    return (int)files.size();\n}\n"
  },
  {
    "path": "src/ImageReader.h",
    "content": "//\n//  ImageReader.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#ifndef OnlinePhotometricCalibration_ImageReader_h_\n#define OnlinePhotometricCalibration_ImageReader_h_\n\n#include <stdio.h>\n#include <iostream>\n#include <dirent.h>\n\n#include \"StandardIncludes.h\"\n\n/**\n * Read input images from image files\n * Resizes the images if requested\n */\n\nclass ImageReader\n{\npublic:\n    \n    /** \n     * Initialize the image reader\n     * @param image_folder Image folder\n     * @param new_size Resize input images to new_size\n     */\n    ImageReader(std::string image_folder,\n                cv::Size new_size);\n\n    /**\n     * Read a new input image from the hard drive and return it\n     *\n     * @param Input image index to read\n     * @return Read input image\n     */\n    cv::Mat readImage(int image_index);\n\n    int getNumImages() { return (int)m_files.size(); }\n\n    int getDir(std::string dir, std::vector<std::string> &files);\n    \nprivate:\n    \n    /**\n     * Resize images to this size\n     */\n    cv::Size m_img_new_size;\n\n    std::vector<std::string> m_files;\n};\n\n#endif // include guard\n"
  },
  {
    "path": "src/JacobianGenerator.cpp",
    "content": "//\n//  JacobianGenerator.cpp\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#include \"JacobianGenerator.h\"\n\nJacobianGenerator::JacobianGenerator()\n{\n    // Nothing to initialize\n}\n\nvoid JacobianGenerator::getRawJacobianRow(double I,\n                                          double r,\n                                          double e,\n                                          std::vector<double>& j_res,\n                                          std::vector<double>& j_vig,\n                                          double& j_e,\n                                          double& j_I)\n{\n    j_res.clear();\n    j_vig.clear();\n    \n    // Get the vignetting value\n    double a2 = m_vignetting_params.at(0);\n    double a4 = m_vignetting_params.at(1);\n    double a6 = m_vignetting_params.at(2);\n    double r2 = r * r;\n    double r4 = r2 * r2;\n    double r6 = r4 * r2;\n    double v  = 1 + a2*r2 + a4*r4 + a6*r6;\n        \n    // Evaluate the Grossberg base functions at the derivatives for the other derivatives\n    double eIv = e * I * v;\n    double h_0_d = evaluateGrossbergBaseFunction(0, true, eIv);\n    double h_1_d = evaluateGrossbergBaseFunction(1, true, eIv);\n    double h_2_d = evaluateGrossbergBaseFunction(2, true, eIv);\n    double h_3_d = evaluateGrossbergBaseFunction(3, true, eIv);\n    double h_4_d = evaluateGrossbergBaseFunction(4, true, eIv);\n        \n    double deriv_value = h_0_d +\n                         m_response_params.at(0)*h_1_d +\n                         m_response_params.at(1)*h_2_d +\n                         m_response_params.at(2)*h_3_d +\n                         m_response_params.at(3)*h_4_d;\n    \n    // Derive by the 4 Grossberg parameters\n    double j_res_1  = 255*evaluateGrossbergBaseFunction(1, false, eIv);\n    double j_res_2  = 255*evaluateGrossbergBaseFunction(2, false, eIv);\n    double j_res_3  = 255*evaluateGrossbergBaseFunction(3, false, eIv);\n    double j_res_4  = 255*evaluateGrossbergBaseFunction(4, false, eIv);\n        \n    j_res.push_back(j_res_1);\n    j_res.push_back(j_res_2);\n    j_res.push_back(j_res_3);\n    j_res.push_back(j_res_4);\n    \n    // Derive by the 3 vignetting parameters\n    double j_vig_1 = 255 * deriv_value * e * I * r2;\n    double j_vig_2 = 255 * deriv_value * e * I * r4;\n    double j_vig_3 = 255 * deriv_value * e * I * r6;\n    \n    j_vig.push_back(j_vig_1);\n    j_vig.push_back(j_vig_2);\n    j_vig.push_back(j_vig_3);\n    \n    // Derive by exposure time\n    j_e = 255 * deriv_value * (I*v);\n\n    double j_I_temp;\n    getJacobianRadiance(I, r, e, j_I_temp);\n    j_I = j_I_temp;\n}\n\nvoid JacobianGenerator::getJacobianRow_eca(double I,\n                                           double r,\n                                           double e,\n                                           cv::Mat jacobian,\n                                           int image_index,\n                                           int residual_index)\n{\n    // Get the vignetting value\n    double a2 = m_vignetting_params.at(0);\n    double a4 = m_vignetting_params.at(1);\n    double a6 = m_vignetting_params.at(2);\n    double r2 = r * r;\n    double r4 = r2 * r2;\n    double r6 = r4 * r2;\n    double v  = 1 + a2*r2 + a4*r4 + a6*r6;\n        \n    // Evaluate the grossberg base functions' derivatives for the other derivatives\n    double eIv = e * I * v;\n    double h_0_d = evaluateGrossbergBaseFunction(0, true, eIv);\n    double h_1_d = evaluateGrossbergBaseFunction(1, true, eIv);\n    double h_2_d = evaluateGrossbergBaseFunction(2, true, eIv);\n    double h_3_d = evaluateGrossbergBaseFunction(3, true, eIv);\n    double h_4_d = evaluateGrossbergBaseFunction(4, true, eIv);\n\n    double deriv_value = h_0_d +\n                         m_response_params.at(0)*h_1_d +\n                         m_response_params.at(1)*h_2_d +\n                         m_response_params.at(2)*h_3_d +\n                         m_response_params.at(3)*h_4_d;\n        \n    // Derive by the 4 Grossberg parameters\n    jacobian.at<double>(residual_index,0) = 255*evaluateGrossbergBaseFunction(1, false, eIv);\n    jacobian.at<double>(residual_index,1) = 255*evaluateGrossbergBaseFunction(2, false, eIv);\n    jacobian.at<double>(residual_index,2) = 255*evaluateGrossbergBaseFunction(3, false, eIv);\n    jacobian.at<double>(residual_index,3) = 255*evaluateGrossbergBaseFunction(4, false, eIv);\n    \n    // Derive by the 3 vignetting parameters\n    jacobian.at<double>(residual_index,4) = 255 * deriv_value * e * I * r2;\n    jacobian.at<double>(residual_index,5) = 255 * deriv_value * e * I * r4;\n    jacobian.at<double>(residual_index,6) = 255 * deriv_value * e * I * r6;\n        \n    // Derive by exposure time\n    jacobian.at<double>(residual_index,7+image_index) = 255 * deriv_value * (I*v);\n}\n\nvoid JacobianGenerator::getJacobianRadiance(double I,double r,double e,double& j_I)\n{\n    double a2 = m_vignetting_params.at(0);\n    double a4 = m_vignetting_params.at(1);\n    double a6 = m_vignetting_params.at(2);\n    \n    // Get the vignetting value\n    double r2 = r * r;\n    double r4 = r2 * r2;\n    double r6 = r4 * r2;\n    double v  = 1 + a2*r2 + a4*r4 + a6*r6;\n        \n    // Evaluate the Grossberg base functions' derivatives for the other derivatives\n    double h_0_d = evaluateGrossbergBaseFunction(0, true, e*I*v);\n    double h_1_d = evaluateGrossbergBaseFunction(1, true, e*I*v);\n    double h_2_d = evaluateGrossbergBaseFunction(2, true, e*I*v);\n    double h_3_d = evaluateGrossbergBaseFunction(3, true, e*I*v);\n    double h_4_d = evaluateGrossbergBaseFunction(4, true, e*I*v);\n        \n    double deriv_value = h_0_d +\n                         m_response_params.at(0)*h_1_d +\n                         m_response_params.at(1)*h_2_d +\n                         m_response_params.at(2)*h_3_d +\n                         m_response_params.at(3)*h_4_d;\n    \n    j_I = 255 * deriv_value * (e*v);\n}\n\n\ndouble JacobianGenerator::evaluateGrossbergBaseFunction(int base_function_index,bool is_derivative,double x)\n{\n    if(x < 0)x = 0.0;\n    else if(x > 1)x = 1.0;\n    \n    int x_int = round(x*1023);\n    int x_der_int = round(x*1021);\n    \n    if(base_function_index == 0)\n    {\n        if(!is_derivative)\n        {\n            return m_f_0[x_int];\n        }\n        else\n        {\n            return m_f_0_der[x_der_int];\n        }\n    }\n    \n    if(base_function_index == 1)\n    {\n        if(!is_derivative)\n        {\n            return m_h_1[x_int];\n        }\n        else\n        {\n            return m_h_1_der[x_der_int];\n        }\n    }\n    \n    if(base_function_index == 2)\n    {\n        if(!is_derivative)\n        {\n            return m_h_2[x_int];\n        }\n        else\n        {\n            return m_h_2_der[x_der_int];\n        }\n    }\n    \n    if(base_function_index == 3)\n    {\n        if(!is_derivative)\n        {\n            return m_h_3[x_int];\n        }\n        else\n        {\n            return m_h_3_der[x_der_int];\n        }\n    }\n    \n    if(base_function_index == 4)\n    {\n        if(!is_derivative)\n        {\n            return m_h_4[x_int];\n        }\n        else\n        {\n            return m_h_4_der[x_der_int];\n        }\n    }\n    \n    // Error code\n    return -1.0;\n}\n\ndouble JacobianGenerator::applyGrossbergResponse(double x)\n{\n    double v0 = evaluateGrossbergBaseFunction(0, false, x);\n    double v1 = evaluateGrossbergBaseFunction(1, false, x);\n    double v2 = evaluateGrossbergBaseFunction(2, false, x);\n    double v3 = evaluateGrossbergBaseFunction(3, false, x);\n    double v4 = evaluateGrossbergBaseFunction(4, false, x);\n    \n    double c1 = m_response_params.at(0);\n    double c2 = m_response_params.at(1);\n    double c3 = m_response_params.at(2);\n    double c4 = m_response_params.at(3);\n    \n    return v0 + c1*v1 + c2*v2 + c3*v3 + c4*v4;\n}\n\nstd::vector<double> JacobianGenerator::fitGrossbergModelToResponseVector(double* response)\n{\n    // Given a response vector, find Grossberg parameters that fit well\n    cv::Mat LeftSide(4,4,CV_64F,0.0);\n    cv::Mat RightSide(4,1,CV_64F,0.0);\n    \n    for(int i = 10;i < 240;i++)\n    {\n        response[i] /= 255.0;\n        \n        double input = i/256.0;\n        \n        double f0 = evaluateGrossbergBaseFunction(0, false, input);\n        double f1 = evaluateGrossbergBaseFunction(1, false, input);\n        double f2 = evaluateGrossbergBaseFunction(2, false, input);\n        double f3 = evaluateGrossbergBaseFunction(3, false, input);\n        double f4 = evaluateGrossbergBaseFunction(4, false, input);\n\n        // For equation 1\n        LeftSide.at<double>(0,0) += f1*f1;\n        LeftSide.at<double>(0,1) += f1*f2;\n        LeftSide.at<double>(0,2) += f1*f3;\n        LeftSide.at<double>(0,3) += f1*f4;\n        \n        RightSide.at<double>(0,0) += (response[i]*f1 - f0*f1);\n        \n        // For equation 2\n        LeftSide.at<double>(1,0) += f2*f1;\n        LeftSide.at<double>(1,1) += f2*f2;\n        LeftSide.at<double>(1,2) += f2*f3;\n        LeftSide.at<double>(1,3) += f2*f4;\n        \n        RightSide.at<double>(1,0) += (response[i]*f2 - f0*f2);\n        \n        // For equation 3\n        LeftSide.at<double>(2,0) += f3*f1;\n        LeftSide.at<double>(2,1) += f3*f2;\n        LeftSide.at<double>(2,2) += f3*f3;\n        LeftSide.at<double>(2,3) += f3*f4;\n        \n        RightSide.at<double>(2,0) += (response[i]*f3 - f0*f3);\n        \n        // For equation 4\n        LeftSide.at<double>(3,0) += f4*f1;\n        LeftSide.at<double>(3,1) += f4*f2;\n        LeftSide.at<double>(3,2) += f4*f3;\n        LeftSide.at<double>(3,3) += f4*f4;\n        \n        RightSide.at<double>(3,0) += (response[i]*f4 - f0*f4);\n    }\n    \n    cv::Mat Solution;\n    cv::solve(LeftSide, RightSide, Solution,cv::DECOMP_SVD);\n    \n    std::vector<double> solution_response;\n    solution_response.push_back(Solution.at<double>(0,0));\n    solution_response.push_back(Solution.at<double>(1,0));\n    solution_response.push_back(Solution.at<double>(2,0));\n    solution_response.push_back(Solution.at<double>(3,0));\n    \n    return solution_response;\n}\n"
  },
  {
    "path": "src/JacobianGenerator.h",
    "content": "//\n//  JacobianGenerator.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#ifndef OnlinePhotometricCalibration_JacobianGenerator_h_\n#define OnlinePhotometricCalibration_JacobianGenerator_h_\n\n#include \"StandardIncludes.h\"\n\nclass JacobianGenerator\n{\n    \npublic:\n    \n    /**\n     * Constructor\n     */\n    JacobianGenerator();\n    \n    /**\n     * Initialize Grossberg response parameters\n     */\n    void setResponseParameters(std::vector<double> response_params)\n    {\n        m_response_params = response_params;\n    };\n    \n    /**\n     * Initialize vignette parameters\n     */\n    void setVignettingParameters(std::vector<double> vignetting_params)\n    {\n        m_vignetting_params = vignetting_params;\n    };\n    \n    /**\n     * Get the raw Jacobian row to construct the Hessian directly\n     */\n    void getRawJacobianRow(double I,\n                           double r,\n                           double e,\n                           std::vector<double>& j_res,\n                           std::vector<double>& j_vig,\n                           double& j_e,\n                           double& j_I);\n    \n    /**\n     * Apply the Grossberg response to a value x\n     */\n    double applyGrossbergResponse(double x);\n    \n    /**\n     * Get the Jacobian information, passing the irradiance, radius of the point and exposure time\n     * of the image\n     */\n     // Todo: what does eca mean? Term for evf from old version.\n    void getJacobianRow_eca(double I, double r, double e,cv::Mat jacobian,int image_index,int residual_index);\n    \n    /**\n     * Get the Jacobian entry for the irradiance\n     */\n    void getJacobianRadiance(double I,double r,double e, double& j_I);\n    \n    /**\n     * Get 4 least squares parameters for an input response vector\n     */\n    std::vector<double> fitGrossbergModelToResponseVector(double* response);\n    \nprivate:\n    \n    /**\n     * Response and vignette parameters\n     */\n    std::vector<double> m_response_params;\n    std::vector<double> m_vignetting_params;\n\n    //Evaluate one of the Grossberg base functions or their derivatives (f0,h0,h1,h2,h3,..) at location x\n    //Base functions are approximated by polynomials (of max. degree 10)\n    double evaluateGrossbergBaseFunction(int base_function_index,bool is_derivative,double x);\n    \n    /**\n     * Here follow the PCA components of the first 4 basis functions of the Grossberg model as discrete 1024 component vectors\n     * The derivatives have 1023 components, obtained by symmetric differences\n     */\n    double m_f_0[1024] = {0,0.0088337,0.014976,0.020237,0.024986,0.029375,0.033483,0.037402,0.041166,0.044812,0.048351,0.05179,0.055148,0.058435,0.061671,0.064858,0.067999,0.071094,0.074141,0.077147,0.080112,0.083033,0.085917,0.08876,0.091569,0.094344,0.097087,0.099801,0.10249,0.10515,0.10778,0.1104,0.11299,0.11557,0.11813,0.12068,0.1232,0.12572,0.12823,0.13072,0.13319,0.13566,0.13811,0.14055,0.14298,0.14539,0.14778,0.15016,0.15253,0.15489,0.15723,0.15957,0.1619,0.16423,0.16654,0.16885,0.17114,0.17343,0.1757,0.17797,0.18023,0.18247,0.1847,0.18693,0.18914,0.19135,0.19355,0.19575,0.19793,0.20011,0.20228,0.20445,0.2066,0.20875,0.2109,0.21303,0.21516,0.21729,0.21941,0.22152,0.22363,0.22574,0.22784,0.22994,0.23204,0.23413,0.23621,0.2383,0.24038,0.24246,0.24453,0.2466,0.24867,0.25073,0.25279,0.25484,0.25689,0.25893,0.26097,0.263,0.26502,0.26705,0.26906,0.27107,0.27308,0.27508,0.27708,0.27908,0.28107,0.28305,0.28503,0.28701,0.28898,0.29094,0.2929,0.29485,0.2968,0.29874,0.30068,0.30261,0.30453,0.30645,0.30836,0.31026,0.31216,0.31406,0.31595,0.31783,0.31971,0.32159,0.32345,0.32532,0.32717,0.32903,0.33087,0.33271,0.33455,0.33638,0.33821,0.34003,0.34184,0.34365,0.34546,0.34726,0.34905,0.35084,0.35262,0.35439,0.35616,0.35792,0.35968,0.36143,0.36318,0.36492,0.36665,0.36838,0.3701,0.37182,0.37354,0.37525,0.37695,0.37865,0.38035,0.38204,0.38372,0.3854,0.38707,0.38874,0.39041,0.39207,0.39372,0.39537,0.39702,0.39866,0.4003,0.40193,0.40356,0.40519,0.40681,0.40842,0.41003,0.41164,0.41324,0.41484,0.41644,0.41803,0.41961,0.4212,0.42278,0.42435,0.42593,0.42749,0.42906,0.43062,0.43217,0.43372,0.43527,0.43681,0.43835,0.43989,0.44142,0.44294,0.44446,0.44598,0.44749,0.449,0.4505,0.452,0.4535,0.45499,0.45647,0.45795,0.45943,0.4609,0.46236,0.46382,0.46528,0.46673,0.46817,0.46962,0.47105,0.47249,0.47392,0.47535,0.47677,0.47819,0.4796,0.48101,0.48242,0.48382,0.48521,0.48661,0.488,0.48938,0.49076,0.49214,0.49351,0.49487,0.49624,0.49759,0.49895,0.50029,0.50164,0.50298,0.50431,0.50564,0.50697,0.50829,0.50961,0.51092,0.51223,0.51354,0.51484,0.51614,0.51743,0.51872,0.52001,0.5213,0.52258,0.52385,0.52513,0.5264,0.52766,0.52893,0.53019,0.53145,0.5327,0.53395,0.5352,0.53644,0.53768,0.53891,0.54014,0.54137,0.54259,0.54382,0.54503,0.54625,0.54746,0.54867,0.54987,0.55107,0.55226,0.55346,0.55465,0.55583,0.55702,0.5582,0.55937,0.56055,0.56172,0.56289,0.56405,0.56521,0.56637,0.56753,0.56868,0.56983,0.57098,0.57213,0.57327,0.57441,0.57554,0.57667,0.5778,0.57892,0.58004,0.58116,0.58228,0.58339,0.58449,0.5856,0.58669,0.58779,0.58888,0.58997,0.59106,0.59214,0.59322,0.5943,0.59537,0.59644,0.5975,0.59856,0.59962,0.60068,0.60173,0.60278,0.60383,0.60487,0.60591,0.60695,0.60799,0.60902,0.61005,0.61108,0.6121,0.61312,0.61414,0.61515,0.61617,0.61717,0.61818,0.61918,0.62018,0.62117,0.62217,0.62316,0.62414,0.62513,0.62611,0.62709,0.62807,0.62904,0.63001,0.63098,0.63195,0.63291,0.63388,0.63484,0.6358,0.63675,0.63771,0.63866,0.63961,0.64056,0.6415,0.64245,0.64339,0.64433,0.64526,0.6462,0.64713,0.64806,0.64898,0.64991,0.65083,0.65175,0.65266,0.65358,0.65449,0.6554,0.6563,0.65721,0.65811,0.65901,0.65991,0.6608,0.6617,0.66259,0.66347,0.66436,0.66524,0.66613,0.66701,0.66788,0.66876,0.66963,0.67051,0.67137,0.67224,0.67311,0.67397,0.67483,0.67568,0.67654,0.67739,0.67824,0.67909,0.67994,0.68078,0.68162,0.68246,0.6833,0.68414,0.68497,0.6858,0.68663,0.68746,0.68828,0.68911,0.68993,0.69075,0.69157,0.69239,0.69321,0.69402,0.69483,0.69564,0.69645,0.69726,0.69807,0.69888,0.69968,0.70048,0.70128,0.70208,0.70288,0.70367,0.70447,0.70526,0.70604,0.70683,0.70762,0.7084,0.70918,0.70996,0.71074,0.71152,0.71229,0.71306,0.71384,0.71461,0.71537,0.71614,0.71691,0.71767,0.71843,0.71919,0.71996,0.72071,0.72147,0.72223,0.72298,0.72374,0.72449,0.72524,0.72599,0.72674,0.72748,0.72823,0.72897,0.72971,0.73046,0.7312,0.73193,0.73267,0.73341,0.73414,0.73487,0.7356,0.73633,0.73706,0.73778,0.73851,0.73923,0.73995,0.74067,0.74138,0.7421,0.74281,0.74352,0.74423,0.74494,0.74565,0.74635,0.74705,0.74775,0.74846,0.74915,0.74985,0.75055,0.75124,0.75194,0.75263,0.75332,0.75401,0.7547,0.75539,0.75608,0.75676,0.75745,0.75813,0.75881,0.75949,0.76016,0.76084,0.76151,0.76218,0.76286,0.76352,0.76419,0.76486,0.76552,0.76619,0.76685,0.76751,0.76817,0.76883,0.76949,0.77014,0.7708,0.77146,0.77211,0.77276,0.77342,0.77407,0.77472,0.77537,0.77602,0.77667,0.77732,0.77796,0.77861,0.77925,0.77989,0.78053,0.78117,0.78181,0.78244,0.78308,0.78371,0.78435,0.78498,0.78561,0.78624,0.78687,0.7875,0.78813,0.78876,0.78939,0.79001,0.79064,0.79126,0.79189,0.79251,0.79313,0.79375,0.79438,0.795,0.79562,0.79623,0.79685,0.79747,0.79808,0.7987,0.79931,0.79992,0.80053,0.80114,0.80175,0.80236,0.80296,0.80357,0.80417,0.80477,0.80537,0.80598,0.80657,0.80717,0.80777,0.80837,0.80896,0.80956,0.81016,0.81075,0.81134,0.81194,0.81253,0.81312,0.81371,0.8143,0.81489,0.81547,0.81606,0.81665,0.81724,0.81782,0.81841,0.81899,0.81957,0.82015,0.82073,0.82131,0.82188,0.82246,0.82303,0.82361,0.82418,0.82475,0.82532,0.82589,0.82646,0.82703,0.82759,0.82816,0.82872,0.82928,0.82985,0.83041,0.83097,0.83153,0.83209,0.83265,0.8332,0.83376,0.83432,0.83488,0.83543,0.83599,0.83654,0.8371,0.83765,0.8382,0.83875,0.8393,0.83985,0.8404,0.84094,0.84149,0.84203,0.84257,0.84311,0.84365,0.84419,0.84473,0.84526,0.8458,0.84633,0.84687,0.8474,0.84793,0.84846,0.84899,0.84952,0.85005,0.85058,0.8511,0.85163,0.85215,0.85268,0.8532,0.85373,0.85425,0.85477,0.85529,0.85581,0.85633,0.85685,0.85737,0.85789,0.85841,0.85892,0.85944,0.85995,0.86047,0.86098,0.86149,0.862,0.86251,0.86301,0.86352,0.86402,0.86453,0.86503,0.86553,0.86604,0.86654,0.86704,0.86753,0.86803,0.86853,0.86903,0.86952,0.87001,0.87051,0.871,0.87149,0.87198,0.87248,0.87297,0.87346,0.87395,0.87443,0.87492,0.87541,0.8759,0.87639,0.87687,0.87736,0.87784,0.87832,0.8788,0.87928,0.87976,0.88024,0.88072,0.8812,0.88167,0.88215,0.88263,0.8831,0.88358,0.88405,0.88452,0.885,0.88547,0.88594,0.88641,0.88689,0.88736,0.88783,0.8883,0.88877,0.88924,0.88971,0.89017,0.89064,0.89111,0.89157,0.89204,0.8925,0.89296,0.89342,0.89388,0.89434,0.89481,0.89526,0.89572,0.89618,0.89664,0.8971,0.89755,0.89801,0.89847,0.89892,0.89938,0.89983,0.90029,0.90074,0.9012,0.90165,0.90211,0.90256,0.90301,0.90346,0.90392,0.90437,0.90482,0.90527,0.90572,0.90617,0.90662,0.90706,0.90751,0.90795,0.9084,0.90884,0.90929,0.90973,0.91017,0.91061,0.91105,0.91149,0.91193,0.91237,0.91281,0.91325,0.91369,0.91413,0.91456,0.915,0.91544,0.91587,0.91631,0.91675,0.91718,0.91762,0.91806,0.91849,0.91893,0.91936,0.9198,0.92023,0.92067,0.9211,0.92153,0.92196,0.92239,0.92282,0.92325,0.92368,0.9241,0.92453,0.92495,0.92538,0.9258,0.92623,0.92665,0.92707,0.9275,0.92792,0.92834,0.92876,0.92918,0.9296,0.93002,0.93044,0.93086,0.93129,0.93171,0.93212,0.93254,0.93296,0.93338,0.9338,0.93422,0.93463,0.93505,0.93546,0.93588,0.93629,0.9367,0.93711,0.93753,0.93794,0.93835,0.93876,0.93917,0.93957,0.93998,0.94039,0.94079,0.9412,0.94161,0.94202,0.94242,0.94283,0.94324,0.94364,0.94405,0.94445,0.94485,0.94526,0.94566,0.94607,0.94647,0.94687,0.94728,0.94768,0.94808,0.94848,0.94888,0.94928,0.94967,0.95007,0.95047,0.95086,0.95125,0.95165,0.95204,0.95243,0.95282,0.95322,0.95361,0.954,0.95439,0.95478,0.95516,0.95555,0.95594,0.95633,0.95671,0.9571,0.95749,0.95787,0.95826,0.95865,0.95903,0.95941,0.9598,0.96018,0.96057,0.96095,0.96133,0.96172,0.9621,0.96248,0.96287,0.96325,0.96363,0.96401,0.96439,0.96477,0.96515,0.96553,0.96591,0.96629,0.96667,0.96705,0.96742,0.9678,0.96818,0.96856,0.96893,0.96931,0.96969,0.97006,0.97044,0.97081,0.97118,0.97156,0.97193,0.9723,0.97268,0.97305,0.97342,0.97379,0.97416,0.97452,0.97489,0.97526,0.97562,0.97599,0.97636,0.97672,0.97709,0.97745,0.97781,0.97818,0.97854,0.9789,0.97926,0.97962,0.97998,0.98034,0.9807,0.98105,0.98141,0.98177,0.98213,0.98248,0.98284,0.9832,0.98355,0.98391,0.98426,0.98462,0.98497,0.98533,0.98568,0.98603,0.98638,0.98673,0.98707,0.98742,0.98777,0.98811,0.98846,0.9888,0.98915,0.98949,0.98983,0.99017,0.99051,0.99086,0.9912,0.99153,0.99187,0.9922,0.99254,0.99287,0.99321,0.99354,0.99387,0.99421,0.99454,0.99487,0.9952,0.99553,0.99586,0.99618,0.99651,0.99683,0.99715,0.99747,0.99779,0.9981,0.99842,0.99874,0.99905,0.99937,0.99968,1\n    };\n    \n    double m_h_1[1024] = {0,-0.0014523,-0.0021332,-0.0026743,-0.0031499,-0.0035824,-0.0039809,-0.0043598,-0.0047252,-0.0050806,-0.0054271,-0.0057658,-0.0060988,-0.0064263,-0.0067504,-0.0070713,-0.0073899,-0.0077059,-0.0080188,-0.0083293,-0.0086366,-0.0089397,-0.0092394,-0.0095354,-0.0098288,-0.010119,-0.010407,-0.010692,-0.010976,-0.011259,-0.011539,-0.011819,-0.012097,-0.012375,-0.012651,-0.012926,-0.0132,-0.013474,-0.013747,-0.014019,-0.01429,-0.014561,-0.014831,-0.015099,-0.015367,-0.015633,-0.015897,-0.016161,-0.016424,-0.016685,-0.016947,-0.017208,-0.017468,-0.017728,-0.017987,-0.018244,-0.018501,-0.018757,-0.019011,-0.019264,-0.019516,-0.019766,-0.020016,-0.020264,-0.020512,-0.020759,-0.021005,-0.02125,-0.021494,-0.021738,-0.02198,-0.022221,-0.022462,-0.022701,-0.02294,-0.023177,-0.023414,-0.02365,-0.023884,-0.024118,-0.02435,-0.024582,-0.024813,-0.025043,-0.025271,-0.025499,-0.025726,-0.025951,-0.026175,-0.026398,-0.02662,-0.026839,-0.027058,-0.027275,-0.027491,-0.027705,-0.027918,-0.028129,-0.028339,-0.028548,-0.028755,-0.028961,-0.029167,-0.02937,-0.029573,-0.029775,-0.029975,-0.030174,-0.030371,-0.030567,-0.030762,-0.030955,-0.031147,-0.031337,-0.031526,-0.031713,-0.031899,-0.032083,-0.032265,-0.032445,-0.032624,-0.032801,-0.032976,-0.033149,-0.033321,-0.03349,-0.033658,-0.033824,-0.033989,-0.034151,-0.034313,-0.034472,-0.034631,-0.034787,-0.034942,-0.035096,-0.035248,-0.035399,-0.035548,-0.035696,-0.035843,-0.035988,-0.036132,-0.036275,-0.036415,-0.036555,-0.036692,-0.036829,-0.036963,-0.037096,-0.037228,-0.037358,-0.037486,-0.037614,-0.037739,-0.037863,-0.037986,-0.038107,-0.038227,-0.038346,-0.038463,-0.03858,-0.038695,-0.038809,-0.038922,-0.039034,-0.039146,-0.039256,-0.039365,-0.039472,-0.039579,-0.039685,-0.03979,-0.039893,-0.039995,-0.040097,-0.040197,-0.040296,-0.040393,-0.04049,-0.040585,-0.040678,-0.040771,-0.040862,-0.040951,-0.041039,-0.041126,-0.041211,-0.041295,-0.041377,-0.041458,-0.041538,-0.041616,-0.041693,-0.041768,-0.041842,-0.041915,-0.041987,-0.042057,-0.042127,-0.042195,-0.042263,-0.042329,-0.042395,-0.04246,-0.042524,-0.042588,-0.04265,-0.042711,-0.042772,-0.042831,-0.04289,-0.042948,-0.043004,-0.04306,-0.043115,-0.043169,-0.043222,-0.043274,-0.043326,-0.043376,-0.043426,-0.043475,-0.043522,-0.043568,-0.043614,-0.043658,-0.043702,-0.043745,-0.043787,-0.043828,-0.043869,-0.043909,-0.043948,-0.043986,-0.044023,-0.04406,-0.044096,-0.044131,-0.044165,-0.044199,-0.044232,-0.044265,-0.044296,-0.044327,-0.044358,-0.044387,-0.044416,-0.044444,-0.044471,-0.044497,-0.044523,-0.044547,-0.044571,-0.044594,-0.044617,-0.044638,-0.044659,-0.044679,-0.044698,-0.044717,-0.044734,-0.04475,-0.044765,-0.04478,-0.044793,-0.044806,-0.044819,-0.044831,-0.044842,-0.044853,-0.044864,-0.044875,-0.044885,-0.044894,-0.044903,-0.044912,-0.04492,-0.044928,-0.044935,-0.044942,-0.044948,-0.044954,-0.044959,-0.044964,-0.044969,-0.044973,-0.044976,-0.044978,-0.04498,-0.044982,-0.044983,-0.044983,-0.044983,-0.044982,-0.04498,-0.044978,-0.044975,-0.044971,-0.044967,-0.044962,-0.044957,-0.044951,-0.044945,-0.044938,-0.044931,-0.044924,-0.044916,-0.044908,-0.0449,-0.044891,-0.044882,-0.044873,-0.044863,-0.044852,-0.044842,-0.044831,-0.044819,-0.044807,-0.044795,-0.044781,-0.044768,-0.044754,-0.04474,-0.044725,-0.044709,-0.044694,-0.044678,-0.044661,-0.044643,-0.044625,-0.044606,-0.044587,-0.044567,-0.044547,-0.044527,-0.044507,-0.044486,-0.044465,-0.044443,-0.044422,-0.0444,-0.044378,-0.044356,-0.044333,-0.044311,-0.044288,-0.044265,-0.044242,-0.044218,-0.044194,-0.04417,-0.044145,-0.04412,-0.044094,-0.044068,-0.044042,-0.044015,-0.043987,-0.043959,-0.043931,-0.043901,-0.043872,-0.043842,-0.043811,-0.043781,-0.04375,-0.043719,-0.043687,-0.043656,-0.043624,-0.043592,-0.043559,-0.043527,-0.043494,-0.043461,-0.043428,-0.043394,-0.043361,-0.043327,-0.043293,-0.043259,-0.043224,-0.04319,-0.043155,-0.04312,-0.043085,-0.043049,-0.043013,-0.042977,-0.042941,-0.042904,-0.042866,-0.042829,-0.042791,-0.042753,-0.042715,-0.042676,-0.042637,-0.042598,-0.042558,-0.042519,-0.042479,-0.042439,-0.042398,-0.042358,-0.042317,-0.042277,-0.042236,-0.042195,-0.042154,-0.042113,-0.042072,-0.042031,-0.041989,-0.041947,-0.041905,-0.041863,-0.041821,-0.041778,-0.041735,-0.041692,-0.041648,-0.041604,-0.04156,-0.041516,-0.041472,-0.041427,-0.041382,-0.041336,-0.04129,-0.041244,-0.041197,-0.041151,-0.041104,-0.041057,-0.04101,-0.040963,-0.040917,-0.04087,-0.040823,-0.040777,-0.04073,-0.040683,-0.040636,-0.040588,-0.040541,-0.040494,-0.040446,-0.040399,-0.040352,-0.040305,-0.040257,-0.04021,-0.040162,-0.040114,-0.040066,-0.040018,-0.039969,-0.03992,-0.039871,-0.039821,-0.039771,-0.039721,-0.039671,-0.039621,-0.03957,-0.039519,-0.039468,-0.039417,-0.039366,-0.039315,-0.039264,-0.039212,-0.03916,-0.039108,-0.039056,-0.039004,-0.038951,-0.038899,-0.038847,-0.038794,-0.038742,-0.03869,-0.038638,-0.038585,-0.038533,-0.038481,-0.038429,-0.038377,-0.038324,-0.038272,-0.03822,-0.038167,-0.038115,-0.038062,-0.038009,-0.037956,-0.037903,-0.03785,-0.037796,-0.037743,-0.037689,-0.037635,-0.037581,-0.037526,-0.037472,-0.037417,-0.037362,-0.037307,-0.037252,-0.037198,-0.037142,-0.037088,-0.037033,-0.036978,-0.036923,-0.036869,-0.036814,-0.03676,-0.036705,-0.03665,-0.036595,-0.03654,-0.036485,-0.03643,-0.036375,-0.036319,-0.036264,-0.036208,-0.036152,-0.036096,-0.03604,-0.035984,-0.035927,-0.03587,-0.035812,-0.035755,-0.035697,-0.035639,-0.035581,-0.035523,-0.035465,-0.035407,-0.035349,-0.035291,-0.035234,-0.035176,-0.035119,-0.035062,-0.035004,-0.034947,-0.034889,-0.034832,-0.034774,-0.034716,-0.034658,-0.0346,-0.034542,-0.034484,-0.034426,-0.034367,-0.034308,-0.034249,-0.03419,-0.03413,-0.03407,-0.034011,-0.03395,-0.03389,-0.03383,-0.033769,-0.033709,-0.033648,-0.033588,-0.033527,-0.033466,-0.033406,-0.033345,-0.033285,-0.033224,-0.033164,-0.033104,-0.033044,-0.032984,-0.032924,-0.032864,-0.032804,-0.032743,-0.032683,-0.032622,-0.032561,-0.0325,-0.032439,-0.032378,-0.032316,-0.032254,-0.032193,-0.032131,-0.032069,-0.032007,-0.031944,-0.031881,-0.031818,-0.031755,-0.031692,-0.031628,-0.031564,-0.031501,-0.031437,-0.031373,-0.031309,-0.031246,-0.031182,-0.031119,-0.031056,-0.030993,-0.03093,-0.030867,-0.030804,-0.030741,-0.030678,-0.030616,-0.030553,-0.03049,-0.030427,-0.030364,-0.0303,-0.030237,-0.030173,-0.030109,-0.030045,-0.029981,-0.029917,-0.029852,-0.029788,-0.029723,-0.029657,-0.029591,-0.029526,-0.02946,-0.029393,-0.029327,-0.029261,-0.029195,-0.029128,-0.029062,-0.028995,-0.028929,-0.028863,-0.028797,-0.028731,-0.028665,-0.028599,-0.028534,-0.028468,-0.028402,-0.028337,-0.028271,-0.028205,-0.02814,-0.028074,-0.028008,-0.027942,-0.027876,-0.02781,-0.027745,-0.027678,-0.027612,-0.027545,-0.027479,-0.027411,-0.027344,-0.027276,-0.027208,-0.02714,-0.027072,-0.027004,-0.026935,-0.026867,-0.026798,-0.026729,-0.02666,-0.026592,-0.026523,-0.026454,-0.026386,-0.026317,-0.026249,-0.02618,-0.026112,-0.026044,-0.025976,-0.025908,-0.02584,-0.025772,-0.025704,-0.025636,-0.025568,-0.025499,-0.025431,-0.025362,-0.025294,-0.025226,-0.025157,-0.025089,-0.02502,-0.024951,-0.024881,-0.024812,-0.024742,-0.024672,-0.024601,-0.024531,-0.024461,-0.024391,-0.024321,-0.024251,-0.024181,-0.024111,-0.024041,-0.023971,-0.023901,-0.023831,-0.023761,-0.02369,-0.02362,-0.02355,-0.02348,-0.02341,-0.02334,-0.023269,-0.023198,-0.023128,-0.023057,-0.022986,-0.022915,-0.022844,-0.022772,-0.0227,-0.022629,-0.022557,-0.022484,-0.022412,-0.02234,-0.022268,-0.022196,-0.022123,-0.022051,-0.021978,-0.021906,-0.021834,-0.021762,-0.02169,-0.021618,-0.021546,-0.021473,-0.0214,-0.021328,-0.021255,-0.021182,-0.021109,-0.021035,-0.020962,-0.020888,-0.020814,-0.02074,-0.020666,-0.020592,-0.020518,-0.020443,-0.020368,-0.020292,-0.020216,-0.020141,-0.020065,-0.019989,-0.019913,-0.019837,-0.019762,-0.019686,-0.019611,-0.019535,-0.01946,-0.019385,-0.01931,-0.019235,-0.01916,-0.019085,-0.019009,-0.018934,-0.018859,-0.018784,-0.018709,-0.018633,-0.018558,-0.018482,-0.018407,-0.018331,-0.018254,-0.018178,-0.018101,-0.018024,-0.017947,-0.01787,-0.017793,-0.017715,-0.017637,-0.017558,-0.01748,-0.017401,-0.017323,-0.017244,-0.017166,-0.017087,-0.017008,-0.01693,-0.016852,-0.016774,-0.016697,-0.016619,-0.016542,-0.016464,-0.016387,-0.01631,-0.016232,-0.016154,-0.016077,-0.015999,-0.015921,-0.015844,-0.015766,-0.015689,-0.015611,-0.015533,-0.015455,-0.015376,-0.015298,-0.015219,-0.01514,-0.015061,-0.014982,-0.014902,-0.014823,-0.014744,-0.014665,-0.014586,-0.014507,-0.014428,-0.01435,-0.014271,-0.014193,-0.014115,-0.014036,-0.013958,-0.01388,-0.013801,-0.013723,-0.013644,-0.013566,-0.013488,-0.013409,-0.01333,-0.013251,-0.013171,-0.013092,-0.013012,-0.012933,-0.012853,-0.012772,-0.012692,-0.012611,-0.01253,-0.012449,-0.012368,-0.012286,-0.012205,-0.012124,-0.012042,-0.011961,-0.01188,-0.011798,-0.011717,-0.011636,-0.011556,-0.011475,-0.011395,-0.011315,-0.011235,-0.011155,-0.011076,-0.010996,-0.010916,-0.010836,-0.010756,-0.010676,-0.010597,-0.010517,-0.010437,-0.010358,-0.010278,-0.010198,-0.010118,-0.010038,-0.0099572,-0.0098768,-0.0097964,-0.0097162,-0.009636,-0.0095558,-0.0094755,-0.009395,-0.0093142,-0.0092332,-0.0091522,-0.0090711,-0.0089901,-0.0089092,-0.0088283,-0.0087475,-0.0086667,-0.0085857,-0.0085044,-0.0084229,-0.0083413,-0.0082596,-0.0081778,-0.0080958,-0.0080136,-0.0079313,-0.0078489,-0.0077663,-0.0076835,-0.0076005,-0.0075175,-0.0074344,-0.0073511,-0.0072679,-0.0071848,-0.0071016,-0.0070185,-0.0069354,-0.006852,-0.0067684,-0.006685,-0.0066018,-0.0065188,-0.0064358,-0.006353,-0.0062704,-0.0061877,-0.006105,-0.0060221,-0.005939,-0.005856,-0.0057731,-0.0056902,-0.0056076,-0.0055251,-0.0054426,-0.0053602,-0.0052777,-0.005195,-0.0051119,-0.0050288,-0.0049455,-0.0048622,-0.0047786,-0.0046949,-0.0046111,-0.0045274,-0.0044436,-0.0043596,-0.0042749,-0.00419,-0.0041049,-0.0040198,-0.0039347,-0.0038496,-0.0037645,-0.0036794,-0.0035945,-0.0035096,-0.0034243,-0.0033393,-0.0032545,-0.0031698,-0.0030852,-0.0030005,-0.0029159,-0.0028313,-0.0027468,-0.0026623,-0.0025773,-0.002492,-0.0024067,-0.0023215,-0.0022367,-0.0021519,-0.0020668,-0.0019817,-0.0018966,-0.0018116,-0.0017261,-0.0016399,-0.0015535,-0.0014668,-0.0013798,-0.0012927,-0.0012056,-0.001119,-0.0010326,-0.00094644,-0.00086078,-0.00077461,-0.00068832,-0.00060193,-0.0005156,-0.00042937,-0.00034323,-0.00025723,-0.00017134,-8.5608e-05,-1.7347e-18\n    };\n    double m_h_2[1024] = {0,-0.0068347,-0.010307,-0.013097,-0.015503,-0.017653,-0.019616,-0.021445,-0.023169,-0.02481,-0.026374,-0.02787,-0.029302,-0.030675,-0.031995,-0.033266,-0.034488,-0.035667,-0.036804,-0.037904,-0.038969,-0.04,-0.040994,-0.041958,-0.042891,-0.043794,-0.04467,-0.045518,-0.04634,-0.047138,-0.047913,-0.048664,-0.049394,-0.050103,-0.050793,-0.051466,-0.052121,-0.052758,-0.05338,-0.053986,-0.054576,-0.05515,-0.055709,-0.056253,-0.056783,-0.057298,-0.057798,-0.058284,-0.058756,-0.059215,-0.05966,-0.060092,-0.060511,-0.060917,-0.06131,-0.061691,-0.062059,-0.062413,-0.062753,-0.063078,-0.06339,-0.063689,-0.063975,-0.064249,-0.06451,-0.064758,-0.064995,-0.06522,-0.065435,-0.065637,-0.065827,-0.066007,-0.066177,-0.066336,-0.066485,-0.066622,-0.06675,-0.066867,-0.066974,-0.067073,-0.067162,-0.067241,-0.06731,-0.067371,-0.067423,-0.067465,-0.067499,-0.067524,-0.067538,-0.067544,-0.06754,-0.067528,-0.067507,-0.067477,-0.067438,-0.06739,-0.067334,-0.06727,-0.067196,-0.067114,-0.067025,-0.066928,-0.066822,-0.066708,-0.066588,-0.066461,-0.066327,-0.066185,-0.066038,-0.065885,-0.065724,-0.065559,-0.065389,-0.065212,-0.065029,-0.064842,-0.064649,-0.064451,-0.064249,-0.064041,-0.06383,-0.063614,-0.063393,-0.063169,-0.062939,-0.062705,-0.062467,-0.062225,-0.06198,-0.061731,-0.061479,-0.061223,-0.060964,-0.060702,-0.060437,-0.060169,-0.0599,-0.059626,-0.059352,-0.059075,-0.058795,-0.058513,-0.058227,-0.057939,-0.057649,-0.057357,-0.057063,-0.056766,-0.056468,-0.056167,-0.055864,-0.055559,-0.055251,-0.054943,-0.054634,-0.054323,-0.054009,-0.053694,-0.053376,-0.053058,-0.052738,-0.052417,-0.052093,-0.051768,-0.051441,-0.051113,-0.050783,-0.050453,-0.050119,-0.049785,-0.04945,-0.049114,-0.048775,-0.048434,-0.048092,-0.047749,-0.047404,-0.047058,-0.046711,-0.046363,-0.046011,-0.04566,-0.045308,-0.044954,-0.044597,-0.044241,-0.043882,-0.043521,-0.043161,-0.042797,-0.042432,-0.042066,-0.041697,-0.041326,-0.040954,-0.040581,-0.040207,-0.039831,-0.039455,-0.039078,-0.038699,-0.038319,-0.037939,-0.037559,-0.037178,-0.036798,-0.036419,-0.036039,-0.035662,-0.035285,-0.034908,-0.034532,-0.034158,-0.033784,-0.033412,-0.03304,-0.032669,-0.032299,-0.031929,-0.031562,-0.031194,-0.030827,-0.030462,-0.030098,-0.029735,-0.029373,-0.029011,-0.028651,-0.028292,-0.027933,-0.027575,-0.027219,-0.026863,-0.026508,-0.026154,-0.0258,-0.025447,-0.025096,-0.024746,-0.024397,-0.024048,-0.023702,-0.023355,-0.02301,-0.022668,-0.022325,-0.021983,-0.021642,-0.021302,-0.020963,-0.020624,-0.020287,-0.019952,-0.019618,-0.019284,-0.018952,-0.018621,-0.018291,-0.017962,-0.017634,-0.017309,-0.016984,-0.016661,-0.016338,-0.016017,-0.015699,-0.015381,-0.015063,-0.014747,-0.014432,-0.014119,-0.013807,-0.013496,-0.013185,-0.012877,-0.012569,-0.012262,-0.011957,-0.011652,-0.011349,-0.011047,-0.010746,-0.010446,-0.010148,-0.0098508,-0.0095548,-0.0092602,-0.0089671,-0.0086748,-0.0083839,-0.0080947,-0.007806,-0.0075181,-0.0072313,-0.0069458,-0.0066611,-0.0063777,-0.0060952,-0.0058141,-0.0055343,-0.0052557,-0.0049783,-0.0047019,-0.0044267,-0.0041522,-0.003879,-0.0036074,-0.0033367,-0.003067,-0.002798,-0.0025299,-0.0022637,-0.0019984,-0.0017338,-0.0014707,-0.0012081,-0.00094708,-0.00068784,-0.00043029,-0.00017447,8.0061e-05,0.00033373,0.00058625,0.00083738,0.0010864,0.0013345,0.0015814,0.0018269,0.0020714,0.0023141,0.0025552,0.0027949,0.0030335,0.0032705,0.0035057,0.00374,0.0039728,0.0042041,0.0044347,0.004664,0.0048907,0.0051161,0.0053408,0.0055642,0.0057866,0.0060076,0.0062269,0.0064447,0.0066615,0.006877,0.0070915,0.0073043,0.0075159,0.0077268,0.0079358,0.008143,0.0083492,0.0085539,0.0087573,0.0089598,0.0091611,0.0093613,0.0095593,0.0097565,0.0099527,0.010148,0.010341,0.010533,0.010725,0.010916,0.011105,0.011294,0.011482,0.011668,0.011853,0.012037,0.012221,0.012403,0.012583,0.012763,0.012943,0.01312,0.013297,0.013472,0.013647,0.013821,0.013993,0.014164,0.014334,0.014504,0.014671,0.014837,0.015001,0.015166,0.015329,0.01549,0.015651,0.015811,0.01597,0.016126,0.016282,0.016437,0.016591,0.016745,0.016897,0.017047,0.017198,0.017346,0.017493,0.017639,0.017785,0.01793,0.018073,0.018216,0.018358,0.0185,0.01864,0.01878,0.018918,0.019054,0.01919,0.019325,0.019458,0.01959,0.019722,0.019852,0.019981,0.02011,0.020237,0.020363,0.020488,0.020613,0.020736,0.020857,0.020977,0.021097,0.021216,0.021334,0.021451,0.021567,0.021682,0.021796,0.021908,0.022019,0.02213,0.022239,0.022348,0.022455,0.022562,0.022668,0.022773,0.022877,0.02298,0.023083,0.023184,0.023285,0.023384,0.023482,0.023579,0.023675,0.023771,0.023865,0.02396,0.024053,0.024145,0.024237,0.024327,0.024417,0.024506,0.024594,0.024681,0.024768,0.024853,0.024937,0.025021,0.025104,0.025187,0.025269,0.02535,0.02543,0.02551,0.025589,0.025667,0.025744,0.02582,0.025895,0.02597,0.026044,0.026117,0.026189,0.02626,0.026331,0.026401,0.02647,0.026538,0.026605,0.026672,0.026738,0.026803,0.026867,0.02693,0.026992,0.027054,0.027116,0.027177,0.027237,0.027296,0.027354,0.027413,0.027471,0.027527,0.027583,0.027638,0.027693,0.027747,0.0278,0.027853,0.027904,0.027955,0.028006,0.028055,0.028104,0.028152,0.028198,0.028243,0.028289,0.028334,0.028378,0.02842,0.028463,0.028504,0.028545,0.028586,0.028626,0.028665,0.028703,0.02874,0.028776,0.028813,0.028848,0.028883,0.028916,0.028949,0.028982,0.029015,0.029047,0.029078,0.029108,0.029138,0.029168,0.029197,0.029226,0.029254,0.02928,0.029306,0.029332,0.029357,0.029382,0.029406,0.029429,0.029451,0.029474,0.029495,0.029516,0.029536,0.029556,0.029575,0.029594,0.029613,0.02963,0.029647,0.029663,0.029679,0.029694,0.029709,0.029724,0.029738,0.029751,0.029763,0.029775,0.029787,0.029799,0.02981,0.029821,0.02983,0.02984,0.029849,0.029857,0.029865,0.029872,0.029878,0.029884,0.029889,0.029895,0.0299,0.029905,0.029909,0.029912,0.029914,0.029916,0.029918,0.029919,0.029919,0.029919,0.029917,0.029916,0.029914,0.029911,0.029909,0.029906,0.029902,0.029898,0.029893,0.029888,0.029883,0.029878,0.029872,0.029866,0.029859,0.029853,0.029846,0.029838,0.02983,0.029822,0.029813,0.029803,0.029792,0.029781,0.02977,0.029759,0.029747,0.029735,0.029721,0.029708,0.029694,0.029679,0.029665,0.029649,0.029632,0.029615,0.029598,0.02958,0.029562,0.029544,0.029524,0.029504,0.029483,0.029461,0.029439,0.029418,0.029396,0.029374,0.029351,0.029328,0.029304,0.02928,0.029257,0.029233,0.029209,0.029183,0.029158,0.029131,0.029105,0.029078,0.02905,0.029022,0.028994,0.028965,0.028936,0.028906,0.028876,0.028845,0.028814,0.028782,0.028749,0.028716,0.028683,0.028649,0.028615,0.028581,0.028546,0.02851,0.028474,0.028438,0.028402,0.028365,0.028327,0.028289,0.028251,0.028212,0.028172,0.028131,0.028091,0.02805,0.028009,0.027968,0.027927,0.027884,0.02784,0.027797,0.027753,0.027709,0.027665,0.02762,0.027574,0.027527,0.02748,0.027433,0.027386,0.027338,0.027289,0.027241,0.027191,0.027141,0.027091,0.027041,0.02699,0.02694,0.026889,0.026839,0.026787,0.026735,0.026682,0.026629,0.026576,0.026523,0.02647,0.026415,0.02636,0.026305,0.02625,0.026195,0.026139,0.026083,0.026027,0.02597,0.025913,0.025855,0.025797,0.025738,0.02568,0.025621,0.025562,0.025503,0.025443,0.025383,0.025323,0.025261,0.025201,0.02514,0.025079,0.025018,0.024956,0.024895,0.024833,0.024772,0.02471,0.024647,0.024584,0.02452,0.024456,0.024392,0.024328,0.024264,0.024199,0.024134,0.024069,0.024003,0.023938,0.023871,0.023804,0.023737,0.023669,0.023602,0.023535,0.023467,0.023399,0.023331,0.023262,0.023193,0.023124,0.023056,0.022987,0.022918,0.022848,0.022779,0.022709,0.022639,0.022568,0.022497,0.022426,0.022354,0.022283,0.022211,0.02214,0.022068,0.021995,0.021922,0.021849,0.021775,0.021701,0.021626,0.021551,0.021476,0.021399,0.021322,0.021246,0.021168,0.021091,0.021014,0.020937,0.020859,0.020781,0.020702,0.020623,0.020543,0.020464,0.020384,0.020305,0.020225,0.020146,0.020066,0.019985,0.019904,0.019823,0.019741,0.01966,0.019578,0.019497,0.019415,0.019332,0.019249,0.019165,0.019082,0.018998,0.018915,0.018831,0.018747,0.018662,0.018577,0.018491,0.018405,0.018318,0.018232,0.018145,0.018058,0.017971,0.017885,0.017798,0.017711,0.017624,0.017536,0.017448,0.017359,0.017271,0.017183,0.017095,0.017007,0.016918,0.016828,0.016737,0.016647,0.016557,0.016467,0.016376,0.016285,0.016194,0.016102,0.016009,0.015916,0.015823,0.01573,0.015636,0.015543,0.015449,0.015355,0.015261,0.015167,0.015072,0.014976,0.014881,0.014784,0.014688,0.014593,0.014497,0.014402,0.014306,0.014209,0.014112,0.014015,0.013918,0.01382,0.013723,0.013625,0.013528,0.01343,0.013332,0.013234,0.013136,0.013037,0.012939,0.01284,0.01274,0.012641,0.012541,0.012442,0.012342,0.012242,0.012142,0.012042,0.011942,0.011841,0.011741,0.01164,0.011539,0.011439,0.011338,0.011237,0.011135,0.011035,0.010934,0.010833,0.010732,0.010631,0.01053,0.010428,0.010327,0.010225,0.010122,0.01002,0.0099181,0.0098158,0.0097133,0.0096106,0.0095075,0.0094048,0.0093019,0.0091988,0.009095,0.008991,0.0088871,0.008783,0.0086783,0.0085738,0.0084696,0.0083655,0.0082614,0.0081571,0.0080524,0.0079474,0.0078423,0.0077377,0.0076328,0.0075276,0.0074223,0.0073169,0.0072114,0.007106,0.0070003,0.0068945,0.0067887,0.0066829,0.0065767,0.0064702,0.0063634,0.0062568,0.0061502,0.0060436,0.0059369,0.0058299,0.0057232,0.0056165,0.0055096,0.005402,0.005294,0.0051864,0.0050787,0.004971,0.0048633,0.0047556,0.0046473,0.004539,0.0044306,0.0043225,0.0042146,0.0041068,0.0039989,0.0038912,0.0037835,0.0036757,0.0035675,0.0034594,0.0033513,0.0032426,0.0031342,0.0030257,0.0029175,0.0028102,0.0027027,0.0025957,0.0024887,0.0023812,0.0022729,0.002164,0.0020547,0.0019452,0.0018357,0.0017267,0.0016181,0.0015099,0.0014027,0.0012957,0.0011884,0.0010802,0.0009714,0.00086302,0.00075512,0.0006472,0.00053931,0.00043141,0.00032353,0.00021566,0.00010782,1.8173e-18\n    };\n    double m_h_3[1024] = {0,-0.014921,-0.021271,-0.026188,-0.030308,-0.033903,-0.037108,-0.040033,-0.042748,-0.045285,-0.04766,-0.049885,-0.05197,-0.053922,-0.055754,-0.057472,-0.059081,-0.060593,-0.062013,-0.063351,-0.064611,-0.065796,-0.066905,-0.067948,-0.068925,-0.069842,-0.0707,-0.0715,-0.072245,-0.072937,-0.073576,-0.074163,-0.074698,-0.07518,-0.075616,-0.076006,-0.07635,-0.076644,-0.076897,-0.077103,-0.077268,-0.077385,-0.077459,-0.077495,-0.077489,-0.077442,-0.077359,-0.077234,-0.077072,-0.076878,-0.076646,-0.076378,-0.076083,-0.075751,-0.075388,-0.074998,-0.074578,-0.07413,-0.07365,-0.073142,-0.072605,-0.072043,-0.071456,-0.070849,-0.070214,-0.069554,-0.068877,-0.068179,-0.067463,-0.066727,-0.065971,-0.065199,-0.064414,-0.063615,-0.062804,-0.061977,-0.061139,-0.06029,-0.05943,-0.058561,-0.057681,-0.056788,-0.055883,-0.054974,-0.054051,-0.053119,-0.052183,-0.051232,-0.050271,-0.049303,-0.048324,-0.047338,-0.04634,-0.045334,-0.044322,-0.043299,-0.042269,-0.041233,-0.040189,-0.039139,-0.038085,-0.037026,-0.035965,-0.034903,-0.033844,-0.032782,-0.031718,-0.030655,-0.029596,-0.028541,-0.027482,-0.02643,-0.025382,-0.024335,-0.023294,-0.022258,-0.021226,-0.020198,-0.019181,-0.018168,-0.017162,-0.016161,-0.015167,-0.014182,-0.013201,-0.012227,-0.011264,-0.010308,-0.0093601,-0.0084202,-0.0074881,-0.0065665,-0.0056555,-0.0047563,-0.0038666,-0.0029902,-0.0021261,-0.0012678,-0.00042104,0.00041688,0.0012467,0.002067,0.0028773,0.00368,0.0044721,0.0052528,0.0060233,0.0067886,0.0075441,0.0082887,0.0090257,0.0097539,0.010475,0.011187,0.011888,0.012583,0.013267,0.013943,0.014612,0.015273,0.015928,0.016571,0.017206,0.01783,0.018444,0.01905,0.019645,0.020231,0.02081,0.021383,0.021945,0.022502,0.023049,0.023588,0.024118,0.02464,0.025149,0.02565,0.026142,0.026626,0.0271,0.027566,0.028021,0.02847,0.028911,0.029342,0.029766,0.030181,0.030587,0.030985,0.031374,0.031753,0.032125,0.032486,0.032838,0.03318,0.033517,0.033843,0.034162,0.034472,0.034775,0.035071,0.03536,0.035642,0.035915,0.03618,0.036437,0.03669,0.036934,0.037173,0.037409,0.037637,0.037857,0.03807,0.038275,0.038474,0.038664,0.038851,0.039031,0.039207,0.039378,0.039545,0.039708,0.039862,0.040014,0.040159,0.040299,0.040435,0.040565,0.040691,0.040813,0.040927,0.041039,0.041146,0.041247,0.041343,0.041432,0.041516,0.041594,0.041664,0.041731,0.041791,0.041845,0.041892,0.041932,0.04197,0.042002,0.042029,0.042053,0.042071,0.042087,0.042096,0.0421,0.0421,0.042097,0.042089,0.042079,0.042065,0.042047,0.042027,0.041999,0.041966,0.041932,0.041893,0.041846,0.041796,0.041743,0.041688,0.04163,0.041567,0.0415,0.04143,0.041354,0.041273,0.04119,0.041103,0.041013,0.040921,0.040824,0.040722,0.040618,0.040512,0.040403,0.040292,0.040178,0.040062,0.039944,0.039823,0.039699,0.039575,0.039451,0.039326,0.039199,0.03907,0.038939,0.038807,0.038674,0.038537,0.038397,0.038254,0.038111,0.037965,0.037817,0.037666,0.037514,0.037359,0.037203,0.037044,0.036882,0.036719,0.036554,0.036386,0.036214,0.036041,0.035867,0.035692,0.035517,0.035341,0.035162,0.034981,0.034798,0.034612,0.034425,0.034237,0.034046,0.033854,0.033662,0.033469,0.033276,0.033083,0.032887,0.032689,0.032488,0.032287,0.032084,0.031878,0.031669,0.03146,0.03125,0.031035,0.03082,0.030603,0.030386,0.030166,0.029946,0.029724,0.0295,0.029274,0.029047,0.028821,0.028593,0.028363,0.028133,0.027903,0.027671,0.027437,0.027203,0.026966,0.026732,0.026497,0.026262,0.026026,0.025789,0.025551,0.025314,0.025077,0.02484,0.024602,0.024366,0.024128,0.023889,0.02365,0.023411,0.02317,0.022928,0.022684,0.022441,0.022198,0.021954,0.021709,0.021466,0.021222,0.020978,0.020734,0.020492,0.02025,0.020008,0.019766,0.019523,0.019282,0.019042,0.0188,0.018558,0.018317,0.018077,0.017836,0.017595,0.017354,0.017113,0.016873,0.016633,0.016392,0.016152,0.015913,0.015673,0.015432,0.015193,0.014954,0.014713,0.014471,0.01423,0.013989,0.013749,0.013509,0.013267,0.013026,0.012786,0.012546,0.012305,0.012064,0.011824,0.011583,0.011342,0.011101,0.01086,0.010619,0.01038,0.010141,0.0099011,0.0096599,0.0094194,0.0091791,0.0089374,0.0086957,0.008456,0.0082158,0.007976,0.0077362,0.0074965,0.0072567,0.0070153,0.0067754,0.0065347,0.0062935,0.0060531,0.0058136,0.0055748,0.0053366,0.0050992,0.0048638,0.0046283,0.0043931,0.0041588,0.0039253,0.0036928,0.0034598,0.0032279,0.0029962,0.0027652,0.0025358,0.002308,0.002081,0.0018547,0.0016299,0.0014069,0.0011842,0.00096186,0.00074029,0.00051977,0.00029953,8.0471e-05,-0.00013786,-0.00035613,-0.00057425,-0.00079129,-0.0010066,-0.0012213,-0.0014365,-0.0016525,-0.0018679,-0.0020822,-0.0022967,-0.0025112,-0.0027253,-0.0029389,-0.003152,-0.0033641,-0.0035754,-0.0037855,-0.0039961,-0.0042057,-0.0044139,-0.0046208,-0.0048281,-0.0050346,-0.0052394,-0.0054423,-0.0056443,-0.0058464,-0.006048,-0.0062491,-0.006448,-0.006646,-0.0068419,-0.0070365,-0.0072305,-0.0074234,-0.0076144,-0.0078043,-0.0079922,-0.0081782,-0.0083641,-0.0085491,-0.0087333,-0.0089189,-0.0091032,-0.0092876,-0.0094723,-0.0096552,-0.0098374,-0.01002,-0.010201,-0.010383,-0.010563,-0.010742,-0.01092,-0.011097,-0.011273,-0.011449,-0.011624,-0.011797,-0.011969,-0.012141,-0.012312,-0.012483,-0.012652,-0.012819,-0.012987,-0.013154,-0.013321,-0.013487,-0.013652,-0.013816,-0.013981,-0.014146,-0.014311,-0.014475,-0.014638,-0.014802,-0.014965,-0.015128,-0.015289,-0.01545,-0.015609,-0.015768,-0.015926,-0.016085,-0.016243,-0.0164,-0.016557,-0.016712,-0.016867,-0.017021,-0.017175,-0.017327,-0.017478,-0.017628,-0.017777,-0.017927,-0.018077,-0.018226,-0.018374,-0.018522,-0.018669,-0.018816,-0.018963,-0.019111,-0.019259,-0.019405,-0.019551,-0.019697,-0.019844,-0.01999,-0.020136,-0.02028,-0.020425,-0.020571,-0.020716,-0.020859,-0.021001,-0.021142,-0.021283,-0.021422,-0.021559,-0.021696,-0.021832,-0.021969,-0.022105,-0.022239,-0.022372,-0.022504,-0.022636,-0.022766,-0.022895,-0.023022,-0.023149,-0.023276,-0.023402,-0.023528,-0.023653,-0.023777,-0.023898,-0.024019,-0.02414,-0.024259,-0.024379,-0.024499,-0.024618,-0.024736,-0.024852,-0.024969,-0.025085,-0.025201,-0.025315,-0.025429,-0.025541,-0.025652,-0.025762,-0.02587,-0.025976,-0.026081,-0.026185,-0.026288,-0.02639,-0.02649,-0.026588,-0.026685,-0.026781,-0.026876,-0.02697,-0.027061,-0.027151,-0.027241,-0.027329,-0.027417,-0.027504,-0.02759,-0.027676,-0.027762,-0.027848,-0.027931,-0.028014,-0.028098,-0.028181,-0.028264,-0.028345,-0.028425,-0.028504,-0.028582,-0.02866,-0.028737,-0.028813,-0.028888,-0.028961,-0.029033,-0.029104,-0.029174,-0.029242,-0.02931,-0.029376,-0.029441,-0.029505,-0.029565,-0.029623,-0.029681,-0.029738,-0.029794,-0.029847,-0.0299,-0.02995,-0.030001,-0.030051,-0.0301,-0.030148,-0.030196,-0.030242,-0.030286,-0.030328,-0.03037,-0.030411,-0.030452,-0.030493,-0.030535,-0.030576,-0.030615,-0.030654,-0.030691,-0.030727,-0.030762,-0.030797,-0.030832,-0.030865,-0.030897,-0.030927,-0.030956,-0.030984,-0.031011,-0.031037,-0.031061,-0.031083,-0.031104,-0.031125,-0.031144,-0.031162,-0.031179,-0.031197,-0.031214,-0.031231,-0.031247,-0.031262,-0.031277,-0.03129,-0.031303,-0.031315,-0.031326,-0.031336,-0.031345,-0.031352,-0.031359,-0.031366,-0.031372,-0.031379,-0.031384,-0.031388,-0.031392,-0.031394,-0.031396,-0.031397,-0.031398,-0.031399,-0.031399,-0.031398,-0.031396,-0.031392,-0.031388,-0.031383,-0.031379,-0.031375,-0.031371,-0.031367,-0.031362,-0.031356,-0.03135,-0.031344,-0.031337,-0.031331,-0.031325,-0.031318,-0.031309,-0.031299,-0.031289,-0.031277,-0.031265,-0.031254,-0.031244,-0.031234,-0.031222,-0.031209,-0.031196,-0.031181,-0.031166,-0.03115,-0.031134,-0.031117,-0.0311,-0.031083,-0.031065,-0.031046,-0.031027,-0.031008,-0.030988,-0.030969,-0.030949,-0.030928,-0.030906,-0.030884,-0.030861,-0.030838,-0.030816,-0.030793,-0.03077,-0.030745,-0.03072,-0.030693,-0.030665,-0.030637,-0.030606,-0.030575,-0.030544,-0.03051,-0.030476,-0.030442,-0.030406,-0.030368,-0.03033,-0.030291,-0.030251,-0.030211,-0.030169,-0.030126,-0.030082,-0.030038,-0.029992,-0.029947,-0.029901,-0.029856,-0.02981,-0.029763,-0.029717,-0.029669,-0.029621,-0.029571,-0.029521,-0.02947,-0.029419,-0.029367,-0.029314,-0.029261,-0.029207,-0.029152,-0.029095,-0.029038,-0.02898,-0.02892,-0.02886,-0.0288,-0.028739,-0.028676,-0.028612,-0.028546,-0.028479,-0.028412,-0.028344,-0.028277,-0.028208,-0.02814,-0.028071,-0.028002,-0.027932,-0.027862,-0.027791,-0.02772,-0.027648,-0.027576,-0.027502,-0.027428,-0.027353,-0.027277,-0.0272,-0.027121,-0.027041,-0.02696,-0.026878,-0.026796,-0.026713,-0.026629,-0.026544,-0.026457,-0.026368,-0.026277,-0.026185,-0.026091,-0.025996,-0.0259,-0.025803,-0.025706,-0.025608,-0.025508,-0.025408,-0.025308,-0.025206,-0.025102,-0.024998,-0.024893,-0.024788,-0.024683,-0.024577,-0.02447,-0.024362,-0.024253,-0.024142,-0.02403,-0.023918,-0.023806,-0.023693,-0.023578,-0.023463,-0.023346,-0.023229,-0.02311,-0.02299,-0.022868,-0.022746,-0.022622,-0.022498,-0.022373,-0.022247,-0.022121,-0.021993,-0.021864,-0.021733,-0.021602,-0.021468,-0.021334,-0.0212,-0.021065,-0.02093,-0.020794,-0.020656,-0.020517,-0.020377,-0.020236,-0.020094,-0.01995,-0.019806,-0.01966,-0.019514,-0.019367,-0.019218,-0.019068,-0.018918,-0.018766,-0.018613,-0.018459,-0.018304,-0.018147,-0.01799,-0.017831,-0.017671,-0.017511,-0.01735,-0.017189,-0.017026,-0.016861,-0.016696,-0.01653,-0.016364,-0.016197,-0.01603,-0.015862,-0.015693,-0.015521,-0.015348,-0.015174,-0.014999,-0.014823,-0.014647,-0.01447,-0.01429,-0.01411,-0.013928,-0.013745,-0.013561,-0.013375,-0.013188,-0.012999,-0.012808,-0.012617,-0.012425,-0.012232,-0.012037,-0.011841,-0.011644,-0.011445,-0.011244,-0.011042,-0.010839,-0.010637,-0.010434,-0.01023,-0.010025,-0.0098193,-0.0096123,-0.0094013,-0.0091896,-0.008978,-0.0087664,-0.0085547,-0.0083427,-0.0081303,-0.0079163,-0.0076998,-0.0074827,-0.0072628,-0.0070403,-0.0068173,-0.0065936,-0.0063689,-0.0061436,-0.0059164,-0.0056888,-0.0054595,-0.0052283,-0.0049945,-0.0047578,-0.0045202,-0.0042818,-0.0040406,-0.0037982,-0.0035542,-0.0033093,-0.0030641,-0.0028175,-0.0025668,-0.0023127,-0.0020571,-0.0018,-0.0015428,-0.0012856,-0.0010284,-0.00077119,-0.00051403,-0.00025699,-7.4722e-18\n    };\n    double m_h_4[1024] = {0,-0.036868,-0.047079,-0.054243,-0.059747,-0.064217,-0.068097,-0.071502,-0.074567,-0.077356,-0.07992,-0.082274,-0.084399,-0.086256,-0.087799,-0.089046,-0.090015,-0.090725,-0.091243,-0.091587,-0.091825,-0.092002,-0.09208,-0.092102,-0.092019,-0.091842,-0.091583,-0.091238,-0.090835,-0.090355,-0.089779,-0.089105,-0.088331,-0.087452,-0.086465,-0.08538,-0.084192,-0.082872,-0.081453,-0.079943,-0.078343,-0.076651,-0.0749,-0.073089,-0.071228,-0.069322,-0.067386,-0.065429,-0.063436,-0.061417,-0.059368,-0.057269,-0.05513,-0.052946,-0.050746,-0.04853,-0.046307,-0.044096,-0.041895,-0.039726,-0.037586,-0.035494,-0.033418,-0.031357,-0.029308,-0.027276,-0.025261,-0.023273,-0.021315,-0.01938,-0.017463,-0.015579,-0.013726,-0.011897,-0.010093,-0.0083305,-0.0066009,-0.0048927,-0.003205,-0.0015393,0.00010878,0.0017415,0.0033578,0.0049465,0.0065261,0.0080872,0.0096236,0.011143,0.012631,0.01409,0.015531,0.016938,0.018321,0.019673,0.020993,0.022284,0.023542,0.024761,0.02594,0.027097,0.028224,0.029323,0.030396,0.031443,0.03247,0.033476,0.034457,0.035417,0.03636,0.037281,0.038172,0.039035,0.039882,0.0407,0.04148,0.042228,0.042944,0.043635,0.044293,0.044918,0.045506,0.046065,0.046591,0.047088,0.047554,0.047989,0.048396,0.048784,0.049148,0.049492,0.049817,0.050128,0.050418,0.050687,0.050937,0.051171,0.051391,0.051605,0.051806,0.051988,0.052161,0.052321,0.052455,0.052581,0.052684,0.052772,0.052845,0.0529,0.052943,0.052952,0.052939,0.052908,0.052858,0.052787,0.052691,0.052584,0.052454,0.052308,0.052153,0.051978,0.051795,0.051602,0.051383,0.051158,0.05092,0.050665,0.050403,0.050124,0.04983,0.049531,0.049224,0.048905,0.048559,0.048207,0.04785,0.047485,0.04709,0.046682,0.046265,0.045847,0.045391,0.044931,0.044464,0.043968,0.043458,0.042942,0.042401,0.04184,0.041274,0.040669,0.040058,0.039425,0.03877,0.038099,0.0374,0.036692,0.035955,0.035212,0.034454,0.033685,0.0329,0.032107,0.031306,0.030498,0.029695,0.028888,0.02809,0.02729,0.026503,0.025711,0.024929,0.024149,0.023384,0.022623,0.021868,0.021119,0.020373,0.019636,0.018897,0.018176,0.017455,0.016738,0.016033,0.01533,0.014632,0.013939,0.013248,0.012567,0.011894,0.011219,0.010554,0.0098912,0.009226,0.0085706,0.0079248,0.0072788,0.006636,0.0060032,0.00537,0.0047369,0.004115,0.0034978,0.0028842,0.0022729,0.0016699,0.0010683,0.00046891,-0.00012734,-0.00071357,-0.001299,-0.0018845,-0.0024658,-0.0030351,-0.0036026,-0.0041658,-0.0047265,-0.0052728,-0.0058155,-0.0063525,-0.0068818,-0.007399,-0.0079097,-0.0084129,-0.0089077,-0.0093985,-0.0098752,-0.010346,-0.010812,-0.011273,-0.011725,-0.012166,-0.0126,-0.01303,-0.013453,-0.013867,-0.014274,-0.014673,-0.015067,-0.015455,-0.015832,-0.016201,-0.016566,-0.016926,-0.01728,-0.017627,-0.017966,-0.018299,-0.018627,-0.01895,-0.019269,-0.019582,-0.01989,-0.020195,-0.020494,-0.020789,-0.021077,-0.021362,-0.021644,-0.021923,-0.022197,-0.022466,-0.022731,-0.022994,-0.023253,-0.023511,-0.023764,-0.024012,-0.024259,-0.024506,-0.024752,-0.025003,-0.02525,-0.025492,-0.025734,-0.025973,-0.02621,-0.026445,-0.026679,-0.026911,-0.027142,-0.027369,-0.027595,-0.027821,-0.028043,-0.028262,-0.02848,-0.028693,-0.028902,-0.029107,-0.029305,-0.029495,-0.029681,-0.029867,-0.030047,-0.030222,-0.030394,-0.030562,-0.030725,-0.030885,-0.031041,-0.031194,-0.031342,-0.031482,-0.031616,-0.031746,-0.031872,-0.031993,-0.032107,-0.032218,-0.032325,-0.032427,-0.032522,-0.032613,-0.032698,-0.032777,-0.032848,-0.032918,-0.032985,-0.033048,-0.033105,-0.033158,-0.033205,-0.033239,-0.033272,-0.033304,-0.033336,-0.033362,-0.033388,-0.033414,-0.03343,-0.033442,-0.033449,-0.033455,-0.033459,-0.03346,-0.033461,-0.033457,-0.033448,-0.033438,-0.033427,-0.033414,-0.0334,-0.033384,-0.033367,-0.033349,-0.033331,-0.03331,-0.033287,-0.033262,-0.033239,-0.033214,-0.033181,-0.033147,-0.033113,-0.03308,-0.033047,-0.033011,-0.032974,-0.032937,-0.032893,-0.032849,-0.032804,-0.032761,-0.032716,-0.03267,-0.032623,-0.032578,-0.032534,-0.032484,-0.032432,-0.032377,-0.032321,-0.032266,-0.032207,-0.032146,-0.032082,-0.032018,-0.031951,-0.03188,-0.031803,-0.031723,-0.031641,-0.031556,-0.031467,-0.031374,-0.031278,-0.031184,-0.031084,-0.030979,-0.030872,-0.030763,-0.030651,-0.030534,-0.03041,-0.030286,-0.030162,-0.030037,-0.029908,-0.02978,-0.029654,-0.029526,-0.029393,-0.029255,-0.029113,-0.028973,-0.028832,-0.028689,-0.028545,-0.028399,-0.028254,-0.028107,-0.027961,-0.027812,-0.027657,-0.027502,-0.027344,-0.027183,-0.027019,-0.026857,-0.026697,-0.026533,-0.026371,-0.026207,-0.026039,-0.025869,-0.025697,-0.025525,-0.025349,-0.025172,-0.024993,-0.024818,-0.024644,-0.024466,-0.024287,-0.024102,-0.023918,-0.023733,-0.023549,-0.023364,-0.023177,-0.02299,-0.022805,-0.022621,-0.022436,-0.022249,-0.022061,-0.021874,-0.021686,-0.021496,-0.021304,-0.021114,-0.020925,-0.020737,-0.020547,-0.020356,-0.020166,-0.019977,-0.019785,-0.019592,-0.019397,-0.0192,-0.019006,-0.018811,-0.018617,-0.018422,-0.018226,-0.018031,-0.017837,-0.017646,-0.017458,-0.017267,-0.017073,-0.016879,-0.016687,-0.016495,-0.016304,-0.016113,-0.015922,-0.01573,-0.015538,-0.015346,-0.015157,-0.014967,-0.014773,-0.014578,-0.014384,-0.014188,-0.013993,-0.013795,-0.013597,-0.0134,-0.013204,-0.013005,-0.012806,-0.012603,-0.0124,-0.012196,-0.011989,-0.011777,-0.011567,-0.011356,-0.011143,-0.010929,-0.010715,-0.0105,-0.010285,-0.010067,-0.009846,-0.0096244,-0.009401,-0.0091754,-0.0089505,-0.0087226,-0.0084913,-0.008259,-0.0080253,-0.0077892,-0.0075518,-0.0073111,-0.007066,-0.0068186,-0.0065705,-0.0063226,-0.0060728,-0.0058209,-0.005566,-0.005309,-0.0050518,-0.0047917,-0.0045281,-0.0042628,-0.0039963,-0.0037282,-0.0034616,-0.0031953,-0.0029282,-0.0026611,-0.0023933,-0.0021255,-0.0018581,-0.001591,-0.0013247,-0.0010584,-0.00079132,-0.00052219,-0.00025102,2.1408e-05,0.00029281,0.00056306,0.00083526,0.0011089,0.0013835,0.001658,0.0019327,0.0022076,0.0024824,0.0027587,0.0030373,0.0033116,0.0035855,0.0038579,0.0041311,0.0044047,0.0046824,0.0049621,0.0052415,0.0055207,0.0057989,0.0060765,0.0063552,0.0066302,0.0068963,0.0071608,0.007423,0.0076836,0.0079428,0.0082002,0.0084565,0.0087107,0.0089649,0.0092187,0.0094694,0.0097184,0.0099671,0.010217,0.010461,0.010698,0.010933,0.011166,0.011399,0.011632,0.011867,0.0121,0.012332,0.012561,0.012789,0.013014,0.01324,0.013468,0.013694,0.013917,0.014136,0.014353,0.014571,0.014789,0.015006,0.015218,0.015428,0.015635,0.015838,0.016034,0.016227,0.016421,0.016614,0.016806,0.016997,0.017188,0.017377,0.017563,0.01775,0.017935,0.018116,0.018297,0.018476,0.018654,0.018833,0.019013,0.019194,0.019374,0.019553,0.019733,0.01991,0.020076,0.020239,0.020402,0.020565,0.020725,0.020884,0.021044,0.021197,0.02135,0.021503,0.021656,0.021808,0.021959,0.022108,0.022247,0.022378,0.022508,0.02264,0.022772,0.022904,0.023033,0.023159,0.023283,0.023405,0.023527,0.023648,0.023766,0.023882,0.023997,0.024108,0.024217,0.024322,0.024422,0.024519,0.024616,0.024714,0.024809,0.024901,0.024991,0.025081,0.025175,0.025271,0.025366,0.02546,0.025553,0.025644,0.025735,0.025825,0.025916,0.026007,0.02609,0.02617,0.026247,0.026322,0.026396,0.026466,0.026536,0.026607,0.026678,0.026749,0.026818,0.026886,0.026953,0.02702,0.027087,0.027152,0.027219,0.027287,0.027354,0.02742,0.027484,0.027546,0.027608,0.02767,0.027731,0.027787,0.027841,0.027894,0.027947,0.027999,0.028051,0.028101,0.028151,0.028201,0.028249,0.028295,0.028337,0.028377,0.028418,0.02846,0.028503,0.028547,0.028588,0.028628,0.028667,0.028708,0.02875,0.028793,0.028837,0.028878,0.02892,0.028962,0.029005,0.029046,0.029088,0.029127,0.029161,0.029195,0.029227,0.029258,0.029287,0.029314,0.02934,0.029365,0.029389,0.029415,0.02944,0.029461,0.02948,0.029496,0.02951,0.029522,0.029534,0.029544,0.029554,0.029565,0.029574,0.02958,0.029586,0.02959,0.029593,0.029596,0.0296,0.029605,0.02961,0.029615,0.02962,0.029625,0.029629,0.029631,0.029632,0.029634,0.029633,0.029631,0.029626,0.029617,0.029605,0.02959,0.029573,0.029552,0.029531,0.029508,0.029485,0.02946,0.029435,0.029408,0.02938,0.029352,0.029321,0.02929,0.02926,0.029231,0.0292,0.02917,0.029139,0.029107,0.029075,0.029041,0.029009,0.028978,0.028947,0.028915,0.028881,0.028847,0.028812,0.028775,0.028737,0.028698,0.028659,0.028617,0.028574,0.02853,0.028482,0.028432,0.028381,0.028329,0.028274,0.02822,0.028165,0.02811,0.028054,0.028001,0.027947,0.027893,0.027839,0.027782,0.027726,0.027671,0.027615,0.027558,0.027502,0.027444,0.027386,0.027328,0.027268,0.027205,0.027144,0.027083,0.02702,0.026954,0.026886,0.026815,0.026742,0.026666,0.026585,0.026502,0.026419,0.026334,0.026248,0.026158,0.026066,0.025972,0.025876,0.025778,0.025677,0.025576,0.025475,0.025374,0.025274,0.025171,0.025065,0.024956,0.024846,0.024736,0.024625,0.024512,0.0244,0.024286,0.024173,0.024058,0.023942,0.023825,0.023707,0.023588,0.023468,0.023347,0.023224,0.023099,0.022972,0.022844,0.022712,0.022581,0.022449,0.022315,0.02218,0.022044,0.021906,0.021767,0.021626,0.021485,0.021338,0.021191,0.021042,0.020893,0.020742,0.020588,0.020434,0.02028,0.020124,0.019968,0.019807,0.019644,0.01948,0.019314,0.019147,0.018978,0.018807,0.018634,0.018459,0.018282,0.0181,0.017915,0.017727,0.017537,0.017345,0.017148,0.016947,0.016743,0.016537,0.016329,0.016118,0.015902,0.015685,0.015468,0.015248,0.015028,0.014807,0.014584,0.014359,0.014133,0.013907,0.013675,0.013444,0.013212,0.01298,0.012747,0.012514,0.01228,0.012047,0.011812,0.011576,0.011332,0.011085,0.010838,0.01059,0.01034,0.010089,0.0098379,0.0095838,0.009327,0.0090686,0.0088051,0.0085413,0.008277,0.0080112,0.0077418,0.0074711,0.0071992,0.0069267,0.0066521,0.0063729,0.0060903,0.0058045,0.0055188,0.0052333,0.0049468,0.0046598,0.0043737,0.0040836,0.0037914,0.0034959,0.0031876,0.002873,0.0025578,0.0022418,0.0019248,0.0016068,0.0012878,0.00096758,0.00064622,0.00032366,7.4351e-18\n    };\n    \n    double m_f_0_der[1022] = {7.6602,5.8328,5.1201,4.6742,4.3465,4.1059,3.9298,3.7902,3.675,3.569,3.4764,3.3991,3.3368,3.2854,3.2364,3.1894,3.1419,3.0962,3.0541,3.0108,2.9692,2.9293,2.8911,2.8561,2.8225,2.7915,2.7626,2.7352,2.7092,2.6852,2.6637,2.6449,2.6282,2.6117,2.5951,2.5816,2.5691,2.5551,2.5412,2.5283,2.5155,2.5008,2.4873,2.4742,2.4584,2.4431,2.4295,2.4162,2.4055,2.3973,2.3886,2.3809,2.3722,2.3621,2.354,2.3434,2.3324,2.324,2.3133,2.3002,2.2896,2.2805,2.2712,2.2637,2.2563,2.2477,2.2394,2.2325,2.2259,2.2182,2.21,2.2027,2.1953,2.1887,2.1832,2.177,2.171,2.1653,2.1608,2.1569,2.1531,2.1481,2.1444,2.1423,2.1379,2.1343,2.1315,2.1276,2.1239,2.1193,2.1153,2.1116,2.1064,2.1019,2.0973,2.0912,2.0858,2.0812,2.0758,2.0703,2.0652,2.0607,2.0565,2.0514,2.0465,2.0421,2.0376,2.0333,2.0286,2.0226,2.0167,2.0123,2.0076,2.001,1.9947,1.9898,1.9836,1.9773,1.9705,1.964,1.9582,1.9521,1.9467,1.941,1.9352,1.93,1.9251,1.9198,1.9142,1.9089,1.9033,1.8972,1.8917,1.8866,1.881,1.8762,1.8706,1.8643,1.8595,1.8541,1.8487,1.8433,1.8373,1.831,1.8248,1.8194,1.8132,1.8064,1.8007,1.7951,1.7889,1.7825,1.7759,1.7703,1.7661,1.7616,1.7569,1.7516,1.7461,1.7408,1.7361,1.7316,1.7259,1.7201,1.7144,1.7093,1.705,1.7008,1.6963,1.691,1.6868,1.6827,1.6777,1.673,1.6684,1.6641,1.6591,1.6537,1.6499,1.6459,1.6413,1.6378,1.6345,1.6301,1.626,1.6224,1.6182,1.6144,1.61,1.6058,1.6024,1.598,1.5938,1.5889,1.584,1.5801,1.5756,1.5715,1.5677,1.5632,1.5589,1.5548,1.5498,1.5448,1.5402,1.5353,1.5301,1.5257,1.5211,1.5165,1.5115,1.5061,1.5013,1.4965,1.4918,1.4867,1.4821,1.4776,1.4732,1.4696,1.4659,1.4612,1.4565,1.4526,1.4485,1.4442,1.4402,1.4361,1.4316,1.4271,1.4232,1.4188,1.414,1.4096,1.405,1.4001,1.3959,1.3911,1.3859,1.3813,1.3765,1.3717,1.367,1.3631,1.3594,1.3544,1.3498,1.3461,1.3424,1.3387,1.3341,1.3297,1.3264,1.3229,1.3187,1.3154,1.3124,1.3086,1.3046,1.3011,1.298,1.2947,1.2914,1.2875,1.2835,1.2802,1.2773,1.2739,1.2696,1.2651,1.2613,1.2574,1.2536,1.2503,1.2473,1.2443,1.2404,1.2364,1.2326,1.2289,1.2258,1.2227,1.2195,1.2156,1.2121,1.2082,1.2043,1.2017,1.199,1.1964,1.1941,1.1912,1.1881,1.1848,1.1813,1.1779,1.1753,1.1727,1.1695,1.1662,1.1626,1.1589,1.1551,1.1513,1.148,1.1447,1.1418,1.1384,1.1344,1.1303,1.1259,1.1221,1.1189,1.1158,1.1132,1.1097,1.1055,1.102,1.0983,1.0946,1.0911,1.0878,1.0847,1.0813,1.0782,1.0756,1.0727,1.0703,1.0674,1.064,1.061,1.0581,1.0551,1.0524,1.0494,1.0459,1.0429,1.0395,1.0357,1.032,1.0285,1.0259,1.0232,1.0201,1.0176,1.0148,1.0114,1.0084,1.0058,1.0033,1.0007,0.99799,0.99538,0.99323,0.99098,0.98873,0.98633,0.98377,0.98198,0.98039,0.97783,0.97517,0.97292,0.97042,0.96822,0.96627,0.96382,0.96126,0.95901,0.95681,0.95487,0.95246,0.94863,0.9453,0.9431,0.9405,0.93758,0.93543,0.93374,0.93154,0.92934,0.92689,0.92413,0.92167,0.91917,0.91691,0.91482,0.91246,0.90986,0.90735,0.90541,0.90341,0.90121,0.89911,0.89692,0.89466,0.89247,0.89001,0.88755,0.88561,0.88372,0.88116,0.87819,0.87559,0.87328,0.87093,0.86868,0.86612,0.86367,0.86208,0.8605,0.85835,0.8563,0.85426,0.8519,0.84975,0.84791,0.84597,0.84408,0.84229,0.8406,0.83891,0.83717,0.83589,0.83451,0.83252,0.83068,0.82935,0.82791,0.82679,0.82531,0.82316,0.82121,0.81978,0.8184,0.8161,0.81375,0.81216,0.81011,0.80745,0.80536,0.80382,0.80244,0.80075,0.79871,0.79661,0.79477,0.79334,0.79211,0.79068,0.78894,0.7872,0.78587,0.78444,0.78193,0.77994,0.77922,0.77835,0.77687,0.77538,0.77426,0.77293,0.77124,0.76991,0.76899,0.76751,0.76582,0.76444,0.76331,0.76208,0.7604,0.75891,0.75753,0.75574,0.75426,0.75313,0.75185,0.75022,0.74858,0.74684,0.74469,0.74229,0.73999,0.7385,0.73728,0.73559,0.73344,0.73134,0.72981,0.72848,0.72694,0.72551,0.72403,0.72183,0.71989,0.71876,0.71774,0.71605,0.71405,0.71257,0.7116,0.71047,0.70904,0.70792,0.70694,0.70587,0.70454,0.70341,0.70214,0.7004,0.69856,0.69707,0.69579,0.69405,0.6918,0.68971,0.68802,0.68669,0.68521,0.68347,0.68193,0.68045,0.67927,0.6781,0.67723,0.67651,0.67528,0.67365,0.67232,0.6717,0.67129,0.67032,0.66909,0.66822,0.66746,0.66669,0.66592,0.66515,0.66408,0.66249,0.66076,0.65932,0.65815,0.65687,0.65544,0.6539,0.65278,0.65216,0.65145,0.65022,0.64874,0.64761,0.64674,0.64602,0.64526,0.64449,0.64336,0.64209,0.64117,0.6405,0.64009,0.63989,0.63943,0.63835,0.63712,0.63636,0.63595,0.63528,0.63421,0.63313,0.63237,0.6316,0.63037,0.62889,0.62776,0.62674,0.62551,0.62398,0.62265,0.62168,0.62065,0.61932,0.61753,0.6159,0.61508,0.61467,0.6139,0.61262,0.6116,0.61104,0.61048,0.60991,0.60925,0.60863,0.60807,0.6071,0.60556,0.60434,0.60357,0.60296,0.6028,0.60244,0.60152,0.60065,0.59994,0.59932,0.59871,0.59764,0.5959,0.59406,0.59288,0.59186,0.59037,0.58915,0.58828,0.5873,0.58623,0.58495,0.58393,0.58311,0.58193,0.58071,0.57938,0.57799,0.57687,0.5762,0.57569,0.57487,0.57395,0.57308,0.57257,0.57237,0.57181,0.57109,0.57068,0.56991,0.56904,0.56828,0.56736,0.56664,0.56613,0.56572,0.56495,0.56337,0.56147,0.55999,0.55881,0.55784,0.55626,0.55426,0.55283,0.55186,0.55119,0.55048,0.5495,0.54833,0.54715,0.54618,0.54541,0.54424,0.54311,0.54234,0.54153,0.54081,0.53979,0.53861,0.53794,0.53774,0.53769,0.53754,0.53636,0.53498,0.53426,0.5337,0.53314,0.53252,0.53191,0.53104,0.53027,0.52956,0.52884,0.52797,0.52695,0.52618,0.5248,0.52296,0.52158,0.52009,0.51851,0.51754,0.51682,0.51595,0.51513,0.51436,0.51355,0.51252,0.51124,0.51043,0.50981,0.50904,0.50823,0.50725,0.50598,0.5048,0.50413,0.50352,0.50306,0.5027,0.50234,0.50214,0.50122,0.5002,0.49989,0.49963,0.49917,0.49841,0.49728,0.49621,0.49518,0.49401,0.49288,0.49186,0.49104,0.49027,0.48971,0.48899,0.48797,0.48725,0.48664,0.48613,0.48582,0.48541,0.48506,0.48475,0.48413,0.48347,0.48316,0.48275,0.48234,0.48188,0.48112,0.48061,0.48015,0.47958,0.47907,0.47815,0.47702,0.47616,0.47534,0.47426,0.47334,0.47288,0.47222,0.47145,0.47094,0.47038,0.46966,0.46915,0.46889,0.46823,0.46756,0.46726,0.46685,0.46633,0.46593,0.46546,0.46526,0.46536,0.46536,0.46511,0.46465,0.46408,0.46357,0.46337,0.46316,0.4626,0.46163,0.46066,0.45989,0.45928,0.45861,0.45789,0.45708,0.45621,0.45534,0.45437,0.4536,0.45314,0.45263,0.45196,0.45135,0.45063,0.45012,0.44981,0.44925,0.44874,0.44859,0.44802,0.44736,0.44726,0.44736,0.44731,0.4469,0.44664,0.44654,0.44639,0.44623,0.44613,0.44608,0.44598,0.44557,0.4447,0.44362,0.44281,0.44209,0.44102,0.44004,0.43902,0.43795,0.43713,0.43641,0.43585,0.43539,0.43493,0.43431,0.43396,0.43385,0.43355,0.43304,0.43237,0.4316,0.43079,0.43032,0.43038,0.43053,0.43053,0.43043,0.43022,0.42976,0.42935,0.4291,0.42879,0.42828,0.42756,0.42685,0.42603,0.42516,0.42444,0.42388,0.42327,0.4226,0.42204,0.42112,0.4203,0.42004,0.41958,0.41882,0.41795,0.41733,0.41672,0.41621,0.41621,0.41636,0.41651,0.41657,0.41641,0.41559,0.41462,0.41426,0.41391,0.41365,0.4137,0.41365,0.41334,0.41288,0.41242,0.41176,0.41114,0.41068,0.41022,0.40946,0.40787,0.40649,0.40582,0.40506,0.40429,0.40357,0.40286,0.40199,0.40143,0.40132,0.40112,0.40081,0.39989,0.39871,0.39779,0.39703,0.39667,0.39641,0.39631,0.39636,0.39621,0.39585,0.39539,0.39503,0.39442,0.39386,0.39355,0.39309,0.39288,0.39268,0.39242,0.39237,0.39227,0.39212,0.39186,0.39166,0.39084,0.38961,0.38925,0.38951,0.3894,0.38905,0.38889,0.38869,0.38802,0.38741,0.3871,0.38685,0.38685,0.3868,0.38669,0.38654,0.38588,0.38506,0.38419,0.38337,0.38281,0.3824,0.38219,0.38209,0.38153,0.38056,0.37964,0.37897,0.37856,0.37805,0.37744,0.37657,0.37534,0.37472,0.37483,0.37467,0.37432,0.37391,0.37299,0.37135,0.36987,0.36889,0.36843,0.36854,0.36889,0.36905,0.36859,0.36767,0.3669,0.36675,0.3668,0.36577,0.3646,0.36444,0.36485,0.36449,0.36332,0.36281,0.36276,0.36265,0.36214,0.36107,0.35994,0.35887,0.35733,0.35565,0.35473,0.35442,0.35406,0.3537,0.3536,0.35304,0.35099,0.34925,0.34925,0.34915,0.34864,0.34808,0.34634,0.3448,0.34342,0.3423,0.34224,0.34107,0.3402,0.34086,0.34143,0.34168,0.3404,0.33851,0.33744,0.33539,0.33324,0.33227,0.33017,0.32849,0.32767,0.32572,0.3246,0.32439,0.32414,0.32378,0.32347,0.32317,0.32281\n    };\n    \n    double m_h_1_der[1022] = {-1.0911,-0.62502,-0.52003,-0.46452,-0.42506,-0.39764,-0.38072,-0.3687,-0.359,-0.35047,-0.34357,-0.33787,-0.33331,-0.32991,-0.32709,-0.32461,-0.3217,-0.31887,-0.31601,-0.31217,-0.30829,-0.30473,-0.30149,-0.29859,-0.29576,-0.29321,-0.29118,-0.28958,-0.28806,-0.28661,-0.28531,-0.2842,-0.28317,-0.28203,-0.28087,-0.28017,-0.27968,-0.27896,-0.27807,-0.27731,-0.27654,-0.27525,-0.27402,-0.27293,-0.27141,-0.27017,-0.2692,-0.26823,-0.26769,-0.26725,-0.2665,-0.26598,-0.26528,-0.26417,-0.26333,-0.26204,-0.26065,-0.25962,-0.2583,-0.25676,-0.25569,-0.25482,-0.25384,-0.2531,-0.25225,-0.25117,-0.25013,-0.24932,-0.24846,-0.24741,-0.24641,-0.24557,-0.24454,-0.24345,-0.24251,-0.24157,-0.24051,-0.23938,-0.23845,-0.23756,-0.23661,-0.23548,-0.23448,-0.23363,-0.23236,-0.23115,-0.22998,-0.2286,-0.22721,-0.22569,-0.22426,-0.2229,-0.22128,-0.21979,-0.21847,-0.21702,-0.21556,-0.21417,-0.21282,-0.21161,-0.21041,-0.20915,-0.20802,-0.20687,-0.20558,-0.20412,-0.20266,-0.20127,-0.19978,-0.19822,-0.19673,-0.1955,-0.19409,-0.19228,-0.19057,-0.18904,-0.18734,-0.18556,-0.18371,-0.18184,-0.18007,-0.17823,-0.17636,-0.17448,-0.17252,-0.17071,-0.16909,-0.16738,-0.16576,-0.16418,-0.1626,-0.16092,-0.15934,-0.15795,-0.1564,-0.155,-0.15361,-0.15213,-0.15084,-0.14946,-0.14793,-0.14646,-0.14485,-0.14316,-0.14158,-0.14012,-0.13858,-0.1369,-0.13532,-0.13385,-0.13237,-0.13085,-0.12918,-0.12766,-0.12622,-0.12472,-0.12344,-0.12207,-0.12089,-0.11973,-0.11845,-0.11734,-0.11623,-0.11523,-0.11424,-0.11311,-0.11198,-0.11094,-0.10985,-0.10878,-0.10761,-0.10643,-0.10525,-0.10413,-0.10293,-0.10173,-0.10054,-0.099226,-0.097927,-0.096551,-0.095139,-0.093758,-0.09229,-0.090822,-0.089431,-0.087988,-0.08652,-0.084986,-0.083461,-0.082039,-0.080628,-0.079201,-0.077809,-0.076382,-0.075134,-0.074009,-0.072812,-0.071671,-0.070572,-0.069508,-0.068536,-0.067748,-0.066955,-0.065983,-0.06514,-0.064301,-0.063273,-0.062377,-0.061513,-0.060454,-0.05939,-0.058388,-0.057447,-0.056603,-0.055667,-0.054736,-0.053927,-0.053078,-0.052219,-0.051344,-0.050378,-0.049022,-0.047835,-0.046976,-0.046025,-0.045247,-0.044403,-0.043493,-0.042659,-0.041784,-0.041012,-0.040301,-0.039447,-0.038618,-0.03781,-0.037125,-0.036501,-0.035687,-0.034895,-0.034153,-0.033421,-0.032736,-0.032035,-0.031447,-0.030741,-0.029754,-0.028915,-0.028153,-0.027381,-0.026542,-0.025585,-0.024757,-0.023994,-0.023171,-0.022409,-0.021764,-0.020982,-0.020138,-0.019176,-0.018082,-0.01712,-0.016189,-0.015197,-0.014261,-0.013529,-0.012967,-0.012542,-0.012087,-0.011626,-0.011319,-0.0109,-0.010343,-0.0099589,-0.0095957,-0.0091098,-0.0087108,-0.0081635,-0.007473,-0.0069206,-0.0065523,-0.0062147,-0.0057646,-0.0053452,-0.0048592,-0.0043375,-0.0036879,-0.0029207,-0.0023478,-0.001867,-0.0013146,-0.00065472,2.5575e-05,0.00069052,0.0014117,0.0021125,0.0028132,0.0034884,0.0040818,0.004537,0.0049309,0.0054935,0.006092,0.0066137,0.0070638,0.0074168,0.0078055,0.008138,0.0083937,0.0086904,0.0089922,0.0094679,0.0098924,0.010261,0.010721,0.011089,0.011504,0.012056,0.012619,0.013181,0.013703,0.014107,0.014496,0.014936,0.015417,0.015836,0.016286,0.016951,0.017596,0.018168,0.018803,0.019432,0.019923,0.020291,0.020578,0.02091,0.021258,0.021463,0.021734,0.021979,0.022209,0.02245,0.022588,0.0227,0.02292,0.02314,0.02337,0.023677,0.023938,0.024271,0.024716,0.025156,0.025575,0.026015,0.026419,0.026879,0.027447,0.027943,0.028445,0.028951,0.029437,0.030005,0.030496,0.030884,0.031268,0.03157,0.031769,0.031979,0.03225,0.032496,0.0327,0.032879,0.033058,0.033411,0.033805,0.034005,0.034122,0.034296,0.03446,0.034639,0.0349,0.03512,0.035283,0.035478,0.035677,0.035918,0.036189,0.036449,0.036777,0.037186,0.037641,0.03804,0.038286,0.038495,0.038787,0.03912,0.039426,0.039708,0.039953,0.040235,0.040495,0.040685,0.04093,0.041166,0.041304,0.041396,0.041478,0.041575,0.041713,0.041856,0.041943,0.04204,0.042255,0.042516,0.0427,0.042843,0.043048,0.043283,0.043554,0.043866,0.044107,0.044316,0.044572,0.044859,0.04514,0.045314,0.045523,0.045943,0.04647,0.046935,0.047242,0.047513,0.047743,0.047841,0.047876,0.047933,0.047984,0.047876,0.047682,0.047667,0.047779,0.047912,0.04805,0.048107,0.048183,0.048296,0.048383,0.048439,0.048398,0.048342,0.048357,0.048424,0.048521,0.048623,0.048812,0.049135,0.049462,0.049764,0.05002,0.050306,0.050608,0.050812,0.050991,0.051242,0.051513,0.051702,0.051897,0.052045,0.052101,0.052168,0.052306,0.052531,0.052766,0.052981,0.053135,0.053211,0.053314,0.053457,0.053585,0.053595,0.053534,0.053498,0.053482,0.053431,0.053355,0.053329,0.053395,0.053426,0.053426,0.053442,0.053467,0.053554,0.053646,0.053707,0.053789,0.053912,0.054014,0.054163,0.054413,0.05471,0.054838,0.054894,0.055099,0.05536,0.055595,0.055789,0.055948,0.056076,0.056117,0.056117,0.056178,0.05626,0.05625,0.056188,0.056025,0.055881,0.055846,0.055789,0.055769,0.055876,0.056025,0.056117,0.056224,0.056296,0.056403,0.056546,0.056664,0.056802,0.05692,0.056996,0.057129,0.057334,0.057605,0.057958,0.058306,0.058562,0.058802,0.059083,0.059262,0.05938,0.059441,0.059441,0.05939,0.059206,0.058971,0.058833,0.058782,0.058725,0.058705,0.058741,0.058787,0.058853,0.058904,0.05895,0.059037,0.059175,0.059324,0.05938,0.059411,0.059503,0.059687,0.059989,0.060342,0.060638,0.060909,0.061104,0.061221,0.06137,0.061528,0.061697,0.061866,0.061953,0.061968,0.061963,0.061999,0.06203,0.061973,0.061917,0.061866,0.061815,0.061764,0.061656,0.061518,0.061426,0.06139,0.061406,0.061493,0.061651,0.061835,0.061999,0.062173,0.062347,0.062505,0.062684,0.062833,0.06295,0.063037,0.06315,0.063344,0.063533,0.063779,0.064117,0.064444,0.064689,0.064848,0.064986,0.065104,0.06517,0.065232,0.065252,0.065196,0.065068,0.064904,0.064766,0.064654,0.064562,0.064495,0.064408,0.064347,0.064316,0.064285,0.06426,0.064239,0.064316,0.064423,0.064592,0.064787,0.064899,0.065022,0.065145,0.065278,0.065472,0.065692,0.065922,0.066147,0.066408,0.066756,0.067099,0.067334,0.067492,0.067605,0.067697,0.067789,0.067845,0.067937,0.068024,0.068014,0.067891,0.067748,0.067631,0.067462,0.067324,0.067257,0.067226,0.067206,0.067175,0.067145,0.06715,0.06718,0.067211,0.067272,0.067324,0.067308,0.067303,0.067329,0.06738,0.067544,0.067774,0.06803,0.06828,0.068521,0.068838,0.069134,0.069334,0.069523,0.069707,0.069881,0.070055,0.070183,0.070265,0.070306,0.070336,0.070347,0.070311,0.070249,0.070208,0.070173,0.070096,0.070014,0.069896,0.069748,0.069661,0.069605,0.069544,0.069533,0.069559,0.06959,0.069697,0.069845,0.069922,0.069927,0.069881,0.069871,0.069973,0.070132,0.070326,0.070592,0.070894,0.071191,0.071421,0.071584,0.071733,0.071815,0.071835,0.07183,0.071799,0.071707,0.071605,0.071554,0.071528,0.071518,0.071574,0.071687,0.071769,0.071779,0.071728,0.071687,0.071702,0.071794,0.071948,0.072101,0.072203,0.07227,0.072336,0.072439,0.072618,0.072832,0.073027,0.073252,0.073482,0.073625,0.073722,0.073809,0.073855,0.073871,0.073902,0.073994,0.07406,0.074029,0.073902,0.073743,0.073641,0.073636,0.073753,0.073963,0.074193,0.074336,0.074387,0.074459,0.074613,0.074771,0.074904,0.075073,0.075257,0.075421,0.075549,0.075697,0.075901,0.076147,0.076439,0.07673,0.077022,0.077303,0.077487,0.077574,0.077584,0.077533,0.077472,0.077395,0.077323,0.077283,0.077175,0.077006,0.076919,0.076878,0.076838,0.076843,0.076863,0.076838,0.076791,0.076817,0.076884,0.07694,0.077032,0.077175,0.077323,0.077462,0.077615,0.077809,0.07805,0.078295,0.07851,0.078699,0.078914,0.079196,0.079502,0.079763,0.079978,0.080157,0.080275,0.080346,0.080392,0.080449,0.080485,0.080413,0.080234,0.079958,0.079676,0.079451,0.079298,0.079216,0.079196,0.079221,0.079247,0.079272,0.079339,0.079426,0.079467,0.079446,0.079375,0.079344,0.079415,0.079543,0.079666,0.07984,0.080075,0.080305,0.080505,0.080679,0.080873,0.081011,0.081073,0.081078,0.081011,0.08093,0.080848,0.080745,0.080577,0.080403,0.080321,0.080275,0.080193,0.080132,0.080152,0.080162,0.080152,0.080157,0.080157,0.080152,0.080254,0.08049,0.080689,0.080837,0.081001,0.08117,0.081318,0.081461,0.081707,0.082009,0.082295,0.082541,0.082725,0.082889,0.083022,0.083165,0.083262,0.083247,0.083242,0.083252,0.083149,0.083078,0.083037,0.082858,0.082612,0.082392,0.082167,0.081968,0.08184,0.081738,0.081671,0.081722,0.081768,0.081717,0.081656,0.081528,0.081415,0.081451,0.081528,0.081538,0.081559,0.081733,0.081953,0.082055,0.082132,0.082231,0.082249,0.08215,0.082051,0.082032,0.082092,0.082265,0.082546,0.08277,0.082854,0.082884,0.08288,0.082851,0.082794,0.082693,0.082662,0.082779,0.083014,0.083242,0.083384,0.083515,0.08364,0.083821,0.084012,0.08413,0.08425,0.084389,0.084605,0.084801,0.084869,0.084981,0.085117,0.085138,0.085102,0.085059,0.085028,0.085025,0.085187,0.085416,0.085394,0.085211,0.085054,0.084915,0.084772,0.084633,0.084546,0.084574,0.084734,0.084922,0.084955,0.084878,0.084804,0.084641,0.084455,0.0844,0.084358,0.08435,0.084496,0.08479,0.085004,0.08509,0.085224,0.085399,0.085555,0.08569,0.085672,0.08564,0.08585,0.086324,0.08674,0.086916,0.087025,0.087063,0.087087,0.087098,0.087034,0.086921,0.086881,0.087043,0.08708,0.086887,0.08673,0.08661,0.08656,0.086554,0.086545,0.086494,0.086451,0.0867,0.087101,0.087273,0.087248,0.086976,0.086758,0.086874,0.087031,0.087087,0.087006,0.087194,0.087825,0.088309,0.088564,0.088821,0.089021,0.08912,0.088884,0.088479,0.08824,0.087883,0.087894,0.088209,0.088322,0.088347,0.088265,0.088168,0.088052,0.087923,0.087784,0.08764\n    };\n    double m_h_2_der[1022] = {-5.2718,-3.2029,-2.6578,-2.3308,-2.1038,-1.9393,-1.8178,-1.7212,-1.6393,-1.5653,-1.4975,-1.4348,-1.3776,-1.3251,-1.2749,-1.2281,-1.1849,-1.1442,-1.1072,-1.0721,-1.0361,-1.0018,-0.97018,-0.93924,-0.90999,-0.88182,-0.8544,-0.8285,-0.80415,-0.78047,-0.75768,-0.73605,-0.71573,-0.69694,-0.67896,-0.66112,-0.64437,-0.62814,-0.61167,-0.59549,-0.5795,-0.56404,-0.54925,-0.53438,-0.51924,-0.50448,-0.49013,-0.47617,-0.46235,-0.44856,-0.4353,-0.4222,-0.40882,-0.39577,-0.38263,-0.36917,-0.35511,-0.34041,-0.32616,-0.31252,-0.29916,-0.28637,-0.27333,-0.26029,-0.24824,-0.23654,-0.22485,-0.21306,-0.20092,-0.18923,-0.17858,-0.16827,-0.15773,-0.14632,-0.13538,-0.12532,-0.11505,-0.10531,-0.095865,-0.085901,-0.075789,-0.066669,-0.057554,-0.047876,-0.039232,-0.030204,-0.019954,-0.010123,-0.0009207,0.0078362,0.016695,0.026086,0.035345,0.04468,0.053431,0.061518,0.070572,0.079666,0.087584,0.095333,0.10365,0.11234,0.11986,0.12635,0.13354,0.14105,0.14784,0.15377,0.16024,0.16644,0.17172,0.1775,0.18379,0.18909,0.19437,0.20029,0.20489,0.20965,0.21437,0.21867,0.22334,0.2276,0.23225,0.23711,0.24151,0.24547,0.24898,0.25283,0.25636,0.25967,0.26326,0.26647,0.26979,0.27257,0.27474,0.27778,0.28025,0.28222,0.28482,0.28729,0.29034,0.2933,0.29559,0.29781,0.29971,0.30233,0.30458,0.30638,0.30912,0.31129,0.3131,0.31468,0.31594,0.31748,0.31967,0.32178,0.32347,0.32508,0.3263,0.32768,0.33013,0.33223,0.33371,0.33522,0.33615,0.33749,0.33964,0.34164,0.34244,0.34327,0.34528,0.34763,0.34918,0.35032,0.35208,0.35369,0.35462,0.35549,0.3576,0.35955,0.35987,0.36121,0.36367,0.36463,0.36579,0.36799,0.3688,0.3705,0.37265,0.37403,0.37627,0.37816,0.37971,0.38106,0.38247,0.38361,0.38451,0.38562,0.38679,0.3879,0.3885,0.38905,0.38911,0.38897,0.38857,0.38812,0.38706,0.38604,0.38568,0.38498,0.38368,0.38245,0.38169,0.38075,0.37985,0.37899,0.37838,0.37716,0.37603,0.37564,0.37444,0.37311,0.37208,0.3707,0.36992,0.36912,0.36799,0.36757,0.3668,0.3652,0.3643,0.36365,0.36262,0.36193,0.36136,0.36024,0.35885,0.35765,0.3567,0.35559,0.35451,0.35347,0.35179,0.35063,0.35033,0.34946,0.34818,0.34733,0.34667,0.34558,0.34379,0.34227,0.34151,0.34075,0.33942,0.33794,0.33698,0.33598,0.33423,0.3324,0.33122,0.33045,0.32925,0.32716,0.32566,0.32506,0.32395,0.3227,0.32137,0.3199,0.31884,0.31791,0.31665,0.31536,0.31431,0.31316,0.31221,0.31098,0.30941,0.30815,0.30715,0.30621,0.3046,0.30317,0.30212,0.30063,0.29943,0.29832,0.29672,0.29555,0.29493,0.29396,0.29271,0.29166,0.29059,0.28945,0.28827,0.28691,0.28564,0.2844,0.28328,0.28215,0.28115,0.28015,0.2787,0.2774,0.27637,0.27553,0.27473,0.27329,0.2719,0.27102,0.26989,0.2689,0.26784,0.26613,0.26434,0.26259,0.26104,0.25994,0.25891,0.25762,0.25585,0.2543,0.25317,0.25182,0.25064,0.24922,0.24744,0.24594,0.24468,0.24324,0.24153,0.24016,0.23893,0.23741,0.23624,0.23523,0.23325,0.23125,0.23024,0.22919,0.22798,0.22682,0.22526,0.22357,0.22226,0.22109,0.21998,0.21856,0.21707,0.21615,0.21475,0.21286,0.21146,0.21021,0.20878,0.20759,0.20653,0.20536,0.2037,0.20215,0.20119,0.20006,0.19849,0.19717,0.19676,0.19583,0.19418,0.19324,0.19259,0.19135,0.19006,0.18902,0.18804,0.18692,0.18545,0.18429,0.18376,0.18276,0.18118,0.18009,0.17917,0.17801,0.17677,0.17574,0.17481,0.17368,0.17222,0.17037,0.16893,0.16828,0.16737,0.16603,0.16504,0.16418,0.16287,0.16117,0.15983,0.15889,0.15806,0.15737,0.15626,0.15488,0.15397,0.15287,0.15112,0.14984,0.14929,0.14853,0.14742,0.14653,0.1459,0.14518,0.14428,0.14324,0.14183,0.14009,0.13912,0.1385,0.13724,0.13595,0.13489,0.13386,0.13275,0.13166,0.13057,0.1295,0.12858,0.12778,0.12662,0.1249,0.12368,0.12305,0.12215,0.12089,0.11993,0.11944,0.11849,0.11697,0.11554,0.11417,0.11331,0.11273,0.11156,0.11031,0.10943,0.10881,0.10809,0.10693,0.10593,0.10519,0.10454,0.10347,0.10197,0.10074,0.099758,0.098878,0.098126,0.097405,0.096633,0.095794,0.094837,0.093998,0.093211,0.092316,0.091339,0.090397,0.089579,0.089021,0.087978,0.086715,0.085866,0.085359,0.085016,0.08428,0.083318,0.082449,0.081681,0.081022,0.080454,0.079564,0.078275,0.077237,0.076669,0.07626,0.075416,0.074024,0.072919,0.072551,0.072162,0.071114,0.069896,0.06916,0.068781,0.068045,0.067073,0.065861,0.064761,0.063963,0.063416,0.063349,0.062879,0.061815,0.060792,0.060168,0.059907,0.059518,0.058654,0.057636,0.056787,0.056199,0.055564,0.054812,0.054137,0.053191,0.05228,0.051841,0.05138,0.050511,0.04937,0.047938,0.046787,0.046424,0.046183,0.045472,0.044342,0.043416,0.042889,0.042373,0.04179,0.041186,0.040393,0.039171,0.038214,0.03782,0.037488,0.036756,0.0356,0.034485,0.034005,0.03401,0.033785,0.033155,0.032138,0.031273,0.030869,0.030572,0.030225,0.0298,0.02893,0.027713,0.026792,0.026363,0.026168,0.025815,0.024874,0.02379,0.023186,0.022864,0.022475,0.021918,0.020977,0.020174,0.019877,0.019642,0.019186,0.018506,0.017657,0.016711,0.01602,0.015667,0.015509,0.015263,0.014747,0.013949,0.013094,0.012491,0.012123,0.011928,0.011688,0.011202,0.010542,0.0098259,0.0092428,0.0087466,0.00823,0.0076265,0.0068081,0.0061278,0.0058413,0.0056009,0.005427,0.0051201,0.0044859,0.003693,0.0028081,0.0022762,0.0018056,0.0012685,0.00077236,-5.6265e-05,-0.00097185,-0.0015447,-0.001867,-0.0022711,-0.002624,-0.0027826,-0.0031969,-0.0040255,-0.0045984,-0.0048592,-0.0051866,-0.0054679,-0.0057339,-0.0061431,-0.0065983,-0.006762,-0.0069206,-0.0073349,-0.0077901,-0.0084346,-0.0090638,-0.009739,-0.010476,-0.010921,-0.0111,-0.011243,-0.011775,-0.012644,-0.013345,-0.013698,-0.014056,-0.014511,-0.014982,-0.015724,-0.016598,-0.01712,-0.017365,-0.017877,-0.018419,-0.018833,-0.019457,-0.020327,-0.021278,-0.021846,-0.022092,-0.022174,-0.022209,-0.022465,-0.022992,-0.023601,-0.023994,-0.024158,-0.024153,-0.02425,-0.02467,-0.025345,-0.026056,-0.026588,-0.026956,-0.027432,-0.027974,-0.028327,-0.028649,-0.029166,-0.029733,-0.030306,-0.030808,-0.03115,-0.031606,-0.032378,-0.033145,-0.033703,-0.034081,-0.03423,-0.034511,-0.034992,-0.035401,-0.035964,-0.036623,-0.036946,-0.037186,-0.037595,-0.038132,-0.038639,-0.039053,-0.039616,-0.04049,-0.041109,-0.041432,-0.041682,-0.041728,-0.04181,-0.042204,-0.043094,-0.04402,-0.044516,-0.044685,-0.044859,-0.04514,-0.045564,-0.046552,-0.047641,-0.047984,-0.048004,-0.048306,-0.048858,-0.049329,-0.049605,-0.050086,-0.050807,-0.051278,-0.051493,-0.051656,-0.051636,-0.051421,-0.051488,-0.05228,-0.053257,-0.05382,-0.054071,-0.054117,-0.05426,-0.054587,-0.055145,-0.05583,-0.056265,-0.056357,-0.056367,-0.056654,-0.057114,-0.0576,-0.05804,-0.058424,-0.058822,-0.059242,-0.059631,-0.059897,-0.060009,-0.060076,-0.060285,-0.060812,-0.06138,-0.061805,-0.062239,-0.062321,-0.062234,-0.062383,-0.062531,-0.062659,-0.062807,-0.062904,-0.063037,-0.063262,-0.063631,-0.064357,-0.065058,-0.065365,-0.065375,-0.065375,-0.065707,-0.066086,-0.066326,-0.066577,-0.066843,-0.067093,-0.067564,-0.068239,-0.068781,-0.069037,-0.06893,-0.068858,-0.069017,-0.069216,-0.069564,-0.070147,-0.070531,-0.070439,-0.070352,-0.070449,-0.070653,-0.070853,-0.070966,-0.07118,-0.071687,-0.072162,-0.072423,-0.072705,-0.072976,-0.073119,-0.073104,-0.07317,-0.073487,-0.073978,-0.074572,-0.075011,-0.075267,-0.075605,-0.07605,-0.076459,-0.076914,-0.077717,-0.078454,-0.078658,-0.078751,-0.078837,-0.078935,-0.079216,-0.079559,-0.079804,-0.080249,-0.080817,-0.081037,-0.081185,-0.08138,-0.081467,-0.081421,-0.081339,-0.081543,-0.082096,-0.082623,-0.083062,-0.083349,-0.083318,-0.083257,-0.08339,-0.083773,-0.08428,-0.084878,-0.085303,-0.085364,-0.085395,-0.085518,-0.085661,-0.085912,-0.086418,-0.086894,-0.087369,-0.087952,-0.088382,-0.088566,-0.088669,-0.088755,-0.08875,-0.088648,-0.088536,-0.08872,-0.089236,-0.089727,-0.090055,-0.090223,-0.090131,-0.090096,-0.090213,-0.090305,-0.090643,-0.091564,-0.092367,-0.092351,-0.092198,-0.092392,-0.092648,-0.092822,-0.093211,-0.093799,-0.094356,-0.095067,-0.095415,-0.095287,-0.095354,-0.095599,-0.095876,-0.095937,-0.095937,-0.096234,-0.096929,-0.097579,-0.09784,-0.098157,-0.098341,-0.098009,-0.097748,-0.097773,-0.097927,-0.0983,-0.099011,-0.099538,-0.099507,-0.099548,-0.099707,-0.099773,-0.099778,-0.099763,-0.099906,-0.10014,-0.10044,-0.10079,-0.10096,-0.10116,-0.10138,-0.10164,-0.10183,-0.10184,-0.10193,-0.10212,-0.10231,-0.10238,-0.10235,-0.1026,-0.10285,-0.1029,-0.10302,-0.10313,-0.10321,-0.10331,-0.10338,-0.10332,-0.10312,-0.10308,-0.10332,-0.10339,-0.10342,-0.10358,-0.10389,-0.10425,-0.1044,-0.10445,-0.10453,-0.10463,-0.10475,-0.10495,-0.10526,-0.10527,-0.10517,-0.10539,-0.10585,-0.10625,-0.10634,-0.10643,-0.10677,-0.10697,-0.10675,-0.10656,-0.10653,-0.1066,-0.10691,-0.10729,-0.10745,-0.10725,-0.10717,-0.10747,-0.10766,-0.10775,-0.10787,-0.10786,-0.10798,-0.1082,-0.10823,-0.10824,-0.10842,-0.10881,-0.10911,-0.10912,-0.10909,-0.10905,-0.10906,-0.10932,-0.10934,-0.10916,-0.10927,-0.10972,-0.11023,-0.11027,-0.11014,-0.11017,-0.11019,-0.11018,-0.11047,-0.1108,-0.11085,-0.11074,-0.11048,-0.11034,-0.11031,-0.11027,-0.11023,-0.11024,-0.11047,-0.11063,-0.1106,-0.11089,-0.11105,-0.11092,-0.11079,-0.11024,-0.1099,-0.10969,-0.10944,-0.10974,-0.11041,-0.11107,-0.1116,-0.11193,-0.11198,-0.11176,-0.11134,-0.11089,-0.11018,-0.10957,-0.10958,-0.11025,-0.111,-0.11107,-0.11063,-0.11039,-0.11039,-0.11038,-0.11037,-0.11036,-0.11034,-0.11031\n    };\n    double m_h_3_der[1022] = {-10.88,-5.763,-4.6224,-3.946,-3.4783,-3.1358,-2.8848,-2.6861,-2.5124,-2.3532,-2.205,-2.0649,-1.9353,-1.8156,-1.7017,-1.5966,-1.4997,-1.4107,-1.3288,-1.2505,-1.1736,-1.1009,-1.0332,-0.96872,-0.90761,-0.84833,-0.79048,-0.73475,-0.68087,-0.62715,-0.57364,-0.51998,-0.46958,-0.42293,-0.37554,-0.32623,-0.27967,-0.23482,-0.18975,-0.14387,-0.097768,-0.056275,-0.015713,0.027069,0.066904,0.1064,0.14668,0.18177,0.2176,0.25578,0.2882,0.32099,0.3553,0.38487,0.41466,0.44421,0.47461,0.5054,0.53427,0.56215,0.58799,0.61067,0.63537,0.66229,0.68393,0.70334,0.72304,0.74288,0.76305,0.78118,0.7966,0.81059,0.82356,0.83776,0.85133,0.86261,0.8742,0.88465,0.89485,0.90677,0.91942,0.92788,0.93726,0.94868,0.95558,0.96527,0.97799,0.98703,0.9958,1.0047,1.0146,1.0253,1.0323,1.0406,1.0501,1.057,1.0641,1.0711,1.0761,1.0806,1.0844,1.0858,1.0851,1.0851,1.0874,1.0878,1.0855,1.0817,1.081,1.0795,1.0745,1.0716,1.0681,1.0625,1.0578,1.0534,1.0456,1.0386,1.0329,1.0267,1.0201,1.0123,1.0056,0.99949,0.99114,0.98193,0.97371,0.96551,0.95749,0.94814,0.93741,0.92592,0.91503,0.90336,0.89025,0.88102,0.87214,0.86171,0.85307,0.84402,0.83401,0.82506,0.81576,0.80447,0.79344,0.78558,0.77788,0.76729,0.75784,0.74945,0.7412,0.73284,0.72265,0.71419,0.70574,0.69561,0.68797,0.68021,0.67279,0.66434,0.65408,0.64357,0.63308,0.62404,0.61442,0.60412,0.596,0.58917,0.58025,0.57255,0.56479,0.55561,0.54697,0.5378,0.52698,0.51697,0.50801,0.49917,0.49023,0.4807,0.47108,0.46234,0.45499,0.44631,0.43727,0.42909,0.42002,0.41087,0.40249,0.3932,0.38437,0.37498,0.36448,0.35496,0.34731,0.33894,0.32995,0.32161,0.31385,0.30671,0.29911,0.29211,0.28375,0.27476,0.26719,0.261,0.254,0.24711,0.24329,0.23712,0.229,0.22159,0.21398,0.20684,0.19871,0.19257,0.18782,0.18219,0.17731,0.17292,0.16888,0.16208,0.1564,0.1518,0.14586,0.14155,0.13625,0.13075,0.1265,0.12063,0.11567,0.11199,0.10677,0.10115,0.094326,0.08849,0.082899,0.075242,0.069871,0.065053,0.058372,0.051733,0.044869,0.0398,0.035795,0.030271,0.025846,0.021744,0.017217,0.012424,0.0066853,0.0020562,-0.0011764,-0.0052326,-0.0092326,-0.012404,-0.016286,-0.019729,-0.02492,-0.031094,-0.034311,-0.037334,-0.043605,-0.04938,-0.05292,-0.055564,-0.057851,-0.061891,-0.066536,-0.069948,-0.07448,-0.080377,-0.083845,-0.086709,-0.0905,-0.093267,-0.096633,-0.1016,-0.10548,-0.10767,-0.10999,-0.11241,-0.1149,-0.11776,-0.12016,-0.12229,-0.12504,-0.12669,-0.12687,-0.12719,-0.12908,-0.13125,-0.13269,-0.13419,-0.13579,-0.13815,-0.14155,-0.14476,-0.1463,-0.14786,-0.15051,-0.15289,-0.15502,-0.15738,-0.15907,-0.16115,-0.16393,-0.1659,-0.16808,-0.17059,-0.17367,-0.17609,-0.17768,-0.17859,-0.17877,-0.17969,-0.1815,-0.18405,-0.18622,-0.18868,-0.19085,-0.19204,-0.19382,-0.19566,-0.1967,-0.19687,-0.19718,-0.19778,-0.19908,-0.20151,-0.20431,-0.20562,-0.20667,-0.20907,-0.21195,-0.21375,-0.21466,-0.2174,-0.21986,-0.22103,-0.22197,-0.22358,-0.22485,-0.22604,-0.22824,-0.2304,-0.23169,-0.23176,-0.23206,-0.23378,-0.23521,-0.23558,-0.23679,-0.23831,-0.23926,-0.24079,-0.24104,-0.24023,-0.24035,-0.24087,-0.24172,-0.24271,-0.24318,-0.24255,-0.24222,-0.24277,-0.24261,-0.24255,-0.2438,-0.2444,-0.24481,-0.24584,-0.24669,-0.24833,-0.24949,-0.24892,-0.24907,-0.2499,-0.24941,-0.24889,-0.24966,-0.24968,-0.24859,-0.24762,-0.24746,-0.24764,-0.24807,-0.24765,-0.24633,-0.24636,-0.24727,-0.24701,-0.24626,-0.24623,-0.24658,-0.24651,-0.24614,-0.24603,-0.24563,-0.24586,-0.24595,-0.24497,-0.24509,-0.24603,-0.24536,-0.24467,-0.24591,-0.24699,-0.2469,-0.24645,-0.24584,-0.24571,-0.24662,-0.24707,-0.24615,-0.24542,-0.24588,-0.2467,-0.24644,-0.24599,-0.2465,-0.2466,-0.24637,-0.24648,-0.24561,-0.24429,-0.24486,-0.24628,-0.24636,-0.24592,-0.24657,-0.24726,-0.2462,-0.24545,-0.24555,-0.24536,-0.24526,-0.24526,-0.24611,-0.24616,-0.24584,-0.24651,-0.24634,-0.24549,-0.24462,-0.24398,-0.24327,-0.24183,-0.24088,-0.24073,-0.24013,-0.23928,-0.23835,-0.23811,-0.23783,-0.23713,-0.23665,-0.23549,-0.23386,-0.23264,-0.23185,-0.23074,-0.22909,-0.22799,-0.22762,-0.22705,-0.22613,-0.22545,-0.2247,-0.22372,-0.22332,-0.22321,-0.22259,-0.22113,-0.21997,-0.21989,-0.22054,-0.22066,-0.2198,-0.21935,-0.21942,-0.21922,-0.21879,-0.21825,-0.21745,-0.21657,-0.21558,-0.21522,-0.21489,-0.21367,-0.21236,-0.21188,-0.21163,-0.21038,-0.20856,-0.20713,-0.20668,-0.2065,-0.20601,-0.2046,-0.20302,-0.20147,-0.19973,-0.19876,-0.19788,-0.19635,-0.19482,-0.19325,-0.19125,-0.19025,-0.18972,-0.18886,-0.18917,-0.18916,-0.18858,-0.1888,-0.18803,-0.18677,-0.18644,-0.18595,-0.18566,-0.18539,-0.18395,-0.18228,-0.18145,-0.18089,-0.18026,-0.17915,-0.17752,-0.17675,-0.17645,-0.17556,-0.17451,-0.17359,-0.17222,-0.17143,-0.17139,-0.17079,-0.17007,-0.16934,-0.16833,-0.16841,-0.16891,-0.16866,-0.16824,-0.16754,-0.16697,-0.16695,-0.16683,-0.16592,-0.16473,-0.1637,-0.16286,-0.16222,-0.162,-0.16204,-0.16126,-0.16044,-0.15965,-0.15838,-0.15778,-0.15756,-0.15659,-0.15522,-0.15393,-0.153,-0.15297,-0.15334,-0.15288,-0.15214,-0.15164,-0.15081,-0.15013,-0.15051,-0.15112,-0.15093,-0.15044,-0.14966,-0.1493,-0.14973,-0.14987,-0.14922,-0.14849,-0.14815,-0.14838,-0.14853,-0.1475,-0.14593,-0.14493,-0.14422,-0.14287,-0.14135,-0.1404,-0.13978,-0.13946,-0.1392,-0.13807,-0.1367,-0.13581,-0.13487,-0.13386,-0.13268,-0.13112,-0.13008,-0.12972,-0.12949,-0.12898,-0.12822,-0.12721,-0.12531,-0.12401,-0.12356,-0.1229,-0.12257,-0.1225,-0.1223,-0.12125,-0.11972,-0.11917,-0.1189,-0.11847,-0.11791,-0.11686,-0.11552,-0.11409,-0.11291,-0.11125,-0.10931,-0.10799,-0.10706,-0.10615,-0.10498,-0.1032,-0.10123,-0.09986,-0.098776,-0.097799,-0.096464,-0.09453,-0.092796,-0.091778,-0.091134,-0.090254,-0.089114,-0.088326,-0.088116,-0.087988,-0.087814,-0.086766,-0.085323,-0.08497,-0.085119,-0.084924,-0.084034,-0.082495,-0.081052,-0.080331,-0.080024,-0.079554,-0.078464,-0.07716,-0.075472,-0.073861,-0.073104,-0.072265,-0.070848,-0.069385,-0.068285,-0.067058,-0.065799,-0.063308,-0.060654,-0.05962,-0.058889,-0.057518,-0.055702,-0.054408,-0.052828,-0.051549,-0.051227,-0.050874,-0.050045,-0.048874,-0.047994,-0.046291,-0.043943,-0.042644,-0.042393,-0.042291,-0.042071,-0.042168,-0.042183,-0.041345,-0.04005,-0.038859,-0.037554,-0.03623,-0.035708,-0.035437,-0.034562,-0.033288,-0.031887,-0.030516,-0.02914,-0.027994,-0.026818,-0.025483,-0.023877,-0.02225,-0.021284,-0.020153,-0.018798,-0.018117,-0.017892,-0.017805,-0.017667,-0.016941,-0.015995,-0.015125,-0.014368,-0.013534,-0.01246,-0.01157,-0.010644,-0.009606,-0.0084653,-0.0074168,-0.0068643,-0.0067211,-0.0066393,-0.0059948,-0.0048848,-0.0039027,-0.002982,-0.0021841,-0.0014834,-0.0010383,-0.00085932,-0.00055242,0.00013811,0.0015652,0.0030383,0.0039846,0.0046547,0.0047058,0.0042557,0.0039385,0.0040255,0.0048183,0.0057544,0.006005,0.0062301,0.006803,0.0065012,0.0058516,0.0064909,0.0082556,0.0097748,0.010486,0.011335,0.012036,0.011611,0.010614,0.010511,0.011422,0.012593,0.01355,0.014312,0.015202,0.016122,0.016158,0.016424,0.017396,0.017729,0.01801,0.018613,0.019335,0.019754,0.02002,0.02003,0.020199,0.020971,0.021749,0.022388,0.023002,0.023299,0.023023,0.023023,0.023657,0.024532,0.025503,0.026726,0.027861,0.028792,0.030184,0.031406,0.031994,0.033299,0.034511,0.035079,0.036194,0.037559,0.038721,0.039549,0.040281,0.041145,0.042255,0.04336,0.044255,0.045099,0.045933,0.046393,0.046526,0.046587,0.04693,0.047477,0.047595,0.047876,0.04891,0.05024,0.051007,0.051534,0.052265,0.052904,0.053651,0.054357,0.054853,0.055794,0.057247,0.058275,0.059012,0.060065,0.06093,0.061549,0.062301,0.063477,0.065017,0.066372,0.067682,0.068807,0.069109,0.069104,0.069482,0.06984,0.070004,0.070434,0.071139,0.071656,0.072208,0.072873,0.073375,0.073743,0.074577,0.075584,0.076086,0.077109,0.078572,0.079963,0.081109,0.082254,0.083334,0.083973,0.084587,0.085267,0.086433,0.087942,0.089845,0.091876,0.093814,0.095615,0.09674,0.097579,0.098244,0.098909,0.099886,0.10125,0.10218,0.10261,0.10376,0.10521,0.10599,0.10679,0.10738,0.10746,0.1081,0.10914,0.11006,0.11107,0.11234,0.1137,0.11459,0.11503,0.11548,0.11622,0.11749,0.11872,0.11965,0.121,0.12231,0.12345,0.12481,0.12593,0.12666,0.12741,0.1283,0.12898,0.12988,0.13142,0.13293,0.1342,0.13565,0.13666,0.13724,0.1378,0.13822,0.1387,0.13983,0.14147,0.14277,0.14377,0.14501,0.14639,0.14736,0.14813,0.14913,0.15037,0.15166,0.15273,0.15348,0.15422,0.1558,0.15741,0.15826,0.15928,0.1602,0.16189,0.16343,0.16378,0.16412,0.16449,0.16583,0.16772,0.16884,0.1694,0.16973,0.17009,0.17065,0.17156,0.17275,0.17415,0.17597,0.17776,0.17881,0.17935,0.17998,0.18094,0.18247,0.18415,0.18545,0.1864,0.18757,0.18949,0.19098,0.1923,0.19397,0.19525,0.19621,0.19717,0.19862,0.19988,0.20074,0.20266,0.20479,0.20604,0.20679,0.20706,0.20728,0.20795,0.20926,0.2103,0.21116,0.21378,0.21618,0.21652,0.21647,0.21653,0.21677,0.21706,0.2181,0.22022,0.22177,0.22353,0.22631,0.2279,0.22846,0.22934,0.23019,0.23145,0.23265,0.23368,0.23551,0.23786,0.24068,0.24261,0.24348,0.24533,0.24735,0.24877,0.25005,0.25072,0.25158,0.25435,0.25819,0.26071,0.26227,0.26307,0.26311,0.26313,0.26312,0.26308,0.26301,0.26292\n    };\n    double m_h_4_der[1022] = {-24.081,-8.8875,-6.4795,-5.1017,-4.2713,-3.7263,-3.3094,-2.9945,-2.7379,-2.5156,-2.291,-2.0368,-1.7392,-1.4269,-1.1332,-0.85892,-0.62824,-0.44061,-0.29802,-0.21254,-0.13043,-0.050997,0.031248,0.13322,0.22316,0.30864,0.38272,0.45163,0.54022,0.63966,0.74063,0.84528,0.95424,1.0599,1.1627,1.2826,1.4008,1.4984,1.591,1.6837,1.7608,1.822,1.8786,1.927,1.9649,1.9909,2.0205,2.0525,2.081,2.1214,2.1675,2.2115,2.2426,2.2585,2.2704,2.268,2.2566,2.2355,2.2041,2.1647,2.132,2.116,2.1025,2.0872,2.0698,2.0476,2.0184,1.9916,1.9704,1.9441,1.9117,1.8832,1.858,1.8243,1.7863,1.7584,1.737,1.7153,1.695,1.6782,1.6619,1.6394,1.6206,1.6065,1.5844,1.5631,1.5382,1.5076,1.4832,1.4563,1.4273,1.3992,1.3665,1.3354,1.3039,1.2672,1.2267,1.1947,1.1684,1.1389,1.1108,1.084,1.0611,1.0399,1.016,0.99307,0.97347,0.95343,0.92674,0.89705,0.87482,0.85164,0.81728,0.78134,0.74904,0.71994,0.69021,0.65647,0.62032,0.58657,0.55465,0.52311,0.49283,0.46078,0.43049,0.40669,0.38478,0.36204,0.34237,0.32548,0.30722,0.28596,0.26536,0.24743,0.23212,0.22232,0.21269,0.19556,0.1813,0.17068,0.1506,0.13254,0.11711,0.098009,0.082229,0.065308,0.050137,0.026828,-0.002092,-0.022567,-0.041416,-0.062024,-0.085405,-0.10372,-0.12127,-0.14149,-0.15404,-0.16852,-0.18294,-0.19261,-0.21091,-0.22669,-0.23643,-0.25221,-0.26445,-0.27704,-0.29331,-0.30323,-0.31022,-0.32,-0.33978,-0.35706,-0.36294,-0.36929,-0.38863,-0.41063,-0.4217,-0.42745,-0.44735,-0.46815,-0.47425,-0.49258,-0.51453,-0.52509,-0.54067,-0.5634,-0.57656,-0.59903,-0.62165,-0.63645,-0.65901,-0.67815,-0.70048,-0.72002,-0.73923,-0.75675,-0.76775,-0.78125,-0.79495,-0.80702,-0.81528,-0.82303,-0.82432,-0.82367,-0.82088,-0.81711,-0.81136,-0.80775,-0.8051,-0.79911,-0.79029,-0.78061,-0.77583,-0.76921,-0.7645,-0.75859,-0.75475,-0.74685,-0.73797,-0.73523,-0.72733,-0.72041,-0.71632,-0.71156,-0.70797,-0.7013,-0.69262,-0.68968,-0.68554,-0.67924,-0.67919,-0.67548,-0.66556,-0.66075,-0.65923,-0.65245,-0.64755,-0.64775,-0.64195,-0.63381,-0.62953,-0.62652,-0.62112,-0.61615,-0.61432,-0.61156,-0.60484,-0.59931,-0.59891,-0.59682,-0.58858,-0.5815,-0.57832,-0.57483,-0.56623,-0.55704,-0.55227,-0.54542,-0.53529,-0.52577,-0.51861,-0.51047,-0.50412,-0.49486,-0.48468,-0.47926,-0.47437,-0.467,-0.45669,-0.44772,-0.4417,-0.43628,-0.42803,-0.41959,-0.41266,-0.40601,-0.39962,-0.39084,-0.38183,-0.37564,-0.37055,-0.36537,-0.35857,-0.35064,-0.34398,-0.338,-0.33279,-0.32858,-0.32326,-0.3177,-0.31353,-0.30887,-0.30385,-0.29802,-0.29302,-0.29016,-0.28711,-0.28308,-0.27759,-0.27315,-0.27024,-0.26671,-0.26457,-0.26169,-0.25609,-0.25321,-0.25269,-0.25216,-0.25424,-0.25443,-0.25027,-0.24758,-0.24594,-0.24342,-0.2412,-0.24014,-0.23874,-0.23685,-0.23415,-0.23186,-0.23137,-0.22876,-0.22521,-0.22381,-0.22077,-0.21565,-0.21167,-0.20601,-0.19843,-0.19255,-0.19006,-0.18702,-0.18165,-0.1775,-0.17384,-0.1693,-0.16524,-0.16177,-0.15818,-0.1539,-0.14737,-0.14036,-0.13508,-0.13091,-0.1264,-0.12015,-0.11472,-0.11133,-0.10709,-0.10108,-0.095338,-0.089748,-0.083661,-0.076674,-0.072306,-0.070464,-0.066173,-0.060966,-0.056434,-0.051222,-0.041483,-0.034204,-0.033222,-0.03271,-0.029897,-0.026644,-0.026138,-0.021846,-0.014394,-0.0097134,-0.0069717,-0.0047774,-0.0025524,-0.0010946,0.0014424,0.00645,0.0097287,0.010757,0.01244,0.013882,0.015534,0.016987,0.017938,0.018209,0.019913,0.022659,0.024204,0.024363,0.024808,0.030061,0.034076,0.034332,0.034235,0.034071,0.035263,0.037508,0.038168,0.041053,0.045058,0.045672,0.0447,0.044853,0.046864,0.047518,0.046838,0.045912,0.047999,0.052055,0.05516,0.056608,0.056352,0.058326,0.061697,0.06404,0.06537,0.067047,0.070659,0.075651,0.08029,0.082781,0.085461,0.088904,0.09317,0.096612,0.096853,0.099635,0.10511,0.1084,0.1105,0.11282,0.11713,0.12312,0.12658,0.12732,0.12773,0.12971,0.13127,0.12992,0.1298,0.13345,0.13871,0.14327,0.14435,0.14386,0.14514,0.14679,0.1484,0.14888,0.14906,0.14975,0.15124,0.15522,0.15819,0.16053,0.16364,0.16621,0.16631,0.1646,0.16574,0.16692,0.16708,0.16944,0.17271,0.17499,0.17602,0.17808,0.18069,0.18191,0.18073,0.17881,0.18018,0.18239,0.18622,0.18899,0.18877,0.18844,0.1889,0.19057,0.19122,0.19043,0.18873,0.18863,0.19004,0.19156,0.19218,0.19179,0.193,0.19532,0.19542,0.19388,0.19315,0.19353,0.19483,0.19496,0.19393,0.1947,0.19651,0.19864,0.20053,0.20006,0.19897,0.1989,0.19894,0.19977,0.20045,0.19939,0.19656,0.19378,0.19417,0.19674,0.19812,0.19728,0.19664,0.19587,0.19513,0.19567,0.19632,0.19617,0.19609,0.19533,0.19416,0.19634,0.19856,0.1988,0.19985,0.20014,0.20089,0.20257,0.20179,0.20101,0.20208,0.20356,0.20578,0.20772,0.20807,0.21,0.21431,0.21596,0.21545,0.21659,0.21842,0.21934,0.21955,0.21976,0.22152,0.22456,0.22629,0.22758,0.22965,0.23046,0.23159,0.23489,0.23714,0.23837,0.24033,0.24215,0.24453,0.24849,0.25191,0.25348,0.25371,0.25455,0.25664,0.25925,0.2618,0.26299,0.26461,0.26788,0.27055,0.27204,0.27344,0.27348,0.27257,0.27286,0.27327,0.27357,0.27394,0.27378,0.2734,0.27284,0.27244,0.27281,0.27425,0.27636,0.27805,0.27817,0.27705,0.27746,0.27921,0.28043,0.28084,0.28091,0.28113,0.28114,0.28187,0.28386,0.28281,0.28038,0.27943,0.27912,0.27972,0.28198,0.28512,0.28596,0.28569,0.28512,0.28432,0.28455,0.28322,0.27674,0.27139,0.26944,0.26742,0.26585,0.26422,0.26278,0.26114,0.26006,0.25982,0.25804,0.25564,0.25457,0.25479,0.25254,0.24622,0.24135,0.23936,0.2383,0.23862,0.23969,0.23942,0.23764,0.23562,0.23371,0.23183,0.2309,0.23193,0.2323,0.22988,0.22607,0.22313,0.22258,0.2228,0.2222,0.21945,0.21601,0.2132,0.2096,0.20409,0.19916,0.19794,0.19782,0.19687,0.19581,0.19571,0.19441,0.1917,0.19076,0.19029,0.18735,0.18517,0.18396,0.18249,0.18261,0.18381,0.18458,0.18441,0.18382,0.18353,0.18273,0.17548,0.16814,0.167,0.16683,0.1649,0.16321,0.16316,0.16024,0.15677,0.1561,0.15637,0.15614,0.15488,0.15343,0.14732,0.13796,0.1338,0.13415,0.13515,0.13496,0.13322,0.13067,0.12786,0.12566,0.12486,0.12449,0.12212,0.11975,0.11826,0.11565,0.11272,0.10947,0.10458,0.10037,0.099569,0.1001,0.098525,0.095369,0.093159,0.092331,0.09428,0.096955,0.097564,0.096945,0.095359,0.093896,0.093323,0.092745,0.092612,0.092786,0.0892,0.083323,0.080004,0.078075,0.076459,0.073769,0.071472,0.071871,0.072761,0.072674,0.071538,0.070147,0.068981,0.068398,0.068459,0.067707,0.067462,0.069068,0.06937,0.06782,0.066024,0.06429,0.063441,0.063595,0.063313,0.059871,0.056035,0.054838,0.054086,0.053421,0.053155,0.052526,0.051314,0.051191,0.050219,0.047979,0.044741,0.04182,0.041774,0.042603,0.043544,0.044137,0.043498,0.041549,0.040306,0.041155,0.042342,0.043263,0.044327,0.043769,0.042593,0.042843,0.043309,0.04313,0.042449,0.041017,0.037718,0.03491,0.033805,0.03247,0.030455,0.028286,0.027104,0.026081,0.025217,0.025718,0.025805,0.023744,0.020685,0.017529,0.01511,0.013442,0.012286,0.011366,0.010496,0.010629,0.010358,0.0076623,0.0057748,0.0053298,0.0039795,0.0030076,0.0035549,0.004716,0.005115,0.0049411,0.0051457,0.005115,0.0042966,0.0029872,0.0016931,0.0017851,0.00047058,-0.0019846,-0.0037544,-0.0069411,-0.010711,-0.013775,-0.016255,-0.019243,-0.021575,-0.022526,-0.023473,-0.024644,-0.0258,-0.026716,-0.027739,-0.028736,-0.030572,-0.031616,-0.030797,-0.030373,-0.030685,-0.030992,-0.031329,-0.032178,-0.033002,-0.033621,-0.033488,-0.03247,-0.031912,-0.032342,-0.033447,-0.034649,-0.0356,-0.0367,-0.038429,-0.039411,-0.039616,-0.041345,-0.043575,-0.044864,-0.047089,-0.049979,-0.051498,-0.052741,-0.054838,-0.055656,-0.055805,-0.056444,-0.056664,-0.055769,-0.054725,-0.054858,-0.055472,-0.056787,-0.057416,-0.057042,-0.056797,-0.057462,-0.058137,-0.058306,-0.058945,-0.059626,-0.060848,-0.062991,-0.06317,-0.062377,-0.063549,-0.065922,-0.068674,-0.071052,-0.073574,-0.076439,-0.079993,-0.083441,-0.08496,-0.086034,-0.087697,-0.090331,-0.093103,-0.094945,-0.096868,-0.099379,-0.10195,-0.10324,-0.10357,-0.10314,-0.10279,-0.10396,-0.10678,-0.1099,-0.1117,-0.11237,-0.11339,-0.11461,-0.11517,-0.11563,-0.11584,-0.11672,-0.11843,-0.11937,-0.12005,-0.12103,-0.12217,-0.12342,-0.12505,-0.12699,-0.12863,-0.13034,-0.13285,-0.13431,-0.13468,-0.13607,-0.13759,-0.13874,-0.14011,-0.14179,-0.14316,-0.14422,-0.14721,-0.15014,-0.15145,-0.15243,-0.1534,-0.15609,-0.15762,-0.15773,-0.15867,-0.15965,-0.16244,-0.16539,-0.16705,-0.16881,-0.17023,-0.17195,-0.17384,-0.17576,-0.17828,-0.1804,-0.18351,-0.1878,-0.19082,-0.19296,-0.19537,-0.19917,-0.20362,-0.2069,-0.20958,-0.21182,-0.21443,-0.21847,-0.22139,-0.22227,-0.22333,-0.22475,-0.22599,-0.22709,-0.22885,-0.23064,-0.23152,-0.23437,-0.23667,-0.23673,-0.23722,-0.23779,-0.23834,-0.23893,-0.23896,-0.23953,-0.24113,-0.2458,-0.25076,-0.25237,-0.25364,-0.25488,-0.25585,-0.25675,-0.25858,-0.26131,-0.26352,-0.26694,-0.26971,-0.27015,-0.27117,-0.27375,-0.27626,-0.27753,-0.27844,-0.27983,-0.28325,-0.28738,-0.29076,-0.2923,-0.29217,-0.29258,-0.29335,-0.29315,-0.2947,-0.29784,-0.30063,-0.30888,-0.3186,-0.3221,-0.32289,-0.32381,-0.32478,-0.32582,-0.32697,-0.32817,-0.32936,-0.33054\n    };\n    \n};\n\n#endif // include guard\n"
  },
  {
    "path": "src/NonlinearOptimizer.cpp",
    "content": "//\n//  NonlinearOptimizer.cpp\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#include \"NonlinearOptimizer.h\"\n\nNonlinearOptimizer::NonlinearOptimizer(int keyframe_spacing,\n                                       Database* database,\n                                       int safe_zone_size,\n                                       int min_keyframes_valid,\n                                       int patch_size)\n{\n    // Initialize object parameters with passed values\n    m_keyframe_spacing = keyframe_spacing;\n    m_database = database;\n    m_safe_zone_size = safe_zone_size;\n    m_min_keyframes_valid = min_keyframes_valid;\n    m_patch_size = patch_size;\n    \n    // No optimization data yet on initialization\n    m_optimization_block = NULL;\n    \n    // Keep an estimate of the inverse response here for fast plotting\n    m_raw_inverse_response = new double[256];\n}\n\nbool NonlinearOptimizer::extractOptimizationBlock()\n{\n    int nr_images_in_database = static_cast<int>(m_database->m_tracked_frames.size());\n\n    // Not enough images in the database yet -> extraction fails\n    // Todo: why 2 times?\n    if(nr_images_in_database < 2*(m_keyframe_spacing*m_min_keyframes_valid)+m_safe_zone_size)\n    {\n        return false;\n    }\n    \n    // Create a new optimization block, delete the old block (if an old one exists)    \n    if(m_optimization_block != NULL)\n    {\n        delete m_optimization_block;\n    }\n    m_optimization_block = new OptimizationBlock(m_patch_size);\n    \n    int nr_images_in_block = 0;\n    m_optimization_block->deleteExposureTimes();\n    \n    // Iterate through all images in the database (except the most current ones used for exposure optimization)\n    for(int i = 0;i < nr_images_in_database - m_safe_zone_size;i++)\n    {\n        // Only add keyframe images\n        if(i%m_keyframe_spacing == 0)\n        {\n            nr_images_in_block++;\n            \n            // Store the image inside the optimization block for later correction\n            m_optimization_block->addImage(m_database->m_tracked_frames.at(i).m_image);\n            \n            // Push exposure time estimate (use either the one found from rapid exposure time estimation or 1.0)\n            // If 1.0 is used, then all the optimization parameters should also be initialized with the same constant values (for V,f,E,L)\n            m_optimization_block->pushExposureTime(m_database->m_tracked_frames.at(i).m_exp_time,m_database->m_tracked_frames.at(i).m_gt_exp_time);\n            //m_optimization_block->pushExposureTime(1.0);\n        }\n        \n        // Get all features in the current keyframe and iterate them\n        // Todo: change features to pointer?\n        std::vector<Feature*> features = m_database->m_tracked_frames.at(i).m_features;\n        \n        for(int p = 0;p < features.size();p++)\n        {\n            // Skip features that are not new, don't attempt at creating a new optimization point\n            if(features.at(p)->m_prev_feature != NULL)\n                continue;\n            \n            // Find out in how many and which keyframes this point is visible\n            Feature* feature_iterator = features.at(p);\n            std::vector<int> keyframes_valid;\n            int feature_iterator_image_index = i;\n            \n            // Track the feature forward until either we hit NULL or the feature is tracked out of the safe zone\n            while(feature_iterator != NULL && feature_iterator_image_index < nr_images_in_database-m_safe_zone_size)\n            {\n                if(feature_iterator_image_index%m_keyframe_spacing == 0) //feature_iterator_image_index is a keyframe image\n                    keyframes_valid.push_back(feature_iterator_image_index/m_keyframe_spacing);\n                \n                // Check the next feature, break if the min. number of keyframes necessary for this feature has been reached\n                // -> then go on to extract image information for this feature\n                feature_iterator = feature_iterator->m_next_feature;\n                feature_iterator_image_index++;\n                \n                // Break early if the feature has been identified as tracked long enough\n                if(keyframes_valid.size() >= m_min_keyframes_valid)\n                    break;\n            }\n            \n            // Feature not tracked long enough\n            if(keyframes_valid.size() < m_min_keyframes_valid)\n                continue;\n            \n            // Allocate new optimization point\n            OptimizedPoint opt_p;\n            opt_p.start_image_idx = keyframes_valid.at(0);\n            \n            // Initialize vector for radiance estimates\n            std::vector<double> radiance_estimate;\n            for(int r = 0;r < features.at(p)->m_radiance_estimates.size();r++)\n            {\n                radiance_estimate.push_back(0.0);\n            }\n            \n            // Iterate the good feature again, now completely and extract its information\n            feature_iterator = features.at(p);\n            feature_iterator_image_index = i;\n            int nr_keyframes_valid = 0;\n            \n            while(feature_iterator != NULL && feature_iterator_image_index < nr_images_in_database-m_safe_zone_size)\n            {\n                if(feature_iterator_image_index%m_keyframe_spacing != 0) //only extract data on keyframe images\n                {\n                    feature_iterator = feature_iterator->m_next_feature;\n                    feature_iterator_image_index++;\n                    continue;\n                }\n                \n                nr_keyframes_valid++;\n                \n                // Accumulate radiance estimates (obtained using the previously corrected image data)\n                for(int r = 0;r < feature_iterator->m_radiance_estimates.size();r++)\n                {\n                    radiance_estimate.at(r) += feature_iterator->m_radiance_estimates.at(r);\n                }\n                \n                // Initialize the estimation problem with the average of the output intensities of the original images\n                // This should be exchanged with the above initialization, if exposure values = 1.0 are used for initialization\n                // Assuming unit response and no vignetting (for this, the avg. of outputs is the optimal radiance estimate)\n                /*for(int r = 0;r < feature_iterator->m_output_values.size();r++)\n                {\n                    radiance_estimate.at(r) += feature_iterator->m_output_values.at(r);\n                }*/\n                \n                // Store output intensities to the optimization point\n                opt_p.output_intensities.push_back(feature_iterator->m_output_values);\n                \n                // Store xy locations\n                opt_p.xy_image_locations.push_back(feature_iterator->m_xy_location);\n                \n                // Calculate point radius\n                double radius = m_database->m_vignette_estimate.getNormalizedRadius(feature_iterator->m_xy_location);\n                opt_p.radii.push_back(radius);\n                \n                // Set gradient weights\n                std::vector<double> grad_weights;\n                for(int r = 0;r < feature_iterator->m_gradient_values.size();r++)\n                {\n                    double grad_value = feature_iterator->m_gradient_values.at(r);\n                    double weight = 1.0 - (grad_value/255.0);\n                    grad_weights.push_back(weight);\n                }\n                opt_p.grad_weights.push_back(grad_weights);\n                \n                feature_iterator = feature_iterator->m_next_feature;\n                feature_iterator_image_index++;\n            }\n            \n            // Average radiance estimates\n            for(int r = 0;r < features.at(p)->m_radiance_estimates.size();r++)\n            {\n                radiance_estimate.at(r) /= 255.0*nr_keyframes_valid;\n            }\n            \n            // Store information about in how many keyframes this feature is valid\n            opt_p.num_images_valid = nr_keyframes_valid;\n            \n            // Store the radiance estimates to the optimization point\n            opt_p.radiances = radiance_estimate;\n            \n            // Add point to optimization block\n            m_optimization_block->addOptimizationPoint(opt_p);\n        }\n    }\n    \n    return true;\n}\n\ndouble NonlinearOptimizer::evfOptimization(bool show_debug_prints)\n{\n    // Used for calculating first order derivatives, creating the Jacobian\n    JacobianGenerator jacobian_generator;\n    jacobian_generator.setResponseParameters(m_response_estimate);\n    jacobian_generator.setVignettingParameters(m_vignette_estimate);\n    \n    // Find maximum number of residuals\n    int points_per_patch = pow(2*m_patch_size+1,2);\n    int num_residuals = m_optimization_block->getNrResiduals();\n    \n    // Number of parameters to optimize for (4 response, 3 vignette + exposure times)\n    int num_parameters = C_NR_RESPONSE_PARAMS + C_NR_VIGNETTE_PARAMS + m_optimization_block->getNrImages();\n    \n    // Initialize empty matrices for the Jacobian and the residual vector\n    cv::Mat Jacobian(num_residuals,num_parameters,CV_64F,0.0);\n    cv::Mat Residuals(num_residuals,1,CV_64F,0.0);\n    \n    // Weight matrix for the Jacobian\n    cv::Mat Weights_Jacobian(num_residuals,num_parameters,CV_64F,0.0);\n    \n    // Fill the Jacobian\n    int residual_id = -1;\n    double residual_sum = 0;\n    int overall_image_index = 0;\n    \n    std::vector<OptimizedPoint>* points_to_optimize = m_optimization_block->getOptimizedPoints();\n    \n    // Iterate all tracked points\n    for(int p = 0;p < points_to_optimize->size();p++)\n    {\n        int image_start_index = points_to_optimize->at(p).start_image_idx;\n        int nr_img_valid = points_to_optimize->at(p).num_images_valid;\n        \n        // Iterate images the point is valid\n        for(int i = 0;i < nr_img_valid;i++)\n        {\n            double radius = points_to_optimize->at(p).radii.at(i);\n            double exposure = m_optimization_block->getExposureTime(image_start_index+i);\n                \n            //iterate all the points in the patch\n            for(int r = 0;r < points_per_patch;r++)\n            {\n                double grad_weight = points_to_optimize->at(p).grad_weights.at(i).at(r);\n                if(grad_weight < 0.001) // Dont include a point with close to 0 weight in optimization\n                    continue;\n                \n                double radiance = points_to_optimize->at(p).radiances.at(r);\n                double o_value = points_to_optimize->at(p).output_intensities.at(i).at(r);\n                \n                // Avoid I = 0 which leads to NaN errors in the Jacobian, also ignore implausible radiance estimates much larger than 1\n                if(radiance < 0.001 || radiance > 1.1)\n                    continue;\n                if(radiance > 1)\n                    radiance = 1;\n                \n                // Count the actual number of residuals up\n                residual_id++;\n                \n                // Fill the Jacobian row\n                jacobian_generator.getJacobianRow_eca(radiance,\n                                                      radius,\n                                                      exposure,\n                                                      Jacobian,\n                                                      overall_image_index + image_start_index + i,\n                                                      residual_id);\n                \n                // For debugging\n                /*if(show_debug_prints)\n                {\n                    std::cout << \"JACOBIAN ROW ADDED \" << std::endl;\n                    for(int j = 0;j < num_parameters;j++)\n                    {\n                        std::cout << Jacobian.at<double>(residual_id,j) << \" \";\n                            \n                        if(Jacobian.at<double>(residual_id,j) != Jacobian.at<double>(residual_id,j))\n                        {\n                            std::cout << \"NAN\" << std::endl;\n                        }\n                    }\n                    std::cout << std::endl;\n                }*/\n                    \n                // Write weight values to weight matrix\n                for(int k = 0;k < num_parameters;k++)\n                {\n                    Weights_Jacobian.at<double>(residual_id,k) = grad_weight;\n                }\n                    \n                // Fill the residual vector\n                double residual = getResidualValue(o_value, radiance, radius, exposure);\n                Residuals.at<double>(residual_id,0) = grad_weight * residual;\n                \n                residual_sum += std::abs(grad_weight * residual);\n            }\n        }\n    }\n        \n    overall_image_index += m_optimization_block->getNrImages();\n    \n    int real_number_of_residuals = residual_id+1;\n    \n    // Get only the relevant part of the Jacobian (actual number of residuals)\n    Jacobian = Jacobian(cv::Rect(0,0,num_parameters,real_number_of_residuals));\n    Weights_Jacobian = Weights_Jacobian(cv::Rect(0,0,num_parameters,real_number_of_residuals));\n    Residuals = Residuals(cv::Rect(0,0,1,real_number_of_residuals));\n    \n    // Transpose the Jacobian, calculate J^T * W *J * X = - J^T * W * r\n    cv::Mat Jacobian_T;\n    cv::transpose(Jacobian, Jacobian_T);\n    \n    cv::Mat A = Jacobian_T* (Weights_Jacobian.mul(Jacobian));\n    //cv::Mat A = Jacobian.t()*Jacobian;\n    cv::Mat b = - Jacobian.t() * Residuals; // Todo: reuse Jacobian_T to save time?\n    \n    // Get the current residual before optimization in order to compare progress\n    double total_error, avg_error;\n    getTotalResidualError(total_error,avg_error);\n    \n    if(show_debug_prints)\n        std::cout << \"Error before ECA adjustment: total: \" << total_error << \" avg: \" << avg_error << std::endl;\n    \n    // Prepare identity matrix for Levenberg-Marquardt dampening\n    cv::Mat Identity = cv::Mat::eye(num_parameters, num_parameters, CV_64F);\n    Identity = Identity.mul(A);\n    \n    // Backup photometric parameters in order to revert if update is not good\n    std::vector<double> response_param_backup    = m_response_estimate;\n    std::vector<double> vignetting_param_backup = m_vignette_estimate;\n    std::vector<double> exp_backups;\n    for(int i = 0;i < m_optimization_block->getNrImages();i++)\n    {\n        exp_backups.push_back(m_optimization_block->getExposureTime(i));\n    }\n    \n    // Perform update steps\n    int max_rounds = 6; // Todo: change this to const class member\n    cv::Mat BestStateUpdate(num_parameters,1,CV_64F,0.0);\n    double current_best_error = total_error;\n    \n    double lambda = 1.0f;\n    double lm_dampening = 1.0;\n\n    // Todo: are these the right LM iterations???\n    // Rui: may be because of the alternative optimization of evf and radiances, thus only one iteration in the evf GN.\n    for(int round = 0;round < max_rounds;round++)\n    {\n        if(show_debug_prints)\n            std::cout << \"ECA Optimization round with dampening = \" << lm_dampening << std::endl;\n        \n        //solve the linear equation system\n        cv::Mat State_Update;\n\n        lambda = 1.0;\n  \n        // Solve state update equation (+LM damping)\n        // Todo: reuse Jacobian_T to save time?\n        State_Update = - (Jacobian.t()* Weights_Jacobian.mul(Jacobian) + lm_dampening*Identity).inv(cv::DECOMP_SVD)*(Jacobian.t()*Residuals);\n        \n        // Update the estimated parameters\n        for(int k = 0;k < m_response_estimate.size();k++)\n        {\n            m_response_estimate.at(k) = response_param_backup.at(k) + lambda * State_Update.at<double>(k,0);\n        }\n        \n        for(int k = 0;k < m_vignette_estimate.size();k++)\n        {\n            m_vignette_estimate.at(k) = vignetting_param_backup.at(k) + lambda * State_Update.at<double>((int)m_response_estimate.size()+k,0);\n        }\n        \n        int abs_image_index = 0;\n        for(int i = 0;i < m_optimization_block->getNrImages();i++)\n        {\n            double new_exp_time = exp_backups.at(i) + lambda*State_Update.at<double>((int)m_response_estimate.size()+(int)m_vignette_estimate.size()+abs_image_index,0);\n            m_optimization_block->setExposureTime(i,new_exp_time);\n            abs_image_index++;\n        }\n        \n        // Evaluate new residual error with new parameter estimate\n        double current_error;\n        getTotalResidualError(current_error,avg_error);\n        \n        if(show_debug_prints)\n            std::cout << \"error after ECA adjustment: total: \" << current_error << \" avg: \" << avg_error << std::endl;\n        \n        // Improvement?\n        if(current_error < current_best_error)\n        {\n            //increase damping factor, re-perform\n            if(lm_dampening >= 0.0625)\n                lm_dampening /= 2.0f;\n            \n            current_best_error = current_error;\n            BestStateUpdate = State_Update;\n        }\n        else\n        {\n            if(lm_dampening <= 1000000)\n            {\n                lm_dampening *= 2;\n            }\n            else\n            {\n                if(show_debug_prints)\n                    std::cout << \"MAX DAMPING REACHED, BREAK EARLY \" << std::endl;\n                break;\n            }\n        }\n    }\n    \n    // Apply the best of the found state updates\n    \n    for(int k = 0;k < m_response_estimate.size();k++)\n    {\n        m_response_estimate.at(k) = response_param_backup.at(k) + lambda * BestStateUpdate.at<double>(k,0);\n    }\n    \n    for(int k = 0;k < m_vignette_estimate.size();k++)\n    {\n        m_vignette_estimate.at(k) = vignetting_param_backup.at(k) + lambda * BestStateUpdate.at<double>((int)m_response_estimate.size()+k,0);\n    }\n    \n    int abs_image_index = 0;\n    for(int i = 0;i < m_optimization_block->getNrImages();i++)\n    {\n        double new_exp_time = exp_backups.at(i) +\n                              lambda*BestStateUpdate.at<double>((int)m_response_estimate.size() +\n                                                                (int)m_vignette_estimate.size() +\n                                                                abs_image_index,0);\n        m_optimization_block->setExposureTime(i,new_exp_time);\n        abs_image_index++;\n    }\n    \n    if(show_debug_prints)\n        std::cout << \"Best update \" << BestStateUpdate << std::endl;\n    \n    double error_after_optimization;\n    getTotalResidualError(error_after_optimization,avg_error);\n    \n    if(show_debug_prints)\n        std::cout << \"error after ECA adjustment: total: \" << error_after_optimization << \" avg: \" << avg_error << std::endl;\n    \n    return avg_error;\n}\n\ndouble NonlinearOptimizer::getResidualValue(double O, double I, double r, double e)\n{\n    double vignetting = applyVignetting(r);\n    \n    if(vignetting < 0)\n    {\n        vignetting = 0;\n    }\n    if(vignetting > 1)\n    {\n        vignetting = 1;\n    }\n    \n    // Argument of response function\n    double inside = e*I*vignetting;\n    double response_value;\n    \n    // Apply response function\n    if(inside < 0)\n    {\n        response_value = 0;\n    }\n    else if(inside > 1)\n    {\n        response_value = 255;\n    }\n    else\n    {\n        response_value = applyResponse(inside);\n    }\n    \n    // Compute residual\n    double residual = response_value - O;\n    \n    return residual;\n}\n\nvoid NonlinearOptimizer::getTotalResidualError(double& total_error,double& avg_error)\n{\n    int residual_id = -1;\n    double residual_sum = 0;\n    int points_per_patch = pow(2*m_patch_size+1,2);\n    \n    std::vector<OptimizedPoint>* points_to_optimize = m_optimization_block->getOptimizedPoints();\n    \n    // Iterate all tracked points\n    for(int p = 0;p < points_to_optimize->size();p++)\n    {\n        int image_start_index = points_to_optimize->at(p).start_image_idx;\n        int nr_images         = points_to_optimize->at(p).num_images_valid;\n        \n        // Iterate all images of the point\n        for(int i = 0;i < nr_images;i++)\n        {\n            double radius = points_to_optimize->at(p).radii.at(i);\n            double exposure = m_optimization_block->getExposureTime(image_start_index+i);\n                \n            // Iterate all the points in the patch\n            for(int r = 0;r < points_per_patch;r++)\n            {\n                // Handle next residual\n                residual_id++;\n                    \n                // Get the radiance value of this residual (image independent)\n                double radiance = points_to_optimize->at(p).radiances.at(r);\n                \n                // Get image output value of this residual (original image)\n                double o_value = points_to_optimize->at(p).output_intensities.at(i).at(r);\n                    \n                // Compute residual\n                double residual = getResidualValue(o_value, radiance, radius, exposure);\n                    \n                // Get the weight of the residual\n                double residual_weight = points_to_optimize->at(p).grad_weights.at(i).at(r);\n                \n                // Accumulate resdiual values\n                residual_sum += residual_weight*std::abs(residual);\n            }\n        }\n    }\n    \n    // Average residual error\n    total_error = residual_sum;\n    avg_error   = total_error/(residual_id+1);\n}\n\ndouble NonlinearOptimizer::radianceFullOptimization()\n{\n    // Compute derivatives using this object\n    JacobianGenerator jacobian_generator;\n    jacobian_generator.setResponseParameters(m_response_estimate);\n    jacobian_generator.setVignettingParameters(m_vignette_estimate);\n    \n    // Get the error before optimization\n    double total_error, avg_error;\n    getTotalResidualError(total_error,avg_error);\n    std::cout << \"error before radiance adjustment: total: \" << total_error << \" avg: \" << avg_error << std::endl;\n    \n    // Nr of residuals around one tracked point\n    int nr_patch_points = pow(2*m_patch_size+1,2);\n    \n    std::vector<OptimizedPoint>* points_to_optimize = m_optimization_block->getOptimizedPoints();\n    \n    // Iterate all tracked points\n    for(int  p = 0;p < points_to_optimize->size();p++)\n    {\n        int start_image = points_to_optimize->at(p).start_image_idx;\n        int num_images  = points_to_optimize->at(p).num_images_valid;\n        \n        // Iterate all point patches\n        for(int r = 0;r < nr_patch_points;r++)\n        {\n            double radiance_guess = points_to_optimize->at(p).radiances.at(r);\n            double left_side_sum = 0;\n            double right_side_sum = 0;\n            double initialResidualError = getResidualErrorPoint(points_to_optimize->at(p),r);\n            \n            // Iterate all images\n            for(int i = 0;i < num_images;i++)\n            {\n                double exposure = m_optimization_block->getExposureTime(start_image+i);\n                double radius   = points_to_optimize->at(p).radii.at(i);\n                double output_value = points_to_optimize->at(p).output_intensities.at(i).at(r);\n                    \n                // Get the jacobian value\n                double jacobian_I_value;\n                jacobian_generator.getJacobianRadiance(radiance_guess, radius, exposure, jacobian_I_value);\n                jacobian_I_value *= 255;\n                    \n                // Get the residual value\n                double residual = getResidualValue(output_value, radiance_guess, radius, exposure);\n                    \n                left_side_sum += (jacobian_I_value*jacobian_I_value);\n                right_side_sum += (jacobian_I_value*residual);\n            }\n            \n            // Update radiance estimate\n            double lambda = 1;\n            double new_error = initialResidualError+1;\n            int max_iterations = 10;\n            int curr_iteration = 0;\n\n            while(new_error > initialResidualError)\n            {\n                if(curr_iteration == max_iterations)\n                {\n                    lambda = 0;\n                }\n\n                // Todo: change to use a local variable\n                points_to_optimize->at(p).radiances.at(r) = radiance_guess - lambda * (right_side_sum/left_side_sum);\n                if(points_to_optimize->at(p).radiances.at(r) < 0.001)\n                { \n                    points_to_optimize->at(p).radiances.at(r) = 0.001;\n                }\n                if(points_to_optimize->at(p).radiances.at(r) > 0.999)\n                {\n                    points_to_optimize->at(p).radiances.at(r) = 0.999;\n                }\n                    \n                lambda /= 2; // Todo: to justify this in literature\n                curr_iteration++;\n                    \n                if(lambda < 0.001)\n                    break;\n\n                new_error = getResidualErrorPoint(points_to_optimize->at(p), r);\n            }\n        }\n    }\n    \n    getTotalResidualError(total_error,avg_error);\n    std::cout << \"error after Radiance adjustment: total: \" << total_error << \" avg: \" << avg_error << std::endl;\n    \n    return avg_error;\n}\n\ndouble NonlinearOptimizer::getResidualErrorPoint(OptimizedPoint p,int r)\n{\n    int start_image = p.start_image_idx;\n    int num_images  = p.num_images_valid;\n    double radiance_guess = p.radiances.at(r);\n    double totalError = 0;\n    \n    for(int i = 0;i < num_images;i++)\n    {\n        double radius = p.radii.at(i);\n        double output_value = p.output_intensities.at(i).at(r);\n        double exposure = m_optimization_block->getExposureTime(start_image+i);\n        double residual = getResidualValue(output_value, radiance_guess, radius, exposure);\n        totalError += std::abs(residual);\n    }\n    \n    return totalError;\n}\n\n\nvoid NonlinearOptimizer::fetchResponseVignetteFromDatabase()\n{\n    // Fetch vignette estimate from database\n    m_vignette_estimate = m_database->m_vignette_estimate.getVignetteEstimate();\n    \n    // Set response estimate to unit response\n    m_response_estimate.clear();\n    m_response_estimate.push_back(6.3);\n    m_response_estimate.push_back(0.0);\n    m_response_estimate.push_back(0.0);\n    m_response_estimate.push_back(0.0);\n}\n\ndouble NonlinearOptimizer::applyVignetting(double r)\n{\n    double r_2 = r*r;\n    double r_4 = r_2 * r_2;\n    double r_6 = r_4 * r_2;\n    \n    double result_vignette = 1 + m_vignette_estimate.at(0) * r_2 + m_vignette_estimate.at(1)*r_4 + m_vignette_estimate.at(2) * r_6;\n    \n    if(result_vignette < 0)\n        result_vignette = 0.0;\n    if(result_vignette > 1)\n        result_vignette = 1.0;\n    \n    return result_vignette;\n}\n\ndouble NonlinearOptimizer::applyResponse(double x)\n{\n    JacobianGenerator jacobian_generator;\n    jacobian_generator.setResponseParameters(m_response_estimate);\n    double result = jacobian_generator.applyGrossbergResponse(x);\n    return 255*result;\n}\n\ndouble NonlinearOptimizer::visualizeOptimizationResult(double* inverse_response)\n{\n    // Define an exponential factor here to scale response + vignette\n    // double exponent = 1.0;\n    // To go through one point of the GT response of the TUM Mono camera\n    //double exponent = determineGammaFixResponseAt(inverse_response, 206, 0.5);\n    double exponent = determineGammaFixResponseAt(inverse_response, 148, 0.3);\n    \n    // Setup output image windows\n    cv::namedWindow(\"Estimated Vignetting\");\n    cv::namedWindow(\"Estimated Response\");\n    \n    double inverse_response_scaled[256];\n    \n    // Scale up inverse response from range [0,1] to range [0,255]\n    for(int i = 0;i < 256;i++)\n    {\n        inverse_response_scaled[i] = 255*pow(inverse_response[i],exponent);\n    }\n    \n    // Invert the inverse response to get the response\n    double response_function[256];\n    response_function[0] = 0;\n    response_function[255] = 255;\n\n    // For each response value i find s, such that inverse_response[s] = i\n    for(int i=1;i<255;i++)\n    {\n        for(int s=0;s<255;s++)\n        {\n            if(inverse_response_scaled[s] <= i && inverse_response_scaled[s+1] >= i)\n            {\n                response_function[i] = s+(i - inverse_response_scaled[s]) / (inverse_response_scaled[s+1]-inverse_response_scaled[s]);\n                break;\n            }\n        }\n    }\n    \n    // Setup a 256x256 mat to display inverse response + response\n    // Todo: change to class member\n    cv::Mat response_vis_image(256,256,CV_8UC3,cv::Scalar(0,0,0));\n    for(int i = 0;i < 256;i++)\n    {\n        int response_value = static_cast<int>(round(response_function[i]));\n        int inv_response_value = static_cast<int>(round(inverse_response_scaled[i]));\n        \n        if(response_value < 0)\n            response_value = 0;\n        if(response_value > 255)\n            response_value = 255;\n        if(inv_response_value < 0)\n            inv_response_value = 0;\n        if(inv_response_value > 255)\n            inv_response_value = 255;\n        \n        //plot the response\n        response_vis_image.at<cv::Vec3b>(255-response_value,i)[0] = 0;\n        response_vis_image.at<cv::Vec3b>(255-response_value,i)[1] = 0;\n        response_vis_image.at<cv::Vec3b>(255-response_value,i)[2] = 255;\n        \n        //plot the inverse response\n        //response_vis_image.at<cv::Vec3b>(255-inv_response_value,i)[0] = 0;\n        //response_vis_image.at<cv::Vec3b>(255-inv_response_value,i)[1] = 0;\n        //response_vis_image.at<cv::Vec3b>(255-inv_response_value,i)[2] = 255;\n        \n        //draw the diagonal\n        //response_vis_image.at<cv::Vec3b>(255-i,i)[0] = 255;\n        //response_vis_image.at<cv::Vec3b>(255-i,i)[1] = 255;\n        //response_vis_image.at<cv::Vec3b>(255-i,i)[2] = 255;\n        \n        // Draw a variety of GT response functions\n        double x = i/255.0;\n        \n        // [1] Draw the best gamma approximation for DSO response\n        /*double dso_gamma_approx_y = pow(x,0.7-0.3*x);\n         int dso_gamma_approx_y_int = static_cast<int>(dso_gamma_approx_y*255);\n         if(dso_gamma_approx_y_int > 255)\n             dso_gamma_approx_y_int = 255;\n         response_vis_image.at<cv::Vec3b>(255-dso_gamma_approx_y_int,i)[0] = 255;\n         response_vis_image.at<cv::Vec3b>(255-dso_gamma_approx_y_int,i)[1] = 255;\n         response_vis_image.at<cv::Vec3b>(255-dso_gamma_approx_y_int,i)[2] = 0;*/\n        \n        double m;\n        double t;\n        //draw GT response for DSO camera\n        if(x < 0.1)\n        {\n            m = (90/255.0)/0.1;\n            t = 0.0f;\n        }\n        else if(x < 0.48)\n        {\n            m = (110.0/255)/0.38;\n            t = (200.0/255.0) - m*0.48;\n        }\n        else\n        {\n            m = (55.0/255)/0.52;\n            t = 1 - m*1;\n        }\n        double dso_value_f = m*x + t;\n        int dso_value = static_cast<int>(dso_value_f*255);\n        if(dso_value > 255)\n            dso_value = 255;\n        response_vis_image.at<cv::Vec3b>(255-dso_value,i)[0] = 255;\n        response_vis_image.at<cv::Vec3b>(255-dso_value,i)[1] = 255;\n        response_vis_image.at<cv::Vec3b>(255-dso_value,i)[2] = 0;\n        \n//        /*\n//         * Draw GT response for artificial dataset\n//         */\n//        //draw the GT response for canon EOS 600 D\n//        double artificial_value_f = pow(x,0.6-0.2*x);\n//        int artificial_value = static_cast<int>(artificial_value_f*255);\n//        if(artificial_value > 255)\n//            artificial_value = 255;\n//        response_vis_image.at<cv::Vec3b>(255-artificial_value,i)[0] = 0;\n//        response_vis_image.at<cv::Vec3b>(255-artificial_value,i)[1] = 255;\n//        response_vis_image.at<cv::Vec3b>(255-artificial_value,i)[2] = 255;\n        \n    }\n    \n    cv::imshow(\"Estimated Response\", response_vis_image);\n    // TODO: move only the first time the window is created,\n    //       to allow the user to move it somewhere else.\n    //       Same for other calls to \"moveWindow\".\n    cv::moveWindow(\"Estimated Response\", 20,20);\n\n    // Show the vignetting\n    \n    //Setup a 256x256 mat to display vignetting\n    cv::Mat vignette_vis_image(256,256,CV_8UC3,cv::Scalar(0,0,0));\n    for(int i = 0;i < 256;i++)\n    {\n        double r = i/255.0f;\n        \n        double r_2 = r*r;\n        double r_4 = r_2 * r_2;\n        double r_6 = r_4 * r_2;\n        \n        double vignette = 1 + m_vignette_estimate.at(0) * r_2 + m_vignette_estimate.at(1)*r_4 + m_vignette_estimate.at(2) * r_6;\n        \n        vignette = pow(vignette,exponent);\n        \n        int y_pos = 245 - round(235*vignette);\n        if(y_pos < 0)\n            y_pos = 0;\n        if(y_pos > 255)\n            y_pos = 255;\n        \n        // Plot the vignetting\n        vignette_vis_image.at<cv::Vec3b>(y_pos,i)[0] = 0;\n        vignette_vis_image.at<cv::Vec3b>(y_pos,i)[1] = 0;\n        vignette_vis_image.at<cv::Vec3b>(y_pos,i)[2] = 255;\n        \n        // Plot the reference line for V = 1\n        //vignette_vis_image.at<cv::Vec3b>(10,i)[0] = 255;\n        //vignette_vis_image.at<cv::Vec3b>(10,i)[1] = 255;\n        //vignette_vis_image.at<cv::Vec3b>(10,i)[2] = 255;\n        \n        // Plot the reference line for V = 0\n        //vignette_vis_image.at<cv::Vec3b>(235,i)[0] = 255;\n        //vignette_vis_image.at<cv::Vec3b>(235,i)[1] = 255;\n        //vignette_vis_image.at<cv::Vec3b>(235,i)[2] = 255;\n        \n        // Plot the vignetting for DSO sequence 47\n         double dso_vignette_47 = 0.971 + 0.1891*r - 1.5958*r_2 + 1.4473*r_2*r - 0.5143* r_4;\n         y_pos = 245 - round(235*dso_vignette_47  );\n         vignette_vis_image.at<cv::Vec3b>(y_pos,i)[0] = 255;\n         vignette_vis_image.at<cv::Vec3b>(y_pos,i)[1] = 255;\n         vignette_vis_image.at<cv::Vec3b>(y_pos,i)[2] = 0;\n        \n//        // Plot the vignetting for artificial dataset\n//        double art_vignette =  0.9983-0.0204*r -0.2341*r_2 - 0.0463*r_2*r;\n//        y_pos = 245 - round(235*art_vignette  );\n//        vignette_vis_image.at<cv::Vec3b>(y_pos,i)[0] = 0;\n//        vignette_vis_image.at<cv::Vec3b>(y_pos,i)[1] = 255;\n//        vignette_vis_image.at<cv::Vec3b>(y_pos,i)[2] = 255;\n        \n    }\n    \n    cv::imshow(\"Estimated Vignetting\", vignette_vis_image);\n    cv::moveWindow(\"Estimated Vignetting\", 20,20+50+256);\n\n\n    // Visualize exposure times \n    // If GT data is available, the estimated exposure times will be aligned \n    // to the GT by computing an optimal alignment factor alignment_alpha\n    // If no GT data is available, the estimated exposure is simply scaled between [0,1]\n    int exp_image_height = 150;\n    int draw_spacing = 5;\n    double alignment_alpha = 1.0;\n    double max_exp = -10000.0;\n    double min_exp = 10000.0;\n    double top = 0;\n    double bot = 0;\n    int nr_block_images = m_optimization_block->getNrImages();\n    std::vector<double> block_exp_estimates;\n    std::vector<double> block_exp_gt;\n    for(int i = 0;i < nr_block_images;i++)\n    {\n        double estimated_exp = m_optimization_block->getExposureTime(i);\n        estimated_exp = pow(estimated_exp,exponent);\n        double gt_exp = m_optimization_block->getGTExposureTime(i);\n\n        // Store max and min exposure for normalization to [0,1] range \n        if(estimated_exp > max_exp)\n            max_exp = estimated_exp;\n        if(estimated_exp < min_exp)\n            min_exp = estimated_exp;\n\n        // Accumulate information for least square fit between GT and estimated exposure\n        top += estimated_exp*gt_exp;\n        bot += estimated_exp*estimated_exp;\n\n        block_exp_estimates.push_back(estimated_exp);\n        if(!(gt_exp < 0))\n            block_exp_gt.push_back(gt_exp);\n    }\n\n    if(block_exp_estimates.size() == block_exp_gt.size())\n    {\n        alignment_alpha = top/bot;\n    }\n    else\n    {\n        // Normalize estimated exposures between [0,1] if no gt information is available for alignment\n        for(int i = 0;i < block_exp_estimates.size();i++)\n        {\n            block_exp_estimates.at(i) = (block_exp_estimates.at(i)-min_exp)/(max_exp-min_exp);\n        }\n    }\n\n    for(int i = 0;i < block_exp_estimates.size();i++)\n    {\n        block_exp_estimates.at(i) *= alignment_alpha;\n    }\n\n    // Create exposure time canvas\n    draw_spacing = 20;\n    int block_exp_window_width = int(fmax(400,draw_spacing*block_exp_estimates.size()));\n    cv::Mat block_exposure_vis_image(exp_image_height,block_exp_window_width,CV_8UC3,cv::Scalar(0,0,0));\n\n    // Draw estimated exposure times as lines to graph\n    for(int i = 0;i < block_exp_estimates.size()-1;i++)\n    {\n        int drawing_y_exp_1 = exp_image_height - exp_image_height*(block_exp_estimates.at(i));\n        drawing_y_exp_1 = int(fmax(0,drawing_y_exp_1));\n        drawing_y_exp_1 = int(fmin(exp_image_height-1,drawing_y_exp_1));\n\n        int drawing_y_exp_2 = exp_image_height - exp_image_height*(block_exp_estimates.at(i+1));\n        drawing_y_exp_2 = int(fmax(0,drawing_y_exp_2));\n        drawing_y_exp_2 = int(fmin(exp_image_height-1,drawing_y_exp_2));\n\n        //draw exposure lines\n        cv::line(block_exposure_vis_image, cv::Point(draw_spacing*i,drawing_y_exp_1), cv::Point(draw_spacing*(i+1),drawing_y_exp_2), cv::Scalar(0,0,255));\n     }\n\n    // draw GT exposure line only if GT exposure data is available\n    if(block_exp_estimates.size() == block_exp_gt.size())\n    {\n        for(int i = 0;i < block_exp_estimates.size()-1;i++)\n        {\n            int drawing_y_gt_exp_1 = exp_image_height - exp_image_height * block_exp_gt.at(i);\n            drawing_y_gt_exp_1 = int(fmax(0,drawing_y_gt_exp_1));\n            drawing_y_gt_exp_1 = int(fmin(exp_image_height-1,drawing_y_gt_exp_1));\n\n            int drawing_y_gt_exp_2 = exp_image_height - exp_image_height * block_exp_gt.at(i+1);\n            drawing_y_gt_exp_2 = int(fmax(0,drawing_y_gt_exp_2));\n            drawing_y_gt_exp_2 = int(fmin(exp_image_height-1,drawing_y_gt_exp_2));\n\n            cv::line(block_exposure_vis_image, cv::Point(draw_spacing*i,drawing_y_gt_exp_1), cv::Point(draw_spacing*(i+1),drawing_y_gt_exp_2), cv::Scalar(255,255,0));\n        }\n    }   \n\n    cv::imshow(\"Estimated Keyframe Exposures (Backend)\", block_exposure_vis_image);\n    cv::moveWindow(\"Estimated Keyframe Exposures (Backend)\", 20+20+256,20+40+exp_image_height);\n\n\n    cv::waitKey(1);\n\n    // Return gamma that was used for visualization\n    return exponent;\n}\n\ndouble NonlinearOptimizer::getInverseResponseFixGamma(double* inverse_response_function)\n{\n    getInverseResponseRaw(inverse_response_function);\n    double gamma = determineGammaFixResponseAt(inverse_response_function, 127, 0.5);\n    \n    // Scale the inverse response\n    for(int i = 0;i < 256;i++)\n    {\n        inverse_response_function[i] = pow(inverse_response_function[i],gamma);\n    }\n    \n    // Return the obtained gamma factor\n    return gamma;\n}\n\nvoid NonlinearOptimizer::getInverseResponseRaw(double* inverse_response_function)\n{\n    //set boundaries of the inverse response\n    inverse_response_function[0] = 0;\n    inverse_response_function[255] = 1.0;\n    \n    // For each inverse response value i find s, such that response[s] = i\n    for(int i=1;i<255;i++)\n    {\n        bool inversion_found = false;\n        \n        for(int s=0;s<255;s++)\n        {\n            double response_s1 = applyResponse(s/255.0f);\n            double response_s2 = applyResponse((s+1)/255.0f);\n            if(response_s1 <= i && response_s2 >= i)\n            {\n                inverse_response_function[i] = s+(i - response_s1) / (response_s2-response_s1);\n                inverse_response_function[i] /= 255.0;\n                inversion_found = true;\n                break;\n            }\n        }\n        \n        if(!inversion_found)\n        {\n            std::cout << \"Error, no inversion found in getInverseResponse(..)\" << std::endl;\n        }\n    }\n}\n\ndouble NonlinearOptimizer::determineGammaFixResponseAt(double*inverse_response,int x,double y)\n{\n    double v_y = inverse_response[x];\n    double gamma = log(y) / log(v_y);\n    return gamma;\n}\n\nvoid NonlinearOptimizer::smoothResponse()\n{\n    // Get inverse response estimate, fixing the gamma value reasonably\n    double inverse_response[256];\n    double gamma = getInverseResponseFixGamma(inverse_response);\n    \n    // Scale up the inverse response to range [0,255]\n    for(int i = 0;i < 256;i++)\n    {\n        inverse_response[i] = 255*inverse_response[i];\n    }\n    \n    // Invert the inverse response to get the response again\n    double response_function[256];\n    response_function[0] = 0;\n    response_function[255] = 255;\n    \n    // For each response value i find s, such that inverse_response[s] = i\n    for(int i=1;i<255;i++)\n    {\n        for(int s=0;s<255;s++)\n        {\n            if(inverse_response[s] <= i && inverse_response[s+1] >= i)\n            {\n                response_function[i] = s+(i - inverse_response[s]) / (inverse_response[s+1]-inverse_response[s]);\n                break;\n            }\n        }\n    }\n    \n    // Fit the Grossberg parameters new to the acquired data\n    JacobianGenerator generator;\n    m_response_estimate = generator.fitGrossbergModelToResponseVector(response_function);\n    \n    // Scale vignette by gamma factor\n    double vfactors[100];\n    for(int r = 0;r < 100;r++)\n    {\n        double radius = r/100.0;\n        double vfactor = applyVignetting(radius);\n        vfactor = pow(vfactor,gamma);\n        vfactors[r] = vfactor;\n    }\n    \n    //[Note] Radiances of optimization should be scaled as well, but since these are not used anymore, its not done\n    int nr_block_images = m_optimization_block->getNrImages();\n    for(int k = 0;k < nr_block_images;k++)\n    {\n        double old_exposure = m_optimization_block->getExposureTime(k);\n        double new_exposure = pow(old_exposure,gamma);\n        m_optimization_block->setExposureTime(k,new_exposure);\n    }\n\n    // Fit new vignetting parameters in least square manner\n    cv::Mat LeftSide(3,3,CV_64F,0.0);\n    cv::Mat RightSide(3,1,CV_64F,0.0);\n    \n    int nr_bad_v = 0;\n    \n    for(int r = 0;r < 100;r++)\n    {\n        double w = 1.0;\n        if(r > 0)\n        {\n            double diff = vfactors[r] - vfactors[r-1];\n            if(diff > 0)\n            {\n                w = 0.5;\n                vfactors[r] = vfactors[r-1];\n                nr_bad_v++;\n            }\n        }\n\n        double radius = r/100.0;\n        double r2 = radius*radius;\n        double r4 = r2*r2;\n        double r6 = r4*r2;\n        double r8 = r4*r4;\n        double r10 = r6*r4;\n        double r12 = r6*r6;\n        \n        LeftSide.at<double>(0,0) += w*r4;\n        LeftSide.at<double>(0,1) += w*r6;\n        LeftSide.at<double>(0,2) += w*r8;\n        RightSide.at<double>(0,0) += (w*vfactors[r]*r2 - w*r2);\n        \n        LeftSide.at<double>(1,0) += w*r6;\n        LeftSide.at<double>(1,1) += w*r8;\n        LeftSide.at<double>(1,2) += w*r10;\n        RightSide.at<double>(1,0) += (w*vfactors[r]*r4 - w*r4);\n        \n        LeftSide.at<double>(2,0) += w*r8;\n        LeftSide.at<double>(2,1) += w*r10;\n        LeftSide.at<double>(2,2) += w*r12;\n        RightSide.at<double>(2,0) += (w*vfactors[r]*r6 - w*r6);\n    }\n    \n    cv::Mat Solution;\n    cv::solve(LeftSide, RightSide, Solution, cv::DECOMP_SVD);\n    \n    std::vector<double> solution_vig;\n    solution_vig.push_back(Solution.at<double>(0,0));\n    solution_vig.push_back(Solution.at<double>(1,0));\n    solution_vig.push_back(Solution.at<double>(2,0));\n    \n    m_vignette_estimate = solution_vig;\n}\n"
  },
  {
    "path": "src/NonlinearOptimizer.h",
    "content": "//\n//  NonlinearOptimizer.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#ifndef OnlinePhotometricCalibration_NonlinearOptimizer_h_\n#define OnlinePhotometricCalibration_NonlinearOptimizer_h_\n\n#include \"Database.h\"\n\n#include \"OptimizationBlock.h\"\n\n#include \"JacobianGenerator.h\"\n\n\n/**\n * Implements optimization functions in order to recover photometric parameters \n * from a data block (optimization block) of keyframes and tracked features\n * Can optimize jointly for exposure times E, vignette V, response function F (EVF optimization), fixing radiances\n * Can optimize for radiances, fixing exposure, response and vignette\n */\n\nclass NonlinearOptimizer\n{\n        \npublic:\n    \n    /**\n     * Number of response and vignette parameters to optimize, must be 4 for response, 3 for vignette\n     */\n    const int C_NR_RESPONSE_PARAMS = 4;\n    const int C_NR_VIGNETTE_PARAMS = 3;\n    \n    /**\n     * Safe zone size is the number of frames in the database in the end that should not \n     * be read by the opt. backend because it still might be modified by the tracking.\n     *\n     * @param keyframe_spacing Number of frames that keyframes are apart (relative to all input frames)\n     * @param database Handle to the database\n     * @param safe_zone_size Don't take images from the first safe_zone_size frames of the database since they are still\n     *        being processed by exposure optimization\n     * @param min_keyframes_valid Minimum amount of keyframes a feature should be present in in order to include it to optimization\n     * @param patch_size Size of tracking patch extracted around features\n     */\n    NonlinearOptimizer(int keyframe_spacing, Database* database,int safe_zone_size,int min_keyframes_valid,int patch_size);\n    \n    /**\n     * Extract new optimization data from the database\n     */\n    bool extractOptimizationBlock();\n    \n    /**\n     * Optimize jointly for exposure times E, Vignette V, response F\n     * Keep radiance estimates fixed for tracked points\n     * \n     * @param show_debug_prints Print some debug information such as reduction of the energy\n     */\n    double evfOptimization(bool show_debug_prints);\n    \n    /**\n     * Optimize for irradiance values, keeping EVF estimate fixed\n     */\n    double radianceFullOptimization();\n    \n    /**\n     * Read the current response + vignette estimates from the database and store them to the local variables of the object\n     */\n    void fetchResponseVignetteFromDatabase();\n    \n    /**\n     * Current estimate of response and vignette parameters\n     */\n    std::vector<double> m_response_estimate;\n    std::vector<double> m_vignette_estimate;\n    \n    /**\n     * Show inverse response + vignette estimate\n     * Returns the exponential factor that was used to align data to the ground truth\n     */\n    double visualizeOptimizationResult(double* inverse_response);\n    \n    /**\n     * Handle exponential ambiguity by fixing the response in one point, also adjusting the vignette estimate\n     */\n    void smoothResponse();\n    \n    /**\n     * Get the inverse response function vector without gamma optimization!\n     */\n    void getInverseResponseRaw(double* inverse_response_function);\n    \n    /**\n     * Visualize passed information to the optimization block\n     * @param img_width  Width of input images\n     * @param img_height Height of input images\n     */\n    void visualizeOptimizationBlock(int img_width,int img_height)\n    {\n        m_optimization_block->visualizeBlockInformation(img_width,img_height);\n    }\n    \n    /**\n     * Store inverse response function here, used for fast plotting\n     */\n    double* m_raw_inverse_response;\n    \nprivate:\n    \n    /**\n     * Spacing of keyframes used for optimization relative to all available input images\n     */\n    int m_keyframe_spacing;\n    \n    /**\n     * Handle to database\n     */\n    Database* m_database;\n    \n    /**\n     * Number of most recent images that should not be used for optimization since they are still modified by the tracker\n     */\n    int m_safe_zone_size;\n    \n    /**\n     * Size of tracking patches\n     */\n    int m_patch_size;\n    \n    /**\n     * Min. amount of keyframes a feature must be valid to be added to opt. block\n     */\n    int m_min_keyframes_valid;\n    \n    /**\n     * Current optimization data for the optimizer\n     */\n    OptimizationBlock* m_optimization_block;\n    \n    /**\n     * Compute photometric error O - f(eVL) for one point in one image\n     */\n    double getResidualValue(double O,double I,double r,double e);\n    \n    /**\n     * Compute total photometric error for entire optimization data\n     * \n     * @param total_error Summed up absolute residual error\n     * @param avg_error Average absolute residual error per point\n     */\n    void getTotalResidualError(double& total_error,double& avg_error);\n    \n    /**\n     * Evaluate the response function at x based on current estimate within the optimizer\n     */\n    double applyResponse(double x);\n    \n    /**\n     * Evaluate the vignetting factor at radius r based on current estimate within the optimizer\n     */\n    double applyVignetting(double r);\n    \n    /**\n     * Get the residual error for point p, subpoint r (in the patch region)\n     */\n    double getResidualErrorPoint(OptimizedPoint p,int r);\n    \n    /**\n     * Determine the gamma factor that is needed to fix the current inverse response estimate at (x,y)\n     */\n    double determineGammaFixResponseAt(double* inverse_response,int x,double y);\n    \n    /**\n     * Get the inverse response function vector, fixing it for a reasonable gamma factor (which is also returned)\n     */\n    double getInverseResponseFixGamma(double* inverse_response_function);\n    \n};\n\n#endif // include guard\n"
  },
  {
    "path": "src/OptimizationBlock.cpp",
    "content": "//\n//  OptimizationBlock.cpp\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 17.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#include \"OptimizationBlock.h\"\n\nOptimizationBlock::OptimizationBlock(int patch_size)\n{\n    m_patch_size = patch_size;\n    m_nr_patch_points =(2*m_patch_size+1)*(2*m_patch_size+1);\n}\n\nvoid OptimizationBlock::addOptimizationPoint(OptimizedPoint p)\n{\n    m_optimized_points.push_back(p);\n}\n\n/**\n * A residual arises from a tracked point location within one image, comparing the \n * photometric image formation process to the actual output intensity\n * Residual r = O - f(e V(r) L)\n */\nint OptimizationBlock::getNrResiduals()\n{\n    int nr_residuals = 0;\n    \n    for(int i = 0;i < m_optimized_points.size();i++)\n    {\n        int nr_images = m_optimized_points.at(i).num_images_valid;\n        \n        nr_residuals += (nr_images * m_nr_patch_points);\n    }\n    \n    return nr_residuals;\n}\n\nvoid OptimizationBlock::visualizeBlockInformation(int image_width,int image_height)\n{\n    // Iterate each key frame image\n    for(int i = 0;i < m_exposure_times.size();i++)\n    {\n        cv::Mat debug_image(image_height,image_width,CV_8UC3,cv::Scalar(0,0,0));\n\n        // Project each tracked point patch into the debug output image\n        for(int p = 0;p < m_optimized_points.size();p++)\n        {\n            // Is p valid in image i? if yes -> draw its output pixel information on the image\n            int start_img = m_optimized_points.at(p).start_image_idx;\n            int end_img = start_img + m_optimized_points.at(p).num_images_valid - 1;\n            \n            if(i < start_img || i > end_img)\n            {\n                continue;\n            }\n            \n            // Drawing happens here\n            cv::Point2f location = m_optimized_points.at(p).xy_image_locations.at(i-start_img);\n            std::vector<double> outputs = m_optimized_points.at(p).output_intensities.at(i-start_img);\n            \n            int output_index = 0;\n            for(int x = -m_patch_size;x <= m_patch_size;x++)\n            {\n                for(int y = -m_patch_size;y <= m_patch_size;y++)\n                {\n                    int x_location = (int)(location.x + x);\n                    int y_location = (int)(location.y + y);\n                                    \n                    \n                    double output = outputs.at(output_index);\n                    output_index++;\n                    debug_image.at<cv::Vec3b>(y_location,x_location)[0] = (uchar)output;\n                    debug_image.at<cv::Vec3b>(y_location,x_location)[1] = (uchar)output;\n                    debug_image.at<cv::Vec3b>(y_location,x_location)[2] = (uchar)output;\n                }\n            }\n            \n            \n        }\n        \n        // For each pixel where no tracking information is present, draw the original image data around (in different color)\n        // in order to check for consistency of the tracking data with the original images\n        cv::Mat original_image = m_original_images.at(i);\n\n        for(int r = 0;r < image_height;r++)\n        {\n            for(int c = 0; c < image_width;c++)\n            {\n                if(debug_image.at<cv::Vec3b>(r,c)[0] == 0)\n                {\n                    debug_image.at<cv::Vec3b>(r,c)[0] = original_image.at<uchar>(r,c);\n                    debug_image.at<cv::Vec3b>(r,c)[1] = original_image.at<uchar>(r,c);\n                    debug_image.at<cv::Vec3b>(r,c)[2] = 0;\n                }\n            }\n        }\n        \n        cv::resize(debug_image, debug_image, cv::Size(640,480));\n        \n        cv::imshow(\"DEB\", debug_image);\n        cv::waitKey(0);\n    }\n}\n"
  },
  {
    "path": "src/OptimizationBlock.h",
    "content": "//\n//  OptimizationBlock.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 17.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#ifndef OnlinePhotometricCalibration_OptimizationBlock_h_\n#define OnlinePhotometricCalibration_OptimizationBlock_h_\n\n#include \"StandardIncludes.h\"\n\n#include \"OptimizedPoint.h\"\n\n/**\n * An optimization block takes up the entire optimization data necessary \n * to optimize for photometric parameters f,V,e,L in the backend.\n */\n\nclass OptimizationBlock\n{\npublic:\n    \n    /**\n     * Constructor, takes the patch size of the tracking patches in pixels\n     */\n    OptimizationBlock(int patch_size);\n    \n    /**\n     * Add data to the optimization block\n     * An optimization point corresponds to a feature tracked through several images\n     * together with associated information\n     */\n    void addOptimizationPoint(OptimizedPoint p);\n    \n    /**\n     * Set the exposure time estimate for image at index i\n     */\n    void setExposureTime(int i,double exp_time)\n    {\n        m_exposure_times.at(i) = exp_time;\n    }\n    \n    /**\n     * Push back a new exposure time\n     */\n    void pushExposureTime(double exp_time,double gt_exp_time)\n    {\n        m_exposure_times.push_back(exp_time);\n        m_gt_exposure_times.push_back(gt_exp_time);\n    }\n    \n    /**\n     * Clear exposure time vector\n     */\n    void deleteExposureTimes()\n    {\n        m_exposure_times.clear();\n        m_gt_exposure_times.clear();\n    }\n    \n    /**\n     * Get the exposure time of image i\n     */\n    double getExposureTime(int i){return m_exposure_times.at(i);}\n\n    /**\n    * Get the ground truth exposure time of image i\n    */\n    double getGTExposureTime(int i){return m_gt_exposure_times.at(i);}\n\n    /**\n     * Get the number of residuals within this optimization block\n     */\n    int getNrResiduals();\n    \n    /**\n     * Get the number of images within the optimization block\n     */\n    int getNrImages()\n    {\n        return static_cast<int>(m_exposure_times.size());\n    }\n    \n    /**\n     * Get optimized point information\n     */\n    std::vector<OptimizedPoint>* getOptimizedPoints()\n    {\n        return &m_optimized_points;\n    }\n    \n    /**\n     * Add the original image to the optimization block\n     * (Image data only needed for later correction and visual verification of the optimization result)\n     */\n    void addImage(cv::Mat image)\n    {\n        m_original_images.push_back(image);\n    }\n    \n    /**\n     * Return the original image at index i\n     */\n    cv::Mat getImage(int i)\n    {\n        return m_original_images.at(i);\n    }\n\n    /**\n     * Visualize the block (for debugging only)\n     * (Shows the original images + projects the tracked image patches inside)\n     */\n    void visualizeBlockInformation(int image_width,int image_height);\n    \n    \nprivate:\n    \n    /**\n     * Contains all information necessary for backend optimization\n     * Is extracted from the database\n     */\n    std::vector<OptimizedPoint> m_optimized_points;\n    \n    /**\n     * Size of tracked patches\n     */\n    int m_patch_size;\n    \n    /**\n     * Total number of points in one patch (equals (2*m_patch_size+1)^2)\n     */\n    int m_nr_patch_points;\n    \n    /**\n     * Exposure time estimates of the keyframes used for backend optimization\n     */\n    std::vector<double> m_exposure_times;\n\n    /**\n     * Ground truth exposure times for the keyframes used for backend optimization\n     */\n    std::vector<double> m_gt_exposure_times;\n    \n    /**\n     * Original images used for this block\n     * (Only stored here for debugging, visual verification)\n     */\n    std::vector<cv::Mat> m_original_images;\n};\n\n#endif // include guard\n"
  },
  {
    "path": "src/OptimizedPoint.h",
    "content": "//\n//  OptimizedPoint.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 17.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#ifndef OnlineCalibration_OptimizedPoint_h\n#define OnlineCalibration_OptimizedPoint_h\n\n#include <vector>\n\n/**\n * Struct storing the data for one optimized feature within this optimization block\n */\nclass OptimizedPoint\n{\npublic:\n    \n    /**\n     * Estimated radiances of this point (patch)\n     */\n    std::vector<double> radiances;\n    \n    /**\n     * First keyframe this point is valid\n     */\n    int start_image_idx;\n    \n    /**\n     * Number of keyframes this point is valid in\n     */\n    int num_images_valid;\n    \n    /**\n     * Output intensities of the original input image\n     */\n    std::vector<std::vector<double> > output_intensities;\n    \n    /**\n     * Normalized radii of the point in its keyframe images\n     */\n    std::vector<double> radii;\n    \n    /**\n     * Gradient based weights used for optimization\n     */\n    std::vector<std::vector<double> > grad_weights;\n    \n    /**\n     * x,y locations in the keyframe images\n     */\n    std::vector<cv::Point2f>  xy_image_locations;\n        \n    /**\n     * Residual error for this point after optimization\n     */\n    double optimization_error;\n};\n\n#endif\n"
  },
  {
    "path": "src/RapidExposureTimeEstimator.cpp",
    "content": "//\n//  RapidExposureTimeEstimator.cpp\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#include \"RapidExposureTimeEstimator.h\"\n\nRapidExposureTimeEstimator::RapidExposureTimeEstimator(int window_size,Database* database)\n{\n    m_window_size = window_size;\n    m_database    = database;\n}\n\ndouble RapidExposureTimeEstimator::estimateExposureTime()\n{\n    /*\n     * A minimum number of m_window_size frames is required for exposure time estimation\n     * Not enough frames available yet, fix exposure time to 1.0\n     */\n    if(m_database->m_tracked_frames.size() < m_window_size)\n    {\n        return 1.0;\n    }\n    \n    /*\n     * Iterate all features in the most current frame backwards for m_window_size images\n     */\n    std::vector<Feature*> features_last_frame = m_database->m_tracked_frames.at(m_database->m_tracked_frames.size()-1).m_features;\n    \n    double e_estimate = 0.0;\n    double nr_estimates = 0;\n    \n    for(int i = 0;i < features_last_frame.size();i++)\n    {\n        //skip newly extracted features (no link to previous frame, cannot be used for exp. estimation)\n        if(features_last_frame.at(i)->m_prev_feature == NULL)\n            continue;\n        \n        // average the radiance estimate for this feature from the last m_window_size frames\n        std::vector<double> radiances = features_last_frame.at(i)->m_prev_feature->m_radiance_estimates;\n        \n        // count the number of features used to estimate the point radiances\n        int nr_features_used  = 1;\n        \n        // Iterate the rest of the m_window_size features in the other images to accumulate radiance information\n        Feature* curr_feature = features_last_frame.at(i)->m_prev_feature;\n\n        // Todo: k should start from 1?\n        for(int k = 0;k < m_window_size;k++)\n        {\n            // Go one feature backwards\n            curr_feature = curr_feature->m_prev_feature;\n            \n            // No tracking information anymore -> break\n            if(curr_feature == NULL)\n                break;\n            \n            // Tracking information is available\n            nr_features_used++;\n            \n            // Accumulate radiance information\n            std::vector<double> radiances_temp = curr_feature->m_radiance_estimates;\n            for(int r = 0;r < radiances_temp.size();r++)\n            {\n                radiances.at(r) += radiances_temp.at(r);\n            }\n        }\n        \n        // Average radiance estimates for this feature\n        for(int r = 0;r < radiances.size();r++)\n        {\n            radiances.at(r) /= nr_features_used;\n        }\n  \n        // Image output values (corrected by vignette + response but NOT yet by exposure) taken from the current frame\n        std::vector<double> outputs = features_last_frame.at(i)->m_radiance_estimates;\n        \n        // Fetch gradient values for the tracked feature\n        std::vector<double> grad_values = features_last_frame.at(i)->m_gradient_values;\n        \n        // Estimate one exposure ratio for each of the tracking patch points\n        for(int k = 0;k < radiances.size();k++)\n        {\n            // Weight the estimate depending on the gradient (high gradient = low confidence)\n            double weight = 1.0 - grad_values.at(k)/125;\n            if(weight < 0)weight = 0;\n            \n            // Avoid division by 0 for underexposed points\n            if(fabs(radiances.at(k)) < 0.0001)\n                continue;\n            \n            // Estimate the exposure ratio\n            double curr_e_estimate = (outputs.at(k) / radiances.at(k));\n            \n            // Skip spurious implausible estimates (such as radiance = 0)\n            if(curr_e_estimate < 0.001 || curr_e_estimate > 100)\n                continue;\n            \n            // Accumulate estimation information\n            e_estimate   += weight*curr_e_estimate;\n            nr_estimates += weight;\n        }\n    }\n    \n    // This should not happen, just for safety\n    if(nr_estimates == 0)\n        return 1.0;\n    \n    // Average the exposure information for each patch\n    // [TODO] Maybe use a more robust way to select an exposure time in the presence of severe noise\n    double final_exp_estimate = e_estimate / nr_estimates;\n\n    // Todo: this part is confusing...\n    // Handle exposure time drift\n    // If corrected images are highly over/underexposes, push exposure times\n    // Todo: change to use ref\n    cv::Mat corrected_image = m_database->m_tracked_frames.at(m_database->m_tracked_frames.size()-1).m_image_corrected;\n    cv::Mat original_image  = m_database->m_tracked_frames.at(m_database->m_tracked_frames.size()-1).m_image;\n    corrected_image /= final_exp_estimate;\n    \n    // Count the number of pixels that are under/overexposed in the corrected image\n    // And not under/overexposed in the original input image\n    int nr_underexposed = 0;\n    int nr_overexposed  = 0;\n    int nr_pixels = corrected_image.rows*corrected_image.cols;\n    \n    for(int r = 0;r < corrected_image.rows;r++)\n    {\n        for(int c = 0;c < corrected_image.cols;c++)\n        {\n            uchar image_value = corrected_image.at<uchar>(r,c);\n            uchar orig_value  = original_image.at<uchar>(r,c);\n            if(image_value < 15 && orig_value >= 30)\n            {\n                nr_underexposed++;\n            }\n            else if(image_value > 240 && orig_value <= 200)\n            {\n                nr_overexposed++;\n            }\n        }\n    }\n    \n    double percentage_underexposed = nr_underexposed / (1.0*nr_pixels);\n    double percentage_overexposed  = nr_overexposed / (1.0*nr_pixels);\n    \n    //std::cout << \"UNDER: \" << percentage_underexposed << \" -- OVER: \" << percentage_overexposed << std::endl;\n    \n    // If the amount of over/underexposed images are too large, correct the exposure time drift\n    if(percentage_overexposed > 0.05)\n    {\n        final_exp_estimate += 0.03;\n    }\n    else if(percentage_underexposed > 0.03)\n    {\n        final_exp_estimate -= 0.05;\n    }\n    \n    return final_exp_estimate;\n}\n"
  },
  {
    "path": "src/RapidExposureTimeEstimator.h",
    "content": "//\n//  RapidExposureTimeEstimator.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#ifndef OnlinePhotometricCalibration_RapidExposureTimeEstimator_h_\n#define OnlinePhotometricCalibration_RapidExposureTimeEstimator_h_\n\n#include \"StandardIncludes.h\"\n\n#include \"Database.h\"\n\nclass RapidExposureTimeEstimator\n{\n    \npublic:\n    \n    /**\n     * Constructor\n     * @param window_size Number of frames to use for exposure optimization\n     * @param database Handle to the database\n     */\n    RapidExposureTimeEstimator(int window_size,Database* database);\n    \n    /**\n     * Estimate a fast exposure time for the latest frame in the database\n     * The tracks of the last m_window_size frames are considered for exposure time optimization\n     * Response and vignette and considered fixed, based on the current estimate in the database\n     */\n    double estimateExposureTime();\n    \nprivate:\n    \n    /**\n     * Handle to the database\n     */\n    Database* m_database;\n    \n    /**\n     * How many frames to take into account for exposure time estimation\n     */\n    int m_window_size;\n};\n\n#endif // include guard\n"
  },
  {
    "path": "src/ResponseModel.h",
    "content": "//\n//  ResponseModel.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#ifndef OnlinePhotometricCalibration_ResponseModel_h_\n#define OnlinePhotometricCalibration_ResponseModel_h_\n\n#include \"StandardIncludes.h\"\n\n/**\n * Represents data needed for the Grossberg camera response model using the first 4 basis functions\n */\nclass ResponseModel\n{\npublic:\n    \n    /**\n     * Constructor\n     * \n     * Initialize response model with Grossberg parameters that correspond to identity response\n     * Also initialize discretely sampled inverse response vector for 256 output intensity values\n     */\n    ResponseModel()\n    {\n        m_inverse_response_vector.clear();\n        for(int i = 0;i < 256;i++)\n        {\n            m_inverse_response_vector.push_back(i);\n        }\n        \n        m_grossberg_parameters.clear();\n        m_grossberg_parameters.push_back(6.1);\n        m_grossberg_parameters.push_back(0.0);\n        m_grossberg_parameters.push_back(0.0);\n        m_grossberg_parameters.push_back(0.0);\n    }\n    \n    /**\n     * Apply inverse response function to output intensity \n     *\n     * @param o Output intensity {0,1,...,255}\n     * @returns Inverse response applied to o i.e. f^(-1)(o)\n     */\n    double removeResponse(int o)\n    {\n        return m_inverse_response_vector.at(o);\n    }\n    \n    /**\n     * Overwrite Grossberg parameter vector\n     */\n    // Todo: change parameter to ref\n    void setGrossbergParameterVector(std::vector<double> params)\n    {\n        m_grossberg_parameters = params;\n    }\n    \n    /**\n     * Fetch Grossberg parameter vector\n     */\n    std::vector<double> getResponseEstimate()\n    {\n        return m_grossberg_parameters;\n    }\n    \n    /**\n     * Overwrite inverse response function vector\n     */\n    void setInverseResponseVector(double* new_inverse)\n    {\n        for(int i = 0;i < 256;i++)\n        {\n            m_inverse_response_vector.at(i) = 255.0 * new_inverse[i];\n        }\n    }\n\nprivate:\n    \n    /**\n     * Discrete vector of 256 values representing the sampled inverse camera response function\n     * for the 256 image output intensities.\n     * This vector is stored here for efficiency since it is not straightforward to invert the response \n     * given only the Grossberg parameters.\n     */\n    std::vector<double> m_inverse_response_vector;\n    \n    /**\n     * Grossberg parameter vector (4 values) representing the camera response\n     */\n    std::vector<double> m_grossberg_parameters;\n\n};\n\n#endif // include guard\n"
  },
  {
    "path": "src/StandardIncludes.h",
    "content": "//\n//  OpencvIncludes.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n/**\n * Defines all kinds of standard includes that are used in most of the classes\n */\n\n#ifndef OnlineCalibration_StandardIncludes_h\n#define OnlineCalibration_StandardIncludes_h\n\n/**\n * Opencv includes\n */\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/video/tracking.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n/**\n * Standard library includes\n */\n#include <iostream>\n#include <stdio.h>\n#include <stdlib.h>\n#include <pthread.h>\n#include <vector>\n#include <fstream>\n\n#endif\n"
  },
  {
    "path": "src/Tracker.cpp",
    "content": "//\n//  Tracker.cpp\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#include \"Tracker.h\"\n\nTracker::Tracker(int patch_size,int nr_active_features,int nr_pyramid_levels,Database* database)\n{\n    // Simply store all passed arguments to object\n    m_patch_size = patch_size;\n    m_max_nr_active_features = nr_active_features;\n    m_nr_pyramid_levels = nr_pyramid_levels;\n    m_database = database;\n}\n\nvoid Tracker::trackNewFrame(cv::Mat input_image,double gt_exp_time)\n{\n    // Compute gradient (necessary for weighting factors)\n    // Todo: move to class member\n    cv::Mat gradient_image;\n    computeGradientImage(input_image, gradient_image);\n    \n    // Correct the input image based on the current response and vignette estimate (exposure time not known yet)\n    // Todo: move to class member\n    cv::Mat corrected_frame = input_image.clone();\n    photometricallyCorrectImage(corrected_frame);\n \n    // Empty database -> First frame - extract features and push them back\n    if(m_database->m_tracked_frames.size() == 0)\n    {\n        initialFeatureExtraction(input_image,gradient_image,gt_exp_time);\n        return;\n    }\n    \n    // Database not empty\n    \n    // Fetch the old active feature locations together with their image\n    std::vector<cv::Point2f> feature_locations = m_database->fetchActiveFeatureLocations();\n    cv::Mat last_frame = m_database->fetchActiveImage();\n    \n    // Track the feature locations forward using gain robust KLT\n    std::vector<cv::Point2f> tracked_points_new_frame;\n    std::vector<unsigned char> tracked_point_status;\n    std::vector<float> tracking_error_values;\n\n    GainRobustTracker gain_robust_klt_tracker(C_KLT_PATCH_SIZE,C_NR_PYRAMID_LEVELS);\n    std::vector<int> tracked_point_status_int;\n    gain_robust_klt_tracker.trackImagePyramids(last_frame,\n                                               input_image,\n                                               feature_locations,\n                                               tracked_points_new_frame,\n                                               tracked_point_status_int);\n    for(int i = 0;i < tracked_point_status_int.size();i++)\n    {\n        if(tracked_point_status_int.at(i) == 0)\n        {\n            tracked_point_status.push_back(0);\n        }\n        else\n        {\n            tracked_point_status.push_back(1);\n        }\n    }\n     \n    // Bidirectional tracking filter: Track points backwards and make sure its consistent\n    std::vector<cv::Point2f> tracked_points_backtracking;\n    std::vector<unsigned char> tracked_point_status_backtracking;\n    std::vector<float> tracking_error_values_backtracking;\n    GainRobustTracker gain_robust_klt_tracker_2(C_KLT_PATCH_SIZE,C_NR_PYRAMID_LEVELS);\n    std::vector<int> tracked_point_status_int2;\n    gain_robust_klt_tracker_2.trackImagePyramids(input_image,\n                                                 last_frame,\n                                                 tracked_points_new_frame,\n                                                 tracked_points_backtracking,\n                                                 tracked_point_status_int2);\n    for(int i = 0;i < tracked_point_status_int.size();i++)\n    {\n        if(tracked_point_status_int.at(i) == 0)\n        {\n            tracked_point_status_backtracking.push_back(0);\n        }\n        else\n        {\n            tracked_point_status_backtracking.push_back(1);\n        }\n    }\n    \n    // Tracked points from backtracking and old frame should be the same -> check and filter by distance\n    for(int p = 0;p < feature_locations.size();p++)\n    {\n        if(tracked_point_status.at(p) == 0) // Point already set invalid by forward tracking -> ignore\n            continue;\n        \n        if(tracked_point_status_backtracking.at(p) == 0) // Invalid in backtracking -> set generally invalid\n            tracked_point_status.at(p) = 0;\n        \n        // Valid in front + backtracked images -> calculate displacement error\n        cv::Point2d d_p = feature_locations.at(p) - tracked_points_backtracking.at(p);\n        double distance = sqrt(d_p.x*d_p.x + d_p.y*d_p.y);\n        \n        if(distance > C_FWD_BWD_TRACKING_THRESH)\n        {\n            tracked_point_status.at(p) = 0;\n        }\n    }\n    \n    Frame frame;\n    frame.m_image = input_image;\n    frame.m_image_corrected = corrected_frame;\n    frame.m_gradient_image = gradient_image;\n    frame.m_exp_time = 1.0;\n    frame.m_gt_exp_time = gt_exp_time;\n\n    // Reject features that have been tracked to the side of the image\n    // Todo: validity_vector can be combined with tracked_point_status to one vector?\n    std::vector<int> validity_vector = checkLocationValidity(tracked_points_new_frame);\n    \n    int nr_pushed_features = 0;\n    for(int i = 0;i < feature_locations.size();i++)\n    {\n        // If the feature became invalid don't do anything, otherwise if its still valid, push it to the database and set the feature pointers\n        if(tracked_point_status.at(i) == 0 || validity_vector.at(i) == 0)\n            continue;\n        \n        // Feature is valid, set its data and push it back\n        Feature* f = new Feature();\n        // Todo: remove os, is, gs\n        std::vector<double> os = bilinearInterpolateImagePatch(input_image,tracked_points_new_frame.at(i).x,tracked_points_new_frame.at(i).y);\n        f->m_output_values = os;\n        std::vector<double> is = bilinearInterpolateImagePatch(corrected_frame,tracked_points_new_frame.at(i).x,tracked_points_new_frame.at(i).y);\n        f->m_radiance_estimates = is;\n        std::vector<double> gs = bilinearInterpolateImagePatch(gradient_image, tracked_points_new_frame.at(i).x, tracked_points_new_frame.at(i).y);\n        f->m_gradient_values = gs;\n        f->m_xy_location = tracked_points_new_frame.at(i);\n        f->m_next_feature = NULL;\n        f->m_prev_feature =  m_database->m_tracked_frames.at(m_database->m_tracked_frames.size()-1).m_features.at(i);\n        \n        m_database->m_tracked_frames.at(m_database->m_tracked_frames.size()-1).m_features.at(i)->m_next_feature = f;\n        \n        frame.m_features.push_back(f);\n        nr_pushed_features++;\n    }\n    \n    m_database->m_tracked_frames.push_back(frame);\n    \n    // Extract new features\n    std::vector<cv::Point2f> new_feature_locations = extractFeatures(input_image,m_database->fetchActiveFeatureLocations());\n    std::vector<int> new_validity_vector = checkLocationValidity(new_feature_locations);\n    for(int p = 0;p < new_feature_locations.size();p++)\n    {\n        // Skip invalid points (too close to the side of image)\n        if(new_validity_vector.at(p) == 0)\n            continue;\n        \n        // Push back new feature information\n        Feature* f = new Feature();\n        f->m_xy_location = new_feature_locations.at(p);\n        f->m_next_feature = NULL;\n        f->m_prev_feature = NULL;\n        // Todo: remove os, is, gs\n        std::vector<double> os = bilinearInterpolateImagePatch(input_image,new_feature_locations.at(p).x,new_feature_locations.at(p).y);\n        f->m_output_values = os;\n        std::vector<double> is = bilinearInterpolateImagePatch(corrected_frame,new_feature_locations.at(p).x,new_feature_locations.at(p).y);\n        f->m_radiance_estimates = is;\n        std::vector<double> gs = bilinearInterpolateImagePatch(gradient_image, new_feature_locations.at(p).x, new_feature_locations.at(p).y);\n        f->m_gradient_values = gs;\n        m_database->m_tracked_frames.at(m_database->m_tracked_frames.size()-1).m_features.push_back(f);\n    }\n}\n\n// Todo: change both types to reference\nstd::vector<cv::Point2f> Tracker::extractFeatures(cv::Mat frame,std::vector<cv::Point2f> old_features)\n{\n    std::vector<cv::Point2f> new_features;\n\n    // No new features have to be extracted\n    if(old_features.size() >= m_max_nr_active_features)\n    {\n        return new_features;\n    }\n    \n    int nr_features_to_extract = static_cast<int>(m_max_nr_active_features-old_features.size());\n    \n    // Build spatial distribution map to check where to extract features\n    int cells_r = 10;\n    int cells_c = 10;\n    \n    double im_width  = m_database->m_image_width;\n    double im_height = m_database->m_image_height;\n        \n    int cell_height = floor(im_height / cells_r);\n    int cell_width  = floor(im_width  / cells_c);\n\n    // Todo: change to class member\n    int pointDistributionMap[cells_r][cells_c];\n    for(int r = 0;r < cells_r;r++)\n    {\n        for(int c = 0;c < cells_c;c++)\n        {\n            pointDistributionMap[r][c] = 0;\n        }\n    }\n    \n    // Build the point distribution map to check where features need to be extracted mostly\n    for(int p = 0;p < old_features.size();p++)\n    {\n        double x_value = old_features.at(p).x;\n        double y_value = old_features.at(p).y;\n        \n        int c_bin = x_value / cell_width;\n        if(c_bin >= cells_c)\n            c_bin = cells_c - 1;\n        \n        int r_bin = y_value / cell_height;\n        if(r_bin >= cells_r)\n            r_bin = cells_r - 1;\n        \n        pointDistributionMap[r_bin][c_bin]++;\n    }\n    \n    // Identify empty cells\n    std::vector<int> empty_row_indices;\n    std::vector<int> empty_col_indices;\n    \n    for(int r = 0;r < cells_r;r++)\n    {\n        for(int c = 0;c < cells_c;c++)\n        {\n            if(pointDistributionMap[r][c] == 0)\n            {\n                empty_row_indices.push_back(r);\n                empty_col_indices.push_back(c);\n            }\n        }\n    }\n\n    // Todo: empty_col_indices might be 0!!!\n    // Todo: Another bad case is: only one cell is empty and all other cells have only 1 feature inside,\n    // Todo: then all the features to extract will be extracted from the single empty cell.\n    int points_per_cell = ceil(nr_features_to_extract / (empty_col_indices.size()*1.0));\n    \n    // Extract \"points per cell\" features from each empty cell\n    for(int i = 0;i < empty_col_indices.size();i++)\n    {\n        // Select random cell from where to extract features\n        int random_index = rand() % empty_row_indices.size();\n        \n        // Select row and col\n        int selected_row = empty_row_indices.at(random_index);\n        int selected_col = empty_col_indices.at(random_index);\n        \n        // Define the region of interest where to detect a feature\n        cv::Rect ROI(selected_col * cell_width,selected_row * cell_height,cell_width,cell_height);\n        \n        // Extract features from this frame\n        cv::Mat frame_roi = frame(ROI);\n        \n        // Extract features\n        std::vector<cv::Point2f> good_corners;\n        cv::goodFeaturesToTrack(frame_roi,\n                                good_corners,\n                                points_per_cell,\n                                0.01,\n                                7,\n                                cv::Mat(),\n                                7,\n                                false,\n                                0.04);\n        \n        // Add the strongest \"points per cell\" features from this extraction\n        for(int k = 0;k < good_corners.size();k++)\n        {\n            if(k == points_per_cell)\n                break;\n            \n            // Add the offset to the point location\n            cv::Point2f point_location = good_corners.at(k);\n            point_location.x += selected_col*cell_width;\n            point_location.y += selected_row*cell_height;\n            \n            new_features.push_back(point_location);\n        }\n    }\n    \n    return new_features;\n}\n\n/**\n * Note: For this function, it is assumed that x,y lies within the image!\n */\ndouble Tracker::bilinearInterpolateImage(cv::Mat image,double x,double y)\n{\n    double floor_x = std::floor(x);\n    double ceil_x  = std::ceil(x);\n    \n    double floor_y = std::floor(y);\n    double ceil_y  = std::ceil(y);\n    \n    // Normalize x,y to be in [0,1)\n    double x_normalized = x - floor_x;\n    double y_normalized = y - floor_y;\n    \n    // Get bilinear interpolation weights\n    double w1 = (1-x_normalized)*(1-y_normalized);\n    double w2 = x_normalized*(1-y_normalized);\n    double w3 = (1-x_normalized)*y_normalized;\n    double w4 = x_normalized*y_normalized;\n    \n    // Evaluate image locations\n    double i1 = static_cast<double>(image.at<uchar>(floor_y,floor_x));\n    double i2 = static_cast<double>(image.at<uchar>(floor_y,ceil_x));\n    double i3 = static_cast<double>(image.at<uchar>(ceil_y,floor_x));\n    double i4 = static_cast<double>(image.at<uchar>(ceil_y,ceil_x));\n    \n    // Interpolate the result\n    return w1*i1 + w2*i2 + w3*i3 + w4*i4;\n}\n\n/**\n * Note: For this function, it is assumed that x,y lies within the image!\n */\nstd::vector<double> Tracker::bilinearInterpolateImagePatch(cv::Mat image,double x,double y)\n{\n    std::vector<double> result;\n    \n    for(int x_offset = -m_patch_size;x_offset <= m_patch_size;x_offset++)\n    {\n        for(int y_offset = -m_patch_size;y_offset <= m_patch_size;y_offset++)\n        {\n            double o_value = bilinearInterpolateImage(image,x+x_offset,y+y_offset);\n            result.push_back(o_value);\n        }\n    }\n    \n    return result;\n}\n\n// Todo: change return to parameter passed by ref\nstd::vector<int> Tracker::checkLocationValidity(std::vector<cv::Point2f> points)\n{\n    // Check for each passed point location if the patch centered around it falls completely within the input images\n    // Return 0 for a point if not, 1 if yes\n    \n    int min_x = m_patch_size+1;  //Todo: should be m_patch_size?\n    int min_y = m_patch_size+1;\n    \n    int max_x = m_database->m_image_width-m_patch_size-1;\n    int max_y = m_database->m_image_height-m_patch_size-1;\n    \n    std::vector<int> is_valid;\n    \n    for(int i = 0;i < points.size();i++)\n    {\n        if(points.at(i).x < min_x || points.at(i).x > max_x || points.at(i).y < min_y || points.at(i).y > max_y)\n        {\n            is_valid.push_back(0);\n        }\n        else\n        {\n            is_valid.push_back(1);\n        }\n    }\n    \n    return is_valid;\n}\n\n// Todo: change parameter type to reference (or const reference)\nvoid Tracker::initialFeatureExtraction(cv::Mat input_image,cv::Mat gradient_image,double gt_exp_time)\n{\n    std::vector<cv::Point2f> old_f;\n    std::vector<cv::Point2f> feature_locations = extractFeatures(input_image,old_f);\n    std::vector<int> validity_vector = checkLocationValidity(feature_locations);\n    \n    // Initialize new tracking Frame\n    Frame frame;\n    frame.m_image = input_image;\n    frame.m_image_corrected = input_image.clone();\n    frame.m_exp_time = 1.0;\n    frame.m_gt_exp_time = gt_exp_time;\n    \n    // Push back tracked feature points to the tracking Frame\n    for(int p = 0;p < feature_locations.size();p++)\n    {\n        // Skip invalid points (too close to the side of image)\n        if(validity_vector.at(p) == 0)\n            continue;\n        \n        // Create new feature object and associate with it output intensities, gradient values, etc.\n        Feature* f = new Feature();\n        f->m_xy_location = feature_locations.at(p);\n        f->m_next_feature = NULL;\n        f->m_prev_feature = NULL;\n        std::vector<double> os = bilinearInterpolateImagePatch(input_image,feature_locations.at(p).x,feature_locations.at(p).y);\n        std::vector<double> gs = bilinearInterpolateImagePatch(gradient_image,feature_locations.at(p).x,feature_locations.at(p).y);\n        f->m_gradient_values = gs;\n        f->m_output_values = os;\n        f->m_radiance_estimates = os;\n        frame.m_features.push_back(f);\n    }\n    \n    m_database->m_tracked_frames.push_back(frame);\n}\n\nvoid Tracker::computeGradientImage(cv::Mat input_image,cv::Mat &gradient_image)\n{\n    // Blur the input image a little and apply discrete 3x3 sobel filter in x,y directions to obtain a gradient estimate\n    // Todo: change to class member\n    cv::Mat blurred_image;\n    cv::GaussianBlur( input_image, blurred_image, cv::Size(3,3), 0, 0, cv::BORDER_DEFAULT );\n    // Todo: change to class member\n    cv::Mat grad_x,grad_y;\n    cv::Sobel( blurred_image, grad_x, CV_16S, 1, 0, 3, 1.0, 0, cv::BORDER_DEFAULT );\n    cv::Sobel( blurred_image, grad_y, CV_16S, 0, 1, 3, 1.0, 0, cv::BORDER_DEFAULT );\n    cv::convertScaleAbs( grad_x, grad_x );\n    cv::convertScaleAbs( grad_y, grad_y );\n    cv::addWeighted( grad_x, 0.5, grad_y, 0.5, 0, gradient_image );\n}\n\nvoid Tracker::photometricallyCorrectImage(cv::Mat &corrected_frame)\n{\n    for(int r = 0;r < corrected_frame.rows;r++)\n    {\n        for(int c = 0;c < corrected_frame.cols;c++)\n        {\n            int o_value = corrected_frame.at<uchar>(r,c);\n            double radiance = m_database->m_response_estimate.removeResponse(o_value);\n            double vig = m_database->m_vignette_estimate.getVignetteFactor(cv::Point2f(c,r));\n            radiance /= vig;\n            if(radiance > 255)radiance = 255;\n            if(radiance < 0)radiance = 0;\n            corrected_frame.at<uchar>(r,c) = (uchar)radiance;\n        }\n    }\n    \n    /*\n     * For debugging: visualize the corrected frame\n     *\n     * cv::imshow(\"radiance image\", corrected_frame);\n     * cv::waitKey(0);\n     *\n     */\n}\n\n"
  },
  {
    "path": "src/Tracker.h",
    "content": "//\n//  Tracker.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#ifndef OnlinePhotometricCalibration_Tracker_h_\n#define OnlinePhotometricCalibration_Tracker_h_\n\n#include \"StandardIncludes.h\"\n\n#include \"Database.h\"\n\n#include \"GainRobustTracker.h\"\n\n#include \"Frame.h\"\n\n#include \"Feature.h\"\n\n/**\n *\n * Keeps original input images, corrects them based on the current V,f estimates\n *\n * Extracts features from input images and tracks them between consecutive frames \n *\n * Implements forward backward tracking in order to filter out spurious tracks\n *\n * Uses gain robust KLT tracking to track robustly when large illumination changes between 2 frames occur\n *\n */\n\nclass Tracker\n{\n    \n    /**\n     * Forward backward tracking error threshold (if error is larger than this the track is set invalid)\n     */\n    const int C_FWD_BWD_TRACKING_THRESH = 2.0;\n    \n    /**\n     * Patch size used for KLT offset optimization\n     */\n    const int C_KLT_PATCH_SIZE = 2;\n    \n    /**\n     * Number of pyramid levels used\n     */\n    const int C_NR_PYRAMID_LEVELS = 3;\n    \npublic:\n    \n    /**\n     * Constructor\n     * @param patch_size Size of the tracked image patches\n     * @param nr_active_features Number of active features aimed to be kept for tracking\n     * @param nr_pyramid_levels Number of pyramid levels to be used for KLT tracking\n     * @param database Handle to the information database\n     */\n    Tracker(int patch_size,int nr_active_features,int nr_pyramid_levels,Database* database);\n    \n    /**\n     * Track features from old frame to the new input frame\n     * Extract new features if necessary\n     * Filter out spurious tracks\n     */\n     // Todo: change param to reference\n    void trackNewFrame(cv::Mat frame,double gt_exp_time);\n    \nprivate:\n    \n    /**\n     * Patchsize extracted around the tracked points \n     * (not the one used for KLT tracking optimization)\n     */\n    int m_patch_size;\n    \n    /**\n     * Number of active features aimed at when tracking\n     */\n    int m_max_nr_active_features;\n    \n    /**\n     * Number of pyramid levels used for KLT tracking\n     */\n    int m_nr_pyramid_levels;\n    \n    /**\n     * Pointer to the database to fetch and update information\n     */\n    Database* m_database;\n    \n    /**\n     * Extract new features from the frame\n     */\n    // Todo: change param to reference\n    std::vector<cv::Point2f> extractFeatures(cv::Mat frame,std::vector<cv::Point2f> old_features);\n    \n    /**\n     * Bilinear interpolation, evaluate image at floating point location (x,y)\n     */\n    // Todo: change param to reference\n    double bilinearInterpolateImage(cv::Mat image,double x,double y);\n    \n    /**\n     * Evaluate entire image patch at floating point location (x,y)\n     */\n    // Todo: change param to reference\n    std::vector<double> bilinearInterpolateImagePatch(cv::Mat image,double x,double y);\n    \n    /**\n     * Check if points locations are not too close to the border of the image\n     */\n    std::vector<int> checkLocationValidity(std::vector<cv::Point2f> points);\n    \n    /**\n     * Extract features for the first time\n     */\n    // Todo: change param to reference\n    void initialFeatureExtraction(cv::Mat input_image,cv::Mat gradient_image,double gt_exp_time);\n    \n    /**\n     * Compute gradient image\n     */\n    // Todo: change param to reference\n    void computeGradientImage(cv::Mat input_image,cv::Mat &gradient_image);\n    \n    /**\n     * Photometrically correct image based on vignette + response estimate\n     */\n    void photometricallyCorrectImage(cv::Mat &corrected_image);\n};\n\n#endif // include guard\n\n"
  },
  {
    "path": "src/VignetteModel.cpp",
    "content": "//\n//  VignetteModel.cpp\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#include \"VignetteModel.h\"\n\nVignetteModel::VignetteModel(double v1,double v2,double v3,int image_width,int image_height)\n{\n    // Initialize vignette parameters\n    m_v1 = v1;\n    m_v2 = v2;\n    m_v3 = v3;\n    \n    // Image information is necessary to compute normalized radial information from pixel coordinates\n    m_image_width = image_width;\n    m_image_height= image_height;\n    \n    // Max. non normalized image radius (center of image = center of radial vignetting assumption)\n    m_max_radius = sqrt((image_width/2)*(image_width/2) + (image_height/2)*(image_height/2));\n}\n\ndouble VignetteModel::getNormalizedRadius(cv::Point2f xy_location)\n{\n    double x = xy_location.x;\n    double y = xy_location.y;\n    \n    double x_norm = x - m_image_width/2;\n    double y_norm = y - m_image_height/2;\n    \n    double radius = sqrt(x_norm*x_norm + y_norm*y_norm);\n    radius /= m_max_radius;\n    \n    return radius;\n}\n\ndouble VignetteModel::getVignetteFactor(cv::Point2f xy_location)\n{\n    double r = getNormalizedRadius(xy_location);\n    double r2 = r * r;\n    double r4 = r2 * r2;\n    double v = 1 + m_v1 * r2 + m_v2 * r4 + m_v3 * r2 * r4;\n    \n    return v;\n}\n\ndouble VignetteModel::getVignetteFactor(double norm_radius)\n{\n    double r = norm_radius;\n    double r2 = r * r;\n    double r4 = r2 * r2;\n    double v = 1 + m_v1 * r2 + m_v2 * r4 + m_v3 * r2 * r4;\n    \n    return v;\n}\n\nvoid VignetteModel::setVignetteParameters(std::vector<double> vignette_model)\n{\n    m_v1 = vignette_model.at(0);\n    m_v2 = vignette_model.at(1);\n    m_v3 = vignette_model.at(2);\n}\n\n\n"
  },
  {
    "path": "src/VignetteModel.h",
    "content": "//\n//  VignetteModel.h\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#ifndef OnlinePhotometricCalibration_VignetteModel_h_\n#define OnlinePhotometricCalibration_VignetteModel_h_\n\n#include \"StandardIncludes.h\"\n\n#include <math.h>\n\n/**\n * Stores vignetting model parameters and provides functionality to \n * apply / remove vignetting\n */\n\nclass VignetteModel\n{\n    \npublic:\n    \n    /**\n     * Initialize vignette model\n     * @param v1,v2,v3 parameters of polynomial vignetting model v(r) = 1 + v1*r^2 + v2*r^4 + v3*r^6\n     * @param image_width,image_height dimensions of the input images\n     */\n    VignetteModel(double v1,double v2,double v3,int image_width,int image_height);\n    \n    /**\n     * Calculate a radius value between [0,1] for a given point location\n     */\n    double getNormalizedRadius(cv::Point2f xy_location);\n    \n    /**\n     * Calculate vignette factor for a given point location based on the current vignette estimate\n     */\n    double getVignetteFactor(cv::Point2f xy_location);\n    \n    /**\n     * Calculate vignette factor for a given normalize radius based on the current vignette estimate\n     */\n    double getVignetteFactor(double norm_radius);\n    \n    /**\n     * Return current vignette estimate parameter vector\n     */\n    std::vector<double> getVignetteEstimate()\n    {\n        std::vector<double> result;\n        result.push_back(m_v1);\n        result.push_back(m_v2);\n        result.push_back(m_v3);\n        return result;\n    }\n    \n    /**\n     * Overwrite vignette estimate parameters\n     */\n    void setVignetteParameters(std::vector<double> vignette_model);\n    \nprivate:\n    \n    /**\n     * Parameter values of the vignette model\n     */\n    double m_v1;\n    double m_v2;\n    double m_v3;\n    \n    /**\n     * Input image dimensions\n     */\n    int m_image_width;\n    int m_image_height;\n    \n    /**\n     * Maximum pixel radius in image coordinates (used for normalization of radius to [0,1])\n     */\n    double m_max_radius;\n};\n\n#endif // include guard\n"
  },
  {
    "path": "src/main.cpp",
    "content": "//\n//  main.cpp\n//  OnlinePhotometricCalibration\n//\n//  Created by Paul on 16.11.17.\n//  Copyright (c) 2017-2018 Paul Bergmann and co-authors. All rights reserved.\n//\n//  See LICENSE.txt\n//\n\n#include \"StandardIncludes.h\"\n\n#include \"ImageReader.h\"\n#include \"Tracker.h\"\n#include \"RapidExposureTimeEstimator.h\"\n#include \"Database.h\"\n#include \"NonlinearOptimizer.h\"\n#include \"CLI11.hpp\"\n\nusing namespace std;\n\n// Optimization thread handle\npthread_t opt_thread = 0;\n\n// This variable indicates if currently an optimization task is running in a second thread\npthread_mutex_t g_is_optimizing_mutex;\nbool g_is_optimizing = false;\n\nstruct Settings{\n    int start_image_index;      // Start image index.\n    int end_image_index;        // End image index.\n    int image_width;            // Image width to resize to.\n    int image_height;           // Image height to resize to.\n    int visualize_cnt;          // Visualize every visualize_cnt image (tracking + correction), rather slow.\n    int tracker_patch_size;     // Image patch size used in tracker.\n    int nr_pyramid_levels;      // Number of image pyramid levels used in tracker.\n    int nr_active_features;     // Number of features maintained for each frame.\n    int nr_images_rapid_exp;    // Number of images for rapid exposure time estimation.\n    int nr_active_frames;       // Number of frames maintained in database.\n    int keyframe_spacing;       // Spacing for sampling keyframes in backend optimization.\n    int min_keyframes_valid;    // Minimum amount of keyframes a feature should be present to be included in optimization.\n    string image_folder;        // Image folder.\n    string exposure_gt_file;    // Exposure times ground truth file.\n    string calibration_mode;    // Choose \"online\" or \"batch\".\n};\n\nvoid run_batch_optimization_task(NonlinearOptimizer *optimizer)\n{\n    std::cout << \"START BATCH OPTIMIZATION\" << std::endl;\n    \n    optimizer->fetchResponseVignetteFromDatabase();\n\n    // Perform optimization (since its offline just do )\n    optimizer->evfOptimization(false);\n    std::cout << \"EVF 1 DONE\" << std::endl;\n    optimizer->radianceFullOptimization();\n    std::cout << \"RAD 1 DONE\" << std::endl;\n    optimizer->evfOptimization(false);\n    std::cout << \"EVF 2 DONE\" << std::endl;\n    optimizer->radianceFullOptimization();\n    std::cout << \"RAD 2 DONE\" << std::endl;\n    optimizer->evfOptimization(false);\n    std::cout << \"EVF 3 DONE\" << std::endl;\n    \n    // Smooth optimization data\n    optimizer->smoothResponse();\n    \n    // Initialize the inverse response vector with the current inverse response estimate\n    // (in order to write it to the database later + visualization)\n    // better to do this here since currently the inversion is done rather inefficiently and not to slow down tracking\n    optimizer->getInverseResponseRaw(optimizer->m_raw_inverse_response);\n    std::cout << \"END OPTIMIZATION\" << std::endl;\n}\n\nvoid *run_optimization_task(void* thread_arg)\n{\n    std::cout << \"START OPTIMIZATION\" << std::endl;\n    \n    pthread_mutex_lock(&g_is_optimizing_mutex);\n    g_is_optimizing = true;\n    pthread_mutex_unlock(&g_is_optimizing_mutex);\n    \n    // The nonlinear optimizer contains all the optimization information\n    NonlinearOptimizer* optimizer = (NonlinearOptimizer*)thread_arg;\n    \n    optimizer->fetchResponseVignetteFromDatabase();\n\n    // Perform optimization\n    optimizer->evfOptimization(false);\n    optimizer->evfOptimization(false);\n    optimizer->evfOptimization(false);\n    \n    // Smooth optimization data\n    optimizer->smoothResponse();\n    \n    // Initialize the inverse response vector with the current inverse response estimate\n    // (in order to write it to the database later + visualization)\n    // better to do this here since currently the inversion is done rather inefficiently and not to slow down tracking\n    optimizer->getInverseResponseRaw(optimizer->m_raw_inverse_response);\n    \n    pthread_mutex_lock(&g_is_optimizing_mutex);\n    g_is_optimizing = false;\n    pthread_mutex_unlock(&g_is_optimizing_mutex);\n    std::cout << \"END OPTIMIZATION\" << std::endl;\n    \n    pthread_exit(NULL);\n}\n\n// Split a string into substrings given a char delimiter\nstd::vector<string> split(const string &s, char delim) {\n    stringstream ss(s);\n    string item;\n    vector<string> tokens;\n    while (getline(ss, item, delim)) {\n        tokens.push_back(item);\n    }\n    return tokens;\n}\n\nint run_batch_calibration(Settings *run_settings,std::vector<double> gt_exp_times)\n{\n    int safe_zone_size = 0;\n\n    double vis_exponent = 1.0;\n\n    //  Set up the object to read new images from\n    ImageReader image_reader(run_settings->image_folder, cv::Size(run_settings->image_width, run_settings->image_height));\n\n    // Set up the information database\n    Database database(run_settings->image_width,run_settings->image_height);\n\n    // Setup the rapid  exposure time estimator\n    RapidExposureTimeEstimator exposure_estimator(run_settings->nr_images_rapid_exp, &database);\n\n    // Setup the nonlinear optimizer\n    NonlinearOptimizer backend_optimizer(run_settings->keyframe_spacing,\n                                         &database,\n                                         safe_zone_size,\n                                         run_settings->min_keyframes_valid,\n                                         run_settings->tracker_patch_size);\n\n    // Set up the object that handles the tracking and receives new images, extracts features\n    Tracker tracker(run_settings->tracker_patch_size,run_settings->nr_active_features,run_settings->nr_pyramid_levels,&database);\n    \n    int num_images = image_reader.getNumImages();\n    \n    // Run over all input images, track the new image, estimate exposure time and optimize other parameters in the background\n    for(int i = run_settings->start_image_index; i < num_images && (run_settings->end_image_index < 0 || i < run_settings->end_image_index); i++)\n    {\n        std::cout << \"image: \" << i << std::endl;\n\n        // Read GT exposure time if available for this frame\n        // Todo: Check if index is out of bounds, if gt exp file has not enough lines\n        double gt_exp_time = -1.0;\n        if(gt_exp_times.size() > 0)\n        {\n            gt_exp_time = gt_exp_times.at(i);\n        }\n        \n        // Read next input image\n        cv::Mat new_image = image_reader.readImage(i);\n        \n        // Track input image (+ time the result)\n        tracker.trackNewFrame(new_image,gt_exp_time);\n\n        // Rapid exposure time estimation (+ time the result)\n        double exposure_time = exposure_estimator.estimateExposureTime();\n        database.m_tracked_frames.at(database.m_tracked_frames.size()-1).m_exp_time = exposure_time;\n        database.visualizeRapidExposureTimeEstimates(vis_exponent);\n\n        // Remove the exposure time from the radiance estimates\n        std::vector<Feature*>* features = &database.m_tracked_frames.at(database.m_tracked_frames.size()-1).m_features;\n        for(int k = 0;k < features->size();k++)\n        {\n            for(int r = 0;r < features->at(k)->m_radiance_estimates.size();r++)\n            {\n                features->at(k)->m_radiance_estimates.at(r) /= exposure_time;\n            }\n        }\n  \n        // Visualize tracking\n        if(i%run_settings->visualize_cnt == 0)\n            database.visualizeTracking();\n\n        //check if number of frames in database are enough to start the optimization\n        if(database.m_tracked_frames.size() != run_settings->nr_active_frames)\n        {\n            continue;\n        }\n        \n        // Required number of frames has been reached, calibrate all of them\n        \n        // Try to fetch a new optimization block\n        bool succeeded = backend_optimizer.extractOptimizationBlock();\n        \n        if(succeeded)\n        {\n            // Optimize all frames\n            run_batch_optimization_task(&backend_optimizer);\n\n            // Show optimization result\n            vis_exponent = backend_optimizer.visualizeOptimizationResult(backend_optimizer.m_raw_inverse_response);\n\n            // Remove frames except for some overlap in order to align exposures later\n            for(int k = 0;k < run_settings->nr_active_frames-30;k++)\n            {\n                database.removeLastFrame();\n            }\n        }\n        else\n        {\n            //Something went wrong..\n            std::cout << \"Error: Optimization block could not be fetched.\" << std::endl;\n            return -1;\n        }\n    }\n    \n    return 0;\n}\n\nint run_online_calibration(Settings *run_settings,std::vector<double> gt_exp_times)\n{\n    double vis_exponent = 1.0;\n\n    int safe_zone_size = run_settings->nr_images_rapid_exp + 5;\n\n    //  Set up the object to read new images from\n    ImageReader image_reader(run_settings->image_folder, cv::Size(run_settings->image_width, run_settings->image_height));\n\n    // Set up the information database\n    Database database(run_settings->image_width,run_settings->image_height);\n\n    // Setup the rapid  exposure time estimator\n    RapidExposureTimeEstimator exposure_estimator(run_settings->nr_images_rapid_exp, &database);\n\n    // Setup the nonlinear optimizer\n    NonlinearOptimizer backend_optimizer(run_settings->keyframe_spacing,\n                                         &database,\n                                         safe_zone_size,\n                                         run_settings->min_keyframes_valid,\n                                         run_settings->tracker_patch_size);\n\n    // Set up the object that handles the tracking and receives new images, extracts features\n    Tracker tracker(run_settings->tracker_patch_size,run_settings->nr_active_features,run_settings->nr_pyramid_levels,&database);\n    \n    int optimize_cnt = 0;\n    int num_images = image_reader.getNumImages();\n    \n    // Run over all input images, track the new image, estimate exposure time and optimize other parameters in the background\n    for(int i = run_settings->start_image_index; i < num_images && (run_settings->end_image_index < 0 || i < run_settings->end_image_index); i++)\n    {\n        std::cout << \"image: \" << i << std::endl;\n\n        // Read GT exposure time if available for this frame\n        // Todo: Check if index is out of bounds, if gt exp file has not enough lines\n        double gt_exp_time = -1.0;\n        if(gt_exp_times.size() > 0)\n        {\n            gt_exp_time = gt_exp_times.at(i);\n        }\n        \n        // If enough images are in the database, remove once all initial images for which no exposure time could be optimized\n        // Since those frames will not be that good for backend optimization\n        if(i == run_settings->nr_images_rapid_exp*2 + safe_zone_size)\n        {\n            for(int ii = 0;ii < run_settings->nr_images_rapid_exp;ii++)\n            {\n                database.removeLastFrame();\n            }\n        }\n        \n        // If the database is large enough, start removing old frames\n        if(i > run_settings->nr_active_frames)\n            database.removeLastFrame();\n        \n        // Read next input image\n        cv::Mat new_image = image_reader.readImage(i);\n        \n        // Track input image (+ time the result)\n        tracker.trackNewFrame(new_image,gt_exp_time);\n      \n        // Rapid exposure time estimation (+ time the result)\n        double exposure_time = exposure_estimator.estimateExposureTime();\n        database.m_tracked_frames.at(database.m_tracked_frames.size()-1).m_exp_time = exposure_time;\n        database.visualizeRapidExposureTimeEstimates(vis_exponent);\n\n        // Remove the exposure time from the radiance estimates\n        std::vector<Feature*>* features = &database.m_tracked_frames.at(database.m_tracked_frames.size()-1).m_features;\n        for(int k = 0;k < features->size();k++)\n        {\n            for(int r = 0;r < features->at(k)->m_radiance_estimates.size();r++)\n            {\n                features->at(k)->m_radiance_estimates.at(r) /= exposure_time;\n            }\n        }\n  \n        // Visualize tracking\n        if(i%run_settings->visualize_cnt == 0)\n            database.visualizeTracking();\n        \n        pthread_mutex_lock(&g_is_optimizing_mutex);\n        bool is_optimizing = g_is_optimizing;\n        pthread_mutex_unlock(&g_is_optimizing_mutex);\n        \n        // Optimization is still running, don't do anything and keep tracking\n        if(is_optimizing)\n        {\n            continue;\n        }\n        \n        //optimization is currently not running\n        // (1) Fetch the current optimization result and update database\n        // (2) Try to extract a new optimization block and restart optimization in the background\n        \n        // Fetch the old optimization result from the optimizer, if available\n        if(optimize_cnt > 0)\n        {\n            // Write the result to the database, visualize the result\n            database.m_vignette_estimate.setVignetteParameters(backend_optimizer.m_vignette_estimate);\n            database.m_response_estimate.setGrossbergParameterVector(backend_optimizer.m_response_estimate);\n            database.m_response_estimate.setInverseResponseVector(backend_optimizer.m_raw_inverse_response);\n            \n            vis_exponent = backend_optimizer.visualizeOptimizationResult(backend_optimizer.m_raw_inverse_response);\n        }\n        \n        // Try to fetch a new optimization block\n        bool succeeded = backend_optimizer.extractOptimizationBlock();\n        \n        if(succeeded)\n        {\n            // TODO: reuse thread\n            //start a new optimization task here\n            pthread_create(&opt_thread, NULL, run_optimization_task, (void*)&backend_optimizer);\n            optimize_cnt++;\n        }\n    }\n\n    pthread_join(opt_thread,NULL);\n\n    if(optimize_cnt > 0)\n    {\n        // Write the result to the database, visualize the result\n        database.m_vignette_estimate.setVignetteParameters(backend_optimizer.m_vignette_estimate);\n        database.m_response_estimate.setGrossbergParameterVector(backend_optimizer.m_response_estimate);\n        database.m_response_estimate.setInverseResponseVector(backend_optimizer.m_raw_inverse_response);\n\n        vis_exponent = backend_optimizer.visualizeOptimizationResult(backend_optimizer.m_raw_inverse_response);\n    }\n    \n    return 0;\n}\n\nint main(int argc, char** argv)\n{\n    CLI::App app(\"Photometric Calibration\");\n\n    Settings run_settings;\n    run_settings.start_image_index   = 0;      \n    run_settings.end_image_index     = -1;\n    run_settings.image_width         = 640;    \n    run_settings.image_height        = 480;   \n    run_settings.visualize_cnt       = 1;       \n    run_settings.tracker_patch_size  = 3;       \n    run_settings.nr_pyramid_levels   = 2;       \n    run_settings.nr_active_features  = 200;     \n    run_settings.nr_images_rapid_exp = 15;     \n    run_settings.image_folder = \"images\";\n    run_settings.exposure_gt_file = \"times.txt\";\n    run_settings.calibration_mode = \"online\";\n    run_settings.nr_active_frames    = 200;    \n    run_settings.keyframe_spacing    = 15; \n    run_settings.min_keyframes_valid = 3;\n\n    app.add_option(\"-i,--image-folder\", run_settings.image_folder, \"Folder with image files to read.\", true);\n    app.add_option(\"--start-image-index\", run_settings.start_image_index, \"Start reading from this image index.\", true);\n    app.add_option(\"--end-image-index\", run_settings.end_image_index, \"Stop reading at this image index.\", true);\n    app.add_option(\"--image-width\", run_settings.image_width, \"Resize image to this width.\", true);\n    app.add_option(\"--image-height\", run_settings.image_height, \"Resize image to this height.\", true);\n    app.add_option(\"--exposure-gt-file\", run_settings.exposure_gt_file, \"Textfile containing ground truth exposure times for each frame for visualization.\", true);\n    app.add_option(\"--calibration-mode\", run_settings.calibration_mode, \"Choose 'online' or 'batch'\", true);\n    \n    app.add_option(\"--nr-active-frames\", run_settings.nr_active_frames, \"Maximum number of frames to be stored in the database.\", true);\n    app.add_option(\"--keyframe-spacing\", run_settings.keyframe_spacing, \"Number of frames that keyframes are apart in the backend optimizer.\", true);\n    app.add_option(\"--min-keyframes-valid\", run_settings.min_keyframes_valid, \"Minimum number of frames a feature has to be tracked to be considered for optimization.\", true);\n\n    CLI11_PARSE(app, argc, argv);\n\n    printf(\"Loading images from '%s'\\n\", run_settings.image_folder.c_str());\n    printf(\"Start at index %d\\n\", run_settings.start_image_index);\n    printf(\"End at index %d\\n\", run_settings.end_image_index);\n    printf(\"Image width %d\\n\", run_settings.image_width);\n    printf(\"Image height %d\\n\", run_settings.image_height);\n\n    // Parse gt exposure times from file if available\n    // Only use the last number in each line, delimiter is the space character ' '\n    std::ifstream exposure_gt_file_handle(run_settings.exposure_gt_file);\n    std::vector<double> gt_exp_times;\n    string line;\n    double min_time = 10000.0;\n    double max_time = -10000.0;\n    if (exposure_gt_file_handle.is_open())\n    {\n        while(getline(exposure_gt_file_handle,line))\n        {\n            std::string delimiter = \" \";\n            std::vector<string> split_line = split(line,' ');\n            double gt_exp_time = stod(split_line.at(split_line.size()-1));\n            gt_exp_times.push_back(gt_exp_time);\n            if(gt_exp_time < min_time)\n                min_time = gt_exp_time;\n            if(gt_exp_time > max_time)\n                max_time = gt_exp_time;\n        }    \n        std::cout << \"Ground truth exposure time file successfully read (\" << run_settings.exposure_gt_file << \").\" << std::endl;\n    }\n    else\n    {\n        std::cout << \"Ground truth exposure time file not found (\" << run_settings.exposure_gt_file << \").\" << std::endl;\n    }\n    exposure_gt_file_handle.close();\n\n    for(int k = 0;k < gt_exp_times.size();k++)\n    {    \n        //normalize gt exposures to range [0,1]\n        gt_exp_times.at(k) = (gt_exp_times.at(k) - min_time)/(max_time - min_time);\n    }\n\n    // Run program either in multithreaded online mode \n    // or in linearized batch mode\n    if(run_settings.calibration_mode == \"online\")\n    {\n        std::cout << \"Run online calibration mode.\" << std::endl;\n        run_online_calibration(&run_settings, gt_exp_times);\n    }\n    else if(run_settings.calibration_mode == \"batch\")\n    {\n        std::cout << \"Run batch calibration mode.\" << std::endl;\n        run_batch_calibration(&run_settings, gt_exp_times);\n    }\n    else\n    {\n        std::cout << \"Error: Unknown calibration mode '\" << run_settings.calibration_mode << \"'.\" << std::endl;\n        return -1;\n    }\n\n    // wait for key-press, then exit\n    std::cout << \"Finished. Press key to exit.\" << std::endl;\n    cv::waitKey(0);\n\n    return 0;\n}\n\n"
  }
]